-
Notifications
You must be signed in to change notification settings - Fork 672
/
dag.go
837 lines (676 loc) · 24.9 KB
/
dag.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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
// Copyright Project Contour Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package dag provides a data model, in the form of a directed acyclic graph,
// of the relationship between Kubernetes Ingress, Service, and Secret objects.
package dag
import (
"errors"
"fmt"
"regexp"
"strconv"
"strings"
"time"
"github.com/projectcontour/contour/internal/status"
"github.com/projectcontour/contour/internal/timeout"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
)
// Observer is an interface for receiving notification of DAG updates.
type Observer interface {
OnChange(*DAG)
}
// ObserverFunc is a function that implements the Observer interface
// by calling itself. It can be nil.
type ObserverFunc func(*DAG)
func (f ObserverFunc) OnChange(d *DAG) {
if f != nil {
f(d)
}
}
var _ Observer = ObserverFunc(nil)
// ComposeObservers returns a new Observer that calls each of its arguments in turn.
func ComposeObservers(observers ...Observer) Observer {
return ObserverFunc(func(d *DAG) {
for _, o := range observers {
o.OnChange(d)
}
})
}
type DAG struct {
// StatusCache holds a cache of status updates to send.
StatusCache status.Cache
Listeners []*Listener
VirtualHosts map[string]*VirtualHost
SecureVirtualHosts map[string]*SecureVirtualHost
ExtensionClusters []*ExtensionCluster
}
type MatchCondition interface {
fmt.Stringer
}
// PrefixMatchType represents different types of prefix matching alternatives.
type PrefixMatchType int
const (
// PrefixMatchString represents a prefix match that functions like a
// string prefix match, i.e. prefix /foo matches /foobar
PrefixMatchString PrefixMatchType = iota
// PrefixMatchSegment represents a prefix match that only matches full path
// segments, i.e. prefix /foo matches /foo/bar but not /foobar
PrefixMatchSegment
)
var prefixMatchTypeToName = map[PrefixMatchType]string{
PrefixMatchString: "string",
PrefixMatchSegment: "segment",
}
// PrefixMatchCondition matches the start of a URL.
type PrefixMatchCondition struct {
Prefix string
PrefixMatchType PrefixMatchType
}
func (ec *ExactMatchCondition) String() string {
return "exact: " + ec.Path
}
// ExactMatchCondition matches the entire path of a URL.
type ExactMatchCondition struct {
Path string
}
func (pc *PrefixMatchCondition) String() string {
str := "prefix: " + pc.Prefix
if typeStr, ok := prefixMatchTypeToName[pc.PrefixMatchType]; ok {
str += " type: " + typeStr
}
return str
}
// RegexMatchCondition matches the URL by regular expression.
type RegexMatchCondition struct {
Regex string
}
func (rc *RegexMatchCondition) String() string {
return "regex: " + rc.Regex
}
const (
// HeaderMatchTypeExact matches a header value exactly.
HeaderMatchTypeExact = "exact"
// HeaderMatchTypeContains matches a header value if it contains the
// provided value.
HeaderMatchTypeContains = "contains"
// HeaderMatchTypePresent matches a header if it is present in a request.
HeaderMatchTypePresent = "present"
// HeaderMatchTypeRegex matches a header if it matches the provided regular
// expression.
HeaderMatchTypeRegex = "regex"
)
// HeaderMatchCondition matches request headers by MatchType
type HeaderMatchCondition struct {
Name string
Value string
MatchType string
Invert bool
}
func (hc *HeaderMatchCondition) String() string {
details := strings.Join([]string{
"name=" + hc.Name,
"value=" + hc.Value,
"matchtype=", hc.MatchType,
"invert=", strconv.FormatBool(hc.Invert),
}, "&")
return "header: " + details
}
// DirectResponse allows for a specific HTTP status code
// to be the response to a route request vs routing to
// an envoy cluster.
type DirectResponse struct {
StatusCode uint32
}
// Redirect allows for a 301/302 redirect to be the response
// to a route request vs. routing to an envoy cluster.
type Redirect struct {
// Hostname is the host name to redirect to.
Hostname string
// Scheme is the scheme (http or https) to
// use in the redirect.
Scheme string
// PortNumber is the port to redirect to,
// if any.
PortNumber uint32
// StatusCode is the HTTP response code to
// use. Valid options are 301 or 302.
StatusCode int
}
// Route defines the properties of a route to a Cluster.
type Route struct {
// PathMatchCondition specifies a MatchCondition to match on the request path.
// Must not be nil.
PathMatchCondition MatchCondition
// HeaderMatchConditions specifies a set of additional Conditions to
// match on the request headers.
HeaderMatchConditions []HeaderMatchCondition
Clusters []*Cluster
// Should this route generate a 301 upgrade if accessed
// over HTTP?
HTTPSUpgrade bool
// AuthDisabled is set if authorization should be disabled
// for this route. If authorization is disabled, the AuthContext
// field has no effect.
AuthDisabled bool
// AuthContext sets the authorization context (if authorization is enabled).
AuthContext map[string]string
// Is this a websocket route?
// TODO(dfc) this should go on the service
Websocket bool
// TimeoutPolicy defines the timeout request/idle
TimeoutPolicy TimeoutPolicy
// RetryPolicy defines the retry / number / timeout options for a route
RetryPolicy *RetryPolicy
// Indicates that during forwarding, the matched prefix (or path) should be swapped with this value
PrefixRewrite string
// Mirror Policy defines the mirroring policy for this Route.
MirrorPolicy *MirrorPolicy
// RequestHeadersPolicy defines how headers are managed during forwarding
RequestHeadersPolicy *HeadersPolicy
// ResponseHeadersPolicy defines how headers are managed during forwarding
ResponseHeadersPolicy *HeadersPolicy
// CookieRewritePolicies is a list of policies that define how HTTP Set-Cookie
// headers should be rewritten for responses on this route.
CookieRewritePolicies []CookieRewritePolicy
// RateLimitPolicy defines if/how requests for the route are rate limited.
RateLimitPolicy *RateLimitPolicy
// RequestHashPolicies is a list of policies for configuring hashes on
// request attributes.
RequestHashPolicies []RequestHashPolicy
// DirectResponse allows for a specific HTTP status code
// to be the response to a route request vs routing to
// an envoy cluster.
DirectResponse *DirectResponse
// Redirect allows for a 301 Redirect to be the response
// to a route request vs. routing to an envoy cluster.
Redirect *Redirect
}
// HasPathPrefix returns whether this route has a PrefixPathCondition.
func (r *Route) HasPathPrefix() bool {
_, ok := r.PathMatchCondition.(*PrefixMatchCondition)
return ok
}
// HasPathRegex returns whether this route has a RegexPathCondition.
func (r *Route) HasPathRegex() bool {
_, ok := r.PathMatchCondition.(*RegexMatchCondition)
return ok
}
// TimeoutPolicy defines the timeout policy for a route.
type TimeoutPolicy struct {
// ResponseTimeout is the timeout applied to the response
// from the backend server.
ResponseTimeout timeout.Setting
// IdleTimeout is the timeout applied to idle connections.
IdleTimeout timeout.Setting
}
// RetryPolicy defines the retry / number / timeout options
type RetryPolicy struct {
// RetryOn specifies the conditions under which retry takes place.
// If empty, retries will not be performed.
RetryOn string
// RetriableStatusCodes specifies the HTTP status codes under which retry takes place.
RetriableStatusCodes []uint32
// NumRetries specifies the allowed number of retries.
// Ignored if RetryOn is blank, or defaults to 1 if RetryOn is set.
NumRetries uint32
// PerTryTimeout specifies the timeout per retry attempt.
// Ignored if RetryOn is blank.
PerTryTimeout timeout.Setting
}
// MirrorPolicy defines the mirroring policy for a route.
type MirrorPolicy struct {
Cluster *Cluster
}
// HeadersPolicy defines how headers are managed during forwarding
type HeadersPolicy struct {
// HostRewrite defines if a host should be rewritten on upstream requests
HostRewrite string
Add map[string]string
Set map[string]string
Remove []string
}
// CookieRewritePolicy defines how attributes of an HTTP Set-Cookie header
// can be rewritten.
type CookieRewritePolicy struct {
Name string
Path *string
Domain *string
// Using an uint since pointer to boolean gets dereferenced in golang
// text templates so we have no way of distinguishing if unset or set to false.
// 0 means unset, 1 means false, 2 means true
Secure uint
SameSite *string
}
// RateLimitPolicy holds rate limiting parameters.
type RateLimitPolicy struct {
Local *LocalRateLimitPolicy
Global *GlobalRateLimitPolicy
}
// LocalRateLimitPolicy holds local rate limiting parameters.
type LocalRateLimitPolicy struct {
MaxTokens uint32
TokensPerFill uint32
FillInterval time.Duration
ResponseStatusCode uint32
ResponseHeadersToAdd map[string]string
}
// HeaderHashOptions contains options for hashing a HTTP header.
type HeaderHashOptions struct {
// HeaderName is the name of the header to hash.
HeaderName string
}
// CookieHashOptions contains options for hashing a HTTP cookie.
type CookieHashOptions struct {
// CookieName is the name of the header to hash.
CookieName string
// TTL is how long the cookie should be valid for.
TTL time.Duration
// Path is the request path the cookie is valid for.
Path string
}
// RequestHashPolicy holds configuration for calculating hashes on
// an individual request attribute.
type RequestHashPolicy struct {
// Terminal determines if the request attribute is present, hash
// calculation should stop with this element.
Terminal bool
// HeaderHashOptions is set when a header hash is desired.
HeaderHashOptions *HeaderHashOptions
// CookieHashOptions is set when a cookie hash is desired.
CookieHashOptions *CookieHashOptions
// HashSourceIP is set to true when source ip hashing is desired.
HashSourceIP bool
}
// GlobalRateLimitPolicy holds global rate limiting parameters.
type GlobalRateLimitPolicy struct {
Descriptors []*RateLimitDescriptor
}
// RateLimitDescriptor is a list of rate limit descriptor entries.
type RateLimitDescriptor struct {
Entries []RateLimitDescriptorEntry
}
// RateLimitDescriptorEntry is an entry in a rate limit descriptor.
// Exactly one field should be non-nil.
type RateLimitDescriptorEntry struct {
GenericKey *GenericKeyDescriptorEntry
HeaderMatch *HeaderMatchDescriptorEntry
HeaderValueMatch *HeaderValueMatchDescriptorEntry
RemoteAddress *RemoteAddressDescriptorEntry
}
// GenericKeyDescriptorEntry configures a descriptor entry
// that has a static key & value.
type GenericKeyDescriptorEntry struct {
Key string
Value string
}
// HeaderMatchDescriptorEntry configures a descriptor entry
// that's populated only if the specified header is present
// on the request.
type HeaderMatchDescriptorEntry struct {
HeaderName string
Key string
}
type HeaderValueMatchDescriptorEntry struct {
Headers []HeaderMatchCondition
ExpectMatch bool
Value string
}
// RemoteAddressDescriptorEntry configures a descriptor entry
// that contains the remote address (i.e. client IP).
type RemoteAddressDescriptorEntry struct{}
// CORSPolicy allows setting the CORS policy
type CORSPolicy struct {
// Specifies whether the resource allows credentials.
AllowCredentials bool
// AllowOrigin specifies the origins that will be allowed to do CORS requests.
AllowOrigin []string
// AllowMethods specifies the content for the *access-control-allow-methods* header.
AllowMethods []string
// AllowHeaders specifies the content for the *access-control-allow-headers* header.
AllowHeaders []string
// ExposeHeaders Specifies the content for the *access-control-expose-headers* header.
ExposeHeaders []string
// MaxAge specifies the content for the *access-control-max-age* header.
MaxAge timeout.Setting
}
type HeaderValue struct {
// Name represents a key of a header
Key string
// Value represents the value of a header specified by a key
Value string
}
// PeerValidationContext defines how to validate the certificate on the upstream service.
type PeerValidationContext struct {
// CACertificate holds a reference to the Secret containing the CA to be used to
// verify the upstream connection.
CACertificate *Secret
// SubjectName holds an optional subject name which Envoy will check against the
// certificate presented by the upstream.
SubjectName string
// SkipClientCertValidation when set to true will ensure Envoy requests but
// does not verify peer certificates.
SkipClientCertValidation bool
}
// GetCACertificate returns the CA certificate from PeerValidationContext.
func (pvc *PeerValidationContext) GetCACertificate() []byte {
if pvc == nil || pvc.CACertificate == nil {
// No validation required.
return nil
}
return pvc.CACertificate.Object.Data[CACertificateKey]
}
// GetSubjectName returns the SubjectName from PeerValidationContext.
func (pvc *PeerValidationContext) GetSubjectName() string {
if pvc == nil {
// No validation required.
return ""
}
return pvc.SubjectName
}
// A VirtualHost represents a named L4/L7 service.
type VirtualHost struct {
// Name is the fully qualified domain name of a network host,
// as defined by RFC 3986.
Name string
// CORSPolicy is the cross-origin policy to apply to the VirtualHost.
CORSPolicy *CORSPolicy
// RateLimitPolicy defines if/how requests for the virtual host
// are rate limited.
RateLimitPolicy *RateLimitPolicy
Routes map[string]*Route
}
func (v *VirtualHost) addRoute(route *Route) {
if v.Routes == nil {
v.Routes = make(map[string]*Route)
}
v.Routes[conditionsToString(route)] = route
}
func conditionsToString(r *Route) string {
s := []string{r.PathMatchCondition.String()}
for _, cond := range r.HeaderMatchConditions {
s = append(s, cond.String())
}
return strings.Join(s, ",")
}
func (v *VirtualHost) Valid() bool {
// A VirtualHost is valid if it has at least one route.
return len(v.Routes) > 0
}
// A SecureVirtualHost represents a HTTP host protected by TLS.
type SecureVirtualHost struct {
VirtualHost
// TLS minimum protocol version. Defaults to envoy_tls_v3.TlsParameters_TLS_AUTO
MinTLSVersion string
// The cert and key for this host.
Secret *Secret
// FallbackCertificate
FallbackCertificate *Secret
// Service to TCP proxy all incoming connections.
*TCPProxy
// DownstreamValidation defines how to verify the client's certificate.
DownstreamValidation *PeerValidationContext
// AuthorizationService points to the extension that client
// requests are forwarded to for authorization. If nil, no
// authorization is enabled for this host.
AuthorizationService *ExtensionCluster
// AuthorizationResponseTimeout sets how long the proxy should wait
// for authorization server responses.
AuthorizationResponseTimeout timeout.Setting
// AuthorizationFailOpen sets whether authorization server
// failures should cause the client request to also fail. The
// only reason to set this to `true` is when you are migrating
// from internal to external authorization.
AuthorizationFailOpen bool
}
func (s *SecureVirtualHost) Valid() bool {
// A SecureVirtualHost is valid if either
// 1. it has a secret and at least one route.
// 2. it has a tcpproxy, because the tcpproxy backend may negotiate TLS itself.
return (s.Secret != nil && len(s.Routes) > 0) || s.TCPProxy != nil
}
// A Listener represents a TCP socket that accepts
// incoming connections.
type Listener struct {
// Name is the unique name of the listener.
Name string
// Address is the TCP address to listen on.
// If blank 0.0.0.0, or ::/0 for IPv6, is assumed.
Address string
// Port is the TCP port to listen on.
Port int
VirtualHosts []*VirtualHost
SecureVirtualHosts []*SecureVirtualHost
}
// TCPProxy represents a cluster of TCP endpoints.
type TCPProxy struct {
// Clusters is the, possibly weighted, set
// of upstream services to forward decrypted traffic.
Clusters []*Cluster
}
// Service represents a single Kubernetes' Service's Port.
type Service struct {
Weighted WeightedService
// Protocol is the layer 7 protocol of this service
// One of "", "h2", "h2c", or "tls".
Protocol string
// Circuit breaking limits
// Max connections is maximum number of connections
// that Envoy will make to the upstream cluster.
MaxConnections uint32
// MaxPendingRequests is maximum number of pending
// requests that Envoy will allow to the upstream cluster.
MaxPendingRequests uint32
// MaxRequests is the maximum number of parallel requests that
// Envoy will make to the upstream cluster.
MaxRequests uint32
// MaxRetries is the maximum number of parallel retries that
// Envoy will allow to the upstream cluster.
MaxRetries uint32
// ExternalName is an optional field referencing a dns entry for Service type "ExternalName"
ExternalName string
}
// Cluster holds the connection specific parameters that apply to
// traffic routed to an upstream service.
type Cluster struct {
// Upstream is the backend Kubernetes service traffic arriving
// at this Cluster will be forwarded too.
Upstream *Service
// The relative weight of this Cluster compared to its siblings.
Weight uint32
// The protocol to use to speak to this cluster.
Protocol string
// UpstreamValidation defines how to verify the backend service's certificate
UpstreamValidation *PeerValidationContext
// The load balancer strategy to use when picking a host in the cluster.
// See https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/cluster/v3/cluster.proto#enum-config-cluster-v3-cluster-lbpolicy
LoadBalancerPolicy string
// Cluster http health check policy
*HTTPHealthCheckPolicy
// Cluster tcp health check policy
*TCPHealthCheckPolicy
// RequestHeadersPolicy defines how headers are managed during forwarding
RequestHeadersPolicy *HeadersPolicy
// ResponseHeadersPolicy defines how headers are managed during forwarding
ResponseHeadersPolicy *HeadersPolicy
// CookieRewritePolicies is a list of policies that define how HTTP Set-Cookie
// headers should be rewritten for responses on this route.
CookieRewritePolicies []CookieRewritePolicy
// SNI is used when a route proxies an upstream using tls.
// SNI describes how the SNI is set on a Cluster and is configured via RequestHeadersPolicy.Host key.
// Policies set on service are used before policies set on a route. Otherwise the value of the externalService
// is used if the route is configured to proxy to an externalService type.
// If the value is not set, then SNI is not changed.
SNI string
// DNSLookupFamily defines how external names are looked up
// When configured as V4, the DNS resolver will only perform a lookup
// for addresses in the IPv4 family. If V6 is configured, the DNS resolver
// will only perform a lookup for addresses in the IPv6 family.
// If AUTO is configured, the DNS resolver will first perform a lookup
// for addresses in the IPv6 family and fallback to a lookup for addresses
// in the IPv4 family.
// Note: This only applies to externalName clusters.
DNSLookupFamily string
// ClientCertificate is the optional identifier of the TLS secret containing client certificate and
// private key to be used when establishing TLS connection to upstream cluster.
ClientCertificate *Secret
}
// WeightedService represents the load balancing weight of a
// particular v1.Weighted port.
type WeightedService struct {
// Weight is the integral load balancing weight.
Weight uint32
// ServiceName is the v1.Service name.
ServiceName string
// ServiceNamespace is the v1.Service namespace.
ServiceNamespace string
// ServicePort is the port to which we forward traffic.
ServicePort v1.ServicePort
}
// ServiceCluster capture the set of Kubernetes Services that will
// compose the endpoints for a Envoy cluster. Traffic is balanced
// across the Service slice based on the weight of the elements.
type ServiceCluster struct {
// ClusterName is a globally unique name for this ServiceCluster.
// It is eventually used as the Envoy ClusterLoadAssignment
// name, and must not be empty.
ClusterName string
// Services are the load balancing targets. This slice must not be empty.
Services []WeightedService
}
// DeepCopy performs a deep copy of ServiceClusters
// TODO(jpeach): apply deepcopy-gen to DAG objects.
func (s *ServiceCluster) DeepCopy() *ServiceCluster {
s2 := ServiceCluster{
ClusterName: s.ClusterName,
Services: make([]WeightedService, len(s.Services)),
}
for i, w := range s.Services {
s2.Services[i] = w
w.ServicePort.DeepCopyInto(&s2.Services[i].ServicePort)
}
return &s2
}
// Validate checks whether this ServiceCluster satisfies its semantic invariants.
func (s *ServiceCluster) Validate() error {
if s.ClusterName == "" {
return errors.New("missing .ClusterName field")
}
if len(s.Services) == 0 {
return errors.New("empty .Services field")
}
for i, w := range s.Services {
if w.ServiceName == "" {
return fmt.Errorf("empty .Services[%d].ServiceName field", i)
}
if w.ServiceNamespace == "" {
return fmt.Errorf("empty .Services[%d].ServiceNamespace field", i)
}
}
return nil
}
// AddService adds the given service with a default weight of 1.
func (s *ServiceCluster) AddService(name types.NamespacedName, port v1.ServicePort) {
s.AddWeightedService(1, name, port)
}
// AddWeightedService adds the given service with the given weight.
func (s *ServiceCluster) AddWeightedService(weight uint32, name types.NamespacedName, port v1.ServicePort) {
w := WeightedService{
Weight: weight,
ServiceName: name.Name,
ServiceNamespace: name.Namespace,
ServicePort: port,
}
s.Services = append(s.Services, w)
}
// Rebalance rewrites the weights for the service cluster so that
// if no weights are specifies, the traffic is evenly distributed.
// This matches the behavior of weighted routes. Note that this is
// a destructive operation.
func (s *ServiceCluster) Rebalance() {
var sum uint32
for _, w := range s.Services {
sum += w.Weight
}
if sum == 0 {
for i := range s.Services {
s.Services[i].Weight = 1
}
}
}
// Secret represents a K8s Secret for TLS usage as a DAG Vertex. A Secret is
// a leaf in the DAG.
type Secret struct {
Object *v1.Secret
}
func (s *Secret) Name() string { return s.Object.Name }
func (s *Secret) Namespace() string { return s.Object.Namespace }
// Data returns the contents of the backing secret's map.
func (s *Secret) Data() map[string][]byte {
return s.Object.Data
}
// Cert returns the secret's tls certificate
func (s *Secret) Cert() []byte {
return s.Object.Data[v1.TLSCertKey]
}
// PrivateKey returns the secret's tls private key
func (s *Secret) PrivateKey() []byte {
return s.Object.Data[v1.TLSPrivateKeyKey]
}
// HTTPHealthCheckPolicy http health check policy
type HTTPHealthCheckPolicy struct {
Path string
Host string
Interval time.Duration
Timeout time.Duration
UnhealthyThreshold uint32
HealthyThreshold uint32
}
// TCPHealthCheckPolicy tcp health check policy
type TCPHealthCheckPolicy struct {
Interval time.Duration
Timeout time.Duration
UnhealthyThreshold uint32
HealthyThreshold uint32
}
// ExtensionCluster generates an Envoy cluster (aka ClusterLoadAssignment)
// for an ExtensionService resource.
type ExtensionCluster struct {
// Name is the (globally unique) name of the corresponding Envoy cluster resource.
Name string
// Upstream is the cluster that receives network traffic.
Upstream ServiceCluster
// The protocol to use to speak to this cluster.
Protocol string
// UpstreamValidation defines how to verify the backend service's certificate
UpstreamValidation *PeerValidationContext
// The load balancer type to use when picking a host in the cluster.
// See https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/cluster/v3/cluster.proto#enum-config-cluster-v3-cluster-lbpolicy
LoadBalancerPolicy string
// TimeoutPolicy specifies how to handle timeouts to this extension.
TimeoutPolicy TimeoutPolicy
// SNI is used when a route proxies an upstream using TLS.
SNI string
// ClientCertificate is the optional identifier of the TLS secret containing client certificate and
// private key to be used when establishing TLS connection to upstream cluster.
ClientCertificate *Secret
}
func wildcardDomainHeaderMatch(fqdn string) HeaderMatchCondition {
return HeaderMatchCondition{
// Internally Envoy uses the HTTP/2 ":authority" header in
// place of the HTTP/1 "host" header.
// See: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#config-route-v3-headermatcher
Name: ":authority",
MatchType: HeaderMatchTypeRegex,
Value: singleDNSLabelWildcardRegex + regexp.QuoteMeta(fqdn[1:]),
}
}