Skip to content

Commit 87bce07

Browse files
committed
add graph to dataplane conversion
Problem: Graph did not support TLS Route to dataplane configuration Solution: Added the code to convert it
1 parent 4899e52 commit 87bce07

File tree

12 files changed

+786
-108
lines changed

12 files changed

+786
-108
lines changed

internal/framework/kinds/kinds.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ const (
1919
HTTPRoute = "HTTPRoute"
2020
// GRPCRoute is the GRPCRoute kind.
2121
GRPCRoute = "GRPCRoute"
22+
// TLSRoute is the TLSRoute kind.
23+
TLSRoute = "TLSRoute"
2224
)
2325

2426
// NGINX Gateway Fabric kinds.

internal/mode/static/state/dataplane/configuration.go

Lines changed: 116 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,26 +45,130 @@ func BuildConfiguration(
4545
baseHTTPConfig := buildBaseHTTPConfig(g)
4646
upstreams := buildUpstreams(ctx, g.Gateway.Listeners, serviceResolver, baseHTTPConfig.IPFamily)
4747
httpServers, sslServers := buildServers(g, generator)
48+
passthroughServers := buildPassthroughServers(g)
49+
streamUpstreams := buildStreamUpstreams(ctx, g.Gateway.Listeners, serviceResolver)
4850
backendGroups := buildBackendGroups(append(httpServers, sslServers...))
4951
keyPairs := buildSSLKeyPairs(g.ReferencedSecrets, g.Gateway.Listeners)
5052
certBundles := buildCertBundles(g.ReferencedCaCertConfigMaps, backendGroups)
5153
telemetry := buildTelemetry(g)
5254

5355
config := Configuration{
54-
HTTPServers: httpServers,
55-
SSLServers: sslServers,
56-
Upstreams: upstreams,
57-
BackendGroups: backendGroups,
58-
SSLKeyPairs: keyPairs,
59-
Version: configVersion,
60-
CertBundles: certBundles,
61-
Telemetry: telemetry,
62-
BaseHTTPConfig: baseHTTPConfig,
56+
HTTPServers: httpServers,
57+
SSLServers: sslServers,
58+
TLSPassthroughServers: passthroughServers,
59+
Upstreams: upstreams,
60+
StreamUpstreams: streamUpstreams,
61+
BackendGroups: backendGroups,
62+
SSLKeyPairs: keyPairs,
63+
Version: configVersion,
64+
CertBundles: certBundles,
65+
Telemetry: telemetry,
66+
BaseHTTPConfig: baseHTTPConfig,
6367
}
6468

6569
return config
6670
}
6771

72+
// buildPassthroughServers builds TLSPassthroughServers from TLSRoutes attaches to listeners.
73+
func buildPassthroughServers(g *graph.Graph) []Layer4VirtualServer {
74+
passthroughServersMap := make(map[graph.L4RouteKey][]Layer4VirtualServer)
75+
routeToListenerHostname := make(map[graph.L4RouteKey]*v1.Hostname)
76+
77+
for _, l := range g.Gateway.Listeners {
78+
if !l.Valid {
79+
continue
80+
}
81+
for key, r := range l.L4Routes {
82+
if !r.Valid {
83+
continue
84+
}
85+
86+
if _, ok := passthroughServersMap[key]; ok {
87+
if listenerHostnameMoreSpecific(l.Source.Hostname, routeToListenerHostname[key]) {
88+
continue
89+
}
90+
passthroughServersMap[key] = []Layer4VirtualServer{}
91+
}
92+
93+
for _, h := range r.Spec.Hostnames {
94+
passthroughServersMap[key] = append(passthroughServersMap[key], Layer4VirtualServer{
95+
Hostname: string(h),
96+
UpstreamName: r.Spec.BackendRef.ServicePortReference(),
97+
Port: int32(l.Source.Port),
98+
})
99+
}
100+
}
101+
}
102+
103+
passthroughServers := make([]Layer4VirtualServer, 0, len(passthroughServersMap))
104+
105+
for _, r := range passthroughServersMap {
106+
passthroughServers = append(passthroughServers, r...)
107+
}
108+
109+
return passthroughServers
110+
}
111+
112+
// buildStreamUpstreams builds all stream upstreams.
113+
func buildStreamUpstreams(
114+
ctx context.Context,
115+
listeners []*graph.Listener,
116+
resolver resolver.ServiceResolver,
117+
) []Upstream {
118+
// There can be duplicate upstreams if multiple routes reference the same upstream.
119+
// We use a map to deduplicate them.
120+
uniqueUpstreams := make(map[string]Upstream)
121+
122+
for _, l := range listeners {
123+
if !l.Valid || l.Source.Protocol != v1.TLSProtocolType {
124+
continue
125+
}
126+
127+
for _, route := range l.L4Routes {
128+
if !route.Valid {
129+
continue
130+
}
131+
132+
br := route.Spec.BackendRef
133+
134+
if !br.Valid {
135+
continue
136+
}
137+
138+
upstreamName := br.ServicePortReference()
139+
_, exist := uniqueUpstreams[upstreamName]
140+
141+
if exist {
142+
continue
143+
}
144+
145+
var errMsg string
146+
147+
eps, err := resolver.Resolve(ctx, br.SvcNsName, br.ServicePort)
148+
if err != nil {
149+
errMsg = err.Error()
150+
}
151+
152+
uniqueUpstreams[upstreamName] = Upstream{
153+
Name: upstreamName,
154+
Endpoints: eps,
155+
ErrorMsg: errMsg,
156+
}
157+
}
158+
}
159+
160+
if len(uniqueUpstreams) == 0 {
161+
return nil
162+
}
163+
164+
upstreams := make([]Upstream, 0, len(uniqueUpstreams))
165+
166+
for _, up := range uniqueUpstreams {
167+
upstreams = append(upstreams, up)
168+
}
169+
return upstreams
170+
}
171+
68172
// buildSSLKeyPairs builds the SSLKeyPairs from the Secrets. It will only include Secrets that are referenced by
69173
// valid listeners, so that we don't include unused Secrets in the configuration of the data plane.
70174
func buildSSLKeyPairs(
@@ -212,6 +316,9 @@ func buildServers(g *graph.Graph, generator policies.ConfigGenerator) (http, ssl
212316
}
213317

214318
for _, l := range g.Gateway.Listeners {
319+
if l.Source.Protocol == v1.TLSProtocolType {
320+
continue
321+
}
215322
if l.Valid {
216323
rules := rulesForProtocol[l.Source.Protocol][l.Source.Port]
217324
if rules == nil {

internal/mode/static/state/dataplane/configuration_test.go

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import (
1313
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1414
"k8s.io/apimachinery/pkg/runtime/schema"
1515
"k8s.io/apimachinery/pkg/types"
16+
"k8s.io/apimachinery/pkg/util/intstr"
17+
"k8s.io/utils/ptr"
1618
"sigs.k8s.io/controller-runtime/pkg/client"
1719
v1 "sigs.k8s.io/gateway-api/apis/v1"
1820
"sigs.k8s.io/gateway-api/apis/v1alpha2"
@@ -3215,3 +3217,207 @@ func TestGetAllowedAddressType(t *testing.T) {
32153217
})
32163218
}
32173219
}
3220+
func TestCreatePassthroughServers(t *testing.T) {
3221+
testGraph := graph.Graph{
3222+
Gateway: &graph.Gateway{
3223+
Listeners: []*graph.Listener{
3224+
{
3225+
Name: "testingListener",
3226+
Source: v1.Listener{
3227+
Protocol: v1.TLSProtocolType,
3228+
Port: 443,
3229+
Hostname: ptr.To[v1.Hostname]("*.example.com"),
3230+
},
3231+
Routes: make(map[graph.RouteKey]*graph.L7Route),
3232+
L4Routes: map[graph.L4RouteKey]*graph.L4Route{
3233+
{NamespacedName: types.NamespacedName{
3234+
Namespace: "default",
3235+
Name: "secure-app",
3236+
}}: {
3237+
Spec: graph.L4RouteSpec{
3238+
Hostnames: []v1.Hostname{"app.example.com", "cafe.example.com"},
3239+
BackendRef: graph.BackendRef{
3240+
SvcNsName: types.NamespacedName{
3241+
Namespace: "default",
3242+
Name: "secure-app",
3243+
},
3244+
ServicePort: apiv1.ServicePort{
3245+
Name: "https",
3246+
Protocol: "TCP",
3247+
Port: 8443,
3248+
TargetPort: intstr.IntOrString{
3249+
Type: intstr.Int,
3250+
IntVal: 8443,
3251+
},
3252+
},
3253+
Valid: true,
3254+
},
3255+
},
3256+
Valid: true,
3257+
},
3258+
{NamespacedName: types.NamespacedName{
3259+
Namespace: "default",
3260+
Name: "secure-app2",
3261+
}}: {},
3262+
},
3263+
Valid: true,
3264+
},
3265+
{
3266+
Name: "testingListener2",
3267+
Source: v1.Listener{
3268+
Protocol: v1.TLSProtocolType,
3269+
Port: 443,
3270+
Hostname: ptr.To[v1.Hostname]("cafe.example.com"),
3271+
},
3272+
Routes: make(map[graph.RouteKey]*graph.L7Route),
3273+
L4Routes: map[graph.L4RouteKey]*graph.L4Route{
3274+
{NamespacedName: types.NamespacedName{
3275+
Namespace: "default",
3276+
Name: "secure-app",
3277+
}}: {
3278+
Spec: graph.L4RouteSpec{
3279+
Hostnames: []v1.Hostname{"app.example.com", "cafe.example.com"},
3280+
BackendRef: graph.BackendRef{
3281+
SvcNsName: types.NamespacedName{
3282+
Namespace: "default",
3283+
Name: "secure-app",
3284+
},
3285+
ServicePort: apiv1.ServicePort{
3286+
Name: "https",
3287+
Protocol: "TCP",
3288+
Port: 8443,
3289+
TargetPort: intstr.IntOrString{
3290+
Type: intstr.Int,
3291+
IntVal: 8443,
3292+
},
3293+
},
3294+
Valid: true,
3295+
},
3296+
},
3297+
Valid: true,
3298+
},
3299+
},
3300+
Valid: true,
3301+
},
3302+
},
3303+
},
3304+
}
3305+
3306+
passthroughServers := buildPassthroughServers(&testGraph)
3307+
3308+
expectedPassthroughServers := []Layer4VirtualServer{
3309+
{
3310+
Hostname: "app.example.com",
3311+
UpstreamName: "default_secure-app_8443",
3312+
Port: 443,
3313+
},
3314+
{
3315+
Hostname: "cafe.example.com",
3316+
UpstreamName: "default_secure-app_8443",
3317+
Port: 443,
3318+
},
3319+
}
3320+
3321+
g := NewWithT(t)
3322+
3323+
g.Expect(passthroughServers).To(Equal(expectedPassthroughServers))
3324+
}
3325+
3326+
func TestBuildStreamUpstreams(t *testing.T) {
3327+
testGraph := graph.Graph{
3328+
Gateway: &graph.Gateway{
3329+
Listeners: []*graph.Listener{
3330+
{
3331+
Name: "testingListener",
3332+
Source: v1.Listener{
3333+
Protocol: v1.TLSProtocolType,
3334+
Port: 443,
3335+
},
3336+
Routes: make(map[graph.RouteKey]*graph.L7Route),
3337+
L4Routes: map[graph.L4RouteKey]*graph.L4Route{
3338+
{NamespacedName: types.NamespacedName{
3339+
Namespace: "default",
3340+
Name: "secure-app",
3341+
}}: {
3342+
Spec: graph.L4RouteSpec{
3343+
Hostnames: []v1.Hostname{"app.example.com", "cafe.example.com"},
3344+
BackendRef: graph.BackendRef{
3345+
SvcNsName: types.NamespacedName{
3346+
Namespace: "default",
3347+
Name: "secure-app",
3348+
},
3349+
ServicePort: apiv1.ServicePort{
3350+
Name: "https",
3351+
Protocol: "TCP",
3352+
Port: 8443,
3353+
TargetPort: intstr.IntOrString{
3354+
Type: intstr.Int,
3355+
IntVal: 8443,
3356+
},
3357+
},
3358+
Valid: true,
3359+
},
3360+
},
3361+
Valid: true,
3362+
},
3363+
{NamespacedName: types.NamespacedName{
3364+
Namespace: "default",
3365+
Name: "secure-app2",
3366+
}}: {},
3367+
{NamespacedName: types.NamespacedName{
3368+
Namespace: "default",
3369+
Name: "secure-app3",
3370+
}}: {
3371+
Valid: true,
3372+
Spec: graph.L4RouteSpec{
3373+
Hostnames: []v1.Hostname{"test.example.com"},
3374+
BackendRef: graph.BackendRef{},
3375+
},
3376+
},
3377+
{NamespacedName: types.NamespacedName{
3378+
Namespace: "default",
3379+
Name: "secure-app4",
3380+
}}: {
3381+
Spec: graph.L4RouteSpec{
3382+
Hostnames: []v1.Hostname{"app.example.com", "cafe.example.com"},
3383+
BackendRef: graph.BackendRef{
3384+
SvcNsName: types.NamespacedName{
3385+
Namespace: "default",
3386+
Name: "secure-app",
3387+
},
3388+
ServicePort: apiv1.ServicePort{
3389+
Name: "https",
3390+
Protocol: "TCP",
3391+
Port: 8443,
3392+
TargetPort: intstr.IntOrString{
3393+
Type: intstr.Int,
3394+
IntVal: 8443,
3395+
},
3396+
},
3397+
Valid: true,
3398+
},
3399+
},
3400+
Valid: true,
3401+
},
3402+
},
3403+
Valid: true,
3404+
},
3405+
},
3406+
},
3407+
}
3408+
3409+
fakeResolver := resolverfakes.FakeServiceResolver{}
3410+
fakeResolver.ResolveReturns(nil, errors.New("error"))
3411+
3412+
streamUpstreams := buildStreamUpstreams(context.Background(), testGraph.Gateway.Listeners, &fakeResolver)
3413+
3414+
expectedStreamUpstreams := []Upstream{
3415+
{
3416+
Name: "default_secure-app_8443",
3417+
ErrorMsg: "error",
3418+
},
3419+
}
3420+
g := NewWithT(t)
3421+
3422+
g.Expect(streamUpstreams).To(Equal(expectedStreamUpstreams))
3423+
}

internal/mode/static/state/graph/backend_refs.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import (
1919
staticConds "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/conditions"
2020
)
2121

22-
// BackendRef is an internal representation of a backendRef in an HTTP/GRPCRoute.
22+
// BackendRef is an internal representation of a backendRef in an HTTP/GRPC/TLSRoute.
2323
type BackendRef struct {
2424
// BackendTLSPolicy is the BackendTLSPolicy of the Service which is referenced by the backendRef.
2525
BackendTLSPolicy *BackendTLSPolicy

0 commit comments

Comments
 (0)