Skip to content

Commit

Permalink
feat: support backend protocol selection for HTTPRoute
Browse files Browse the repository at this point in the history
  • Loading branch information
rtribotte committed Aug 29, 2024
1 parent 12a3734 commit 91fa9bb
Show file tree
Hide file tree
Showing 5 changed files with 284 additions and 3 deletions.
2 changes: 2 additions & 0 deletions integration/k8s_conformance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,8 @@ func (s *K8sConformanceSuite) TestK8sGatewayAPIConformance() {
features.SupportHTTPRoutePathRewrite,
features.SupportHTTPRoutePathRedirect,
features.SupportHTTPRouteResponseHeaderModification,
features.SupportHTTPRouteBackendProtocolH2C,
features.SupportHTTPRouteBackendProtocolWebSocket,
),
})
require.NoError(s.T(), err)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
kind: GatewayClass
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: my-gateway-class
spec:
controllerName: traefik.io/gateway-controller

---
kind: Gateway
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: my-gateway
namespace: default
spec:
gatewayClassName: my-gateway-class
listeners: # Use GatewayClass defaults for listener definition.
- name: http
protocol: HTTP
port: 80
allowedRoutes:
kinds:
- kind: HTTPRoute
group: gateway.networking.k8s.io
namespaces:
from: Same

---
kind: HTTPRoute
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: http-multi-protocols
namespace: default
spec:
parentRefs:
- name: my-gateway
kind: Gateway
group: gateway.networking.k8s.io
hostnames:
- "foo.com"
rules:
- matches:
- path:
type: Exact
value: /bar
backendRefs:
- name: whoami-h2c
port: 80
weight: 1
kind: Service
group: ""
- name: whoami-ws
port: 80
weight: 1
kind: Service
group: ""
- name: whoami-wss
port: 80
weight: 1
kind: Service
group: ""
102 changes: 102 additions & 0 deletions pkg/provider/kubernetes/gateway/fixtures/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -317,3 +317,105 @@ status:
ingress:
- hostname: foo.bar
- ip: 1.2.3.4

---
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami-h2c
namespace: default
labels:
kubernetes.io/service-name: whoami-h2c

addressType: IPv4
ports:
- name: h2c
protocol: TCP
port: 80
endpoints:
- addresses:
- 10.10.0.13
conditions:
ready: true

---
apiVersion: v1
kind: Service
metadata:
name: whoami-h2c
namespace: default

spec:
ports:
- protocol: TCP
port: 80
name: h2c
appProtocol: kubernetes.io/h2c

---
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami-ws
namespace: default
labels:
kubernetes.io/service-name: whoami-ws

addressType: IPv4
ports:
- name: ws
protocol: TCP
port: 80
endpoints:
- addresses:
- 10.10.0.14
conditions:
ready: true

---
apiVersion: v1
kind: Service
metadata:
name: whoami-ws
namespace: default

spec:
ports:
- protocol: TCP
port: 80
name: ws
appProtocol: kubernetes.io/ws

---
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami-wss
namespace: default
labels:
kubernetes.io/service-name: whoami-wss

addressType: IPv4
ports:
- name: wss
protocol: TCP
port: 80
endpoints:
- addresses:
- 10.10.0.15
conditions:
ready: true

---
apiVersion: v1
kind: Service
metadata:
name: whoami-wss
namespace: default

spec:
ports:
- protocol: TCP
port: 80
name: wss
appProtocol: kubernetes.io/wss
24 changes: 21 additions & 3 deletions pkg/provider/kubernetes/gateway/httproute.go
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,10 @@ func (p *Provider) loadHTTPServers(namespace string, backendRef gatev1.HTTPBacke
lb := &dynamic.ServersLoadBalancer{}
lb.SetDefaults()

protocol := getProtocol(*svcPort)
protocol, err := getProtocol(*svcPort)
if err != nil {
return nil, err
}

addresses := map[string]struct{}{}
for _, endpointSlice := range endpointSlices {
Expand Down Expand Up @@ -702,13 +705,28 @@ func createURLRewrite(filter *gatev1.HTTPURLRewriteFilter, pathMatch gatev1.HTTP
}, nil
}

func getProtocol(portSpec corev1.ServicePort) string {
func getProtocol(portSpec corev1.ServicePort) (string, error) {
if portSpec.Protocol != "TCP" {
return "", errors.New("only support TCP protocol")
}

protocol := "http"
if portSpec.Port == 443 || strings.HasPrefix(portSpec.Name, "https") {
protocol = "https"
}

return protocol
if portSpec.AppProtocol != nil {
switch *portSpec.AppProtocol {
case "kubernetes.io/h2c":
protocol = "h2c"
case "kubernetes.io/ws":
protocol = "http"
case "kubernetes.io/wss":
protocol = "https"
}
}

return protocol, nil
}

func mergeHTTPConfiguration(from, to *dynamic.Configuration) {
Expand Down
98 changes: 98 additions & 0 deletions pkg/provider/kubernetes/gateway/kubernetes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2452,6 +2452,104 @@ func TestLoadHTTPRoutes_backendExtensionRef(t *testing.T) {
TLS: &dynamic.TLSConfiguration{},
},
},
{
desc: "Simple HTTPRoute, with appProtocol service",
paths: []string{"services.yml", "httproute/with_app_protocol_service.yml"},
groupKindBackendFuncs: map[string]map[string]BuildBackendFunc{
traefikv1alpha1.GroupName: {"TraefikService": func(name, namespace string) (string, *dynamic.Service, error) {
// func should never be executed in case of cross-provider reference.
return "", nil, errors.New("BOOM")
}},
},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"default-http-multi-protocols-my-gateway-web-0-1c0cf64bde37d9d0df06": {
EntryPoints: []string{"web"},
Service: "default-http-multi-protocols-my-gateway-web-0-wrr",
Rule: "Host(`foo.com`) && Path(`/bar`)",
Priority: 100008,
RuleSyntax: "v3",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"default-http-multi-protocols-my-gateway-web-0-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "default-whoami-h2c-80",
Weight: ptr.To(1),
},
{
Name: "default-whoami-ws-80",
Weight: ptr.To(1),
},
{
Name: "default-whoami-wss-80",
Weight: ptr.To(1),
},
},
},
},
"default-whoami-h2c-80": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "h2c://10.10.0.13:80",
},
},
PassHostHeader: ptr.To(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"default-whoami-ws-80": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://10.10.0.14:80",
},
},
PassHostHeader: ptr.To(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"default-whoami-wss-80": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "https://10.10.0.15:80",
},
},
PassHostHeader: ptr.To(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{},
},
},
}

for _, test := range testCases {
Expand Down

0 comments on commit 91fa9bb

Please sign in to comment.