Skip to content

Commit 55f3d19

Browse files
abaguasytsarev
andauthored
Add support for GatewayAPI's TLSRoute (#2118)
* Add support for GatewayAPI's TLSRoute Signed-off-by: Andre Aguas <andre.aguas@protonmail.com> * Local setup for TLSRoute + docs Signed-off-by: Yury Tsarev <yury@upbound.io> * Make resource as headers so table of contents picks it up Signed-off-by: Yury Tsarev <yury@upbound.io> * Prettify headers Signed-off-by: Yury Tsarev <yury@upbound.io> --------- Signed-off-by: Andre Aguas <andre.aguas@protonmail.com> Signed-off-by: Yury Tsarev <yury@upbound.io> Co-authored-by: Yury Tsarev <yury@upbound.io>
1 parent 2fa5311 commit 55f3d19

15 files changed

+498
-9
lines changed

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,9 @@ deploy-test-apps: ## Deploy Podinfo (example app) and Apply Gslb Custom Resource
286286
@echo -e "\n$(YELLOW)Deploy GSLB CR for Gateway API UDPRoute $(NC)"
287287
$(call apply-cr,deploy/gslb/k8gb.absa.oss_v1beta1_gslb_cr_failover_gatewayapi_udproute.yaml)
288288

289+
@echo -e "\n$(YELLOW)Deploy GSLB CR for Gateway API TLSRoute $(NC)"
290+
$(call apply-cr,deploy/gslb/k8gb.absa.oss_v1beta1_gslb_cr_failover_gatewayapi_tlsroute.yaml)
291+
289292
@echo -e "\n$(YELLOW)Deploy podinfo $(NC)"
290293
kubectl apply -f deploy/test-apps
291294
helm repo add podinfo https://stefanprodan.github.io/podinfo
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package gatewayapitlsroute
2+
3+
/*
4+
Copyright 2021-2025 The k8gb Contributors.
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
18+
Generated by GoLic, for more details see: https://github.com/AbsaOSS/golic
19+
*/
20+
21+
import (
22+
"github.com/k8gb-io/k8gb/controllers/refresolver/gatewayapi"
23+
gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1"
24+
gatewayapiv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
25+
gatewayapiv1alpha3 "sigs.k8s.io/gateway-api/apis/v1alpha3"
26+
)
27+
28+
// TLSRouteAdapter adapts TLSRoute to the RouteAdapter interface
29+
type TLSRouteAdapter struct {
30+
tlsRoute *gatewayapiv1alpha3.TLSRoute
31+
}
32+
33+
// NewTLSRouteAdapter creates a new TLSRoute adapter
34+
func NewTLSRouteAdapter(tlsRoute *gatewayapiv1alpha3.TLSRoute) *TLSRouteAdapter {
35+
return &TLSRouteAdapter{tlsRoute: tlsRoute}
36+
}
37+
38+
func (a *TLSRouteAdapter) GetHostnames() ([]gatewayapiv1alpha3.Hostname, error) {
39+
return a.tlsRoute.Spec.Hostnames, nil
40+
}
41+
42+
func (a *TLSRouteAdapter) GetParentRefs() []gatewayapiv1.ParentReference {
43+
return a.tlsRoute.Spec.ParentRefs
44+
}
45+
46+
func (a *TLSRouteAdapter) GetRules() []gatewayapi.RouteRule {
47+
rules := make([]gatewayapi.RouteRule, len(a.tlsRoute.Spec.Rules))
48+
for i := range a.tlsRoute.Spec.Rules {
49+
rules[i] = &TLSRouteRuleAdapter{rule: &a.tlsRoute.Spec.Rules[i]}
50+
}
51+
return rules
52+
}
53+
54+
func (a *TLSRouteAdapter) GetName() string {
55+
return a.tlsRoute.Name
56+
}
57+
58+
func (a *TLSRouteAdapter) GetNamespace() string {
59+
return a.tlsRoute.Namespace
60+
}
61+
62+
// TLSRouteRuleAdapter adapts TLSRouteRule to the RouteRule interface
63+
type TLSRouteRuleAdapter struct {
64+
rule *gatewayapiv1alpha2.TLSRouteRule
65+
}
66+
67+
func (a *TLSRouteRuleAdapter) GetBackendRefs() []gatewayapiv1.BackendRef {
68+
return a.rule.BackendRefs
69+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package gatewayapitlsroute
2+
3+
/*
4+
Copyright 2021-2025 The k8gb Contributors.
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
18+
Generated by GoLic, for more details see: https://github.com/AbsaOSS/golic
19+
*/
20+
21+
import (
22+
"fmt"
23+
24+
context "context"
25+
26+
k8gbv1beta1 "github.com/k8gb-io/k8gb/api/v1beta1"
27+
"github.com/k8gb-io/k8gb/controllers/logging"
28+
"github.com/k8gb-io/k8gb/controllers/refresolver/gatewayapi"
29+
"github.com/k8gb-io/k8gb/controllers/refresolver/queryopts"
30+
"github.com/k8gb-io/k8gb/controllers/utils"
31+
"k8s.io/apimachinery/pkg/api/errors"
32+
"sigs.k8s.io/controller-runtime/pkg/client"
33+
gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1"
34+
gatewayapiv1alpha3 "sigs.k8s.io/gateway-api/apis/v1alpha3"
35+
)
36+
37+
var log = logging.Logger()
38+
39+
type ReferenceResolver struct {
40+
tlsRoute *TLSRouteAdapter
41+
gateway *gatewayapiv1.Gateway
42+
}
43+
44+
// NewReferenceResolver creates a new reference resolver capable of understanding `networking.gateway.api/v1` resources
45+
func NewReferenceResolver(gslb *k8gbv1beta1.Gslb, k8sClient client.Client) (*ReferenceResolver, error) {
46+
tlsRouteList, err := getGslbTLSRouteRef(gslb, k8sClient)
47+
if err != nil {
48+
return nil, err
49+
}
50+
51+
if len(tlsRouteList) != 1 {
52+
return nil, fmt.Errorf("exactly 1 TLSRoute resource expected but %d were found", len(tlsRouteList))
53+
}
54+
tlsRoute := tlsRouteList[0]
55+
56+
tlsRouteAdapter := NewTLSRouteAdapter(&tlsRoute)
57+
gateway, err := gatewayapi.GetGateway(tlsRouteAdapter, k8sClient)
58+
if err != nil {
59+
return nil, err
60+
}
61+
62+
return &ReferenceResolver{
63+
tlsRoute: tlsRouteAdapter,
64+
gateway: gateway,
65+
}, nil
66+
}
67+
68+
// getGslbTLSRouteRef resolves a Gateway API TLSRoute resource referenced by the Gslb spec
69+
func getGslbTLSRouteRef(gslb *k8gbv1beta1.Gslb, k8sClient client.Client) ([]gatewayapiv1alpha3.TLSRoute, error) {
70+
query, err := queryopts.Get(gslb.Spec.ResourceRef, gslb.Namespace)
71+
if err != nil {
72+
return nil, err
73+
}
74+
75+
switch query.Mode {
76+
case queryopts.QueryModeGet:
77+
var tlsRoute = gatewayapiv1alpha3.TLSRoute{}
78+
err = k8sClient.Get(context.TODO(), *query.GetKey, &tlsRoute)
79+
if err != nil {
80+
if errors.IsNotFound(err) {
81+
log.Info().
82+
Str("gslb", gslb.Name).
83+
Str("namespace", gslb.Namespace).
84+
Msg("Can't find referenced TLSRoute resource")
85+
}
86+
return nil, err
87+
}
88+
return []gatewayapiv1alpha3.TLSRoute{tlsRoute}, nil
89+
90+
case queryopts.QueryModeList:
91+
tlsrouteList := &gatewayapiv1alpha3.TLSRouteList{}
92+
err = k8sClient.List(context.TODO(), tlsrouteList, query.ListOpts...)
93+
if err != nil {
94+
if errors.IsNotFound(err) {
95+
log.Info().
96+
Str("gslb", gslb.Name).
97+
Str("namespace", gslb.Namespace).
98+
Msg("Can't find referenced TLSRoute resource")
99+
}
100+
return nil, err
101+
}
102+
return tlsrouteList.Items, nil
103+
}
104+
return nil, fmt.Errorf("unknown query mode %v", query.Mode)
105+
}
106+
107+
// GetServers retrieves the GSLB server configuration from the TLSRoute resource
108+
func (rr *ReferenceResolver) GetServers() ([]*k8gbv1beta1.Server, error) {
109+
return gatewayapi.GetServersFromRoute(rr.tlsRoute)
110+
}
111+
112+
// GetGslbExposedIPs retrieves the load balancer IP address of the GSLB
113+
func (rr *ReferenceResolver) GetGslbExposedIPs(gslbAnnotations map[string]string, parentZoneDNSServers utils.DNSList) ([]string, error) {
114+
return gatewayapi.GetGslbExposedIPs(rr.gateway, gslbAnnotations, parentZoneDNSServers)
115+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package gatewayapitlsroute
2+
3+
/*
4+
Copyright 2021-2025 The k8gb Contributors.
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
18+
Generated by GoLic, for more details see: https://github.com/AbsaOSS/golic
19+
*/
20+
21+
import (
22+
"testing"
23+
24+
k8gbv1beta1 "github.com/k8gb-io/k8gb/api/v1beta1"
25+
"github.com/k8gb-io/k8gb/controllers/utils"
26+
"github.com/stretchr/testify/assert"
27+
)
28+
29+
func TestGetServers(t *testing.T) {
30+
var tests = []struct {
31+
name string
32+
tlsRouteFile string
33+
expectedServers []*k8gbv1beta1.Server
34+
}{
35+
{
36+
name: "single hostname and backend; backend without namespace specified",
37+
tlsRouteFile: "../testdata/gatewayapi_tlsroute.yaml",
38+
expectedServers: []*k8gbv1beta1.Server{
39+
{
40+
Host: "gatewayapi-tlsroute.cloud.example.com",
41+
Services: []*k8gbv1beta1.NamespacedName{
42+
{
43+
Name: "gatewayapi-tlsroute-service",
44+
Namespace: "test-gslb",
45+
},
46+
},
47+
},
48+
},
49+
},
50+
{
51+
name: "single hostname and backend; backend with namespace specified",
52+
tlsRouteFile: "./testdata/gatewayapi_tlsroute_backend_with_namespace.yaml",
53+
expectedServers: []*k8gbv1beta1.Server{
54+
{
55+
Host: "gatewayapi-tlsroute.cloud.example.com",
56+
Services: []*k8gbv1beta1.NamespacedName{
57+
{
58+
Name: "gatewayapi-tlsroute-service",
59+
Namespace: "test-backend",
60+
},
61+
},
62+
},
63+
},
64+
},
65+
{
66+
name: "multiple hostnames",
67+
tlsRouteFile: "./testdata/gatewayapi_tlsroute_multiple_hostnames.yaml",
68+
expectedServers: []*k8gbv1beta1.Server{
69+
{
70+
Host: "gatewayapi-tlsroute1.cloud.example.com",
71+
Services: []*k8gbv1beta1.NamespacedName{
72+
{
73+
Name: "gatewayapi-tlsroute-service",
74+
Namespace: "test-gslb",
75+
},
76+
},
77+
},
78+
{
79+
Host: "gatewayapi-tlsroute2.cloud.example.com",
80+
Services: []*k8gbv1beta1.NamespacedName{
81+
{
82+
Name: "gatewayapi-tlsroute-service",
83+
Namespace: "test-gslb",
84+
},
85+
},
86+
},
87+
},
88+
},
89+
{
90+
name: "multiple backendRefs",
91+
tlsRouteFile: "./testdata/gatewayapi_tlsroute_multiple_backendrefs.yaml",
92+
expectedServers: []*k8gbv1beta1.Server{
93+
{
94+
Host: "gatewayapi-tlsroute.cloud.example.com",
95+
Services: []*k8gbv1beta1.NamespacedName{
96+
{
97+
Name: "gatewayapi-tlsroute-service1",
98+
Namespace: "test-gslb",
99+
},
100+
{
101+
Name: "gatewayapi-tlsroute-service2",
102+
Namespace: "test-gslb",
103+
},
104+
},
105+
},
106+
},
107+
},
108+
}
109+
for _, test := range tests {
110+
t.Run(test.name, func(t *testing.T) {
111+
// arrange
112+
tlsRoute := utils.FileToGatewayApiTlsRoute(test.tlsRouteFile)
113+
resolver := ReferenceResolver{
114+
tlsRoute: NewTLSRouteAdapter(tlsRoute),
115+
}
116+
117+
// act
118+
servers, err := resolver.GetServers()
119+
assert.NoError(t, err)
120+
121+
// assert
122+
assert.Equal(t, test.expectedServers, servers)
123+
})
124+
}
125+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
apiVersion: gateway.networking.k8s.io/v1alpha3
2+
kind: TLSRoute
3+
metadata:
4+
name: gatewayapi-tlsroute
5+
namespace: test-gslb
6+
spec:
7+
parentRefs:
8+
- name: gatewayapi-gateway
9+
hostnames:
10+
- gatewayapi-tlsroute.cloud.example.com
11+
rules:
12+
- backendRefs:
13+
- name: gatewayapi-tlsroute-service
14+
namespace: test-backend
15+
port: 8080
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
apiVersion: gateway.networking.k8s.io/v1alpha3
2+
kind: TLSRoute
3+
metadata:
4+
name: gatewayapi-tlsroute
5+
namespace: test-gslb
6+
spec:
7+
parentRefs:
8+
- name: gatewayapi-gateway
9+
hostnames:
10+
- gatewayapi-tlsroute.cloud.example.com
11+
rules:
12+
- matches:
13+
- path:
14+
type: PathPrefix
15+
prefix: /debug
16+
backendRefs:
17+
- name: gatewayapi-tlsroute-service1
18+
port: 8080
19+
- backendRefs:
20+
- name: gatewayapi-tlsroute-service2
21+
port: 8080
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
apiVersion: gateway.networking.k8s.io/v1alpha3
2+
kind: TLSRoute
3+
metadata:
4+
name: gatewayapi-tlsroute
5+
namespace: test-gslb
6+
spec:
7+
parentRefs:
8+
- name: gatewayapi-gateway
9+
hostnames:
10+
- gatewayapi-tlsroute1.cloud.example.com
11+
- gatewayapi-tlsroute2.cloud.example.com
12+
rules:
13+
- backendRefs:
14+
- name: gatewayapi-tlsroute-service
15+
port: 8080

controllers/refresolver/refresolver.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"github.com/k8gb-io/k8gb/controllers/refresolver/gatewayapigrpcroute"
2727
"github.com/k8gb-io/k8gb/controllers/refresolver/gatewayapihttproute"
2828
"github.com/k8gb-io/k8gb/controllers/refresolver/gatewayapitcproute"
29+
"github.com/k8gb-io/k8gb/controllers/refresolver/gatewayapitlsroute"
2930
"github.com/k8gb-io/k8gb/controllers/refresolver/gatewayapiudproute"
3031
"github.com/k8gb-io/k8gb/controllers/refresolver/ingress"
3132
"github.com/k8gb-io/k8gb/controllers/refresolver/istiovirtualservice"
@@ -71,5 +72,8 @@ func New(gslb *k8gbv1beta1.Gslb, k8sClient client.Client) (GslbReferenceResolver
7172
if gslb.Spec.ResourceRef.Kind == "UDPRoute" && gslb.Spec.ResourceRef.APIVersion == "gateway.networking.k8s.io/v1alpha2" {
7273
return gatewayapiudproute.NewReferenceResolver(gslb, k8sClient)
7374
}
75+
if gslb.Spec.ResourceRef.Kind == "TLSRoute" && gslb.Spec.ResourceRef.APIVersion == "gateway.networking.k8s.io/v1alpha3" {
76+
return gatewayapitlsroute.NewReferenceResolver(gslb, k8sClient)
77+
}
7478
return nil, fmt.Errorf("APIVersion:%s, Kind:%s not supported", gslb.Spec.ResourceRef.APIVersion, gslb.Spec.ResourceRef.Kind)
7579
}

0 commit comments

Comments
 (0)