diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c8ae5d..a628aa0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ 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. + +### 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 ed1068c..f391f7a 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. 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) | | 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/_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 9fd2fbf..abe2584 100644 --- a/charts/sourcebot/templates/deployment.yaml +++ b/charts/sourcebot/templates/deployment.yaml @@ -60,9 +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" }} + value: {{ . }} {{- 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..0ffe30a --- /dev/null +++ b/charts/sourcebot/templates/httproute.yaml @@ -0,0 +1,38 @@ +{{- 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 +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/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.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..eedc560 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. 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. + rules: [] + # - matches: + # - path: + # type: PathPrefix + # value: / + # backendRefs: + # - name: sourcebot + # port: 3000 + # -- Configure init containers to run before the main container initContainers: [] # - name: sleeper