Files
helm-charts/template-chart/values.yaml
T
jklein 50f683776e
Validate Helm Charts / Detect changed charts (pull_request) Successful in 4s
Validate Helm Charts / Resource quantities (pull_request) Successful in 14s
Validate Helm Charts / HTTPRoute match (pull_request) Successful in 13s
Validate Helm Charts / Build cyberchef (pull_request) Successful in 6s
Validate Helm Charts / Trivy config cyberchef (pull_request) Successful in 18s
Validate Helm Charts / Build template-chart (pull_request) Successful in 7s
Validate Helm Charts / Trivy config template-chart (pull_request) Successful in 17s
Install-test / Install-test cyberchef (pull_request) Successful in 1m0s
Validate Helm Charts / Install-test (pull_request) Successful in 1m0s
Validate Helm Charts / Selector match (pull_request) Successful in 12s
feat(cyberchef): Gateway-API as an alternative to the Ingress (pilot)
The Gateway-API pilot of Gitea #114: one chart, the convention, the CI that
holds it. The remaining 20 charts follow in their own PRs.

The form, additive and off by default:

    httpRoute:
      enabled: false     # counterpart to ingress.enabled
      parentRefs: []     # the Gateway - a platform object
      hostnames: []      # empty = [ingress.domain]

Only HTTPRoute with parentRefs is rendered: no Gateway, no gatewayClassName,
no Certificate, and no implementation-specific policy object. That subset is
identical on nginx-gateway-fabric, Cilium, Envoy Gateway and Istio, which is
what lets the implementation be chosen later without touching the charts
again. Class and certificate move to the platform.

Four guards, all against silent failures:

  - mutually exclusive with ingress.enabled, with a render abort naming BOTH
    value paths. A migration runs over a second hostname, not in parallel.
  - parentRefs mandatory in Gateway mode, render abort on the empty list: a
    route without a parent is created, is schema-valid, and routes nothing.
  - hostnames empty means [ingress.domain], so the hostname has one source.
  - the mode is a VALUE, not auto-detection: .Capabilities.APIVersions is
    unreliable at template time and a missing CRD fails at install time.

ingress.clusterIssuer was already only read inside the Ingress template, so
the "conditionally required" half of #114 needed no change in this chart -
checked rather than assumed, and it has to be re-checked per chart in the
sweep.

CI, both halves, because neither covers the other:

  - the install-test installs cyberchef in GATEWAY mode against the REAL
    Gateway-API CRDs (standard channel v1.6.3, pinned and Renovate-tracked,
    applied --server-side). A schema-less stub would accept a malformed route
    and prove only that something was created. The Ingress path of this one
    chart is no longer installed; it is a built-in type that never routed in
    k3d anyway, and helm lint renders this chart's defaults - Ingress mode -
    on every PR.
  - ci/run-httproute-match-test.sh, over every chart on every PR: every
    backendRefs entry must name a Service, on a port, that the SAME render
    creates, and parentRefs must not be empty. The CRDs accept both of those
    mistakes, so the schema cannot catch them, and the only report would be a
    route status no controller writes in CI.

What stays unprovable, and is written down instead of implied: that traffic
flows. No Gateway controller runs in k3d.

Minor: additive with unchanged defaults, proven by an empty helm template
diff against origin/main for the default values.

Part of #114
2026-10-12 15:16:26 +00:00

177 lines
7.3 KiB
YAML

# Scale the whole chart down without uninstalling it: scaleDown: true renders
# every Deployment/StatefulSet with replicas: 0 (and suspends CronJobs) and
# takes precedence over any per-workload replicas value. PVCs, Secrets and
# Services stay in place; scaleDown: false brings the workloads back up.
scaleDown: false
secrets:
xxxSecretRef: null
xxx:
# Replica count for this workload; 0 is allowed and scales it down.
# The chart-global scaleDown flag takes precedence.
replicas: 1
resources:
# Limits are optional: memory and ephemeral-storage limits default to
# limitFactor x the request, an explicitly set limit always wins, and a
# CPU limit is only set when configured explicitly under "limits".
limitFactor: 3
requests:
cpu: 0.1
memory: 512Mi
ephemeral-storage: 10Mi
livenessProbe:
httpGet:
path: /
port: http
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /
port: http
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
# The startup probe gates liveness/readiness, so those need no initialDelaySeconds.
# Startup budget = periodSeconds * failureThreshold = 150s.
startupProbe:
httpGet:
path: /
port: http
periodSeconds: 5
timeoutSeconds: 5
failureThreshold: 30
securityContext:
pod:
runAsNonRoot: true
runAsUser: 20000
runAsGroup: 1000
fsGroup: 1000
fsGroupChangePolicy: Always
# RuntimeDefault applies the container runtime's default seccomp filter,
# which blocks the syscalls a normal workload never makes. It sits in the
# pod context on purpose: from here it covers every container in the pod
# including init containers, while a container-level seccompProfile would
# have to be repeated for each one (and silently missed on the next one
# added). A container that genuinely needs a different filter can still
# override it under securityContext.container, which wins over this value.
seccompProfile:
type: RuntimeDefault
container:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
capabilities:
drop:
- ALL
add: []
ports:
# An unprivileged port on purpose (AVD-KSV-0117 flags containerPort < 1024).
#
# Note what the reason is NOT: on current Kubernetes a non-root container
# can bind port 80 just fine. The kubelet sets
# net.ipv4.ip_unprivileged_port_start=0 in the pod network namespace, so
# the classic "ports below 1024 need NET_BIND_SERVICE" rule does not apply
# inside a pod. Measured in k3d (k3s v1.35.5): the sysctl reads 0, and a
# container running as runAsUser 20000 with capabilities.drop: [ALL] binds
# port 80 successfully. Anyone who "fixes" this back to 80 after testing it
# once will find that it works - which is exactly why the reason is written
# down here.
#
# The reason to stay above 1024 is that this is a runtime default, not a
# guarantee: a cluster that restores ip_unprivileged_port_start=1024 (node
# sysctl or a hardened distribution) breaks every workload that copied the
# 80, and the failure then appears in the copied chart, not here. Every
# real chart in this repo listens above 1024 (3000, 8080, 8123, 9998, ...);
# the one exception is samba, which needs 139/445 and adds NET_BIND_SERVICE
# with a documented justification.
#
# containerPort is declarative - it does not force the application onto the
# port. Point the application at the same port when copying this template.
- containerPort: 8080
protocol: TCP
name: http
env:
# Reference for guideline 1: ingress.domain is read HERE, outside the
# Ingress template, so it needs its own required(). Without it, switching
# the Ingress off via ingress.enabled would render an empty value and the
# workload would start with a broken address - silently. Copy this shape
# whenever a chart uses the domain for anything but the Ingress host.
- name: NORMAL_ENV
value: '{{ required "A Domain/Host must be provided (ingress.domain)" .Values.ingress.domain }}'
- name: ENV_FROM_SECRET
valueFrom:
secretKeyRef:
name: "{{ .Release.Name }}-{{ .Chart.Name }}-secret"
key: "secretKey"
- name: ENV_FROM_CONFIGMAP
valueFrom:
configMapKeyRef:
name: "{{ .Release.Name }}-{{ .Chart.Name }}-config-map"
key: "configMapKey"
storage:
amount: 10Gi
storageClass: null
ingress:
# Gates the whole Ingress template. Note that ingress.domain is ALSO read by
# the env block above AND by the HTTPRoute below, so it stays required even
# with the Ingress switched off; only clusterIssuer becomes unnecessary.
# Check this per chart instead of assuming either way (guideline 1).
#
# Mutually exclusive with httpRoute.enabled below: with both true the render
# aborts, naming both values.
enabled: true
clusterIssuer: null
domain: null
className: nginx
# Extra annotations for the Ingress; merged with the cert-manager
# cluster-issuer annotation the chart always sets.
#
# NO COUNTERPART IN GATEWAY MODE. The Gateway API has no annotation channel
# for controller behaviour, so everything set here is gone when
# httpRoute.enabled is used instead. rewrite-target has an equivalent (the
# URLRewrite filter in an HTTPRoute rule); an upload limit
# (proxy-body-size) has none at all, and a chart that depends on one must say
# so in its NOTES.txt for that mode.
annotations: {}
# Gateway-API alternative to the Ingress above (Gitea #114). Off by default, so
# adding it to an existing chart is additive; exactly one of ingress.enabled
# and httpRoute.enabled may be true.
#
# The chart renders ONLY an HTTPRoute with parentRefs - no Gateway, no
# gatewayClassName, no Certificate and no implementation-specific policy
# object. Class and certificate belong to the Gateway and therefore to the
# platform, which is what keeps a chart independent of whether the cluster runs
# nginx-gateway-fabric, Cilium, Envoy Gateway or Istio.
#
# The Gateway-API CRDs must be installed in the cluster: the chart does not and
# cannot detect them (.Capabilities.APIVersions is unreliable at template
# time), so with httpRoute.enabled: true and no CRDs the INSTALL fails.
httpRoute:
enabled: false
# The Gateway(s) this route attaches to - a platform object, usually in
# another namespace, which must allow this namespace in its listener's
# allowedRoutes. One entry per parent; the fields are the Gateway API's
# ParentReference (name, and optionally namespace, sectionName, port, group,
# kind), passed through unchanged:
#
# parentRefs:
# - name: public
# namespace: gateway
# sectionName: https
#
# REQUIRED while httpRoute.enabled is true - the render aborts on an empty
# list, because a route without a parent is created, is valid and routes
# nothing, with no event to say so.
parentRefs: []
# Empty means [ingress.domain], so the hostname has one source of truth. Set
# explicitly only to publish under a different or an additional name - which
# is how a migration from the Ingress runs: the HTTPRoute takes a second
# hostname while the Ingress keeps ingress.domain.
hostnames: []