/
grpcroute.go
144 lines (130 loc) · 4.7 KB
/
grpcroute.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package subtranslator
import (
"fmt"
"github.com/kong/go-kong/kong"
"github.com/samber/lo"
"github.com/kong/kubernetes-ingress-controller/v3/internal/dataplane/kongstate"
"github.com/kong/kubernetes-ingress-controller/v3/internal/gatewayapi"
"github.com/kong/kubernetes-ingress-controller/v3/internal/util"
)
func getGRPCMatchDefaults() (
map[gatewayapi.GRPCMethodMatchType]string,
map[gatewayapi.GRPCMethodMatchType]string,
) {
// Kong routes derived from a GRPCRoute use a path composed of the match's gRPC service and method
// If either the service or method is omitted, there is a default regex determined by the match type
// https://gateway-api.sigs.k8s.io/geps/gep-1016/#matcher-types describes the defaults
// default path components for the GRPC service
return map[gatewayapi.GRPCMethodMatchType]string{
gatewayapi.GRPCMethodMatchType(""): ".+",
gatewayapi.GRPCMethodMatchExact: ".+",
gatewayapi.GRPCMethodMatchRegularExpression: ".+",
},
// default path components for the GRPC method
map[gatewayapi.GRPCMethodMatchType]string{
gatewayapi.GRPCMethodMatchType(""): "",
gatewayapi.GRPCMethodMatchExact: "",
gatewayapi.GRPCMethodMatchRegularExpression: ".+",
}
}
func GenerateKongRoutesFromGRPCRouteRule(
grpcroute *gatewayapi.GRPCRoute,
ruleNumber int,
) []kongstate.Route {
if ruleNumber >= len(grpcroute.Spec.Rules) {
return nil
}
routeName := func(namespace string, name string, ruleNumber int, matchNumber int) *string {
return kong.String(fmt.Sprintf(
"grpcroute.%s.%s.%d.%d",
namespace,
name,
ruleNumber,
matchNumber,
))
}
// Gather the K8s object information and hostnames from the GRPCRoute.
ingressObjectInfo := util.FromK8sObject(grpcroute)
tags := util.GenerateTagsForObject(grpcroute)
grpcProtocols := kong.StringSlice("grpc", "grpcs")
rule := grpcroute.Spec.Rules[ruleNumber]
// Kong Route expects to have for gRPC, at least one of Hosts, Headers or Paths fields set.
// For no matches it can be a catch-all or route based on hostnames.
if len(rule.Matches) == 0 {
r := kongstate.Route{
Ingress: ingressObjectInfo,
Route: kong.Route{
Name: routeName(grpcroute.Namespace, grpcroute.Name, ruleNumber, 0),
Protocols: grpcProtocols,
Tags: tags,
},
}
if configuredHostnames := getGRPCRouteHostnamesAsSliceOfStringPointers(grpcroute); len(configuredHostnames) > 0 {
// Match based on hostnames.
r.Hosts = configuredHostnames
} else {
// No hostnames configured, so this is a catch-all.
// https://docs.konghq.com/gateway/latest/production/configuring-a-grpc-service/#single-grpc-service-and-route
r.Paths = kong.StringSlice("/")
}
return []kongstate.Route{r}
}
// Rule matches are configured, hostname may be specified too.
routes := make([]kongstate.Route, 0, len(rule.Matches))
for matchNumber, match := range rule.Matches {
r := kongstate.Route{
Ingress: ingressObjectInfo,
Route: kong.Route{
Name: routeName(grpcroute.Namespace, grpcroute.Name, ruleNumber, matchNumber),
Protocols: grpcProtocols,
Tags: tags,
Hosts: getGRPCRouteHostnamesAsSliceOfStringPointers(grpcroute),
},
}
if match.Method != nil {
serviceMap, methodMap := getGRPCMatchDefaults()
var method, service string
matchMethod := match.Method.Method
matchService := match.Method.Service
var matchType gatewayapi.GRPCMethodMatchType
if match.Method.Type == nil {
matchType = gatewayapi.GRPCMethodMatchExact
} else {
matchType = *match.Method.Type
}
if matchMethod == nil {
method = methodMap[matchType]
} else {
method = *matchMethod
}
if matchService == nil {
service = serviceMap[matchType]
} else {
service = *matchService
}
path := kong.String(KongPathRegexPrefix + fmt.Sprintf("/%s/%s", service, method))
r.Paths = append(r.Paths, path)
}
r.Headers = map[string][]string{}
for _, hmatch := range match.Headers {
name := string(hmatch.Name)
r.Headers[name] = append(r.Headers[name], hmatch.Value)
}
routes = append(routes, r)
}
return routes
}
// -----------------------------------------------------------------------------
// Translate GRPCRoute - Utils
// -----------------------------------------------------------------------------
// getGRPCRouteHostnamesAsSliceOfStringPointers translates the hostnames defined
// in an GRPCRoute specification into a []*string slice, which is the type required
// by kong.Route{}.
func getGRPCRouteHostnamesAsSliceOfStringPointers(grpcroute *gatewayapi.GRPCRoute) []*string {
if len(grpcroute.Spec.Hostnames) == 0 {
return nil
}
return lo.Map(grpcroute.Spec.Hostnames, func(h gatewayapi.Hostname, _ int) *string {
return lo.ToPtr(string(h))
})
}