From fba8ad370d6f7b9d15fcb7912964cf387e5fc897 Mon Sep 17 00:00:00 2001 From: antoninguyot Date: Tue, 24 Mar 2026 16:31:19 +0100 Subject: [PATCH] feat: http route support --- templates/_helpers.tpl | 7 +- templates/httproute.yml | 81 +++++++++++++ tests/httproute_test.yaml | 234 ++++++++++++++++++++++++++++++++++++++ values.yaml | 36 ++++++ 4 files changed, 357 insertions(+), 1 deletion(-) create mode 100644 templates/httproute.yml create mode 100644 tests/httproute_test.yaml diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl index b56900a..6ad4131 100644 --- a/templates/_helpers.tpl +++ b/templates/_helpers.tpl @@ -94,8 +94,13 @@ Prints all Horizon allowed hosts. {{- end }} {{- end }} {{- end }} +{{- if .Values.route.enabled }} + {{- range .Values.route.hostnames }} + {{- $allowedHosts = append $allowedHosts . }} + {{- end }} +{{- end }} {{- $allowedHosts = concat $allowedHosts .Values.allowedHosts }} -{{- toJson $allowedHosts}} +{{- toJson $allowedHosts }} {{- end }} {{/* diff --git a/templates/httproute.yml b/templates/httproute.yml new file mode 100644 index 0000000..47c0857 --- /dev/null +++ b/templates/httproute.yml @@ -0,0 +1,81 @@ +{{- if .Values.route.enabled }} +{{- if empty .Values.route.parentRefs }} +{{- fail "route.parentRefs must be specified when route.enabled is true" }} +{{- end }} +{{- if and .Values.route.redirect.enabled (empty .Values.route.redirect.parentRefs) }} +{{- fail "route.redirect.parentRefs must be specified when route.redirect.enabled is true" }} +{{- end }} +{{- $fullName := include "common.names.fullname" . }} +{{- $hostnames := .Values.route.hostnames }} +{{- if empty $hostnames }} +{{- fail "route.hostnames must contain at least one hostname when route.enabled is true" }} +{{- end }} +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: {{ $fullName }} + labels: {{- include "horizon.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonLabels "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: + {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + parentRefs: + {{- include "common.tplvalues.render" (dict "value" .Values.route.parentRefs "context" $ ) | nindent 4 }} + hostnames: + {{- range $hostnames }} + - {{ . | quote }} + {{- end }} + rules: + - matches: + - path: + type: PathPrefix + value: {{ .Values.ingress.path | quote }} + backendRefs: + - name: {{ $fullName }} + port: 9000 +{{- if .Values.route.redirect.enabled }} +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: {{ $fullName }}-http + labels: {{- include "horizon.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonLabels "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: + {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + parentRefs: + {{- include "common.tplvalues.render" (dict "value" .Values.route.redirect.parentRefs "context" $ ) | nindent 4 }} + hostnames: + {{- range $hostnames }} + - {{ . | quote }} + {{- end }} + rules: + - matches: + {{- range .Values.route.redirect.exceptionPaths }} + - path: + type: PathPrefix + value: {{ . | quote }} + {{- end }} + backendRefs: + - name: {{ $fullName }} + port: 9000 + - matches: + - path: + type: PathPrefix + value: / + filters: + - type: RequestRedirect + requestRedirect: + scheme: https + statusCode: 301 +{{- end }} +{{- end }} diff --git a/tests/httproute_test.yaml b/tests/httproute_test.yaml new file mode 100644 index 0000000..0453560 --- /dev/null +++ b/tests/httproute_test.yaml @@ -0,0 +1,234 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/helm-unittest/helm-unittest/main/schema/helm-testsuite.json +suite: gateway api httproute +templates: + - httproute.yml + - ingress.yml + - deployment.yml +tests: + - it: should not render httproute resources by default + template: httproute.yml + asserts: + - hasDocuments: + count: 0 + + - it: should render two httproutes when redirect is enabled + set: + route: + enabled: true + hostnames: + - horizon.example.com + parentRefs: + - name: horizon-gateway + sectionName: https + redirect: + parentRefs: + - name: horizon-gateway + sectionName: http + template: httproute.yml + asserts: + - hasDocuments: + count: 2 + + - it: should name the HTTP redirect route with -http suffix + set: + route: + enabled: true + hostnames: + - horizon.example.com + parentRefs: + - name: horizon-gateway + sectionName: https + redirect: + parentRefs: + - name: horizon-gateway + sectionName: http + template: httproute.yml + documentIndex: 1 + asserts: + - isKind: + of: HTTPRoute + - matchRegex: + path: metadata.name + pattern: ".*-http$" + - equal: + path: spec.parentRefs[0].sectionName + value: http + + - it: should require redirect parentRefs when redirect is enabled + set: + route: + enabled: true + hostnames: + - horizon.example.com + parentRefs: + - name: horizon-gateway + sectionName: https + template: httproute.yml + asserts: + - failedTemplate: + errorMessage: route.redirect.parentRefs must be specified when route.redirect.enabled is true + + - it: should keep ingress rendering when route is enabled + set: + ingress: + enabled: true + hostname: horizon.example.com + route: + enabled: true + hostnames: + - horizon.example.com + parentRefs: + - name: horizon-gateway + sectionName: https + redirect: + parentRefs: + - name: horizon-gateway + sectionName: http + template: ingress.yml + asserts: + - hasDocuments: + count: 1 + - isKind: + of: Ingress + + - it: should configure redirect route with path prefix exceptions + set: + route: + enabled: true + hostnames: + - horizon.example.com + parentRefs: + - name: horizon-gateway + sectionName: https + redirect: + parentRefs: + - name: horizon-gateway + sectionName: http + template: httproute.yml + documentIndex: 1 + asserts: + - equal: + path: spec.rules[0].matches[0].path.type + value: PathPrefix + - equal: + path: spec.rules[0].matches[0].path.value + value: /scep + - equal: + path: spec.rules[0].matches[1].path.value + value: /certsrv + - equal: + path: spec.rules[0].matches[2].path.value + value: /certSrv + - equal: + path: spec.rules[0].matches[3].path.value + value: /intune + - equal: + path: spec.rules[0].matches[4].path.value + value: /jamf + - equal: + path: spec.rules[0].backendRefs[0].port + value: 9000 + - notExists: + path: spec.rules[0].filters + - equal: + path: spec.rules[1].filters[0].type + value: RequestRedirect + - equal: + path: spec.rules[1].filters[0].requestRedirect.scheme + value: https + - equal: + path: spec.rules[1].filters[0].requestRedirect.statusCode + value: 301 + + - it: should use http backend port for main route when tls is disabled + set: + route: + enabled: true + hostnames: + - horizon.example.com + parentRefs: + - name: horizon-gateway + sectionName: https + redirect: + enabled: false + tls: + enabled: false + template: httproute.yml + documentIndex: 0 + asserts: + - equal: + path: spec.rules[0].backendRefs[0].port + value: 9000 + + - it: should keep using http backend port for main route when tls is enabled + set: + route: + enabled: true + hostnames: + - horizon.example.com + parentRefs: + - name: horizon-gateway + sectionName: https + redirect: + enabled: false + tls: + enabled: true + template: httproute.yml + documentIndex: 0 + asserts: + - equal: + path: spec.rules[0].backendRefs[0].port + value: 9000 + + - it: should fail when route.hostnames is not provided + set: + route: + enabled: true + parentRefs: + - name: horizon-gateway + sectionName: https + redirect: + enabled: false + template: httproute.yml + asserts: + - failedTemplate: + errorMessage: route.hostnames must contain at least one hostname when route.enabled is true + + - it: should use explicit route hostnames in the main route + set: + route: + enabled: true + hostnames: + - route.horizon.example.com + parentRefs: + - name: horizon-gateway + sectionName: https + redirect: + enabled: false + template: httproute.yml + documentIndex: 0 + asserts: + - equal: + path: spec.hostnames[0] + value: route.horizon.example.com + - notExists: + path: spec.hostnames[1] + + - it: should add explicit route hostnames to deployment allowed hosts + set: + route: + enabled: true + hostnames: + - route.horizon.example.com + parentRefs: + - name: horizon-gateway + sectionName: https + redirect: + enabled: false + template: deployment.yml + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: HOSTS_ALLOWED.0 + value: route.horizon.example.com diff --git a/values.yaml b/values.yaml index 0ceba50..7223e27 100644 --- a/values.yaml +++ b/values.yaml @@ -427,6 +427,42 @@ ingress: ## extraRules: [] +## @section Horizon Route configuration +## +route: + ## @param route.enabled Set to true to enable Gateway API HTTPRoute generation + ## + enabled: false + ## @param route.parentRefs Parent references used by the HTTPRoute + ## e.g: + ## parentRefs: + ## - name: horizon-gateway + ## sectionName: https + ## + parentRefs: [] + ## @param route.hostnames Explicit hostnames for HTTPRoute. Mandatory when route.enabled=true. + ## + hostnames: [] + redirect: + ## @param route.redirect.enabled Set to true to generate HTTP to HTTPS redirects + ## + enabled: true + ## @param route.redirect.parentRefs Parent references used by the HTTP redirect route (e.g. HTTP listener) + ## e.g: + ## parentRefs: + ## - name: horizon-gateway + ## sectionName: http + ## + parentRefs: [] + ## @param route.redirect.exceptionPaths Path prefixes that must not be redirected to HTTPS + ## + exceptionPaths: + - /scep + - /certsrv + - /certSrv + - /intune + - /jamf + ## @section Prometheus monitor configuration ## @param monitoring.enabled Enable the creation of a ServiceMonitor object for Horizon if the cluster has the monitoring.coreos.com/v1 capability. monitoring: