From 5afe0693debef39acc9d14577f567ef7a094cec4 Mon Sep 17 00:00:00 2001
From: Saloni
Date: Mon, 1 Jul 2024 18:56:35 -0600
Subject: [PATCH 01/28] add support for ipv6
---
apis/v1alpha1/nginxproxy_types.go | 21 +-
charts/nginx-gateway-fabric/values.yaml | 1 +
.../bases/gateway.nginx.org_nginxproxies.yaml | 9 +
deploy/crds.yaml | 9 +
docs/developer/quickstart.md | 19 ++
.../mode/static/nginx/config/http/config.go | 7 +
internal/mode/static/nginx/config/servers.go | 42 ++-
.../static/nginx/config/servers_template.go | 20 ++
.../mode/static/nginx/config/servers_test.go | 261 +++++++++++-------
.../mode/static/nginx/config/upstreams.go | 5 +
.../static/nginx/config/upstreams_test.go | 53 ++++
.../static/state/dataplane/configuration.go | 10 +-
.../state/dataplane/configuration_test.go | 142 ++++++++--
internal/mode/static/state/dataplane/types.go | 7 +-
.../mode/static/state/resolver/resolver.go | 10 +-
.../static/state/resolver/resolver_test.go | 26 +-
.../state/resolver/service_resolver_test.go | 7 +-
site/content/reference/api.md | 58 ++++
18 files changed, 574 insertions(+), 133 deletions(-)
diff --git a/apis/v1alpha1/nginxproxy_types.go b/apis/v1alpha1/nginxproxy_types.go
index 2c2a125946..50818c7425 100644
--- a/apis/v1alpha1/nginxproxy_types.go
+++ b/apis/v1alpha1/nginxproxy_types.go
@@ -10,7 +10,7 @@ import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
// NginxProxy is a configuration object that is attached to a GatewayClass parametersRef. It provides a way
// to configure global settings for all Gateways defined from the GatewayClass.
-type NginxProxy struct { //nolint:govet // standard field alignment, don't change it
+type NginxProxy struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
@@ -27,12 +27,31 @@ type NginxProxyList struct {
Items []NginxProxy `json:"items"`
}
+// IPFamilyType specifies the IP family to be used by the server.
+//
+// +kubebuilder:validation:Enum=dual;ipv4;ipv6
+type IPFamilyType string
+
+const (
+ // Dual specifies that the server will use both IPv4 and IPv6.
+ Dual IPFamilyType = "dual"
+ // IPv4 specifies that the server will use only IPv4.
+ IPv4 IPFamilyType = "ipv4"
+ // IPv6 specifies that the server will use only IPv6.
+ IPv6 IPFamilyType = "ipv6"
+)
+
// NginxProxySpec defines the desired state of the NginxProxy.
type NginxProxySpec struct {
// Telemetry specifies the OpenTelemetry configuration.
//
// +optional
Telemetry *Telemetry `json:"telemetry,omitempty"`
+ // IPFamily specifies the IP family to be used by the server.
+ // Default is "dual", meaning the server will use both IPv4 and IPv6.
+ //
+ // +optional
+ IPFamily IPFamilyType `json:"ipFamily,omitempty"`
// DisableHTTP2 defines if http2 should be disabled for all servers.
// Default is false, meaning http2 will be enabled for all servers.
//
diff --git a/charts/nginx-gateway-fabric/values.yaml b/charts/nginx-gateway-fabric/values.yaml
index 827d414fab..9cfc2064b2 100644
--- a/charts/nginx-gateway-fabric/values.yaml
+++ b/charts/nginx-gateway-fabric/values.yaml
@@ -92,6 +92,7 @@ nginx:
config:
{}
# disableHTTP2: false
+ # ipFamily: dual
# 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 b42d562f75..a4534f4145 100644
--- a/config/crd/bases/gateway.nginx.org_nginxproxies.yaml
+++ b/config/crd/bases/gateway.nginx.org_nginxproxies.yaml
@@ -52,6 +52,15 @@ spec:
DisableHTTP2 defines if http2 should be disabled for all servers.
Default is false, meaning http2 will be enabled for all servers.
type: boolean
+ ipFamily:
+ description: |-
+ IPFamily specifies the IP family to be used by the server.
+ Default is "dual", meaning the server will use both IPv4 and IPv6.
+ enum:
+ - dual
+ - ipv4
+ - ipv6
+ type: string
telemetry:
description: Telemetry specifies the OpenTelemetry configuration.
properties:
diff --git a/deploy/crds.yaml b/deploy/crds.yaml
index 51ae8b34bf..6a8d14c83e 100644
--- a/deploy/crds.yaml
+++ b/deploy/crds.yaml
@@ -697,6 +697,15 @@ spec:
DisableHTTP2 defines if http2 should be disabled for all servers.
Default is false, meaning http2 will be enabled for all servers.
type: boolean
+ ipFamily:
+ description: |-
+ IPFamily specifies the IP family to be used by the server.
+ Default is "dual", meaning the server will use both IPv4 and IPv6.
+ enum:
+ - dual
+ - ipv4
+ - ipv6
+ type: string
telemetry:
description: Telemetry specifies the OpenTelemetry configuration.
properties:
diff --git a/docs/developer/quickstart.md b/docs/developer/quickstart.md
index 448123a973..76a13ee476 100644
--- a/docs/developer/quickstart.md
+++ b/docs/developer/quickstart.md
@@ -134,10 +134,29 @@ This will build the docker images `nginx-gateway-fabric:` and `nginx-
1. Create a `kind` cluster:
+ To create a `kind` cluster with IPv4 enabled:
+
```makefile
make create-kind-cluster
```
+ To create a `kind` cluster with IPv6 or Dual IPFamily enabled, use this `config.yaml`:
+
+ ```yaml
+ kind: Cluster
+ apiVersion: kind.x-k8s.io/v1alpha4
+ nodes:
+ - role: control-plane
+ networking:
+ ipFamily: dual
+ # ipFamily: ipv6
+ apiServerAddress: 127.0.0.1
+ ```
+
+ ```shell
+ kind create cluster --config ~/cluster.yaml
+ ```
+
2. Load the previously built images onto your `kind` cluster:
```shell
diff --git a/internal/mode/static/nginx/config/http/config.go b/internal/mode/static/nginx/config/http/config.go
index 19e7db223d..198d295cc2 100644
--- a/internal/mode/static/nginx/config/http/config.go
+++ b/internal/mode/static/nginx/config/http/config.go
@@ -10,6 +10,13 @@ type Server struct {
IsDefaultHTTP bool
IsDefaultSSL bool
GRPC bool
+ IPFamily IPFamily
+}
+
+// IPFamily holds the IP family configuration for all servers.
+type IPFamily struct {
+ IPv4 bool
+ IPv6 bool
}
// Location holds all configuration for an HTTP location.
diff --git a/internal/mode/static/nginx/config/servers.go b/internal/mode/static/nginx/config/servers.go
index 54bbe709ed..e61ff5bd31 100644
--- a/internal/mode/static/nginx/config/servers.go
+++ b/internal/mode/static/nginx/config/servers.go
@@ -8,6 +8,7 @@ import (
"strings"
gotemplate "text/template"
+ ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1"
"github.com/nginxinc/nginx-gateway-fabric/internal/framework/helpers"
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/config/http"
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/dataplane"
@@ -58,7 +59,8 @@ var grpcBaseHeaders = []http.Header{
}
func executeServers(conf dataplane.Configuration) []executeResult {
- servers, httpMatchPairs := createServers(conf.HTTPServers, conf.SSLServers)
+ ipFamily := getIPFamily(conf.BaseHTTPConfig)
+ servers, httpMatchPairs := createServers(conf.HTTPServers, conf.SSLServers, ipFamily)
serverResult := executeResult{
dest: httpConfigFile,
@@ -86,6 +88,18 @@ func executeServers(conf dataplane.Configuration) []executeResult {
return allResults
}
+// getIPFamily returns whether the server should be configured for IPv4, IPv6, or both.
+func getIPFamily(baseHTTPConfig dataplane.BaseHTTPConfig) http.IPFamily {
+ switch ip := baseHTTPConfig.IPFamily; ip {
+ case ngfAPI.IPv4:
+ return http.IPFamily{IPv4: true}
+ case ngfAPI.IPv6:
+ return http.IPFamily{IPv6: true}
+ }
+
+ return http.IPFamily{IPv4: true, IPv6: true}
+}
+
func createAdditionFileResults(conf dataplane.Configuration) []executeResult {
uniqueAdditions := make(map[string][]byte)
@@ -141,18 +155,22 @@ func createIncludes(additions []dataplane.Addition) []string {
return includes
}
-func createServers(httpServers, sslServers []dataplane.VirtualServer) ([]http.Server, httpMatchPairs) {
+func createServers(
+ httpServers,
+ sslServers []dataplane.VirtualServer,
+ ipFamily http.IPFamily,
+) ([]http.Server, httpMatchPairs) {
servers := make([]http.Server, 0, len(httpServers)+len(sslServers))
finalMatchPairs := make(httpMatchPairs)
for serverID, s := range httpServers {
- httpServer, matchPairs := createServer(s, serverID)
+ httpServer, matchPairs := createServer(s, serverID, ipFamily)
servers = append(servers, httpServer)
maps.Copy(finalMatchPairs, matchPairs)
}
for serverID, s := range sslServers {
- sslServer, matchPair := createSSLServer(s, serverID)
+ sslServer, matchPair := createSSLServer(s, serverID, ipFamily)
servers = append(servers, sslServer)
maps.Copy(finalMatchPairs, matchPair)
}
@@ -160,11 +178,16 @@ func createServers(httpServers, sslServers []dataplane.VirtualServer) ([]http.Se
return servers, finalMatchPairs
}
-func createSSLServer(virtualServer dataplane.VirtualServer, serverID int) (http.Server, httpMatchPairs) {
+func createSSLServer(
+ virtualServer dataplane.VirtualServer,
+ serverID int,
+ ipFamily http.IPFamily,
+) (http.Server, httpMatchPairs) {
if virtualServer.IsDefault {
return http.Server{
IsDefaultSSL: true,
Port: virtualServer.Port,
+ IPFamily: ipFamily,
}, nil
}
@@ -180,14 +203,20 @@ func createSSLServer(virtualServer dataplane.VirtualServer, serverID int) (http.
Port: virtualServer.Port,
GRPC: grpc,
Includes: createIncludes(virtualServer.Additions),
+ IPFamily: ipFamily,
}, matchPairs
}
-func createServer(virtualServer dataplane.VirtualServer, serverID int) (http.Server, httpMatchPairs) {
+func createServer(
+ virtualServer dataplane.VirtualServer,
+ serverID int,
+ ipFamily http.IPFamily,
+) (http.Server, httpMatchPairs) {
if virtualServer.IsDefault {
return http.Server{
IsDefaultHTTP: true,
Port: virtualServer.Port,
+ IPFamily: ipFamily,
}, nil
}
@@ -199,6 +228,7 @@ func createServer(virtualServer dataplane.VirtualServer, serverID int) (http.Ser
Port: virtualServer.Port,
GRPC: grpc,
Includes: createIncludes(virtualServer.Additions),
+ IPFamily: ipFamily,
}, matchPairs
}
diff --git a/internal/mode/static/nginx/config/servers_template.go b/internal/mode/static/nginx/config/servers_template.go
index 465bd6fc81..22cd357ff5 100644
--- a/internal/mode/static/nginx/config/servers_template.go
+++ b/internal/mode/static/nginx/config/servers_template.go
@@ -5,13 +5,23 @@ js_preload_object matches from /etc/nginx/conf.d/matches.json;
{{- range $s := . -}}
{{ if $s.IsDefaultSSL -}}
server {
+ {{- if $s.IPFamily.IPv4 }}
listen {{ $s.Port }} ssl default_server;
+ {{- end }}
+ {{- if $s.IPFamily.IPv6 }}
+ listen [::]:{{ $s.Port }} ssl default_server;
+ {{- end }}
ssl_reject_handshake on;
}
{{- else if $s.IsDefaultHTTP }}
server {
+ {{- if $s.IPFamily.IPv4 }}
listen {{ $s.Port }} default_server;
+ {{- end }}
+ {{- if $s.IPFamily.IPv6 }}
+ listen [::]:{{ $s.Port }} default_server;
+ {{- end }}
default_type text/html;
return 404;
@@ -19,7 +29,12 @@ server {
{{- else }}
server {
{{- if $s.SSL }}
+ {{- if $s.IPFamily.IPv4 }}
listen {{ $s.Port }} ssl;
+ {{- end }}
+ {{- if $s.IPFamily.IPv6 }}
+ listen [::]:{{ $s.Port }} ssl;
+ {{- end }}
ssl_certificate {{ $s.SSL.Certificate }};
ssl_certificate_key {{ $s.SSL.CertificateKey }};
@@ -27,8 +42,13 @@ server {
return 421;
}
{{- else }}
+ {{- if $s.IPFamily.IPv4 }}
listen {{ $s.Port }};
{{- end }}
+ {{- if $s.IPFamily.IPv6 }}
+ listen [::]:{{ $s.Port }};
+ {{- end }}
+ {{- end }}
server_name {{ $s.ServerName }};
diff --git a/internal/mode/static/nginx/config/servers_test.go b/internal/mode/static/nginx/config/servers_test.go
index ae458e8f03..e8eeff37f0 100644
--- a/internal/mode/static/nginx/config/servers_test.go
+++ b/internal/mode/static/nginx/config/servers_test.go
@@ -9,131 +9,198 @@ import (
. "github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/types"
+ ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1"
"github.com/nginxinc/nginx-gateway-fabric/internal/framework/helpers"
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/config/http"
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/dataplane"
)
func TestExecuteServers(t *testing.T) {
- conf := dataplane.Configuration{
- HTTPServers: []dataplane.VirtualServer{
- {
- IsDefault: true,
- Port: 8080,
- },
- {
- Hostname: "example.com",
- Port: 8080,
- },
- {
- Hostname: "cafe.example.com",
- Port: 8080,
- Additions: []dataplane.Addition{
+ tests := []struct {
+ msg string
+ expectedHTTPConfig map[string]int
+ expectedHTTPMatchVars string
+ expectedIncludedFileConfigs map[string]string
+ config dataplane.Configuration
+ }{
+ {
+ msg: "http and ssl servers with IPv4 IP family",
+ config: dataplane.Configuration{
+ HTTPServers: []dataplane.VirtualServer{
+ {
+ IsDefault: true,
+ Port: 8080,
+ },
{
- Bytes: []byte("addition-1"),
- Identifier: "addition-1",
+ Hostname: "example.com",
+ Port: 8080,
},
},
- },
- },
- SSLServers: []dataplane.VirtualServer{
- {
- IsDefault: true,
- Port: 8443,
- },
- {
- Hostname: "example.com",
- SSL: &dataplane.SSL{
- KeyPairID: "test-keypair",
+ SSLServers: []dataplane.VirtualServer{
+ {
+ IsDefault: true,
+ Port: 8443,
+ },
+ {
+ Hostname: "example.com",
+ SSL: &dataplane.SSL{
+ KeyPairID: "test-keypair",
+ },
+ Port: 8443,
+ },
+ },
+ BaseHTTPConfig: dataplane.BaseHTTPConfig{
+ IPFamily: ngfAPI.IPv4,
},
- Port: 8443,
},
- {
- Hostname: "cafe.example.com",
- SSL: &dataplane.SSL{
- KeyPairID: "test-keypair",
+ expectedHTTPConfig: map[string]int{
+ "listen 8080 default_server;": 1,
+ "listen 8080;": 1,
+ "listen 8443 ssl default_server;": 1,
+ "listen 8443 ssl;": 1,
+ "server_name example.com;": 2,
+ "ssl_certificate /etc/nginx/secrets/test-keypair.pem;": 1,
+ "ssl_certificate_key /etc/nginx/secrets/test-keypair.pem;": 1,
+ "ssl_reject_handshake on;": 1,
+ },
+ expectedHTTPMatchVars: "{}",
+ },
+ {
+ msg: "http and ssl servers with dual IP family",
+ config: dataplane.Configuration{
+ HTTPServers: []dataplane.VirtualServer{
+ {
+ IsDefault: true,
+ Port: 8080,
+ },
+ {
+ Hostname: "example.com",
+ Port: 8080,
+ },
+ {
+ Hostname: "cafe.example.com",
+ Port: 8080,
+ Additions: []dataplane.Addition{
+ {
+ Bytes: []byte("addition-1"),
+ Identifier: "addition-1",
+ },
+ },
+ },
},
- Port: 8443,
- PathRules: []dataplane.PathRule{
+ SSLServers: []dataplane.VirtualServer{
{
- Path: "/",
- PathType: dataplane.PathTypePrefix,
- MatchRules: []dataplane.MatchRule{
+ IsDefault: true,
+ Port: 8443,
+ },
+ {
+ Hostname: "example.com",
+ SSL: &dataplane.SSL{
+ KeyPairID: "test-keypair",
+ },
+ Port: 8443,
+ },
+ {
+ Hostname: "cafe.example.com",
+ SSL: &dataplane.SSL{
+ KeyPairID: "test-keypair",
+ },
+ Port: 8443,
+ PathRules: []dataplane.PathRule{
{
- Match: dataplane.Match{},
- BackendGroup: dataplane.BackendGroup{
- Source: types.NamespacedName{Namespace: "test", Name: "route1"},
- RuleIdx: 0,
- Backends: []dataplane.Backend{
- {
- UpstreamName: "test_foo_443",
- Valid: true,
- Weight: 1,
- VerifyTLS: &dataplane.VerifyTLS{
- CertBundleID: "test-foo",
- Hostname: "test-foo.example.com",
+ Path: "/",
+ PathType: dataplane.PathTypePrefix,
+ MatchRules: []dataplane.MatchRule{
+ {
+ Match: dataplane.Match{},
+ BackendGroup: dataplane.BackendGroup{
+ Source: types.NamespacedName{Namespace: "test", Name: "route1"},
+ RuleIdx: 0,
+ Backends: []dataplane.Backend{
+ {
+ UpstreamName: "test_foo_443",
+ Valid: true,
+ Weight: 1,
+ VerifyTLS: &dataplane.VerifyTLS{
+ CertBundleID: "test-foo",
+ Hostname: "test-foo.example.com",
+ },
+ },
},
},
},
},
},
},
+ Additions: []dataplane.Addition{
+ {
+ Bytes: []byte("addition-1"),
+ Identifier: "addition-1", // duplicate
+ },
+ {
+ Bytes: []byte("addition-2"),
+ Identifier: "addition-2",
+ },
+ },
},
},
- Additions: []dataplane.Addition{
- {
- Bytes: []byte("addition-1"),
- Identifier: "addition-1", // duplicate
- },
- {
- Bytes: []byte("addition-2"),
- Identifier: "addition-2",
- },
+ BaseHTTPConfig: dataplane.BaseHTTPConfig{
+ IPFamily: ngfAPI.Dual,
},
},
+ expectedHTTPConfig: map[string]int{
+ "listen 8080 default_server;": 1,
+ "listen [::]:8080 default_server;": 1,
+ "listen 8080;": 2,
+ "listen [::]:8080;": 2,
+ "listen 8443 ssl;": 2,
+ "listen [::]:8443 ssl;": 2,
+ "listen 8443 ssl default_server;": 1,
+ "listen [::]:8443 ssl default_server;": 1,
+ "server_name example.com;": 2,
+ "server_name cafe.example.com;": 2,
+ "ssl_certificate /etc/nginx/secrets/test-keypair.pem;": 2,
+ "ssl_certificate_key /etc/nginx/secrets/test-keypair.pem;": 2,
+ "proxy_ssl_server_name on;": 1,
+ },
+ expectedHTTPMatchVars: "{}",
+ expectedIncludedFileConfigs: map[string]string{
+ includesFolder + "/addition-1.conf": "addition-1",
+ includesFolder + "/addition-2.conf": "addition-2",
+ },
},
}
- expSubStrings := map[string]int{
- "listen 8080 default_server;": 1,
- "listen 8080;": 2,
- "listen 8443 ssl;": 2,
- "listen 8443 ssl default_server;": 1,
- "server_name example.com;": 2,
- "server_name cafe.example.com;": 2,
- "ssl_certificate /etc/nginx/secrets/test-keypair.pem;": 2,
- "ssl_certificate_key /etc/nginx/secrets/test-keypair.pem;": 2,
- "proxy_ssl_server_name on;": 1,
- }
-
type assertion func(g *WithT, data string)
-
- expectedResults := map[string]assertion{
- httpConfigFile: func(g *WithT, data string) {
- for expSubStr, expCount := range expSubStrings {
- g.Expect(strings.Count(data, expSubStr)).To(Equal(expCount))
+ for _, test := range tests {
+ t.Run(test.msg, func(t *testing.T) {
+ g := NewWithT(t)
+ results := executeServers(test.config)
+ expectedResults := map[string]assertion{
+ httpConfigFile: func(g *WithT, data string) {
+ for expSubStr, expCount := range test.expectedHTTPConfig {
+ g.Expect(strings.Count(data, expSubStr)).To(Equal(expCount))
+ }
+ },
+ httpMatchVarsFile: func(g *WithT, data string) {
+ g.Expect(data).To(Equal(test.expectedHTTPMatchVars))
+ },
}
- },
- httpMatchVarsFile: func(g *WithT, data string) {
- g.Expect(data).To(Equal("{}"))
- },
- includesFolder + "/addition-1.conf": func(g *WithT, data string) {
- g.Expect(data).To(Equal("addition-1"))
- },
- includesFolder + "/addition-2.conf": func(g *WithT, data string) {
- g.Expect(data).To(Equal("addition-2"))
- },
- }
- g := NewWithT(t)
- results := executeServers(conf)
- g.Expect(results).To(HaveLen(len(expectedResults)))
+ for file, assertData := range test.expectedIncludedFileConfigs {
+ expectedResults[file] = func(g *WithT, data string) {
+ g.Expect(data).To(Equal(assertData))
+ }
+ }
- for _, res := range results {
- g.Expect(expectedResults).To(HaveKey(res.dest), "executeServers returned unexpected result destination")
+ g.Expect(results).To(HaveLen(len(expectedResults)))
- assertData := expectedResults[res.dest]
- assertData(g, string(res.data))
+ for _, res := range results {
+ g.Expect(expectedResults).To(HaveKey(res.dest), "executeServers returned unexpected result destination")
+ assertData := expectedResults[res.dest]
+ assertData(g, string(res.data))
+ }
+ })
}
}
@@ -1025,6 +1092,7 @@ func TestCreateServers(t *testing.T) {
{
IsDefaultHTTP: true,
Port: 8080,
+ IPFamily: http.IPFamily{},
},
{
ServerName: "cafe.example.com",
@@ -1035,10 +1103,12 @@ func TestCreateServers(t *testing.T) {
includesFolder + "/server-addition-1.conf",
includesFolder + "/server-addition-2.conf",
},
+ IPFamily: http.IPFamily{},
},
{
IsDefaultSSL: true,
Port: 8443,
+ IPFamily: http.IPFamily{},
},
{
ServerName: "cafe.example.com",
@@ -1053,12 +1123,13 @@ func TestCreateServers(t *testing.T) {
includesFolder + "/server-addition-1.conf",
includesFolder + "/server-addition-3.conf",
},
+ IPFamily: http.IPFamily{},
},
}
g := NewWithT(t)
- result, httpMatchPair := createServers(httpServers, sslServers)
+ result, httpMatchPair := createServers(httpServers, sslServers, http.IPFamily{})
g.Expect(httpMatchPair).To(Equal(allExpMatchPair))
g.Expect(helpers.Diff(expectedServers, result)).To(BeEmpty())
@@ -1267,7 +1338,7 @@ func TestCreateServersConflicts(t *testing.T) {
g := NewWithT(t)
- result, _ := createServers(httpServers, []dataplane.VirtualServer{})
+ result, _ := createServers(httpServers, []dataplane.VirtualServer{}, http.IPFamily{})
g.Expect(helpers.Diff(expectedServers, result)).To(BeEmpty())
})
}
diff --git a/internal/mode/static/nginx/config/upstreams.go b/internal/mode/static/nginx/config/upstreams.go
index 2e1906234e..e1c6a7ae42 100644
--- a/internal/mode/static/nginx/config/upstreams.go
+++ b/internal/mode/static/nginx/config/upstreams.go
@@ -71,6 +71,11 @@ func (g GeneratorImpl) createUpstream(up dataplane.Upstream) http.Upstream {
upstreamServers[idx] = http.UpstreamServer{
Address: fmt.Sprintf("%s:%d", ep.Address, ep.Port),
}
+ if ep.IPv6 {
+ upstreamServers[idx] = http.UpstreamServer{
+ Address: fmt.Sprintf("[%s]:%d", ep.Address, ep.Port),
+ }
+ }
}
return http.Upstream{
diff --git a/internal/mode/static/nginx/config/upstreams_test.go b/internal/mode/static/nginx/config/upstreams_test.go
index b0a631858d..351345a21f 100644
--- a/internal/mode/static/nginx/config/upstreams_test.go
+++ b/internal/mode/static/nginx/config/upstreams_test.go
@@ -35,15 +35,27 @@ func TestExecuteUpstreams(t *testing.T) {
Name: "up3",
Endpoints: []resolver.Endpoint{},
},
+ {
+ Name: "up4",
+ Endpoints: []resolver.Endpoint{
+ {
+ Address: "2001:db8::1",
+ Port: 80,
+ IPv6: true,
+ },
+ },
+ },
}
expectedSubStrings := []string{
"upstream up1",
"upstream up2",
"upstream up3",
+ "upstream up4",
"upstream invalid-backend-ref",
"server 10.0.0.0:80;",
"server 11.0.0.0:80;",
+ "server [2001:db8::1]:80",
"server unix:/var/run/nginx/nginx-502-server.sock;",
}
@@ -91,6 +103,16 @@ func TestCreateUpstreams(t *testing.T) {
Name: "up3",
Endpoints: []resolver.Endpoint{},
},
+ {
+ Name: "up4",
+ Endpoints: []resolver.Endpoint{
+ {
+ Address: "fd00:10:244:1::7",
+ Port: 80,
+ IPv6: true,
+ },
+ },
+ },
}
expUpstreams := []http.Upstream{
@@ -127,6 +149,15 @@ func TestCreateUpstreams(t *testing.T) {
},
},
},
+ {
+ Name: "up4",
+ ZoneSize: ossZoneSize,
+ Servers: []http.UpstreamServer{
+ {
+ Address: "[fd00:10:244:1::7]:80",
+ },
+ },
+ },
{
Name: invalidBackendRef,
Servers: []http.UpstreamServer{
@@ -216,6 +247,28 @@ func TestCreateUpstream(t *testing.T) {
},
msg: "multiple endpoints",
},
+ {
+ stateUpstream: dataplane.Upstream{
+ Name: "endpoint-ipv6",
+ Endpoints: []resolver.Endpoint{
+ {
+ Address: "fd00:10:244:1::7",
+ Port: 80,
+ IPv6: true,
+ },
+ },
+ },
+ expectedUpstream: http.Upstream{
+ Name: "endpoint-ipv6",
+ ZoneSize: ossZoneSize,
+ Servers: []http.UpstreamServer{
+ {
+ Address: "[fd00:10:244:1::7]:80",
+ },
+ },
+ },
+ msg: "endpoint ipv6",
+ },
}
for _, test := range tests {
diff --git a/internal/mode/static/state/dataplane/configuration.go b/internal/mode/static/state/dataplane/configuration.go
index 56569aa432..a43f338f57 100644
--- a/internal/mode/static/state/dataplane/configuration.go
+++ b/internal/mode/static/state/dataplane/configuration.go
@@ -658,7 +658,8 @@ func buildTelemetry(g *graph.Graph) Telemetry {
func buildBaseHTTPConfig(g *graph.Graph) BaseHTTPConfig {
baseConfig := BaseHTTPConfig{
// HTTP2 should be enabled by default
- HTTP2: true,
+ HTTP2: true,
+ IPFamily: ngfAPI.Dual,
}
if g.NginxProxy == nil || !g.NginxProxy.Valid {
return baseConfig
@@ -668,6 +669,13 @@ func buildBaseHTTPConfig(g *graph.Graph) BaseHTTPConfig {
baseConfig.HTTP2 = false
}
+ switch ipFamily := g.NginxProxy.Source.Spec.IPFamily; ipFamily {
+ case ngfAPI.IPv4:
+ baseConfig.IPFamily = ngfAPI.IPv4
+ case ngfAPI.IPv6:
+ baseConfig.IPFamily = ngfAPI.IPv6
+ }
+
return baseConfig
}
diff --git a/internal/mode/static/state/dataplane/configuration_test.go b/internal/mode/static/state/dataplane/configuration_test.go
index e053a42d96..8580b01b73 100644
--- a/internal/mode/static/state/dataplane/configuration_test.go
+++ b/internal/mode/static/state/dataplane/configuration_test.go
@@ -633,6 +633,27 @@ func TestBuildConfiguration(t *testing.T) {
ServiceName: helpers.GetPointer("my-svc"),
},
DisableHTTP2: true,
+ IPFamily: ngfAPI.Dual,
+ },
+ },
+ Valid: true,
+ }
+
+ nginxProxyIPv4 := &graph.NginxProxy{
+ Source: &ngfAPI.NginxProxy{
+ Spec: ngfAPI.NginxProxySpec{
+ Telemetry: &ngfAPI.Telemetry{},
+ IPFamily: ngfAPI.IPv4,
+ },
+ },
+ Valid: true,
+ }
+
+ nginxProxyIPv6 := &graph.NginxProxy{
+ Source: &ngfAPI.NginxProxy{
+ Spec: ngfAPI.NginxProxySpec{
+ Telemetry: &ngfAPI.Telemetry{},
+ IPFamily: ngfAPI.IPv6,
},
},
Valid: true,
@@ -660,7 +681,7 @@ func TestBuildConfiguration(t *testing.T) {
SSLServers: []VirtualServer{},
SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
},
msg: "no listeners and routes",
},
@@ -693,7 +714,7 @@ func TestBuildConfiguration(t *testing.T) {
SSLServers: []VirtualServer{},
SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
},
msg: "http listener with no routes",
},
@@ -757,7 +778,7 @@ func TestBuildConfiguration(t *testing.T) {
},
},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
},
msg: "http and https listeners with no valid routes",
},
@@ -821,7 +842,7 @@ func TestBuildConfiguration(t *testing.T) {
},
},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
},
msg: "https listeners with no routes",
},
@@ -855,7 +876,7 @@ func TestBuildConfiguration(t *testing.T) {
SSLServers: []VirtualServer{},
SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
},
msg: "invalid https listener with resolved secret",
},
@@ -928,7 +949,7 @@ func TestBuildConfiguration(t *testing.T) {
BackendGroups: []BackendGroup{expHR1Groups[0], expHR2Groups[0]},
SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
},
msg: "one http listener with two routes for different hostnames",
},
@@ -984,7 +1005,7 @@ func TestBuildConfiguration(t *testing.T) {
BackendGroups: []BackendGroup{expGRGroups[0]},
SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
},
msg: "one http listener with one grpc route",
},
@@ -1105,7 +1126,7 @@ func TestBuildConfiguration(t *testing.T) {
},
},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
},
msg: "two https listeners each with routes for different hostnames",
},
@@ -1266,7 +1287,7 @@ func TestBuildConfiguration(t *testing.T) {
},
},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
},
msg: "one http and one https listener with two routes with the same hostname with and without collisions",
},
@@ -1480,7 +1501,7 @@ func TestBuildConfiguration(t *testing.T) {
},
},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
},
msg: "multiple http and https listener; different ports",
@@ -1613,7 +1634,7 @@ func TestBuildConfiguration(t *testing.T) {
BackendGroups: []BackendGroup{expHR5Groups[0], expHR5Groups[1]},
SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
},
msg: "one http listener with one route with filters",
},
@@ -1716,7 +1737,7 @@ func TestBuildConfiguration(t *testing.T) {
},
},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
},
msg: "one http and one https listener with routes with valid and invalid rules",
},
@@ -1781,7 +1802,7 @@ func TestBuildConfiguration(t *testing.T) {
BackendGroups: []BackendGroup{expHR7Groups[0], expHR7Groups[1]},
SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
},
msg: "duplicate paths with different types",
},
@@ -1870,7 +1891,7 @@ func TestBuildConfiguration(t *testing.T) {
},
},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
},
msg: "two https listeners with different hostnames but same route; chooses listener with more specific hostname",
},
@@ -1947,7 +1968,7 @@ func TestBuildConfiguration(t *testing.T) {
CertBundles: map[CertBundleID]CertBundle{
"cert_bundle_test_configmap-1": []byte("cert-1"),
},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
},
msg: "https listener with httproute with backend that has a backend TLS policy attached",
},
@@ -2024,7 +2045,7 @@ func TestBuildConfiguration(t *testing.T) {
CertBundles: map[CertBundleID]CertBundle{
"cert_bundle_test_configmap-2": []byte("cert-2"),
},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
},
msg: "https listener with httproute with backend that has a backend TLS policy with binaryData attached",
},
@@ -2063,7 +2084,7 @@ func TestBuildConfiguration(t *testing.T) {
SSLServers: []VirtualServer{},
SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: false},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: false, IPFamily: ngfAPI.Dual},
Telemetry: Telemetry{
Endpoint: "my-otel.svc:4563",
Interval: "5s",
@@ -2103,6 +2124,7 @@ func TestBuildConfiguration(t *testing.T) {
Source: &ngfAPI.NginxProxy{
Spec: ngfAPI.NginxProxySpec{
DisableHTTP2: true,
+ IPFamily: ngfAPI.Dual,
Telemetry: &ngfAPI.Telemetry{
Exporter: &ngfAPI.TelemetryExporter{
Endpoint: "some-endpoint",
@@ -2122,7 +2144,7 @@ func TestBuildConfiguration(t *testing.T) {
SSLServers: []VirtualServer{},
SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
Telemetry: Telemetry{},
},
msg: "invalid NginxProxy",
@@ -2289,12 +2311,90 @@ func TestBuildConfiguration(t *testing.T) {
expHRWithPolicyGroups[0],
expHTTPSHRWithPolicyGroups[0],
},
- BaseHTTPConfig: BaseHTTPConfig{
- HTTP2: true,
- },
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
},
msg: "Simple Gateway and HTTPRoute with policies attached",
},
+ {
+ graph: &graph.Graph{
+ GatewayClass: &graph.GatewayClass{
+ Source: &v1.GatewayClass{},
+ Valid: true,
+ },
+ Gateway: &graph.Gateway{
+ Source: &v1.Gateway{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "gw",
+ Namespace: "ns",
+ },
+ },
+ Listeners: []*graph.Listener{
+ {
+ Name: "listener-80-1",
+ Source: listener80,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{},
+ },
+ },
+ },
+ Routes: map[graph.RouteKey]*graph.L7Route{},
+ NginxProxy: nginxProxyIPv4,
+ },
+ expConf: Configuration{
+ HTTPServers: []VirtualServer{
+ {
+ IsDefault: true,
+ Port: 80,
+ },
+ },
+ SSLServers: []VirtualServer{},
+ SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
+ CertBundles: map[CertBundleID]CertBundle{},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.IPv4},
+ Telemetry: Telemetry{},
+ },
+ msg: "NginxProxy with IPv4 IPFamily and no routes",
+ },
+ {
+ graph: &graph.Graph{
+ GatewayClass: &graph.GatewayClass{
+ Source: &v1.GatewayClass{},
+ Valid: true,
+ },
+ Gateway: &graph.Gateway{
+ Source: &v1.Gateway{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "gw",
+ Namespace: "ns",
+ },
+ },
+ Listeners: []*graph.Listener{
+ {
+ Name: "listener-80-1",
+ Source: listener80,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{},
+ },
+ },
+ },
+ Routes: map[graph.RouteKey]*graph.L7Route{},
+ NginxProxy: nginxProxyIPv6,
+ },
+ expConf: Configuration{
+ HTTPServers: []VirtualServer{
+ {
+ IsDefault: true,
+ Port: 80,
+ },
+ },
+ SSLServers: []VirtualServer{},
+ SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
+ CertBundles: map[CertBundleID]CertBundle{},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.IPv6},
+ Telemetry: Telemetry{},
+ },
+ msg: "NginxProxy with IPv6 IPFamily",
+ },
}
for _, test := range tests {
diff --git a/internal/mode/static/state/dataplane/types.go b/internal/mode/static/state/dataplane/types.go
index 77cd66c008..3fb99da50d 100644
--- a/internal/mode/static/state/dataplane/types.go
+++ b/internal/mode/static/state/dataplane/types.go
@@ -6,6 +6,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
+ ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1"
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/resolver"
)
@@ -33,10 +34,10 @@ type Configuration struct {
Upstreams []Upstream
// BackendGroups holds all unique BackendGroups.
BackendGroups []BackendGroup
- // Telemetry holds the Otel configuration.
- Telemetry Telemetry
// BaseHTTPConfig holds the configuration options at the http context.
BaseHTTPConfig BaseHTTPConfig
+ // Telemetry holds the Otel configuration.
+ Telemetry Telemetry
// Version represents the version of the generated configuration.
Version int
}
@@ -296,6 +297,8 @@ type SpanAttribute struct {
// BaseHTTPConfig holds the configuration options at the http context.
type BaseHTTPConfig struct {
+ // IPFamily specifies the IP family for all servers.
+ IPFamily ngfAPI.IPFamilyType
// HTTP2 specifies whether http2 should be enabled for all servers.
HTTP2 bool
}
diff --git a/internal/mode/static/state/resolver/resolver.go b/internal/mode/static/state/resolver/resolver.go
index 1200add7f3..16d62189a6 100644
--- a/internal/mode/static/state/resolver/resolver.go
+++ b/internal/mode/static/state/resolver/resolver.go
@@ -28,6 +28,8 @@ type Endpoint struct {
Address string
// Port is the port of the endpoint.
Port int32
+ // IPv6 is true if the endpoint is an IPv6 address.
+ IPv6 bool
}
// ServiceResolverImpl implements ServiceResolver.
@@ -109,6 +111,10 @@ func resolveEndpoints(
endpointSet := initEndpointsSet(filteredSlices)
for _, eps := range filteredSlices {
+ var ipv6 bool
+ if eps.AddressType == discoveryV1.AddressTypeIPv6 {
+ ipv6 = true
+ }
for _, endpoint := range eps.Endpoints {
if !endpointReady(endpoint) {
continue
@@ -119,7 +125,7 @@ func resolveEndpoints(
endpointPort := findPort(eps.Ports, svcPort)
for _, address := range endpoint.Addresses {
- ep := Endpoint{Address: address, Port: endpointPort}
+ ep := Endpoint{Address: address, Port: endpointPort, IPv6: ipv6}
endpointSet[ep] = struct{}{}
}
}
@@ -149,7 +155,7 @@ func getDefaultPort(svcPort v1.ServicePort) int32 {
}
func ignoreEndpointSlice(endpointSlice discoveryV1.EndpointSlice, port v1.ServicePort) bool {
- if endpointSlice.AddressType != discoveryV1.AddressTypeIPv4 {
+ if endpointSlice.AddressType == discoveryV1.AddressTypeFQDN {
return true
}
diff --git a/internal/mode/static/state/resolver/resolver_test.go b/internal/mode/static/state/resolver/resolver_test.go
index 1b7c248621..c6ff068633 100644
--- a/internal/mode/static/state/resolver/resolver_test.go
+++ b/internal/mode/static/state/resolver/resolver_test.go
@@ -16,13 +16,19 @@ import (
var (
svcPortName = "svc-port"
- addresses = []string{"10.0.0.1", "10.0.0.2", "10.0.0.3"}
+ addresses = []string{"10.0.0.1", "10.0.0.2", "10.0.0.3"}
+ addressesIPv6 = []string{"2001:db8::1", "2001:db8::2", "2001:db8::3"}
readyEndpoint1 = discoveryV1.Endpoint{
Addresses: addresses,
Conditions: discoveryV1.EndpointConditions{Ready: helpers.GetPointer(true)},
}
+ readyEndpoint2 = discoveryV1.Endpoint{
+ Addresses: addressesIPv6,
+ Conditions: discoveryV1.EndpointConditions{Ready: helpers.GetPointer(true)},
+ }
+
notReadyEndpoint = discoveryV1.Endpoint{
Addresses: addresses,
Conditions: discoveryV1.EndpointConditions{Ready: helpers.GetPointer(false)},
@@ -59,8 +65,19 @@ var (
},
}
- invalidAddressTypeEndpointSlice = discoveryV1.EndpointSlice{
+ validIPv6EndpointSlice = discoveryV1.EndpointSlice{
AddressType: discoveryV1.AddressTypeIPv6,
+ Endpoints: []discoveryV1.Endpoint{readyEndpoint2},
+ Ports: []discoveryV1.EndpointPort{
+ {
+ Name: &svcPortName,
+ Port: helpers.GetPointer[int32](80),
+ },
+ },
+ }
+
+ invalidAddressTypeEndpointSlice = discoveryV1.EndpointSlice{
+ AddressType: discoveryV1.AddressTypeFQDN,
Endpoints: []discoveryV1.Endpoint{readyEndpoint1},
Ports: []discoveryV1.EndpointPort{
{
@@ -86,6 +103,7 @@ func TestFilterEndpointSliceList(t *testing.T) {
sliceList := discoveryV1.EndpointSliceList{
Items: []discoveryV1.EndpointSlice{
validEndpointSlice,
+ validIPv6EndpointSlice,
invalidAddressTypeEndpointSlice,
invalidPortEndpointSlice,
nilEndpoints,
@@ -99,7 +117,7 @@ func TestFilterEndpointSliceList(t *testing.T) {
TargetPort: intstr.FromInt(80),
}
- expFilteredList := []discoveryV1.EndpointSlice{validEndpointSlice, mixedValidityEndpointSlice}
+ expFilteredList := []discoveryV1.EndpointSlice{validEndpointSlice, validIPv6EndpointSlice, mixedValidityEndpointSlice}
filteredSliceList := filterEndpointSliceList(sliceList, svcPort)
g := NewWithT(t)
@@ -171,7 +189,7 @@ func TestIgnoreEndpointSlice(t *testing.T) {
Port: 80,
TargetPort: intstr.FromInt(8080),
},
- ignore: true,
+ ignore: false,
},
{
msg: "FQDN address type",
diff --git a/internal/mode/static/state/resolver/service_resolver_test.go b/internal/mode/static/state/resolver/service_resolver_test.go
index ef605eb5ba..83573d51c5 100644
--- a/internal/mode/static/state/resolver/service_resolver_test.go
+++ b/internal/mode/static/state/resolver/service_resolver_test.go
@@ -196,6 +196,11 @@ var _ = Describe("ServiceResolver", func() {
Address: "12.0.0.1",
Port: 8080,
},
+ {
+ Address: "FE80:CD00:0:CDE:1257:0:211E:729C",
+ Port: 8080,
+ IPv6: true,
+ },
}
endpoints, err := serviceResolver.Resolve(context.TODO(), svcNsName, svcPort)
@@ -207,6 +212,7 @@ var _ = Describe("ServiceResolver", func() {
Expect(fakeK8sClient.Delete(context.TODO(), slice1)).To(Succeed())
Expect(fakeK8sClient.Delete(context.TODO(), slice2)).To(Succeed())
Expect(fakeK8sClient.Delete(context.TODO(), dupeEndpointSlice)).To(Succeed())
+ Expect(fakeK8sClient.Delete(context.TODO(), sliceIPV6)).To(Succeed())
endpoints, err := serviceResolver.Resolve(context.TODO(), svcNsName, svcPort)
Expect(err).To(HaveOccurred())
@@ -214,7 +220,6 @@ var _ = Describe("ServiceResolver", func() {
})
It("returns an error if there are no endpoint slices for the service", func() {
// delete remaining endpoint slices
- Expect(fakeK8sClient.Delete(context.TODO(), sliceIPV6)).To(Succeed())
Expect(fakeK8sClient.Delete(context.TODO(), sliceNoMatchingPortName)).To(Succeed())
endpoints, err := serviceResolver.Resolve(context.TODO(), svcNsName, svcPort)
diff --git a/site/content/reference/api.md b/site/content/reference/api.md
index fef67d181f..1f33b3419b 100644
--- a/site/content/reference/api.md
+++ b/site/content/reference/api.md
@@ -313,6 +313,21 @@ Telemetry
+ipFamily
+
+
+IPFamilyType
+
+
+ |
+
+(Optional)
+ IPFamily specifies the IP family to be used by the server.
+Default is “dual”, meaning the server will use both IPv4 and IPv6.
+ |
+
+
+
disableHTTP2
bool
@@ -715,6 +730,34 @@ Support: Gateway, HTTPRoute, GRPCRoute.
Duration can be specified in milliseconds (ms) or seconds (s) A value without a suffix is seconds.
Examples: 120s, 50ms.
+IPFamilyType
+(string alias)
+
+
+(Appears on:
+NginxProxySpec)
+
+
+ IPFamilyType specifies the IP family to be used by the server.
+
+
+
+
+Value |
+Description |
+
+
+"dual" |
+Dual specifies that the server will use both IPv4 and IPv6.
+ |
+ "ipv4" |
+IPv4 specifies that the server will use only IPv4.
+ |
+ "ipv6" |
+IPv6 specifies that the server will use only IPv6.
+ |
+
+
Logging
@@ -892,6 +935,21 @@ Telemetry
|
+ipFamily
+
+
+IPFamilyType
+
+
+ |
+
+(Optional)
+ IPFamily specifies the IP family to be used by the server.
+Default is “dual”, meaning the server will use both IPv4 and IPv6.
+ |
+
+
+
disableHTTP2
bool
From 1fe7bb162f7d2c220e327b6bf6b05786cf91db19 Mon Sep 17 00:00:00 2001
From: salonichf5 <146118978+salonichf5@users.noreply.github.com>
Date: Mon, 8 Jul 2024 16:12:54 -0600
Subject: [PATCH 02/28] Update apis/v1alpha1/nginxproxy_types.go
Co-authored-by: Michael Pleshakov
---
apis/v1alpha1/nginxproxy_types.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apis/v1alpha1/nginxproxy_types.go b/apis/v1alpha1/nginxproxy_types.go
index 50818c7425..7d60cfae91 100644
--- a/apis/v1alpha1/nginxproxy_types.go
+++ b/apis/v1alpha1/nginxproxy_types.go
@@ -27,7 +27,7 @@ type NginxProxyList struct {
Items []NginxProxy `json:"items"`
}
-// IPFamilyType specifies the IP family to be used by the server.
+// IPFamilyType specifies the IP family to be used by NGINX.
//
// +kubebuilder:validation:Enum=dual;ipv4;ipv6
type IPFamilyType string
From caafe3340a82265eeeeb410780839ced3c3729de Mon Sep 17 00:00:00 2001
From: Saloni
Date: Tue, 9 Jul 2024 13:45:13 -0600
Subject: [PATCH 03/28] updates based on reviews
---
apis/v1alpha1/nginxproxy_types.go | 15 +++---
apis/v1alpha1/zz_generated.deepcopy.go | 7 ++-
.../mode/static/nginx/config/http/config.go | 1 -
internal/mode/static/nginx/config/servers.go | 45 +++++++----------
.../static/nginx/config/servers_template.go | 18 +++----
.../mode/static/nginx/config/servers_test.go | 13 ++---
.../mode/static/nginx/config/upstreams.go | 11 ++--
.../static/state/dataplane/configuration.go | 8 +--
.../state/dataplane/configuration_test.go | 50 +++++++++----------
internal/mode/static/state/dataplane/types.go | 15 +++++-
.../mode/static/state/resolver/resolver.go | 5 +-
.../static/state/resolver/resolver_test.go | 30 +++++------
site/content/reference/api.md | 42 ++++++++--------
13 files changed, 130 insertions(+), 130 deletions(-)
diff --git a/apis/v1alpha1/nginxproxy_types.go b/apis/v1alpha1/nginxproxy_types.go
index 7d60cfae91..5445eb945f 100644
--- a/apis/v1alpha1/nginxproxy_types.go
+++ b/apis/v1alpha1/nginxproxy_types.go
@@ -10,12 +10,13 @@ import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
// NginxProxy is a configuration object that is attached to a GatewayClass parametersRef. It provides a way
// to configure global settings for all Gateways defined from the GatewayClass.
-type NginxProxy struct {
- metav1.TypeMeta `json:",inline"`
+type NginxProxy struct { //nolint:govet // standard field alignment, don't change it
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec defines the desired state of the NginxProxy.
Spec NginxProxySpec `json:"spec"`
+
+ metav1.TypeMeta `json:",inline"`
}
// +kubebuilder:object:root=true
@@ -43,15 +44,15 @@ const (
// NginxProxySpec defines the desired state of the NginxProxy.
type NginxProxySpec struct {
- // Telemetry specifies the OpenTelemetry configuration.
- //
- // +optional
- Telemetry *Telemetry `json:"telemetry,omitempty"`
// IPFamily specifies the IP family to be used by the server.
// Default is "dual", meaning the server will use both IPv4 and IPv6.
//
// +optional
- IPFamily IPFamilyType `json:"ipFamily,omitempty"`
+ IPFamily *IPFamilyType `json:"ipFamily,omitempty"`
+ // Telemetry specifies the OpenTelemetry configuration.
+ //
+ // +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.
//
diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go
index a341140561..0c61370b0c 100644
--- a/apis/v1alpha1/zz_generated.deepcopy.go
+++ b/apis/v1alpha1/zz_generated.deepcopy.go
@@ -299,9 +299,9 @@ func (in *NginxGatewayStatus) DeepCopy() *NginxGatewayStatus {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NginxProxy) DeepCopyInto(out *NginxProxy) {
*out = *in
- out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
+ out.TypeMeta = in.TypeMeta
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NginxProxy.
@@ -362,6 +362,11 @@ func (in *NginxProxySpec) DeepCopyInto(out *NginxProxySpec) {
*out = new(Telemetry)
(*in).DeepCopyInto(*out)
}
+ if in.IPFamily != nil {
+ in, out := &in.IPFamily, &out.IPFamily
+ *out = new(IPFamilyType)
+ **out = **in
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NginxProxySpec.
diff --git a/internal/mode/static/nginx/config/http/config.go b/internal/mode/static/nginx/config/http/config.go
index 198d295cc2..98594b25c4 100644
--- a/internal/mode/static/nginx/config/http/config.go
+++ b/internal/mode/static/nginx/config/http/config.go
@@ -10,7 +10,6 @@ type Server struct {
IsDefaultHTTP bool
IsDefaultSSL bool
GRPC bool
- IPFamily IPFamily
}
// IPFamily holds the IP family configuration for all servers.
diff --git a/internal/mode/static/nginx/config/servers.go b/internal/mode/static/nginx/config/servers.go
index e61ff5bd31..a9aa55521f 100644
--- a/internal/mode/static/nginx/config/servers.go
+++ b/internal/mode/static/nginx/config/servers.go
@@ -8,7 +8,6 @@ import (
"strings"
gotemplate "text/template"
- ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1"
"github.com/nginxinc/nginx-gateway-fabric/internal/framework/helpers"
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/config/http"
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/dataplane"
@@ -58,13 +57,23 @@ var grpcBaseHeaders = []http.Header{
},
}
+type serverConfig struct {
+ Servers []http.Server
+ IPFamily http.IPFamily
+}
+
func executeServers(conf dataplane.Configuration) []executeResult {
ipFamily := getIPFamily(conf.BaseHTTPConfig)
- servers, httpMatchPairs := createServers(conf.HTTPServers, conf.SSLServers, ipFamily)
+ servers, httpMatchPairs := createServers(conf.HTTPServers, conf.SSLServers)
+
+ serverConfig := serverConfig{
+ Servers: servers,
+ IPFamily: ipFamily,
+ }
serverResult := executeResult{
dest: httpConfigFile,
- data: helpers.MustExecuteTemplate(serversTemplate, servers),
+ data: helpers.MustExecuteTemplate(serversTemplate, serverConfig),
}
// create httpMatchPair conf
@@ -91,9 +100,9 @@ func executeServers(conf dataplane.Configuration) []executeResult {
// getIPFamily returns whether the server should be configured for IPv4, IPv6, or both.
func getIPFamily(baseHTTPConfig dataplane.BaseHTTPConfig) http.IPFamily {
switch ip := baseHTTPConfig.IPFamily; ip {
- case ngfAPI.IPv4:
+ case dataplane.IPv4:
return http.IPFamily{IPv4: true}
- case ngfAPI.IPv6:
+ case dataplane.IPv6:
return http.IPFamily{IPv6: true}
}
@@ -155,22 +164,18 @@ func createIncludes(additions []dataplane.Addition) []string {
return includes
}
-func createServers(
- httpServers,
- sslServers []dataplane.VirtualServer,
- ipFamily http.IPFamily,
-) ([]http.Server, httpMatchPairs) {
+func createServers(httpServers, sslServers []dataplane.VirtualServer) ([]http.Server, httpMatchPairs) {
servers := make([]http.Server, 0, len(httpServers)+len(sslServers))
finalMatchPairs := make(httpMatchPairs)
for serverID, s := range httpServers {
- httpServer, matchPairs := createServer(s, serverID, ipFamily)
+ httpServer, matchPairs := createServer(s, serverID)
servers = append(servers, httpServer)
maps.Copy(finalMatchPairs, matchPairs)
}
for serverID, s := range sslServers {
- sslServer, matchPair := createSSLServer(s, serverID, ipFamily)
+ sslServer, matchPair := createSSLServer(s, serverID)
servers = append(servers, sslServer)
maps.Copy(finalMatchPairs, matchPair)
}
@@ -178,16 +183,11 @@ func createServers(
return servers, finalMatchPairs
}
-func createSSLServer(
- virtualServer dataplane.VirtualServer,
- serverID int,
- ipFamily http.IPFamily,
-) (http.Server, httpMatchPairs) {
+func createSSLServer(virtualServer dataplane.VirtualServer, serverID int) (http.Server, httpMatchPairs) {
if virtualServer.IsDefault {
return http.Server{
IsDefaultSSL: true,
Port: virtualServer.Port,
- IPFamily: ipFamily,
}, nil
}
@@ -203,20 +203,14 @@ func createSSLServer(
Port: virtualServer.Port,
GRPC: grpc,
Includes: createIncludes(virtualServer.Additions),
- IPFamily: ipFamily,
}, matchPairs
}
-func createServer(
- virtualServer dataplane.VirtualServer,
- serverID int,
- ipFamily http.IPFamily,
-) (http.Server, httpMatchPairs) {
+func createServer(virtualServer dataplane.VirtualServer, serverID int) (http.Server, httpMatchPairs) {
if virtualServer.IsDefault {
return http.Server{
IsDefaultHTTP: true,
Port: virtualServer.Port,
- IPFamily: ipFamily,
}, nil
}
@@ -228,7 +222,6 @@ func createServer(
Port: virtualServer.Port,
GRPC: grpc,
Includes: createIncludes(virtualServer.Additions),
- IPFamily: ipFamily,
}, matchPairs
}
diff --git a/internal/mode/static/nginx/config/servers_template.go b/internal/mode/static/nginx/config/servers_template.go
index 22cd357ff5..9334d97263 100644
--- a/internal/mode/static/nginx/config/servers_template.go
+++ b/internal/mode/static/nginx/config/servers_template.go
@@ -2,13 +2,13 @@ package config
const serversTemplateText = `
js_preload_object matches from /etc/nginx/conf.d/matches.json;
-{{- range $s := . -}}
+{{- range $s := .Servers -}}
{{ if $s.IsDefaultSSL -}}
server {
- {{- if $s.IPFamily.IPv4 }}
+ {{- if $.IPFamily.IPv4 }}
listen {{ $s.Port }} ssl default_server;
{{- end }}
- {{- if $s.IPFamily.IPv6 }}
+ {{- if $.IPFamily.IPv6 }}
listen [::]:{{ $s.Port }} ssl default_server;
{{- end }}
@@ -16,10 +16,10 @@ server {
}
{{- else if $s.IsDefaultHTTP }}
server {
- {{- if $s.IPFamily.IPv4 }}
+ {{- if $.IPFamily.IPv4 }}
listen {{ $s.Port }} default_server;
{{- end }}
- {{- if $s.IPFamily.IPv6 }}
+ {{- if $.IPFamily.IPv6 }}
listen [::]:{{ $s.Port }} default_server;
{{- end }}
@@ -29,10 +29,10 @@ server {
{{- else }}
server {
{{- if $s.SSL }}
- {{- if $s.IPFamily.IPv4 }}
+ {{- if $.IPFamily.IPv4 }}
listen {{ $s.Port }} ssl;
{{- end }}
- {{- if $s.IPFamily.IPv6 }}
+ {{- if $.IPFamily.IPv6 }}
listen [::]:{{ $s.Port }} ssl;
{{- end }}
ssl_certificate {{ $s.SSL.Certificate }};
@@ -42,10 +42,10 @@ server {
return 421;
}
{{- else }}
- {{- if $s.IPFamily.IPv4 }}
+ {{- if $.IPFamily.IPv4 }}
listen {{ $s.Port }};
{{- end }}
- {{- if $s.IPFamily.IPv6 }}
+ {{- if $.IPFamily.IPv6 }}
listen [::]:{{ $s.Port }};
{{- end }}
{{- end }}
diff --git a/internal/mode/static/nginx/config/servers_test.go b/internal/mode/static/nginx/config/servers_test.go
index e8eeff37f0..c954deeb03 100644
--- a/internal/mode/static/nginx/config/servers_test.go
+++ b/internal/mode/static/nginx/config/servers_test.go
@@ -9,7 +9,6 @@ import (
. "github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/types"
- ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1"
"github.com/nginxinc/nginx-gateway-fabric/internal/framework/helpers"
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/config/http"
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/dataplane"
@@ -50,7 +49,7 @@ func TestExecuteServers(t *testing.T) {
},
},
BaseHTTPConfig: dataplane.BaseHTTPConfig{
- IPFamily: ngfAPI.IPv4,
+ IPFamily: dataplane.IPv4,
},
},
expectedHTTPConfig: map[string]int{
@@ -145,7 +144,7 @@ func TestExecuteServers(t *testing.T) {
},
},
BaseHTTPConfig: dataplane.BaseHTTPConfig{
- IPFamily: ngfAPI.Dual,
+ IPFamily: dataplane.Dual,
},
},
expectedHTTPConfig: map[string]int{
@@ -1092,7 +1091,6 @@ func TestCreateServers(t *testing.T) {
{
IsDefaultHTTP: true,
Port: 8080,
- IPFamily: http.IPFamily{},
},
{
ServerName: "cafe.example.com",
@@ -1103,12 +1101,10 @@ func TestCreateServers(t *testing.T) {
includesFolder + "/server-addition-1.conf",
includesFolder + "/server-addition-2.conf",
},
- IPFamily: http.IPFamily{},
},
{
IsDefaultSSL: true,
Port: 8443,
- IPFamily: http.IPFamily{},
},
{
ServerName: "cafe.example.com",
@@ -1123,13 +1119,12 @@ func TestCreateServers(t *testing.T) {
includesFolder + "/server-addition-1.conf",
includesFolder + "/server-addition-3.conf",
},
- IPFamily: http.IPFamily{},
},
}
g := NewWithT(t)
- result, httpMatchPair := createServers(httpServers, sslServers, http.IPFamily{})
+ result, httpMatchPair := createServers(httpServers, sslServers)
g.Expect(httpMatchPair).To(Equal(allExpMatchPair))
g.Expect(helpers.Diff(expectedServers, result)).To(BeEmpty())
@@ -1338,7 +1333,7 @@ func TestCreateServersConflicts(t *testing.T) {
g := NewWithT(t)
- result, _ := createServers(httpServers, []dataplane.VirtualServer{}, http.IPFamily{})
+ result, _ := createServers(httpServers, []dataplane.VirtualServer{})
g.Expect(helpers.Diff(expectedServers, result)).To(BeEmpty())
})
}
diff --git a/internal/mode/static/nginx/config/upstreams.go b/internal/mode/static/nginx/config/upstreams.go
index e1c6a7ae42..a76ee23a6a 100644
--- a/internal/mode/static/nginx/config/upstreams.go
+++ b/internal/mode/static/nginx/config/upstreams.go
@@ -68,13 +68,12 @@ func (g GeneratorImpl) createUpstream(up dataplane.Upstream) http.Upstream {
upstreamServers := make([]http.UpstreamServer, len(up.Endpoints))
for idx, ep := range up.Endpoints {
- upstreamServers[idx] = http.UpstreamServer{
- Address: fmt.Sprintf("%s:%d", ep.Address, ep.Port),
- }
+ format := "%s:%d"
if ep.IPv6 {
- upstreamServers[idx] = http.UpstreamServer{
- Address: fmt.Sprintf("[%s]:%d", ep.Address, ep.Port),
- }
+ format = "[%s]:%d"
+ }
+ upstreamServers[idx] = http.UpstreamServer{
+ Address: fmt.Sprintf(format, ep.Address, ep.Port),
}
}
diff --git a/internal/mode/static/state/dataplane/configuration.go b/internal/mode/static/state/dataplane/configuration.go
index a43f338f57..9a9384e783 100644
--- a/internal/mode/static/state/dataplane/configuration.go
+++ b/internal/mode/static/state/dataplane/configuration.go
@@ -659,7 +659,7 @@ func buildBaseHTTPConfig(g *graph.Graph) BaseHTTPConfig {
baseConfig := BaseHTTPConfig{
// HTTP2 should be enabled by default
HTTP2: true,
- IPFamily: ngfAPI.Dual,
+ IPFamily: Dual,
}
if g.NginxProxy == nil || !g.NginxProxy.Valid {
return baseConfig
@@ -669,11 +669,11 @@ func buildBaseHTTPConfig(g *graph.Graph) BaseHTTPConfig {
baseConfig.HTTP2 = false
}
- switch ipFamily := g.NginxProxy.Source.Spec.IPFamily; ipFamily {
+ switch ipFamily := g.NginxProxy.Source.Spec.IPFamily; *ipFamily {
case ngfAPI.IPv4:
- baseConfig.IPFamily = ngfAPI.IPv4
+ baseConfig.IPFamily = IPv4
case ngfAPI.IPv6:
- baseConfig.IPFamily = ngfAPI.IPv6
+ baseConfig.IPFamily = IPv6
}
return baseConfig
diff --git a/internal/mode/static/state/dataplane/configuration_test.go b/internal/mode/static/state/dataplane/configuration_test.go
index 8580b01b73..7562c0f8b5 100644
--- a/internal/mode/static/state/dataplane/configuration_test.go
+++ b/internal/mode/static/state/dataplane/configuration_test.go
@@ -633,7 +633,7 @@ func TestBuildConfiguration(t *testing.T) {
ServiceName: helpers.GetPointer("my-svc"),
},
DisableHTTP2: true,
- IPFamily: ngfAPI.Dual,
+ IPFamily: helpers.GetPointer(ngfAPI.IPv4),
},
},
Valid: true,
@@ -643,7 +643,7 @@ func TestBuildConfiguration(t *testing.T) {
Source: &ngfAPI.NginxProxy{
Spec: ngfAPI.NginxProxySpec{
Telemetry: &ngfAPI.Telemetry{},
- IPFamily: ngfAPI.IPv4,
+ IPFamily: helpers.GetPointer(ngfAPI.IPv4),
},
},
Valid: true,
@@ -653,7 +653,7 @@ func TestBuildConfiguration(t *testing.T) {
Source: &ngfAPI.NginxProxy{
Spec: ngfAPI.NginxProxySpec{
Telemetry: &ngfAPI.Telemetry{},
- IPFamily: ngfAPI.IPv6,
+ IPFamily: helpers.GetPointer(ngfAPI.IPv6),
},
},
Valid: true,
@@ -681,7 +681,7 @@ func TestBuildConfiguration(t *testing.T) {
SSLServers: []VirtualServer{},
SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
},
msg: "no listeners and routes",
},
@@ -714,7 +714,7 @@ func TestBuildConfiguration(t *testing.T) {
SSLServers: []VirtualServer{},
SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
},
msg: "http listener with no routes",
},
@@ -778,7 +778,7 @@ func TestBuildConfiguration(t *testing.T) {
},
},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
},
msg: "http and https listeners with no valid routes",
},
@@ -842,7 +842,7 @@ func TestBuildConfiguration(t *testing.T) {
},
},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
},
msg: "https listeners with no routes",
},
@@ -876,7 +876,7 @@ func TestBuildConfiguration(t *testing.T) {
SSLServers: []VirtualServer{},
SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
},
msg: "invalid https listener with resolved secret",
},
@@ -949,7 +949,7 @@ func TestBuildConfiguration(t *testing.T) {
BackendGroups: []BackendGroup{expHR1Groups[0], expHR2Groups[0]},
SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
},
msg: "one http listener with two routes for different hostnames",
},
@@ -1005,7 +1005,7 @@ func TestBuildConfiguration(t *testing.T) {
BackendGroups: []BackendGroup{expGRGroups[0]},
SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
},
msg: "one http listener with one grpc route",
},
@@ -1126,7 +1126,7 @@ func TestBuildConfiguration(t *testing.T) {
},
},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
},
msg: "two https listeners each with routes for different hostnames",
},
@@ -1287,7 +1287,7 @@ func TestBuildConfiguration(t *testing.T) {
},
},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
},
msg: "one http and one https listener with two routes with the same hostname with and without collisions",
},
@@ -1501,7 +1501,7 @@ func TestBuildConfiguration(t *testing.T) {
},
},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
},
msg: "multiple http and https listener; different ports",
@@ -1634,7 +1634,7 @@ func TestBuildConfiguration(t *testing.T) {
BackendGroups: []BackendGroup{expHR5Groups[0], expHR5Groups[1]},
SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
},
msg: "one http listener with one route with filters",
},
@@ -1737,7 +1737,7 @@ func TestBuildConfiguration(t *testing.T) {
},
},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
},
msg: "one http and one https listener with routes with valid and invalid rules",
},
@@ -1802,7 +1802,7 @@ func TestBuildConfiguration(t *testing.T) {
BackendGroups: []BackendGroup{expHR7Groups[0], expHR7Groups[1]},
SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
},
msg: "duplicate paths with different types",
},
@@ -1891,7 +1891,7 @@ func TestBuildConfiguration(t *testing.T) {
},
},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
},
msg: "two https listeners with different hostnames but same route; chooses listener with more specific hostname",
},
@@ -1968,7 +1968,7 @@ func TestBuildConfiguration(t *testing.T) {
CertBundles: map[CertBundleID]CertBundle{
"cert_bundle_test_configmap-1": []byte("cert-1"),
},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
},
msg: "https listener with httproute with backend that has a backend TLS policy attached",
},
@@ -2045,7 +2045,7 @@ func TestBuildConfiguration(t *testing.T) {
CertBundles: map[CertBundleID]CertBundle{
"cert_bundle_test_configmap-2": []byte("cert-2"),
},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
},
msg: "https listener with httproute with backend that has a backend TLS policy with binaryData attached",
},
@@ -2084,7 +2084,7 @@ func TestBuildConfiguration(t *testing.T) {
SSLServers: []VirtualServer{},
SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: false, IPFamily: ngfAPI.Dual},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: false, IPFamily: IPv4},
Telemetry: Telemetry{
Endpoint: "my-otel.svc:4563",
Interval: "5s",
@@ -2124,7 +2124,7 @@ func TestBuildConfiguration(t *testing.T) {
Source: &ngfAPI.NginxProxy{
Spec: ngfAPI.NginxProxySpec{
DisableHTTP2: true,
- IPFamily: ngfAPI.Dual,
+ IPFamily: helpers.GetPointer(ngfAPI.Dual),
Telemetry: &ngfAPI.Telemetry{
Exporter: &ngfAPI.TelemetryExporter{
Endpoint: "some-endpoint",
@@ -2144,7 +2144,7 @@ func TestBuildConfiguration(t *testing.T) {
SSLServers: []VirtualServer{},
SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
Telemetry: Telemetry{},
},
msg: "invalid NginxProxy",
@@ -2311,7 +2311,7 @@ func TestBuildConfiguration(t *testing.T) {
expHRWithPolicyGroups[0],
expHTTPSHRWithPolicyGroups[0],
},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.Dual},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
},
msg: "Simple Gateway and HTTPRoute with policies attached",
},
@@ -2350,7 +2350,7 @@ func TestBuildConfiguration(t *testing.T) {
SSLServers: []VirtualServer{},
SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.IPv4},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: IPv4},
Telemetry: Telemetry{},
},
msg: "NginxProxy with IPv4 IPFamily and no routes",
@@ -2390,7 +2390,7 @@ func TestBuildConfiguration(t *testing.T) {
SSLServers: []VirtualServer{},
SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: ngfAPI.IPv6},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: IPv6},
Telemetry: Telemetry{},
},
msg: "NginxProxy with IPv6 IPFamily",
diff --git a/internal/mode/static/state/dataplane/types.go b/internal/mode/static/state/dataplane/types.go
index 3fb99da50d..d342ff3b0c 100644
--- a/internal/mode/static/state/dataplane/types.go
+++ b/internal/mode/static/state/dataplane/types.go
@@ -6,7 +6,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
- ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1"
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/resolver"
)
@@ -298,11 +297,23 @@ type SpanAttribute struct {
// BaseHTTPConfig holds the configuration options at the http context.
type BaseHTTPConfig struct {
// IPFamily specifies the IP family for all servers.
- IPFamily ngfAPI.IPFamilyType
+ IPFamily IPFamilyType
// HTTP2 specifies whether http2 should be enabled for all servers.
HTTP2 bool
}
+// IPFamilyType specifies the IP family to be used by NGINX.
+type IPFamilyType string
+
+const (
+ // Dual specifies that the server will use both IPv4 and IPv6.
+ Dual IPFamilyType = "dual"
+ // IPv4 specifies that the server will use only IPv4.
+ IPv4 IPFamilyType = "ipv4"
+ // IPv6 specifies that the server will use only IPv6.
+ IPv6 IPFamilyType = "ipv6"
+)
+
// Ratio represents a tracing sampling ratio used in an nginx config with the otel_module.
type Ratio struct {
// Name is based on the associated ObservabilityPolicy's NamespacedName,
diff --git a/internal/mode/static/state/resolver/resolver.go b/internal/mode/static/state/resolver/resolver.go
index 16d62189a6..79dec3b338 100644
--- a/internal/mode/static/state/resolver/resolver.go
+++ b/internal/mode/static/state/resolver/resolver.go
@@ -111,10 +111,7 @@ func resolveEndpoints(
endpointSet := initEndpointsSet(filteredSlices)
for _, eps := range filteredSlices {
- var ipv6 bool
- if eps.AddressType == discoveryV1.AddressTypeIPv6 {
- ipv6 = true
- }
+ ipv6 := eps.AddressType == discoveryV1.AddressTypeIPv6
for _, endpoint := range eps.Endpoints {
if !endpointReady(endpoint) {
continue
diff --git a/internal/mode/static/state/resolver/resolver_test.go b/internal/mode/static/state/resolver/resolver_test.go
index c6ff068633..2763893f2c 100644
--- a/internal/mode/static/state/resolver/resolver_test.go
+++ b/internal/mode/static/state/resolver/resolver_test.go
@@ -174,9 +174,9 @@ func TestIgnoreEndpointSlice(t *testing.T) {
ignore bool
}{
{
- msg: "IPV6 address type",
+ msg: "FQDN address type",
slice: discoveryV1.EndpointSlice{
- AddressType: discoveryV1.AddressTypeIPv6,
+ AddressType: discoveryV1.AddressTypeFQDN,
Ports: []discoveryV1.EndpointPort{
{
Name: &svcPortName,
@@ -189,16 +189,16 @@ func TestIgnoreEndpointSlice(t *testing.T) {
Port: 80,
TargetPort: intstr.FromInt(8080),
},
- ignore: false,
+ ignore: true,
},
{
- msg: "FQDN address type",
+ msg: "no matching port",
slice: discoveryV1.EndpointSlice{
- AddressType: discoveryV1.AddressTypeFQDN,
+ AddressType: discoveryV1.AddressTypeIPv4,
Ports: []discoveryV1.EndpointPort{
{
- Name: &svcPortName,
- Port: &port8080,
+ Name: helpers.GetPointer("other-svc-port"),
+ Port: &port4000,
},
},
},
@@ -210,13 +210,12 @@ func TestIgnoreEndpointSlice(t *testing.T) {
ignore: true,
},
{
- msg: "no matching port",
+ msg: "nil endpoint port",
slice: discoveryV1.EndpointSlice{
AddressType: discoveryV1.AddressTypeIPv4,
Ports: []discoveryV1.EndpointPort{
{
- Name: helpers.GetPointer("other-svc-port"),
- Port: &port4000,
+ Port: nil,
},
},
},
@@ -225,15 +224,16 @@ func TestIgnoreEndpointSlice(t *testing.T) {
Port: 80,
TargetPort: intstr.FromInt(8080),
},
- ignore: true,
+ ignore: false,
},
{
- msg: "nil endpoint port",
+ msg: "normal",
slice: discoveryV1.EndpointSlice{
AddressType: discoveryV1.AddressTypeIPv4,
Ports: []discoveryV1.EndpointPort{
{
- Port: nil,
+ Name: &svcPortName,
+ Port: &port8080,
},
},
},
@@ -245,9 +245,9 @@ func TestIgnoreEndpointSlice(t *testing.T) {
ignore: false,
},
{
- msg: "normal",
+ msg: "normal IPV6 address type",
slice: discoveryV1.EndpointSlice{
- AddressType: discoveryV1.AddressTypeIPv4,
+ AddressType: discoveryV1.AddressTypeIPv6,
Ports: []discoveryV1.EndpointPort{
{
Name: &svcPortName,
diff --git a/site/content/reference/api.md b/site/content/reference/api.md
index 1f33b3419b..10d794dd1c 100644
--- a/site/content/reference/api.md
+++ b/site/content/reference/api.md
@@ -313,30 +313,30 @@ Telemetry
|
-ipFamily
+disableHTTP2
-
-IPFamilyType
-
+bool
|
(Optional)
- IPFamily specifies the IP family to be used by the server.
-Default is “dual”, meaning the server will use both IPv4 and IPv6.
+DisableHTTP2 defines if http2 should be disabled for all servers.
+Default is false, meaning http2 will be enabled for all servers.
|
-disableHTTP2
+ipFamily
-bool
+
+IPFamilyType
+
|
(Optional)
- DisableHTTP2 defines if http2 should be disabled for all servers.
-Default is false, meaning http2 will be enabled for all servers.
+IPFamily specifies the IP family to be used by the server.
+Default is “dual”, meaning the server will use both IPv4 and IPv6.
|
@@ -738,7 +738,7 @@ Examples: 120s, 50ms.
NginxProxySpec)
-
IPFamilyType specifies the IP family to be used by the server.
+IPFamilyType specifies the IP family to be used by NGINX.
@@ -935,30 +935,30 @@ Telemetry
-ipFamily
+disableHTTP2
-
-IPFamilyType
-
+bool
|
(Optional)
- IPFamily specifies the IP family to be used by the server.
-Default is “dual”, meaning the server will use both IPv4 and IPv6.
+DisableHTTP2 defines if http2 should be disabled for all servers.
+Default is false, meaning http2 will be enabled for all servers.
|
-disableHTTP2
+ipFamily
-bool
+
+IPFamilyType
+
|
(Optional)
- DisableHTTP2 defines if http2 should be disabled for all servers.
-Default is false, meaning http2 will be enabled for all servers.
+IPFamily specifies the IP family to be used by the server.
+Default is “dual”, meaning the server will use both IPv4 and IPv6.
|
From 6c96e949a70c1c25942770840c5dbc7111be9d27 Mon Sep 17 00:00:00 2001
From: Saloni
Date: Tue, 9 Jul 2024 16:59:27 -0600
Subject: [PATCH 04/28] edit kind cluster creation with dual stack
---
Makefile | 3 +-
apis/v1alpha1/nginxproxy_types.go | 1 +
apis/v1alpha1/zz_generated.deepcopy.go | 10 ++---
config/cluster/kind-cluster.yaml | 9 +++++
docs/developer/quickstart.md | 11 ++---
site/content/reference/api.md | 56 +++++++++++++-------------
6 files changed, 48 insertions(+), 42 deletions(-)
create mode 100644 config/cluster/kind-cluster.yaml
diff --git a/Makefile b/Makefile
index f26c1aef5e..af7cda6c20 100644
--- a/Makefile
+++ b/Makefile
@@ -7,6 +7,7 @@ MANIFEST_DIR = $(CURDIR)/deploy/manifests
CHART_DIR = $(SELF_DIR)charts/nginx-gateway-fabric
NGINX_CONF_DIR = internal/mode/static/nginx/conf
NJS_DIR = internal/mode/static/nginx/modules/src
+KIND_CONFIG_FILE = $(SELF_DIR)config/cluster/kind-cluster.yaml
NGINX_DOCKER_BUILD_PLUS_ARGS = --secret id=nginx-repo.crt,src=nginx-repo.crt --secret id=nginx-repo.key,src=nginx-repo.key
BUILD_AGENT=local
PLUS_ENABLED ?= false
@@ -160,7 +161,7 @@ deps: ## Add missing and remove unused modules, verify deps and download them to
.PHONY: create-kind-cluster
create-kind-cluster: ## Create a kind cluster
$(eval KIND_IMAGE=$(shell grep -m1 'FROM kindest/node' <$(SELF_DIR)tests/Dockerfile | awk -F'[ ]' '{print $$2}'))
- kind create cluster --image $(KIND_IMAGE)
+ kind create cluster --image $(KIND_IMAGE) --config $(KIND_CONFIG_FILE)
.PHONY: delete-kind-cluster
delete-kind-cluster: ## Delete kind cluster
diff --git a/apis/v1alpha1/nginxproxy_types.go b/apis/v1alpha1/nginxproxy_types.go
index 5445eb945f..eab725b6d2 100644
--- a/apis/v1alpha1/nginxproxy_types.go
+++ b/apis/v1alpha1/nginxproxy_types.go
@@ -31,6 +31,7 @@ type NginxProxyList struct {
// IPFamilyType specifies the IP family to be used by NGINX.
//
// +kubebuilder:validation:Enum=dual;ipv4;ipv6
+// +kubebuilder:default:=dual
type IPFamilyType string
const (
diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go
index 0c61370b0c..58e50ed64a 100644
--- a/apis/v1alpha1/zz_generated.deepcopy.go
+++ b/apis/v1alpha1/zz_generated.deepcopy.go
@@ -357,16 +357,16 @@ func (in *NginxProxyList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NginxProxySpec) DeepCopyInto(out *NginxProxySpec) {
*out = *in
- if in.Telemetry != nil {
- in, out := &in.Telemetry, &out.Telemetry
- *out = new(Telemetry)
- (*in).DeepCopyInto(*out)
- }
if in.IPFamily != nil {
in, out := &in.IPFamily, &out.IPFamily
*out = new(IPFamilyType)
**out = **in
}
+ if in.Telemetry != nil {
+ in, out := &in.Telemetry, &out.Telemetry
+ *out = new(Telemetry)
+ (*in).DeepCopyInto(*out)
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NginxProxySpec.
diff --git a/config/cluster/kind-cluster.yaml b/config/cluster/kind-cluster.yaml
new file mode 100644
index 0000000000..ce0de55cf1
--- /dev/null
+++ b/config/cluster/kind-cluster.yaml
@@ -0,0 +1,9 @@
+kind: Cluster
+apiVersion: kind.x-k8s.io/v1alpha4
+nodes:
+- role: control-plane
+networking:
+ ipFamily: dual
+ # ipFamily: ipv6
+ # ipFamily: ipv4
+ apiServerAddress: 127.0.0.1
diff --git a/docs/developer/quickstart.md b/docs/developer/quickstart.md
index 76a13ee476..e64706b174 100644
--- a/docs/developer/quickstart.md
+++ b/docs/developer/quickstart.md
@@ -134,13 +134,13 @@ This will build the docker images `nginx-gateway-fabric:` and `nginx-
1. Create a `kind` cluster:
- To create a `kind` cluster with IPv4 enabled:
+ To create a `kind` cluster with dual (IPv4 and IPv6) enabled:
```makefile
make create-kind-cluster
```
- To create a `kind` cluster with IPv6 or Dual IPFamily enabled, use this `config.yaml`:
+ To create a `kind` cluster with IPv6 or IPv4 only, edit kind cluster config located at `nginx-gateway-fabric/config/cluster/kind-cluster.yaml`:
```yaml
kind: Cluster
@@ -148,15 +148,10 @@ This will build the docker images `nginx-gateway-fabric:` and `nginx-
nodes:
- role: control-plane
networking:
- ipFamily: dual
- # ipFamily: ipv6
+ ipFamily: ipv6
apiServerAddress: 127.0.0.1
```
- ```shell
- kind create cluster --config ~/cluster.yaml
- ```
-
2. Load the previously built images onto your `kind` cluster:
```shell
diff --git a/site/content/reference/api.md b/site/content/reference/api.md
index 10d794dd1c..e16aac444d 100644
--- a/site/content/reference/api.md
+++ b/site/content/reference/api.md
@@ -299,44 +299,44 @@ NginxProxySpec
-telemetry
+ipFamily
-
-Telemetry
+
+IPFamilyType
|
(Optional)
- Telemetry specifies the OpenTelemetry configuration.
+IPFamily specifies the IP family to be used by the server.
+Default is “dual”, meaning the server will use both IPv4 and IPv6.
|
-disableHTTP2
+telemetry
-bool
+
+Telemetry
+
|
(Optional)
- DisableHTTP2 defines if http2 should be disabled for all servers.
-Default is false, meaning http2 will be enabled for all servers.
+Telemetry specifies the OpenTelemetry configuration.
|
-ipFamily
+disableHTTP2
-
-IPFamilyType
-
+bool
|
(Optional)
- IPFamily specifies the IP family to be used by the server.
-Default is “dual”, meaning the server will use both IPv4 and IPv6.
+DisableHTTP2 defines if http2 should be disabled for all servers.
+Default is false, meaning http2 will be enabled for all servers.
|
@@ -921,44 +921,44 @@ Logging
-telemetry
+ipFamily
-
-Telemetry
+
+IPFamilyType
|
(Optional)
- Telemetry specifies the OpenTelemetry configuration.
+IPFamily specifies the IP family to be used by the server.
+Default is “dual”, meaning the server will use both IPv4 and IPv6.
|
-disableHTTP2
+telemetry
-bool
+
+Telemetry
+
|
(Optional)
- DisableHTTP2 defines if http2 should be disabled for all servers.
-Default is false, meaning http2 will be enabled for all servers.
+Telemetry specifies the OpenTelemetry configuration.
|
-ipFamily
+disableHTTP2
-
-IPFamilyType
-
+bool
|
(Optional)
- IPFamily specifies the IP family to be used by the server.
-Default is “dual”, meaning the server will use both IPv4 and IPv6.
+DisableHTTP2 defines if http2 should be disabled for all servers.
+Default is false, meaning http2 will be enabled for all servers.
|
From 0a5ead0c8a00759dd6a696bf6091ec2032f81d3c Mon Sep 17 00:00:00 2001
From: Saloni
Date: Tue, 9 Jul 2024 17:01:18 -0600
Subject: [PATCH 05/28] fix fieldalignment
---
apis/v1alpha1/nginxproxy_types.go | 3 +--
apis/v1alpha1/zz_generated.deepcopy.go | 2 +-
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/apis/v1alpha1/nginxproxy_types.go b/apis/v1alpha1/nginxproxy_types.go
index eab725b6d2..1dce407c2c 100644
--- a/apis/v1alpha1/nginxproxy_types.go
+++ b/apis/v1alpha1/nginxproxy_types.go
@@ -11,12 +11,11 @@ import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
// NginxProxy is a configuration object that is attached to a GatewayClass parametersRef. It provides a way
// to configure global settings for all Gateways defined from the GatewayClass.
type NginxProxy struct { //nolint:govet // standard field alignment, don't change it
+ metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec defines the desired state of the NginxProxy.
Spec NginxProxySpec `json:"spec"`
-
- metav1.TypeMeta `json:",inline"`
}
// +kubebuilder:object:root=true
diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go
index 58e50ed64a..90cdca7a41 100644
--- a/apis/v1alpha1/zz_generated.deepcopy.go
+++ b/apis/v1alpha1/zz_generated.deepcopy.go
@@ -299,9 +299,9 @@ func (in *NginxGatewayStatus) DeepCopy() *NginxGatewayStatus {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NginxProxy) DeepCopyInto(out *NginxProxy) {
*out = *in
+ out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
- out.TypeMeta = in.TypeMeta
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NginxProxy.
From 8a930d72875fe206574782291fb9499e1bcf4b8f Mon Sep 17 00:00:00 2001
From: Saloni
Date: Wed, 10 Jul 2024 13:28:52 -0600
Subject: [PATCH 06/28] updates based on reviews
---
apis/v1alpha1/nginxproxy_types.go | 2 +-
.../bases/gateway.nginx.org_nginxproxies.yaml | 2 +-
deploy/crds.yaml | 2 +-
.../mode/static/nginx/config/http/config.go | 8 ++++-
internal/mode/static/nginx/config/servers.go | 7 +---
.../static/nginx/config/servers_template.go | 16 ++++-----
.../mode/static/nginx/config/servers_test.go | 33 +++++++++++++++++++
site/content/reference/api.md | 4 +--
8 files changed, 54 insertions(+), 20 deletions(-)
diff --git a/apis/v1alpha1/nginxproxy_types.go b/apis/v1alpha1/nginxproxy_types.go
index 1dce407c2c..881c4cccbc 100644
--- a/apis/v1alpha1/nginxproxy_types.go
+++ b/apis/v1alpha1/nginxproxy_types.go
@@ -44,7 +44,7 @@ const (
// NginxProxySpec defines the desired state of the NginxProxy.
type NginxProxySpec struct {
- // IPFamily specifies the IP family to be used by the server.
+ // IPFamily specifies the IP family to be used by the NGINX.
// Default is "dual", meaning the server will use both IPv4 and IPv6.
//
// +optional
diff --git a/config/crd/bases/gateway.nginx.org_nginxproxies.yaml b/config/crd/bases/gateway.nginx.org_nginxproxies.yaml
index a4534f4145..bed5896b82 100644
--- a/config/crd/bases/gateway.nginx.org_nginxproxies.yaml
+++ b/config/crd/bases/gateway.nginx.org_nginxproxies.yaml
@@ -54,7 +54,7 @@ spec:
type: boolean
ipFamily:
description: |-
- IPFamily specifies the IP family to be used by the server.
+ IPFamily specifies the IP family to be used by the NGINX.
Default is "dual", meaning the server will use both IPv4 and IPv6.
enum:
- dual
diff --git a/deploy/crds.yaml b/deploy/crds.yaml
index 6a8d14c83e..9f2fec33e8 100644
--- a/deploy/crds.yaml
+++ b/deploy/crds.yaml
@@ -699,7 +699,7 @@ spec:
type: boolean
ipFamily:
description: |-
- IPFamily specifies the IP family to be used by the server.
+ IPFamily specifies the IP family to be used by the NGINX.
Default is "dual", meaning the server will use both IPv4 and IPv6.
enum:
- dual
diff --git a/internal/mode/static/nginx/config/http/config.go b/internal/mode/static/nginx/config/http/config.go
index 98594b25c4..9326ebb439 100644
--- a/internal/mode/static/nginx/config/http/config.go
+++ b/internal/mode/static/nginx/config/http/config.go
@@ -12,7 +12,7 @@ type Server struct {
GRPC bool
}
-// IPFamily holds the IP family configuration for all servers.
+// IPFamily holds the IP family configuration to be used by NGINX.
type IPFamily struct {
IPv4 bool
IPv6 bool
@@ -112,3 +112,9 @@ type ProxySSLVerify struct {
TrustedCertificate string
Name string
}
+
+// ServerConfig holds configuration for an HTTP server and IP family to be used by NGINX.
+type ServerConfig struct {
+ Servers []Server
+ IPFamily IPFamily
+}
diff --git a/internal/mode/static/nginx/config/servers.go b/internal/mode/static/nginx/config/servers.go
index a9aa55521f..f56bdcf60b 100644
--- a/internal/mode/static/nginx/config/servers.go
+++ b/internal/mode/static/nginx/config/servers.go
@@ -57,16 +57,11 @@ var grpcBaseHeaders = []http.Header{
},
}
-type serverConfig struct {
- Servers []http.Server
- IPFamily http.IPFamily
-}
-
func executeServers(conf dataplane.Configuration) []executeResult {
ipFamily := getIPFamily(conf.BaseHTTPConfig)
servers, httpMatchPairs := createServers(conf.HTTPServers, conf.SSLServers)
- serverConfig := serverConfig{
+ serverConfig := http.ServerConfig{
Servers: servers,
IPFamily: ipFamily,
}
diff --git a/internal/mode/static/nginx/config/servers_template.go b/internal/mode/static/nginx/config/servers_template.go
index 9334d97263..4d8196b180 100644
--- a/internal/mode/static/nginx/config/servers_template.go
+++ b/internal/mode/static/nginx/config/servers_template.go
@@ -29,12 +29,12 @@ server {
{{- else }}
server {
{{- if $s.SSL }}
- {{- if $.IPFamily.IPv4 }}
+ {{- if $.IPFamily.IPv4 }}
listen {{ $s.Port }} ssl;
- {{- end }}
- {{- if $.IPFamily.IPv6 }}
+ {{- end }}
+ {{- if $.IPFamily.IPv6 }}
listen [::]:{{ $s.Port }} ssl;
- {{- end }}
+ {{- end }}
ssl_certificate {{ $s.SSL.Certificate }};
ssl_certificate_key {{ $s.SSL.CertificateKey }};
@@ -42,12 +42,12 @@ server {
return 421;
}
{{- else }}
- {{- if $.IPFamily.IPv4 }}
+ {{- if $.IPFamily.IPv4 }}
listen {{ $s.Port }};
- {{- end }}
- {{- if $.IPFamily.IPv6 }}
+ {{- end }}
+ {{- if $.IPFamily.IPv6 }}
listen [::]:{{ $s.Port }};
- {{- end }}
+ {{- end }}
{{- end }}
server_name {{ $s.ServerName }};
diff --git a/internal/mode/static/nginx/config/servers_test.go b/internal/mode/static/nginx/config/servers_test.go
index c954deeb03..cedba6c24c 100644
--- a/internal/mode/static/nginx/config/servers_test.go
+++ b/internal/mode/static/nginx/config/servers_test.go
@@ -2581,3 +2581,36 @@ func TestAdditionFilename(t *testing.T) {
name := createAdditionFileName(dataplane.Addition{Identifier: "my-addition"})
g.Expect(name).To(Equal(includesFolder + "/" + "my-addition.conf"))
}
+
+func TestGetIPFamily(t *testing.T) {
+ test := []struct {
+ msg string
+ baseHTTPConfig dataplane.BaseHTTPConfig
+ expected http.IPFamily
+ }{
+ {
+ msg: "ipv4",
+ baseHTTPConfig: dataplane.BaseHTTPConfig{IPFamily: dataplane.IPv4},
+ expected: http.IPFamily{IPv4: true, IPv6: false},
+ },
+ {
+ msg: "ipv6",
+ baseHTTPConfig: dataplane.BaseHTTPConfig{IPFamily: dataplane.IPv6},
+ expected: http.IPFamily{IPv4: false, IPv6: true},
+ },
+ {
+ msg: "dual",
+ baseHTTPConfig: dataplane.BaseHTTPConfig{IPFamily: dataplane.Dual},
+ expected: http.IPFamily{IPv4: true, IPv6: true},
+ },
+ }
+
+ for _, tc := range test {
+ t.Run(tc.msg, func(t *testing.T) {
+ g := NewWithT(t)
+
+ result := getIPFamily(tc.baseHTTPConfig)
+ g.Expect(result).To(Equal(tc.expected))
+ })
+ }
+}
diff --git a/site/content/reference/api.md b/site/content/reference/api.md
index e16aac444d..9c509b34a5 100644
--- a/site/content/reference/api.md
+++ b/site/content/reference/api.md
@@ -308,7 +308,7 @@ IPFamilyType
(Optional)
- IPFamily specifies the IP family to be used by the server.
+ IPFamily specifies the IP family to be used by the NGINX.
Default is “dual”, meaning the server will use both IPv4 and IPv6.
|
@@ -930,7 +930,7 @@ IPFamilyType
(Optional)
- IPFamily specifies the IP family to be used by the server.
+ IPFamily specifies the IP family to be used by the NGINX.
Default is “dual”, meaning the server will use both IPv4 and IPv6.
|
From 2f03c33d80c6ffe1bff1b879565afc71e160ad39 Mon Sep 17 00:00:00 2001
From: Saloni
Date: Wed, 10 Jul 2024 13:38:15 -0600
Subject: [PATCH 07/28] update unit test
---
internal/mode/static/nginx/config/servers_test.go | 1 -
1 file changed, 1 deletion(-)
diff --git a/internal/mode/static/nginx/config/servers_test.go b/internal/mode/static/nginx/config/servers_test.go
index cedba6c24c..1e991a337e 100644
--- a/internal/mode/static/nginx/config/servers_test.go
+++ b/internal/mode/static/nginx/config/servers_test.go
@@ -2608,7 +2608,6 @@ func TestGetIPFamily(t *testing.T) {
for _, tc := range test {
t.Run(tc.msg, func(t *testing.T) {
g := NewWithT(t)
-
result := getIPFamily(tc.baseHTTPConfig)
g.Expect(result).To(Equal(tc.expected))
})
From a19793cc499c452e5aa33cd22a8d89c55b890dfc Mon Sep 17 00:00:00 2001
From: Saloni
Date: Thu, 11 Jul 2024 11:00:42 -0600
Subject: [PATCH 08/28] update endpoint verification based on NGINX IP Family
---
apis/v1alpha1/nginxproxy_types.go | 8 +-
.../bases/gateway.nginx.org_nginxproxies.yaml | 1 +
deploy/crds.yaml | 1 +
.../static/state/dataplane/configuration.go | 29 +++++--
.../state/dataplane/configuration_test.go | 51 +++++++++++-
.../mode/static/state/resolver/resolver.go | 39 ++++++++--
.../static/state/resolver/resolver_test.go | 78 +++++++++++++++----
.../resolverfakes/fake_service_resolver.go | 20 +++--
.../state/resolver/service_resolver_test.go | 10 +--
site/content/reference/api.md | 6 +-
10 files changed, 192 insertions(+), 51 deletions(-)
diff --git a/apis/v1alpha1/nginxproxy_types.go b/apis/v1alpha1/nginxproxy_types.go
index 881c4cccbc..018911da7e 100644
--- a/apis/v1alpha1/nginxproxy_types.go
+++ b/apis/v1alpha1/nginxproxy_types.go
@@ -30,15 +30,14 @@ type NginxProxyList struct {
// IPFamilyType specifies the IP family to be used by NGINX.
//
// +kubebuilder:validation:Enum=dual;ipv4;ipv6
-// +kubebuilder:default:=dual
type IPFamilyType string
const (
- // Dual specifies that the server will use both IPv4 and IPv6.
+ // Dual specifies that NGINX will use both IPv4 and IPv6.
Dual IPFamilyType = "dual"
- // IPv4 specifies that the server will use only IPv4.
+ // IPv4 specifies that NGINX will use only IPv4.
IPv4 IPFamilyType = "ipv4"
- // IPv6 specifies that the server will use only IPv6.
+ // IPv6 specifies that NGINX will use only IPv6.
IPv6 IPFamilyType = "ipv6"
)
@@ -48,6 +47,7 @@ type NginxProxySpec struct {
// Default is "dual", meaning the server will use both IPv4 and IPv6.
//
// +optional
+ // +kubebuilder:default:=dual
IPFamily *IPFamilyType `json:"ipFamily,omitempty"`
// Telemetry specifies the OpenTelemetry configuration.
//
diff --git a/config/crd/bases/gateway.nginx.org_nginxproxies.yaml b/config/crd/bases/gateway.nginx.org_nginxproxies.yaml
index bed5896b82..73acae5e84 100644
--- a/config/crd/bases/gateway.nginx.org_nginxproxies.yaml
+++ b/config/crd/bases/gateway.nginx.org_nginxproxies.yaml
@@ -53,6 +53,7 @@ spec:
Default is false, meaning http2 will be enabled for all servers.
type: boolean
ipFamily:
+ default: dual
description: |-
IPFamily specifies the IP family to be used by the NGINX.
Default is "dual", meaning the server will use both IPv4 and IPv6.
diff --git a/deploy/crds.yaml b/deploy/crds.yaml
index 9f2fec33e8..547c912748 100644
--- a/deploy/crds.yaml
+++ b/deploy/crds.yaml
@@ -698,6 +698,7 @@ spec:
Default is false, meaning http2 will be enabled for all servers.
type: boolean
ipFamily:
+ default: dual
description: |-
IPFamily specifies the IP family to be used by the NGINX.
Default is "dual", meaning the server will use both IPv4 and IPv6.
diff --git a/internal/mode/static/state/dataplane/configuration.go b/internal/mode/static/state/dataplane/configuration.go
index 9a9384e783..b64155d0f6 100644
--- a/internal/mode/static/state/dataplane/configuration.go
+++ b/internal/mode/static/state/dataplane/configuration.go
@@ -41,13 +41,13 @@ func BuildConfiguration(
return Configuration{Version: configVersion}
}
- upstreams := buildUpstreams(ctx, g.Gateway.Listeners, resolver)
+ baseHTTPConfig := buildBaseHTTPConfig(g)
+ upstreams := buildUpstreams(ctx, g.Gateway.Listeners, resolver, baseHTTPConfig)
httpServers, sslServers := buildServers(g, generator)
backendGroups := buildBackendGroups(append(httpServers, sslServers...))
keyPairs := buildSSLKeyPairs(g.ReferencedSecrets, g.Gateway.Listeners)
certBundles := buildCertBundles(g.ReferencedCaCertConfigMaps, backendGroups)
telemetry := buildTelemetry(g)
- baseHTTPConfig := buildBaseHTTPConfig(g)
config := Configuration{
HTTPServers: httpServers,
@@ -472,11 +472,15 @@ func buildUpstreams(
ctx context.Context,
listeners []*graph.Listener,
resolver resolver.ServiceResolver,
+ baseHTTPConfig BaseHTTPConfig,
) []Upstream {
// There can be duplicate upstreams if multiple routes reference the same upstream.
// We use a map to deduplicate them.
uniqueUpstreams := make(map[string]Upstream)
+ // We need to build endpoints based on the IPFamily of NGINX.
+ ipv4, ipv6 := getIPFamily(baseHTTPConfig)
+
for _, l := range listeners {
if !l.Valid {
continue
@@ -503,7 +507,7 @@ func buildUpstreams(
var errMsg string
- eps, err := resolver.Resolve(ctx, br.SvcNsName, br.ServicePort)
+ eps, err := resolver.Resolve(ctx, br.SvcNsName, br.ServicePort, ipv4, ipv6)
if err != nil {
errMsg = err.Error()
}
@@ -531,6 +535,13 @@ func buildUpstreams(
return upstreams
}
+func getIPFamily(config BaseHTTPConfig) (bool, bool) {
+ ipv4 := config.IPFamily == IPv4 || config.IPFamily == Dual
+ ipv6 := config.IPFamily == IPv6 || config.IPFamily == Dual
+
+ return ipv4, ipv6
+}
+
func getListenerHostname(h *v1.Hostname) string {
if h == nil || *h == "" {
return wildcardHostname
@@ -669,11 +680,13 @@ func buildBaseHTTPConfig(g *graph.Graph) BaseHTTPConfig {
baseConfig.HTTP2 = false
}
- switch ipFamily := g.NginxProxy.Source.Spec.IPFamily; *ipFamily {
- case ngfAPI.IPv4:
- baseConfig.IPFamily = IPv4
- case ngfAPI.IPv6:
- baseConfig.IPFamily = IPv6
+ if g.NginxProxy.Source.Spec.IPFamily != nil {
+ switch ipFamily := g.NginxProxy.Source.Spec.IPFamily; *ipFamily {
+ case ngfAPI.IPv4:
+ baseConfig.IPFamily = IPv4
+ case ngfAPI.IPv6:
+ baseConfig.IPFamily = IPv6
+ }
}
return baseConfig
diff --git a/internal/mode/static/state/dataplane/configuration_test.go b/internal/mode/static/state/dataplane/configuration_test.go
index 7562c0f8b5..13695f4c25 100644
--- a/internal/mode/static/state/dataplane/configuration_test.go
+++ b/internal/mode/static/state/dataplane/configuration_test.go
@@ -2707,6 +2707,10 @@ func TestBuildUpstreams(t *testing.T) {
Address: "10.0.0.2",
Port: 8080,
},
+ {
+ Address: "fd00:10:244::6",
+ Port: 8080,
+ },
}
barEndpoints := []resolver.Endpoint{
@@ -2733,6 +2737,10 @@ func TestBuildUpstreams(t *testing.T) {
Address: "12.0.0.0",
Port: 80,
},
+ {
+ Address: "fd00:10:244::9",
+ Port: 80,
+ },
}
baz2Endpoints := []resolver.Endpoint{
@@ -2749,6 +2757,21 @@ func TestBuildUpstreams(t *testing.T) {
},
}
+ ipv6Endpoints := []resolver.Endpoint{
+ {
+ Address: "fd00:10:244::7",
+ Port: 80,
+ },
+ {
+ Address: "fd00:10:244::8",
+ Port: 80,
+ },
+ {
+ Address: "fd00:10:244::9",
+ Port: 80,
+ },
+ }
+
createBackendRefs := func(serviceNames ...string) []graph.BackendRef {
var backends []graph.BackendRef
for _, name := range serviceNames {
@@ -2775,6 +2798,8 @@ func TestBuildUpstreams(t *testing.T) {
hr4Refs1 := createBackendRefs("baz2")
+ hr5Refs0 := createBackendRefs("ipv6-endpoints")
+
nonExistingRefs := createBackendRefs("non-existing")
invalidHRRefs := createBackendRefs("abc")
@@ -2809,6 +2834,15 @@ func TestBuildUpstreams(t *testing.T) {
},
}
+ routes3 := map[graph.RouteKey]*graph.L7Route{
+ {NamespacedName: types.NamespacedName{Name: "hr4", Namespace: "test"}}: {
+ Valid: true,
+ Spec: graph.L7RouteSpec{
+ Rules: refsToValidRules(hr5Refs0, hr2Refs1),
+ },
+ },
+ }
+
routesWithNonExistingRefs := map[graph.RouteKey]*graph.L7Route{
{NamespacedName: types.NamespacedName{Name: "non-existing", Namespace: "test"}}: {
Valid: true,
@@ -2848,6 +2882,11 @@ func TestBuildUpstreams(t *testing.T) {
Valid: true,
Routes: invalidRoutes, // shouldn't be included since routes are invalid
},
+ {
+ Name: "listener-4",
+ Valid: true,
+ Routes: routes3,
+ },
}
emptyEndpointsErrMsg := "empty endpoints error"
@@ -2880,6 +2919,10 @@ func TestBuildUpstreams(t *testing.T) {
Endpoints: nil,
ErrorMsg: nilEndpointsErrMsg,
},
+ {
+ Name: "test_ipv6-endpoints_80",
+ Endpoints: ipv6Endpoints,
+ },
}
fakeResolver := &resolverfakes.FakeServiceResolver{}
@@ -2887,6 +2930,8 @@ func TestBuildUpstreams(t *testing.T) {
_ context.Context,
svcNsName types.NamespacedName,
_ apiv1.ServicePort,
+ _ bool,
+ _ bool,
) ([]resolver.Endpoint, error) {
switch svcNsName.Name {
case "bar":
@@ -2903,6 +2948,8 @@ func TestBuildUpstreams(t *testing.T) {
return nil, errors.New(nilEndpointsErrMsg)
case "abc":
return abcEndpoints, nil
+ case "ipv6-endpoints":
+ return ipv6Endpoints, nil
default:
return nil, fmt.Errorf("unexpected service %s", svcNsName.Name)
}
@@ -2910,7 +2957,9 @@ func TestBuildUpstreams(t *testing.T) {
g := NewWithT(t)
- upstreams := buildUpstreams(context.TODO(), listeners, fakeResolver)
+ upstreams := buildUpstreams(context.TODO(), listeners, fakeResolver, BaseHTTPConfig{
+ IPFamily: Dual,
+ })
g.Expect(upstreams).To(ConsistOf(expUpstreams))
}
diff --git a/internal/mode/static/state/resolver/resolver.go b/internal/mode/static/state/resolver/resolver.go
index 79dec3b338..05e8beb378 100644
--- a/internal/mode/static/state/resolver/resolver.go
+++ b/internal/mode/static/state/resolver/resolver.go
@@ -19,7 +19,13 @@ import (
// ServiceResolver resolves a Service's NamespacedName and ServicePort to a list of Endpoints.
// Returns an error if the Service or Service Port cannot be resolved.
type ServiceResolver interface {
- Resolve(ctx context.Context, svcNsName types.NamespacedName, svcPort v1.ServicePort) ([]Endpoint, error)
+ Resolve(
+ ctx context.Context,
+ svcNsName types.NamespacedName,
+ svcPort v1.ServicePort,
+ ipv4Enabled bool,
+ ipv6Enabled bool,
+ ) ([]Endpoint, error)
}
// Endpoint is the internal representation of a Kubernetes endpoint.
@@ -48,6 +54,7 @@ func (e *ServiceResolverImpl) Resolve(
ctx context.Context,
svcNsName types.NamespacedName,
svcPort v1.ServicePort,
+ ipv4Enabled, ipv6Enabled bool,
) ([]Endpoint, error) {
if svcPort.Port == 0 || svcNsName.Name == "" || svcNsName.Namespace == "" {
panic(fmt.Errorf("expected the following fields to be non-empty: name: %s, ns: %s, port: %d",
@@ -68,7 +75,14 @@ func (e *ServiceResolverImpl) Resolve(
return nil, fmt.Errorf("no endpoints found for Service %s", svcNsName)
}
- return resolveEndpoints(svcNsName, svcPort, endpointSliceList, initEndpointSetWithCalculatedSize)
+ return resolveEndpoints(
+ svcNsName,
+ svcPort,
+ endpointSliceList,
+ initEndpointSetWithCalculatedSize,
+ ipv4Enabled,
+ ipv6Enabled,
+ )
}
type initEndpointSetFunc func([]discoveryV1.EndpointSlice) map[Endpoint]struct{}
@@ -99,8 +113,9 @@ func resolveEndpoints(
svcPort v1.ServicePort,
endpointSliceList discoveryV1.EndpointSliceList,
initEndpointsSet initEndpointSetFunc,
+ ipv4Enabled, ipv6Enabled bool,
) ([]Endpoint, error) {
- filteredSlices := filterEndpointSliceList(endpointSliceList, svcPort)
+ filteredSlices := filterEndpointSliceList(endpointSliceList, svcPort, ipv4Enabled, ipv6Enabled)
if len(filteredSlices) == 0 {
return nil, fmt.Errorf("no valid endpoints found for Service %s and port %d", svcNsName, svcPort.Port)
@@ -151,11 +166,23 @@ func getDefaultPort(svcPort v1.ServicePort) int32 {
return svcPort.Port
}
-func ignoreEndpointSlice(endpointSlice discoveryV1.EndpointSlice, port v1.ServicePort) bool {
+func ignoreEndpointSlice(
+ endpointSlice discoveryV1.EndpointSlice,
+ port v1.ServicePort,
+ ipv4Enabled, ipv6Enabled bool,
+) bool {
if endpointSlice.AddressType == discoveryV1.AddressTypeFQDN {
return true
}
+ if endpointSlice.AddressType == discoveryV1.AddressTypeIPv4 && !ipv4Enabled {
+ return true
+ }
+
+ if endpointSlice.AddressType == discoveryV1.AddressTypeIPv6 && !ipv6Enabled {
+ return true
+ }
+
// ignore endpoint slices that don't have a matching port.
return findPort(endpointSlice.Ports, port) == 0
}
@@ -168,11 +195,13 @@ func endpointReady(endpoint discoveryV1.Endpoint) bool {
func filterEndpointSliceList(
endpointSliceList discoveryV1.EndpointSliceList,
port v1.ServicePort,
+ ipv4Enabled bool,
+ ipv6Enabled bool,
) []discoveryV1.EndpointSlice {
filtered := make([]discoveryV1.EndpointSlice, 0, len(endpointSliceList.Items))
for _, endpointSlice := range endpointSliceList.Items {
- if !ignoreEndpointSlice(endpointSlice, port) {
+ if !ignoreEndpointSlice(endpointSlice, port, ipv4Enabled, ipv6Enabled) {
filtered = append(filtered, endpointSlice)
}
}
diff --git a/internal/mode/static/state/resolver/resolver_test.go b/internal/mode/static/state/resolver/resolver_test.go
index 2763893f2c..b8baa688af 100644
--- a/internal/mode/static/state/resolver/resolver_test.go
+++ b/internal/mode/static/state/resolver/resolver_test.go
@@ -50,7 +50,7 @@ var (
Endpoints: nil,
}
- validEndpointSlice = discoveryV1.EndpointSlice{
+ validIPv4EndpontSlice = discoveryV1.EndpointSlice{
AddressType: discoveryV1.AddressTypeIPv4,
Endpoints: []discoveryV1.Endpoint{
readyEndpoint1,
@@ -100,14 +100,58 @@ var (
)
func TestFilterEndpointSliceList(t *testing.T) {
- sliceList := discoveryV1.EndpointSliceList{
- Items: []discoveryV1.EndpointSlice{
- validEndpointSlice,
- validIPv6EndpointSlice,
- invalidAddressTypeEndpointSlice,
- invalidPortEndpointSlice,
- nilEndpoints,
- mixedValidityEndpointSlice,
+ test := []struct {
+ msg string
+ sliceList discoveryV1.EndpointSliceList
+ expList []discoveryV1.EndpointSlice
+ ipv4 bool
+ ipv6 bool
+ }{
+ {
+ msg: "only ipv4 enabled",
+ sliceList: discoveryV1.EndpointSliceList{
+ Items: []discoveryV1.EndpointSlice{
+ validIPv4EndpontSlice,
+ validIPv6EndpointSlice,
+ },
+ },
+ expList: []discoveryV1.EndpointSlice{
+ validIPv4EndpontSlice,
+ },
+ ipv4: true,
+ ipv6: false,
+ },
+ {
+ msg: "only ipv6 enabled",
+ sliceList: discoveryV1.EndpointSliceList{
+ Items: []discoveryV1.EndpointSlice{
+ validIPv4EndpontSlice,
+ validIPv6EndpointSlice,
+ },
+ },
+ expList: []discoveryV1.EndpointSlice{
+ validIPv6EndpointSlice,
+ },
+ ipv4: false,
+ ipv6: true,
+ },
+ {
+ msg: "ipv4 and ipv6 enabled",
+ sliceList: discoveryV1.EndpointSliceList{
+ Items: []discoveryV1.EndpointSlice{
+ validIPv4EndpontSlice,
+ validIPv6EndpointSlice,
+ invalidAddressTypeEndpointSlice,
+ invalidPortEndpointSlice,
+ nilEndpoints,
+ mixedValidityEndpointSlice,
+ },
+ },
+ expList: []discoveryV1.EndpointSlice{
+ validIPv4EndpontSlice, validIPv6EndpointSlice, mixedValidityEndpointSlice,
+ },
+ ipv4: true,
+ ipv6: true,
},
}
@@ -117,11 +161,11 @@ func TestFilterEndpointSliceList(t *testing.T) {
TargetPort: intstr.FromInt(80),
}
- expFilteredList := []discoveryV1.EndpointSlice{validEndpointSlice, validIPv6EndpointSlice, mixedValidityEndpointSlice}
-
- filteredSliceList := filterEndpointSliceList(sliceList, svcPort)
- g := NewWithT(t)
- g.Expect(filteredSliceList).To(Equal(expFilteredList))
+ for _, tc := range test {
+ filteredSliceList := filterEndpointSliceList(tc.sliceList, svcPort, tc.ipv4, tc.ipv6)
+ g := NewWithT(t)
+ g.Expect(filteredSliceList).To(Equal(tc.expList))
+ }
}
func TestGetDefaultPort(t *testing.T) {
@@ -227,7 +271,7 @@ func TestIgnoreEndpointSlice(t *testing.T) {
ignore: false,
},
{
- msg: "normal",
+ msg: "normal IPV4 address type",
slice: discoveryV1.EndpointSlice{
AddressType: discoveryV1.AddressTypeIPv4,
Ports: []discoveryV1.EndpointPort{
@@ -265,7 +309,7 @@ func TestIgnoreEndpointSlice(t *testing.T) {
}
for _, tc := range testcases {
g := NewWithT(t)
- g.Expect(ignoreEndpointSlice(tc.slice, tc.servicePort)).To(Equal(tc.ignore))
+ g.Expect(ignoreEndpointSlice(tc.slice, tc.servicePort, true, true)).To(Equal(tc.ignore))
}
}
@@ -560,7 +604,7 @@ func bench(b *testing.B, svcNsName types.NamespacedName,
) {
b.Helper()
for i := 0; i < b.N; i++ {
- res, err := resolveEndpoints(svcNsName, v1.ServicePort{Port: 80}, list, initSet)
+ res, err := resolveEndpoints(svcNsName, v1.ServicePort{Port: 80}, list, initSet, true, true)
if len(res) != n {
b.Fatalf("expected %d endpoints, got %d", n, len(res))
}
diff --git a/internal/mode/static/state/resolver/resolverfakes/fake_service_resolver.go b/internal/mode/static/state/resolver/resolverfakes/fake_service_resolver.go
index 8fa8a9fcdc..152d495d52 100644
--- a/internal/mode/static/state/resolver/resolverfakes/fake_service_resolver.go
+++ b/internal/mode/static/state/resolver/resolverfakes/fake_service_resolver.go
@@ -11,12 +11,14 @@ import (
)
type FakeServiceResolver struct {
- ResolveStub func(context.Context, types.NamespacedName, v1.ServicePort) ([]resolver.Endpoint, error)
+ ResolveStub func(context.Context, types.NamespacedName, v1.ServicePort, bool, bool) ([]resolver.Endpoint, error)
resolveMutex sync.RWMutex
resolveArgsForCall []struct {
arg1 context.Context
arg2 types.NamespacedName
arg3 v1.ServicePort
+ arg4 bool
+ arg5 bool
}
resolveReturns struct {
result1 []resolver.Endpoint
@@ -30,20 +32,22 @@ type FakeServiceResolver struct {
invocationsMutex sync.RWMutex
}
-func (fake *FakeServiceResolver) Resolve(arg1 context.Context, arg2 types.NamespacedName, arg3 v1.ServicePort) ([]resolver.Endpoint, error) {
+func (fake *FakeServiceResolver) Resolve(arg1 context.Context, arg2 types.NamespacedName, arg3 v1.ServicePort, arg4 bool, arg5 bool) ([]resolver.Endpoint, error) {
fake.resolveMutex.Lock()
ret, specificReturn := fake.resolveReturnsOnCall[len(fake.resolveArgsForCall)]
fake.resolveArgsForCall = append(fake.resolveArgsForCall, struct {
arg1 context.Context
arg2 types.NamespacedName
arg3 v1.ServicePort
- }{arg1, arg2, arg3})
+ arg4 bool
+ arg5 bool
+ }{arg1, arg2, arg3, arg4, arg5})
stub := fake.ResolveStub
fakeReturns := fake.resolveReturns
- fake.recordInvocation("Resolve", []interface{}{arg1, arg2, arg3})
+ fake.recordInvocation("Resolve", []interface{}{arg1, arg2, arg3, arg4, arg5})
fake.resolveMutex.Unlock()
if stub != nil {
- return stub(arg1, arg2, arg3)
+ return stub(arg1, arg2, arg3, arg4, arg5)
}
if specificReturn {
return ret.result1, ret.result2
@@ -57,17 +61,17 @@ func (fake *FakeServiceResolver) ResolveCallCount() int {
return len(fake.resolveArgsForCall)
}
-func (fake *FakeServiceResolver) ResolveCalls(stub func(context.Context, types.NamespacedName, v1.ServicePort) ([]resolver.Endpoint, error)) {
+func (fake *FakeServiceResolver) ResolveCalls(stub func(context.Context, types.NamespacedName, v1.ServicePort, bool, bool) ([]resolver.Endpoint, error)) {
fake.resolveMutex.Lock()
defer fake.resolveMutex.Unlock()
fake.ResolveStub = stub
}
-func (fake *FakeServiceResolver) ResolveArgsForCall(i int) (context.Context, types.NamespacedName, v1.ServicePort) {
+func (fake *FakeServiceResolver) ResolveArgsForCall(i int) (context.Context, types.NamespacedName, v1.ServicePort, bool, bool) {
fake.resolveMutex.RLock()
defer fake.resolveMutex.RUnlock()
argsForCall := fake.resolveArgsForCall[i]
- return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
+ return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4, argsForCall.arg5
}
func (fake *FakeServiceResolver) ResolveReturns(result1 []resolver.Endpoint, result2 error) {
diff --git a/internal/mode/static/state/resolver/service_resolver_test.go b/internal/mode/static/state/resolver/service_resolver_test.go
index 83573d51c5..1a2ac046db 100644
--- a/internal/mode/static/state/resolver/service_resolver_test.go
+++ b/internal/mode/static/state/resolver/service_resolver_test.go
@@ -203,7 +203,7 @@ var _ = Describe("ServiceResolver", func() {
},
}
- endpoints, err := serviceResolver.Resolve(context.TODO(), svcNsName, svcPort)
+ endpoints, err := serviceResolver.Resolve(context.TODO(), svcNsName, svcPort, true, true)
Expect(err).ToNot(HaveOccurred())
Expect(endpoints).To(ConsistOf(expectedEndpoints))
})
@@ -214,7 +214,7 @@ var _ = Describe("ServiceResolver", func() {
Expect(fakeK8sClient.Delete(context.TODO(), dupeEndpointSlice)).To(Succeed())
Expect(fakeK8sClient.Delete(context.TODO(), sliceIPV6)).To(Succeed())
- endpoints, err := serviceResolver.Resolve(context.TODO(), svcNsName, svcPort)
+ endpoints, err := serviceResolver.Resolve(context.TODO(), svcNsName, svcPort, true, true)
Expect(err).To(HaveOccurred())
Expect(endpoints).To(BeNil())
})
@@ -222,19 +222,19 @@ var _ = Describe("ServiceResolver", func() {
// delete remaining endpoint slices
Expect(fakeK8sClient.Delete(context.TODO(), sliceNoMatchingPortName)).To(Succeed())
- endpoints, err := serviceResolver.Resolve(context.TODO(), svcNsName, svcPort)
+ endpoints, err := serviceResolver.Resolve(context.TODO(), svcNsName, svcPort, true, true)
Expect(err).To(HaveOccurred())
Expect(endpoints).To(BeNil())
})
It("panics if the service NamespacedName is empty", func() {
resolve := func() {
- _, _ = serviceResolver.Resolve(context.TODO(), types.NamespacedName{}, svcPort)
+ _, _ = serviceResolver.Resolve(context.TODO(), types.NamespacedName{}, svcPort, true, true)
}
Expect(resolve).Should(Panic())
})
It("panics if the ServicePort is empty", func() {
resolve := func() {
- _, _ = serviceResolver.Resolve(context.TODO(), types.NamespacedName{}, v1.ServicePort{})
+ _, _ = serviceResolver.Resolve(context.TODO(), types.NamespacedName{}, v1.ServicePort{}, true, true)
}
Expect(resolve).Should(Panic())
})
diff --git a/site/content/reference/api.md b/site/content/reference/api.md
index 9c509b34a5..b597e1a39e 100644
--- a/site/content/reference/api.md
+++ b/site/content/reference/api.md
@@ -748,13 +748,13 @@ Examples: 120s, 50ms.
"dual" |
-Dual specifies that the server will use both IPv4 and IPv6.
+ | Dual specifies that NGINX will use both IPv4 and IPv6.
|
"ipv4" |
-IPv4 specifies that the server will use only IPv4.
+ | IPv4 specifies that NGINX will use only IPv4.
|
"ipv6" |
-IPv6 specifies that the server will use only IPv6.
+ | IPv6 specifies that NGINX will use only IPv6.
|
From 56007263ca10ec63364054ac370bca031206d572 Mon Sep 17 00:00:00 2001
From: Saloni
Date: Thu, 11 Jul 2024 16:32:48 -0600
Subject: [PATCH 09/28] update resolver
---
.../static/state/dataplane/configuration.go | 22 +++++++++-----
.../state/dataplane/configuration_test.go | 8 ++---
.../mode/static/state/resolver/resolver.go | 26 +++++++---------
.../static/state/resolver/resolver_test.go | 30 +++++++++----------
.../resolverfakes/fake_service_resolver.go | 28 +++++++++--------
.../state/resolver/service_resolver_test.go | 14 +++++----
6 files changed, 67 insertions(+), 61 deletions(-)
diff --git a/internal/mode/static/state/dataplane/configuration.go b/internal/mode/static/state/dataplane/configuration.go
index b64155d0f6..5429698155 100644
--- a/internal/mode/static/state/dataplane/configuration.go
+++ b/internal/mode/static/state/dataplane/configuration.go
@@ -7,6 +7,7 @@ import (
"sort"
apiv1 "k8s.io/api/core/v1"
+ discoveryV1 "k8s.io/api/discovery/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -42,7 +43,7 @@ func BuildConfiguration(
}
baseHTTPConfig := buildBaseHTTPConfig(g)
- upstreams := buildUpstreams(ctx, g.Gateway.Listeners, resolver, baseHTTPConfig)
+ upstreams := buildUpstreams(ctx, g.Gateway.Listeners, resolver, baseHTTPConfig.IPFamily)
httpServers, sslServers := buildServers(g, generator)
backendGroups := buildBackendGroups(append(httpServers, sslServers...))
keyPairs := buildSSLKeyPairs(g.ReferencedSecrets, g.Gateway.Listeners)
@@ -472,14 +473,14 @@ func buildUpstreams(
ctx context.Context,
listeners []*graph.Listener,
resolver resolver.ServiceResolver,
- baseHTTPConfig BaseHTTPConfig,
+ ipFamily IPFamilyType,
) []Upstream {
// There can be duplicate upstreams if multiple routes reference the same upstream.
// We use a map to deduplicate them.
uniqueUpstreams := make(map[string]Upstream)
// We need to build endpoints based on the IPFamily of NGINX.
- ipv4, ipv6 := getIPFamily(baseHTTPConfig)
+ allowedAddressType := getAllowedAddressType(ipFamily)
for _, l := range listeners {
if !l.Valid {
@@ -507,7 +508,7 @@ func buildUpstreams(
var errMsg string
- eps, err := resolver.Resolve(ctx, br.SvcNsName, br.ServicePort, ipv4, ipv6)
+ eps, err := resolver.Resolve(ctx, br.SvcNsName, br.ServicePort, allowedAddressType)
if err != nil {
errMsg = err.Error()
}
@@ -535,11 +536,16 @@ func buildUpstreams(
return upstreams
}
-func getIPFamily(config BaseHTTPConfig) (bool, bool) {
- ipv4 := config.IPFamily == IPv4 || config.IPFamily == Dual
- ipv6 := config.IPFamily == IPv6 || config.IPFamily == Dual
+func getAllowedAddressType(ipFamily IPFamilyType) []discoveryV1.AddressType {
+ allowedAddressType := make([]discoveryV1.AddressType, 0)
+ if ipFamily == IPv4 || ipFamily == Dual {
+ allowedAddressType = append(allowedAddressType, discoveryV1.AddressTypeIPv4)
+ }
+ if ipFamily == IPv6 || ipFamily == Dual {
+ allowedAddressType = append(allowedAddressType, discoveryV1.AddressTypeIPv6)
+ }
- return ipv4, ipv6
+ return allowedAddressType
}
func getListenerHostname(h *v1.Hostname) string {
diff --git a/internal/mode/static/state/dataplane/configuration_test.go b/internal/mode/static/state/dataplane/configuration_test.go
index 13695f4c25..7409b9e630 100644
--- a/internal/mode/static/state/dataplane/configuration_test.go
+++ b/internal/mode/static/state/dataplane/configuration_test.go
@@ -9,6 +9,7 @@ import (
. "github.com/onsi/gomega"
apiv1 "k8s.io/api/core/v1"
+ discoveryV1 "k8s.io/api/discovery/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
@@ -2930,8 +2931,7 @@ func TestBuildUpstreams(t *testing.T) {
_ context.Context,
svcNsName types.NamespacedName,
_ apiv1.ServicePort,
- _ bool,
- _ bool,
+ _ []discoveryV1.AddressType,
) ([]resolver.Endpoint, error) {
switch svcNsName.Name {
case "bar":
@@ -2957,9 +2957,7 @@ func TestBuildUpstreams(t *testing.T) {
g := NewWithT(t)
- upstreams := buildUpstreams(context.TODO(), listeners, fakeResolver, BaseHTTPConfig{
- IPFamily: Dual,
- })
+ upstreams := buildUpstreams(context.TODO(), listeners, fakeResolver, Dual)
g.Expect(upstreams).To(ConsistOf(expUpstreams))
}
diff --git a/internal/mode/static/state/resolver/resolver.go b/internal/mode/static/state/resolver/resolver.go
index 05e8beb378..6ceb9637cd 100644
--- a/internal/mode/static/state/resolver/resolver.go
+++ b/internal/mode/static/state/resolver/resolver.go
@@ -3,6 +3,7 @@ package resolver
import (
"context"
"fmt"
+ "slices"
v1 "k8s.io/api/core/v1"
discoveryV1 "k8s.io/api/discovery/v1"
@@ -23,8 +24,7 @@ type ServiceResolver interface {
ctx context.Context,
svcNsName types.NamespacedName,
svcPort v1.ServicePort,
- ipv4Enabled bool,
- ipv6Enabled bool,
+ allowedAddressType []discoveryV1.AddressType,
) ([]Endpoint, error)
}
@@ -54,7 +54,7 @@ func (e *ServiceResolverImpl) Resolve(
ctx context.Context,
svcNsName types.NamespacedName,
svcPort v1.ServicePort,
- ipv4Enabled, ipv6Enabled bool,
+ allowedAddressType []discoveryV1.AddressType,
) ([]Endpoint, error) {
if svcPort.Port == 0 || svcNsName.Name == "" || svcNsName.Namespace == "" {
panic(fmt.Errorf("expected the following fields to be non-empty: name: %s, ns: %s, port: %d",
@@ -80,8 +80,7 @@ func (e *ServiceResolverImpl) Resolve(
svcPort,
endpointSliceList,
initEndpointSetWithCalculatedSize,
- ipv4Enabled,
- ipv6Enabled,
+ allowedAddressType,
)
}
@@ -113,9 +112,9 @@ func resolveEndpoints(
svcPort v1.ServicePort,
endpointSliceList discoveryV1.EndpointSliceList,
initEndpointsSet initEndpointSetFunc,
- ipv4Enabled, ipv6Enabled bool,
+ allowedAddressType []discoveryV1.AddressType,
) ([]Endpoint, error) {
- filteredSlices := filterEndpointSliceList(endpointSliceList, svcPort, ipv4Enabled, ipv6Enabled)
+ filteredSlices := filterEndpointSliceList(endpointSliceList, svcPort, allowedAddressType)
if len(filteredSlices) == 0 {
return nil, fmt.Errorf("no valid endpoints found for Service %s and port %d", svcNsName, svcPort.Port)
@@ -169,17 +168,13 @@ func getDefaultPort(svcPort v1.ServicePort) int32 {
func ignoreEndpointSlice(
endpointSlice discoveryV1.EndpointSlice,
port v1.ServicePort,
- ipv4Enabled, ipv6Enabled bool,
+ allowedAddressType []discoveryV1.AddressType,
) bool {
if endpointSlice.AddressType == discoveryV1.AddressTypeFQDN {
return true
}
- if endpointSlice.AddressType == discoveryV1.AddressTypeIPv4 && !ipv4Enabled {
- return true
- }
-
- if endpointSlice.AddressType == discoveryV1.AddressTypeIPv6 && !ipv6Enabled {
+ if !slices.Contains(allowedAddressType, endpointSlice.AddressType) {
return true
}
@@ -195,13 +190,12 @@ func endpointReady(endpoint discoveryV1.Endpoint) bool {
func filterEndpointSliceList(
endpointSliceList discoveryV1.EndpointSliceList,
port v1.ServicePort,
- ipv4Enabled bool,
- ipv6Enabled bool,
+ allowedAddressType []discoveryV1.AddressType,
) []discoveryV1.EndpointSlice {
filtered := make([]discoveryV1.EndpointSlice, 0, len(endpointSliceList.Items))
for _, endpointSlice := range endpointSliceList.Items {
- if !ignoreEndpointSlice(endpointSlice, port, ipv4Enabled, ipv6Enabled) {
+ if !ignoreEndpointSlice(endpointSlice, port, allowedAddressType) {
filtered = append(filtered, endpointSlice)
}
}
diff --git a/internal/mode/static/state/resolver/resolver_test.go b/internal/mode/static/state/resolver/resolver_test.go
index b8baa688af..adc2051175 100644
--- a/internal/mode/static/state/resolver/resolver_test.go
+++ b/internal/mode/static/state/resolver/resolver_test.go
@@ -14,7 +14,11 @@ import (
)
var (
- svcPortName = "svc-port"
+ svcPortName = "svc-port"
+ dualAddressType = []discoveryV1.AddressType{
+ discoveryV1.AddressTypeIPv4,
+ discoveryV1.AddressTypeIPv6,
+ }
addresses = []string{"10.0.0.1", "10.0.0.2", "10.0.0.3"}
addressesIPv6 = []string{"2001:db8::1", "2001:db8::2", "2001:db8::3"}
@@ -101,11 +105,10 @@ var (
func TestFilterEndpointSliceList(t *testing.T) {
test := []struct {
- msg string
- sliceList discoveryV1.EndpointSliceList
- expList []discoveryV1.EndpointSlice
- ipv4 bool
- ipv6 bool
+ msg string
+ sliceList discoveryV1.EndpointSliceList
+ expList []discoveryV1.EndpointSlice
+ allowedAddressType []discoveryV1.AddressType
}{
{
msg: "only ipv4 enabled",
@@ -118,8 +121,7 @@ func TestFilterEndpointSliceList(t *testing.T) {
expList: []discoveryV1.EndpointSlice{
validIPv4EndpontSlice,
},
- ipv4: true,
- ipv6: false,
+ allowedAddressType: []discoveryV1.AddressType{discoveryV1.AddressTypeIPv4},
},
{
msg: "only ipv6 enabled",
@@ -132,8 +134,7 @@ func TestFilterEndpointSliceList(t *testing.T) {
expList: []discoveryV1.EndpointSlice{
validIPv6EndpointSlice,
},
- ipv4: false,
- ipv6: true,
+ allowedAddressType: []discoveryV1.AddressType{discoveryV1.AddressTypeIPv6},
},
{
msg: "ipv4 and ipv6 enabled",
@@ -150,8 +151,7 @@ func TestFilterEndpointSliceList(t *testing.T) {
expList: []discoveryV1.EndpointSlice{
validIPv4EndpontSlice, validIPv6EndpointSlice, mixedValidityEndpointSlice,
},
- ipv4: true,
- ipv6: true,
+ allowedAddressType: dualAddressType,
},
}
@@ -162,7 +162,7 @@ func TestFilterEndpointSliceList(t *testing.T) {
}
for _, tc := range test {
- filteredSliceList := filterEndpointSliceList(tc.sliceList, svcPort, tc.ipv4, tc.ipv6)
+ filteredSliceList := filterEndpointSliceList(tc.sliceList, svcPort, tc.allowedAddressType)
g := NewWithT(t)
g.Expect(filteredSliceList).To(Equal(tc.expList))
}
@@ -309,7 +309,7 @@ func TestIgnoreEndpointSlice(t *testing.T) {
}
for _, tc := range testcases {
g := NewWithT(t)
- g.Expect(ignoreEndpointSlice(tc.slice, tc.servicePort, true, true)).To(Equal(tc.ignore))
+ g.Expect(ignoreEndpointSlice(tc.slice, tc.servicePort, dualAddressType)).To(Equal(tc.ignore))
}
}
@@ -604,7 +604,7 @@ func bench(b *testing.B, svcNsName types.NamespacedName,
) {
b.Helper()
for i := 0; i < b.N; i++ {
- res, err := resolveEndpoints(svcNsName, v1.ServicePort{Port: 80}, list, initSet, true, true)
+ res, err := resolveEndpoints(svcNsName, v1.ServicePort{Port: 80}, list, initSet, dualAddressType)
if len(res) != n {
b.Fatalf("expected %d endpoints, got %d", n, len(res))
}
diff --git a/internal/mode/static/state/resolver/resolverfakes/fake_service_resolver.go b/internal/mode/static/state/resolver/resolverfakes/fake_service_resolver.go
index 152d495d52..09e624cfe5 100644
--- a/internal/mode/static/state/resolver/resolverfakes/fake_service_resolver.go
+++ b/internal/mode/static/state/resolver/resolverfakes/fake_service_resolver.go
@@ -7,18 +7,18 @@ import (
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/resolver"
v1 "k8s.io/api/core/v1"
+ v1a "k8s.io/api/discovery/v1"
"k8s.io/apimachinery/pkg/types"
)
type FakeServiceResolver struct {
- ResolveStub func(context.Context, types.NamespacedName, v1.ServicePort, bool, bool) ([]resolver.Endpoint, error)
+ ResolveStub func(context.Context, types.NamespacedName, v1.ServicePort, []v1a.AddressType) ([]resolver.Endpoint, error)
resolveMutex sync.RWMutex
resolveArgsForCall []struct {
arg1 context.Context
arg2 types.NamespacedName
arg3 v1.ServicePort
- arg4 bool
- arg5 bool
+ arg4 []v1a.AddressType
}
resolveReturns struct {
result1 []resolver.Endpoint
@@ -32,22 +32,26 @@ type FakeServiceResolver struct {
invocationsMutex sync.RWMutex
}
-func (fake *FakeServiceResolver) Resolve(arg1 context.Context, arg2 types.NamespacedName, arg3 v1.ServicePort, arg4 bool, arg5 bool) ([]resolver.Endpoint, error) {
+func (fake *FakeServiceResolver) Resolve(arg1 context.Context, arg2 types.NamespacedName, arg3 v1.ServicePort, arg4 []v1a.AddressType) ([]resolver.Endpoint, error) {
+ var arg4Copy []v1a.AddressType
+ if arg4 != nil {
+ arg4Copy = make([]v1a.AddressType, len(arg4))
+ copy(arg4Copy, arg4)
+ }
fake.resolveMutex.Lock()
ret, specificReturn := fake.resolveReturnsOnCall[len(fake.resolveArgsForCall)]
fake.resolveArgsForCall = append(fake.resolveArgsForCall, struct {
arg1 context.Context
arg2 types.NamespacedName
arg3 v1.ServicePort
- arg4 bool
- arg5 bool
- }{arg1, arg2, arg3, arg4, arg5})
+ arg4 []v1a.AddressType
+ }{arg1, arg2, arg3, arg4Copy})
stub := fake.ResolveStub
fakeReturns := fake.resolveReturns
- fake.recordInvocation("Resolve", []interface{}{arg1, arg2, arg3, arg4, arg5})
+ fake.recordInvocation("Resolve", []interface{}{arg1, arg2, arg3, arg4Copy})
fake.resolveMutex.Unlock()
if stub != nil {
- return stub(arg1, arg2, arg3, arg4, arg5)
+ return stub(arg1, arg2, arg3, arg4)
}
if specificReturn {
return ret.result1, ret.result2
@@ -61,17 +65,17 @@ func (fake *FakeServiceResolver) ResolveCallCount() int {
return len(fake.resolveArgsForCall)
}
-func (fake *FakeServiceResolver) ResolveCalls(stub func(context.Context, types.NamespacedName, v1.ServicePort, bool, bool) ([]resolver.Endpoint, error)) {
+func (fake *FakeServiceResolver) ResolveCalls(stub func(context.Context, types.NamespacedName, v1.ServicePort, []v1a.AddressType) ([]resolver.Endpoint, error)) {
fake.resolveMutex.Lock()
defer fake.resolveMutex.Unlock()
fake.ResolveStub = stub
}
-func (fake *FakeServiceResolver) ResolveArgsForCall(i int) (context.Context, types.NamespacedName, v1.ServicePort, bool, bool) {
+func (fake *FakeServiceResolver) ResolveArgsForCall(i int) (context.Context, types.NamespacedName, v1.ServicePort, []v1a.AddressType) {
fake.resolveMutex.RLock()
defer fake.resolveMutex.RUnlock()
argsForCall := fake.resolveArgsForCall[i]
- return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4, argsForCall.arg5
+ return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4
}
func (fake *FakeServiceResolver) ResolveReturns(result1 []resolver.Endpoint, result2 error) {
diff --git a/internal/mode/static/state/resolver/service_resolver_test.go b/internal/mode/static/state/resolver/service_resolver_test.go
index 1a2ac046db..51cdd29485 100644
--- a/internal/mode/static/state/resolver/service_resolver_test.go
+++ b/internal/mode/static/state/resolver/service_resolver_test.go
@@ -150,6 +150,10 @@ var _ = Describe("ServiceResolver", func() {
"other-svc-port",
discoveryV1.AddressTypeIPv4,
)
+ dualAddressType = []discoveryV1.AddressType{
+ discoveryV1.AddressTypeIPv4,
+ discoveryV1.AddressTypeIPv6,
+ }
)
var (
@@ -203,7 +207,7 @@ var _ = Describe("ServiceResolver", func() {
},
}
- endpoints, err := serviceResolver.Resolve(context.TODO(), svcNsName, svcPort, true, true)
+ endpoints, err := serviceResolver.Resolve(context.TODO(), svcNsName, svcPort, dualAddressType)
Expect(err).ToNot(HaveOccurred())
Expect(endpoints).To(ConsistOf(expectedEndpoints))
})
@@ -214,7 +218,7 @@ var _ = Describe("ServiceResolver", func() {
Expect(fakeK8sClient.Delete(context.TODO(), dupeEndpointSlice)).To(Succeed())
Expect(fakeK8sClient.Delete(context.TODO(), sliceIPV6)).To(Succeed())
- endpoints, err := serviceResolver.Resolve(context.TODO(), svcNsName, svcPort, true, true)
+ endpoints, err := serviceResolver.Resolve(context.TODO(), svcNsName, svcPort, dualAddressType)
Expect(err).To(HaveOccurred())
Expect(endpoints).To(BeNil())
})
@@ -222,19 +226,19 @@ var _ = Describe("ServiceResolver", func() {
// delete remaining endpoint slices
Expect(fakeK8sClient.Delete(context.TODO(), sliceNoMatchingPortName)).To(Succeed())
- endpoints, err := serviceResolver.Resolve(context.TODO(), svcNsName, svcPort, true, true)
+ endpoints, err := serviceResolver.Resolve(context.TODO(), svcNsName, svcPort, dualAddressType)
Expect(err).To(HaveOccurred())
Expect(endpoints).To(BeNil())
})
It("panics if the service NamespacedName is empty", func() {
resolve := func() {
- _, _ = serviceResolver.Resolve(context.TODO(), types.NamespacedName{}, svcPort, true, true)
+ _, _ = serviceResolver.Resolve(context.TODO(), types.NamespacedName{}, svcPort, dualAddressType)
}
Expect(resolve).Should(Panic())
})
It("panics if the ServicePort is empty", func() {
resolve := func() {
- _, _ = serviceResolver.Resolve(context.TODO(), types.NamespacedName{}, v1.ServicePort{}, true, true)
+ _, _ = serviceResolver.Resolve(context.TODO(), types.NamespacedName{}, v1.ServicePort{}, dualAddressType)
}
Expect(resolve).Should(Panic())
})
From ec8632211446ddc5900c443972a71d1102f58b42 Mon Sep 17 00:00:00 2001
From: Saloni
Date: Thu, 11 Jul 2024 17:19:02 -0600
Subject: [PATCH 10/28] update nginx proxy validator for ipFamily
---
internal/mode/static/state/graph/nginxproxy.go | 16 ++++++++++++++++
.../mode/static/state/graph/nginxproxy_test.go | 13 +++++++++++++
2 files changed, 29 insertions(+)
diff --git a/internal/mode/static/state/graph/nginxproxy.go b/internal/mode/static/state/graph/nginxproxy.go
index 29cebdbea2..b79f63b627 100644
--- a/internal/mode/static/state/graph/nginxproxy.go
+++ b/internal/mode/static/state/graph/nginxproxy.go
@@ -108,5 +108,21 @@ func validateNginxProxy(
}
}
+ if npCfg.Spec.IPFamily != nil {
+ ipFamily := npCfg.Spec.IPFamily
+ ipFamilyPath := spec.Child("ipFamily")
+ switch *ipFamily {
+ case ngfAPI.Dual,
+ ngfAPI.IPv4,
+ ngfAPI.IPv6:
+ default:
+ allErrs = append(allErrs, field.NotSupported(
+ ipFamilyPath,
+ ipFamily,
+ []string{string(ngfAPI.Dual), string(ngfAPI.IPv4), string(ngfAPI.IPv6)}),
+ )
+ }
+ }
+
return allErrs
}
diff --git a/internal/mode/static/state/graph/nginxproxy_test.go b/internal/mode/static/state/graph/nginxproxy_test.go
index 1aed0913fc..a664fa80d2 100644
--- a/internal/mode/static/state/graph/nginxproxy_test.go
+++ b/internal/mode/static/state/graph/nginxproxy_test.go
@@ -262,6 +262,7 @@ func TestValidateNginxProxy(t *testing.T) {
{Key: "key", Value: "value"},
},
},
+ IPFamily: helpers.GetPointer[ngfAPI.IPFamilyType](ngfAPI.Dual),
},
},
expectErrCount: 0,
@@ -326,6 +327,18 @@ func TestValidateNginxProxy(t *testing.T) {
expErrSubstring: "telemetry.spanAttributes",
expectErrCount: 2,
},
+ {
+ name: "invalid ipFamily type",
+ validator: createInvalidValidator(),
+ np: &ngfAPI.NginxProxy{
+ Spec: ngfAPI.NginxProxySpec{
+ Telemetry: &ngfAPI.Telemetry{},
+ IPFamily: helpers.GetPointer[ngfAPI.IPFamilyType]("invalid"),
+ },
+ },
+ expErrSubstring: "spec.ipFamily",
+ expectErrCount: 1,
+ },
}
for _, test := range tests {
From 5f6f583833df12140f840a28c513d14b0b076c87 Mon Sep 17 00:00:00 2001
From: Saloni
Date: Thu, 11 Jul 2024 22:17:23 -0600
Subject: [PATCH 11/28] add conditions to service
---
.../static/state/conditions/conditions.go | 13 ++++
.../mode/static/state/graph/backend_refs.go | 54 ++++++++++++--
.../static/state/graph/backend_refs_test.go | 70 ++++++++++++++++++-
internal/mode/static/state/graph/graph.go | 2 +-
4 files changed, 130 insertions(+), 9 deletions(-)
diff --git a/internal/mode/static/state/conditions/conditions.go b/internal/mode/static/state/conditions/conditions.go
index bb53854c68..a85dc4835f 100644
--- a/internal/mode/static/state/conditions/conditions.go
+++ b/internal/mode/static/state/conditions/conditions.go
@@ -40,6 +40,10 @@ const (
// Used with Accepted (false).
RouteReasonUnsupportedConfiguration v1.RouteConditionReason = "UnsupportedConfiguration"
+ // RouteReasonInvalidIPFamily is used when the Service associated with the Route is not configured with
+ // the same IP family as the NGINX server.
+ RouteReasonInvalidIPFamily v1.RouteConditionReason = "InvalidIPFamily"
+
// 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).
@@ -276,6 +280,15 @@ func NewRouteGatewayNotProgrammed(msg string) conditions.Condition {
}
}
+func NewRouteInvalidIPFamily(msg string) conditions.Condition {
+ return conditions.Condition{
+ Type: string(v1.RouteConditionAccepted),
+ Status: metav1.ConditionFalse,
+ Reason: string(RouteReasonInvalidIPFamily),
+ Message: msg,
+ }
+}
+
// NewDefaultListenerConditions returns the default Conditions that must be present in the status of a Listener.
func NewDefaultListenerConditions() []conditions.Condition {
return []conditions.Condition{
diff --git a/internal/mode/static/state/graph/backend_refs.go b/internal/mode/static/state/graph/backend_refs.go
index 2673fa4365..fef67e2600 100644
--- a/internal/mode/static/state/graph/backend_refs.go
+++ b/internal/mode/static/state/graph/backend_refs.go
@@ -10,6 +10,8 @@ import (
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
"sigs.k8s.io/gateway-api/apis/v1alpha3"
+ 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"
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/sort"
@@ -44,9 +46,10 @@ func addBackendRefsToRouteRules(
refGrantResolver *referenceGrantResolver,
services map[types.NamespacedName]*v1.Service,
backendTLSPolicies map[types.NamespacedName]*BackendTLSPolicy,
+ npCfg *NginxProxy,
) {
for _, r := range routes {
- addBackendRefsToRules(r, refGrantResolver, services, backendTLSPolicies)
+ addBackendRefsToRules(r, refGrantResolver, services, backendTLSPolicies, npCfg)
}
}
@@ -57,6 +60,7 @@ func addBackendRefsToRules(
refGrantResolver *referenceGrantResolver,
services map[types.NamespacedName]*v1.Service,
backendTLSPolicies map[types.NamespacedName]*BackendTLSPolicy,
+ npCfg *NginxProxy,
) {
if !route.Valid {
return
@@ -87,6 +91,7 @@ func addBackendRefsToRules(
services,
refPath,
backendTLSPolicies,
+ npCfg,
)
backendRefs = append(backendRefs, ref)
@@ -116,6 +121,7 @@ func createBackendRef(
services map[types.NamespacedName]*v1.Service,
refPath *field.Path,
backendTLSPolicies map[types.NamespacedName]*BackendTLSPolicy,
+ npCfg *NginxProxy,
) (BackendRef, *conditions.Condition) {
// Data plane will handle invalid ref by responding with 500.
// Because of that, we always need to add a BackendRef to group.Backends, even if the ref is invalid.
@@ -142,7 +148,7 @@ func createBackendRef(
return backendRef, &cond
}
- svcNsName, svcPort, err := getServiceAndPortFromRef(ref.BackendRef, sourceNamespace, services, refPath)
+ svcNsName, svcPort, svcIPFamilies, err := getServiceAndPortFromRef(ref.BackendRef, sourceNamespace, services, refPath)
if err != nil {
backendRef = BackendRef{
SvcNsName: svcNsName,
@@ -155,6 +161,19 @@ func createBackendRef(
return backendRef, &cond
}
+ err = verifyIPFamily(npCfg, svcIPFamilies)
+ if err != nil {
+ backendRef = BackendRef{
+ SvcNsName: svcNsName,
+ ServicePort: svcPort,
+ Weight: weight,
+ Valid: false,
+ }
+
+ cond := staticConds.NewRouteInvalidIPFamily(err.Error())
+ return backendRef, &cond
+ }
+
backendTLSPolicy, err := findBackendTLSPolicyForService(
backendTLSPolicies,
ref.Namespace,
@@ -278,7 +297,7 @@ func getServiceAndPortFromRef(
routeNamespace string,
services map[types.NamespacedName]*v1.Service,
refPath *field.Path,
-) (types.NamespacedName, v1.ServicePort, error) {
+) (types.NamespacedName, v1.ServicePort, []v1.IPFamily, error) {
ns := routeNamespace
if ref.Namespace != nil {
ns = string(*ref.Namespace)
@@ -288,16 +307,39 @@ func getServiceAndPortFromRef(
svc, ok := services[svcNsName]
if !ok {
- return svcNsName, v1.ServicePort{}, field.NotFound(refPath.Child("name"), ref.Name)
+ return svcNsName, v1.ServicePort{}, []v1.IPFamily{}, field.NotFound(refPath.Child("name"), ref.Name)
}
// safe to dereference port here because we already validated that the port is not nil in validateBackendRef.
svcPort, err := getServicePort(svc, int32(*ref.Port))
if err != nil {
- return svcNsName, v1.ServicePort{}, err
+ return svcNsName, v1.ServicePort{}, []v1.IPFamily{}, err
}
- return svcNsName, svcPort, nil
+ svcIPFamilies := svc.Spec.IPFamilies
+
+ return svcNsName, svcPort, svcIPFamilies, nil
+}
+
+func verifyIPFamily(npCfg *NginxProxy, svcIPFamily []v1.IPFamily) error {
+ if npCfg.Source == nil {
+ return nil
+ }
+
+ // we can access this field since we have already validated that ipFamily is not nil in validateNginxProxy.
+ npIPFamily := npCfg.Source.Spec.IPFamily
+ if *npIPFamily == ngfAPI.IPv4 {
+ if slices.Contains(svcIPFamily, v1.IPv6Protocol) {
+ return fmt.Errorf("service configured with IPv6 stack but NGINX only supports IPv4")
+ }
+ }
+ if *npIPFamily == ngfAPI.IPv6 {
+ if slices.Contains(svcIPFamily, v1.IPv4Protocol) {
+ return fmt.Errorf("service configured with IPv4 stack but NGINX only supports IPv6")
+ }
+ }
+
+ return nil
}
func validateRouteBackendRef(
diff --git a/internal/mode/static/state/graph/backend_refs_test.go b/internal/mode/static/state/graph/backend_refs_test.go
index 140d718864..5086f97fba 100644
--- a/internal/mode/static/state/graph/backend_refs_test.go
+++ b/internal/mode/static/state/graph/backend_refs_test.go
@@ -1,6 +1,7 @@
package graph
import (
+ "fmt"
"testing"
. "github.com/onsi/gomega"
@@ -14,6 +15,7 @@ import (
"sigs.k8s.io/gateway-api/apis/v1alpha3"
"sigs.k8s.io/gateway-api/apis/v1beta1"
+ 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"
"github.com/nginxinc/nginx-gateway-fabric/internal/framework/kinds"
@@ -266,6 +268,7 @@ func TestGetServiceAndPortFromRef(t *testing.T) {
Port: 80,
},
},
+ IPFamilies: []v1.IPFamily{v1.IPv4Protocol},
},
}
svc1NsName := types.NamespacedName{
@@ -285,6 +288,7 @@ func TestGetServiceAndPortFromRef(t *testing.T) {
expServiceNsName types.NamespacedName
name string
expServicePort v1.ServicePort
+ expSvcIPFamily []v1.IPFamily
expErr bool
}{
{
@@ -292,6 +296,7 @@ func TestGetServiceAndPortFromRef(t *testing.T) {
ref: getNormalRef(),
expServiceNsName: svc1NsName,
expServicePort: v1.ServicePort{Port: 80},
+ expSvcIPFamily: []v1.IPFamily{v1.IPv4Protocol},
},
{
name: "service does not exist",
@@ -302,6 +307,7 @@ func TestGetServiceAndPortFromRef(t *testing.T) {
expErr: true,
expServiceNsName: types.NamespacedName{Name: "does-not-exist", Namespace: "test"},
expServicePort: v1.ServicePort{},
+ expSvcIPFamily: []v1.IPFamily{},
},
{
name: "no matching port for service and port",
@@ -312,6 +318,7 @@ func TestGetServiceAndPortFromRef(t *testing.T) {
expErr: true,
expServiceNsName: svc1NsName,
expServicePort: v1.ServicePort{},
+ expSvcIPFamily: []v1.IPFamily{},
},
}
@@ -326,11 +333,69 @@ func TestGetServiceAndPortFromRef(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
g := NewWithT(t)
- svcNsName, servicePort, err := getServiceAndPortFromRef(test.ref, "test", services, refPath)
+ svcNsName, servicePort, svcIPFamily, err := getServiceAndPortFromRef(test.ref, "test", services, refPath)
g.Expect(err != nil).To(Equal(test.expErr))
g.Expect(svcNsName).To(Equal(test.expServiceNsName))
g.Expect(servicePort).To(Equal(test.expServicePort))
+ g.Expect(svcIPFamily).To(Equal(test.expSvcIPFamily))
+ })
+ }
+}
+
+func TestVerifyIPFamily(t *testing.T) {
+ test := []struct {
+ name string
+ expErr error
+ npCfg *NginxProxy
+ svcIPFamily []v1.IPFamily
+ }{
+ {
+ name: "Valid - IPv6 and IPv4 configured for NGINX, service has only IPv4",
+ npCfg: &NginxProxy{
+ Source: &ngfAPI.NginxProxy{
+ Spec: ngfAPI.NginxProxySpec{
+ IPFamily: helpers.GetPointer(ngfAPI.Dual),
+ },
+ },
+ },
+ svcIPFamily: []v1.IPFamily{v1.IPv4Protocol},
+ },
+ {
+ name: "Invalid - IPv4 configured for NGINX, service has only IPv6",
+ npCfg: &NginxProxy{
+ Source: &ngfAPI.NginxProxy{
+ Spec: ngfAPI.NginxProxySpec{
+ IPFamily: helpers.GetPointer(ngfAPI.IPv4),
+ },
+ },
+ },
+ svcIPFamily: []v1.IPFamily{v1.IPv6Protocol},
+ expErr: fmt.Errorf("service configured with IPv6 stack but NGINX only supports IPv4"),
+ },
+ {
+ name: "Invalid - IPv6 configured for NGINX, service has only IPv4",
+ npCfg: &NginxProxy{
+ Source: &ngfAPI.NginxProxy{
+ Spec: ngfAPI.NginxProxySpec{
+ IPFamily: helpers.GetPointer(ngfAPI.IPv6),
+ },
+ },
+ },
+ svcIPFamily: []v1.IPFamily{v1.IPv4Protocol},
+ expErr: fmt.Errorf("service configured with IPv4 stack but NGINX only supports IPv6"),
+ },
+ }
+
+ for _, test := range test {
+ t.Run(test.name, func(t *testing.T) {
+ g := NewWithT(t)
+ err := verifyIPFamily(test.npCfg, test.svcIPFamily)
+ if test.expErr != nil {
+ g.Expect(err).To(Equal(test.expErr))
+ } else {
+ g.Expect(err).ToNot(HaveOccurred())
+ }
})
}
}
@@ -680,7 +745,7 @@ func TestAddBackendRefsToRulesTest(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
g := NewWithT(t)
resolver := newReferenceGrantResolver(nil)
- addBackendRefsToRules(test.route, resolver, services, test.policies)
+ addBackendRefsToRules(test.route, resolver, services, test.policies, &NginxProxy{})
var actual []BackendRef
if test.route.Spec.Rules != nil {
@@ -940,6 +1005,7 @@ func TestCreateBackend(t *testing.T) {
services,
refPath,
policies,
+ &NginxProxy{},
)
g.Expect(helpers.Diff(test.expectedBackend, backend)).To(BeEmpty())
diff --git a/internal/mode/static/state/graph/graph.go b/internal/mode/static/state/graph/graph.go
index 4fc86119cb..d874d5d2c7 100644
--- a/internal/mode/static/state/graph/graph.go
+++ b/internal/mode/static/state/graph/graph.go
@@ -225,7 +225,7 @@ func BuildGraph(
)
bindRoutesToListeners(routes, gw, state.Namespaces)
- addBackendRefsToRouteRules(routes, refGrantResolver, state.Services, processedBackendTLSPolicies)
+ addBackendRefsToRouteRules(routes, refGrantResolver, state.Services, processedBackendTLSPolicies, npCfg)
referencedNamespaces := buildReferencedNamespaces(state.Namespaces, gw)
From 5b38ff987d09165370f1ac602730180ec526ebaf Mon Sep 17 00:00:00 2001
From: Saloni
Date: Fri, 12 Jul 2024 09:58:59 -0600
Subject: [PATCH 12/28] improve verify IPFamily
---
.../static/state/conditions/conditions.go | 4 ++--
.../mode/static/state/graph/backend_refs.go | 6 ++---
.../static/state/graph/backend_refs_test.go | 24 +++++++++++++++++--
.../mode/static/state/graph/nginxproxy.go | 3 +++
.../static/state/graph/nginxproxy_test.go | 3 +++
5 files changed, 33 insertions(+), 7 deletions(-)
diff --git a/internal/mode/static/state/conditions/conditions.go b/internal/mode/static/state/conditions/conditions.go
index a85dc4835f..c0b354670d 100644
--- a/internal/mode/static/state/conditions/conditions.go
+++ b/internal/mode/static/state/conditions/conditions.go
@@ -42,7 +42,7 @@ const (
// RouteReasonInvalidIPFamily is used when the Service associated with the Route is not configured with
// the same IP family as the NGINX server.
- RouteReasonInvalidIPFamily v1.RouteConditionReason = "InvalidIPFamily"
+ RouteReasonInvalidIPFamily v1.RouteConditionReason = "InvalidServiceIPFamily"
// GatewayReasonGatewayConflict indicates there are multiple Gateway resources to choose from,
// and we ignored the resource in question and picked another Gateway as the winner.
@@ -282,7 +282,7 @@ func NewRouteGatewayNotProgrammed(msg string) conditions.Condition {
func NewRouteInvalidIPFamily(msg string) conditions.Condition {
return conditions.Condition{
- Type: string(v1.RouteConditionAccepted),
+ Type: string(v1.RouteConditionResolvedRefs),
Status: metav1.ConditionFalse,
Reason: string(RouteReasonInvalidIPFamily),
Message: msg,
diff --git a/internal/mode/static/state/graph/backend_refs.go b/internal/mode/static/state/graph/backend_refs.go
index fef67e2600..2aa1dcce5e 100644
--- a/internal/mode/static/state/graph/backend_refs.go
+++ b/internal/mode/static/state/graph/backend_refs.go
@@ -322,7 +322,7 @@ func getServiceAndPortFromRef(
}
func verifyIPFamily(npCfg *NginxProxy, svcIPFamily []v1.IPFamily) error {
- if npCfg.Source == nil {
+ if npCfg == nil || npCfg.Source == nil || !npCfg.Valid {
return nil
}
@@ -330,12 +330,12 @@ func verifyIPFamily(npCfg *NginxProxy, svcIPFamily []v1.IPFamily) error {
npIPFamily := npCfg.Source.Spec.IPFamily
if *npIPFamily == ngfAPI.IPv4 {
if slices.Contains(svcIPFamily, v1.IPv6Protocol) {
- return fmt.Errorf("service configured with IPv6 stack but NGINX only supports IPv4")
+ return fmt.Errorf("service configured with IPv6 family but NginxProxy is configured with IPv4")
}
}
if *npIPFamily == ngfAPI.IPv6 {
if slices.Contains(svcIPFamily, v1.IPv4Protocol) {
- return fmt.Errorf("service configured with IPv4 stack but NGINX only supports IPv6")
+ return fmt.Errorf("service configured with IPv4 family but NginxProxy is configured with IPv6")
}
}
diff --git a/internal/mode/static/state/graph/backend_refs_test.go b/internal/mode/static/state/graph/backend_refs_test.go
index 5086f97fba..d767a95053 100644
--- a/internal/mode/static/state/graph/backend_refs_test.go
+++ b/internal/mode/static/state/graph/backend_refs_test.go
@@ -358,9 +358,22 @@ func TestVerifyIPFamily(t *testing.T) {
IPFamily: helpers.GetPointer(ngfAPI.Dual),
},
},
+ Valid: true,
},
svcIPFamily: []v1.IPFamily{v1.IPv4Protocol},
},
+ {
+ name: "Valid - IPv6 and IPv4 configured for NGINX, service has only IPv6",
+ npCfg: &NginxProxy{
+ Source: &ngfAPI.NginxProxy{
+ Spec: ngfAPI.NginxProxySpec{
+ IPFamily: helpers.GetPointer(ngfAPI.Dual),
+ },
+ },
+ Valid: true,
+ },
+ svcIPFamily: []v1.IPFamily{v1.IPv6Protocol},
+ },
{
name: "Invalid - IPv4 configured for NGINX, service has only IPv6",
npCfg: &NginxProxy{
@@ -369,9 +382,10 @@ func TestVerifyIPFamily(t *testing.T) {
IPFamily: helpers.GetPointer(ngfAPI.IPv4),
},
},
+ Valid: true,
},
svcIPFamily: []v1.IPFamily{v1.IPv6Protocol},
- expErr: fmt.Errorf("service configured with IPv6 stack but NGINX only supports IPv4"),
+ expErr: fmt.Errorf("service configured with IPv6 family but NginxProxy is configured with IPv4"),
},
{
name: "Invalid - IPv6 configured for NGINX, service has only IPv4",
@@ -381,9 +395,15 @@ func TestVerifyIPFamily(t *testing.T) {
IPFamily: helpers.GetPointer(ngfAPI.IPv6),
},
},
+ Valid: true,
},
svcIPFamily: []v1.IPFamily{v1.IPv4Protocol},
- expErr: fmt.Errorf("service configured with IPv4 stack but NGINX only supports IPv6"),
+ expErr: fmt.Errorf("service configured with IPv4 family but NginxProxy is configured with IPv6"),
+ },
+ {
+ name: "Invalid - When NginxProxy is nil",
+ npCfg: &NginxProxy{},
+ svcIPFamily: []v1.IPFamily{v1.IPv4Protocol},
},
}
diff --git a/internal/mode/static/state/graph/nginxproxy.go b/internal/mode/static/state/graph/nginxproxy.go
index b79f63b627..a3449d711e 100644
--- a/internal/mode/static/state/graph/nginxproxy.go
+++ b/internal/mode/static/state/graph/nginxproxy.go
@@ -6,6 +6,7 @@ import (
v1 "sigs.k8s.io/gateway-api/apis/v1"
ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1"
+ "github.com/nginxinc/nginx-gateway-fabric/internal/framework/helpers"
"github.com/nginxinc/nginx-gateway-fabric/internal/framework/kinds"
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/validation"
)
@@ -122,6 +123,8 @@ func validateNginxProxy(
[]string{string(ngfAPI.Dual), string(ngfAPI.IPv4), string(ngfAPI.IPv6)}),
)
}
+ } else {
+ npCfg.Spec.IPFamily = helpers.GetPointer[ngfAPI.IPFamilyType](ngfAPI.Dual)
}
return allErrs
diff --git a/internal/mode/static/state/graph/nginxproxy_test.go b/internal/mode/static/state/graph/nginxproxy_test.go
index a664fa80d2..efec06e865 100644
--- a/internal/mode/static/state/graph/nginxproxy_test.go
+++ b/internal/mode/static/state/graph/nginxproxy_test.go
@@ -71,6 +71,9 @@ func TestGetNginxProxy(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{
Name: "np2",
},
+ Spec: ngfAPI.NginxProxySpec{
+ IPFamily: helpers.GetPointer(ngfAPI.Dual),
+ },
},
Valid: true,
},
From b3dcc2c1876acbc601d7d1a5330e7486e4091719 Mon Sep 17 00:00:00 2001
From: salonichf5 <146118978+salonichf5@users.noreply.github.com>
Date: Fri, 12 Jul 2024 15:34:01 -0600
Subject: [PATCH 13/28] Update
internal/mode/static/state/resolver/resolver_test.go
Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com>
---
internal/mode/static/state/resolver/resolver_test.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/internal/mode/static/state/resolver/resolver_test.go b/internal/mode/static/state/resolver/resolver_test.go
index adc2051175..4ba6a8f85c 100644
--- a/internal/mode/static/state/resolver/resolver_test.go
+++ b/internal/mode/static/state/resolver/resolver_test.go
@@ -28,7 +28,7 @@ var (
Conditions: discoveryV1.EndpointConditions{Ready: helpers.GetPointer(true)},
}
- readyEndpoint2 = discoveryV1.Endpoint{
+ readyIPv6Endpoint2 = discoveryV1.Endpoint{
Addresses: addressesIPv6,
Conditions: discoveryV1.EndpointConditions{Ready: helpers.GetPointer(true)},
}
From c80427420b3dbddd3b3ab088e1f700a83b80119a Mon Sep 17 00:00:00 2001
From: salonichf5 <146118978+salonichf5@users.noreply.github.com>
Date: Fri, 12 Jul 2024 15:34:31 -0600
Subject: [PATCH 14/28] Update
internal/mode/static/nginx/config/upstreams_test.go
Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com>
---
internal/mode/static/nginx/config/upstreams_test.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/internal/mode/static/nginx/config/upstreams_test.go b/internal/mode/static/nginx/config/upstreams_test.go
index 351345a21f..f45b942224 100644
--- a/internal/mode/static/nginx/config/upstreams_test.go
+++ b/internal/mode/static/nginx/config/upstreams_test.go
@@ -36,7 +36,7 @@ func TestExecuteUpstreams(t *testing.T) {
Endpoints: []resolver.Endpoint{},
},
{
- Name: "up4",
+ Name: "up-ipv6",
Endpoints: []resolver.Endpoint{
{
Address: "2001:db8::1",
From a578387a4d4a93d37e991a1d33ef4fb2ba44159d Mon Sep 17 00:00:00 2001
From: salonichf5 <146118978+salonichf5@users.noreply.github.com>
Date: Fri, 12 Jul 2024 15:34:53 -0600
Subject: [PATCH 15/28] Update internal/mode/static/nginx/config/servers.go
Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com>
---
internal/mode/static/nginx/config/servers.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/internal/mode/static/nginx/config/servers.go b/internal/mode/static/nginx/config/servers.go
index f56bdcf60b..df42fba802 100644
--- a/internal/mode/static/nginx/config/servers.go
+++ b/internal/mode/static/nginx/config/servers.go
@@ -63,7 +63,7 @@ func executeServers(conf dataplane.Configuration) []executeResult {
serverConfig := http.ServerConfig{
Servers: servers,
- IPFamily: ipFamily,
+ IPFamily: getIPFamily(conf.BaseHTTPConfig),
}
serverResult := executeResult{
From 624fd81c544eeea106821389468d34018de672c2 Mon Sep 17 00:00:00 2001
From: salonichf5 <146118978+salonichf5@users.noreply.github.com>
Date: Fri, 12 Jul 2024 15:35:11 -0600
Subject: [PATCH 16/28] Update
internal/mode/static/state/dataplane/configuration_test.go
Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com>
---
internal/mode/static/state/dataplane/configuration_test.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/internal/mode/static/state/dataplane/configuration_test.go b/internal/mode/static/state/dataplane/configuration_test.go
index 7409b9e630..f5c1ca4674 100644
--- a/internal/mode/static/state/dataplane/configuration_test.go
+++ b/internal/mode/static/state/dataplane/configuration_test.go
@@ -2394,7 +2394,7 @@ func TestBuildConfiguration(t *testing.T) {
BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: IPv6},
Telemetry: Telemetry{},
},
- msg: "NginxProxy with IPv6 IPFamily",
+ msg: "NginxProxy with IPv6 IPFamily and no routes",
},
}
From de05457088036f598b78a0393c2897dd15fde145 Mon Sep 17 00:00:00 2001
From: salonichf5 <146118978+salonichf5@users.noreply.github.com>
Date: Fri, 12 Jul 2024 15:35:27 -0600
Subject: [PATCH 17/28] Update internal/mode/static/state/graph/backend_refs.go
Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com>
---
internal/mode/static/state/graph/backend_refs.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/internal/mode/static/state/graph/backend_refs.go b/internal/mode/static/state/graph/backend_refs.go
index 2aa1dcce5e..063dcbc4f1 100644
--- a/internal/mode/static/state/graph/backend_refs.go
+++ b/internal/mode/static/state/graph/backend_refs.go
@@ -162,7 +162,7 @@ func createBackendRef(
}
err = verifyIPFamily(npCfg, svcIPFamilies)
- if err != nil {
+ if err := verifyIPFamily(npCfg, svcIPFamilies); err != nil {
backendRef = BackendRef{
SvcNsName: svcNsName,
ServicePort: svcPort,
From fe42fa56f1ef57b434bfaa8626c49ad5bb1e7e82 Mon Sep 17 00:00:00 2001
From: salonichf5 <146118978+salonichf5@users.noreply.github.com>
Date: Fri, 12 Jul 2024 15:36:05 -0600
Subject: [PATCH 18/28] Update internal/mode/static/state/graph/backend_refs.go
Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com>
---
internal/mode/static/state/graph/backend_refs.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/internal/mode/static/state/graph/backend_refs.go b/internal/mode/static/state/graph/backend_refs.go
index 063dcbc4f1..01090ed6bf 100644
--- a/internal/mode/static/state/graph/backend_refs.go
+++ b/internal/mode/static/state/graph/backend_refs.go
@@ -330,7 +330,7 @@ func verifyIPFamily(npCfg *NginxProxy, svcIPFamily []v1.IPFamily) error {
npIPFamily := npCfg.Source.Spec.IPFamily
if *npIPFamily == ngfAPI.IPv4 {
if slices.Contains(svcIPFamily, v1.IPv6Protocol) {
- return fmt.Errorf("service configured with IPv6 family but NginxProxy is configured with IPv4")
+ return errors.New("service configured with IPv6 family but NginxProxy is configured with IPv4")
}
}
if *npIPFamily == ngfAPI.IPv6 {
From 82584b2e5004539138594ae855a8c6bd8df65ca6 Mon Sep 17 00:00:00 2001
From: salonichf5 <146118978+salonichf5@users.noreply.github.com>
Date: Fri, 12 Jul 2024 15:36:23 -0600
Subject: [PATCH 19/28] Update internal/mode/static/state/graph/backend_refs.go
Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com>
---
internal/mode/static/state/graph/backend_refs.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/internal/mode/static/state/graph/backend_refs.go b/internal/mode/static/state/graph/backend_refs.go
index 01090ed6bf..d72a7cbe9d 100644
--- a/internal/mode/static/state/graph/backend_refs.go
+++ b/internal/mode/static/state/graph/backend_refs.go
@@ -335,7 +335,7 @@ func verifyIPFamily(npCfg *NginxProxy, svcIPFamily []v1.IPFamily) error {
}
if *npIPFamily == ngfAPI.IPv6 {
if slices.Contains(svcIPFamily, v1.IPv4Protocol) {
- return fmt.Errorf("service configured with IPv4 family but NginxProxy is configured with IPv6")
+ return errors.New("service configured with IPv4 family but NginxProxy is configured with IPv6")
}
}
From 931c61359b5e4422016df77fb7889f0c21f158ab Mon Sep 17 00:00:00 2001
From: salonichf5 <146118978+salonichf5@users.noreply.github.com>
Date: Fri, 12 Jul 2024 15:36:41 -0600
Subject: [PATCH 20/28] Update
internal/mode/static/state/graph/backend_refs_test.go
Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com>
---
internal/mode/static/state/graph/backend_refs_test.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/internal/mode/static/state/graph/backend_refs_test.go b/internal/mode/static/state/graph/backend_refs_test.go
index d767a95053..e78f8c8c3d 100644
--- a/internal/mode/static/state/graph/backend_refs_test.go
+++ b/internal/mode/static/state/graph/backend_refs_test.go
@@ -385,7 +385,7 @@ func TestVerifyIPFamily(t *testing.T) {
Valid: true,
},
svcIPFamily: []v1.IPFamily{v1.IPv6Protocol},
- expErr: fmt.Errorf("service configured with IPv6 family but NginxProxy is configured with IPv4"),
+ expErr: errors.New("service configured with IPv6 family but NginxProxy is configured with IPv4"),
},
{
name: "Invalid - IPv6 configured for NGINX, service has only IPv4",
From 2d1938cc6d8356c8178b396c6ca65342d6bbad4a Mon Sep 17 00:00:00 2001
From: salonichf5 <146118978+salonichf5@users.noreply.github.com>
Date: Fri, 12 Jul 2024 15:36:59 -0600
Subject: [PATCH 21/28] Update
internal/mode/static/state/graph/backend_refs_test.go
Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com>
---
internal/mode/static/state/graph/backend_refs_test.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/internal/mode/static/state/graph/backend_refs_test.go b/internal/mode/static/state/graph/backend_refs_test.go
index e78f8c8c3d..044738e16c 100644
--- a/internal/mode/static/state/graph/backend_refs_test.go
+++ b/internal/mode/static/state/graph/backend_refs_test.go
@@ -398,7 +398,7 @@ func TestVerifyIPFamily(t *testing.T) {
Valid: true,
},
svcIPFamily: []v1.IPFamily{v1.IPv4Protocol},
- expErr: fmt.Errorf("service configured with IPv4 family but NginxProxy is configured with IPv6"),
+ expErr: errors.New("service configured with IPv4 family but NginxProxy is configured with IPv6"),
},
{
name: "Invalid - When NginxProxy is nil",
From efe7fa1e4e3f87c2f6fd8277f77300397190a453 Mon Sep 17 00:00:00 2001
From: Saloni
Date: Fri, 12 Jul 2024 15:41:48 -0600
Subject: [PATCH 22/28] address comments
---
config/cluster/kind-cluster.yaml | 2 -
internal/mode/static/nginx/config/servers.go | 1 -
.../mode/static/nginx/config/servers_test.go | 321 ++++++++++--------
.../static/nginx/config/upstreams_test.go | 2 +-
.../static/state/conditions/conditions.go | 3 +
.../static/state/dataplane/configuration.go | 15 +-
.../state/dataplane/configuration_test.go | 35 +-
.../mode/static/state/graph/backend_refs.go | 16 +-
.../static/state/graph/backend_refs_test.go | 7 +-
.../mode/static/state/graph/nginxproxy.go | 15 +-
.../static/state/resolver/resolver_test.go | 2 +-
11 files changed, 245 insertions(+), 174 deletions(-)
diff --git a/config/cluster/kind-cluster.yaml b/config/cluster/kind-cluster.yaml
index ce0de55cf1..d61c10ca6f 100644
--- a/config/cluster/kind-cluster.yaml
+++ b/config/cluster/kind-cluster.yaml
@@ -4,6 +4,4 @@ nodes:
- role: control-plane
networking:
ipFamily: dual
- # ipFamily: ipv6
- # ipFamily: ipv4
apiServerAddress: 127.0.0.1
diff --git a/internal/mode/static/nginx/config/servers.go b/internal/mode/static/nginx/config/servers.go
index df42fba802..80d7bf3de0 100644
--- a/internal/mode/static/nginx/config/servers.go
+++ b/internal/mode/static/nginx/config/servers.go
@@ -58,7 +58,6 @@ var grpcBaseHeaders = []http.Header{
}
func executeServers(conf dataplane.Configuration) []executeResult {
- ipFamily := getIPFamily(conf.BaseHTTPConfig)
servers, httpMatchPairs := createServers(conf.HTTPServers, conf.SSLServers)
serverConfig := http.ServerConfig{
diff --git a/internal/mode/static/nginx/config/servers_test.go b/internal/mode/static/nginx/config/servers_test.go
index 1e991a337e..effb0099ab 100644
--- a/internal/mode/static/nginx/config/servers_test.go
+++ b/internal/mode/static/nginx/config/servers_test.go
@@ -15,39 +15,162 @@ import (
)
func TestExecuteServers(t *testing.T) {
- tests := []struct {
- msg string
- expectedHTTPConfig map[string]int
- expectedHTTPMatchVars string
- expectedIncludedFileConfigs map[string]string
- config dataplane.Configuration
- }{
- {
- msg: "http and ssl servers with IPv4 IP family",
- config: dataplane.Configuration{
- HTTPServers: []dataplane.VirtualServer{
+ conf := dataplane.Configuration{
+ HTTPServers: []dataplane.VirtualServer{
+ {
+ IsDefault: true,
+ Port: 8080,
+ },
+ {
+ Hostname: "example.com",
+ Port: 8080,
+ },
+ {
+ Hostname: "cafe.example.com",
+ Port: 8080,
+ Additions: []dataplane.Addition{
{
- IsDefault: true,
- Port: 8080,
+ Bytes: []byte("addition-1"),
+ Identifier: "addition-1",
},
+ },
+ },
+ },
+ SSLServers: []dataplane.VirtualServer{
+ {
+ IsDefault: true,
+ Port: 8443,
+ },
+ {
+ Hostname: "example.com",
+ SSL: &dataplane.SSL{
+ KeyPairID: "test-keypair",
+ },
+ Port: 8443,
+ },
+ {
+ Hostname: "cafe.example.com",
+ SSL: &dataplane.SSL{
+ KeyPairID: "test-keypair",
+ },
+ Port: 8443,
+ PathRules: []dataplane.PathRule{
{
- Hostname: "example.com",
- Port: 8080,
+ Path: "/",
+ PathType: dataplane.PathTypePrefix,
+ MatchRules: []dataplane.MatchRule{
+ {
+ Match: dataplane.Match{},
+ BackendGroup: dataplane.BackendGroup{
+ Source: types.NamespacedName{Namespace: "test", Name: "route1"},
+ RuleIdx: 0,
+ Backends: []dataplane.Backend{
+ {
+ UpstreamName: "test_foo_443",
+ Valid: true,
+ Weight: 1,
+ VerifyTLS: &dataplane.VerifyTLS{
+ CertBundleID: "test-foo",
+ Hostname: "test-foo.example.com",
+ },
+ },
+ },
+ },
+ },
+ },
},
},
- SSLServers: []dataplane.VirtualServer{
+ Additions: []dataplane.Addition{
{
- IsDefault: true,
- Port: 8443,
+ Bytes: []byte("addition-1"),
+ Identifier: "addition-1", // duplicate
},
{
- Hostname: "example.com",
- SSL: &dataplane.SSL{
- KeyPairID: "test-keypair",
- },
- Port: 8443,
+ Bytes: []byte("addition-2"),
+ Identifier: "addition-2",
},
},
+ },
+ },
+ }
+
+ expSubStrings := map[string]int{
+ "listen 8080 default_server;": 1,
+ "listen 8080;": 2,
+ "listen 8443 ssl;": 2,
+ "listen 8443 ssl default_server;": 1,
+ "server_name example.com;": 2,
+ "server_name cafe.example.com;": 2,
+ "ssl_certificate /etc/nginx/secrets/test-keypair.pem;": 2,
+ "ssl_certificate_key /etc/nginx/secrets/test-keypair.pem;": 2,
+ "proxy_ssl_server_name on;": 1,
+ }
+
+ type assertion func(g *WithT, data string)
+
+ expectedResults := map[string]assertion{
+ httpConfigFile: func(g *WithT, data string) {
+ for expSubStr, expCount := range expSubStrings {
+ g.Expect(strings.Count(data, expSubStr)).To(Equal(expCount))
+ }
+ },
+ httpMatchVarsFile: func(g *WithT, data string) {
+ g.Expect(data).To(Equal("{}"))
+ },
+ includesFolder + "/addition-1.conf": func(g *WithT, data string) {
+ g.Expect(data).To(Equal("addition-1"))
+ },
+ includesFolder + "/addition-2.conf": func(g *WithT, data string) {
+ g.Expect(data).To(Equal("addition-2"))
+ },
+ }
+ g := NewWithT(t)
+
+ results := executeServers(conf)
+ g.Expect(results).To(HaveLen(len(expectedResults)))
+
+ for _, res := range results {
+ g.Expect(expectedResults).To(HaveKey(res.dest), "executeServers returned unexpected result destination")
+
+ assertData := expectedResults[res.dest]
+ assertData(g, string(res.data))
+ }
+}
+
+func TestExecuteServersForIPFamily(t *testing.T) {
+ httpServers := []dataplane.VirtualServer{
+ {
+ IsDefault: true,
+ Port: 8080,
+ },
+ {
+ Hostname: "example.com",
+ Port: 8080,
+ },
+ }
+ sslServers := []dataplane.VirtualServer{
+ {
+ IsDefault: true,
+ Port: 8443,
+ },
+ {
+ Hostname: "example.com",
+ SSL: &dataplane.SSL{
+ KeyPairID: "test-keypair",
+ },
+ Port: 8443,
+ },
+ }
+ tests := []struct {
+ msg string
+ expectedHTTPConfig map[string]int
+ config dataplane.Configuration
+ }{
+ {
+ msg: "http and ssl servers with IPv4 IP family",
+ config: dataplane.Configuration{
+ HTTPServers: httpServers,
+ SSLServers: sslServers,
BaseHTTPConfig: dataplane.BaseHTTPConfig{
IPFamily: dataplane.IPv4,
},
@@ -62,142 +185,64 @@ func TestExecuteServers(t *testing.T) {
"ssl_certificate_key /etc/nginx/secrets/test-keypair.pem;": 1,
"ssl_reject_handshake on;": 1,
},
- expectedHTTPMatchVars: "{}",
},
{
- msg: "http and ssl servers with dual IP family",
+ msg: "http and ssl servers with IPv6 IP family",
config: dataplane.Configuration{
- HTTPServers: []dataplane.VirtualServer{
- {
- IsDefault: true,
- Port: 8080,
- },
- {
- Hostname: "example.com",
- Port: 8080,
- },
- {
- Hostname: "cafe.example.com",
- Port: 8080,
- Additions: []dataplane.Addition{
- {
- Bytes: []byte("addition-1"),
- Identifier: "addition-1",
- },
- },
- },
- },
- SSLServers: []dataplane.VirtualServer{
- {
- IsDefault: true,
- Port: 8443,
- },
- {
- Hostname: "example.com",
- SSL: &dataplane.SSL{
- KeyPairID: "test-keypair",
- },
- Port: 8443,
- },
- {
- Hostname: "cafe.example.com",
- SSL: &dataplane.SSL{
- KeyPairID: "test-keypair",
- },
- Port: 8443,
- PathRules: []dataplane.PathRule{
- {
- Path: "/",
- PathType: dataplane.PathTypePrefix,
- MatchRules: []dataplane.MatchRule{
- {
- Match: dataplane.Match{},
- BackendGroup: dataplane.BackendGroup{
- Source: types.NamespacedName{Namespace: "test", Name: "route1"},
- RuleIdx: 0,
- Backends: []dataplane.Backend{
- {
- UpstreamName: "test_foo_443",
- Valid: true,
- Weight: 1,
- VerifyTLS: &dataplane.VerifyTLS{
- CertBundleID: "test-foo",
- Hostname: "test-foo.example.com",
- },
- },
- },
- },
- },
- },
- },
- },
- Additions: []dataplane.Addition{
- {
- Bytes: []byte("addition-1"),
- Identifier: "addition-1", // duplicate
- },
- {
- Bytes: []byte("addition-2"),
- Identifier: "addition-2",
- },
- },
- },
+ HTTPServers: httpServers,
+ SSLServers: sslServers,
+ BaseHTTPConfig: dataplane.BaseHTTPConfig{
+ IPFamily: dataplane.IPv6,
},
+ },
+ expectedHTTPConfig: map[string]int{
+ "listen [::]:8080 default_server;": 1,
+ "listen [::]:8080;": 1,
+ "listen [::]:8443 ssl default_server;": 1,
+ "listen [::]:8443 ssl;": 1,
+ "server_name example.com;": 2,
+ "ssl_certificate /etc/nginx/secrets/test-keypair.pem;": 1,
+ "ssl_certificate_key /etc/nginx/secrets/test-keypair.pem;": 1,
+ "ssl_reject_handshake on;": 1,
+ },
+ },
+ {
+ msg: "http and ssl servers with Dual IP family",
+ config: dataplane.Configuration{
+ HTTPServers: httpServers,
+ SSLServers: sslServers,
BaseHTTPConfig: dataplane.BaseHTTPConfig{
IPFamily: dataplane.Dual,
},
},
expectedHTTPConfig: map[string]int{
"listen 8080 default_server;": 1,
- "listen [::]:8080 default_server;": 1,
- "listen 8080;": 2,
- "listen [::]:8080;": 2,
- "listen 8443 ssl;": 2,
- "listen [::]:8443 ssl;": 2,
+ "listen 8080;": 1,
"listen 8443 ssl default_server;": 1,
- "listen [::]:8443 ssl default_server;": 1,
+ "listen 8443 ssl;": 1,
"server_name example.com;": 2,
- "server_name cafe.example.com;": 2,
- "ssl_certificate /etc/nginx/secrets/test-keypair.pem;": 2,
- "ssl_certificate_key /etc/nginx/secrets/test-keypair.pem;": 2,
- "proxy_ssl_server_name on;": 1,
- },
- expectedHTTPMatchVars: "{}",
- expectedIncludedFileConfigs: map[string]string{
- includesFolder + "/addition-1.conf": "addition-1",
- includesFolder + "/addition-2.conf": "addition-2",
+ "ssl_certificate /etc/nginx/secrets/test-keypair.pem;": 1,
+ "ssl_certificate_key /etc/nginx/secrets/test-keypair.pem;": 1,
+ "ssl_reject_handshake on;": 1,
+ "listen [::]:8080 default_server;": 1,
+ "listen [::]:8080;": 1,
+ "listen [::]:8443 ssl default_server;": 1,
+ "listen [::]:8443 ssl;": 1,
},
},
}
- type assertion func(g *WithT, data string)
for _, test := range tests {
t.Run(test.msg, func(t *testing.T) {
g := NewWithT(t)
results := executeServers(test.config)
- expectedResults := map[string]assertion{
- httpConfigFile: func(g *WithT, data string) {
- for expSubStr, expCount := range test.expectedHTTPConfig {
- g.Expect(strings.Count(data, expSubStr)).To(Equal(expCount))
- }
- },
- httpMatchVarsFile: func(g *WithT, data string) {
- g.Expect(data).To(Equal(test.expectedHTTPMatchVars))
- },
- }
-
- for file, assertData := range test.expectedIncludedFileConfigs {
- expectedResults[file] = func(g *WithT, data string) {
- g.Expect(data).To(Equal(assertData))
- }
- }
-
- g.Expect(results).To(HaveLen(len(expectedResults)))
+ g.Expect(results).To(HaveLen(2))
+ serverConf := string(results[0].data)
+ httpMatchConf := string(results[1].data)
+ g.Expect(httpMatchConf).To(Equal("{}"))
- for _, res := range results {
- g.Expect(expectedResults).To(HaveKey(res.dest), "executeServers returned unexpected result destination")
- assertData := expectedResults[res.dest]
- assertData(g, string(res.data))
+ for expSubStr, expCount := range test.expectedHTTPConfig {
+ g.Expect(strings.Count(serverConf, expSubStr)).To(Equal(expCount))
}
})
}
diff --git a/internal/mode/static/nginx/config/upstreams_test.go b/internal/mode/static/nginx/config/upstreams_test.go
index f45b942224..c7db4f5756 100644
--- a/internal/mode/static/nginx/config/upstreams_test.go
+++ b/internal/mode/static/nginx/config/upstreams_test.go
@@ -104,7 +104,7 @@ func TestCreateUpstreams(t *testing.T) {
Endpoints: []resolver.Endpoint{},
},
{
- Name: "up4",
+ Name: "up4-ipv6",
Endpoints: []resolver.Endpoint{
{
Address: "fd00:10:244:1::7",
diff --git a/internal/mode/static/state/conditions/conditions.go b/internal/mode/static/state/conditions/conditions.go
index c0b354670d..5b075a6446 100644
--- a/internal/mode/static/state/conditions/conditions.go
+++ b/internal/mode/static/state/conditions/conditions.go
@@ -42,6 +42,7 @@ const (
// RouteReasonInvalidIPFamily is used when the Service associated with the Route is not configured with
// the same IP family as the NGINX server.
+ // Used with ResolvedRefs (false).
RouteReasonInvalidIPFamily v1.RouteConditionReason = "InvalidServiceIPFamily"
// GatewayReasonGatewayConflict indicates there are multiple Gateway resources to choose from,
@@ -280,6 +281,8 @@ func NewRouteGatewayNotProgrammed(msg string) conditions.Condition {
}
}
+// NewRouteInvalidIPFamily returns a Condition that indicates that the Service associated with the Route
+// is not configured with the same IP family as the NGINX server.
func NewRouteInvalidIPFamily(msg string) conditions.Condition {
return conditions.Condition{
Type: string(v1.RouteConditionResolvedRefs),
diff --git a/internal/mode/static/state/dataplane/configuration.go b/internal/mode/static/state/dataplane/configuration.go
index 5429698155..536213a1d6 100644
--- a/internal/mode/static/state/dataplane/configuration.go
+++ b/internal/mode/static/state/dataplane/configuration.go
@@ -537,15 +537,14 @@ func buildUpstreams(
}
func getAllowedAddressType(ipFamily IPFamilyType) []discoveryV1.AddressType {
- allowedAddressType := make([]discoveryV1.AddressType, 0)
- if ipFamily == IPv4 || ipFamily == Dual {
- allowedAddressType = append(allowedAddressType, discoveryV1.AddressTypeIPv4)
+ switch ipFamily {
+ case IPv4:
+ return []discoveryV1.AddressType{discoveryV1.AddressTypeIPv4}
+ case IPv6:
+ return []discoveryV1.AddressType{discoveryV1.AddressTypeIPv6}
+ default:
+ return []discoveryV1.AddressType{discoveryV1.AddressTypeIPv4, discoveryV1.AddressTypeIPv6}
}
- if ipFamily == IPv6 || ipFamily == Dual {
- allowedAddressType = append(allowedAddressType, discoveryV1.AddressTypeIPv6)
- }
-
- return allowedAddressType
}
func getListenerHostname(h *v1.Hostname) string {
diff --git a/internal/mode/static/state/dataplane/configuration_test.go b/internal/mode/static/state/dataplane/configuration_test.go
index f5c1ca4674..b0c1295d18 100644
--- a/internal/mode/static/state/dataplane/configuration_test.go
+++ b/internal/mode/static/state/dataplane/configuration_test.go
@@ -634,7 +634,7 @@ func TestBuildConfiguration(t *testing.T) {
ServiceName: helpers.GetPointer("my-svc"),
},
DisableHTTP2: true,
- IPFamily: helpers.GetPointer(ngfAPI.IPv4),
+ IPFamily: helpers.GetPointer(ngfAPI.Dual),
},
},
Valid: true,
@@ -2085,7 +2085,7 @@ func TestBuildConfiguration(t *testing.T) {
SSLServers: []VirtualServer{},
SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: false, IPFamily: IPv4},
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: false, IPFamily: Dual},
Telemetry: Telemetry{
Endpoint: "my-otel.svc:4563",
Interval: "5s",
@@ -3486,3 +3486,34 @@ func TestBuildAdditions(t *testing.T) {
})
}
}
+
+func TestGetAllowedAddressType(t *testing.T) {
+ test := []struct {
+ msg string
+ ipFamily IPFamilyType
+ expected []discoveryV1.AddressType
+ }{
+ {
+ msg: "dual ip family",
+ ipFamily: Dual,
+ expected: []discoveryV1.AddressType{discoveryV1.AddressTypeIPv4, discoveryV1.AddressTypeIPv6},
+ },
+ {
+ msg: "ipv4 ip family",
+ ipFamily: IPv4,
+ expected: []discoveryV1.AddressType{discoveryV1.AddressTypeIPv4},
+ },
+ {
+ msg: "ipv6 ip family",
+ ipFamily: IPv6,
+ expected: []discoveryV1.AddressType{discoveryV1.AddressTypeIPv6},
+ },
+ }
+
+ for _, tc := range test {
+ t.Run(tc.msg, func(t *testing.T) {
+ g := NewWithT(t)
+ g.Expect(getAllowedAddressType(tc.ipFamily)).To(Equal(tc.expected))
+ })
+ }
+}
diff --git a/internal/mode/static/state/graph/backend_refs.go b/internal/mode/static/state/graph/backend_refs.go
index d72a7cbe9d..9edd49c6ff 100644
--- a/internal/mode/static/state/graph/backend_refs.go
+++ b/internal/mode/static/state/graph/backend_refs.go
@@ -1,6 +1,7 @@
package graph
import (
+ "errors"
"fmt"
"slices"
@@ -148,7 +149,8 @@ func createBackendRef(
return backendRef, &cond
}
- svcNsName, svcPort, svcIPFamilies, err := getServiceAndPortFromRef(ref.BackendRef, sourceNamespace, services, refPath)
+ svcIPFamilies, svcPort, err := getServiceAndPortFromRef(ref.BackendRef, sourceNamespace, services, refPath)
+ svcNsName := types.NamespacedName{Namespace: sourceNamespace, Name: string(ref.Name)}
if err != nil {
backendRef = BackendRef{
SvcNsName: svcNsName,
@@ -161,7 +163,6 @@ func createBackendRef(
return backendRef, &cond
}
- err = verifyIPFamily(npCfg, svcIPFamilies)
if err := verifyIPFamily(npCfg, svcIPFamilies); err != nil {
backendRef = BackendRef{
SvcNsName: svcNsName,
@@ -297,28 +298,25 @@ func getServiceAndPortFromRef(
routeNamespace string,
services map[types.NamespacedName]*v1.Service,
refPath *field.Path,
-) (types.NamespacedName, v1.ServicePort, []v1.IPFamily, error) {
+) ([]v1.IPFamily, v1.ServicePort, error) {
ns := routeNamespace
if ref.Namespace != nil {
ns = string(*ref.Namespace)
}
svcNsName := types.NamespacedName{Name: string(ref.Name), Namespace: ns}
-
svc, ok := services[svcNsName]
if !ok {
- return svcNsName, v1.ServicePort{}, []v1.IPFamily{}, field.NotFound(refPath.Child("name"), ref.Name)
+ return []v1.IPFamily{}, v1.ServicePort{}, field.NotFound(refPath.Child("name"), ref.Name)
}
// safe to dereference port here because we already validated that the port is not nil in validateBackendRef.
svcPort, err := getServicePort(svc, int32(*ref.Port))
if err != nil {
- return svcNsName, v1.ServicePort{}, []v1.IPFamily{}, err
+ return []v1.IPFamily{}, v1.ServicePort{}, err
}
- svcIPFamilies := svc.Spec.IPFamilies
-
- return svcNsName, svcPort, svcIPFamilies, nil
+ return svc.Spec.IPFamilies, svcPort, nil
}
func verifyIPFamily(npCfg *NginxProxy, svcIPFamily []v1.IPFamily) error {
diff --git a/internal/mode/static/state/graph/backend_refs_test.go b/internal/mode/static/state/graph/backend_refs_test.go
index 044738e16c..9712dbe8d6 100644
--- a/internal/mode/static/state/graph/backend_refs_test.go
+++ b/internal/mode/static/state/graph/backend_refs_test.go
@@ -1,7 +1,7 @@
package graph
import (
- "fmt"
+ "errors"
"testing"
. "github.com/onsi/gomega"
@@ -333,10 +333,9 @@ func TestGetServiceAndPortFromRef(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
g := NewWithT(t)
- svcNsName, servicePort, svcIPFamily, err := getServiceAndPortFromRef(test.ref, "test", services, refPath)
+ svcIPFamily, servicePort, err := getServiceAndPortFromRef(test.ref, "test", services, refPath)
g.Expect(err != nil).To(Equal(test.expErr))
- g.Expect(svcNsName).To(Equal(test.expServiceNsName))
g.Expect(servicePort).To(Equal(test.expServicePort))
g.Expect(svcIPFamily).To(Equal(test.expSvcIPFamily))
})
@@ -401,7 +400,7 @@ func TestVerifyIPFamily(t *testing.T) {
expErr: errors.New("service configured with IPv4 family but NginxProxy is configured with IPv6"),
},
{
- name: "Invalid - When NginxProxy is nil",
+ name: "Valid - When NginxProxy is nil",
npCfg: &NginxProxy{},
svcIPFamily: []v1.IPFamily{v1.IPv4Protocol},
},
diff --git a/internal/mode/static/state/graph/nginxproxy.go b/internal/mode/static/state/graph/nginxproxy.go
index a3449d711e..d36133308b 100644
--- a/internal/mode/static/state/graph/nginxproxy.go
+++ b/internal/mode/static/state/graph/nginxproxy.go
@@ -113,15 +113,14 @@ func validateNginxProxy(
ipFamily := npCfg.Spec.IPFamily
ipFamilyPath := spec.Child("ipFamily")
switch *ipFamily {
- case ngfAPI.Dual,
- ngfAPI.IPv4,
- ngfAPI.IPv6:
+ case ngfAPI.Dual, ngfAPI.IPv4, ngfAPI.IPv6:
default:
- allErrs = append(allErrs, field.NotSupported(
- ipFamilyPath,
- ipFamily,
- []string{string(ngfAPI.Dual), string(ngfAPI.IPv4), string(ngfAPI.IPv6)}),
- )
+ allErrs = append(
+ allErrs,
+ field.NotSupported(
+ ipFamilyPath,
+ ipFamily,
+ []string{string(ngfAPI.Dual), string(ngfAPI.IPv4), string(ngfAPI.IPv6)}))
}
} else {
npCfg.Spec.IPFamily = helpers.GetPointer[ngfAPI.IPFamilyType](ngfAPI.Dual)
diff --git a/internal/mode/static/state/resolver/resolver_test.go b/internal/mode/static/state/resolver/resolver_test.go
index 4ba6a8f85c..adb87bb1ac 100644
--- a/internal/mode/static/state/resolver/resolver_test.go
+++ b/internal/mode/static/state/resolver/resolver_test.go
@@ -71,7 +71,7 @@ var (
validIPv6EndpointSlice = discoveryV1.EndpointSlice{
AddressType: discoveryV1.AddressTypeIPv6,
- Endpoints: []discoveryV1.Endpoint{readyEndpoint2},
+ Endpoints: []discoveryV1.Endpoint{readyIPv6Endpoint2},
Ports: []discoveryV1.EndpointPort{
{
Name: &svcPortName,
From 4175da4e2ea2261b3a5445ca359fe09cacbe4f3e Mon Sep 17 00:00:00 2001
From: Saloni
Date: Sun, 14 Jul 2024 17:51:09 -0600
Subject: [PATCH 23/28] update compat doc and troubleshooting guide
---
.../static/nginx/config/upstreams_test.go | 6 +--
.../mode/static/state/graph/backend_refs.go | 18 ++++---
.../static/state/graph/backend_refs_test.go | 48 ++++++++++++++++---
.../how-to/monitoring/troubleshooting.md | 27 +++++++++++
.../overview/gateway-api-compatibility.md | 1 +
5 files changed, 83 insertions(+), 17 deletions(-)
diff --git a/internal/mode/static/nginx/config/upstreams_test.go b/internal/mode/static/nginx/config/upstreams_test.go
index c7db4f5756..9b6dbbf7ec 100644
--- a/internal/mode/static/nginx/config/upstreams_test.go
+++ b/internal/mode/static/nginx/config/upstreams_test.go
@@ -36,7 +36,7 @@ func TestExecuteUpstreams(t *testing.T) {
Endpoints: []resolver.Endpoint{},
},
{
- Name: "up-ipv6",
+ Name: "up4-ipv6",
Endpoints: []resolver.Endpoint{
{
Address: "2001:db8::1",
@@ -51,7 +51,7 @@ func TestExecuteUpstreams(t *testing.T) {
"upstream up1",
"upstream up2",
"upstream up3",
- "upstream up4",
+ "upstream up4-ipv6",
"upstream invalid-backend-ref",
"server 10.0.0.0:80;",
"server 11.0.0.0:80;",
@@ -150,7 +150,7 @@ func TestCreateUpstreams(t *testing.T) {
},
},
{
- Name: "up4",
+ Name: "up4-ipv6",
ZoneSize: ossZoneSize,
Servers: []http.UpstreamServer{
{
diff --git a/internal/mode/static/state/graph/backend_refs.go b/internal/mode/static/state/graph/backend_refs.go
index 9edd49c6ff..7cb4b494d1 100644
--- a/internal/mode/static/state/graph/backend_refs.go
+++ b/internal/mode/static/state/graph/backend_refs.go
@@ -6,8 +6,10 @@ import (
"slices"
v1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/validation/field"
+ "sigs.k8s.io/controller-runtime/pkg/client"
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
"sigs.k8s.io/gateway-api/apis/v1alpha3"
@@ -149,8 +151,8 @@ func createBackendRef(
return backendRef, &cond
}
- svcIPFamilies, svcPort, err := getServiceAndPortFromRef(ref.BackendRef, sourceNamespace, services, refPath)
- svcNsName := types.NamespacedName{Namespace: sourceNamespace, Name: string(ref.Name)}
+ svc, svcPort, err := getServiceAndPortFromRef(ref.BackendRef, sourceNamespace, services, refPath)
+ svcNsName := client.ObjectKeyFromObject(svc)
if err != nil {
backendRef = BackendRef{
SvcNsName: svcNsName,
@@ -163,7 +165,7 @@ func createBackendRef(
return backendRef, &cond
}
- if err := verifyIPFamily(npCfg, svcIPFamilies); err != nil {
+ if err := verifyIPFamily(npCfg, svc.Spec.IPFamilies); err != nil {
backendRef = BackendRef{
SvcNsName: svcNsName,
ServicePort: svcPort,
@@ -298,7 +300,7 @@ func getServiceAndPortFromRef(
routeNamespace string,
services map[types.NamespacedName]*v1.Service,
refPath *field.Path,
-) ([]v1.IPFamily, v1.ServicePort, error) {
+) (*v1.Service, v1.ServicePort, error) {
ns := routeNamespace
if ref.Namespace != nil {
ns = string(*ref.Namespace)
@@ -307,16 +309,18 @@ func getServiceAndPortFromRef(
svcNsName := types.NamespacedName{Name: string(ref.Name), Namespace: ns}
svc, ok := services[svcNsName]
if !ok {
- return []v1.IPFamily{}, v1.ServicePort{}, field.NotFound(refPath.Child("name"), ref.Name)
+ return &v1.Service{ObjectMeta: metav1.ObjectMeta{Namespace: ns, Name: string(ref.Name)}},
+ v1.ServicePort{},
+ field.NotFound(refPath.Child("name"), ref.Name)
}
// safe to dereference port here because we already validated that the port is not nil in validateBackendRef.
svcPort, err := getServicePort(svc, int32(*ref.Port))
if err != nil {
- return []v1.IPFamily{}, v1.ServicePort{}, err
+ return &v1.Service{ObjectMeta: metav1.ObjectMeta{Namespace: ns, Name: string(ref.Name)}}, v1.ServicePort{}, err
}
- return svc.Spec.IPFamilies, svcPort, nil
+ return svc, svcPort, nil
}
func verifyIPFamily(npCfg *NginxProxy, svcIPFamily []v1.IPFamily) error {
diff --git a/internal/mode/static/state/graph/backend_refs_test.go b/internal/mode/static/state/graph/backend_refs_test.go
index 9712dbe8d6..f889e1b088 100644
--- a/internal/mode/static/state/graph/backend_refs_test.go
+++ b/internal/mode/static/state/graph/backend_refs_test.go
@@ -284,11 +284,11 @@ func TestGetServiceAndPortFromRef(t *testing.T) {
}
tests := []struct {
+ expSvc *v1.Service
ref gatewayv1.BackendRef
expServiceNsName types.NamespacedName
name string
expServicePort v1.ServicePort
- expSvcIPFamily []v1.IPFamily
expErr bool
}{
{
@@ -296,7 +296,7 @@ func TestGetServiceAndPortFromRef(t *testing.T) {
ref: getNormalRef(),
expServiceNsName: svc1NsName,
expServicePort: v1.ServicePort{Port: 80},
- expSvcIPFamily: []v1.IPFamily{v1.IPv4Protocol},
+ expSvc: svc1,
},
{
name: "service does not exist",
@@ -307,7 +307,11 @@ func TestGetServiceAndPortFromRef(t *testing.T) {
expErr: true,
expServiceNsName: types.NamespacedName{Name: "does-not-exist", Namespace: "test"},
expServicePort: v1.ServicePort{},
- expSvcIPFamily: []v1.IPFamily{},
+ expSvc: &v1.Service{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "does-not-exist", Namespace: "test",
+ },
+ },
},
{
name: "no matching port for service and port",
@@ -318,7 +322,11 @@ func TestGetServiceAndPortFromRef(t *testing.T) {
expErr: true,
expServiceNsName: svc1NsName,
expServicePort: v1.ServicePort{},
- expSvcIPFamily: []v1.IPFamily{},
+ expSvc: &v1.Service{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "service1", Namespace: "test",
+ },
+ },
},
}
@@ -333,11 +341,11 @@ func TestGetServiceAndPortFromRef(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
g := NewWithT(t)
- svcIPFamily, servicePort, err := getServiceAndPortFromRef(test.ref, "test", services, refPath)
+ svc, servicePort, err := getServiceAndPortFromRef(test.ref, "test", services, refPath)
g.Expect(err != nil).To(Equal(test.expErr))
g.Expect(servicePort).To(Equal(test.expServicePort))
- g.Expect(svcIPFamily).To(Equal(test.expSvcIPFamily))
+ g.Expect(svc).To(Equal(test.expSvc))
})
}
}
@@ -790,6 +798,7 @@ func TestCreateBackend(t *testing.T) {
Port: 80,
},
},
+ IPFamilies: []v1.IPFamily{v1.IPv4Protocol},
},
}
}
@@ -855,6 +864,7 @@ func TestCreateBackend(t *testing.T) {
tests := []struct {
expectedCondition *conditions.Condition
+ nginxProxy *NginxProxy
name string
expectedServicePortReference string
ref gatewayv1.HTTPBackendRef
@@ -952,6 +962,30 @@ func TestCreateBackend(t *testing.T) {
),
name: "service doesn't exist",
},
+ {
+ ref: gatewayv1.HTTPBackendRef{
+ BackendRef: getModifiedRef(func(backend gatewayv1.BackendRef) gatewayv1.BackendRef {
+ backend.Name = "service2"
+ return backend
+ }),
+ },
+ expectedBackend: BackendRef{
+ SvcNsName: svc2NamespacedName,
+ ServicePort: svc1.Spec.Ports[0],
+ Weight: 5,
+ Valid: false,
+ },
+ nginxProxy: &NginxProxy{
+ Source: &ngfAPI.NginxProxy{
+ Spec: ngfAPI.NginxProxySpec{IPFamily: helpers.GetPointer(ngfAPI.IPv6)},
+ },
+ Valid: true,
+ },
+ expectedCondition: helpers.GetPointer(
+ staticConds.NewRouteInvalidIPFamily(`service configured with IPv4 family but NginxProxy is configured with IPv6`),
+ ),
+ name: "service IPFamily doesn't match NginxProxy IPFamily",
+ },
{
ref: gatewayv1.HTTPBackendRef{
BackendRef: getModifiedRef(func(backend gatewayv1.BackendRef) gatewayv1.BackendRef {
@@ -1024,7 +1058,7 @@ func TestCreateBackend(t *testing.T) {
services,
refPath,
policies,
- &NginxProxy{},
+ test.nginxProxy,
)
g.Expect(helpers.Diff(test.expectedBackend, backend)).To(BeEmpty())
diff --git a/site/content/how-to/monitoring/troubleshooting.md b/site/content/how-to/monitoring/troubleshooting.md
index 6de1f8f76e..ac4b657e09 100644
--- a/site/content/how-to/monitoring/troubleshooting.md
+++ b/site/content/how-to/monitoring/troubleshooting.md
@@ -405,6 +405,33 @@ Or view the following error message in the NGINX logs:
The request body exceeds the [client_max_body_size](https://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size).
To **resolve** this, you can configure the `client_max_body_size` using the `ClientSettingsPolicy` API. Read the [Client Settings Policy]({{< relref "how-to/traffic-management/client-settings.md" >}}) documentation for more information.
+##### IP Family Mismatch Errors
+
+If you `describe` your HTTPRoute and see the following error:
+
+```text
+ Conditions:
+ Last Transition Time: 2024-07-14T23:36:37Z
+ Message: The route is accepted
+ Observed Generation: 1
+ Reason: Accepted
+ Status: True
+ Type: Accepted
+ Last Transition Time: 2024-07-14T23:36:37Z
+ Message: service configured with IPv4 family but NginxProxy is configured with IPv6
+ Observed Generation: 1
+ Reason: InvalidServiceIPFamily
+ Status: False
+ Type: ResolvedRefs
+ Controller Name: gateway.nginx.org/nginx-gateway-controller
+```
+
+The Service associated with your HTTPRoute is configured with a IP Family different than the one specified in NginxProxy configuration.
+To **resolve** this, you can do the following:
+
+- Change the IPFamily by modifying the field `nginx.config.ipFamily` in the `values.yaml` or add the `--set nginx.config.ipFamily=` flag to the `helm install` command. The supported IPFamilies are `ipv4`, `ipv6` and `dual` (default).
+
+- Adjust the IPFamily of your Service to match that of NginxProxy.
### Further reading
diff --git a/site/content/overview/gateway-api-compatibility.md b/site/content/overview/gateway-api-compatibility.md
index 50c118a41e..c7dd0e0a9b 100644
--- a/site/content/overview/gateway-api-compatibility.md
+++ b/site/content/overview/gateway-api-compatibility.md
@@ -179,6 +179,7 @@ See the [static-mode]({{< relref "/reference/cli-help.md#static-mode">}}) comman
- `ResolvedRefs/False/RefNotPermitted`
- `ResolvedRefs/False/BackendNotFound`
- `ResolvedRefs/False/UnsupportedValue`: Custom reason for when one of the HTTPRoute rules has a backendRef with an unsupported value.
+ - `ResolvedRefs/False/InvalidIPFamily`: Custom reason for when one of the HTTPRoute rules has a backendRef that has unsupported IPFamily.
- `PartiallyInvalid/True/UnsupportedValue`
---
From e3c1b57ed7870ab0ba394ca1924fd2c3dcf28ff3 Mon Sep 17 00:00:00 2001
From: Saloni
Date: Mon, 15 Jul 2024 09:20:49 -0600
Subject: [PATCH 24/28] update doc
---
site/content/overview/gateway-api-compatibility.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/site/content/overview/gateway-api-compatibility.md b/site/content/overview/gateway-api-compatibility.md
index c7dd0e0a9b..c106ec85af 100644
--- a/site/content/overview/gateway-api-compatibility.md
+++ b/site/content/overview/gateway-api-compatibility.md
@@ -179,7 +179,7 @@ See the [static-mode]({{< relref "/reference/cli-help.md#static-mode">}}) comman
- `ResolvedRefs/False/RefNotPermitted`
- `ResolvedRefs/False/BackendNotFound`
- `ResolvedRefs/False/UnsupportedValue`: Custom reason for when one of the HTTPRoute rules has a backendRef with an unsupported value.
- - `ResolvedRefs/False/InvalidIPFamily`: Custom reason for when one of the HTTPRoute rules has a backendRef that has unsupported IPFamily.
+ - `ResolvedRefs/False/InvalidIPFamily`: Custom reason for when one of the HTTPRoute rules has a backendRef that has invalid IPFamily.
- `PartiallyInvalid/True/UnsupportedValue`
---
From 2ea4db67c74fd96bf7aa8affcff97ff93bd5eaf1 Mon Sep 17 00:00:00 2001
From: Saloni
Date: Mon, 15 Jul 2024 09:23:37 -0600
Subject: [PATCH 25/28] update troubleshooting guide
---
site/content/how-to/monitoring/troubleshooting.md | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/site/content/how-to/monitoring/troubleshooting.md b/site/content/how-to/monitoring/troubleshooting.md
index ac4b657e09..a7c15ed8c6 100644
--- a/site/content/how-to/monitoring/troubleshooting.md
+++ b/site/content/how-to/monitoring/troubleshooting.md
@@ -426,12 +426,18 @@ If you `describe` your HTTPRoute and see the following error:
Controller Name: gateway.nginx.org/nginx-gateway-controller
```
-The Service associated with your HTTPRoute is configured with a IP Family different than the one specified in NginxProxy configuration.
-To **resolve** this, you can do the following:
+The Service associated with your HTTPRoute is configured with a IP Family different than the one specified in the NginxProxy configuration.
+To **resolve** this, you can do one of the following:
-- Change the IPFamily by modifying the field `nginx.config.ipFamily` in the `values.yaml` or add the `--set nginx.config.ipFamily=` flag to the `helm install` command. The supported IPFamilies are `ipv4`, `ipv6` and `dual` (default).
+- Update the NginxProxy configuration with the proper [`ipFamily`](({{< relref "reference/api.md" >}})) field. You can edit the NginxProxy configuration using `kubectl edit`. For example:
-- Adjust the IPFamily of your Service to match that of NginxProxy.
+ ```shell
+ kubectl edit -n nginx-gateway nginxproxies.gateway.nginx.org ngf-proxy-config
+ ```
+
+- When installing NGINX Gateway Fabric, change the IPFamily by modifying the field `nginx.config.ipFamily` in the `values.yaml` or add the `--set nginx.config.ipFamily=` flag to the `helm install` command. The supported IPFamilies are `ipv4`, `ipv6` and `dual` (default).
+
+- Adjust the IPFamily of your Service to match that of the NginxProxy configuration.
### Further reading
From b52b90d440f749ec5999965789d82d101322dee6 Mon Sep 17 00:00:00 2001
From: Saloni
Date: Mon, 15 Jul 2024 15:30:35 -0600
Subject: [PATCH 26/28] update based on reviews
---
internal/mode/static/nginx/config/servers.go | 2 +-
.../static/state/dataplane/configuration.go | 6 +-
.../state/dataplane/configuration_test.go | 1646 +++++++----------
.../mode/static/state/graph/backend_refs.go | 17 +-
.../static/state/graph/backend_refs_test.go | 12 +-
.../overview/gateway-api-compatibility.md | 2 +-
6 files changed, 709 insertions(+), 976 deletions(-)
diff --git a/internal/mode/static/nginx/config/servers.go b/internal/mode/static/nginx/config/servers.go
index 80d7bf3de0..3aeefa47c7 100644
--- a/internal/mode/static/nginx/config/servers.go
+++ b/internal/mode/static/nginx/config/servers.go
@@ -93,7 +93,7 @@ func executeServers(conf dataplane.Configuration) []executeResult {
// getIPFamily returns whether the server should be configured for IPv4, IPv6, or both.
func getIPFamily(baseHTTPConfig dataplane.BaseHTTPConfig) http.IPFamily {
- switch ip := baseHTTPConfig.IPFamily; ip {
+ switch baseHTTPConfig.IPFamily {
case dataplane.IPv4:
return http.IPFamily{IPv4: true}
case dataplane.IPv6:
diff --git a/internal/mode/static/state/dataplane/configuration.go b/internal/mode/static/state/dataplane/configuration.go
index 536213a1d6..b81dbfe7a0 100644
--- a/internal/mode/static/state/dataplane/configuration.go
+++ b/internal/mode/static/state/dataplane/configuration.go
@@ -542,8 +542,10 @@ func getAllowedAddressType(ipFamily IPFamilyType) []discoveryV1.AddressType {
return []discoveryV1.AddressType{discoveryV1.AddressTypeIPv4}
case IPv6:
return []discoveryV1.AddressType{discoveryV1.AddressTypeIPv6}
- default:
+ case Dual:
return []discoveryV1.AddressType{discoveryV1.AddressTypeIPv4, discoveryV1.AddressTypeIPv6}
+ default:
+ return []discoveryV1.AddressType{}
}
}
@@ -686,7 +688,7 @@ func buildBaseHTTPConfig(g *graph.Graph) BaseHTTPConfig {
}
if g.NginxProxy.Source.Spec.IPFamily != nil {
- switch ipFamily := g.NginxProxy.Source.Spec.IPFamily; *ipFamily {
+ switch *g.NginxProxy.Source.Spec.IPFamily {
case ngfAPI.IPv4:
baseConfig.IPFamily = IPv4
case ngfAPI.IPv6:
diff --git a/internal/mode/static/state/dataplane/configuration_test.go b/internal/mode/static/state/dataplane/configuration_test.go
index b0c1295d18..273bd2e071 100644
--- a/internal/mode/static/state/dataplane/configuration_test.go
+++ b/internal/mode/static/state/dataplane/configuration_test.go
@@ -27,6 +27,68 @@ import (
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/resolver/resolverfakes"
)
+func getNormalBackendRef() graph.BackendRef {
+ return graph.BackendRef{
+ SvcNsName: types.NamespacedName{Name: "foo", Namespace: "test"},
+ ServicePort: apiv1.ServicePort{Port: 80},
+ Valid: true,
+ Weight: 1,
+ }
+}
+
+func getModifiedBackendRef(mod func(ref graph.BackendRef) graph.BackendRef) graph.BackendRef {
+ return mod(getNormalBackendRef())
+}
+
+func getExpectedConfiguration() Configuration {
+ return Configuration{
+ BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
+ HTTPServers: []VirtualServer{{
+ IsDefault: true,
+ Port: 80,
+ }},
+ SSLServers: []VirtualServer{
+ {
+ IsDefault: true,
+ Port: 443,
+ },
+ },
+ Upstreams: []Upstream{},
+ BackendGroups: []BackendGroup{},
+ SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{
+ "ssl_keypair_test_secret-1": {
+ Cert: []byte("cert-1"),
+ Key: []byte("privateKey-1"),
+ },
+ },
+ CertBundles: map[CertBundleID]CertBundle{},
+ }
+}
+
+func getNormalGraph() *graph.Graph {
+ return &graph.Graph{
+ GatewayClass: &graph.GatewayClass{
+ Source: &v1.GatewayClass{},
+ Valid: true,
+ },
+ Gateway: &graph.Gateway{
+ Source: &v1.Gateway{},
+ Listeners: []*graph.Listener{},
+ },
+ Routes: map[graph.RouteKey]*graph.L7Route{},
+ ReferencedSecrets: map[types.NamespacedName]*graph.Secret{},
+ ReferencedCaCertConfigMaps: map[types.NamespacedName]*graph.CaCertConfigMap{},
+ }
+}
+
+func getModifiedGraph(mod func(g *graph.Graph) *graph.Graph) *graph.Graph {
+ return mod(getNormalGraph())
+}
+
+func getModifiedExpectedConfiguration(mod func(conf Configuration) Configuration) Configuration {
+ return mod(getExpectedConfiguration())
+}
+
func createFakePolicy(name string, kind string) policies.Policy {
fakeKind := &policiesfakes.FakeObjectKind{
GroupVersionKindStub: func() schema.GroupVersionKind {
@@ -121,12 +183,9 @@ func TestBuildConfiguration(t *testing.T) {
fakeResolver := &resolverfakes.FakeServiceResolver{}
fakeResolver.ResolveReturns(fooEndpoints, nil)
- validBackendRef := graph.BackendRef{
- SvcNsName: types.NamespacedName{Name: "foo", Namespace: "test"},
- ServicePort: apiv1.ServicePort{Port: 80},
- Valid: true,
- Weight: 1,
- }
+ validBackendRef := getModifiedBackendRef(func(backend graph.BackendRef) graph.BackendRef {
+ return backend
+ })
expValidBackend := Backend{
UpstreamName: fooUpstreamName,
@@ -666,161 +725,97 @@ func TestBuildConfiguration(t *testing.T) {
expConf Configuration
}{
{
- graph: &graph.Graph{
- GatewayClass: &graph.GatewayClass{
- Source: &v1.GatewayClass{},
- Valid: true,
- },
- Gateway: &graph.Gateway{
- Source: &v1.Gateway{},
- Listeners: []*graph.Listener{},
- },
- Routes: map[graph.RouteKey]*graph.L7Route{},
- },
- expConf: Configuration{
- HTTPServers: []VirtualServer{},
- SSLServers: []VirtualServer{},
- SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
- CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
- },
+ graph: getNormalGraph(),
+ expConf: getModifiedExpectedConfiguration(func(conf Configuration) Configuration {
+ conf.HTTPServers = []VirtualServer{}
+ conf.SSLServers = []VirtualServer{}
+ conf.SSLKeyPairs = map[SSLKeyPairID]SSLKeyPair{}
+ return conf
+ }),
msg: "no listeners and routes",
},
{
- graph: &graph.Graph{
- GatewayClass: &graph.GatewayClass{
- Source: &v1.GatewayClass{},
+ graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
+ g.Gateway.Listeners = append(g.Gateway.Listeners, &graph.Listener{
+ Name: "listener-80-1",
+ Source: listener80,
Valid: true,
- },
- Gateway: &graph.Gateway{
- Source: &v1.Gateway{},
- Listeners: []*graph.Listener{
- {
- Name: "listener-80-1",
- Source: listener80,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{},
- },
- },
- },
- Routes: map[graph.RouteKey]*graph.L7Route{},
- },
- expConf: Configuration{
- HTTPServers: []VirtualServer{
- {
- IsDefault: true,
- Port: 80,
- },
- },
- SSLServers: []VirtualServer{},
- SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
- CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
- },
+ })
+ return g
+ }),
+ expConf: getModifiedExpectedConfiguration(func(conf Configuration) Configuration {
+ conf.SSLServers = []VirtualServer{}
+ conf.SSLKeyPairs = map[SSLKeyPairID]SSLKeyPair{}
+ return conf
+ }),
msg: "http listener with no routes",
},
{
- graph: &graph.Graph{
- GatewayClass: &graph.GatewayClass{
- Source: &v1.GatewayClass{},
- Valid: true,
- },
- Gateway: &graph.Gateway{
- Source: &v1.Gateway{},
- Listeners: []*graph.Listener{
- {
- Name: "listener-80-1",
- Source: listener80,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(hr1Invalid): routeHR1Invalid,
- },
- },
- {
- Name: "listener-443-1",
- Source: listener443, // nil hostname
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(httpsHR1Invalid): httpsRouteHR1Invalid,
- },
- ResolvedSecret: &secret1NsName,
- },
- },
- },
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(hr1Invalid): routeHR1Invalid,
- },
- ReferencedSecrets: map[types.NamespacedName]*graph.Secret{
- secret1NsName: secret1,
- },
- },
- expConf: Configuration{
- HTTPServers: []VirtualServer{
+ graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
+ g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
{
- IsDefault: true,
- Port: 80,
- },
- },
- SSLServers: []VirtualServer{
- {
- IsDefault: true,
- Port: 443,
+ Name: "listener-80-1",
+ Source: listener80,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(hr1Invalid): routeHR1Invalid,
+ },
},
{
- Hostname: wildcardHostname,
- SSL: &SSL{KeyPairID: "ssl_keypair_test_secret-1"},
- Port: 443,
- },
- },
- SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{
- "ssl_keypair_test_secret-1": {
- Cert: []byte("cert-1"),
- Key: []byte("privateKey-1"),
- },
- },
- CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
- },
+ Name: "listener-443-1",
+ Source: listener443, // nil hostname
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(httpsHR1Invalid): httpsRouteHR1Invalid,
+ },
+ ResolvedSecret: &secret1NsName,
+ },
+ }...)
+ g.Routes[graph.CreateRouteKey(hr1Invalid)] = routeHR1Invalid
+ g.ReferencedSecrets[secret1NsName] = secret1
+ return g
+ }),
+ expConf: getModifiedExpectedConfiguration(func(conf Configuration) Configuration {
+ conf.HTTPServers = []VirtualServer{{
+ IsDefault: true,
+ Port: 80,
+ }}
+ conf.SSLServers = append(conf.SSLServers, VirtualServer{
+ Hostname: wildcardHostname,
+ SSL: &SSL{KeyPairID: "ssl_keypair_test_secret-1"},
+ Port: 443,
+ })
+ return conf
+ }),
msg: "http and https listeners with no valid routes",
},
{
- graph: &graph.Graph{
- GatewayClass: &graph.GatewayClass{
- Source: &v1.GatewayClass{},
- Valid: true,
- },
- Gateway: &graph.Gateway{
- Source: &v1.Gateway{},
- Listeners: []*graph.Listener{
- {
- Name: "listener-443-1",
- Source: listener443, // nil hostname
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{},
- ResolvedSecret: &secret1NsName,
- },
- {
- Name: "listener-443-with-hostname",
- Source: listener443WithHostname, // non-nil hostname
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{},
- ResolvedSecret: &secret2NsName,
- },
+ graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
+ g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
+ {
+ Name: "listener-443-1",
+ Source: listener443, // nil hostname
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{},
+ ResolvedSecret: &secret1NsName,
},
- },
- Routes: map[graph.RouteKey]*graph.L7Route{},
- ReferencedSecrets: map[types.NamespacedName]*graph.Secret{
+ {
+ Name: "listener-443-with-hostname",
+ Source: listener443WithHostname, // non-nil hostname
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{},
+ ResolvedSecret: &secret2NsName,
+ },
+ }...)
+ g.ReferencedSecrets = map[types.NamespacedName]*graph.Secret{
secret1NsName: secret1,
secret2NsName: secret2,
- },
- },
- expConf: Configuration{
- HTTPServers: []VirtualServer{},
- SSLServers: []VirtualServer{
- {
- IsDefault: true,
- Port: 443,
- },
+ }
+ return g
+ }),
+ expConf: getModifiedExpectedConfiguration(func(conf Configuration) Configuration {
+ conf.HTTPServers = []VirtualServer{}
+ conf.SSLServers = append(conf.SSLServers, []VirtualServer{
{
Hostname: string(hostname),
SSL: &SSL{KeyPairID: "ssl_keypair_test_secret-2"},
@@ -831,87 +826,61 @@ func TestBuildConfiguration(t *testing.T) {
SSL: &SSL{KeyPairID: "ssl_keypair_test_secret-1"},
Port: 443,
},
- },
- SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{
- "ssl_keypair_test_secret-1": {
- Cert: []byte("cert-1"),
- Key: []byte("privateKey-1"),
- },
- "ssl_keypair_test_secret-2": {
- Cert: []byte("cert-2"),
- Key: []byte("privateKey-2"),
- },
- },
- CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
- },
+ }...)
+ conf.SSLKeyPairs["ssl_keypair_test_secret-2"] = SSLKeyPair{
+ Cert: []byte("cert-2"),
+ Key: []byte("privateKey-2"),
+ }
+ return conf
+ }),
msg: "https listeners with no routes",
},
{
- graph: &graph.Graph{
- GatewayClass: &graph.GatewayClass{
- Source: &v1.GatewayClass{},
- Valid: true,
- },
- Gateway: &graph.Gateway{
- Source: &v1.Gateway{},
- Listeners: []*graph.Listener{
- {
- Name: "invalid-listener",
- Source: invalidListener,
- Valid: false,
- ResolvedSecret: &secret1NsName,
- },
+ graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
+ g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
+ {
+ Name: "invalid-listener",
+ Source: invalidListener,
+ Valid: false,
+ ResolvedSecret: &secret1NsName,
},
- },
- Routes: map[graph.RouteKey]*graph.L7Route{
+ }...)
+ g.Routes = map[graph.RouteKey]*graph.L7Route{
graph.CreateRouteKey(httpsHR1): httpsRouteHR1,
graph.CreateRouteKey(httpsHR2): httpsRouteHR2,
- },
- ReferencedSecrets: map[types.NamespacedName]*graph.Secret{
- secret1NsName: secret1,
- },
- },
- expConf: Configuration{
- HTTPServers: []VirtualServer{},
- SSLServers: []VirtualServer{},
- SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
- CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
- },
+ }
+ g.ReferencedSecrets[secret1NsName] = secret1
+ return g
+ }),
+ expConf: getModifiedExpectedConfiguration(func(conf Configuration) Configuration {
+ conf.HTTPServers = []VirtualServer{}
+ conf.SSLServers = []VirtualServer{}
+ conf.SSLKeyPairs = map[SSLKeyPairID]SSLKeyPair{}
+ return conf
+ }),
msg: "invalid https listener with resolved secret",
},
{
- graph: &graph.Graph{
- GatewayClass: &graph.GatewayClass{
- Source: &v1.GatewayClass{},
- Valid: true,
- },
- Gateway: &graph.Gateway{
- Source: &v1.Gateway{},
- Listeners: []*graph.Listener{
- {
- Name: "listener-80-1",
- Source: listener80,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(hr1): routeHR1,
- graph.CreateRouteKey(hr2): routeHR2,
- },
+ graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
+ g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
+ {
+ Name: "listener-80-1",
+ Source: listener80,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(hr1): routeHR1,
+ graph.CreateRouteKey(hr2): routeHR2,
},
},
- },
- Routes: map[graph.RouteKey]*graph.L7Route{
+ }...)
+ g.Routes = map[graph.RouteKey]*graph.L7Route{
graph.CreateRouteKey(hr1): routeHR1,
graph.CreateRouteKey(hr2): routeHR2,
- },
- },
- expConf: Configuration{
- HTTPServers: []VirtualServer{
- {
- IsDefault: true,
- Port: 80,
- },
+ }
+ return g
+ }),
+ expConf: getModifiedExpectedConfiguration(func(conf Configuration) Configuration {
+ conf.HTTPServers = append(conf.HTTPServers, []VirtualServer{
{
Hostname: "bar.example.com",
PathRules: []PathRule{
@@ -944,119 +913,95 @@ 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{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
- },
+ }...)
+ conf.SSLServers = []VirtualServer{}
+ conf.Upstreams = []Upstream{fooUpstream}
+ conf.BackendGroups = []BackendGroup{expHR1Groups[0], expHR2Groups[0]}
+ conf.SSLKeyPairs = map[SSLKeyPairID]SSLKeyPair{}
+
+ return conf
+ }),
msg: "one http listener with two routes for different hostnames",
},
{
- graph: &graph.Graph{
- GatewayClass: &graph.GatewayClass{
- Source: &v1.GatewayClass{},
- Valid: true,
- },
- Gateway: &graph.Gateway{
- Source: &v1.Gateway{},
- Listeners: []*graph.Listener{
- {
- Name: "listener-80-1",
- Source: listener80,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(gr): routeGR,
- },
- },
- },
- },
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(gr): routeGR,
- },
- },
- expConf: Configuration{
- HTTPServers: []VirtualServer{
+ graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
+ g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
{
- IsDefault: true,
- Port: 80,
+ Name: "listener-80-1",
+ Source: listener80,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(gr): routeGR,
+ },
},
- {
- Hostname: "foo.example.com",
- PathRules: []PathRule{
- {
- Path: "/",
- PathType: PathTypePrefix,
- GRPC: true,
- MatchRules: []MatchRule{
- {
- BackendGroup: expGRGroups[0],
- Source: &gr.ObjectMeta,
- },
+ }...)
+ g.Routes[graph.CreateRouteKey(gr)] = routeGR
+ return g
+ }),
+ expConf: getModifiedExpectedConfiguration(func(conf Configuration) Configuration {
+ conf.HTTPServers = append(conf.HTTPServers, VirtualServer{
+ Hostname: "foo.example.com",
+ PathRules: []PathRule{
+ {
+ Path: "/",
+ PathType: PathTypePrefix,
+ GRPC: true,
+ MatchRules: []MatchRule{
+ {
+ BackendGroup: expGRGroups[0],
+ Source: &gr.ObjectMeta,
},
},
},
- Port: 80,
},
+ Port: 80,
},
- SSLServers: []VirtualServer{},
- Upstreams: []Upstream{fooUpstream},
- BackendGroups: []BackendGroup{expGRGroups[0]},
- SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
- CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
- },
+ )
+ conf.SSLServers = []VirtualServer{}
+ conf.Upstreams = append(conf.Upstreams, fooUpstream)
+ conf.BackendGroups = []BackendGroup{expGRGroups[0]}
+ conf.SSLKeyPairs = map[SSLKeyPairID]SSLKeyPair{}
+ return conf
+ }),
msg: "one http listener with one grpc route",
},
{
- graph: &graph.Graph{
- GatewayClass: &graph.GatewayClass{
- Source: &v1.GatewayClass{},
- Valid: true,
- },
- Gateway: &graph.Gateway{
- Source: &v1.Gateway{},
- Listeners: []*graph.Listener{
- {
- Name: "listener-443-1",
- Source: listener443,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(httpsHR1): httpsRouteHR1,
- graph.CreateRouteKey(httpsHR2): httpsRouteHR2,
- },
- ResolvedSecret: &secret1NsName,
+ graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
+ g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
+ {
+ Name: "listener-443-1",
+ Source: listener443,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(httpsHR1): httpsRouteHR1,
+ graph.CreateRouteKey(httpsHR2): httpsRouteHR2,
},
- {
- Name: "listener-443-with-hostname",
- Source: listener443WithHostname,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(httpsHR5): httpsRouteHR5,
- },
- ResolvedSecret: &secret2NsName,
+ ResolvedSecret: &secret1NsName,
+ },
+ {
+ Name: "listener-443-with-hostname",
+ Source: listener443WithHostname,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(httpsHR5): httpsRouteHR5,
},
+ ResolvedSecret: &secret2NsName,
},
- },
- Routes: map[graph.RouteKey]*graph.L7Route{
+ }...)
+ g.Routes = map[graph.RouteKey]*graph.L7Route{
graph.CreateRouteKey(hr1): httpsRouteHR1,
graph.CreateRouteKey(hr2): httpsRouteHR2,
graph.CreateRouteKey(httpsHR5): httpsRouteHR5,
- },
- ReferencedSecrets: map[types.NamespacedName]*graph.Secret{
+ }
+ g.ReferencedSecrets = map[types.NamespacedName]*graph.Secret{
secret1NsName: secret1,
secret2NsName: secret2,
- },
- },
- expConf: Configuration{
- HTTPServers: []VirtualServer{},
- SSLServers: []VirtualServer{
- {
- IsDefault: true,
- Port: 443,
- },
+ }
+ return g
+ }),
+ expConf: getModifiedExpectedConfiguration(func(conf Configuration) Configuration {
+ conf.HTTPServers = []VirtualServer{}
+ conf.SSLServers = append(conf.SSLServers, []VirtualServer{
{
Hostname: "bar.example.com",
PathRules: []PathRule{
@@ -1113,10 +1058,10 @@ func TestBuildConfiguration(t *testing.T) {
SSL: &SSL{KeyPairID: "ssl_keypair_test_secret-1"},
Port: 443,
},
- },
- Upstreams: []Upstream{fooUpstream},
- BackendGroups: []BackendGroup{expHTTPSHR1Groups[0], expHTTPSHR2Groups[0], expHTTPSHR5Groups[0]},
- SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{
+ }...)
+ conf.Upstreams = []Upstream{fooUpstream}
+ conf.BackendGroups = []BackendGroup{expHTTPSHR1Groups[0], expHTTPSHR2Groups[0], expHTTPSHR5Groups[0]}
+ conf.SSLKeyPairs = map[SSLKeyPairID]SSLKeyPair{
"ssl_keypair_test_secret-1": {
Cert: []byte("cert-1"),
Key: []byte("privateKey-1"),
@@ -1125,58 +1070,47 @@ func TestBuildConfiguration(t *testing.T) {
Cert: []byte("cert-2"),
Key: []byte("privateKey-2"),
},
- },
- CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
- },
+ }
+ return conf
+ }),
msg: "two https listeners each with routes for different hostnames",
},
{
- graph: &graph.Graph{
- GatewayClass: &graph.GatewayClass{
- Source: &v1.GatewayClass{},
- Valid: true,
- },
- Gateway: &graph.Gateway{
- Source: &v1.Gateway{},
- Listeners: []*graph.Listener{
- {
- Name: "listener-80-1",
- Source: listener80,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(hr3): routeHR3,
- graph.CreateRouteKey(hr4): routeHR4,
- },
+ graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
+ g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
+ {
+ Name: "listener-80-1",
+ Source: listener80,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(hr3): routeHR3,
+ graph.CreateRouteKey(hr4): routeHR4,
},
- {
- Name: "listener-443-1",
- Source: listener443,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(httpsHR3): httpsRouteHR3,
- graph.CreateRouteKey(httpsHR4): httpsRouteHR4,
- },
- ResolvedSecret: &secret1NsName,
+ },
+ {
+ Name: "listener-443-1",
+ Source: listener443,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(httpsHR3): httpsRouteHR3,
+ graph.CreateRouteKey(httpsHR4): httpsRouteHR4,
},
+ ResolvedSecret: &secret1NsName,
},
- },
- Routes: map[graph.RouteKey]*graph.L7Route{
+ }...)
+ g.Routes = map[graph.RouteKey]*graph.L7Route{
graph.CreateRouteKey(hr3): routeHR3,
graph.CreateRouteKey(hr4): routeHR4,
graph.CreateRouteKey(httpsHR3): httpsRouteHR3,
graph.CreateRouteKey(httpsHR4): httpsRouteHR4,
- },
- ReferencedSecrets: map[types.NamespacedName]*graph.Secret{
+ }
+ g.ReferencedSecrets = map[types.NamespacedName]*graph.Secret{
secret1NsName: secret1,
- },
- },
- expConf: Configuration{
- HTTPServers: []VirtualServer{
- {
- IsDefault: true,
- Port: 80,
- },
+ }
+ return g
+ }),
+ expConf: getModifiedExpectedConfiguration(func(conf Configuration) Configuration {
+ conf.HTTPServers = append(conf.HTTPServers, []VirtualServer{
{
Hostname: "foo.example.com",
PathRules: []PathRule{
@@ -1217,12 +1151,8 @@ func TestBuildConfiguration(t *testing.T) {
},
Port: 80,
},
- },
- SSLServers: []VirtualServer{
- {
- IsDefault: true,
- Port: 443,
- },
+ }...)
+ conf.SSLServers = append(conf.SSLServers, []VirtualServer{
{
Hostname: "foo.example.com",
SSL: &SSL{KeyPairID: "ssl_keypair_test_secret-1"},
@@ -1269,9 +1199,9 @@ func TestBuildConfiguration(t *testing.T) {
SSL: &SSL{KeyPairID: "ssl_keypair_test_secret-1"},
Port: 443,
},
- },
- Upstreams: []Upstream{fooUpstream},
- BackendGroups: []BackendGroup{
+ }...)
+ conf.Upstreams = append(conf.Upstreams, fooUpstream)
+ conf.BackendGroups = []BackendGroup{
expHR3Groups[0],
expHR3Groups[1],
expHR4Groups[0],
@@ -1280,79 +1210,62 @@ func TestBuildConfiguration(t *testing.T) {
expHTTPSHR3Groups[1],
expHTTPSHR4Groups[0],
expHTTPSHR4Groups[1],
- },
- SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{
- "ssl_keypair_test_secret-1": {
- Cert: []byte("cert-1"),
- Key: []byte("privateKey-1"),
- },
- },
- CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
- },
+ }
+ return conf
+ }),
msg: "one http and one https listener with two routes with the same hostname with and without collisions",
},
{
- graph: &graph.Graph{
- GatewayClass: &graph.GatewayClass{
- Source: &v1.GatewayClass{},
- Valid: true,
- },
- Gateway: &graph.Gateway{
- Source: &v1.Gateway{},
- Listeners: []*graph.Listener{
- {
- Name: "listener-80-1",
- Source: listener80,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(hr3): routeHR3,
- },
+ graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
+ g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
+ {
+ Name: "listener-80-1",
+ Source: listener80,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(hr3): routeHR3,
},
- {
- Name: "listener-8080",
- Source: listener8080,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(hr8): routeHR8,
- },
+ },
+ {
+ Name: "listener-8080",
+ Source: listener8080,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(hr8): routeHR8,
},
- {
- Name: "listener-443-1",
- Source: listener443,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(httpsHR3): httpsRouteHR3,
- },
- ResolvedSecret: &secret1NsName,
+ },
+ {
+ Name: "listener-443-1",
+ Source: listener443,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(httpsHR3): httpsRouteHR3,
},
- {
- Name: "listener-8443",
- Source: listener8443,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(httpsHR7): httpsRouteHR7,
- },
- ResolvedSecret: &secret1NsName,
+ ResolvedSecret: &secret1NsName,
+ },
+ {
+ Name: "listener-8443",
+ Source: listener8443,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(httpsHR7): httpsRouteHR7,
},
+ ResolvedSecret: &secret1NsName,
},
- },
- Routes: map[graph.RouteKey]*graph.L7Route{
+ }...)
+ g.Routes = map[graph.RouteKey]*graph.L7Route{
graph.CreateRouteKey(hr3): routeHR3,
graph.CreateRouteKey(hr8): routeHR8,
graph.CreateRouteKey(httpsHR3): httpsRouteHR3,
graph.CreateRouteKey(httpsHR7): httpsRouteHR7,
- },
- ReferencedSecrets: map[types.NamespacedName]*graph.Secret{
+ }
+ g.ReferencedSecrets = map[types.NamespacedName]*graph.Secret{
secret1NsName: secret1,
- },
- },
- expConf: Configuration{
- HTTPServers: []VirtualServer{
- {
- IsDefault: true,
- Port: 80,
- },
+ }
+ return g
+ }),
+ expConf: getModifiedExpectedConfiguration(func(conf Configuration) Configuration {
+ conf.HTTPServers = append(conf.HTTPServers, []VirtualServer{
{
Hostname: "foo.example.com",
PathRules: []PathRule{
@@ -1409,12 +1322,8 @@ func TestBuildConfiguration(t *testing.T) {
},
Port: 8080,
},
- },
- SSLServers: []VirtualServer{
- {
- IsDefault: true,
- Port: 443,
- },
+ }...)
+ conf.SSLServers = append(conf.SSLServers, []VirtualServer{
{
Hostname: "foo.example.com",
SSL: &SSL{KeyPairID: "ssl_keypair_test_secret-1"},
@@ -1483,9 +1392,9 @@ func TestBuildConfiguration(t *testing.T) {
SSL: &SSL{KeyPairID: "ssl_keypair_test_secret-1"},
Port: 8443,
},
- },
- Upstreams: []Upstream{fooUpstream},
- BackendGroups: []BackendGroup{
+ }...)
+ conf.Upstreams = append(conf.Upstreams, fooUpstream)
+ conf.BackendGroups = []BackendGroup{
expHR3Groups[0],
expHR3Groups[1],
expHR8Groups[0],
@@ -1494,109 +1403,80 @@ func TestBuildConfiguration(t *testing.T) {
expHTTPSHR3Groups[1],
expHTTPSHR7Groups[0],
expHTTPSHR7Groups[1],
- },
- SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{
- "ssl_keypair_test_secret-1": {
- Cert: []byte("cert-1"),
- Key: []byte("privateKey-1"),
- },
- },
- CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
- },
-
+ }
+ return conf
+ }),
msg: "multiple http and https listener; different ports",
},
{
- graph: &graph.Graph{
- GatewayClass: &graph.GatewayClass{
- Source: &v1.GatewayClass{},
- Valid: false,
- },
- Gateway: &graph.Gateway{
- Source: &v1.Gateway{},
- Listeners: []*graph.Listener{
- {
- Name: "listener-80-1",
- Source: listener80,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(hr1): routeHR1,
- },
+ graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
+ g.GatewayClass.Valid = false
+ g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
+ {
+ Name: "listener-80-1",
+ Source: listener80,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(hr1): routeHR1,
},
},
- },
- Routes: map[graph.RouteKey]*graph.L7Route{
+ }...)
+ g.Routes = map[graph.RouteKey]*graph.L7Route{
graph.CreateRouteKey(hr1): routeHR1,
- },
- },
+ }
+ return g
+ }),
expConf: Configuration{},
msg: "invalid gatewayclass",
},
{
- graph: &graph.Graph{
- GatewayClass: nil,
- Gateway: &graph.Gateway{
- Source: &v1.Gateway{},
- Listeners: []*graph.Listener{
- {
- Name: "listener-80-1",
- Source: listener80,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(hr1): routeHR1,
- },
+ graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
+ g.GatewayClass.Valid = false
+ g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
+ {
+ Name: "listener-80-1",
+ Source: listener80,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(hr1): routeHR1,
},
},
- },
- Routes: map[graph.RouteKey]*graph.L7Route{
+ }...)
+ g.Routes = map[graph.RouteKey]*graph.L7Route{
graph.CreateRouteKey(hr1): routeHR1,
- },
- },
+ }
+ return g
+ }),
expConf: Configuration{},
msg: "missing gatewayclass",
},
{
- graph: &graph.Graph{
- GatewayClass: &graph.GatewayClass{
- Source: &v1.GatewayClass{},
- Valid: true,
- },
- Gateway: nil,
- Routes: map[graph.RouteKey]*graph.L7Route{},
- },
+ graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
+ g.Gateway = nil
+ return g
+ }),
expConf: Configuration{},
msg: "missing gateway",
},
{
- graph: &graph.Graph{
- GatewayClass: &graph.GatewayClass{
- Source: &v1.GatewayClass{},
- Valid: true,
- },
- Gateway: &graph.Gateway{
- Source: &v1.Gateway{},
- Listeners: []*graph.Listener{
- {
- Name: "listener-80-1",
- Source: listener80,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(hr5): routeHR5,
- },
+ graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
+ g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
+ {
+ Name: "listener-80-1",
+ Source: listener80,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(hr5): routeHR5,
},
},
- },
- Routes: map[graph.RouteKey]*graph.L7Route{
+ }...)
+ g.Routes = map[graph.RouteKey]*graph.L7Route{
graph.CreateRouteKey(hr5): routeHR5,
- },
- },
- expConf: Configuration{
- HTTPServers: []VirtualServer{
- {
- IsDefault: true,
- Port: 80,
- },
+ }
+ return g
+ }),
+ expConf: getModifiedExpectedConfiguration(func(conf Configuration) Configuration {
+ conf.HTTPServers = append(conf.HTTPServers, []VirtualServer{
{
Hostname: "foo.example.com",
PathRules: []PathRule{
@@ -1629,58 +1509,47 @@ 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{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
- },
+ }...)
+ conf.SSLServers = []VirtualServer{}
+ conf.Upstreams = []Upstream{fooUpstream}
+ conf.BackendGroups = []BackendGroup{expHR5Groups[0], expHR5Groups[1]}
+ conf.SSLKeyPairs = map[SSLKeyPairID]SSLKeyPair{}
+ return conf
+ }),
msg: "one http listener with one route with filters",
},
{
- graph: &graph.Graph{
- GatewayClass: &graph.GatewayClass{
- Source: &v1.GatewayClass{},
- Valid: true,
- },
- Gateway: &graph.Gateway{
- Source: &v1.Gateway{},
- Listeners: []*graph.Listener{
- {
- Name: "listener-80-1",
- Source: listener80,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(hr6): routeHR6,
- },
+ graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
+ g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
+ {
+ Name: "listener-80-1",
+ Source: listener80,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(hr6): routeHR6,
},
- {
- Name: "listener-443-1",
- Source: listener443,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(httpsHR6): httpsRouteHR6,
- },
- ResolvedSecret: &secret1NsName,
+ },
+ {
+ Name: "listener-443-1",
+ Source: listener443,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(httpsHR6): httpsRouteHR6,
},
+ ResolvedSecret: &secret1NsName,
},
- },
- Routes: map[graph.RouteKey]*graph.L7Route{
+ }...)
+ g.Routes = map[graph.RouteKey]*graph.L7Route{
graph.CreateRouteKey(hr6): routeHR6,
graph.CreateRouteKey(httpsHR6): httpsRouteHR6,
- },
- ReferencedSecrets: map[types.NamespacedName]*graph.Secret{
+ }
+ g.ReferencedSecrets = map[types.NamespacedName]*graph.Secret{
secret1NsName: secret1,
- },
- },
- expConf: Configuration{
- HTTPServers: []VirtualServer{
- {
- IsDefault: true,
- Port: 80,
- },
+ }
+ return g
+ }),
+ expConf: getModifiedExpectedConfiguration(func(conf Configuration) Configuration {
+ conf.HTTPServers = append(conf.HTTPServers, []VirtualServer{
{
Hostname: "foo.example.com",
PathRules: []PathRule{
@@ -1697,12 +1566,8 @@ func TestBuildConfiguration(t *testing.T) {
},
Port: 80,
},
- },
- SSLServers: []VirtualServer{
- {
- IsDefault: true,
- Port: 443,
- },
+ }...)
+ conf.SSLServers = append(conf.SSLServers, []VirtualServer{
{
Hostname: "foo.example.com",
SSL: &SSL{KeyPairID: "ssl_keypair_test_secret-1"},
@@ -1725,52 +1590,32 @@ func TestBuildConfiguration(t *testing.T) {
SSL: &SSL{KeyPairID: "ssl_keypair_test_secret-1"},
Port: 443,
},
- },
- Upstreams: []Upstream{fooUpstream},
- BackendGroups: []BackendGroup{
- expHR6Groups[0],
- expHTTPSHR6Groups[0],
- },
- SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{
- "ssl_keypair_test_secret-1": {
- Cert: []byte("cert-1"),
- Key: []byte("privateKey-1"),
- },
- },
- CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
- },
+ }...)
+ conf.Upstreams = []Upstream{fooUpstream}
+ conf.BackendGroups = []BackendGroup{expHR6Groups[0], expHTTPSHR6Groups[0]}
+ return conf
+ }),
msg: "one http and one https listener with routes with valid and invalid rules",
},
{
- graph: &graph.Graph{
- GatewayClass: &graph.GatewayClass{
- Source: &v1.GatewayClass{},
- Valid: true,
- },
- Gateway: &graph.Gateway{
- Source: &v1.Gateway{},
- Listeners: []*graph.Listener{
- {
- Name: "listener-80-1",
- Source: listener80,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(hr7): routeHR7,
- },
+ graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
+ g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
+ {
+ Name: "listener-80-1",
+ Source: listener80,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(hr7): routeHR7,
},
},
- },
- Routes: map[graph.RouteKey]*graph.L7Route{
+ }...)
+ g.Routes = map[graph.RouteKey]*graph.L7Route{
graph.CreateRouteKey(hr7): routeHR7,
- },
- },
- expConf: Configuration{
- HTTPServers: []VirtualServer{
- {
- IsDefault: true,
- Port: 80,
- },
+ }
+ return g
+ }),
+ expConf: getModifiedExpectedConfiguration(func(conf Configuration) Configuration {
+ conf.HTTPServers = append(conf.HTTPServers, []VirtualServer{
{
Hostname: "foo.example.com",
PathRules: []PathRule{
@@ -1797,60 +1642,48 @@ 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{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
- },
+ }...)
+ conf.SSLServers = []VirtualServer{}
+ conf.Upstreams = []Upstream{fooUpstream}
+ conf.BackendGroups = []BackendGroup{expHR7Groups[0], expHR7Groups[1]}
+ conf.SSLKeyPairs = map[SSLKeyPairID]SSLKeyPair{}
+ return conf
+ }),
msg: "duplicate paths with different types",
},
{
- graph: &graph.Graph{
- GatewayClass: &graph.GatewayClass{
- Source: &v1.GatewayClass{},
- Valid: true,
- },
- Gateway: &graph.Gateway{
- Source: &v1.Gateway{},
- Listeners: []*graph.Listener{
- {
- Name: "listener-443-with-hostname",
- Source: listener443WithHostname,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(httpsHR5): httpsRouteHR5,
- },
- ResolvedSecret: &secret2NsName,
+ graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
+ g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
+ {
+ Name: "listener-443-with-hostname",
+ Source: listener443WithHostname,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(httpsHR5): httpsRouteHR5,
},
- {
- Name: "listener-443-1",
- Source: listener443,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(httpsHR5): httpsRouteHR5,
- },
- ResolvedSecret: &secret1NsName,
+ ResolvedSecret: &secret2NsName,
+ },
+ {
+ Name: "listener-443-1",
+ Source: listener443,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(httpsHR5): httpsRouteHR5,
},
+ ResolvedSecret: &secret1NsName,
},
- },
- Routes: map[graph.RouteKey]*graph.L7Route{
+ }...)
+ g.Routes = map[graph.RouteKey]*graph.L7Route{
graph.CreateRouteKey(httpsHR5): httpsRouteHR5,
- },
- ReferencedSecrets: map[types.NamespacedName]*graph.Secret{
+ }
+ g.ReferencedSecrets = map[types.NamespacedName]*graph.Secret{
secret1NsName: secret1,
secret2NsName: secret2,
- },
- },
- expConf: Configuration{
- HTTPServers: []VirtualServer{},
- SSLServers: []VirtualServer{
- {
- IsDefault: true,
- Port: 443,
- },
+ }
+ return g
+ }),
+ expConf: getModifiedExpectedConfiguration(func(conf Configuration) Configuration {
+ conf.SSLServers = append(conf.SSLServers, []VirtualServer{
{
Hostname: "example.com",
PathRules: []PathRule{
@@ -1878,10 +1711,11 @@ func TestBuildConfiguration(t *testing.T) {
SSL: &SSL{KeyPairID: "ssl_keypair_test_secret-1"},
Port: 443,
},
- },
- Upstreams: []Upstream{fooUpstream},
- BackendGroups: []BackendGroup{expHTTPSHR5Groups[0]},
- SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{
+ }...)
+ conf.HTTPServers = []VirtualServer{}
+ conf.Upstreams = []Upstream{fooUpstream}
+ conf.BackendGroups = []BackendGroup{expHTTPSHR5Groups[0]}
+ conf.SSLKeyPairs = map[SSLKeyPairID]SSLKeyPair{
"ssl_keypair_test_secret-1": {
Cert: []byte("cert-1"),
Key: []byte("privateKey-1"),
@@ -1890,47 +1724,35 @@ func TestBuildConfiguration(t *testing.T) {
Cert: []byte("cert-2"),
Key: []byte("privateKey-2"),
},
- },
- CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
- },
+ }
+ return conf
+ }),
msg: "two https listeners with different hostnames but same route; chooses listener with more specific hostname",
},
{
- graph: &graph.Graph{
- GatewayClass: &graph.GatewayClass{
- Source: &v1.GatewayClass{},
- Valid: true,
- },
- Gateway: &graph.Gateway{
- Source: &v1.Gateway{},
- Listeners: []*graph.Listener{
- {
- Name: "listener-443",
- Source: listener443,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(httpsHR8): httpsRouteHR8,
- },
- ResolvedSecret: &secret1NsName,
+ graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
+ g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
+ {
+ Name: "listener-443",
+ Source: listener443,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(httpsHR8): httpsRouteHR8,
},
+ ResolvedSecret: &secret1NsName,
},
- },
- Routes: map[graph.RouteKey]*graph.L7Route{
+ }...)
+ g.Routes = map[graph.RouteKey]*graph.L7Route{
graph.CreateRouteKey(httpsHR8): httpsRouteHR8,
- },
- ReferencedSecrets: map[types.NamespacedName]*graph.Secret{
+ }
+ g.ReferencedSecrets = map[types.NamespacedName]*graph.Secret{
secret1NsName: secret1,
- },
- ReferencedCaCertConfigMaps: referencedConfigMaps,
- },
- expConf: Configuration{
- HTTPServers: []VirtualServer{},
- SSLServers: []VirtualServer{
- {
- IsDefault: true,
- Port: 443,
- },
+ }
+ g.ReferencedCaCertConfigMaps = referencedConfigMaps
+ return g
+ }),
+ expConf: getModifiedExpectedConfiguration(func(conf Configuration) Configuration {
+ conf.SSLServers = append(conf.SSLServers, []VirtualServer{
{
Hostname: "foo.example.com",
PathRules: []PathRule{
@@ -1957,57 +1779,41 @@ func TestBuildConfiguration(t *testing.T) {
SSL: &SSL{KeyPairID: "ssl_keypair_test_secret-1"},
Port: 443,
},
- },
- Upstreams: []Upstream{fooUpstream},
- BackendGroups: []BackendGroup{expHTTPSHR8Groups[0], expHTTPSHR8Groups[1]},
- SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{
- "ssl_keypair_test_secret-1": {
- Cert: []byte("cert-1"),
- Key: []byte("privateKey-1"),
- },
- },
- CertBundles: map[CertBundleID]CertBundle{
+ }...)
+ conf.HTTPServers = []VirtualServer{}
+ conf.Upstreams = []Upstream{fooUpstream}
+ conf.BackendGroups = []BackendGroup{expHTTPSHR8Groups[0], expHTTPSHR8Groups[1]}
+ conf.CertBundles = map[CertBundleID]CertBundle{
"cert_bundle_test_configmap-1": []byte("cert-1"),
- },
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
- },
+ }
+ return conf
+ }),
msg: "https listener with httproute with backend that has a backend TLS policy attached",
},
{
- graph: &graph.Graph{
- GatewayClass: &graph.GatewayClass{
- Source: &v1.GatewayClass{},
- Valid: true,
- },
- Gateway: &graph.Gateway{
- Source: &v1.Gateway{},
- Listeners: []*graph.Listener{
- {
- Name: "listener-443",
- Source: listener443,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(httpsHR9): httpsRouteHR9,
- },
- ResolvedSecret: &secret1NsName,
+ graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
+ g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
+ {
+ Name: "listener-443",
+ Source: listener443,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(httpsHR9): httpsRouteHR9,
},
+ ResolvedSecret: &secret1NsName,
},
- },
- Routes: map[graph.RouteKey]*graph.L7Route{
+ }...)
+ g.Routes = map[graph.RouteKey]*graph.L7Route{
graph.CreateRouteKey(httpsHR9): httpsRouteHR9,
- },
- ReferencedSecrets: map[types.NamespacedName]*graph.Secret{
+ }
+ g.ReferencedSecrets = map[types.NamespacedName]*graph.Secret{
secret1NsName: secret1,
- },
- ReferencedCaCertConfigMaps: referencedConfigMaps,
- },
- expConf: Configuration{
- HTTPServers: []VirtualServer{},
- SSLServers: []VirtualServer{
- {
- IsDefault: true,
- Port: 443,
- },
+ }
+ g.ReferencedCaCertConfigMaps = referencedConfigMaps
+ return g
+ }),
+ expConf: getModifiedExpectedConfiguration(func(conf Configuration) Configuration {
+ conf.SSLServers = append(conf.SSLServers, []VirtualServer{
{
Hostname: "foo.example.com",
PathRules: []PathRule{
@@ -2034,93 +1840,65 @@ func TestBuildConfiguration(t *testing.T) {
SSL: &SSL{KeyPairID: "ssl_keypair_test_secret-1"},
Port: 443,
},
- },
- Upstreams: []Upstream{fooUpstream},
- BackendGroups: []BackendGroup{expHTTPSHR9Groups[0], expHTTPSHR9Groups[1]},
- SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{
- "ssl_keypair_test_secret-1": {
- Cert: []byte("cert-1"),
- Key: []byte("privateKey-1"),
- },
- },
- CertBundles: map[CertBundleID]CertBundle{
+ }...)
+ conf.HTTPServers = []VirtualServer{}
+ conf.Upstreams = []Upstream{fooUpstream}
+ conf.BackendGroups = []BackendGroup{expHTTPSHR9Groups[0], expHTTPSHR9Groups[1]}
+ conf.CertBundles = map[CertBundleID]CertBundle{
"cert_bundle_test_configmap-2": []byte("cert-2"),
- },
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
- },
+ }
+ return conf
+ }),
msg: "https listener with httproute with backend that has a backend TLS policy with binaryData attached",
},
{
- graph: &graph.Graph{
- GatewayClass: &graph.GatewayClass{
- Source: &v1.GatewayClass{},
- Valid: true,
- },
- Gateway: &graph.Gateway{
- Source: &v1.Gateway{
- ObjectMeta: metav1.ObjectMeta{
- Name: "gw",
- Namespace: "ns",
- },
- },
- Listeners: []*graph.Listener{
- {
- Name: "listener-80-1",
- Source: listener80,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{},
- },
- },
- },
- Routes: map[graph.RouteKey]*graph.L7Route{},
- NginxProxy: nginxProxy,
- },
- expConf: Configuration{
- HTTPServers: []VirtualServer{
+ graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
+ g.Gateway.Source.ObjectMeta = metav1.ObjectMeta{
+ Name: "gw",
+ Namespace: "ns",
+ }
+ g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
{
- IsDefault: true,
- Port: 80,
- },
- },
- SSLServers: []VirtualServer{},
- SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
- CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: false, IPFamily: Dual},
- Telemetry: Telemetry{
+ Name: "listener-80-1",
+ Source: listener80,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{},
+ },
+ }...)
+ g.NginxProxy = nginxProxy
+ return g
+ }),
+ expConf: getModifiedExpectedConfiguration(func(conf Configuration) Configuration {
+ conf.SSLServers = []VirtualServer{}
+ conf.SSLKeyPairs = map[SSLKeyPairID]SSLKeyPair{}
+ conf.Telemetry = Telemetry{
Endpoint: "my-otel.svc:4563",
Interval: "5s",
BatchSize: 512,
BatchCount: 4,
ServiceName: "ngf:ns:gw:my-svc",
Ratios: []Ratio{},
- },
- },
+ }
+ conf.BaseHTTPConfig = BaseHTTPConfig{HTTP2: false, IPFamily: Dual}
+ return conf
+ }),
msg: "NginxProxy with tracing config and http2 disabled",
},
{
- graph: &graph.Graph{
- GatewayClass: &graph.GatewayClass{
- Source: &v1.GatewayClass{},
- Valid: true,
- },
- Gateway: &graph.Gateway{
- Source: &v1.Gateway{
- ObjectMeta: metav1.ObjectMeta{
- Name: "gw",
- Namespace: "ns",
- },
- },
- Listeners: []*graph.Listener{
- {
- Name: "listener-80-1",
- Source: listener80,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{},
- },
+ graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
+ g.Gateway.Source.ObjectMeta = metav1.ObjectMeta{
+ Name: "gw",
+ Namespace: "ns",
+ }
+ g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
+ {
+ Name: "listener-80-1",
+ Source: listener80,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{},
},
- },
- Routes: map[graph.RouteKey]*graph.L7Route{},
- NginxProxy: &graph.NginxProxy{
+ }...)
+ g.NginxProxy = &graph.NginxProxy{
Valid: false,
Source: &ngfAPI.NginxProxy{
Spec: ngfAPI.NginxProxySpec{
@@ -2133,72 +1911,52 @@ func TestBuildConfiguration(t *testing.T) {
},
},
},
- },
- },
- expConf: Configuration{
- HTTPServers: []VirtualServer{
- {
- IsDefault: true,
- Port: 80,
- },
- },
- SSLServers: []VirtualServer{},
- SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
- CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
- Telemetry: Telemetry{},
- },
+ }
+ return g
+ }),
+ expConf: getModifiedExpectedConfiguration(func(conf Configuration) Configuration {
+ conf.SSLServers = []VirtualServer{}
+ conf.SSLKeyPairs = map[SSLKeyPairID]SSLKeyPair{}
+ return conf
+ }),
msg: "invalid NginxProxy",
},
{
- graph: &graph.Graph{
- GatewayClass: &graph.GatewayClass{
- Source: &v1.GatewayClass{},
- Valid: true,
- },
- Gateway: &graph.Gateway{
- Source: &v1.Gateway{},
- Listeners: []*graph.Listener{
- {
- Name: "listener-80-1",
- Source: listener80,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(hrWithPolicy): l7RouteWithPolicy,
- },
+ graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
+ g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
+ {
+ Name: "listener-80-1",
+ Source: listener80,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(hrWithPolicy): l7RouteWithPolicy,
},
- {
- Name: "listener-443",
- Source: listener443,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(httpsHRWithPolicy): l7HTTPSRouteWithPolicy,
- },
- ResolvedSecret: &secret1NsName,
+ },
+ {
+ Name: "listener-443",
+ Source: listener443,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(httpsHRWithPolicy): l7HTTPSRouteWithPolicy,
},
+ ResolvedSecret: &secret1NsName,
},
- Policies: []*graph.Policy{gwPolicy1, gwPolicy2},
- },
- Routes: map[graph.RouteKey]*graph.L7Route{
+ }...)
+ g.Gateway.Policies = []*graph.Policy{gwPolicy1, gwPolicy2}
+ g.Routes = map[graph.RouteKey]*graph.L7Route{
graph.CreateRouteKey(hrWithPolicy): l7RouteWithPolicy,
graph.CreateRouteKey(httpsHRWithPolicy): l7HTTPSRouteWithPolicy,
- },
- ReferencedSecrets: map[types.NamespacedName]*graph.Secret{
+ }
+ g.ReferencedSecrets = map[types.NamespacedName]*graph.Secret{
secret1NsName: secret1,
- },
- },
- expConf: Configuration{
- SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{
- "ssl_keypair_test_secret-1": {
- Cert: []byte("cert-1"),
- Key: []byte("privateKey-1"),
- },
- },
- CertBundles: map[CertBundleID]CertBundle{},
- HTTPServers: []VirtualServer{
+ }
+ return g
+ }),
+ expConf: getModifiedExpectedConfiguration(func(conf Configuration) Configuration {
+ conf.SSLServers = []VirtualServer{
{
IsDefault: true,
- Port: 80,
+ Port: 443,
Additions: []Addition{
{
Bytes: []byte("apple"),
@@ -2218,19 +1976,20 @@ func TestBuildConfiguration(t *testing.T) {
PathType: PathTypePrefix,
MatchRules: []MatchRule{
{
- Source: &hrWithPolicy.ObjectMeta,
- BackendGroup: expHRWithPolicyGroups[0],
+ BackendGroup: expHTTPSHRWithPolicyGroups[0],
+ Source: &httpsHRWithPolicy.ObjectMeta,
Additions: []Addition{
{
- Bytes: []byte("lemon"),
- Identifier: "LemonPolicy_default_attach-hr",
+ Bytes: []byte("lime"),
+ Identifier: "LimePolicy_default_attach-hr",
},
},
},
},
},
},
- Port: 80,
+ SSL: &SSL{KeyPairID: "ssl_keypair_test_secret-1"},
+ Port: 443,
Additions: []Addition{
{
Bytes: []byte("apple"),
@@ -2242,11 +2001,26 @@ func TestBuildConfiguration(t *testing.T) {
},
},
},
- },
- SSLServers: []VirtualServer{
+ {
+ Hostname: wildcardHostname,
+ SSL: &SSL{KeyPairID: "ssl_keypair_test_secret-1"},
+ Port: 443,
+ Additions: []Addition{
+ {
+ Bytes: []byte("apple"),
+ Identifier: "ApplePolicy_default_attach-gw",
+ },
+ {
+ Bytes: []byte("orange"),
+ Identifier: "OrangePolicy_default_attach-gw",
+ },
+ },
+ },
+ }
+ conf.HTTPServers = []VirtualServer{
{
IsDefault: true,
- Port: 443,
+ Port: 80,
Additions: []Addition{
{
Bytes: []byte("apple"),
@@ -2266,35 +2040,19 @@ func TestBuildConfiguration(t *testing.T) {
PathType: PathTypePrefix,
MatchRules: []MatchRule{
{
- BackendGroup: expHTTPSHRWithPolicyGroups[0],
- Source: &httpsHRWithPolicy.ObjectMeta,
+ Source: &hrWithPolicy.ObjectMeta,
+ BackendGroup: expHRWithPolicyGroups[0],
Additions: []Addition{
{
- Bytes: []byte("lime"),
- Identifier: "LimePolicy_default_attach-hr",
+ Bytes: []byte("lemon"),
+ Identifier: "LemonPolicy_default_attach-hr",
},
},
},
},
},
},
- SSL: &SSL{KeyPairID: "ssl_keypair_test_secret-1"},
- Port: 443,
- Additions: []Addition{
- {
- Bytes: []byte("apple"),
- Identifier: "ApplePolicy_default_attach-gw",
- },
- {
- Bytes: []byte("orange"),
- Identifier: "OrangePolicy_default_attach-gw",
- },
- },
- },
- {
- Hostname: wildcardHostname,
- SSL: &SSL{KeyPairID: "ssl_keypair_test_secret-1"},
- Port: 443,
+ Port: 80,
Additions: []Addition{
{
Bytes: []byte("apple"),
@@ -2306,94 +2064,61 @@ func TestBuildConfiguration(t *testing.T) {
},
},
},
- },
- Upstreams: []Upstream{fooUpstream},
- BackendGroups: []BackendGroup{
- expHRWithPolicyGroups[0],
- expHTTPSHRWithPolicyGroups[0],
- },
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
- },
+ }
+ conf.Upstreams = []Upstream{fooUpstream}
+ conf.BackendGroups = []BackendGroup{expHRWithPolicyGroups[0], expHTTPSHRWithPolicyGroups[0]}
+ return conf
+ }),
msg: "Simple Gateway and HTTPRoute with policies attached",
},
{
- graph: &graph.Graph{
- GatewayClass: &graph.GatewayClass{
- Source: &v1.GatewayClass{},
- Valid: true,
- },
- Gateway: &graph.Gateway{
- Source: &v1.Gateway{
- ObjectMeta: metav1.ObjectMeta{
- Name: "gw",
- Namespace: "ns",
- },
- },
- Listeners: []*graph.Listener{
- {
- Name: "listener-80-1",
- Source: listener80,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{},
- },
- },
- },
- Routes: map[graph.RouteKey]*graph.L7Route{},
- NginxProxy: nginxProxyIPv4,
- },
- expConf: Configuration{
- HTTPServers: []VirtualServer{
+ graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
+ g.Gateway.Source.ObjectMeta = metav1.ObjectMeta{
+ Name: "gw",
+ Namespace: "ns",
+ }
+ g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
{
- IsDefault: true,
- Port: 80,
- },
- },
- SSLServers: []VirtualServer{},
- SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
- CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: IPv4},
- Telemetry: Telemetry{},
- },
+ Name: "listener-80-1",
+ Source: listener80,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{},
+ },
+ }...)
+ g.NginxProxy = nginxProxyIPv4
+ return g
+ }),
+ expConf: getModifiedExpectedConfiguration(func(conf Configuration) Configuration {
+ conf.SSLServers = []VirtualServer{}
+ conf.SSLKeyPairs = map[SSLKeyPairID]SSLKeyPair{}
+ conf.BaseHTTPConfig = BaseHTTPConfig{HTTP2: true, IPFamily: IPv4}
+ return conf
+ }),
msg: "NginxProxy with IPv4 IPFamily and no routes",
},
{
- graph: &graph.Graph{
- GatewayClass: &graph.GatewayClass{
- Source: &v1.GatewayClass{},
- Valid: true,
- },
- Gateway: &graph.Gateway{
- Source: &v1.Gateway{
- ObjectMeta: metav1.ObjectMeta{
- Name: "gw",
- Namespace: "ns",
- },
- },
- Listeners: []*graph.Listener{
- {
- Name: "listener-80-1",
- Source: listener80,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{},
- },
- },
- },
- Routes: map[graph.RouteKey]*graph.L7Route{},
- NginxProxy: nginxProxyIPv6,
- },
- expConf: Configuration{
- HTTPServers: []VirtualServer{
+ graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
+ g.Gateway.Source.ObjectMeta = metav1.ObjectMeta{
+ Name: "gw",
+ Namespace: "ns",
+ }
+ g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
{
- IsDefault: true,
- Port: 80,
- },
- },
- SSLServers: []VirtualServer{},
- SSLKeyPairs: map[SSLKeyPairID]SSLKeyPair{},
- CertBundles: map[CertBundleID]CertBundle{},
- BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: IPv6},
- Telemetry: Telemetry{},
- },
+ Name: "listener-80-1",
+ Source: listener80,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{},
+ },
+ }...)
+ g.NginxProxy = nginxProxyIPv6
+ return g
+ }),
+ expConf: getModifiedExpectedConfiguration(func(conf Configuration) Configuration {
+ conf.SSLServers = []VirtualServer{}
+ conf.SSLKeyPairs = map[SSLKeyPairID]SSLKeyPair{}
+ conf.BaseHTTPConfig = BaseHTTPConfig{HTTP2: true, IPFamily: IPv6}
+ return conf
+ }),
msg: "NginxProxy with IPv6 IPFamily and no routes",
},
}
@@ -3508,6 +3233,11 @@ func TestGetAllowedAddressType(t *testing.T) {
ipFamily: IPv6,
expected: []discoveryV1.AddressType{discoveryV1.AddressTypeIPv6},
},
+ {
+ msg: "unknown ip family",
+ ipFamily: "unknown",
+ expected: []discoveryV1.AddressType{},
+ },
}
for _, tc := range test {
diff --git a/internal/mode/static/state/graph/backend_refs.go b/internal/mode/static/state/graph/backend_refs.go
index 7cb4b494d1..9a87dc368d 100644
--- a/internal/mode/static/state/graph/backend_refs.go
+++ b/internal/mode/static/state/graph/backend_refs.go
@@ -152,19 +152,18 @@ func createBackendRef(
}
svc, svcPort, err := getServiceAndPortFromRef(ref.BackendRef, sourceNamespace, services, refPath)
- svcNsName := client.ObjectKeyFromObject(svc)
if err != nil {
backendRef = BackendRef{
- SvcNsName: svcNsName,
- ServicePort: svcPort,
- Weight: weight,
- Valid: false,
+ Weight: weight,
+ Valid: false,
}
cond := staticConds.NewRouteBackendRefRefBackendNotFound(err.Error())
return backendRef, &cond
}
+ svcNsName := client.ObjectKeyFromObject(svc)
+
if err := verifyIPFamily(npCfg, svc.Spec.IPFamilies); err != nil {
backendRef = BackendRef{
SvcNsName: svcNsName,
@@ -332,12 +331,16 @@ func verifyIPFamily(npCfg *NginxProxy, svcIPFamily []v1.IPFamily) error {
npIPFamily := npCfg.Source.Spec.IPFamily
if *npIPFamily == ngfAPI.IPv4 {
if slices.Contains(svcIPFamily, v1.IPv6Protocol) {
- return errors.New("service configured with IPv6 family but NginxProxy is configured with IPv4")
+ // capitalizing error message to match the rest of the error messages associated with a condition
+ return errors.New( //nolint: stylecheck
+ "Service configured with IPv6 family but NginxProxy is configured with IPv4")
}
}
if *npIPFamily == ngfAPI.IPv6 {
if slices.Contains(svcIPFamily, v1.IPv4Protocol) {
- return errors.New("service configured with IPv4 family but NginxProxy is configured with IPv6")
+ // capitalizing error message to match the rest of the error messages associated with a condition
+ return errors.New( //nolint: stylecheck
+ "Service configured with IPv4 family but NginxProxy is configured with IPv6")
}
}
diff --git a/internal/mode/static/state/graph/backend_refs_test.go b/internal/mode/static/state/graph/backend_refs_test.go
index f889e1b088..21b987244c 100644
--- a/internal/mode/static/state/graph/backend_refs_test.go
+++ b/internal/mode/static/state/graph/backend_refs_test.go
@@ -392,7 +392,7 @@ func TestVerifyIPFamily(t *testing.T) {
Valid: true,
},
svcIPFamily: []v1.IPFamily{v1.IPv6Protocol},
- expErr: errors.New("service configured with IPv6 family but NginxProxy is configured with IPv4"),
+ expErr: errors.New("Service configured with IPv6 family but NginxProxy is configured with IPv4"),
},
{
name: "Invalid - IPv6 configured for NGINX, service has only IPv4",
@@ -405,7 +405,7 @@ func TestVerifyIPFamily(t *testing.T) {
Valid: true,
},
svcIPFamily: []v1.IPFamily{v1.IPv4Protocol},
- expErr: errors.New("service configured with IPv4 family but NginxProxy is configured with IPv6"),
+ expErr: errors.New("Service configured with IPv4 family but NginxProxy is configured with IPv6"),
},
{
name: "Valid - When NginxProxy is nil",
@@ -951,10 +951,8 @@ func TestCreateBackend(t *testing.T) {
}),
},
expectedBackend: BackendRef{
- SvcNsName: types.NamespacedName{Name: "not-exist", Namespace: "test"},
- ServicePort: v1.ServicePort{},
- Weight: 5,
- Valid: false,
+ Weight: 5,
+ Valid: false,
},
expectedServicePortReference: "",
expectedCondition: helpers.GetPointer(
@@ -982,7 +980,7 @@ func TestCreateBackend(t *testing.T) {
Valid: true,
},
expectedCondition: helpers.GetPointer(
- staticConds.NewRouteInvalidIPFamily(`service configured with IPv4 family but NginxProxy is configured with IPv6`),
+ staticConds.NewRouteInvalidIPFamily(`Service configured with IPv4 family but NginxProxy is configured with IPv6`),
),
name: "service IPFamily doesn't match NginxProxy IPFamily",
},
diff --git a/site/content/overview/gateway-api-compatibility.md b/site/content/overview/gateway-api-compatibility.md
index c106ec85af..567b3c5af0 100644
--- a/site/content/overview/gateway-api-compatibility.md
+++ b/site/content/overview/gateway-api-compatibility.md
@@ -179,7 +179,7 @@ See the [static-mode]({{< relref "/reference/cli-help.md#static-mode">}}) comman
- `ResolvedRefs/False/RefNotPermitted`
- `ResolvedRefs/False/BackendNotFound`
- `ResolvedRefs/False/UnsupportedValue`: Custom reason for when one of the HTTPRoute rules has a backendRef with an unsupported value.
- - `ResolvedRefs/False/InvalidIPFamily`: Custom reason for when one of the HTTPRoute rules has a backendRef that has invalid IPFamily.
+ - `ResolvedRefs/False/InvalidIPFamily`: Custom reason for when one of the HTTPRoute rules has a backendRef that has an invalid IPFamily.
- `PartiallyInvalid/True/UnsupportedValue`
---
From 9ef486828ddc9a810b25466aaa38bddcccf46648 Mon Sep 17 00:00:00 2001
From: Saloni
Date: Mon, 15 Jul 2024 19:04:24 -0600
Subject: [PATCH 27/28] move service name creation out of function
---
.../mode/static/state/graph/backend_refs.go | 29 ++++++------
.../static/state/graph/backend_refs_test.go | 45 +++++++++----------
2 files changed, 37 insertions(+), 37 deletions(-)
diff --git a/internal/mode/static/state/graph/backend_refs.go b/internal/mode/static/state/graph/backend_refs.go
index 9a87dc368d..200dc71a08 100644
--- a/internal/mode/static/state/graph/backend_refs.go
+++ b/internal/mode/static/state/graph/backend_refs.go
@@ -6,10 +6,8 @@ import (
"slices"
v1 "k8s.io/api/core/v1"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/validation/field"
- "sigs.k8s.io/controller-runtime/pkg/client"
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
"sigs.k8s.io/gateway-api/apis/v1alpha3"
@@ -151,20 +149,25 @@ func createBackendRef(
return backendRef, &cond
}
- svc, svcPort, err := getServiceAndPortFromRef(ref.BackendRef, sourceNamespace, services, refPath)
+ ns := sourceNamespace
+ if ref.BackendRef.Namespace != nil {
+ ns = string(*ref.Namespace)
+ }
+ svcNsName := types.NamespacedName{Name: string(ref.BackendRef.Name), Namespace: ns}
+ svcIPFamily, svcPort, err := getServiceAndPortFromRef(ref.BackendRef, sourceNamespace, services, refPath)
if err != nil {
backendRef = BackendRef{
- Weight: weight,
- Valid: false,
+ Weight: weight,
+ Valid: false,
+ SvcNsName: svcNsName,
+ ServicePort: v1.ServicePort{},
}
cond := staticConds.NewRouteBackendRefRefBackendNotFound(err.Error())
return backendRef, &cond
}
- svcNsName := client.ObjectKeyFromObject(svc)
-
- if err := verifyIPFamily(npCfg, svc.Spec.IPFamilies); err != nil {
+ if err := verifyIPFamily(npCfg, svcIPFamily); err != nil {
backendRef = BackendRef{
SvcNsName: svcNsName,
ServicePort: svcPort,
@@ -299,7 +302,7 @@ func getServiceAndPortFromRef(
routeNamespace string,
services map[types.NamespacedName]*v1.Service,
refPath *field.Path,
-) (*v1.Service, v1.ServicePort, error) {
+) ([]v1.IPFamily, v1.ServicePort, error) {
ns := routeNamespace
if ref.Namespace != nil {
ns = string(*ref.Namespace)
@@ -308,18 +311,16 @@ func getServiceAndPortFromRef(
svcNsName := types.NamespacedName{Name: string(ref.Name), Namespace: ns}
svc, ok := services[svcNsName]
if !ok {
- return &v1.Service{ObjectMeta: metav1.ObjectMeta{Namespace: ns, Name: string(ref.Name)}},
- v1.ServicePort{},
- field.NotFound(refPath.Child("name"), ref.Name)
+ return []v1.IPFamily{}, v1.ServicePort{}, field.NotFound(refPath.Child("name"), ref.Name)
}
// safe to dereference port here because we already validated that the port is not nil in validateBackendRef.
svcPort, err := getServicePort(svc, int32(*ref.Port))
if err != nil {
- return &v1.Service{ObjectMeta: metav1.ObjectMeta{Namespace: ns, Name: string(ref.Name)}}, v1.ServicePort{}, err
+ return []v1.IPFamily{}, v1.ServicePort{}, err
}
- return svc, svcPort, nil
+ return svc.Spec.IPFamilies, svcPort, nil
}
func verifyIPFamily(npCfg *NginxProxy, svcIPFamily []v1.IPFamily) error {
diff --git a/internal/mode/static/state/graph/backend_refs_test.go b/internal/mode/static/state/graph/backend_refs_test.go
index 21b987244c..caafae5f28 100644
--- a/internal/mode/static/state/graph/backend_refs_test.go
+++ b/internal/mode/static/state/graph/backend_refs_test.go
@@ -271,10 +271,6 @@ func TestGetServiceAndPortFromRef(t *testing.T) {
IPFamilies: []v1.IPFamily{v1.IPv4Protocol},
},
}
- svc1NsName := types.NamespacedName{
- Namespace: "test",
- Name: "service1",
- }
svc2 := &v1.Service{
ObjectMeta: metav1.ObjectMeta{
@@ -284,19 +280,18 @@ func TestGetServiceAndPortFromRef(t *testing.T) {
}
tests := []struct {
- expSvc *v1.Service
- ref gatewayv1.BackendRef
- expServiceNsName types.NamespacedName
- name string
- expServicePort v1.ServicePort
- expErr bool
+ expSvc *v1.Service
+ ref gatewayv1.BackendRef
+ expSvcIPFamily []v1.IPFamily
+ name string
+ expServicePort v1.ServicePort
+ expErr bool
}{
{
- name: "normal case",
- ref: getNormalRef(),
- expServiceNsName: svc1NsName,
- expServicePort: v1.ServicePort{Port: 80},
- expSvc: svc1,
+ name: "normal case",
+ ref: getNormalRef(),
+ expServicePort: v1.ServicePort{Port: 80},
+ expSvcIPFamily: []v1.IPFamily{v1.IPv4Protocol},
},
{
name: "service does not exist",
@@ -304,14 +299,14 @@ func TestGetServiceAndPortFromRef(t *testing.T) {
backend.Name = "does-not-exist"
return backend
}),
- expErr: true,
- expServiceNsName: types.NamespacedName{Name: "does-not-exist", Namespace: "test"},
- expServicePort: v1.ServicePort{},
+ expErr: true,
+ expServicePort: v1.ServicePort{},
expSvc: &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "does-not-exist", Namespace: "test",
},
},
+ expSvcIPFamily: []v1.IPFamily{},
},
{
name: "no matching port for service and port",
@@ -319,14 +314,14 @@ func TestGetServiceAndPortFromRef(t *testing.T) {
backend.Port = helpers.GetPointer[gatewayv1.PortNumber](504)
return backend
}),
- expErr: true,
- expServiceNsName: svc1NsName,
- expServicePort: v1.ServicePort{},
+ expErr: true,
+ expServicePort: v1.ServicePort{},
expSvc: &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "service1", Namespace: "test",
},
},
+ expSvcIPFamily: []v1.IPFamily{},
},
}
@@ -341,11 +336,11 @@ func TestGetServiceAndPortFromRef(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
g := NewWithT(t)
- svc, servicePort, err := getServiceAndPortFromRef(test.ref, "test", services, refPath)
+ svcIPFamily, servicePort, err := getServiceAndPortFromRef(test.ref, "test", services, refPath)
g.Expect(err != nil).To(Equal(test.expErr))
g.Expect(servicePort).To(Equal(test.expServicePort))
- g.Expect(svc).To(Equal(test.expSvc))
+ g.Expect(svcIPFamily).To(Equal(test.expSvcIPFamily))
})
}
}
@@ -953,6 +948,10 @@ func TestCreateBackend(t *testing.T) {
expectedBackend: BackendRef{
Weight: 5,
Valid: false,
+ SvcNsName: types.NamespacedName{
+ Namespace: "test",
+ Name: "not-exist",
+ },
},
expectedServicePortReference: "",
expectedCondition: helpers.GetPointer(
From e802e820d60afe3bdea9ffc7340756946a6dd6f6 Mon Sep 17 00:00:00 2001
From: Saloni
Date: Tue, 16 Jul 2024 14:28:12 -0600
Subject: [PATCH 28/28] address comments
---
.../state/dataplane/configuration_test.go | 212 ++++++++----------
.../mode/static/state/graph/backend_refs.go | 20 +-
.../static/state/graph/backend_refs_test.go | 22 +-
.../how-to/monitoring/troubleshooting.md | 2 +-
4 files changed, 106 insertions(+), 150 deletions(-)
diff --git a/internal/mode/static/state/dataplane/configuration_test.go b/internal/mode/static/state/dataplane/configuration_test.go
index 273bd2e071..83d5873ce0 100644
--- a/internal/mode/static/state/dataplane/configuration_test.go
+++ b/internal/mode/static/state/dataplane/configuration_test.go
@@ -36,10 +36,6 @@ func getNormalBackendRef() graph.BackendRef {
}
}
-func getModifiedBackendRef(mod func(ref graph.BackendRef) graph.BackendRef) graph.BackendRef {
- return mod(getNormalBackendRef())
-}
-
func getExpectedConfiguration() Configuration {
return Configuration{
BaseHTTPConfig: BaseHTTPConfig{HTTP2: true, IPFamily: Dual},
@@ -183,9 +179,7 @@ func TestBuildConfiguration(t *testing.T) {
fakeResolver := &resolverfakes.FakeServiceResolver{}
fakeResolver.ResolveReturns(fooEndpoints, nil)
- validBackendRef := getModifiedBackendRef(func(backend graph.BackendRef) graph.BackendRef {
- return backend
- })
+ validBackendRef := getNormalBackendRef()
expValidBackend := Backend{
UpstreamName: fooUpstreamName,
@@ -837,14 +831,12 @@ func TestBuildConfiguration(t *testing.T) {
},
{
graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
- g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
- {
- Name: "invalid-listener",
- Source: invalidListener,
- Valid: false,
- ResolvedSecret: &secret1NsName,
- },
- }...)
+ g.Gateway.Listeners = append(g.Gateway.Listeners, &graph.Listener{
+ Name: "invalid-listener",
+ Source: invalidListener,
+ Valid: false,
+ ResolvedSecret: &secret1NsName,
+ })
g.Routes = map[graph.RouteKey]*graph.L7Route{
graph.CreateRouteKey(httpsHR1): httpsRouteHR1,
graph.CreateRouteKey(httpsHR2): httpsRouteHR2,
@@ -862,17 +854,15 @@ func TestBuildConfiguration(t *testing.T) {
},
{
graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
- g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
- {
- Name: "listener-80-1",
- Source: listener80,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(hr1): routeHR1,
- graph.CreateRouteKey(hr2): routeHR2,
- },
+ g.Gateway.Listeners = append(g.Gateway.Listeners, &graph.Listener{
+ Name: "listener-80-1",
+ Source: listener80,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(hr1): routeHR1,
+ graph.CreateRouteKey(hr2): routeHR2,
},
- }...)
+ })
g.Routes = map[graph.RouteKey]*graph.L7Route{
graph.CreateRouteKey(hr1): routeHR1,
graph.CreateRouteKey(hr2): routeHR2,
@@ -925,16 +915,14 @@ func TestBuildConfiguration(t *testing.T) {
},
{
graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
- g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
- {
- Name: "listener-80-1",
- Source: listener80,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(gr): routeGR,
- },
+ g.Gateway.Listeners = append(g.Gateway.Listeners, &graph.Listener{
+ Name: "listener-80-1",
+ Source: listener80,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(gr): routeGR,
},
- }...)
+ })
g.Routes[graph.CreateRouteKey(gr)] = routeGR
return g
}),
@@ -1411,16 +1399,14 @@ func TestBuildConfiguration(t *testing.T) {
{
graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
g.GatewayClass.Valid = false
- g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
- {
- Name: "listener-80-1",
- Source: listener80,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(hr1): routeHR1,
- },
+ g.Gateway.Listeners = append(g.Gateway.Listeners, &graph.Listener{
+ Name: "listener-80-1",
+ Source: listener80,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(hr1): routeHR1,
},
- }...)
+ })
g.Routes = map[graph.RouteKey]*graph.L7Route{
graph.CreateRouteKey(hr1): routeHR1,
}
@@ -1432,16 +1418,14 @@ func TestBuildConfiguration(t *testing.T) {
{
graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
g.GatewayClass.Valid = false
- g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
- {
- Name: "listener-80-1",
- Source: listener80,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(hr1): routeHR1,
- },
+ g.Gateway.Listeners = append(g.Gateway.Listeners, &graph.Listener{
+ Name: "listener-80-1",
+ Source: listener80,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(hr1): routeHR1,
},
- }...)
+ })
g.Routes = map[graph.RouteKey]*graph.L7Route{
graph.CreateRouteKey(hr1): routeHR1,
}
@@ -1460,16 +1444,14 @@ func TestBuildConfiguration(t *testing.T) {
},
{
graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
- g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
- {
- Name: "listener-80-1",
- Source: listener80,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(hr5): routeHR5,
- },
+ g.Gateway.Listeners = append(g.Gateway.Listeners, &graph.Listener{
+ Name: "listener-80-1",
+ Source: listener80,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(hr5): routeHR5,
},
- }...)
+ })
g.Routes = map[graph.RouteKey]*graph.L7Route{
graph.CreateRouteKey(hr5): routeHR5,
}
@@ -1599,16 +1581,14 @@ func TestBuildConfiguration(t *testing.T) {
},
{
graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
- g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
- {
- Name: "listener-80-1",
- Source: listener80,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(hr7): routeHR7,
- },
+ g.Gateway.Listeners = append(g.Gateway.Listeners, &graph.Listener{
+ Name: "listener-80-1",
+ Source: listener80,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(hr7): routeHR7,
},
- }...)
+ })
g.Routes = map[graph.RouteKey]*graph.L7Route{
graph.CreateRouteKey(hr7): routeHR7,
}
@@ -1731,17 +1711,15 @@ func TestBuildConfiguration(t *testing.T) {
},
{
graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
- g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
- {
- Name: "listener-443",
- Source: listener443,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(httpsHR8): httpsRouteHR8,
- },
- ResolvedSecret: &secret1NsName,
+ g.Gateway.Listeners = append(g.Gateway.Listeners, &graph.Listener{
+ Name: "listener-443",
+ Source: listener443,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(httpsHR8): httpsRouteHR8,
},
- }...)
+ ResolvedSecret: &secret1NsName,
+ })
g.Routes = map[graph.RouteKey]*graph.L7Route{
graph.CreateRouteKey(httpsHR8): httpsRouteHR8,
}
@@ -1792,17 +1770,15 @@ func TestBuildConfiguration(t *testing.T) {
},
{
graph: getModifiedGraph(func(g *graph.Graph) *graph.Graph {
- g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
- {
- Name: "listener-443",
- Source: listener443,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{
- graph.CreateRouteKey(httpsHR9): httpsRouteHR9,
- },
- ResolvedSecret: &secret1NsName,
+ g.Gateway.Listeners = append(g.Gateway.Listeners, &graph.Listener{
+ Name: "listener-443",
+ Source: listener443,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{
+ graph.CreateRouteKey(httpsHR9): httpsRouteHR9,
},
- }...)
+ ResolvedSecret: &secret1NsName,
+ })
g.Routes = map[graph.RouteKey]*graph.L7Route{
graph.CreateRouteKey(httpsHR9): httpsRouteHR9,
}
@@ -1857,14 +1833,12 @@ func TestBuildConfiguration(t *testing.T) {
Name: "gw",
Namespace: "ns",
}
- g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
- {
- Name: "listener-80-1",
- Source: listener80,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{},
- },
- }...)
+ g.Gateway.Listeners = append(g.Gateway.Listeners, &graph.Listener{
+ Name: "listener-80-1",
+ Source: listener80,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{},
+ })
g.NginxProxy = nginxProxy
return g
}),
@@ -1890,14 +1864,12 @@ func TestBuildConfiguration(t *testing.T) {
Name: "gw",
Namespace: "ns",
}
- g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
- {
- Name: "listener-80-1",
- Source: listener80,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{},
- },
- }...)
+ g.Gateway.Listeners = append(g.Gateway.Listeners, &graph.Listener{
+ Name: "listener-80-1",
+ Source: listener80,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{},
+ })
g.NginxProxy = &graph.NginxProxy{
Valid: false,
Source: &ngfAPI.NginxProxy{
@@ -2077,14 +2049,12 @@ func TestBuildConfiguration(t *testing.T) {
Name: "gw",
Namespace: "ns",
}
- g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
- {
- Name: "listener-80-1",
- Source: listener80,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{},
- },
- }...)
+ g.Gateway.Listeners = append(g.Gateway.Listeners, &graph.Listener{
+ Name: "listener-80-1",
+ Source: listener80,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{},
+ })
g.NginxProxy = nginxProxyIPv4
return g
}),
@@ -2102,14 +2072,12 @@ func TestBuildConfiguration(t *testing.T) {
Name: "gw",
Namespace: "ns",
}
- g.Gateway.Listeners = append(g.Gateway.Listeners, []*graph.Listener{
- {
- Name: "listener-80-1",
- Source: listener80,
- Valid: true,
- Routes: map[graph.RouteKey]*graph.L7Route{},
- },
- }...)
+ g.Gateway.Listeners = append(g.Gateway.Listeners, &graph.Listener{
+ Name: "listener-80-1",
+ Source: listener80,
+ Valid: true,
+ Routes: map[graph.RouteKey]*graph.L7Route{},
+ })
g.NginxProxy = nginxProxyIPv6
return g
}),
diff --git a/internal/mode/static/state/graph/backend_refs.go b/internal/mode/static/state/graph/backend_refs.go
index 200dc71a08..2b79569dc3 100644
--- a/internal/mode/static/state/graph/backend_refs.go
+++ b/internal/mode/static/state/graph/backend_refs.go
@@ -154,7 +154,7 @@ func createBackendRef(
ns = string(*ref.Namespace)
}
svcNsName := types.NamespacedName{Name: string(ref.BackendRef.Name), Namespace: ns}
- svcIPFamily, svcPort, err := getServiceAndPortFromRef(ref.BackendRef, sourceNamespace, services, refPath)
+ svcIPFamily, svcPort, err := getIPFamilyAndPortFromRef(ref.BackendRef, svcNsName, services, refPath)
if err != nil {
backendRef = BackendRef{
Weight: weight,
@@ -293,22 +293,16 @@ func findBackendTLSPolicyForService(
return beTLSPolicy, err
}
-// getServiceAndPortFromRef extracts the NamespacedName of the Service and the port from a BackendRef.
+// getIPFamilyAndPortFromRef extracts the IPFamily of the Service and the port from a BackendRef.
// It can return an error and an empty v1.ServicePort in two cases:
// 1. The Service referenced from the BackendRef does not exist in the cluster/state.
// 2. The Port on the BackendRef does not match any of the ServicePorts on the Service.
-func getServiceAndPortFromRef(
+func getIPFamilyAndPortFromRef(
ref gatewayv1.BackendRef,
- routeNamespace string,
+ svcNsName types.NamespacedName,
services map[types.NamespacedName]*v1.Service,
refPath *field.Path,
) ([]v1.IPFamily, v1.ServicePort, error) {
- ns := routeNamespace
- if ref.Namespace != nil {
- ns = string(*ref.Namespace)
- }
-
- svcNsName := types.NamespacedName{Name: string(ref.Name), Namespace: ns}
svc, ok := services[svcNsName]
if !ok {
return []v1.IPFamily{}, v1.ServicePort{}, field.NotFound(refPath.Child("name"), ref.Name)
@@ -334,14 +328,16 @@ func verifyIPFamily(npCfg *NginxProxy, svcIPFamily []v1.IPFamily) error {
if slices.Contains(svcIPFamily, v1.IPv6Protocol) {
// capitalizing error message to match the rest of the error messages associated with a condition
return errors.New( //nolint: stylecheck
- "Service configured with IPv6 family but NginxProxy is configured with IPv4")
+ "Service configured with IPv6 family but NginxProxy is configured with IPv4",
+ )
}
}
if *npIPFamily == ngfAPI.IPv6 {
if slices.Contains(svcIPFamily, v1.IPv4Protocol) {
// capitalizing error message to match the rest of the error messages associated with a condition
return errors.New( //nolint: stylecheck
- "Service configured with IPv4 family but NginxProxy is configured with IPv6")
+ "Service configured with IPv4 family but NginxProxy is configured with IPv6",
+ )
}
}
diff --git a/internal/mode/static/state/graph/backend_refs_test.go b/internal/mode/static/state/graph/backend_refs_test.go
index caafae5f28..c260408c43 100644
--- a/internal/mode/static/state/graph/backend_refs_test.go
+++ b/internal/mode/static/state/graph/backend_refs_test.go
@@ -256,7 +256,7 @@ func TestValidateWeight(t *testing.T) {
}
}
-func TestGetServiceAndPortFromRef(t *testing.T) {
+func TestGetIPFamilyAndPortFromRef(t *testing.T) {
svc1 := &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "service1",
@@ -280,8 +280,8 @@ func TestGetServiceAndPortFromRef(t *testing.T) {
}
tests := []struct {
- expSvc *v1.Service
ref gatewayv1.BackendRef
+ svcNsName types.NamespacedName
expSvcIPFamily []v1.IPFamily
name string
expServicePort v1.ServicePort
@@ -292,6 +292,7 @@ func TestGetServiceAndPortFromRef(t *testing.T) {
ref: getNormalRef(),
expServicePort: v1.ServicePort{Port: 80},
expSvcIPFamily: []v1.IPFamily{v1.IPv4Protocol},
+ svcNsName: types.NamespacedName{Namespace: "test", Name: "service1"},
},
{
name: "service does not exist",
@@ -301,12 +302,8 @@ func TestGetServiceAndPortFromRef(t *testing.T) {
}),
expErr: true,
expServicePort: v1.ServicePort{},
- expSvc: &v1.Service{
- ObjectMeta: metav1.ObjectMeta{
- Name: "does-not-exist", Namespace: "test",
- },
- },
expSvcIPFamily: []v1.IPFamily{},
+ svcNsName: types.NamespacedName{Namespace: "test", Name: "does-not-exist"},
},
{
name: "no matching port for service and port",
@@ -316,12 +313,8 @@ func TestGetServiceAndPortFromRef(t *testing.T) {
}),
expErr: true,
expServicePort: v1.ServicePort{},
- expSvc: &v1.Service{
- ObjectMeta: metav1.ObjectMeta{
- Name: "service1", Namespace: "test",
- },
- },
expSvcIPFamily: []v1.IPFamily{},
+ svcNsName: types.NamespacedName{Namespace: "test", Name: "service1"},
},
}
@@ -336,7 +329,7 @@ func TestGetServiceAndPortFromRef(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
g := NewWithT(t)
- svcIPFamily, servicePort, err := getServiceAndPortFromRef(test.ref, "test", services, refPath)
+ svcIPFamily, servicePort, err := getIPFamilyAndPortFromRef(test.ref, test.svcNsName, services, refPath)
g.Expect(err != nil).To(Equal(test.expErr))
g.Expect(servicePort).To(Equal(test.expServicePort))
@@ -404,7 +397,6 @@ func TestVerifyIPFamily(t *testing.T) {
},
{
name: "Valid - When NginxProxy is nil",
- npCfg: &NginxProxy{},
svcIPFamily: []v1.IPFamily{v1.IPv4Protocol},
},
}
@@ -767,7 +759,7 @@ func TestAddBackendRefsToRulesTest(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
g := NewWithT(t)
resolver := newReferenceGrantResolver(nil)
- addBackendRefsToRules(test.route, resolver, services, test.policies, &NginxProxy{})
+ addBackendRefsToRules(test.route, resolver, services, test.policies, nil)
var actual []BackendRef
if test.route.Spec.Rules != nil {
diff --git a/site/content/how-to/monitoring/troubleshooting.md b/site/content/how-to/monitoring/troubleshooting.md
index a7c15ed8c6..e15bee6a1c 100644
--- a/site/content/how-to/monitoring/troubleshooting.md
+++ b/site/content/how-to/monitoring/troubleshooting.md
@@ -418,7 +418,7 @@ If you `describe` your HTTPRoute and see the following error:
Status: True
Type: Accepted
Last Transition Time: 2024-07-14T23:36:37Z
- Message: service configured with IPv4 family but NginxProxy is configured with IPv6
+ Message: Service configured with IPv4 family but NginxProxy is configured with IPv6
Observed Generation: 1
Reason: InvalidServiceIPFamily
Status: False