From 3dd253790081482287a55fdca83382bdecdfc89c Mon Sep 17 00:00:00 2001 From: "Jakub A. W" Date: Fri, 16 Jan 2026 16:22:49 +0100 Subject: [PATCH 1/2] feat: simplified helm charts --- helm/README.md | 9 +- helm/templates/NOTES.txt | 12 +- helm/templates/_helpers.tpl | 16 +- helm/values.schema.json | 363 ++++-------------------------------- helm/values.yaml | 10 +- 5 files changed, 60 insertions(+), 350 deletions(-) diff --git a/helm/README.md b/helm/README.md index 966443a6..eb400e73 100644 --- a/helm/README.md +++ b/helm/README.md @@ -20,18 +20,15 @@ helm repo update ### Install from local chart ```bash -# Basic install with OpenAI +# Basic install with OpenAI (provider auto-enables when apiKey is set) helm install gomodel ./helm \ -n gomodel --create-namespace \ - --set providers.openai.enabled=true \ --set providers.openai.apiKey="sk-..." # Multi-provider setup with Redis cache helm install gomodel ./helm \ -n gomodel --create-namespace \ - --set providers.openai.enabled=true \ --set providers.openai.apiKey="sk-..." \ - --set providers.anthropic.enabled=true \ --set providers.anthropic.apiKey="sk-ant-..." \ --set redis.enabled=true @@ -86,7 +83,7 @@ stringData: GEMINI_API_KEY: "..." ``` -Then reference it: +Then reference it (use `enabled=true` when using existingSecret since apiKey isn't set directly): ```bash helm install gomodel ./helm \ @@ -141,4 +138,4 @@ helm uninstall gomodel -n gomodel - Add a values-demo.yaml file with a demo setup ready to run - Consider adding prometheus + grafana stack as an optional subchart -- Add an example for production-ready redis configuration with persistence and authentication enabled \ No newline at end of file +- Add an example for production-ready redis configuration with persistence and authentication enabled diff --git a/helm/templates/NOTES.txt b/helm/templates/NOTES.txt index af33024f..b86ce6c6 100644 --- a/helm/templates/NOTES.txt +++ b/helm/templates/NOTES.txt @@ -34,16 +34,16 @@ To get the application URL: {{- end }} {{- $enabledProviders := list }} -{{- if .Values.providers.openai.enabled }}{{ $enabledProviders = append $enabledProviders "openai" }}{{ end }} -{{- if .Values.providers.anthropic.enabled }}{{ $enabledProviders = append $enabledProviders "anthropic" }}{{ end }} -{{- if .Values.providers.gemini.enabled }}{{ $enabledProviders = append $enabledProviders "gemini" }}{{ end }} -{{- if .Values.providers.groq.enabled }}{{ $enabledProviders = append $enabledProviders "groq" }}{{ end }} -{{- if .Values.providers.xai.enabled }}{{ $enabledProviders = append $enabledProviders "xai" }}{{ end }} +{{- if or .Values.providers.openai.enabled .Values.providers.openai.apiKey }}{{ $enabledProviders = append $enabledProviders "openai" }}{{ end }} +{{- if or .Values.providers.anthropic.enabled .Values.providers.anthropic.apiKey }}{{ $enabledProviders = append $enabledProviders "anthropic" }}{{ end }} +{{- if or .Values.providers.gemini.enabled .Values.providers.gemini.apiKey }}{{ $enabledProviders = append $enabledProviders "gemini" }}{{ end }} +{{- if or .Values.providers.groq.enabled .Values.providers.groq.apiKey }}{{ $enabledProviders = append $enabledProviders "groq" }}{{ end }} +{{- if or .Values.providers.xai.enabled .Values.providers.xai.apiKey }}{{ $enabledProviders = append $enabledProviders "xai" }}{{ end }} {{- if eq (len $enabledProviders) 0 }} ⚠️ WARNING: No providers enabled! - Enable at least one provider (e.g., providers.openai.enabled=true) + Provide an API key for at least one provider (e.g., providers.openai.apiKey) {{- else }} Enabled providers: {{ join ", " $enabledProviders }} diff --git a/helm/templates/_helpers.tpl b/helm/templates/_helpers.tpl index 24083bf3..3c999eb8 100644 --- a/helm/templates/_helpers.tpl +++ b/helm/templates/_helpers.tpl @@ -103,15 +103,23 @@ Create the image reference {{- printf "%s:%s" .Values.image.repository $tag }} {{- end }} +{{/* +Check if a provider is enabled (explicitly or by having an API key) +*/}} +{{- define "gomodel.providerEnabled" -}} +{{- $config := . -}} +{{- if or $config.enabled $config.apiKey -}} +true +{{- end -}} +{{- end -}} + {{/* Generate provider API key entries for the Secret stringData. */}} {{- define "gomodel.providerSecretData" -}} {{- range $name, $config := .Values.providers }} - {{- if and (kindIs "map" $config) (hasKey $config "enabled") }} - {{- if and $config.enabled $config.apiKey }} + {{- if and (kindIs "map" $config) (hasKey $config "apiKey") $config.apiKey }} {{ upper $name }}_API_KEY: {{ $config.apiKey | quote }} - {{- end }} {{- end }} {{- end }} {{- end }} @@ -122,7 +130,7 @@ Generate provider environment variables for the Deployment. {{- define "gomodel.providerEnvVars" -}} {{- $secretName := include "gomodel.providerSecretName" . -}} {{- range $name, $config := .Values.providers }} - {{- if and (kindIs "map" $config) (hasKey $config "enabled") $config.enabled }} + {{- if and (kindIs "map" $config) (hasKey $config "apiKey") (or $config.enabled $config.apiKey) }} - name: {{ upper $name }}_API_KEY valueFrom: secretKeyRef: diff --git a/helm/values.schema.json b/helm/values.schema.json index f75a74ba..1b4f95d1 100644 --- a/helm/values.schema.json +++ b/helm/values.schema.json @@ -59,10 +59,7 @@ { "properties": { "providers": { - "properties": { - "existingSecret": { "minLength": 1 } - }, - "required": ["existingSecret"] + "properties": { "existingSecret": { "minLength": 1 } } } } }, @@ -71,37 +68,8 @@ "providers": { "properties": { "openai": { - "properties": { - "enabled": { "const": true } - } + "properties": { "apiKey": { "minLength": 1 } } } - }, - "if": { - "properties": { - "openai": { - "properties": { "enabled": { "const": true } } - } - } - }, - "then": { - "anyOf": [ - { - "properties": { - "existingSecret": { "minLength": 1 } - }, - "required": ["existingSecret"] - }, - { - "properties": { - "openai": { - "properties": { - "apiKey": { "minLength": 1 } - }, - "required": ["apiKey"] - } - } - } - ] } } } @@ -111,37 +79,8 @@ "providers": { "properties": { "anthropic": { - "properties": { - "enabled": { "const": true } - } - } - }, - "if": { - "properties": { - "anthropic": { - "properties": { "enabled": { "const": true } } - } + "properties": { "apiKey": { "minLength": 1 } } } - }, - "then": { - "anyOf": [ - { - "properties": { - "existingSecret": { "minLength": 1 } - }, - "required": ["existingSecret"] - }, - { - "properties": { - "anthropic": { - "properties": { - "apiKey": { "minLength": 1 } - }, - "required": ["apiKey"] - } - } - } - ] } } } @@ -151,37 +90,8 @@ "providers": { "properties": { "gemini": { - "properties": { - "enabled": { "const": true } - } - } - }, - "if": { - "properties": { - "gemini": { - "properties": { "enabled": { "const": true } } - } + "properties": { "apiKey": { "minLength": 1 } } } - }, - "then": { - "anyOf": [ - { - "properties": { - "existingSecret": { "minLength": 1 } - }, - "required": ["existingSecret"] - }, - { - "properties": { - "gemini": { - "properties": { - "apiKey": { "minLength": 1 } - }, - "required": ["apiKey"] - } - } - } - ] } } } @@ -191,37 +101,8 @@ "providers": { "properties": { "groq": { - "properties": { - "enabled": { "const": true } - } - } - }, - "if": { - "properties": { - "groq": { - "properties": { "enabled": { "const": true } } - } + "properties": { "apiKey": { "minLength": 1 } } } - }, - "then": { - "anyOf": [ - { - "properties": { - "existingSecret": { "minLength": 1 } - }, - "required": ["existingSecret"] - }, - { - "properties": { - "groq": { - "properties": { - "apiKey": { "minLength": 1 } - }, - "required": ["apiKey"] - } - } - } - ] } } } @@ -231,37 +112,8 @@ "providers": { "properties": { "xai": { - "properties": { - "enabled": { "const": true } - } - } - }, - "if": { - "properties": { - "xai": { - "properties": { "enabled": { "const": true } } - } + "properties": { "apiKey": { "minLength": 1 } } } - }, - "then": { - "anyOf": [ - { - "properties": { - "existingSecret": { "minLength": 1 } - }, - "required": ["existingSecret"] - }, - { - "properties": { - "xai": { - "properties": { - "apiKey": { "minLength": 1 } - }, - "required": ["apiKey"] - } - } - } - ] } } } @@ -283,32 +135,6 @@ "tag": { "type": "string" } } }, - "imagePullSecrets": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { "type": "string" } - } - } - }, - "nameOverride": { "type": "string" }, - "fullnameOverride": { "type": "string" }, - "server": { - "type": "object", - "properties": { - "port": { "type": "integer" }, - "bodySizeLimit": { "type": "string" } - } - }, - "auth": { - "type": "object", - "properties": { - "masterKey": { "type": "string" }, - "existingSecret": { "type": "string" }, - "existingSecretKey": { "type": "string" } - } - }, "providers": { "type": "object", "properties": { @@ -376,6 +202,21 @@ "type": "object", "additionalProperties": true }, + "server": { + "type": "object", + "properties": { + "port": { "type": "integer" }, + "bodySizeLimit": { "type": "string" } + } + }, + "auth": { + "type": "object", + "properties": { + "masterKey": { "type": "string" }, + "existingSecret": { "type": "string" }, + "existingSecretKey": { "type": "string" } + } + }, "metrics": { "type": "object", "properties": { @@ -404,181 +245,45 @@ }, "ingress": { "type": "object", - "properties": { - "enabled": { "type": "boolean" }, - "className": { "type": "string" }, - "annotations": { "type": "object" }, - "hosts": { - "type": "array", - "items": { - "type": "object", - "properties": { - "host": { "type": "string" }, - "paths": { - "type": "array", - "items": { - "type": "object", - "properties": { - "path": { "type": "string" }, - "pathType": { - "type": "string", - "enum": ["Prefix", "Exact", "ImplementationSpecific"] - } - } - } - } - } - } - }, - "tls": { - "type": "array", - "items": { - "type": "object", - "properties": { - "secretName": { "type": "string" }, - "hosts": { - "type": "array", - "items": { "type": "string" } - } - } - } - } - } + "additionalProperties": true }, "gateway": { "type": "object", - "properties": { - "enabled": { "type": "boolean" }, - "parentRef": { - "type": "object", - "properties": { - "name": { "type": "string" }, - "namespace": { "type": "string" } - } - }, - "hostnames": { - "type": "array", - "items": { "type": "string" } - } - } + "additionalProperties": true }, "resources": { "type": "object", - "properties": { - "requests": { - "type": "object", - "properties": { - "cpu": { "type": ["string", "number"] }, - "memory": { "type": "string" } - } - }, - "limits": { - "type": "object", - "properties": { - "cpu": { "type": ["string", "number"] }, - "memory": { "type": "string" } - } - } - } + "additionalProperties": true }, "autoscaling": { "type": "object", - "properties": { - "enabled": { "type": "boolean" }, - "minReplicas": { "type": "integer", "minimum": 1 }, - "maxReplicas": { "type": "integer", "minimum": 1 }, - "targetCPUUtilizationPercentage": { "type": "integer" }, - "targetMemoryUtilizationPercentage": { "type": "integer" } - } + "additionalProperties": true }, "podDisruptionBudget": { "type": "object", - "properties": { - "enabled": { "type": "boolean" }, - "minAvailable": { "type": ["integer", "string"] }, - "maxUnavailable": { "type": ["integer", "string"] } - } + "additionalProperties": true }, "livenessProbe": { "type": "object", - "properties": { - "httpGet": { - "type": "object", - "properties": { - "path": { "type": "string" }, - "port": { "type": ["string", "integer"] } - } - }, - "initialDelaySeconds": { "type": "integer" }, - "periodSeconds": { "type": "integer" }, - "timeoutSeconds": { "type": "integer" }, - "failureThreshold": { "type": "integer" }, - "successThreshold": { "type": "integer" } - } + "additionalProperties": true }, "readinessProbe": { "type": "object", - "properties": { - "httpGet": { - "type": "object", - "properties": { - "path": { "type": "string" }, - "port": { "type": ["string", "integer"] } - } - }, - "initialDelaySeconds": { "type": "integer" }, - "periodSeconds": { "type": "integer" }, - "timeoutSeconds": { "type": "integer" }, - "failureThreshold": { "type": "integer" }, - "successThreshold": { "type": "integer" } - } + "additionalProperties": true }, "podSecurityContext": { "type": "object", - "properties": { - "runAsNonRoot": { "type": "boolean" }, - "runAsUser": { "type": "integer" }, - "runAsGroup": { "type": "integer" }, - "fsGroup": { "type": "integer" } - } + "additionalProperties": true }, "securityContext": { "type": "object", - "properties": { - "allowPrivilegeEscalation": { "type": "boolean" }, - "readOnlyRootFilesystem": { "type": "boolean" }, - "runAsNonRoot": { "type": "boolean" }, - "runAsUser": { "type": "integer" }, - "runAsGroup": { "type": "integer" }, - "capabilities": { - "type": "object", - "properties": { - "drop": { - "type": "array", - "items": { "type": "string" } - }, - "add": { - "type": "array", - "items": { "type": "string" } - } - } - } - } + "additionalProperties": true }, + "imagePullSecrets": { "type": "array" }, + "nameOverride": { "type": "string" }, + "fullnameOverride": { "type": "string" }, "nodeSelector": { "type": "object" }, - "tolerations": { - "type": "array", - "items": { - "type": "object", - "properties": { - "key": { "type": "string" }, - "operator": { "type": "string" }, - "value": { "type": "string" }, - "effect": { "type": "string" }, - "tolerationSeconds": { "type": "integer" } - } - } - }, + "tolerations": { "type": "array" }, "affinity": { "type": "object" }, "podAnnotations": { "type": "object" }, "podLabels": { "type": "object" } diff --git a/helm/values.yaml b/helm/values.yaml index fd6f7de8..194b2a4f 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -42,7 +42,7 @@ providers: existingSecret: "" openai: - # -- Enable OpenAI provider + # -- Enable OpenAI provider (auto-enabled if apiKey is set) enabled: false # -- OpenAI API key (ignored if providers.existingSecret is set) apiKey: "" @@ -50,7 +50,7 @@ providers: baseUrl: "" anthropic: - # -- Enable Anthropic provider + # -- Enable Anthropic provider (auto-enabled if apiKey is set) enabled: false # -- Anthropic API key (ignored if providers.existingSecret is set) apiKey: "" @@ -58,7 +58,7 @@ providers: baseUrl: "" gemini: - # -- Enable Google Gemini provider + # -- Enable Google Gemini provider (auto-enabled if apiKey is set) enabled: false # -- Gemini API key (ignored if providers.existingSecret is set) apiKey: "" @@ -66,7 +66,7 @@ providers: baseUrl: "" groq: - # -- Enable Groq provider + # -- Enable Groq provider (auto-enabled if apiKey is set) enabled: false # -- Groq API key (ignored if providers.existingSecret is set) apiKey: "" @@ -74,7 +74,7 @@ providers: baseUrl: "" xai: - # -- Enable xAI (Grok) provider + # -- Enable xAI (Grok) provider (auto-enabled if apiKey is set) enabled: false # -- xAI API key (ignored if providers.existingSecret is set) apiKey: "" From af1088fffb6249367cb9331a59b975ac1f88e884 Mon Sep 17 00:00:00 2001 From: "Jakub A. W" Date: Fri, 16 Jan 2026 16:49:06 +0100 Subject: [PATCH 2/2] fix: reverted a removed validation --- helm/templates/_helpers.tpl | 12 +---- helm/values.schema.json | 94 +++++++++++++++++++++++++++++++++++-- 2 files changed, 91 insertions(+), 15 deletions(-) diff --git a/helm/templates/_helpers.tpl b/helm/templates/_helpers.tpl index 3c999eb8..5af376aa 100644 --- a/helm/templates/_helpers.tpl +++ b/helm/templates/_helpers.tpl @@ -103,16 +103,6 @@ Create the image reference {{- printf "%s:%s" .Values.image.repository $tag }} {{- end }} -{{/* -Check if a provider is enabled (explicitly or by having an API key) -*/}} -{{- define "gomodel.providerEnabled" -}} -{{- $config := . -}} -{{- if or $config.enabled $config.apiKey -}} -true -{{- end -}} -{{- end -}} - {{/* Generate provider API key entries for the Secret stringData. */}} @@ -130,7 +120,7 @@ Generate provider environment variables for the Deployment. {{- define "gomodel.providerEnvVars" -}} {{- $secretName := include "gomodel.providerSecretName" . -}} {{- range $name, $config := .Values.providers }} - {{- if and (kindIs "map" $config) (hasKey $config "apiKey") (or $config.enabled $config.apiKey) }} + {{- if and (kindIs "map" $config) (hasKey $config "apiKey") $config.apiKey }} - name: {{ upper $name }}_API_KEY valueFrom: secretKeyRef: diff --git a/helm/values.schema.json b/helm/values.schema.json index 1b4f95d1..083a5b18 100644 --- a/helm/values.schema.json +++ b/helm/values.schema.json @@ -245,7 +245,46 @@ }, "ingress": { "type": "object", - "additionalProperties": true + "properties": { + "enabled": { "type": "boolean" }, + "className": { "type": "string" }, + "annotations": { "type": "object" }, + "hosts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "host": { "type": "string" }, + "paths": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { "type": "string" }, + "pathType": { + "type": "string", + "enum": ["Prefix", "Exact", "ImplementationSpecific"] + } + } + } + } + } + } + }, + "tls": { + "type": "array", + "items": { + "type": "object", + "properties": { + "secretName": { "type": "string" }, + "hosts": { + "type": "array", + "items": { "type": "string" } + } + } + } + } + } }, "gateway": { "type": "object", @@ -257,7 +296,27 @@ }, "autoscaling": { "type": "object", - "additionalProperties": true + "properties": { + "enabled": { "type": "boolean" }, + "minReplicas": { + "type": "integer", + "minimum": 1 + }, + "maxReplicas": { + "type": "integer", + "minimum": 1 + }, + "targetCPUUtilizationPercentage": { + "type": "integer", + "minimum": 1, + "maximum": 100 + }, + "targetMemoryUtilizationPercentage": { + "type": "integer", + "minimum": 1, + "maximum": 100 + } + } }, "podDisruptionBudget": { "type": "object", @@ -279,11 +338,38 @@ "type": "object", "additionalProperties": true }, - "imagePullSecrets": { "type": "array" }, + "imagePullSecrets": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { "type": "string", "minLength": 1 } + }, + "required": ["name"] + } + }, "nameOverride": { "type": "string" }, "fullnameOverride": { "type": "string" }, "nodeSelector": { "type": "object" }, - "tolerations": { "type": "array" }, + "tolerations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { "type": "string" }, + "operator": { + "type": "string", + "enum": ["Exists", "Equal"] + }, + "value": { "type": "string" }, + "effect": { + "type": "string", + "enum": ["NoSchedule", "PreferNoSchedule", "NoExecute", ""] + }, + "tolerationSeconds": { "type": "integer" } + } + } + }, "affinity": { "type": "object" }, "podAnnotations": { "type": "object" }, "podLabels": { "type": "object" }