1 package gateway
2
3 import (
4
5 "fmt"
6
7
8 "github.com/pkg/errors"
9 "google.golang.org/protobuf/types/known/anypb"
10 "google.golang.org/protobuf/types/known/wrapperspb"
11 gw "sigs.k8s.io/gateway-api/apis/v1alpha1"
12
13
14 v3core "github.com/emissary-ingress/emissary/v3/pkg/api/envoy/config/core/v3"
15 v3listener "github.com/emissary-ingress/emissary/v3/pkg/api/envoy/config/listener/v3"
16 v3route "github.com/emissary-ingress/emissary/v3/pkg/api/envoy/config/route/v3"
17 v3httpman "github.com/emissary-ingress/emissary/v3/pkg/api/envoy/extensions/filters/network/http_connection_manager/v3"
18 v3matcher "github.com/emissary-ingress/emissary/v3/pkg/api/envoy/type/matcher/v3"
19
20
21 ecp_wellknown "github.com/emissary-ingress/emissary/v3/pkg/envoy-control-plane/wellknown"
22
23
24 "github.com/emissary-ingress/emissary/v3/pkg/kates"
25 )
26
27 func Compile_Gateway(gateway *gw.Gateway) (*CompiledConfig, error) {
28 src := SourceFromResource(gateway)
29 var listeners []*CompiledListener
30 for idx, l := range gateway.Spec.Listeners {
31 name := fmt.Sprintf("%s-%d", getName(gateway), idx)
32 listener, err := Compile_Listener(src, l, name)
33 if err != nil {
34 return nil, err
35 }
36 listeners = append(listeners, listener)
37 }
38 return &CompiledConfig{
39 CompiledItem: NewCompiledItem(src),
40 Listeners: listeners,
41 }, nil
42 }
43
44 func Compile_Listener(parent Source, lst gw.Listener, name string) (*CompiledListener, error) {
45 hcm := &v3httpman.HttpConnectionManager{
46 StatPrefix: name,
47 HttpFilters: []*v3httpman.HttpFilter{
48 {Name: ecp_wellknown.CORS},
49 {Name: ecp_wellknown.Router},
50 },
51 RouteSpecifier: &v3httpman.HttpConnectionManager_Rds{
52 Rds: &v3httpman.Rds{
53 ConfigSource: &v3core.ConfigSource{
54 ConfigSourceSpecifier: &v3core.ConfigSource_Ads{
55 Ads: &v3core.AggregatedConfigSource{},
56 },
57 },
58 RouteConfigName: name,
59 },
60 },
61 }
62 hcmAny, err := anypb.New(hcm)
63 if err != nil {
64 return nil, err
65 }
66
67 return &CompiledListener{
68 CompiledItem: NewCompiledItem(Sourcef("listener %s", lst.Hostname)),
69 Listener: &v3listener.Listener{
70 Name: name,
71 Address: &v3core.Address{Address: &v3core.Address_SocketAddress{SocketAddress: &v3core.SocketAddress{
72 Address: "0.0.0.0",
73 PortSpecifier: &v3core.SocketAddress_PortValue{PortValue: uint32(lst.Port)},
74 }}},
75 FilterChains: []*v3listener.FilterChain{
76 {
77 Filters: []*v3listener.Filter{
78 {
79 Name: ecp_wellknown.HTTPConnectionManager,
80 ConfigType: &v3listener.Filter_TypedConfig{TypedConfig: hcmAny},
81 },
82 },
83 },
84 },
85 },
86 Predicate: func(route *CompiledRoute) bool {
87 return true
88 },
89 Domains: []string{"*"},
90 }, nil
91
92 }
93
94 func Compile_HTTPRoute(httpRoute *gw.HTTPRoute) (*CompiledConfig, error) {
95 src := SourceFromResource(httpRoute)
96 clusterRefs := []*ClusterRef{}
97 var routes []*v3route.Route
98 for idx, rule := range httpRoute.Spec.Rules {
99 s := Sourcef("rule %d in %s", idx, src)
100 _routes, err := Compile_HTTPRouteRule(s, rule, httpRoute.Namespace, &clusterRefs)
101 if err != nil {
102 return nil, err
103 }
104 routes = append(routes, _routes...)
105 }
106 return &CompiledConfig{
107 CompiledItem: NewCompiledItem(src),
108 Routes: []*CompiledRoute{
109 {
110 CompiledItem: CompiledItem{Source: src, Namespace: httpRoute.Namespace},
111 HTTPRoute: httpRoute,
112 Routes: routes,
113 ClusterRefs: clusterRefs,
114 },
115 },
116 }, nil
117 }
118
119 func Compile_HTTPRouteRule(src Source, rule gw.HTTPRouteRule, namespace string, clusterRefs *[]*ClusterRef) ([]*v3route.Route, error) {
120 var clusters []*v3route.WeightedCluster_ClusterWeight
121 for idx, fwd := range rule.ForwardTo {
122 s := Sourcef("forwardTo %d in %s", idx, src)
123 clusters = append(clusters, Compile_HTTPRouteForwardTo(s, fwd, namespace, clusterRefs))
124 }
125
126 wc := &v3route.WeightedCluster{Clusters: clusters}
127
128 matches, err := Compile_HTTPRouteMatches(rule.Matches)
129 if err != nil {
130 return nil, err
131 }
132 var result []*v3route.Route
133 for _, match := range matches {
134 result = append(result, &v3route.Route{
135 Match: match,
136 Action: &v3route.Route_Route{Route: &v3route.RouteAction{
137 ClusterSpecifier: &v3route.RouteAction_WeightedClusters{WeightedClusters: wc},
138 }},
139 })
140 }
141
142 return result, err
143 }
144
145 func Compile_HTTPRouteForwardTo(src Source, forward gw.HTTPRouteForwardTo, namespace string, clusterRefs *[]*ClusterRef) *v3route.WeightedCluster_ClusterWeight {
146 suffix := ""
147 clusterName := *forward.ServiceName
148 if forward.Port != nil {
149 suffix = fmt.Sprintf("/%d", *forward.Port)
150 clusterName = fmt.Sprintf("%s_%d", *forward.ServiceName, *forward.Port)
151 }
152
153 *clusterRefs = append(*clusterRefs, &ClusterRef{
154 CompiledItem: NewCompiledItem(src),
155 Name: clusterName,
156 EndpointPath: fmt.Sprintf("k8s/%s/%s%s", namespace, *forward.ServiceName, suffix),
157 })
158 return &v3route.WeightedCluster_ClusterWeight{
159 Name: clusterName,
160 Weight: &wrapperspb.UInt32Value{Value: uint32(forward.Weight)},
161 }
162 }
163
164 func Compile_HTTPRouteMatches(matches []gw.HTTPRouteMatch) ([]*v3route.RouteMatch, error) {
165 var result []*v3route.RouteMatch
166 for _, match := range matches {
167 item, err := Compile_HTTPRouteMatch(match)
168 if err != nil {
169 return nil, err
170 }
171 result = append(result, item)
172 }
173 return result, nil
174 }
175
176 func Compile_HTTPRouteMatch(match gw.HTTPRouteMatch) (*v3route.RouteMatch, error) {
177 headers, err := Compile_HTTPHeaderMatch(match.Headers)
178 if err != nil {
179 return nil, err
180 }
181 result := &v3route.RouteMatch{
182 Headers: headers,
183 }
184
185 switch match.Path.Type {
186 case gw.PathMatchExact:
187 result.PathSpecifier = &v3route.RouteMatch_Path{Path: match.Path.Value}
188 case gw.PathMatchPrefix:
189 result.PathSpecifier = &v3route.RouteMatch_Prefix{Prefix: match.Path.Value}
190 case gw.PathMatchRegularExpression:
191 result.PathSpecifier = &v3route.RouteMatch_SafeRegex{SafeRegex: regexMatcher(match.Path.Value)}
192 case "":
193
194 result.PathSpecifier = &v3route.RouteMatch_Prefix{}
195 default:
196 return nil, errors.Errorf("unknown path match type: %q", match.Path.Type)
197 }
198
199 return result, nil
200 }
201
202 func Compile_HTTPHeaderMatch(headerMatch *gw.HTTPHeaderMatch) ([]*v3route.HeaderMatcher, error) {
203 if headerMatch == nil {
204 return nil, nil
205 }
206
207 var result []*v3route.HeaderMatcher
208 for hdr, pattern := range headerMatch.Values {
209 hm := &v3route.HeaderMatcher{
210 Name: hdr,
211 InvertMatch: false,
212 }
213
214 switch headerMatch.Type {
215 case gw.HeaderMatchExact:
216 hm.HeaderMatchSpecifier = &v3route.HeaderMatcher_ExactMatch{ExactMatch: pattern}
217 case gw.HeaderMatchRegularExpression:
218 hm.HeaderMatchSpecifier = &v3route.HeaderMatcher_SafeRegexMatch{SafeRegexMatch: regexMatcher(pattern)}
219 default:
220 return nil, errors.Errorf("unknown header match type: %s", headerMatch.Type)
221 }
222
223 result = append(result, hm)
224 }
225 return result, nil
226 }
227
228 func regexMatcher(pattern string) *v3matcher.RegexMatcher {
229 return &v3matcher.RegexMatcher{
230 EngineType: &v3matcher.RegexMatcher_GoogleRe2{GoogleRe2: &v3matcher.RegexMatcher_GoogleRE2{}},
231 Regex: pattern,
232 }
233 }
234
235 func getName(resource kates.Object) string {
236 return fmt.Sprintf("%s-%s", resource.GetNamespace(), resource.GetName())
237 }
238
View as plain text