Skip to content

Commit 936b2e7

Browse files
github-actions[bot]AlinsRanronething
authored
feat(gateway-api): support TLSRoute (#2594) (1afb9ac) (#303)
Co-authored-by: AlinsRan <[email protected]> Co-authored-by: Ashing Zheng <[email protected]>
1 parent bb9e53d commit 936b2e7

File tree

22 files changed

+1042
-9
lines changed

22 files changed

+1042
-9
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ GATEAY_API_VERSION ?= v1.3.0
6262
SUPPORTED_EXTENDED_FEATURES = "HTTPRouteDestinationPortMatching,HTTPRouteMethodMatching,HTTPRoutePortRedirect,HTTPRouteRequestMirror,HTTPRouteSchemeRedirect,GatewayAddressEmpty,HTTPRouteResponseHeaderModification,GatewayPort8080,HTTPRouteHostRewrite,HTTPRouteQueryParamMatching,HTTPRoutePathRewrite"
6363
CONFORMANCE_TEST_REPORT_OUTPUT ?= $(DIR)/apisix-ingress-controller-conformance-report.yaml
6464
## https://github.com/kubernetes-sigs/gateway-api/blob/v1.3.0/conformance/utils/suite/profiles.go
65-
CONFORMANCE_PROFILES ?= GATEWAY-HTTP,GATEWAY-GRPC
65+
CONFORMANCE_PROFILES ?= GATEWAY-HTTP,GATEWAY-GRPC,GATEWAY-TLS
6666

6767
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
6868
ifeq (,$(shell go env GOBIN))

api/v2/shared_types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ const (
101101
SchemeTCP = "tcp"
102102
// SchemeUDP represents the UDP protocol.
103103
SchemeUDP = "udp"
104+
// SchemeTLS represents the TLS protocol.
105+
SchemeTLS = "tls"
104106
)
105107

106108
const (

config/rbac/role.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ rules:
9494
- httproutes/status
9595
- referencegrants/status
9696
- tcproutes/status
97+
- tlsroutes/status
9798
- udproutes/status
9899
verbs:
99100
- get
@@ -106,6 +107,7 @@ rules:
106107
- httproutes
107108
- referencegrants
108109
- tcproutes
110+
- tlsroutes
109111
- udproutes
110112
verbs:
111113
- get

docs/en/latest/concepts/gateway-api.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ By supporting Gateway API, the APISIX Ingress controller can realize richer func
5050
| HTTPRoute | Supported | Partially supported | Not supported | v1 |
5151
| GRPCRoute | Supported | Supported | Not supported | v1 |
5252
| ReferenceGrant | Supported | Not supported | Not supported | v1beta1 |
53-
| TLSRoute | Not supported | Not supported | Not supported | v1alpha2 |
53+
| TLSRoute | Supported | Supported | Not supported | v1alpha2 |
5454
| TCPRoute | Supported | Supported | Not supported | v1alpha2 |
5555
| UDPRoute | Supported | Supported | Not supported | v1alpha2 |
5656
| BackendTLSPolicy | Not supported | Not supported | Not supported | v1alpha3 |
@@ -59,7 +59,7 @@ By supporting Gateway API, the APISIX Ingress controller can realize richer func
5959

6060
For configuration examples, see the Gateway API tabs in [Configuration Examples](../reference/example.md).
6161

62-
For a complete list of configuration options, refer to the [Gateway API Reference](https://gateway-api.sigs.k8s.io/reference/main/spec/). Be aware that some fields are not supported, or partially supported.
62+
For a complete list of configuration options, refer to the [Gateway API Reference](https://gateway-api.sigs.k8s.io/reference/spec/). Be aware that some fields are not supported, or partially supported.
6363

6464
## Unsupported / Partially Supported Fields
6565

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package translator
19+
20+
import (
21+
"fmt"
22+
23+
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
24+
gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
25+
26+
adctypes "github.com/apache/apisix-ingress-controller/api/adc"
27+
apiv2 "github.com/apache/apisix-ingress-controller/api/v2"
28+
"github.com/apache/apisix-ingress-controller/internal/controller/label"
29+
"github.com/apache/apisix-ingress-controller/internal/id"
30+
"github.com/apache/apisix-ingress-controller/internal/provider"
31+
"github.com/apache/apisix-ingress-controller/internal/types"
32+
)
33+
34+
func (t *Translator) TranslateTLSRoute(tctx *provider.TranslateContext, tlsRoute *gatewayv1alpha2.TLSRoute) (*TranslateResult, error) {
35+
result := &TranslateResult{}
36+
rules := tlsRoute.Spec.Rules
37+
labels := label.GenLabel(tlsRoute)
38+
hosts := make([]string, 0, len(tlsRoute.Spec.Hostnames))
39+
for _, hostname := range tlsRoute.Spec.Hostnames {
40+
hosts = append(hosts, string(hostname))
41+
}
42+
for ruleIndex, rule := range rules {
43+
service := adctypes.NewDefaultService()
44+
service.Labels = labels
45+
service.Name = adctypes.ComposeServiceNameWithStream(tlsRoute.Namespace, tlsRoute.Name, fmt.Sprintf("%d", ruleIndex), "TLS")
46+
service.ID = id.GenID(service.Name)
47+
var (
48+
upstreams = make([]*adctypes.Upstream, 0)
49+
weightedUpstreams = make([]adctypes.TrafficSplitConfigRuleWeightedUpstream, 0)
50+
)
51+
for _, backend := range rule.BackendRefs {
52+
if backend.Namespace == nil {
53+
namespace := gatewayv1.Namespace(tlsRoute.Namespace)
54+
backend.Namespace = &namespace
55+
}
56+
upstream := newDefaultUpstreamWithoutScheme()
57+
upNodes, err := t.translateBackendRef(tctx, backend, DefaultEndpointFilter)
58+
if err != nil {
59+
continue
60+
}
61+
if len(upNodes) == 0 {
62+
continue
63+
}
64+
// TODO: Confirm BackendTrafficPolicy attachment with e2e test case.
65+
t.AttachBackendTrafficPolicyToUpstream(backend, tctx.BackendTrafficPolicies, upstream)
66+
upstream.Nodes = upNodes
67+
var (
68+
kind string
69+
port int32
70+
)
71+
if backend.Kind == nil {
72+
kind = types.KindService
73+
} else {
74+
kind = string(*backend.Kind)
75+
}
76+
if backend.Port != nil {
77+
port = int32(*backend.Port)
78+
}
79+
namespace := string(*backend.Namespace)
80+
name := string(backend.Name)
81+
upstreamName := adctypes.ComposeUpstreamNameForBackendRef(kind, namespace, name, port)
82+
upstream.Name = upstreamName
83+
upstream.ID = id.GenID(upstreamName)
84+
upstreams = append(upstreams, upstream)
85+
}
86+
87+
// Handle multiple backends with traffic-split plugin
88+
if len(upstreams) == 0 {
89+
// Create a default upstream if no valid backends
90+
upstream := adctypes.NewDefaultUpstream()
91+
service.Upstream = upstream
92+
} else if len(upstreams) == 1 {
93+
// Single backend - use directly as service upstream
94+
service.Upstream = upstreams[0]
95+
// remove the id and name of the service.upstream, adc schema does not need id and name for it
96+
service.Upstream.ID = ""
97+
service.Upstream.Name = ""
98+
} else {
99+
// Multiple backends - use traffic-split plugin
100+
service.Upstream = upstreams[0]
101+
// remove the id and name of the service.upstream, adc schema does not need id and name for it
102+
service.Upstream.ID = ""
103+
service.Upstream.Name = ""
104+
105+
upstreams = upstreams[1:]
106+
107+
if len(upstreams) > 0 {
108+
service.Upstreams = upstreams
109+
}
110+
111+
// Set weight in traffic-split for the default upstream
112+
weight := apiv2.DefaultWeight
113+
if rule.BackendRefs[0].Weight != nil {
114+
weight = int(*rule.BackendRefs[0].Weight)
115+
}
116+
weightedUpstreams = append(weightedUpstreams, adctypes.TrafficSplitConfigRuleWeightedUpstream{
117+
Weight: weight,
118+
})
119+
120+
// Set other upstreams in traffic-split using upstream_id
121+
for i, upstream := range upstreams {
122+
weight := apiv2.DefaultWeight
123+
// get weight from the backend refs starting from the second backend
124+
if i+1 < len(rule.BackendRefs) && rule.BackendRefs[i+1].Weight != nil {
125+
weight = int(*rule.BackendRefs[i+1].Weight)
126+
}
127+
weightedUpstreams = append(weightedUpstreams, adctypes.TrafficSplitConfigRuleWeightedUpstream{
128+
UpstreamID: upstream.ID,
129+
Weight: weight,
130+
})
131+
}
132+
133+
if len(weightedUpstreams) > 0 {
134+
if service.Plugins == nil {
135+
service.Plugins = make(map[string]any)
136+
}
137+
service.Plugins["traffic-split"] = &adctypes.TrafficSplitConfig{
138+
Rules: []adctypes.TrafficSplitConfigRule{
139+
{
140+
WeightedUpstreams: weightedUpstreams,
141+
},
142+
},
143+
}
144+
}
145+
}
146+
147+
for _, host := range hosts {
148+
streamRoute := adctypes.NewDefaultStreamRoute()
149+
streamRouteName := adctypes.ComposeStreamRouteName(tlsRoute.Namespace, tlsRoute.Name, fmt.Sprintf("%d", ruleIndex), "TLS")
150+
streamRoute.Name = streamRouteName
151+
streamRoute.ID = id.GenID(streamRouteName)
152+
streamRoute.SNI = host
153+
streamRoute.Labels = labels
154+
service.StreamRoutes = append(service.StreamRoutes, streamRoute)
155+
}
156+
result.Services = append(result.Services, service)
157+
}
158+
return result, nil
159+
}

internal/controller/indexer/indexer.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ func SetupIndexer(mgr ctrl.Manager) error {
6767
&gatewayv1.GRPCRoute{}: setupGRPCRouteIndexer,
6868
&gatewayv1alpha2.TCPRoute{}: setupTCPRouteIndexer,
6969
&gatewayv1alpha2.UDPRoute{}: setupUDPRouteIndexer,
70+
&gatewayv1alpha2.TLSRoute{}: setupTLSRouteIndexer,
7071
&gatewayv1.GatewayClass{}: setupGatewayClassIndexer,
7172
&v1alpha1.Consumer{}: setupConsumerIndexer,
7273
} {
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package indexer
19+
20+
import (
21+
"context"
22+
23+
ctrl "sigs.k8s.io/controller-runtime"
24+
"sigs.k8s.io/controller-runtime/pkg/client"
25+
gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
26+
27+
internaltypes "github.com/apache/apisix-ingress-controller/internal/types"
28+
)
29+
30+
func setupTLSRouteIndexer(mgr ctrl.Manager) error {
31+
if err := mgr.GetFieldIndexer().IndexField(
32+
context.Background(),
33+
&gatewayv1alpha2.TLSRoute{},
34+
ParentRefs,
35+
TLSRouteParentRefsIndexFunc,
36+
); err != nil {
37+
return err
38+
}
39+
40+
if err := mgr.GetFieldIndexer().IndexField(
41+
context.Background(),
42+
&gatewayv1alpha2.TLSRoute{},
43+
ServiceIndexRef,
44+
TLSPRouteServiceIndexFunc,
45+
); err != nil {
46+
return err
47+
}
48+
return nil
49+
}
50+
51+
func TLSRouteParentRefsIndexFunc(rawObj client.Object) []string {
52+
tr := rawObj.(*gatewayv1alpha2.TLSRoute)
53+
keys := make([]string, 0, len(tr.Spec.ParentRefs))
54+
for _, ref := range tr.Spec.ParentRefs {
55+
ns := tr.GetNamespace()
56+
if ref.Namespace != nil {
57+
ns = string(*ref.Namespace)
58+
}
59+
keys = append(keys, GenIndexKey(ns, string(ref.Name)))
60+
}
61+
return keys
62+
}
63+
64+
func TLSPRouteServiceIndexFunc(rawObj client.Object) []string {
65+
tr := rawObj.(*gatewayv1alpha2.TLSRoute)
66+
keys := make([]string, 0, len(tr.Spec.Rules))
67+
for _, rule := range tr.Spec.Rules {
68+
for _, backend := range rule.BackendRefs {
69+
namespace := tr.GetNamespace()
70+
if backend.Kind != nil && *backend.Kind != internaltypes.KindService {
71+
continue
72+
}
73+
if backend.Namespace != nil {
74+
namespace = string(*backend.Namespace)
75+
}
76+
keys = append(keys, GenIndexKey(namespace, string(backend.Name)))
77+
}
78+
}
79+
return keys
80+
}

0 commit comments

Comments
 (0)