From 88e7177193c96845acd35ad3d36c9fb1f0f469ab Mon Sep 17 00:00:00 2001 From: "charly.chiu" Date: Thu, 20 Aug 2026 11:23:23 +0800 Subject: [PATCH 1/2] feat: add Gateway API HTTPRoute support Adds sourcebot.httpRoute values and a templates/httproute.yaml rendering a gateway.networking.k8s.io/v1 HTTPRoute. When rules is empty a default rule forwarding all traffic to the Sourcebot service is generated. AUTH_URL is now also derived from sourcebot.httpRoute.hostnames when ingress is disabled. Previously it was only set from ingress.hosts, so disabling ingress to use a Gateway made Sourcebot fall back to http://localhost:3000, breaking login callbacks and search result links. Ingress takes precedence over httpRoute for AUTH_URL, so existing installations are unaffected. --- CHANGELOG.md | 3 ++ charts/sourcebot/README.md | 5 ++++ charts/sourcebot/templates/deployment.yaml | 3 ++ charts/sourcebot/templates/httproute.yaml | 35 ++++++++++++++++++++++ charts/sourcebot/values.schema.json | 19 ++++++++++++ charts/sourcebot/values.yaml | 25 ++++++++++++++++ 6 files changed, 90 insertions(+) create mode 100644 charts/sourcebot/templates/httproute.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c8ae5d..cacb14c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- `sourcebot.httpRoute` values for exposing Sourcebot through a Gateway API `HTTPRoute`. `AUTH_URL` is now also derived from `sourcebot.httpRoute.hostnames` when ingress is disabled. + ## [0.1.102] - 2026-08-19 ### Changed diff --git a/charts/sourcebot/README.md b/charts/sourcebot/README.md index ed1068c..a0e1131 100644 --- a/charts/sourcebot/README.md +++ b/charts/sourcebot/README.md @@ -76,6 +76,11 @@ Sourcebot is a self-hosted tool that helps you understand your codebase. | sourcebot.extraVolumeMounts | list | `[]` | Define volume mounts for the container See: https://kubernetes.io/docs/concepts/storage/volumes/ | | sourcebot.extraVolumes | list | `[]` | Define additional volumes See: https://kubernetes.io/docs/concepts/storage/volumes/ | | sourcebot.hostAliases | list | `[]` | Set host aliases to inject entries into the pod's /etc/hosts file See: https://kubernetes.io/docs/tasks/network/customize-hosts-file-for-pods/ | +| sourcebot.httpRoute.annotations | object | `{}` | HTTPRoute annotations | +| sourcebot.httpRoute.enabled | bool | `false` | Enable or disable the HTTPRoute. Requires the Gateway API CRDs (`gateway.networking.k8s.io/v1`) to be installed in the cluster. | +| sourcebot.httpRoute.hostnames | list | `[]` | Hostnames to match. The first hostname is used to derive `AUTH_URL` when ingress is disabled. | +| sourcebot.httpRoute.parentRefs | list | `[]` | Gateways to attach this route to. Required when the HTTPRoute is enabled. | +| sourcebot.httpRoute.rules | list | `[]` | Routing rules. When empty, a single rule forwarding all traffic to the Sourcebot service is generated. | | sourcebot.image.digest | string | `""` | Container image digest (used instead of tag if set) | | sourcebot.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy | | sourcebot.image.pullSecrets | list | `[]` | Configure image pull secrets for private registries | diff --git a/charts/sourcebot/templates/deployment.yaml b/charts/sourcebot/templates/deployment.yaml index 9fd2fbf..0f4d4d8 100644 --- a/charts/sourcebot/templates/deployment.yaml +++ b/charts/sourcebot/templates/deployment.yaml @@ -63,6 +63,9 @@ spec: {{- if and $.Values.sourcebot.ingress.enabled (gt (len $.Values.sourcebot.ingress.hosts) 0) }} - name: AUTH_URL value: {{ (index $.Values.sourcebot.ingress.hosts 0).host | printf "https://%s" }} + {{- else if and $.Values.sourcebot.httpRoute.enabled (gt (len $.Values.sourcebot.httpRoute.hostnames) 0) }} + - name: AUTH_URL + value: {{ index $.Values.sourcebot.httpRoute.hostnames 0 | printf "https://%s" }} {{- end }} - name: CONFIG_PATH value: /etc/sourcebot/config.json diff --git a/charts/sourcebot/templates/httproute.yaml b/charts/sourcebot/templates/httproute.yaml new file mode 100644 index 0000000..9116daa --- /dev/null +++ b/charts/sourcebot/templates/httproute.yaml @@ -0,0 +1,35 @@ +{{- if $.Values.sourcebot.httpRoute.enabled -}} +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: {{ include "sourcebot.fullname" $ }} + namespace: {{ $.Release.Namespace }} + labels: + {{- include "sourcebot.labels" $ | nindent 4 }} + {{- with $.Values.sourcebot.httpRoute.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- with $.Values.sourcebot.httpRoute.parentRefs }} + parentRefs: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with $.Values.sourcebot.httpRoute.hostnames }} + hostnames: + {{- toYaml . | nindent 4 }} + {{- end }} + rules: + {{- with $.Values.sourcebot.httpRoute.rules }} + {{- toYaml . | nindent 4 }} + {{- else }} + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: {{ include "sourcebot.fullname" $ }} + port: {{ $.Values.sourcebot.service.port }} + {{- end }} +{{- end }} diff --git a/charts/sourcebot/values.schema.json b/charts/sourcebot/values.schema.json index 1e823bc..4c7463b 100644 --- a/charts/sourcebot/values.schema.json +++ b/charts/sourcebot/values.schema.json @@ -158,6 +158,25 @@ "ingress": { "type": "object" }, + "httpRoute": { + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "annotations": { "type": "object" }, + "parentRefs": { + "type": "array", + "items": { "type": "object" } + }, + "hostnames": { + "type": "array", + "items": { "type": "string" } + }, + "rules": { + "type": "array", + "items": { "type": "object" } + } + } + }, "initContainers": { "type": "array" }, diff --git a/charts/sourcebot/values.yaml b/charts/sourcebot/values.yaml index feac102..90e4e55 100644 --- a/charts/sourcebot/values.yaml +++ b/charts/sourcebot/values.yaml @@ -180,6 +180,31 @@ sourcebot: # - chart-example.local # secretName: chart-example-tls + # Gateway API HTTPRoute configuration. TLS is terminated by the Gateway + # listener, so there is no TLS section here. + httpRoute: + # -- Enable or disable the HTTPRoute. Requires the Gateway API CRDs (`gateway.networking.k8s.io/v1`) to be installed in the cluster. + enabled: false + # -- HTTPRoute annotations + annotations: {} + # -- Gateways to attach this route to. Required when the HTTPRoute is enabled. + parentRefs: [] + # - name: my-gateway + # namespace: gateway-system + # sectionName: https + # -- Hostnames to match. The first hostname is used to derive `AUTH_URL` when ingress is disabled. + hostnames: [] + # - chart-example.local + # -- Routing rules. When empty, a single rule forwarding all traffic to the Sourcebot service is generated. + rules: [] + # - matches: + # - path: + # type: PathPrefix + # value: / + # backendRefs: + # - name: sourcebot + # port: 3000 + # -- Configure init containers to run before the main container initContainers: [] # - name: sleeper From 5ad88eb2b1e265e7758b70540f93dbaa836a1342 Mon Sep 17 00:00:00 2001 From: "charly.chiu" Date: Thu, 20 Aug 2026 11:45:43 +0800 Subject: [PATCH 2/2] test: cover HTTPRoute rendering and AUTH_URL derivation Adds a helm-unittest suite for the HTTPRoute template and the AUTH_URL logic, matching the existing tests/basic_test.yaml conventions. Also fixes two gaps found while writing the tests: enabling httpRoute without parentRefs now fails with an explanatory message instead of silently rendering a route attached to no Gateway, and AUTH_URL derivation skips wildcard hostnames, which are common on Gateway listeners and produced an unusable URL. AUTH_URL derivation moved into a sourcebot.authUrl helper. The rendered output for existing ingress configurations is unchanged. --- CHANGELOG.md | 3 + charts/sourcebot/README.md | 2 +- charts/sourcebot/templates/_helpers.tpl | 22 +++ charts/sourcebot/templates/deployment.yaml | 7 +- charts/sourcebot/templates/httproute.yaml | 3 + charts/sourcebot/tests/httproute_test.yaml | 214 +++++++++++++++++++++ charts/sourcebot/values.yaml | 2 +- 7 files changed, 246 insertions(+), 7 deletions(-) create mode 100644 charts/sourcebot/tests/httproute_test.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index cacb14c..a628aa0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - `sourcebot.httpRoute` values for exposing Sourcebot through a Gateway API `HTTPRoute`. `AUTH_URL` is now also derived from `sourcebot.httpRoute.hostnames` when ingress is disabled. +### Fixed +- `AUTH_URL` derivation no longer picks a wildcard hostname, which produced an unusable URL. + ## [0.1.102] - 2026-08-19 ### Changed diff --git a/charts/sourcebot/README.md b/charts/sourcebot/README.md index a0e1131..f391f7a 100644 --- a/charts/sourcebot/README.md +++ b/charts/sourcebot/README.md @@ -78,7 +78,7 @@ Sourcebot is a self-hosted tool that helps you understand your codebase. | sourcebot.hostAliases | list | `[]` | Set host aliases to inject entries into the pod's /etc/hosts file See: https://kubernetes.io/docs/tasks/network/customize-hosts-file-for-pods/ | | sourcebot.httpRoute.annotations | object | `{}` | HTTPRoute annotations | | sourcebot.httpRoute.enabled | bool | `false` | Enable or disable the HTTPRoute. Requires the Gateway API CRDs (`gateway.networking.k8s.io/v1`) to be installed in the cluster. | -| sourcebot.httpRoute.hostnames | list | `[]` | Hostnames to match. The first hostname is used to derive `AUTH_URL` when ingress is disabled. | +| sourcebot.httpRoute.hostnames | list | `[]` | Hostnames to match. When ingress is disabled, the first non-wildcard hostname is used to derive `AUTH_URL`. If every hostname is a wildcard, set `AUTH_URL` explicitly via `sourcebot.additionalEnv`. | | sourcebot.httpRoute.parentRefs | list | `[]` | Gateways to attach this route to. Required when the HTTPRoute is enabled. | | sourcebot.httpRoute.rules | list | `[]` | Routing rules. When empty, a single rule forwarding all traffic to the Sourcebot service is generated. | | sourcebot.image.digest | string | `""` | Container image digest (used instead of tag if set) | diff --git a/charts/sourcebot/templates/_helpers.tpl b/charts/sourcebot/templates/_helpers.tpl index 568e1f6..7f705bc 100644 --- a/charts/sourcebot/templates/_helpers.tpl +++ b/charts/sourcebot/templates/_helpers.tpl @@ -112,6 +112,28 @@ Return Redis hostname {{- end }} {{- end }} +{{/* +Return the public URL Sourcebot is served on, used for AUTH_URL. +Derived from the first ingress host, or from the first non-wildcard HTTPRoute +hostname when ingress is disabled. Wildcard hostnames are skipped because they +are not a usable URL. Returns an empty string when no host can be determined. +*/}} +{{- define "sourcebot.authUrl" -}} +{{- if and .Values.sourcebot.ingress.enabled (gt (len .Values.sourcebot.ingress.hosts) 0) -}} +{{- printf "https://%s" (index .Values.sourcebot.ingress.hosts 0).host -}} +{{- else if .Values.sourcebot.httpRoute.enabled -}} +{{- $hostnames := list -}} +{{- range .Values.sourcebot.httpRoute.hostnames -}} +{{- if not (hasPrefix "*" .) -}} +{{- $hostnames = append $hostnames . -}} +{{- end -}} +{{- end -}} +{{- with $hostnames -}} +{{- printf "https://%s" (first .) -}} +{{- end -}} +{{- end -}} +{{- end }} + {{/* Helper to get value or secret reference Returns either a direct value or a valueFrom secretKeyRef diff --git a/charts/sourcebot/templates/deployment.yaml b/charts/sourcebot/templates/deployment.yaml index 0f4d4d8..abe2584 100644 --- a/charts/sourcebot/templates/deployment.yaml +++ b/charts/sourcebot/templates/deployment.yaml @@ -60,12 +60,9 @@ spec: {{- toYaml . | nindent 12 }} {{- end }} env: - {{- if and $.Values.sourcebot.ingress.enabled (gt (len $.Values.sourcebot.ingress.hosts) 0) }} + {{- with (include "sourcebot.authUrl" $) }} - name: AUTH_URL - value: {{ (index $.Values.sourcebot.ingress.hosts 0).host | printf "https://%s" }} - {{- else if and $.Values.sourcebot.httpRoute.enabled (gt (len $.Values.sourcebot.httpRoute.hostnames) 0) }} - - name: AUTH_URL - value: {{ index $.Values.sourcebot.httpRoute.hostnames 0 | printf "https://%s" }} + value: {{ . }} {{- end }} - name: CONFIG_PATH value: /etc/sourcebot/config.json diff --git a/charts/sourcebot/templates/httproute.yaml b/charts/sourcebot/templates/httproute.yaml index 9116daa..0ffe30a 100644 --- a/charts/sourcebot/templates/httproute.yaml +++ b/charts/sourcebot/templates/httproute.yaml @@ -1,4 +1,7 @@ {{- if $.Values.sourcebot.httpRoute.enabled -}} +{{- if not $.Values.sourcebot.httpRoute.parentRefs -}} +{{- fail "sourcebot.httpRoute.parentRefs is required when sourcebot.httpRoute.enabled is true. An HTTPRoute without parentRefs is not attached to any Gateway and silently receives no traffic." -}} +{{- end -}} --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute diff --git a/charts/sourcebot/tests/httproute_test.yaml b/charts/sourcebot/tests/httproute_test.yaml new file mode 100644 index 0000000..64d5309 --- /dev/null +++ b/charts/sourcebot/tests/httproute_test.yaml @@ -0,0 +1,214 @@ +suite: test Gateway API HTTPRoute +templates: + - httproute.yaml + - deployment.yaml + - config.yaml +tests: + - it: should not render an HTTPRoute by default + values: + - ../values.lint.yaml + asserts: + - hasDocuments: + count: 0 + template: httproute.yaml + + - it: should render an HTTPRoute when enabled + values: + - ../values.lint.yaml + set: + sourcebot.httpRoute.enabled: true + sourcebot.httpRoute.parentRefs: + - name: my-gateway + namespace: gateway-system + sectionName: https + sourcebot.httpRoute.hostnames: + - chart-example.local + asserts: + - hasDocuments: + count: 1 + template: httproute.yaml + - isKind: + of: HTTPRoute + template: httproute.yaml + - isAPIVersion: + of: gateway.networking.k8s.io/v1 + template: httproute.yaml + - equal: + path: spec.parentRefs[0] + value: + name: my-gateway + namespace: gateway-system + sectionName: https + template: httproute.yaml + - equal: + path: spec.hostnames[0] + value: chart-example.local + template: httproute.yaml + + - it: should generate a default rule pointing at the Sourcebot service + values: + - ../values.lint.yaml + set: + sourcebot.httpRoute.enabled: true + sourcebot.httpRoute.parentRefs: + - name: my-gateway + asserts: + - lengthEqual: + path: spec.rules + count: 1 + template: httproute.yaml + - equal: + path: spec.rules[0].matches[0].path + value: + type: PathPrefix + value: / + template: httproute.yaml + - equal: + path: spec.rules[0].backendRefs[0] + value: + name: RELEASE-NAME-sourcebot + port: 3000 + template: httproute.yaml + + - it: should use custom rules when provided + values: + - ../values.lint.yaml + set: + sourcebot.httpRoute.enabled: true + sourcebot.httpRoute.parentRefs: + - name: my-gateway + sourcebot.httpRoute.rules: + - matches: + - path: + type: PathPrefix + value: /api + backendRefs: + - name: custom-backend + port: 8080 + asserts: + - lengthEqual: + path: spec.rules + count: 1 + template: httproute.yaml + - equal: + path: spec.rules[0].backendRefs[0].name + value: custom-backend + template: httproute.yaml + + - it: should set HTTPRoute annotations + values: + - ../values.lint.yaml + set: + sourcebot.httpRoute.enabled: true + sourcebot.httpRoute.parentRefs: + - name: my-gateway + sourcebot.httpRoute.annotations: + custom.io/annotation: my-value + asserts: + - equal: + path: metadata.annotations["custom.io/annotation"] + value: my-value + template: httproute.yaml + + - it: should fail when enabled without parentRefs + values: + - ../values.lint.yaml + set: + sourcebot.httpRoute.enabled: true + asserts: + - failedTemplate: + errorMessage: sourcebot.httpRoute.parentRefs is required when sourcebot.httpRoute.enabled is true. An HTTPRoute without parentRefs is not attached to any Gateway and silently receives no traffic. + template: httproute.yaml + + - it: should derive AUTH_URL from the HTTPRoute hostname when ingress is disabled + values: + - ../values.lint.yaml + set: + sourcebot.httpRoute.enabled: true + sourcebot.httpRoute.parentRefs: + - name: my-gateway + sourcebot.httpRoute.hostnames: + - chart-example.local + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: AUTH_URL + value: https://chart-example.local + count: 1 + template: deployment.yaml + + - it: should skip wildcard hostnames when deriving AUTH_URL + values: + - ../values.lint.yaml + set: + sourcebot.httpRoute.enabled: true + sourcebot.httpRoute.parentRefs: + - name: my-gateway + sourcebot.httpRoute.hostnames: + - "*.chart-example.local" + - sourcebot.chart-example.local + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: AUTH_URL + value: https://sourcebot.chart-example.local + count: 1 + template: deployment.yaml + + - it: should not set AUTH_URL when every hostname is a wildcard + values: + - ../values.lint.yaml + set: + sourcebot.httpRoute.enabled: true + sourcebot.httpRoute.parentRefs: + - name: my-gateway + sourcebot.httpRoute.hostnames: + - "*.chart-example.local" + asserts: + - notContains: + path: spec.template.spec.containers[0].env + content: + name: AUTH_URL + template: deployment.yaml + + - it: should prefer the ingress host for AUTH_URL when both are enabled + values: + - ../values.lint.yaml + set: + sourcebot.ingress.enabled: true + sourcebot.ingress.hosts: + - host: ingress.chart-example.local + paths: + - path: / + pathType: Prefix + sourcebot.httpRoute.enabled: true + sourcebot.httpRoute.parentRefs: + - name: my-gateway + sourcebot.httpRoute.hostnames: + - route.chart-example.local + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: AUTH_URL + value: https://ingress.chart-example.local + count: 1 + template: deployment.yaml + - notContains: + path: spec.template.spec.containers[0].env + content: + name: AUTH_URL + value: https://route.chart-example.local + template: deployment.yaml + + - it: should not set AUTH_URL when neither ingress nor httpRoute is enabled + values: + - ../values.lint.yaml + asserts: + - notContains: + path: spec.template.spec.containers[0].env + content: + name: AUTH_URL + template: deployment.yaml diff --git a/charts/sourcebot/values.yaml b/charts/sourcebot/values.yaml index 90e4e55..eedc560 100644 --- a/charts/sourcebot/values.yaml +++ b/charts/sourcebot/values.yaml @@ -192,7 +192,7 @@ sourcebot: # - name: my-gateway # namespace: gateway-system # sectionName: https - # -- Hostnames to match. The first hostname is used to derive `AUTH_URL` when ingress is disabled. + # -- Hostnames to match. When ingress is disabled, the first non-wildcard hostname is used to derive `AUTH_URL`. If every hostname is a wildcard, set `AUTH_URL` explicitly via `sourcebot.additionalEnv`. hostnames: [] # - chart-example.local # -- Routing rules. When empty, a single rule forwarding all traffic to the Sourcebot service is generated.