forked from zalando/skipper
-
Notifications
You must be signed in to change notification settings - Fork 0
/
skipper.go
819 lines (667 loc) · 26.1 KB
/
skipper.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
package skipper
import (
"crypto/tls"
"fmt"
"io"
"net"
"net/http"
"os"
"path"
"strings"
"time"
log "github.com/sirupsen/logrus"
"github.com/zalando/skipper/circuit"
"github.com/zalando/skipper/dataclients/kubernetes"
"github.com/zalando/skipper/dataclients/routestring"
"github.com/zalando/skipper/eskipfile"
"github.com/zalando/skipper/etcd"
"github.com/zalando/skipper/filters"
"github.com/zalando/skipper/filters/auth"
"github.com/zalando/skipper/filters/builtin"
logfilter "github.com/zalando/skipper/filters/log"
"github.com/zalando/skipper/innkeeper"
"github.com/zalando/skipper/loadbalancer"
"github.com/zalando/skipper/logging"
"github.com/zalando/skipper/metrics"
"github.com/zalando/skipper/predicates/cookie"
"github.com/zalando/skipper/predicates/interval"
"github.com/zalando/skipper/predicates/query"
"github.com/zalando/skipper/predicates/source"
"github.com/zalando/skipper/predicates/traffic"
"github.com/zalando/skipper/proxy"
"github.com/zalando/skipper/ratelimit"
"github.com/zalando/skipper/routing"
"github.com/zalando/skipper/tracing"
)
const (
defaultSourcePollTimeout = 30 * time.Millisecond
defaultRoutingUpdateBuffer = 1 << 5
)
const DefaultPluginDir = "./plugins"
// Options to start skipper.
type Options struct {
// WhitelistedHealthcheckCIDR appends the whitelisted IP Range to the inernalIPS range for healthcheck purposes
WhitelistedHealthCheckCIDR []string
// Network address that skipper should listen on.
Address string
// List of custom filter specifications.
CustomFilters []filters.Spec
// Urls of nodes in an etcd cluster, storing route definitions.
EtcdUrls []string
// Path prefix for skipper related data in the etcd storage.
EtcdPrefix string
// Timeout used for a single request when querying for updates
// in etcd. This is independent of, and an addition to,
// SourcePollTimeout. When not set, the internally defined 1s
// is used.
EtcdWaitTimeout time.Duration
// Skip TLS certificate check for etcd connections.
EtcdInsecure bool
// If set enables skipper to generate based on ingress resources in kubernetes cluster
Kubernetes bool
// If set makes skipper authenticate with the kubernetes API server with service account assigned to the
// skipper POD.
// If omitted skipper will rely on kubectl proxy to authenticate with API server
KubernetesInCluster bool
// Kubernetes API base URL. Only makes sense if KubernetesInCluster is set to false. If omitted and
// skipper is not running in-cluster, the default API URL will be used.
KubernetesURL string
// KubernetesHealthcheck, when Kubernetes ingress is set, indicates
// whether an automatic healthcheck route should be generated. The
// generated route will report healthyness when the Kubernetes API
// calls are successful. The healthcheck endpoint is accessible from
// internal IPs, with the path /kube-system/healthz.
KubernetesHealthcheck bool
// KubernetesHTTPSRedirect, when Kubernetes ingress is set, indicates
// whether an automatic redirect route should be generated to redirect
// HTTP requests to their HTTPS equivalent. The generated route will
// match requests with the X-Forwarded-Proto and X-Forwarded-Port,
// expected to be set by the load-balancer.
KubernetesHTTPSRedirect bool
// KubernetesIngressClass is a regular expression, that will make
// skipper load only the ingress resources that that have a matching
// kubernetes.io/ingress.class annotation. For backwards compatibility,
// the ingresses without an annotation, or an empty annotation, will
// be loaded, too.
KubernetesIngressClass string
// *DEPRECATED* API endpoint of the Innkeeper service, storing route definitions.
InnkeeperUrl string
// *DEPRECATED* Fixed token for innkeeper authentication. (Used mainly in
// development environments.)
InnkeeperAuthToken string
// *DEPRECATED* Filters to be prepended to each route loaded from Innkeeper.
InnkeeperPreRouteFilters string
// *DEPRECATED* Filters to be appended to each route loaded from Innkeeper.
InnkeeperPostRouteFilters string
// *DEPRECATED* Skip TLS certificate check for Innkeeper connections.
InnkeeperInsecure bool
// *DEPRECATED* OAuth2 URL for Innkeeper authentication.
OAuthUrl string
// *DEPRECATED* Directory where oauth credentials are stored, with file names:
// client.json and user.json.
OAuthCredentialsDir string
// *DEPRECATED* The whitespace separated list of OAuth2 scopes.
OAuthScope string
// File containing static route definitions.
RoutesFile string
// File containing route definitions with file watch enabled. (For the skipper
// command this option is used when starting it with the -routes-file flag.)
WatchRoutesFile string
// InlineRoutes can define routes as eskip text.
InlineRoutes string
// Polling timeout of the routing data sources.
SourcePollTimeout time.Duration
// Deprecated. See ProxyFlags. When used together with ProxyFlags,
// the values will be combined with |.
ProxyOptions proxy.Options
// Flags controlling the proxy behavior.
ProxyFlags proxy.Flags
// Tells the proxy maximum how many idle connections can it keep
// alive.
IdleConnectionsPerHost int
// Defines the time period of how often the idle connections maintained
// by the proxy are closed.
CloseIdleConnsPeriod time.Duration
// Defines ReadTimeoutServer for server http connections.
ReadTimeoutServer time.Duration
// Defines ReadHeaderTimeout for server http connections.
ReadHeaderTimeoutServer time.Duration
// Defines WriteTimeout for server http connections.
WriteTimeoutServer time.Duration
// Defines IdleTimeout for server http connections.
IdleTimeoutServer time.Duration
// Defines MaxHeaderBytes for server http connections.
MaxHeaderBytes int
// Enable connection state metrics for server http connections.
EnableConnMetricsServer bool
// TimeoutBackend sets the TCP client connection timeout for
// proxy http connections to the backend.
TimeoutBackend time.Duration
// KeepAliveBackend sets the TCP keepalive for proxy http
// connections to the backend.
KeepAliveBackend time.Duration
// DualStackBackend sets if the proxy TCP connections to the
// backend should be dual stack.
DualStackBackend bool
// TLSHandshakeTimeoutBackend sets the TLS handshake timeout
// for proxy connections to the backend.
TLSHandshakeTimeoutBackend time.Duration
// MaxIdleConnsBackend sets MaxIdleConns, which limits the
// number of idle connections to all backends, 0 means no
// limit.
MaxIdleConnsBackend int
// Flag indicating to ignore trailing slashes in paths during route
// lookup.
IgnoreTrailingSlash bool
// Priority routes that are matched against the requests before
// the standard routes from the data clients.
PriorityRoutes []proxy.PriorityRoute
// Specifications of custom, user defined predicates.
CustomPredicates []routing.PredicateSpec
// Custom data clients to be used together with the default etcd and Innkeeper.
CustomDataClients []routing.DataClient
// SuppressRouteUpdateLogs indicates to log only summaries of the routing updates
// instead of full details of the updated/deleted routes.
SuppressRouteUpdateLogs bool
// Dev mode. Currently this flag disables prioritization of the
// consumer side over the feeding side during the routing updates to
// populate the updated routes faster.
DevMode bool
// Network address for the support endpoints
SupportListener string
// Deprecated: Network address for the /metrics endpoint
MetricsListener string
// Skipper provides a set of metrics with different keys which are exposed via HTTP in JSON
// You can customize those key names with your own prefix
MetricsPrefix string
// EnableProfile exposes profiling information on /profile of the
// metrics listener.
EnableProfile bool
// Flag that enables reporting of the Go garbage collector statistics exported in debug.GCStats
EnableDebugGcMetrics bool
// Flag that enables reporting of the Go runtime statistics exported in runtime and specifically runtime.MemStats
EnableRuntimeMetrics bool
// If set, detailed response time metrics will be collected
// for each route, additionally grouped by status and method.
EnableServeRouteMetrics bool
// If set, detailed response time metrics will be collected
// for each host, additionally grouped by status and method.
EnableServeHostMetrics bool
// If set, detailed response time metrics will be collected
// for each backend host
EnableBackendHostMetrics bool
// EnableAllFiltersMetrics enables collecting combined filter
// metrics per each route. Without the DisableMetricsCompatibilityDefaults,
// it is enabled by default.
EnableAllFiltersMetrics bool
// EnableCombinedResponseMetrics enables collecting response time
// metrics combined for every route.
EnableCombinedResponseMetrics bool
// EnableRouteResponseMetrics enables collecting response time
// metrics per each route. Without the DisableMetricsCompatibilityDefaults,
// it is enabled by default.
EnableRouteResponseMetrics bool
// EnableRouteBackendErrorsCounters enables counters for backend
// errors per each route. Without the DisableMetricsCompatibilityDefaults,
// it is enabled by default.
EnableRouteBackendErrorsCounters bool
// EnableRouteStreamingErrorsCounters enables counters for streaming
// errors per each route. Without the DisableMetricsCompatibilityDefaults,
// it is enabled by default.
EnableRouteStreamingErrorsCounters bool
// EnableRouteBackendMetrics enables backend response time metrics
// per each route. Without the DisableMetricsCompatibilityDefaults, it is
// enabled by default.
EnableRouteBackendMetrics bool
// When set, makes the histograms use an exponentially decaying sample
// instead of the default uniform one.
MetricsUseExpDecaySample bool
// The following options, for backwards compatibility, are true
// by default: EnableAllFiltersMetrics, EnableRouteResponseMetrics,
// EnableRouteBackendErrorsCounters, EnableRouteStreamingErrorsCounters,
// EnableRouteBackendMetrics. With this compatibility flag, the default
// for these options can be set to false.
DisableMetricsCompatibilityDefaults bool
// Output file for the application log. Default value: /dev/stderr.
//
// When /dev/stderr or /dev/stdout is passed in, it will be resolved
// to os.Stderr or os.Stdout.
//
// Warning: passing an arbitrary file will try to open it append
// on start and use it, or fail on start, but the current
// implementation doesn't support any more proper handling
// of temporary failures or log-rolling.
ApplicationLogOutput string
// Application log prefix. Default value: "[APP]".
ApplicationLogPrefix string
// Output file for the access log. Default value: /dev/stderr.
//
// When /dev/stderr or /dev/stdout is passed in, it will be resolved
// to os.Stderr or os.Stdout.
//
// Warning: passing an arbitrary file will try to open for append
// it on start and use it, or fail on start, but the current
// implementation doesn't support any more proper handling
// of temporary failures or log-rolling.
AccessLogOutput string
// Disables the access log.
AccessLogDisabled bool
// Enables logs in JSON format
AccessLogJSONEnabled bool
DebugListener string
// Path of certificate(s) when using TLS, mutiple may be given comma separated
CertPathTLS string
// Path of key(s) when using TLS, multiple may be given comma separated. For
// multiple keys, the order must match the one given in CertPathTLS
KeyPathTLS string
// Flush interval for upgraded Proxy connections
BackendFlushInterval time.Duration
// Experimental feature to handle protocol Upgrades for Websockets, SPDY, etc.
ExperimentalUpgrade bool
// MaxLoopbacks defines the maximum number of loops that the proxy can execute when the routing table
// contains loop backends (<loopback>).
MaxLoopbacks int
// EnableBreakers enables the usage of the breakers in the route definitions without initializing any
// by default. It is a shortcut for setting the BreakerSettings to:
//
// []circuit.BreakerSettings{{Type: BreakerDisabled}}
//
EnableBreakers bool
// BreakerSettings contain global and host specific settings for the circuit breakers.
BreakerSettings []circuit.BreakerSettings
// EnableRatelimiters enables the usage of the ratelimiter in the route definitions without initializing any
// by default. It is a shortcut for setting the RatelimitSettings to:
//
// []ratelimit.Settings{{Type: DisableRatelimit}}
//
EnableRatelimiters bool
// RatelimitSettings contain global and host specific settings for the ratelimiters.
RatelimitSettings []ratelimit.Settings
// OpenTracing enables opentracing
OpenTracing []string
// OpenTracingInitialSpan can override the default initial, pre-routing, span name.
// Default: "ingress".
OpenTracingInitialSpan string
// PluginDir defines the directory to load plugins from, DEPRECATED, use PluginDirs
PluginDir string
// PluginDirs defines the directories to load plugins from
PluginDirs []string
// FilterPlugins loads additional filters from modules. The first value in each []string
// needs to be the plugin name (as on disk, without path, without ".so" suffix). The
// following values are passed as arguments to the plugin while loading, see also
// https://zalando.github.io/skipper/plugins/
FilterPlugins [][]string
// PredicatePlugins loads additional predicates from modules. See above for FilterPlugins
// what the []string should contain.
PredicatePlugins [][]string
// DataClientPlugins loads additional data clients from modules. See above for FilterPlugins
// what the []string should contain.
DataClientPlugins [][]string
// Plugins combine multiple types of the above plugin types in one plugin (where
// necessary because of shared data between e.g. a filter and a data client).
Plugins [][]string
// DefaultHTTPStatus is the HTTP status used when no routes are found
// for a request.
DefaultHTTPStatus int
// EnablePrometheusMetrics enables Prometheus format metrics.
//
// This option is *deprecated*. The recommended way to enable prometheus metrics is to
// use the MetricsFlavours option.
EnablePrometheusMetrics bool
// MetricsFlavours sets the metrics storage and exposed format
// of metrics endpoints.
MetricsFlavours []string
// LoadBalancerHealthCheckInterval enables and sets the
// interval when to schedule health checks for dead or
// unhealthy routes
LoadBalancerHealthCheckInterval time.Duration
// ReverseSourcePredicate enables the automatic use of IP
// whitelisting in different places to use the reversed way of
// identifying a client IP within the X-Forwarded-For
// header. Amazon's ALB for example writes the client IP to
// the last item of the string list of the X-Forwarded-For
// header, in this case you want to set this to true.
ReverseSourcePredicate bool
// OAuthTokeninfoURL sets the OAuthTokeninfoURL similar to https://godoc.org/golang.org/x/oauth2#Endpoint
OAuthTokeninfoURL string
// MaxAuditBody sets the maximum read size of the body read by the audit log filter
MaxAuditBody int
}
func createDataClients(o Options, auth innkeeper.Authentication) ([]routing.DataClient, error) {
var clients []routing.DataClient
if o.RoutesFile != "" {
f, err := eskipfile.Open(o.RoutesFile)
if err != nil {
log.Error("error while opening eskip file", err)
return nil, err
}
clients = append(clients, f)
}
if o.WatchRoutesFile != "" {
f := eskipfile.Watch(o.WatchRoutesFile)
clients = append(clients, f)
}
if o.InlineRoutes != "" {
ir, err := routestring.New(o.InlineRoutes)
if err != nil {
log.Error("error while parsing inline routes", err)
return nil, err
}
clients = append(clients, ir)
}
if o.InnkeeperUrl != "" {
ic, err := innkeeper.New(innkeeper.Options{
Address: o.InnkeeperUrl,
Insecure: o.InnkeeperInsecure,
Authentication: auth,
PreRouteFilters: o.InnkeeperPreRouteFilters,
PostRouteFilters: o.InnkeeperPostRouteFilters,
})
if err != nil {
log.Error("error while initializing Innkeeper client", err)
return nil, err
}
clients = append(clients, ic)
}
if len(o.EtcdUrls) > 0 {
etcdClient, err := etcd.New(etcd.Options{
Endpoints: o.EtcdUrls,
Prefix: o.EtcdPrefix,
Timeout: o.EtcdWaitTimeout,
Insecure: o.EtcdInsecure,
})
if err != nil {
return nil, err
}
clients = append(clients, etcdClient)
}
if o.Kubernetes {
kubernetesClient, err := kubernetes.New(kubernetes.Options{
KubernetesInCluster: o.KubernetesInCluster,
KubernetesURL: o.KubernetesURL,
ProvideHealthcheck: o.KubernetesHealthcheck,
ProvideHTTPSRedirect: o.KubernetesHTTPSRedirect,
IngressClass: o.KubernetesIngressClass,
ReverseSourcePredicate: o.ReverseSourcePredicate,
WhitelistedHealthCheckCIDR: o.WhitelistedHealthCheckCIDR,
})
if err != nil {
return nil, err
}
clients = append(clients, kubernetesClient)
}
return clients, nil
}
func getLogOutput(name string) (io.Writer, error) {
name = path.Clean(name)
if name == "/dev/stdout" {
return os.Stdout, nil
}
if name == "/dev/stderr" {
return os.Stderr, nil
}
return os.OpenFile(name, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
}
func initLog(o Options) error {
var (
logOutput io.Writer
accessLogOutput io.Writer
err error
)
if o.ApplicationLogOutput != "" {
logOutput, err = getLogOutput(o.ApplicationLogOutput)
if err != nil {
return err
}
}
if !o.AccessLogDisabled && o.AccessLogOutput != "" {
accessLogOutput, err = getLogOutput(o.AccessLogOutput)
if err != nil {
return err
}
}
logging.Init(logging.Options{
ApplicationLogPrefix: o.ApplicationLogPrefix,
ApplicationLogOutput: logOutput,
AccessLogOutput: accessLogOutput,
AccessLogDisabled: o.AccessLogDisabled,
AccessLogJSONEnabled: o.AccessLogJSONEnabled})
return nil
}
func (o *Options) isHTTPS() bool {
return o.CertPathTLS != "" && o.KeyPathTLS != ""
}
func listenAndServe(proxy http.Handler, o *Options) error {
// create the access log handler
loggingHandler := logging.NewHandler(proxy)
log.Infof("proxy listener on %v", o.Address)
srv := &http.Server{
Addr: o.Address,
Handler: loggingHandler,
ReadTimeout: o.ReadTimeoutServer,
ReadHeaderTimeout: o.ReadHeaderTimeoutServer,
WriteTimeout: o.WriteTimeoutServer,
IdleTimeout: o.IdleTimeoutServer,
MaxHeaderBytes: o.MaxHeaderBytes,
}
if o.EnableConnMetricsServer {
m := metrics.Default
srv.ConnState = func(conn net.Conn, state http.ConnState) {
m.IncCounter(fmt.Sprintf("lb-conn-%s", state))
}
}
if o.isHTTPS() {
if strings.Index(o.CertPathTLS, ",") > 0 && strings.Index(o.KeyPathTLS, ",") > 0 {
tlsCfg := &tls.Config{}
crts := strings.Split(o.CertPathTLS, ",")
keys := strings.Split(o.KeyPathTLS, ",")
if len(crts) != len(keys) {
log.Fatalf("number of certs does not match number of keys")
}
for i, crt := range crts {
kp, err := tls.LoadX509KeyPair(crt, keys[i])
if err != nil {
log.Fatalf("Failed to load X509 keypair from %s/%s: %v", crt, keys[i], err)
}
tlsCfg.Certificates = append(tlsCfg.Certificates, kp)
}
tlsCfg.BuildNameToCertificate()
o.CertPathTLS = ""
o.KeyPathTLS = ""
srv.TLSConfig = tlsCfg
}
return srv.ListenAndServeTLS(o.CertPathTLS, o.KeyPathTLS)
}
log.Infof("certPathTLS or keyPathTLS not found, defaulting to HTTP")
return srv.ListenAndServe()
}
// Run skipper.
func Run(o Options) error {
// init log
err := initLog(o)
if err != nil {
return err
}
// *DEPRECATED* create authentication for Innkeeper
inkeeperAuth := innkeeper.CreateInnkeeperAuthentication(innkeeper.AuthOptions{
InnkeeperAuthToken: o.InnkeeperAuthToken,
OAuthCredentialsDir: o.OAuthCredentialsDir,
OAuthUrl: o.OAuthUrl,
OAuthScope: o.OAuthScope})
var lbInstance *loadbalancer.LB
if o.LoadBalancerHealthCheckInterval != 0 {
lbInstance = loadbalancer.New(o.LoadBalancerHealthCheckInterval)
}
if err := o.findAndLoadPlugins(); err != nil {
return err
}
// *DEPRECATED* inkeeper - create data clients
dataClients, err := createDataClients(o, inkeeperAuth)
if err != nil {
return err
}
// append custom data clients
dataClients = append(dataClients, o.CustomDataClients...)
if len(dataClients) == 0 {
log.Warning("no route source specified")
}
if o.OAuthTokeninfoURL != "" {
o.CustomFilters = append(o.CustomFilters, auth.NewOAuthTokeninfoAllScope(o.OAuthTokeninfoURL))
o.CustomFilters = append(o.CustomFilters, auth.NewOAuthTokeninfoAnyScope(o.OAuthTokeninfoURL))
o.CustomFilters = append(o.CustomFilters, auth.NewOAuthTokeninfoAllKV(o.OAuthTokeninfoURL))
o.CustomFilters = append(o.CustomFilters, auth.NewOAuthTokeninfoAnyKV(o.OAuthTokeninfoURL))
}
o.CustomFilters = append(o.CustomFilters, logfilter.NewAuditLog(o.MaxAuditBody))
// create a filter registry with the available filter specs registered,
// and register the custom filters
registry := builtin.MakeRegistry()
for _, f := range o.CustomFilters {
registry.Register(f)
}
// create routing
// create the proxy instance
var mo routing.MatchingOptions
if o.IgnoreTrailingSlash {
mo = routing.IgnoreTrailingSlash
}
// ensure a non-zero poll timeout
if o.SourcePollTimeout <= 0 {
o.SourcePollTimeout = defaultSourcePollTimeout
}
// check for dev mode, and set update buffer of the routes
updateBuffer := defaultRoutingUpdateBuffer
if o.DevMode {
updateBuffer = 0
}
// include bundled custom predicates
o.CustomPredicates = append(o.CustomPredicates,
source.New(),
source.NewFromLast(),
interval.NewBetween(),
interval.NewBefore(),
interval.NewAfter(),
cookie.New(),
query.New(),
traffic.New(),
loadbalancer.NewGroup(),
loadbalancer.NewMember(),
)
// create a routing engine
routing := routing.New(routing.Options{
FilterRegistry: registry,
MatchingOptions: mo,
PollTimeout: o.SourcePollTimeout,
DataClients: dataClients,
Predicates: o.CustomPredicates,
UpdateBuffer: updateBuffer,
SuppressLogs: o.SuppressRouteUpdateLogs,
PostProcessors: []routing.PostProcessor{loadbalancer.HealthcheckPostProcessor{LB: lbInstance}},
})
defer routing.Close()
proxyFlags := proxy.Flags(o.ProxyOptions) | o.ProxyFlags
proxyParams := proxy.Params{
Routing: routing,
Flags: proxyFlags,
PriorityRoutes: o.PriorityRoutes,
IdleConnectionsPerHost: o.IdleConnectionsPerHost,
CloseIdleConnsPeriod: o.CloseIdleConnsPeriod,
FlushInterval: o.BackendFlushInterval,
ExperimentalUpgrade: o.ExperimentalUpgrade,
MaxLoopbacks: o.MaxLoopbacks,
DefaultHTTPStatus: o.DefaultHTTPStatus,
LoadBalancer: lbInstance,
Timeout: o.TimeoutBackend,
KeepAlive: o.KeepAliveBackend,
DualStack: o.DualStackBackend,
TLSHandshakeTimeout: o.TLSHandshakeTimeoutBackend,
MaxIdleConns: o.MaxIdleConnsBackend,
}
if o.EnableBreakers || len(o.BreakerSettings) > 0 {
proxyParams.CircuitBreakers = circuit.NewRegistry(o.BreakerSettings...)
}
if o.EnableRatelimiters || len(o.RatelimitSettings) > 0 {
log.Infof("enabled ratelimiters %v: %v", o.EnableRatelimiters, o.RatelimitSettings)
proxyParams.RateLimiters = ratelimit.NewRegistry(o.RatelimitSettings...)
}
if o.DebugListener != "" {
do := proxyParams
do.Flags |= proxy.Debug
dbg := proxy.WithParams(do)
log.Infof("debug listener on %v", o.DebugListener)
go func() { http.ListenAndServe(o.DebugListener, dbg) }()
}
// init support endpoints
supportListener := o.SupportListener
// Backward compatibility
if supportListener == "" {
supportListener = o.MetricsListener
}
if supportListener != "" {
mux := http.NewServeMux()
mux.Handle("/routes", routing)
mux.Handle("/routes/", routing)
if o.EnablePrometheusMetrics {
o.MetricsFlavours = append(o.MetricsFlavours, "prometheus")
}
metricsKind := metrics.UnkownKind
for _, s := range o.MetricsFlavours {
switch s {
case "codahale":
metricsKind |= metrics.CodaHaleKind
case "prometheus":
metricsKind |= metrics.PrometheusKind
}
}
// set default if unset
if metricsKind == metrics.UnkownKind {
metricsKind = metrics.CodaHaleKind
}
log.Infof("Expose metrics in %s format", metricsKind)
metricsHandler := metrics.NewDefaultHandler(metrics.Options{
Format: metricsKind,
Prefix: o.MetricsPrefix,
EnableDebugGcMetrics: o.EnableDebugGcMetrics,
EnableRuntimeMetrics: o.EnableRuntimeMetrics,
EnableServeRouteMetrics: o.EnableServeRouteMetrics,
EnableServeHostMetrics: o.EnableServeHostMetrics,
EnableBackendHostMetrics: o.EnableBackendHostMetrics,
EnableProfile: o.EnableProfile,
EnableAllFiltersMetrics: o.EnableAllFiltersMetrics,
EnableCombinedResponseMetrics: o.EnableCombinedResponseMetrics,
EnableRouteResponseMetrics: o.EnableRouteResponseMetrics,
EnableRouteBackendErrorsCounters: o.EnableRouteBackendErrorsCounters,
EnableRouteStreamingErrorsCounters: o.EnableRouteStreamingErrorsCounters,
EnableRouteBackendMetrics: o.EnableRouteBackendMetrics,
UseExpDecaySample: o.MetricsUseExpDecaySample,
DisableCompatibilityDefaults: o.DisableMetricsCompatibilityDefaults,
})
mux.Handle("/metrics", metricsHandler)
mux.Handle("/metrics/", metricsHandler)
mux.Handle("/debug/pprof", metricsHandler)
mux.Handle("/debug/pprof/", metricsHandler)
log.Infof("support listener on %s", supportListener)
go http.ListenAndServe(supportListener, mux)
} else {
log.Infoln("Metrics are disabled")
}
o.PluginDirs = append(o.PluginDirs, o.PluginDir)
if len(o.OpenTracing) > 0 {
tracer, err := tracing.LoadTracingPlugin(o.PluginDirs, o.OpenTracing)
if err != nil {
return err
}
proxyParams.OpenTracer = tracer
proxyParams.OpenTracingInitialSpan = o.OpenTracingInitialSpan
} else {
// always have a tracer available, so filter authors can rely on the
// existence of a tracer
proxyParams.OpenTracer, _ = tracing.LoadTracingPlugin(o.PluginDirs, []string{"noop"})
}
if proxyParams.OpenTracingInitialSpan != "" {
proxyParams.OpenTracingInitialSpan = o.OpenTracingInitialSpan
}
// create the proxy
proxy := proxy.WithParams(proxyParams)
defer proxy.Close()
return listenAndServe(proxy, &o)
}