Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ require (
k8s.io/apiserver v0.35.0
k8s.io/client-go v0.35.0
k8s.io/klog/v2 v2.130.1
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4
)

require (
Expand Down Expand Up @@ -110,7 +111,6 @@ require (
k8s.io/component-base v0.35.0 // indirect
k8s.io/kms v0.35.0 // indirect
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
Expand Down
59 changes: 12 additions & 47 deletions images/router/haproxy/conf/haproxy-config.template
Original file line number Diff line number Diff line change
Expand Up @@ -744,11 +744,16 @@ backend {{ genBackendNamePrefix $cfg.TLSTermination }}:{{ $cfgIdx }}
{{- end }}
{{- end }}

{{- if $dynamicConfigManager }}
dynamic-cookie-key {{ $cfg.RoutingKeyName }}
{{- end }}
{{- range $serviceUnitName, $weight := $cfg.ServiceUnitNames }}
{{- if ge $weight 0 }}{{/* weight=0 is reasonable to keep existing connections to backends with cookies as we can see the HTTP headers */}}
{{- with $serviceUnit := index $.ServiceUnits $serviceUnitName }}
{{- range $idx, $endpoint := processEndpointsForAlias $cfg $serviceUnit (env "ROUTER_BACKEND_PROCESS_ENDPOINTS" "") }}
server {{ $endpoint.ID }} {{ $endpoint.IP }}:{{ $endpoint.Port }} cookie {{ $endpoint.IdHash }} weight {{ $weight }}
{{- /* This should always follow backend.go/innerAddServer() method, changes here should be reflected there. */}}
{{- /* TODO: either move this configuration to the Go counterpart, or read it from here instead */}}
server {{ $endpoint.ID }} {{ $endpoint.IP }}:{{ $endpoint.Port }}{{ if not $dynamicConfigManager }} cookie {{ $endpoint.IdHash }}{{ end }} weight {{ $weight }}
{{- if (eq $cfg.TLSTermination "reencrypt") }} ssl
{{- if not (isTrue $router_disable_http2) }} alpn h2,http/1.1
{{- end }}
Expand Down Expand Up @@ -776,43 +781,6 @@ backend {{ genBackendNamePrefix $cfg.TLSTermination }}:{{ $cfgIdx }}
{{- end }}{{/* end get serviceUnit from its name */}}
{{- end }}{{/* end range over serviceUnitNames */}}

{{- with $dynamicConfigManager }}
{{- if (eq $cfg.TLSTermination "reencrypt") }}
dynamic-cookie-key {{ $cfg.RoutingKeyName }}
{{- range $idx, $serverName := $dynamicConfigManager.GenerateDynamicServerNames $cfgIdx }}
server {{ $serverName }} 172.4.0.4:8765 weight 0 ssl disabled check inter {{ $health_check_interval }}
{{- if gt (len (index $cfg.Certificates (printf "%s_pod" $cfg.Host)).Contents) 0 }} verify required ca-file {{ $workingDir }}/router/cacerts/{{$cfgIdx }}.pem
{{- else }}
{{- if not (isTrue $router_disable_http2) }} alpn h2,http/1.1
{{- end }}
{{- /*
Add "verifyhost" for the primary service only. This server is not intended
to be used for alternate backend endpoints. A reload will be triggered if
an alternate backend endpoint is added dynamically.
*/ -}}
{{- with $serviceUnit := index $.ServiceUnits $cfg.PrimaryServiceUnitKey -}}
{{- if $cfg.VerifyServiceHostname }} verifyhost {{ $serviceUnit.Hostname }}
{{- end }}
{{- end }}
{{- if gt (len $defaultDestinationCA) 0 }} verify required ca-file {{ $defaultDestinationCA }}
{{- else }} verify none
{{- end }}
{{- end }}
{{- with $podMaxConn := index $cfg.Annotations "haproxy.router.openshift.io/pod-concurrent-connections" }}
{{- if (isInteger (index $cfg.Annotations "haproxy.router.openshift.io/pod-concurrent-connections")) }} maxconn {{$podMaxConn }} {{- end }}
{{- end }}{{/* end pod-concurrent-connections annotation */}}
{{- end }}{{/* end range over dynamic server names */}}

{{- else }}
{{- with $name := $dynamicConfigManager.ServerTemplateName $cfgIdx }}
{{- with $size := $dynamicConfigManager.ServerTemplateSize $cfgIdx }}
dynamic-cookie-key {{ $cfg.RoutingKeyName }}
server-template {{ $name }}- 1-{{ $size }} 172.4.0.4:8765 check inter {{ $health_check_interval }} disabled
{{- end }}
{{- end }}
{{- end }}
{{- end }}

{{- end }}{{/* end if tls==edge/none/reencrypt */}}

{{- if eq $cfg.TLSTermination "passthrough" }}
Expand Down Expand Up @@ -856,10 +824,16 @@ backend {{ genBackendNamePrefix $cfg.TLSTermination }}:{{ $cfgIdx }}

hash-type consistent
timeout check 5000ms

{{- if $dynamicConfigManager }}
dynamic-cookie-key {{ $cfg.RoutingKeyName }}
{{- end }}
{{- range $serviceUnitName, $weight := $cfg.ServiceUnitNames }}
{{- if ne $weight 0 }}{{/* drop connections where weight=0 as we can't use cookies, leaving only r-r and src-ip as dispatch methods and weight make no sense there */}}
{{- with $serviceUnit := index $.ServiceUnits $serviceUnitName }}
{{- range $idx, $endpoint := processEndpointsForAlias $cfg $serviceUnit (env "ROUTER_BACKEND_PROCESS_ENDPOINTS" "") }}
{{- /* This should always follow backend.go/innerAddServer() method, changes here should be reflected there. */}}
{{- /* TODO: either move this configuration to the Go counterpart, or read it from here instead */}}
server {{ $endpoint.ID }} {{ $endpoint.IP }}:{{ $endpoint.Port }} weight {{ $weight }}
{{- if and (not $endpoint.NoHealthCheck) (gt $cfg.ActiveEndpoints 1) }} check inter {{ $health_check_interval }}
{{- end }}{{/* end else no health check */}}
Expand All @@ -872,15 +846,6 @@ backend {{ genBackendNamePrefix $cfg.TLSTermination }}:{{ $cfgIdx }}
{{- end }}{{/* end if weight != 0 */}}
{{- end }}{{/* end iterate over services*/}}

{{- with $dynamicConfigManager }}
{{- with $name := $dynamicConfigManager.ServerTemplateName $cfgIdx }}
{{- with $size := $dynamicConfigManager.ServerTemplateSize $cfgIdx }}
dynamic-cookie-key {{ $cfg.RoutingKeyName }}
server-template {{ $name }}- 1-{{ $size }} 172.4.0.4:8765 check inter {{ $health_check_interval }} disabled
{{- end }}
{{- end }}
{{- end }}

{{- end }}{{/*end tls==passthrough*/}}

{{- end }}{{/* end loop over routes */}}
Expand Down
8 changes: 5 additions & 3 deletions pkg/cmd/infra/router/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,6 @@ type TemplateRouterConfigManager struct {
BlueprintRouteNamespace string
BlueprintRouteLabelSelector string
BlueprintRoutePoolSize int
MaxDynamicServers int
}

// isTrue here has the same logic as the function within package pkg/router/template
Expand Down Expand Up @@ -182,13 +181,15 @@ func (o *TemplateRouter) Bind(flag *pflag.FlagSet) {
flag.StringVar(&o.BlueprintRouteNamespace, "blueprint-route-namespace", env("ROUTER_BLUEPRINT_ROUTE_NAMESPACE", ""), "Specifies the namespace which contains the routes that serve as blueprints for the dynamic configuration manager.")
flag.StringVar(&o.BlueprintRouteLabelSelector, "blueprint-route-labels", env("ROUTER_BLUEPRINT_ROUTE_LABELS", ""), "A label selector to apply to the routes in the blueprint route namespace. These selected routes will serve as blueprints for the dynamic dynamic configuration manager.")
flag.IntVar(&o.BlueprintRoutePoolSize, "blueprint-route-pool-size", int(envInt("ROUTER_BLUEPRINT_ROUTE_POOL_SIZE", 10, 0)), "Specifies the size of the pre-allocated pool for each route blueprint managed by the router specific dynamic configuration manager. This can be overriden by an annotation router.openshift.io/pool-size on an individual route.")
flag.IntVar(&o.MaxDynamicServers, "max-dynamic-servers", int(envInt("ROUTER_MAX_DYNAMIC_SERVERS", 5, 1)), "Specifies the maximum number of dynamic servers added to a route for use by the router specific dynamic configuration manager.")
flag.StringVar(&o.CaptureHTTPRequestHeadersString, "capture-http-request-headers", env("ROUTER_CAPTURE_HTTP_REQUEST_HEADERS", ""), "A comma-delimited list of HTTP request header names and maximum header value lengths that should be captured for logging. Each item must have the following form: name:maxLength")
flag.StringVar(&o.CaptureHTTPResponseHeadersString, "capture-http-response-headers", env("ROUTER_CAPTURE_HTTP_RESPONSE_HEADERS", ""), "A comma-delimited list of HTTP response header names and maximum header value lengths that should be captured for logging. Each item must have the following form: name:maxLength")
flag.StringVar(&o.CaptureHTTPCookieString, "capture-http-cookie", env("ROUTER_CAPTURE_HTTP_COOKIE", ""), "Name and maximum length of HTTP cookie that should be captured for logging. The argument must have the following form: name:maxLength. Append '=' to the name to indicate that an exact match should be performed; otherwise a prefix match will be performed. The value of first cookie that matches the name is captured.")
flag.StringVar(&o.HTTPHeaderNameCaseAdjustmentsString, "http-header-name-case-adjustments", env("ROUTER_H1_CASE_ADJUST", ""), "A comma-delimited list of HTTP header names that should have their case adjusted. Each item must be a valid HTTP header name and should have the desired capitalization.")
flag.StringVar(&o.HTTPResponseHeadersString, "set-delete-http-response-header", env("ROUTER_HTTP_RESPONSE_HEADERS", ""), "A comma-delimited list of HTTP response header names and values that should be set/deleted.")
flag.StringVar(&o.HTTPRequestHeadersString, "set-delete-http-request-header", env("ROUTER_HTTP_REQUEST_HEADERS", ""), "A comma-delimited list of HTTP request header names and values that should be set/deleted.")

// deprecated flags
_ = flag.Int("max-dynamic-servers", int(envInt("ROUTER_MAX_DYNAMIC_SERVERS", 5, 1)), "Specifies the maximum number of dynamic servers added to a route for use by the router specific dynamic configuration manager. DEPRECATED: router now created backend servers dynamically.")
}

type RouterStats struct {
Expand Down Expand Up @@ -740,9 +741,10 @@ func (o *TemplateRouterOptions) Run(stopCh <-chan struct{}) error {
CommitInterval: o.CommitInterval,
BlueprintRoutes: blueprintRoutes,
BlueprintRoutePoolSize: o.BlueprintRoutePoolSize,
MaxDynamicServers: o.MaxDynamicServers,
WildcardRoutesAllowed: o.AllowWildcardRoutes,
ExtendedValidation: o.ExtendedValidation,
WorkingDir: o.WorkingDir,
DefaultDestinationCA: o.DefaultDestinationCAPath,
}
cfgManager = haproxyconfigmanager.NewHAProxyConfigManager(cmopts)
if len(o.BlueprintRouteNamespace) > 0 {
Expand Down
110 changes: 1 addition & 109 deletions pkg/router/router_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,7 @@ u3YLAbyW/lHhOCiZu2iAI8AbmXem9lW6Tr7p/97s0w==
Action: "Set",
}},
DynamicConfigManager: haproxyconfigmanager.NewHAProxyConfigManager(templateplugin.ConfigManagerOptions{
ConnectionInfo: "unix:///var/lib/dymmy",
MaxDynamicServers: 1,
ConnectionInfo: "unix:///var/lib/dummy",
}),
}
plugin, err = templateplugin.NewTemplatePlugin(pluginCfg, svcFetcher)
Expand Down Expand Up @@ -749,62 +748,6 @@ func TestConfigTemplate(t *testing.T) {
},
},
},
"Verifyhost for dynamic slot": {
mustCreateWithConfig{
mustCreateEndpointSlices: []mustCreateEndpointSlice{
{
name: "serviceq",
serviceName: "serviceq",
},
},
mustCreateRoute: mustCreateRoute{
name: "q",
host: "qexample.com",
targetServiceName: "serviceq",
weight: int32(100),
time: start,
tlsTermination: routev1.TLSTerminationReencrypt,
},
mustMatchConfig: mustMatchConfig{
section: "backend",
sectionName: reencryptBackendName(h.namespace, "q"),
attribute: "server",
value: "_dynamic-pod-1 172.4.0.4:8765 weight 0 ssl disabled check inter 5000ms alpn h2,http/1.1 verifyhost serviceq.default.svc verify required ca-file dummy",
fullMatch: true,
},
},
},
"Verifyhost for dynamic slot with alternate backend": {
mustCreateWithConfig{
mustCreateEndpointSlices: []mustCreateEndpointSlice{
{
name: "serviceq1",
serviceName: "serviceq1",
},
{
name: "serviceq2",
serviceName: "serviceq2",
},
},
mustCreateRoute: mustCreateRoute{
name: "q1",
host: "q1example.com",
targetServiceName: "serviceq1",
weight: int32(50),
alternateBackend: "serviceq2",
alternateBackendWeight: int32(50),
time: start,
tlsTermination: routev1.TLSTerminationReencrypt,
},
mustMatchConfig: mustMatchConfig{
section: "backend",
sectionName: reencryptBackendName(h.namespace, "q1"),
attribute: "server",
value: "_dynamic-pod-1 172.4.0.4:8765 weight 0 ssl disabled check inter 5000ms alpn h2,http/1.1 verifyhost serviceq1.default.svc verify required ca-file dummy",
fullMatch: true,
},
},
},
"valid route health check interval annotation": {
mustCreateWithConfig{
mustCreateEndpointSlices: []mustCreateEndpointSlice{
Expand Down Expand Up @@ -931,57 +874,6 @@ func TestConfigTemplate(t *testing.T) {
},
},
},
"server-template with default health check interval": {
mustCreateWithConfig{
mustCreateEndpointSlices: []mustCreateEndpointSlice{
{
name: "servicest2",
serviceName: "servicest2",
addresses: []string{"1.1.1.1"},
},
},
mustCreateRoute: mustCreateRoute{
name: "st2",
host: "st2example.com",
targetServiceName: "servicest2",
weight: 1,
time: start,
},
mustMatchConfig: mustMatchConfig{
section: "backend",
sectionName: insecureBackendName(h.namespace, "st2"),
attribute: "server-template",
value: "inter 5000ms", // Default value
},
},
},
"server-template with custom health check interval": {
mustCreateWithConfig{
mustCreateEndpointSlices: []mustCreateEndpointSlice{
{
name: "servicest1",
serviceName: "servicest1",
addresses: []string{"1.1.1.1"}, // Single endpoint initially
},
},
mustCreateRoute: mustCreateRoute{
name: "st1",
host: "st1example.com",
targetServiceName: "servicest1",
weight: 1,
time: start,
annotations: map[string]string{
"router.openshift.io/haproxy.health.check.interval": "15s",
},
},
mustMatchConfig: mustMatchConfig{
section: "backend",
sectionName: insecureBackendName(h.namespace, "st1"),
attribute: "server-template",
value: "inter 15s",
},
},
},
}

defer cleanUpRoutes(t)
Expand Down
Loading