go-bananas/deploy/helm/go-bananas/values.yaml
Daniel Noulet 951dd8d26b
Some checks failed
ci / go (push) Has been cancelled
ci / admin-ui (push) Has been cancelled
ci / runtime-sdk (push) Has been cancelled
ci / helm (push) Has been cancelled
ci / openapi (push) Has been cancelled
ci / container (push) Has been cancelled
ci / release-config (push) Has been cancelled
ci / shared styling (push) Has been cancelled
ci / security (push) Has been cancelled
ci / all green (push) Has been cancelled
initial commit
2026-10-07 15:11:35 +02:00

481 lines
18 KiB
YAML

# Go Bananas!! Helm values
#
# Every value has a safe default so that `helm install` works with no arguments at
# all. The exceptions are the authentication credentials, which are required when
# auth.mode is oidc and are supplied through an existing Secret.
#
# The defaults are tuned for a small internal deployment. Before running this in
# production, read the annotated sections: replica count, the ingress, the resource
# requests and the persistence choice all deserve a deliberate decision.
# ------------------------------------------------------------------ global ---
global:
# imageRegistry overrides the registry for every image the chart pulls. Used by a
# cluster with a mirrored registry or an air-gapped pull-through cache.
imageRegistry: ""
# imagePullSecrets are added to every ServiceAccount.
imagePullSecrets:
- name: nexus-registry
# extraLabels are added to every object the chart creates, for cost allocation
# and for a Prometheus `release` label if the cluster does not add one.
extraLabels: {}
# extraAnnotations are added to every object.
extraAnnotations: {}
# ------------------------------------------------------------------- image ---
image:
registry: nexus.urpi.be
repository: docker-hosted/go-bananas
# tag defaults to the chart's appVersion, so upgrading the chart upgrades the
# image. Set it to an immutable digest for a reproducible deployment.
tag: "0.0.1"
# digest pins the image by content. Takes precedence over tag when set, which is
# the strongest guarantee that a rollout is running exactly what was reviewed.
digest: ""
pullPolicy: IfNotPresent
# --------------------------------------------------------------- deployment ---
deployment:
# Two replicas by default: the service is stateless apart from the SQLite file,
# so horizontal scaling is only safe when persistence is a shared volume or a
# network filesystem. See the persistence section.
replicaCount: 2
# extraArgs are appended to the container command. The binary takes no positional
# arguments; this exists for future flags without a chart change.
extraArgs: []
# revisionHistoryLimit keeps the last N ReplicaSets so a rollback is possible
# without the Deployment controller's default of ten consuming etcd history.
revisionHistoryLimit: 5
# minReadySeconds makes a new pod count as ready only after it has been healthy
# for this long, which stops a rolling update from proceeding on the strength of
# a single lucky probe.
minReadySeconds: 10
# progressDeadlineSeconds aborts a rollout that cannot complete, rather than
# leaving a Deployment stuck in progress indefinitely.
progressDeadlineSeconds: 600
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
podAnnotations: {}
podLabels: {}
# hostAliases is mostly useful for a development cluster where the identity
# provider is not in public DNS.
hostAliases: []
# terminationGracePeriodSeconds must exceed the application's shutdown timeout
# (config.shutdownTimeoutMs) or in-flight requests are cut off mid-response.
# The chart refuses to render when it does not.
terminationGracePeriodSeconds: 45
# A preStop sleep gives the Service endpoints controller time to remove this pod
# from the endpoint list before the process starts refusing connections.
# Without it, a rolling update drops requests that were already routed.
lifecycle:
preStop:
enabled: true
sleepSeconds: 3
# nodeSelector, tolerations, affinity and topologySpreadConstraints are passed
# through to the pod spec unchanged.
nodeSelector: {}
tolerations: []
affinity: {}
topologySpreadConstraints: []
# priorityClassName for a cluster where the API tier matters.
priorityClassName: ""
# ---------------------------------------------------------------- resources ---
resources:
requests:
cpu: 50m
memory: 96Mi
limits:
# No CPU limit is set deliberately. A CPU limit throttles the process when the
# node is busy, which for a latency-sensitive asset server produces exactly the
# tail latency it is meant to prevent. The memory limit is the one that matters:
# it is what keeps a runaway cache from taking the node down.
memory: 384Mi
# ------------------------------------------------------------------- config ---
config:
# environment is the deployment name. Anything other than development or test
# turns off the dev authentication bypass and the TLS-skip switch.
environment: production
logLevel: info
# json in production so the log is structured for a collector.
logFormat: json
# publicBaseUrl is the externally reachable origin, with no trailing slash.
# It is used to build the OIDC redirect URI, the absolute URLs in the runtime
# manifest and the HSTS decision, so getting it wrong means a login loop.
#
# The default points at a local hostname so that `helm install` works with no
# arguments. Override it for any real deployment.
publicBaseUrl: "http://themes.go-bananas.local"
# basePath prefixes every route, for deployments behind a path-based ingress
# such as https://example.com/themes/. Leave empty for the default of /.
basePath: ""
# rateLimitPerMinute caps requests per client per minute on the runtime API.
# The default is high because the legitimate pattern is one request per page
# view; the limit exists to stop abuse, not to ration traffic.
rateLimitPerMinute: 3000
# trustedProxies are the CIDRs whose X-Forwarded-For header may be believed.
# Empty means forwarding headers are ignored entirely and the direct peer is
# recorded, which is the safe default. Set this to the ingress controller's
# address or the audit log will record the ingress pod instead of the client.
trustedProxies: []
# corsAllowedOrigins are browser origins allowed to call the admin API
# cross-origin. The runtime API is public by design and is not affected.
corsAllowedOrigins: []
# enableMetrics exposes /metrics in Prometheus format.
enableMetrics: true
# enableProfiler exposes net/http/pprof. Off by default: a profiler on a
# network-reachable endpoint is a denial-of-service surface, so it is opt-in and
# should be paired with a NetworkPolicy.
enableProfiler: false
# shutdownTimeoutMs bounds graceful shutdown. Must be lower than
# deployment.terminationGracePeriodSeconds.
shutdownTimeoutMs: 25000
# runtimeCdnBaseUrl is the absolute base the runtime manifest hands to
# consumers. Set it when the stylesheets are served from a CDN or a separate
# asset host, so that consumers fetch from there instead of from this service.
runtimeCdnBaseUrl: ""
# database tuning. The defaults suit an embedded SQLite file; see docs on
# persistence before changing them.
database:
# maxConns bounds the connection pool. SQLite serialises writers, so a small
# pool is faster than a large one, not slower.
maxConns: 8
# busyTimeoutMs is how long a blocked writer waits before failing.
busyTimeoutMs: 5000
# sessionTTLHours is how long an administrative session lasts. The session is a
# signed cookie with no server-side record, so an expiry cannot be revoked
# early — see the identity section for the trade-off this represents.
sessionTTLHours: 8
# -------------------------------------------------------------- persistence ---
#
# The database is a single SQLite file. Two deployment shapes are supported:
#
# type: emptyDir A per-pod temporary file. Every theme change is lost when a
# pod restarts. Correct only for a demo or for evaluating the
# service; the built-in themes are re-seeded automatically so
# the instance still comes up, but operator work is not kept.
#
# type: persistentVolumeClaim A ReadWriteOnce volume. Correct for a
# single-replica deployment, which is the shape this chart
# recommends in production.
#
# Note on replicas: RWO volumes cannot be attached to two pods at once. With
# replicaCount > 1 and a PVC, only one pod mounts the volume successfully and the
# others crash-loop. Running more than one replica therefore requires either
# replicaCount: 1, or a filesystem that permits multiple mounts.
persistence:
enabled: true
type: persistentVolumeClaim
accessMode: ReadWriteOnce
size: 2Gi
# storageClassName: "" uses the cluster default.
storageClassName: ""
# existingClaim mounts a volume the operator manages. Its lifecycle is then the
# operator's responsibility: this chart will not delete or resize it.
existingClaim: ""
mountPath: /srv/data
# A snapshot taken before an upgrade, when the upgrade involves a destructive
# change to the schema. Restoring is a manual kubectl apply.
snapshot:
enabled: false
# CronJob schedule in the cluster's timezone.
schedule: "0 3 * * *"
# How long to keep snapshots.
retention: 14
# ------------------------------------------------------------------ service ---
service:
type: ClusterIP
port: 80
# nodePort, only used when type is NodePort.
nodePort: ""
# ClusterIP: set to None for a headless service, which a StatefulSet needs.
clusterIP: ""
# The session cookie's Secure attribute and SameSite=None both require the
# browser to see the service over HTTPS. Set to true when TLS terminates at the
# ingress.
sessionSecure: true
sessionSameSite: Lax
annotations: {}
labels: {}
# ----------------------------------------------------------------- ingress ---
ingress:
enabled: false
className: nginx
annotations: {}
hosts:
- host: themes.example.com
paths:
- path: /
pathType: Prefix
# tls is empty for a plain-HTTP installation. For the usual production shape,
# point it at a cert-manager cluster issuer so the certificate is renewed
# without anyone remembering to.
tls: []
# - secretName: go-bananas-tls
# hosts:
# - themes.example.com
# ---------------------------------------------------------------- identity ---
#
# This is the part that most often goes wrong in a deployment, so it is worth being
# explicit about the three modes.
identity:
# disabled No authentication. Every request is granted administrator access.
# Refused by the application outside a development environment, so
# this cannot be deployed to production by accident.
# dev Every request is authenticated as a local administrator.
# Also refused outside development.
# oidc OpenID Connect against a real provider. The production choice.
mode: oidc
# sessionSecretKey is the key inside the Secret that holds the session signing
# secret. The secret must be at least 32 bytes.
#
# kubectl create secret generic go-bananas-auth \
# --from-literal=session-secret="$(openssl rand -base64 48)" \
# --from-literal=oidc-client-secret="..."
#
# The secret is never templated into a ConfigMap or written to a log. Rotating
# it invalidates every open session at once.
sessionSecretKey: session-secret
oidc:
# issuer is the OIDC issuer URL. Everything else — the authorization,
# token, userinfo and JWKS endpoints — is discovered from
# /.well-known/openid-configuration, so this is the only provider-specific
# value needed.
#
# The default is the shared development realm. The realm path is part of the
# issuer: this string must match the `iss` claim in the ID token exactly,
# including a trailing slash if the provider emits one. A mismatch here is the
# single most common cause of "invalid issuer" at the callback.
issuer: "https://auth-dev.urpi.be/realms/urpi"
# clientId is the client identifier registered with the provider.
clientId: "go-bananas"
# clientSecretKey names the key inside existingSecret (or generatedSecret)
# holding the client secret. Referencing it by key name rather than by value
# is what keeps the secret out of values.yaml.
clientSecretKey: oidc-client-secret
# existingSecret is a Secret you manage. Preferred for production: the chart
# will not create, rotate or delete it, and its contents never appear in a
# rendered manifest.
#
# kubectl create secret generic go-bananas-auth \
# --from-literal=session-secret="$(openssl rand -base64 48)" \
# --from-literal=oidc-client-secret="…"
existingSecret: ""
# existingSecretKeys overrides the key names inside existingSecret, for a
# Secret managed by an external secrets operator with its own naming.
existingSecretKeys: {}
# generatedSecret creates a Secret owned by this release. The session secret is
# preserved across upgrades by reading the live Secret back, so an upgrade does
# not sign every user out.
#
# The OIDC client secret cannot be generated — it has to come from the provider.
# A generated Secret therefore contains an empty client secret, which means the
# deployment will fail to authenticate until one is supplied. For anything real,
# use existingSecret.
generateSecret: true
# scopes requested in addition to openid. The email scope is what makes
# `email` appear in the session; groups is what the role mapping reads.
scopes:
- profile
- email
- groups
# Group-to-role mapping, read from the `groups` claim.
#
# The most privileged matching group wins; a user in no configured group gets
# viewer. The default is deliberately least privilege, so a mapping mistake
# fails closed rather than granting access nobody intended.
adminGroups: []
editorGroups: []
viewerGroups: []
# With all three empty, every authenticated user is a viewer. Populate these
# from the provider's actual group names before rolling this out to real users;
# the group names are realm-specific and are not guessed here.
# redirectURL overrides the computed callback. Leave empty unless the
# provider has a different redirect URI registered, which happens behind a
# path-based ingress or when several environments share a provider client.
redirectURL: ""
postLogoutRedirectURL: ""
# End-session endpoint, for providers that do not follow the RP-initiated
# logout convention.
logoutURL: ""
# skipTLSVerify disables certificate verification against the provider. It
# exists so that a development provider presenting a self-signed certificate
# can be used, and the application refuses to start with it enabled outside
# development.
skipTLSVerify: false
# timeout and insecureSkipVerify exist for the same development reason.
timeoutSeconds: 15
# ------------------------------------------------------------------- probes ---
probes:
liveness:
enabled: true
path: /healthz
initialDelaySeconds: 3
periodSeconds: 15
timeoutSeconds: 3
failureThreshold: 3
readiness:
enabled: true
path: /readyz
initialDelaySeconds: 2
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
startup:
enabled: true
path: /healthz
periodSeconds: 3
failureThreshold: 30
# --------------------------------------------------------------- autoscaling ---
autoscaling:
enabled: false
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 70
# targetMemoryUtilizationPercentage is left unset by default: memory here is
# dominated by the render cache, which grows with the number of themes rather
# than with load, so scaling on it would respond to a content change rather than
# to traffic.
targetMemoryUtilizationPercentage: 0
behavior: {}
# ------------------------------------------------------- disruption budgets ---
#
# A PDB only helps if it does not also block a node drain. minAvailable: 1 with two
# replicas permits exactly one disruption, which is the useful setting.
podDisruptionBudget:
enabled: true
minAvailable: 1
# maxUnavailable: 1
# unhealthyPodEvictionPolicy: AlwaysAllow
# --------------------------------------------------------- service account ---
serviceAccount:
create: true
name: ""
annotations: {}
automountServiceAccountToken: false
rbac:
# create is off by default: the service talks to nothing in the cluster, so the
# default ServiceAccount needs no rules at all.
create: false
rules: []
# ------------------------------------------------------------ network policy ---
#
# A default-deny ingress with an explicit allowance for the ingress controller and
# for Prometheus. Without this, any pod in the cluster can reach the admin API.
networkPolicy:
enabled: false
# ingressNamespaces restricts which namespaces may reach the service. Empty means
# the whole cluster.
allowedNamespaces: []
# metricsPaths is the additional path allowed from the metrics sources.
metricsPaths: ["/metrics"]
# ------------------------------------------------------------------- extras ---
podSecurityContext:
runAsNonRoot: true
runAsUser: 65532
runAsGroup: 65532
fsGroup: 65532
seccompProfile:
type: RuntimeDefault
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
# extraEnv is for one-off variables that do not warrant a chart value. Anything
# added here is the operator's responsibility to keep consistent across replicas.
extraEnv: []
# extraVolumes and extraVolumeMounts are for a sidecar or a config file the chart
# does not model.
extraVolumes: []
extraVolumeMounts: []
# metricsServiceMonitor creates a Prometheus Operator ServiceMonitor. Requires the
# Prometheus Operator CRDs to be installed.
metrics:
serviceMonitor:
enabled: false
interval: 30s
scrapeTimeout: 10s
labels: {}
honorLabels: true
# ------------------------------------------------------------------- extras ---
#
# `extraManifests` is an escape hatch for a resource this chart does not model. It
# is templated, so a value can reference another template's output.
extraManifests: []
# - apiVersion: v1
# kind: ConfigMap
# metadata:
# name: {{ include "go-bananas.fullname" . }}-extra
# data:
# example: "value"