diff --git a/apis/v1alpha1/nginxproxy_types.go b/apis/v1alpha1/nginxproxy_types.go index 39bfb6eab..a91c585e5 100644 --- a/apis/v1alpha1/nginxproxy_types.go +++ b/apis/v1alpha1/nginxproxy_types.go @@ -32,6 +32,11 @@ type NginxProxySpec struct { // // +optional Telemetry *Telemetry `json:"telemetry,omitempty"` + // DisableHTTP2 defines if http2 should be disabled for all servers. + // Default is false, meaning http2 will be enabled for all servers. + // + // +optional + DisableHTTP2 bool `json:"disableHTTP2,omitempty"` } // Telemetry specifies the OpenTelemetry configuration. diff --git a/charts/nginx-gateway-fabric/values.yaml b/charts/nginx-gateway-fabric/values.yaml index 4b7430e9a..abbe6f1af 100644 --- a/charts/nginx-gateway-fabric/values.yaml +++ b/charts/nginx-gateway-fabric/values.yaml @@ -72,6 +72,7 @@ nginx: ## The configuration for the data plane that is contained in the NginxProxy resource. config: {} + # disableHTTP2: false # telemetry: # exporter: # endpoint: otel-collector.default.svc:4317 diff --git a/config/crd/bases/gateway.nginx.org_nginxproxies.yaml b/config/crd/bases/gateway.nginx.org_nginxproxies.yaml index 2a7dff8ee..b42d562f7 100644 --- a/config/crd/bases/gateway.nginx.org_nginxproxies.yaml +++ b/config/crd/bases/gateway.nginx.org_nginxproxies.yaml @@ -47,6 +47,11 @@ spec: spec: description: Spec defines the desired state of the NginxProxy. properties: + disableHTTP2: + description: |- + DisableHTTP2 defines if http2 should be disabled for all servers. + Default is false, meaning http2 will be enabled for all servers. + type: boolean telemetry: description: Telemetry specifies the OpenTelemetry configuration. properties: diff --git a/deploy/crds.yaml b/deploy/crds.yaml index 3505fd191..da732fcc5 100644 --- a/deploy/crds.yaml +++ b/deploy/crds.yaml @@ -700,6 +700,11 @@ spec: spec: description: Spec defines the desired state of the NginxProxy. properties: + disableHTTP2: + description: |- + DisableHTTP2 defines if http2 should be disabled for all servers. + Default is false, meaning http2 will be enabled for all servers. + type: boolean telemetry: description: Telemetry specifies the OpenTelemetry configuration. properties: diff --git a/internal/mode/static/nginx/conf/nginx-plus.conf b/internal/mode/static/nginx/conf/nginx-plus.conf index 009dba530..b8b9cc77d 100644 --- a/internal/mode/static/nginx/conf/nginx-plus.conf +++ b/internal/mode/static/nginx/conf/nginx-plus.conf @@ -27,8 +27,6 @@ http { sendfile on; tcp_nopush on; - http2 on; - server { listen 127.0.0.1:8765; root /usr/share/nginx/html; diff --git a/internal/mode/static/nginx/conf/nginx.conf b/internal/mode/static/nginx/conf/nginx.conf index 33b2333eb..b70e857e0 100644 --- a/internal/mode/static/nginx/conf/nginx.conf +++ b/internal/mode/static/nginx/conf/nginx.conf @@ -27,8 +27,6 @@ http { sendfile on; tcp_nopush on; - http2 on; - server { listen unix:/var/run/nginx/nginx-status.sock; access_log off; diff --git a/internal/mode/static/nginx/config/base_http_config.go b/internal/mode/static/nginx/config/base_http_config.go new file mode 100644 index 000000000..bcd8ae948 --- /dev/null +++ b/internal/mode/static/nginx/config/base_http_config.go @@ -0,0 +1,18 @@ +package config + +import ( + gotemplate "text/template" + + "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/dataplane" +) + +var baseHTTPTemplate = gotemplate.Must(gotemplate.New("baseHttp").Parse(baseHTTPTemplateText)) + +func executeBaseHTTPConfig(conf dataplane.Configuration) []executeResult { + result := executeResult{ + dest: httpConfigFile, + data: execute(baseHTTPTemplate, conf.BaseHTTPConfig), + } + + return []executeResult{result} +} diff --git a/internal/mode/static/nginx/config/base_http_config_template.go b/internal/mode/static/nginx/config/base_http_config_template.go new file mode 100644 index 000000000..a909001ab --- /dev/null +++ b/internal/mode/static/nginx/config/base_http_config_template.go @@ -0,0 +1,5 @@ +package config + +const baseHTTPTemplateText = ` +{{- if .HTTP2 }}http2 on;{{ end }} +` diff --git a/internal/mode/static/nginx/config/base_http_config_test.go b/internal/mode/static/nginx/config/base_http_config_test.go new file mode 100644 index 000000000..2f609d840 --- /dev/null +++ b/internal/mode/static/nginx/config/base_http_config_test.go @@ -0,0 +1,52 @@ +package config + +import ( + "strings" + "testing" + + . "github.com/onsi/gomega" + + "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/dataplane" +) + +func TestExecuteBaseHttp(t *testing.T) { + confOn := dataplane.Configuration{ + BaseHTTPConfig: dataplane.BaseHTTPConfig{ + HTTP2: true, + }, + } + + confOff := dataplane.Configuration{ + BaseHTTPConfig: dataplane.BaseHTTPConfig{ + HTTP2: false, + }, + } + + expSubStr := "http2 on;" + + tests := []struct { + name string + conf dataplane.Configuration + expCount int + }{ + { + name: "http2 on", + conf: confOn, + expCount: 1, + }, + { + name: "http2 off", + expCount: 0, + conf: confOff, + }, + } + + for _, test := range tests { + + g := NewWithT(t) + + res := executeBaseHTTPConfig(test.conf) + g.Expect(res).To(HaveLen(1)) + g.Expect(test.expCount).To(Equal(strings.Count(string(res[0].data), expSubStr))) + } +} diff --git a/internal/mode/static/nginx/config/generator.go b/internal/mode/static/nginx/config/generator.go index 6ed5afb94..4a39de209 100644 --- a/internal/mode/static/nginx/config/generator.go +++ b/internal/mode/static/nginx/config/generator.go @@ -147,6 +147,7 @@ func (g GeneratorImpl) generateHTTPConfig(conf dataplane.Configuration) []file.F func (g GeneratorImpl) getExecuteFuncs() []executeFunc { return []executeFunc{ + executeBaseHTTPConfig, executeServers, g.executeUpstreams, executeSplitClients, diff --git a/internal/mode/static/nginx/config/generator_test.go b/internal/mode/static/nginx/config/generator_test.go index 7f2bc0f7a..d4e6a5f84 100644 --- a/internal/mode/static/nginx/config/generator_test.go +++ b/internal/mode/static/nginx/config/generator_test.go @@ -70,6 +70,9 @@ func TestGenerate(t *testing.T) { BatchSize: 512, BatchCount: 4, }, + BaseHTTPConfig: dataplane.BaseHTTPConfig{ + HTTP2: true, + }, } g := NewWithT(t) @@ -104,6 +107,7 @@ func TestGenerate(t *testing.T) { g.Expect(httpCfg).To(ContainSubstring("batch_size 512;")) g.Expect(httpCfg).To(ContainSubstring("batch_count 4;")) g.Expect(httpCfg).To(ContainSubstring("otel_service_name ngf:gw-ns:gw-name:my-name;")) + g.Expect(httpCfg).To(ContainSubstring("http2 on;")) g.Expect(files[2].Path).To(Equal("/etc/nginx/conf.d/matches.json")) diff --git a/internal/mode/static/state/conditions/conditions.go b/internal/mode/static/state/conditions/conditions.go index ec700f03a..84d1cbafe 100644 --- a/internal/mode/static/state/conditions/conditions.go +++ b/internal/mode/static/state/conditions/conditions.go @@ -44,6 +44,10 @@ const ( // Used with Accepted (false). RouteReasonGatewayNotProgrammed v1.RouteConditionReason = "GatewayNotProgrammed" + // RouteReasonUnsupportedConfiguration is used when the associated Gateway does not support the Route. + // Used with Accepted (false). + RouteReasonUnsupportedConfiguration v1.RouteConditionReason = "UnsupportedConfiguration" + // GatewayReasonGatewayConflict indicates there are multiple Gateway resources to choose from, // and we ignored the resource in question and picked another Gateway as the winner. // This reason is used with GatewayConditionAccepted (false). @@ -241,6 +245,17 @@ func NewRouteNoMatchingParent() conditions.Condition { } } +// NewRouteUnsupportedConfiguration returns a Condition that indicates that the Route is not Accepted because +// it is incompatible with the Gateway's configuration. +func NewRouteUnsupportedConfiguration(msg string) conditions.Condition { + return conditions.Condition{ + Type: string(v1.RouteConditionAccepted), + Status: metav1.ConditionFalse, + Reason: string(RouteReasonUnsupportedConfiguration), + Message: msg, + } +} + // NewRouteGatewayNotProgrammed returns a Condition that indicates that the Gateway it references is not programmed, // which does not guarantee that the Route has been configured. func NewRouteGatewayNotProgrammed(msg string) conditions.Condition { diff --git a/internal/mode/static/state/dataplane/configuration.go b/internal/mode/static/state/dataplane/configuration.go index 3978438a7..35164b9f1 100644 --- a/internal/mode/static/state/dataplane/configuration.go +++ b/internal/mode/static/state/dataplane/configuration.go @@ -44,16 +44,18 @@ func BuildConfiguration( keyPairs := buildSSLKeyPairs(g.ReferencedSecrets, g.Gateway.Listeners) certBundles := buildCertBundles(g.ReferencedCaCertConfigMaps, backendGroups) telemetry := buildTelemetry(g) + baseHTTPConfig := buildBaseHTTPConfig(g) config := Configuration{ - HTTPServers: httpServers, - SSLServers: sslServers, - Upstreams: upstreams, - BackendGroups: backendGroups, - SSLKeyPairs: keyPairs, - Version: configVersion, - CertBundles: certBundles, - Telemetry: telemetry, + HTTPServers: httpServers, + SSLServers: sslServers, + Upstreams: upstreams, + BackendGroups: backendGroups, + SSLKeyPairs: keyPairs, + Version: configVersion, + CertBundles: certBundles, + Telemetry: telemetry, + BaseHTTPConfig: baseHTTPConfig, } return config @@ -619,3 +621,20 @@ func buildTelemetry(g *graph.Graph) Telemetry { return tel } + +// buildBaseHTTPConfig generates the base http context config that should be applied to all servers. +func buildBaseHTTPConfig(g *graph.Graph) BaseHTTPConfig { + baseConfig := BaseHTTPConfig{ + // HTTP2 should be enabled by default + HTTP2: true, + } + if g.NginxProxy == nil { + return baseConfig + } + + if g.NginxProxy.Spec.DisableHTTP2 { + baseConfig.HTTP2 = false + } + + return baseConfig +} diff --git a/internal/mode/static/state/dataplane/configuration_test.go b/internal/mode/static/state/dataplane/configuration_test.go index ab78a4638..db98a605f 100644 --- a/internal/mode/static/state/dataplane/configuration_test.go +++ b/internal/mode/static/state/dataplane/configuration_test.go @@ -555,6 +555,7 @@ func TestBuildConfiguration(t *testing.T) { }, ServiceName: helpers.GetPointer("my-svc"), }, + DisableHTTP2: true, }, } @@ -576,10 +577,11 @@ func TestBuildConfiguration(t *testing.T) { Routes: map[graph.RouteKey]*graph.L7Route{}, }, expConf: Configuration{ - HTTPServers: []VirtualServer{}, - SSLServers: []VirtualServer{}, - SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{}, - CertBundles: map[CertBundleID]CertBundle{}, + HTTPServers: []VirtualServer{}, + SSLServers: []VirtualServer{}, + SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{}, + CertBundles: map[CertBundleID]CertBundle{}, + BaseHTTPConfig: BaseHTTPConfig{HTTP2: true}, }, msg: "no listeners and routes", }, @@ -609,9 +611,10 @@ func TestBuildConfiguration(t *testing.T) { Port: 80, }, }, - SSLServers: []VirtualServer{}, - SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{}, - CertBundles: map[CertBundleID]CertBundle{}, + SSLServers: []VirtualServer{}, + SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{}, + CertBundles: map[CertBundleID]CertBundle{}, + BaseHTTPConfig: BaseHTTPConfig{HTTP2: true}, }, msg: "http listener with no routes", }, @@ -674,7 +677,8 @@ func TestBuildConfiguration(t *testing.T) { Key: []byte("privateKey-1"), }, }, - CertBundles: map[CertBundleID]CertBundle{}, + CertBundles: map[CertBundleID]CertBundle{}, + BaseHTTPConfig: BaseHTTPConfig{HTTP2: true}, }, msg: "http and https listeners with no valid routes", }, @@ -737,7 +741,8 @@ func TestBuildConfiguration(t *testing.T) { Key: []byte("privateKey-2"), }, }, - CertBundles: map[CertBundleID]CertBundle{}, + CertBundles: map[CertBundleID]CertBundle{}, + BaseHTTPConfig: BaseHTTPConfig{HTTP2: true}, }, msg: "https listeners with no routes", }, @@ -767,10 +772,11 @@ func TestBuildConfiguration(t *testing.T) { }, }, expConf: Configuration{ - HTTPServers: []VirtualServer{}, - SSLServers: []VirtualServer{}, - SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{}, - CertBundles: map[CertBundleID]CertBundle{}, + HTTPServers: []VirtualServer{}, + SSLServers: []VirtualServer{}, + SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{}, + CertBundles: map[CertBundleID]CertBundle{}, + BaseHTTPConfig: BaseHTTPConfig{HTTP2: true}, }, msg: "invalid https listener with resolved secret", }, @@ -838,11 +844,12 @@ func TestBuildConfiguration(t *testing.T) { Port: 80, }, }, - SSLServers: []VirtualServer{}, - Upstreams: []Upstream{fooUpstream}, - BackendGroups: []BackendGroup{expHR1Groups[0], expHR2Groups[0]}, - SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{}, - CertBundles: map[CertBundleID]CertBundle{}, + SSLServers: []VirtualServer{}, + Upstreams: []Upstream{fooUpstream}, + BackendGroups: []BackendGroup{expHR1Groups[0], expHR2Groups[0]}, + SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{}, + CertBundles: map[CertBundleID]CertBundle{}, + BaseHTTPConfig: BaseHTTPConfig{HTTP2: true}, }, msg: "one http listener with two routes for different hostnames", }, @@ -893,11 +900,12 @@ func TestBuildConfiguration(t *testing.T) { Port: 80, }, }, - SSLServers: []VirtualServer{}, - Upstreams: []Upstream{fooUpstream}, - BackendGroups: []BackendGroup{expGRGroups[0]}, - SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{}, - CertBundles: map[CertBundleID]CertBundle{}, + SSLServers: []VirtualServer{}, + Upstreams: []Upstream{fooUpstream}, + BackendGroups: []BackendGroup{expGRGroups[0]}, + SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{}, + CertBundles: map[CertBundleID]CertBundle{}, + BaseHTTPConfig: BaseHTTPConfig{HTTP2: true}, }, msg: "one http listener with one grpc route", }, @@ -1017,7 +1025,8 @@ func TestBuildConfiguration(t *testing.T) { Key: []byte("privateKey-2"), }, }, - CertBundles: map[CertBundleID]CertBundle{}, + CertBundles: map[CertBundleID]CertBundle{}, + BaseHTTPConfig: BaseHTTPConfig{HTTP2: true}, }, msg: "two https listeners each with routes for different hostnames", }, @@ -1177,7 +1186,8 @@ func TestBuildConfiguration(t *testing.T) { Key: []byte("privateKey-1"), }, }, - CertBundles: map[CertBundleID]CertBundle{}, + CertBundles: map[CertBundleID]CertBundle{}, + BaseHTTPConfig: BaseHTTPConfig{HTTP2: true}, }, msg: "one http and one https listener with two routes with the same hostname with and without collisions", }, @@ -1390,7 +1400,8 @@ func TestBuildConfiguration(t *testing.T) { Key: []byte("privateKey-1"), }, }, - CertBundles: map[CertBundleID]CertBundle{}, + CertBundles: map[CertBundleID]CertBundle{}, + BaseHTTPConfig: BaseHTTPConfig{HTTP2: true}, }, msg: "multiple http and https listener; different ports", @@ -1518,11 +1529,12 @@ func TestBuildConfiguration(t *testing.T) { Port: 80, }, }, - SSLServers: []VirtualServer{}, - Upstreams: []Upstream{fooUpstream}, - BackendGroups: []BackendGroup{expHR5Groups[0], expHR5Groups[1]}, - SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{}, - CertBundles: map[CertBundleID]CertBundle{}, + SSLServers: []VirtualServer{}, + Upstreams: []Upstream{fooUpstream}, + BackendGroups: []BackendGroup{expHR5Groups[0], expHR5Groups[1]}, + SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{}, + CertBundles: map[CertBundleID]CertBundle{}, + BaseHTTPConfig: BaseHTTPConfig{HTTP2: true}, }, msg: "one http listener with one route with filters", }, @@ -1624,7 +1636,8 @@ func TestBuildConfiguration(t *testing.T) { Key: []byte("privateKey-1"), }, }, - CertBundles: map[CertBundleID]CertBundle{}, + CertBundles: map[CertBundleID]CertBundle{}, + BaseHTTPConfig: BaseHTTPConfig{HTTP2: true}, }, msg: "one http and one https listener with routes with valid and invalid rules", }, @@ -1684,11 +1697,12 @@ func TestBuildConfiguration(t *testing.T) { Port: 80, }, }, - SSLServers: []VirtualServer{}, - Upstreams: []Upstream{fooUpstream}, - BackendGroups: []BackendGroup{expHR7Groups[0], expHR7Groups[1]}, - SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{}, - CertBundles: map[CertBundleID]CertBundle{}, + SSLServers: []VirtualServer{}, + Upstreams: []Upstream{fooUpstream}, + BackendGroups: []BackendGroup{expHR7Groups[0], expHR7Groups[1]}, + SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{}, + CertBundles: map[CertBundleID]CertBundle{}, + BaseHTTPConfig: BaseHTTPConfig{HTTP2: true}, }, msg: "duplicate paths with different types", }, @@ -1776,7 +1790,8 @@ func TestBuildConfiguration(t *testing.T) { Key: []byte("privateKey-2"), }, }, - CertBundles: map[CertBundleID]CertBundle{}, + CertBundles: map[CertBundleID]CertBundle{}, + BaseHTTPConfig: BaseHTTPConfig{HTTP2: true}, }, msg: "two https listeners with different hostnames but same route; chooses listener with more specific hostname", }, @@ -1853,6 +1868,7 @@ func TestBuildConfiguration(t *testing.T) { CertBundles: map[CertBundleID]CertBundle{ "cert_bundle_test_configmap-1": []byte("cert-1"), }, + BaseHTTPConfig: BaseHTTPConfig{HTTP2: true}, }, msg: "https listener with httproute with backend that has a backend TLS policy attached", }, @@ -1929,6 +1945,7 @@ func TestBuildConfiguration(t *testing.T) { CertBundles: map[CertBundleID]CertBundle{ "cert_bundle_test_configmap-2": []byte("cert-2"), }, + BaseHTTPConfig: BaseHTTPConfig{HTTP2: true}, }, msg: "https listener with httproute with backend that has a backend TLS policy with binaryData attached", }, @@ -1964,9 +1981,10 @@ func TestBuildConfiguration(t *testing.T) { Port: 80, }, }, - SSLServers: []VirtualServer{}, - SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{}, - CertBundles: map[CertBundleID]CertBundle{}, + SSLServers: []VirtualServer{}, + SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{}, + CertBundles: map[CertBundleID]CertBundle{}, + BaseHTTPConfig: BaseHTTPConfig{HTTP2: false}, Telemetry: Telemetry{ Endpoint: "my-otel.svc:4563", Interval: "5s", @@ -1976,7 +1994,7 @@ func TestBuildConfiguration(t *testing.T) { SpanAttributes: []SpanAttribute{}, }, }, - msg: "NginxProxy with tracing config", + msg: "NginxProxy with tracing config and http2 disabled", }, } @@ -1994,6 +2012,7 @@ func TestBuildConfiguration(t *testing.T) { g.Expect(result.Version).To(Equal(1)) g.Expect(result.CertBundles).To(Equal(test.expConf.CertBundles)) g.Expect(result.Telemetry).To(Equal(test.expConf.Telemetry)) + g.Expect(result.BaseHTTPConfig).To(Equal(test.expConf.BaseHTTPConfig)) }) } } diff --git a/internal/mode/static/state/dataplane/types.go b/internal/mode/static/state/dataplane/types.go index 3e7a861af..23ab0a46e 100644 --- a/internal/mode/static/state/dataplane/types.go +++ b/internal/mode/static/state/dataplane/types.go @@ -35,6 +35,8 @@ type Configuration struct { BackendGroups []BackendGroup // Telemetry holds the Otel configuration. Telemetry Telemetry + // BaseHTTPConfig holds the configuration options at the http context. + BaseHTTPConfig BaseHTTPConfig // Version represents the version of the generated configuration. Version int } @@ -279,3 +281,9 @@ type SpanAttribute struct { // Value is the value for a span attribute. Value string } + +// BaseHTTPConfig holds the configuration options at the http context. +type BaseHTTPConfig struct { + // HTTP2 specifies whether http2 should be enabled for all servers. + HTTP2 bool +} diff --git a/internal/mode/static/state/graph/graph.go b/internal/mode/static/state/graph/graph.go index e121e90fa..4ea89583f 100644 --- a/internal/mode/static/state/graph/graph.go +++ b/internal/mode/static/state/graph/graph.go @@ -149,6 +149,7 @@ func BuildGraph( state.HTTPRoutes, state.GRPCRoutes, processedGws.GetAllNsNames(), + npCfg, ) bindRoutesToListeners(routes, gw, state.Namespaces) addBackendRefsToRouteRules(routes, refGrantResolver, state.Services, processedBackendTLSPolicies) diff --git a/internal/mode/static/state/graph/grpcroute.go b/internal/mode/static/state/graph/grpcroute.go index d50001422..1d67cdf55 100644 --- a/internal/mode/static/state/graph/grpcroute.go +++ b/internal/mode/static/state/graph/grpcroute.go @@ -15,6 +15,7 @@ func buildGRPCRoute( validator validation.HTTPFieldsValidator, ghr *v1alpha2.GRPCRoute, gatewayNsNames []types.NamespacedName, + http2disabled bool, ) *L7Route { r := &L7Route{ Source: ghr, @@ -33,6 +34,14 @@ func buildGRPCRoute( } r.ParentRefs = sectionNameRefs + if http2disabled { + r.Valid = false + msg := "HTTP2 is disabled - cannot configure GRPCRoutes" + r.Conditions = append(r.Conditions, staticConds.NewRouteUnsupportedConfiguration(msg)) + + return r + } + if err := validateHostnames( ghr.Spec.Hostnames, field.NewPath("spec").Child("hostnames"), diff --git a/internal/mode/static/state/graph/grpcroute_test.go b/internal/mode/static/state/graph/grpcroute_test.go index 2983127d6..4d3800319 100644 --- a/internal/mode/static/state/graph/grpcroute_test.go +++ b/internal/mode/static/state/graph/grpcroute_test.go @@ -11,6 +11,7 @@ import ( v1 "sigs.k8s.io/gateway-api/apis/v1" "sigs.k8s.io/gateway-api/apis/v1alpha2" + ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/conditions" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/helpers" staticConds "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/conditions" @@ -127,10 +128,22 @@ func TestBuildGRPCRoutes(t *testing.T) { validator := &validationfakes.FakeHTTPFieldsValidator{} + npCfg := &ngfAPI.NginxProxy{ + Spec: ngfAPI.NginxProxySpec{ + DisableHTTP2: false, + }, + } + for _, test := range tests { t.Run(test.name, func(t *testing.T) { g := NewWithT(t) - routes := buildRoutesForGateways(validator, map[types.NamespacedName]*v1.HTTPRoute{}, grRoutes, test.gwNsNames) + routes := buildRoutesForGateways( + validator, + map[types.NamespacedName]*v1.HTTPRoute{}, + grRoutes, + test.gwNsNames, + npCfg, + ) g.Expect(helpers.Diff(test.expected, routes)).To(BeEmpty()) }) } @@ -268,10 +281,11 @@ func TestBuildGRPCRoute(t *testing.T) { } tests := []struct { - validator *validationfakes.FakeHTTPFieldsValidator - gr *v1alpha2.GRPCRoute - expected *L7Route - name string + validator *validationfakes.FakeHTTPFieldsValidator + gr *v1alpha2.GRPCRoute + expected *L7Route + name string + http2disabled bool }{ { validator: createAllValidValidator(), @@ -453,6 +467,28 @@ func TestBuildGRPCRoute(t *testing.T) { }, name: "invalid route with duplicate sectionName", }, + { + validator: createAllValidValidator(), + gr: grBoth, + expected: &L7Route{ + RouteType: RouteTypeGRPC, + Source: grBoth, + ParentRefs: []ParentRef{ + { + Idx: 0, + Gateway: gatewayNsName, + SectionName: grBoth.Spec.ParentRefs[0].SectionName, + }, + }, + Conditions: []conditions.Condition{ + staticConds.NewRouteUnsupportedConfiguration( + `HTTP2 is disabled - cannot configure GRPCRoutes`, + ), + }, + }, + http2disabled: true, + name: "invalid route with disabled http2", + }, { validator: createAllValidValidator(), gr: grOneInvalid, @@ -634,7 +670,7 @@ func TestBuildGRPCRoute(t *testing.T) { t.Run(test.name, func(t *testing.T) { g := NewWithT(t) - route := buildGRPCRoute(test.validator, test.gr, gatewayNsNames) + route := buildGRPCRoute(test.validator, test.gr, gatewayNsNames, test.http2disabled) g.Expect(helpers.Diff(test.expected, route)).To(BeEmpty()) }) } diff --git a/internal/mode/static/state/graph/httproute_test.go b/internal/mode/static/state/graph/httproute_test.go index 60af11914..0fd49b78b 100644 --- a/internal/mode/static/state/graph/httproute_test.go +++ b/internal/mode/static/state/graph/httproute_test.go @@ -146,7 +146,13 @@ func TestBuildHTTPRoutes(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { g := NewWithT(t) - routes := buildRoutesForGateways(validator, hrRoutes, map[types.NamespacedName]*v1alpha2.GRPCRoute{}, test.gwNsNames) + routes := buildRoutesForGateways( + validator, + hrRoutes, + map[types.NamespacedName]*v1alpha2.GRPCRoute{}, + test.gwNsNames, + nil, + ) g.Expect(helpers.Diff(test.expected, routes)).To(BeEmpty()) }) } diff --git a/internal/mode/static/state/graph/route_common.go b/internal/mode/static/state/graph/route_common.go index 914cbdfeb..d9bede3cb 100644 --- a/internal/mode/static/state/graph/route_common.go +++ b/internal/mode/static/state/graph/route_common.go @@ -12,6 +12,7 @@ import ( v1 "sigs.k8s.io/gateway-api/apis/v1" v1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1" "github.com/nginxinc/nginx-gateway-fabric/internal/framework/conditions" staticConds "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/conditions" "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/validation" @@ -134,6 +135,7 @@ func buildRoutesForGateways( httpRoutes map[types.NamespacedName]*v1.HTTPRoute, grpcRoutes map[types.NamespacedName]*v1alpha2.GRPCRoute, gatewayNsNames []types.NamespacedName, + npCfg *ngfAPI.NginxProxy, ) map[RouteKey]*L7Route { if len(gatewayNsNames) == 0 { return nil @@ -141,6 +143,8 @@ func buildRoutesForGateways( routes := make(map[RouteKey]*L7Route) + http2disabled := isHTTP2Disabled(npCfg) + for _, route := range httpRoutes { r := buildHTTPRoute(validator, route, gatewayNsNames) if r != nil { @@ -149,7 +153,7 @@ func buildRoutesForGateways( } for _, route := range grpcRoutes { - r := buildGRPCRoute(validator, route, gatewayNsNames) + r := buildGRPCRoute(validator, route, gatewayNsNames, http2disabled) if r != nil { routes[CreateRouteKey(route)] = r } @@ -158,6 +162,13 @@ func buildRoutesForGateways( return routes } +func isHTTP2Disabled(npCfg *ngfAPI.NginxProxy) bool { + if npCfg == nil { + return false + } + return npCfg.Spec.DisableHTTP2 +} + func buildSectionNameRefs( parentRefs []v1.ParentReference, routeNamespace string,