diff --git a/circuit/doc.go b/circuit/doc.go index 70c37a4965..dbd1f466ae 100644 --- a/circuit/doc.go +++ b/circuit/doc.go @@ -9,7 +9,7 @@ The circuit breakers are always assigned to backend hosts, so that the outcome o affects the circuit breaker behavior of another host. Besides hosts, individual routes can have separate circuit breakers, too. -Breaker Type - Consecutive Failures +# Breaker Type - Consecutive Failures This breaker opens when the proxy couldn't connect to a backend or received a >=500 status code at least N times in a row. When open, the proxy returns 503 - Service Unavailable response during the breaker timeout. After this @@ -17,14 +17,14 @@ timeout, the breaker goes into half-open state, in which it expects that M numbe requests in the half-open state are accepted concurrently. If any of the requests during the half-open state fails, the breaker goes back to open state. If all succeed, it goes to closed state again. -Breaker Type - Failure Rate +# Breaker Type - Failure Rate The "rate breaker" works similar to the "consecutive breaker", but instead of considering N consecutive failures for going open, it maintains a sliding window of the last M events, both successes and failures, and opens only when the number of failures reaches N within the window. This way the sliding window is not time based and allows the same breaker characteristics for low and high rate traffic. -Usage +# Usage When imported as a package, the Registry can be used to hold the circuit breakers and their settings. On a higher level, the circuit breaker settings can be simply passed to skipper as part of the skipper.Options @@ -63,7 +63,7 @@ Setting global values happens the same way as setting host values, but leaving t route based values happens with filters in the route definitions. (https://godoc.org/github.com/zalando/skipper/filters/circuit) -Settings - Type +# Settings - Type It can be ConsecutiveFailures, FailureRate or Disabled, where the first two values select which breaker to use, while the Disabled value can override a global or host configuration disabling the circuit breaker for the @@ -71,46 +71,46 @@ specific host or route. Command line name: type. Possible command line values: consecutive, rate, disabled. -Settings - Host +# Settings - Host The Host field indicates to which backend host should the current set of settings be applied. Leaving it empty indicates global settings. Command line name: host. -Settings - Window +# Settings - Window The window value sets the size of the sliding counter window of the failure rate breaker. Command line name: window. Possible command line values: any positive integer. -Settings - Failures +# Settings - Failures The failures value sets the max failure count for both the "consecutive" and "rate" breakers. Command line name: failures. Possible command line values: any positive integer. -Settings - Timeout +# Settings - Timeout With the timeout we can set how long the breaker should stay open, before becoming half-open. Command line name: timeout. Possible command line values: any positive integer as milliseconds or a duration string, e.g. 15m30s. -Settings - Half-Open Requests +# Settings - Half-Open Requests Defines the number of requests expected to succeed while the circuit breaker is in the half-open state. Command line name: half-open-requests. Possible command line values: any positive integer. -Settings - Idle TTL +# Settings - Idle TTL Defines the idle timeout after which a circuit breaker gets recycled, if it hasn't been used. Command line name: idle-ttl. Possible command line values: any positive integer as milliseconds or a duration string, e.g. 15m30s. -Filters +# Filters The following circuit breaker filters are supported: consecutiveBreaker(), rateBreaker() and disableBreaker(). @@ -129,7 +129,7 @@ route that it appears in. disableBreaker() -Proxy Usage +# Proxy Usage The proxy, when circuit breakers are configured, uses them for backend connections. It checks the breaker for the current backend host if it's closed before making backend requests. It reports the outcome of the request to @@ -139,7 +139,7 @@ breaker is open, the proxy doesn't try to make backend requests, and returns a r X-Circuit-Open: true -Registry +# Registry The active circuit breakers are stored in a registry. They are created on-demand, for the requested settings. The registry synchronizes access to the shared circuit breakers. When the registry detects that a circuit diff --git a/circuit/registry.go b/circuit/registry.go index d403e31e54..eb388ab626 100644 --- a/circuit/registry.go +++ b/circuit/registry.go @@ -96,7 +96,7 @@ func (r *Registry) get(s BreakerSettings) *Breaker { // Get returns a circuit breaker for the provided settings. The BreakerSettings object is used here as a key, // but typically it is enough to just set its Host field: // -// r.Get(BreakerSettings{Host: backendHost}) +// r.Get(BreakerSettings{Host: backendHost}) // // The key will be filled up with the defaults and the matching circuit breaker will be returned if it exists, // or a new one will be created if not. diff --git a/cmd/eskip/doc.go b/cmd/eskip/doc.go index 356936313b..38f2ba9dc9 100644 --- a/cmd/eskip/doc.go +++ b/cmd/eskip/doc.go @@ -18,41 +18,41 @@ formatted routes from and to different data sources. For command line help, enter: - eskip -help + eskip -help -Examples +# Examples Check if an eskip file has valid syntax: - eskip check routes.eskip + eskip check routes.eskip Print routes stored in etcd: - eskip print -etcd-urls https://etcd.example.org + eskip print -etcd-urls https://etcd.example.org Print routes as JSON: - eskip print -json + eskip print -json Insert/update routes in etcd from an eskip file: - eskip upsert routes.eskip + eskip upsert routes.eskip Sync routes from an eskip file to etcd: - eskip reset routes.eskip + eskip reset routes.eskip Delete routes from etcd: - eskip delete -ids route1,route2,route3 + eskip delete -ids route1,route2,route3 Delete all routes from etcd: - eskip print | eskip delete + eskip print | eskip delete Copy all routes in etcd under a different prefix: - eskip print | eskip upsert -etcd-prefix /skipper-backup + eskip print | eskip upsert -etcd-prefix /skipper-backup (Where -etcd-urls is not set for write operations like upsert, reset and delete, the default etcd cluster urls are used: diff --git a/cmd/skipper/main.go b/cmd/skipper/main.go index ba30378dbd..b39d7db6a4 100644 --- a/cmd/skipper/main.go +++ b/cmd/skipper/main.go @@ -4,7 +4,7 @@ set of filters. For the list of command line options, run: - skipper -help + skipper -help For details about the usage and extensibility of skipper, please see the documentation of the root skipper package. diff --git a/dataclients/kubernetes/clusterclient.go b/dataclients/kubernetes/clusterclient.go index 532ee8d5f6..aed07eec6d 100644 --- a/dataclients/kubernetes/clusterclient.go +++ b/dataclients/kubernetes/clusterclient.go @@ -194,10 +194,11 @@ func newClusterClient(o Options, apiURL, ingCls, rgCls string, quit <-chan struc // serializes a given map of label selectors to a string that can be appended to a request URI to kubernetes // Examples (note that the resulting value in the query is URL escaped, for readability this is not done in examples): -// [] becomes `` -// ["label": ""] becomes `?labelSelector=label` -// ["label": "value"] becomes `?labelSelector=label=value` -// ["label": "value", "label2": "value2"] becomes `?labelSelector=label=value&label2=value2` +// +// [] becomes `` +// ["label": ""] becomes `?labelSelector=label` +// ["label": "value"] becomes `?labelSelector=label=value` +// ["label": "value", "label2": "value2"] becomes `?labelSelector=label=value&label2=value2` func toLabelSelectorQuery(selectors map[string]string) string { if len(selectors) == 0 { return "" diff --git a/dataclients/kubernetes/definitions/doc.go b/dataclients/kubernetes/definitions/doc.go index 96feb8a047..1eda8fd7f2 100644 --- a/dataclients/kubernetes/definitions/doc.go +++ b/dataclients/kubernetes/definitions/doc.go @@ -1,3 +1,3 @@ -/* Package definitions provides type definitions, parsing, marshaling and -validation for Kubernetes resources used by Skipper. */ +// Package definitions provides type definitions, parsing, marshaling and +// validation for Kubernetes resources used by Skipper. package definitions diff --git a/dataclients/kubernetes/doc.go b/dataclients/kubernetes/doc.go index f8b1b290ec..18e9b1a314 100644 --- a/dataclients/kubernetes/doc.go +++ b/dataclients/kubernetes/doc.go @@ -16,7 +16,7 @@ Both Kube-ingress-aws-controller and Skipper Kubernetes are part of the larger p https://github.com/zalando-incubator/kubernetes-on-aws/ -Ingress shutdown by healthcheck +# Ingress shutdown by healthcheck The Kubernetes ingress client catches TERM signals when the ProvideHealthcheck option is enabled, and reports failing healthcheck after the signal was received. This means that, when the Ingress client is responsible for @@ -24,149 +24,149 @@ the healthcheck of the cluster, and the Skipper process receives the TERM signal immediately, but will start reporting failures on healthcheck requests. Until it gets killed by the kubelet, Skipper keeps serving the requests in this case. -Example - Ingress +# Example - Ingress A basic ingress specification: - apiVersion: extensions/v1beta1 - kind: Ingress - Metadata: - name: app - spec: - rules: - - host: app-default.example.org - http: - paths: - - backend: - serviceName: app-svc - servicePort: 80 - -Example - Ingress with ratelimiting + apiVersion: extensions/v1beta1 + kind: Ingress + Metadata: + name: app + spec: + rules: + - host: app-default.example.org + http: + paths: + - backend: + serviceName: app-svc + servicePort: 80 + +# Example - Ingress with ratelimiting The example shows 50 calls per minute are allowed to each skipper instance for the given ingress. - apiVersion: extensions/v1beta1 - kind: Ingress - Metadata: - annotations: - zalando.org/ratelimit: ratelimit(50, "1m") - name: app - spec: - rules: - - host: app-default.example.org - http: - paths: - - backend: - serviceName: app-svc - servicePort: 80 - -Example - Ingress with client based ratelimiting + apiVersion: extensions/v1beta1 + kind: Ingress + Metadata: + annotations: + zalando.org/ratelimit: ratelimit(50, "1m") + name: app + spec: + rules: + - host: app-default.example.org + http: + paths: + - backend: + serviceName: app-svc + servicePort: 80 + +# Example - Ingress with client based ratelimiting The example shows 3 calls per minute per client, based on X-Forwarded-For header or IP incase there is no X-Forwarded-For header set, are allowed to each skipper instance for the given ingress. - apiVersion: extensions/v1beta1 - kind: Ingress - Metadata: - annotations: - zalando.org/ratelimit: localRatelimit(3, "1m") - name: app - spec: - rules: - - host: app-default.example.org - http: - paths: - - backend: - serviceName: app-svc - servicePort: 80 + apiVersion: extensions/v1beta1 + kind: Ingress + Metadata: + annotations: + zalando.org/ratelimit: localRatelimit(3, "1m") + name: app + spec: + rules: + - host: app-default.example.org + http: + paths: + - backend: + serviceName: app-svc + servicePort: 80 The example shows 500 calls per hour per client, based on Authorization header set, are allowed to each skipper instance for the given ingress. - apiVersion: extensions/v1beta1 - kind: Ingress - Metadata: - annotations: - zalando.org/ratelimit: localRatelimit(500, "1h", "auth") - name: app - spec: - rules: - - host: app-default.example.org - http: - paths: - - backend: - serviceName: app-svc - servicePort: 80 - -Example - Ingress with custom skipper filter configuration + apiVersion: extensions/v1beta1 + kind: Ingress + Metadata: + annotations: + zalando.org/ratelimit: localRatelimit(500, "1h", "auth") + name: app + spec: + rules: + - host: app-default.example.org + http: + paths: + - backend: + serviceName: app-svc + servicePort: 80 + +# Example - Ingress with custom skipper filter configuration The example shows the use of 2 filters from skipper for the implicitly defined route in ingress. - apiVersion: extensions/v1beta1 - kind: Ingress - Metadata: - annotations: - zalando.org/skipper-filter: localRatelimit(50, "10m") -> requestCookie("test-session", "abc") - name: app - spec: - rules: - - host: app-default.example.org - http: - paths: - - backend: - serviceName: app-svc - servicePort: 80 - -Example - Ingress with custom skipper Predicate configuration + apiVersion: extensions/v1beta1 + kind: Ingress + Metadata: + annotations: + zalando.org/skipper-filter: localRatelimit(50, "10m") -> requestCookie("test-session", "abc") + name: app + spec: + rules: + - host: app-default.example.org + http: + paths: + - backend: + serviceName: app-svc + servicePort: 80 + +# Example - Ingress with custom skipper Predicate configuration The example shows the use of a skipper predicates for the implicitly defined route in ingress. - apiVersion: extensions/v1beta1 - kind: Ingress - Metadata: - annotations: - zalando.org/skipper-predicate: QueryParam("query", "^example$") - name: app - spec: - rules: - - host: app-default.example.org - http: - paths: - - backend: - serviceName: app-svc - servicePort: 80 - -Example - Ingress with custom skipper Routes configuration + apiVersion: extensions/v1beta1 + kind: Ingress + Metadata: + annotations: + zalando.org/skipper-predicate: QueryParam("query", "^example$") + name: app + spec: + rules: + - host: app-default.example.org + http: + paths: + - backend: + serviceName: app-svc + servicePort: 80 + +# Example - Ingress with custom skipper Routes configuration The example shows the use of custom skipper routes which be additional to the routes generated for the ingress. - apiVersion: extensions/v1beta1 - kind: Ingress - Metadata: - annotations: - zalando.org/skipper-routes: | - Method("OPTIONS") -> - setResponseHeader("Access-Control-Allow-Origin", "*") -> - setResponseHeader("Access-Control-Allow-Methods", "GET, OPTIONS") -> - setResponseHeader("Access-Control-Allow-Headers", "Authorization") -> - status(200) -> - name: app - spec: - rules: - - host: app-default.example.org - http: - paths: - - backend: - serviceName: app-svc - servicePort: 80 - -Example - Ingress with shadow traffic + apiVersion: extensions/v1beta1 + kind: Ingress + Metadata: + annotations: + zalando.org/skipper-routes: | + Method("OPTIONS") -> + setResponseHeader("Access-Control-Allow-Origin", "*") -> + setResponseHeader("Access-Control-Allow-Methods", "GET, OPTIONS") -> + setResponseHeader("Access-Control-Allow-Headers", "Authorization") -> + status(200) -> + name: app + spec: + rules: + - host: app-default.example.org + http: + paths: + - backend: + serviceName: app-svc + servicePort: 80 + +# Example - Ingress with shadow traffic This will send production traffic to app-default.example.org and copies incoming requests to https://app.shadow.example.org, but drops @@ -174,20 +174,19 @@ responses from shadow URL. This is helpful to test your next generation software with production workload. See also https://godoc.org/github.com/zalando/skipper/filters/tee for details. - apiVersion: extensions/v1beta1 - kind: Ingress - Metadata: - annotations: - zalando.org/skipper-filter: tee("https://app.shadow.example.org") - name: app - spec: - rules: - - host: app-default.example.org - http: - paths: - - backend: - serviceName: app-svc - servicePort: 80 - + apiVersion: extensions/v1beta1 + kind: Ingress + Metadata: + annotations: + zalando.org/skipper-filter: tee("https://app.shadow.example.org") + name: app + spec: + rules: + - host: app-default.example.org + http: + paths: + - backend: + serviceName: app-svc + servicePort: 80 */ package kubernetes diff --git a/dataclients/kubernetes/ingressv1.go b/dataclients/kubernetes/ingressv1.go index 8befeb42f6..8826bba5b6 100644 --- a/dataclients/kubernetes/ingressv1.go +++ b/dataclients/kubernetes/ingressv1.go @@ -214,23 +214,23 @@ func (ing *ingress) addEndpointsRuleV1(ic ingressContext, host string, prule *de // rule backends. // The traffic is calculated based on the following rules: // -// * if no weight is defined for a backend it will get weight 0. -// * if no weights are specified for all backends of a path, then traffic will -// be distributed equally. +// - if no weight is defined for a backend it will get weight 0. +// - if no weights are specified for all backends of a path, then traffic will +// be distributed equally. // // Each traffic weight is relative to the number of backends per path. If there // are multiple backends per path the weight will be relative to the number of // remaining backends for the path e.g. if the weight is specified as // -// backend-1: 0.2 -// backend-2: 0.6 -// backend-3: 0.2 +// backend-1: 0.2 +// backend-2: 0.6 +// backend-3: 0.2 // // then the weight will be calculated to: // -// backend-1: 0.2 -// backend-2: 0.75 -// backend-3: 1.0 +// backend-1: 0.2 +// backend-2: 0.75 +// backend-3: 1.0 // // where for a weight of 1.0 no Traffic predicate will be generated. func computeBackendWeightsV1(backendWeights map[string]float64, rule *definitions.RuleV1) { diff --git a/dataclients/kubernetes/ingressv1beta1.go b/dataclients/kubernetes/ingressv1beta1.go index cb81148c75..e46ed95e33 100644 --- a/dataclients/kubernetes/ingressv1beta1.go +++ b/dataclients/kubernetes/ingressv1beta1.go @@ -213,23 +213,23 @@ func (ing *ingress) addEndpointsRule(ic ingressContext, host string, prule *defi // rule backends. // The traffic is calculated based on the following rules: // -// * if no weight is defined for a backend it will get weight 0. -// * if no weights are specified for all backends of a path, then traffic will -// be distributed equally. +// - if no weight is defined for a backend it will get weight 0. +// - if no weights are specified for all backends of a path, then traffic will +// be distributed equally. // // Each traffic weight is relative to the number of backends per path. If there // are multiple backends per path the weight will be relative to the number of // remaining backends for the path e.g. if the weight is specified as // -// backend-1: 0.2 -// backend-2: 0.6 -// backend-3: 0.2 +// backend-1: 0.2 +// backend-2: 0.6 +// backend-3: 0.2 // // then the weight will be calculated to: // -// backend-1: 0.2 -// backend-2: 0.75 -// backend-3: 1.0 +// backend-1: 0.2 +// backend-2: 0.75 +// backend-3: 1.0 // // where for a weight of 1.0 no Traffic predicate will be generated. func computeBackendWeights(backendWeights map[string]float64, rule *definitions.Rule) { diff --git a/dataclients/routestring/string.go b/dataclients/routestring/string.go index d68fc6b986..cb3dff6016 100644 --- a/dataclients/routestring/string.go +++ b/dataclients/routestring/string.go @@ -3,8 +3,7 @@ // // Usage from the command line: // -// skipper -inline-routes '* -> inlineContent("Hello, world!") -> ' -// +// skipper -inline-routes '* -> inlineContent("Hello, world!") -> ' package routestring import ( diff --git a/doc.go b/doc.go index 67c49ae9e1..da3573ad20 100644 --- a/doc.go +++ b/doc.go @@ -20,35 +20,33 @@ predicates or data sources. For further information read Skipper took the core design and inspiration from Vulcand: https://github.com/mailgun/vulcand. - -Quickstart +# Quickstart Skipper is 'go get' compatible. If needed, create a 'go workspace' first: - mkdir ws - cd ws - export GOPATH=$(pwd) - export PATH=$PATH:$GOPATH/bin + mkdir ws + cd ws + export GOPATH=$(pwd) + export PATH=$PATH:$GOPATH/bin Get the Skipper packages: - go get github.com/zalando/skipper/... + go get github.com/zalando/skipper/... Create a file with a route: - echo 'hello: Path("/hello") -> "https://www.example.org"' > example.eskip + echo 'hello: Path("/hello") -> "https://www.example.org"' > example.eskip Optionally, verify the syntax of the file: - eskip check example.eskip + eskip check example.eskip Start Skipper and make an HTTP request: - skipper -routes-file example.eskip & - curl localhost:9090/hello - + skipper -routes-file example.eskip & + curl localhost:9090/hello -Routing Mechanism +# Routing Mechanism The core of Skipper's request processing is implemented by a reverse proxy in the 'proxy' package. The proxy receives the incoming request, @@ -81,8 +79,7 @@ calculated decision. For further details, see the 'proxy' and 'filters' package documentation. - -Matching Requests +# Matching Requests Finding a request's route happens by matching the request attributes to the conditions in the route's definitions. Such definitions may have the @@ -108,8 +105,7 @@ meaning, that a request must fulfill each condition to match a route. For further details, see the 'routing' package documentation. - -Filters - Augmenting Requests +# Filters - Augmenting Requests Filters are applied in order of definition to the request and in reverse order to the response. They are used to modify request and response @@ -120,8 +116,7 @@ parameters, that are set specifically to the route. For further details, see the 'filters' package documentation. - -Service Backends +# Service Backends Each route has one of the following backends: HTTP endpoint, shunt, loopback or dynamic. @@ -143,8 +138,7 @@ serves as a form of an internal redirect. A dynamic route means that the final target will be defined in a filter. One of the filters in the chain must set the target backend url explicitly. - -Route Definitions +# Route Definitions Route definitions consist of the following: @@ -164,15 +158,13 @@ parser.) For further details, see the 'eskip' package documentation - -Authentication and Authorization +# Authentication and Authorization Skipper has filter implementations of basic auth and OAuth2. It can be integrated with tokeninfo based OAuth2 providers. For details, see: https://godoc.org/github.com/zalando/skipper/filters/auth. - -Data Sources +# Data Sources Skipper's route definitions of Skipper are loaded from one or more data sources. It can receive incremental updates from those data sources at @@ -200,8 +192,7 @@ updates. Skipper can use additional data sources, provided by extensions. Sources must implement the DataClient interface in the routing package. - -Circuit Breaker +# Circuit Breaker Skipper provides circuit breakers, configured either globally, based on backend hosts or based on individual routes. It supports two types of @@ -209,8 +200,7 @@ circuit breaker behavior: open on N consecutive failures, or open on N failures out of M requests. For details, see: https://godoc.org/github.com/zalando/skipper/circuit. - -Running Skipper +# Running Skipper Skipper can be started with the default executable command 'skipper', or as a library built into an application. The easiest way to start Skipper @@ -222,23 +212,21 @@ default executable as well, as a command line flag. E.g. EtcdUrls becomes -etcd-urls as a comma separated list. For command line help, enter: - skipper -help + skipper -help An additional utility, eskip, can be used to verify, print, update and delete routes from/to files or etcd (Innkeeper on the roadmap). See the cmd/eskip command package, and/or enter in the command line: - eskip -help - + eskip -help -Extending Skipper +# Extending Skipper Skipper doesn't use dynamically loaded plugins, however, it can be used as a library, and it can be extended with custom predicates, filters and/or custom data sources. - -Custom Predicates +# Custom Predicates To create a custom predicate, one needs to implement the PredicateSpec interface in the routing package. Instances of the PredicateSpec are @@ -247,45 +235,44 @@ objects as referenced in eskip routes, with concrete arguments. Example, randompredicate.go: - package main + package main - import ( - "github.com/zalando/skipper/routing" - "math/rand" - "net/http" - ) + import ( + "github.com/zalando/skipper/routing" + "math/rand" + "net/http" + ) - type randomSpec struct {} + type randomSpec struct {} - type randomPredicate struct { - chance float64 - } + type randomPredicate struct { + chance float64 + } - func (s *randomSpec) Name() string { return "Random" } + func (s *randomSpec) Name() string { return "Random" } - func (s *randomSpec) Create(args []interface{}) (routing.Predicate, error) { - p := &randomPredicate{.5} - if len(args) > 0 { - if c, ok := args[0].(float64); ok { - p.chance = c - } - } + func (s *randomSpec) Create(args []interface{}) (routing.Predicate, error) { + p := &randomPredicate{.5} + if len(args) > 0 { + if c, ok := args[0].(float64); ok { + p.chance = c + } + } - return p, nil - } + return p, nil + } - func (p *randomPredicate) Match(_ *http.Request) bool { - return rand.Float64() < p.chance - } + func (p *randomPredicate) Match(_ *http.Request) bool { + return rand.Float64() < p.chance + } In the above example, a custom predicate is created, that can be referenced in eskip definitions with the name 'Random': - Random(.33) -> "https://test.example.org"; - * -> "https://www.example.org" - + Random(.33) -> "https://test.example.org"; + * -> "https://www.example.org" -Custom Filters +# Custom Filters To create a custom filter we need to implement the Spec interface of the filters package. 'Spec' is the specification of a filter, and it is used @@ -294,48 +281,47 @@ processed. Example, hellofilter.go: - package main + package main - import ( - "fmt" - "github.com/zalando/skipper/filters" - ) + import ( + "fmt" + "github.com/zalando/skipper/filters" + ) - type helloSpec struct {} + type helloSpec struct {} - type helloFilter struct { - who string - } + type helloFilter struct { + who string + } - func (s *helloSpec) Name() string { return "hello" } + func (s *helloSpec) Name() string { return "hello" } - func (s *helloSpec) CreateFilter(config []interface{}) (filters.Filter, error) { - if len(config) == 0 { - return nil, filters.ErrInvalidFilterParameters - } + func (s *helloSpec) CreateFilter(config []interface{}) (filters.Filter, error) { + if len(config) == 0 { + return nil, filters.ErrInvalidFilterParameters + } - if who, ok := config[0].(string); ok { - return &helloFilter{who}, nil - } else { - return nil, filters.ErrInvalidFilterParameters - } - } + if who, ok := config[0].(string); ok { + return &helloFilter{who}, nil + } else { + return nil, filters.ErrInvalidFilterParameters + } + } - func (f *helloFilter) Request(ctx filters.FilterContext) {} + func (f *helloFilter) Request(ctx filters.FilterContext) {} - func (f *helloFilter) Response(ctx filters.FilterContext) { - ctx.Response().Header.Set("X-Hello", fmt.Sprintf("Hello, %s!", f.who)) - } + func (f *helloFilter) Response(ctx filters.FilterContext) { + ctx.Response().Header.Set("X-Hello", fmt.Sprintf("Hello, %s!", f.who)) + } The above example creates a filter specification, and in the routes where they are included, the filter instances will set the 'X-Hello' header for each and every response. The name of the filter is 'hello', and in a route definition it is referenced as: - * -> hello("world") -> "https://www.example.org" + r: * -> hello("world") -> "https://www.example.org"; - -Custom Build +# Custom Build The easiest way to create a custom Skipper variant is to implement the required filters (as in the example above) by importing the Skipper @@ -343,45 +329,43 @@ package, and starting it with the 'Run' command. Example, hello.go: - package main + package main - import ( - "log" + import ( + "log" - "github.com/zalando/skipper" - "github.com/zalando/skipper/filters" - "github.com/zalando/skipper/routing" - ) + "github.com/zalando/skipper" + "github.com/zalando/skipper/filters" + "github.com/zalando/skipper/routing" + ) - func main() { - log.Fatal(skipper.Run(skipper.Options{ - Address: ":9090", - RoutesFile: "routes.eskip", - CustomPredicates: []routing.PredicateSpec{&randomSpec{}}, - CustomFilters: []filters.Spec{&helloSpec{}}})) - } + func main() { + log.Fatal(skipper.Run(skipper.Options{ + Address: ":9090", + RoutesFile: "routes.eskip", + CustomPredicates: []routing.PredicateSpec{&randomSpec{}}, + CustomFilters: []filters.Spec{&helloSpec{}}})) + } A file containing the routes, routes.eskip: - random: - Random(.05) -> hello("fish?") -> "https://fish.example.org"; - hello: - * -> hello("world") -> "https://www.example.org" + random: + Random(.05) -> hello("fish?") -> "https://fish.example.org"; + hello: + * -> hello("world") -> "https://www.example.org" Start the custom router: - go run hello.go - + go run hello.go -Proxy Package Used Individually +# Proxy Package Used Individually The 'Run' function in the root Skipper package starts its own listener but it doesn't provide the best composability. The proxy package, however, provides a standard http.Handler, so it is possible to use it in a more complex solution as a building block for routing. - -Logging and Metrics +# Logging and Metrics Skipper provides detailed logging of failures, and access logs in Apache log format. Skipper also collects detailed performance metrics, and @@ -389,8 +373,7 @@ exposes them on a separate listener endpoint for pulling snapshots. For details, see the 'logging' and 'metrics' packages documentation. - -Performance Considerations +# Performance Considerations The router's performance depends on the environment and on the used filters. Under ideal circumstances, and without filters, the biggest @@ -401,7 +384,7 @@ lookup tree in a single structure. Benchmarks for the tree lookup can be run by: - go test github.com/zalando/skipper/routing -bench=Tree + go test github.com/zalando/skipper/routing -bench=Tree In case more aggressive scale is needed, it is possible to setup Skipper in a cascade model, with multiple Skipper instances for specific route diff --git a/eskip/doc.go b/eskip/doc.go index ffd0ece177..37a4b9082f 100644 --- a/eskip/doc.go +++ b/eskip/doc.go @@ -3,8 +3,7 @@ Package eskip implements an in-memory representation of Skipper routes and a DSL for describing Skipper route expressions, route definitions and complete routing tables. - -Grammar Summary +# Grammar Summary A routing table is built up from 0 or more route definitions. The definitions are separated by ';'. A route definition contains one route @@ -32,8 +31,7 @@ A route expression example: modPath("^/api", "") -> requestHeader("X-Type", "external") -> "https://api.example.org" - -Match Expressions - Predicates +# Match Expressions - Predicates A match expression contains one or more predicates. An incoming request must fulfill each of them to match the route. The predicates are @@ -99,8 +97,7 @@ Catch all predicate. Former, deprecated form of the catch all predicate. - -Custom Predicates +# Custom Predicates Eskip supports custom route matching predicates, that can be implemented in extensions. The notation of custom predicates is the same as of the @@ -114,8 +111,7 @@ responsibility of the implementation to validate them. (See the documentation of the routing package.) - -Filters +# Filters Filters are used to augment the incoming requests and the outgoing responses, or do other useful or fun stuff. Filters can have different @@ -176,15 +172,13 @@ extendable primarily by implementing custom filters, for details about how to create custom filters, please, refer to the documentation of the root skipper package. - -Naming conventions +# Naming conventions Note, that the naming of predicates and filters follows the following convention: both predicates and filters are written in camel case, and predicates start with upper case, while filters start with lower case. - -Backend +# Backend There are four backend types: network endpoint address, shunt, loopback and dynamic. @@ -223,8 +217,7 @@ dynamic: The dynamic backend means that a filter must be present in the filter chain which must set the target url explicitly. - -Comments +# Comments An eskip document can contain comments. The rule for comments is simple: everything is a comment that starts with '//' and ends with a new-line @@ -236,22 +229,19 @@ Example with comments: route1: Path("/api") -> "https://api.example.org"; route2: * -> // everything else 404 - -Regular expressions +# Regular expressions The matching predicates and the built-in filters that use regular expressions, use the go stdlib regexp, which uses re2: https://github.com/google/re2/wiki/Syntax - -Parsing Filters +# Parsing Filters The eskip.ParseFilters method can be used to parse a chain of filters, without the matcher and backend part of a full route expression. - -Parsing +# Parsing Parsing a routing table or a route expression happens with the eskip.Parse function. In case of grammar error, it returns an error with @@ -262,15 +252,13 @@ The eskip parser does not validate the routes against all semantic rules, e.g., whether a filter or a custom predicate implementation is available. This validation happens during processing the parsed definitions. - -Serializing +# Serializing Serializing a single route happens by calling its String method. Serializing a complete routing table happens by calling the eskip.String method. - -JSON +# JSON Both serializing and parsing is possible via the standard json.Marshal and json.Unmarshal functions. diff --git a/eskip/eq.go b/eskip/eq.go index cc1a013691..49d18658e6 100644 --- a/eskip/eq.go +++ b/eskip/eq.go @@ -135,7 +135,6 @@ func eq2Lists(left, right []*Route) bool { // If there are multiple methods, only the last one is considered, to // reproduce the route matching (even if how it works, may not be the // most expected in regard of the method predicates). -// func Eq(r ...*Route) bool { for i := 1; i < len(r); i++ { if !eq2(r[i-1], r[i]) { @@ -150,7 +149,6 @@ func Eq(r ...*Route) bool { // by each list are equal by Eq(). Repeated route IDs are considered invalid // and EqLists always returns false in this case. The order of the routes in // the lists doesn't matter. -// func EqLists(r ...[]*Route) bool { rc := make([][]*Route, len(r)) for i := range rc { @@ -175,7 +173,6 @@ func EqLists(r ...[]*Route) bool { // standard, non-legacy representation of the predicates and the backends. // Canonical creates a copy of the route, but doesn't necessarily creates a // copy of every field. See also Copy(). -// func Canonical(r *Route) *Route { if r == nil { return nil @@ -272,7 +269,6 @@ func Canonical(r *Route) *Route { // keeping the order. The returned slice is a new slice of the input // slice but the routes in the slice and their fields are not necessarily // all copied. See more at CopyRoutes() and Canonical(). -// func CanonicalList(l []*Route) []*Route { if len(l) == 0 { return nil diff --git a/eskip/eskip.go b/eskip/eskip.go index fa9a66802d..07c26e02b3 100644 --- a/eskip/eskip.go +++ b/eskip/eskip.go @@ -28,10 +28,10 @@ var ( // --edit-route='/Source[(](.*)[)]/ClientIP($1)/', which will change // routes as you can see: // -// # input -// r0: Source("127.0.0.1/8", "10.0.0.0/8") -> inlineContent("OK") -> -// # actual route -// edit_r0: ClientIP("127.0.0.1/8", "10.0.0.0/8") -> inlineContent("OK") -> +// # input +// r0: Source("127.0.0.1/8", "10.0.0.0/8") -> inlineContent("OK") -> ; +// # actual route +// edit_r0: ClientIP("127.0.0.1/8", "10.0.0.0/8") -> inlineContent("OK") -> ; func NewEditor(reg *regexp.Regexp, repl string) *Editor { return &Editor{ reg: reg, @@ -50,11 +50,11 @@ type Editor struct { // --clone-route='/Source[(](.*)[)]/ClientIP($1)/', which will change // routes as you can see: // -// # input -// r0: Source("127.0.0.1/8", "10.0.0.0/8") -> inlineContent("OK") -> -// # actual route -// clone_r0: ClientIP("127.0.0.1/8", "10.0.0.0/8") -> inlineContent("OK") -> -// r0: Source("127.0.0.1/8", "10.0.0.0/8") -> inlineContent("OK") -> +// # input +// r0: Source("127.0.0.1/8", "10.0.0.0/8") -> inlineContent("OK") -> ; +// # actual route +// clone_r0: ClientIP("127.0.0.1/8", "10.0.0.0/8") -> inlineContent("OK") -> ; +// r0: Source("127.0.0.1/8", "10.0.0.0/8") -> inlineContent("OK") -> ; func NewClone(reg *regexp.Regexp, repl string) *Clone { return &Clone{ reg: reg, diff --git a/eskip/template.go b/eskip/template.go index 8551baa873..0c57c57668 100644 --- a/eskip/template.go +++ b/eskip/template.go @@ -35,8 +35,7 @@ type TemplateContext interface { // New parses a template string and returns a reusable *Template object. // The template string can contain named placeholders of the format: // -// Hello, ${who}! -// +// Hello, ${who}! func NewTemplate(template string) *Template { matches := placeholderRegexp.FindAllStringSubmatch(template, -1) placeholders := make([]string, len(matches)) diff --git a/filters/accesslog/control.go b/filters/accesslog/control.go index ce4e3860bf..cdfc1a1a74 100644 --- a/filters/accesslog/control.go +++ b/filters/accesslog/control.go @@ -58,8 +58,8 @@ type disableAccessLog struct{} // Optionally takes in response code prefixes as arguments. When provided, access log is disabled // only if response code matches one of the arguments. // -// disableAccessLog() or -// disableAccessLog(1, 20, 301) to disable logs for 1xx, 20x and 301 codes +// disableAccessLog() or +// disableAccessLog(1, 20, 301) to disable logs for 1xx, 20x and 301 codes func NewDisableAccessLog() filters.Spec { return &disableAccessLog{} } @@ -76,8 +76,8 @@ type enableAccessLog struct{} // Optionally takes in response code prefixes as arguments. When provided, access log is enabled // only if response code matches one of the arguments. // -// enableAccessLog() -// enableAccessLog(1, 20, 301) to enable logs for 1xx, 20x and 301 codes +// enableAccessLog() +// enableAccessLog(1, 20, 301) to enable logs for 1xx, 20x and 301 codes func NewEnableAccessLog() filters.Spec { return &enableAccessLog{} } diff --git a/filters/accesslog/disable.go b/filters/accesslog/disable.go index a1bf32543c..dbc59c9ce6 100644 --- a/filters/accesslog/disable.go +++ b/filters/accesslog/disable.go @@ -15,7 +15,8 @@ type accessLogDisabled struct { // NewAccessLogDisabled creates a filter spec for overriding the state of the AccessLogDisabled setting. (By default global setting is used.) // -// accessLogDisabled("false") +// accessLogDisabled("false") +// // Deprecated: use disableAccessLog or enableAccessLog func NewAccessLogDisabled() filters.Spec { return &accessLogDisabled{} diff --git a/filters/accesslog/doc.go b/filters/accesslog/doc.go index 99dc479bff..177330a3f8 100644 --- a/filters/accesslog/doc.go +++ b/filters/accesslog/doc.go @@ -1,7 +1,7 @@ /* Package accesslog provides request filters that give the ability to override AccessLogDisabled setting. -How It Works +# How It Works There are two filters that change the state of access log "disableAccessLog" and "enableAccessLog". If "disableAccessLog" is present access log entries for this route won't be produced even if global AccessLogDisabled is false. Otherwise, if @@ -10,8 +10,8 @@ is true. Usage - enableAccessLog() - disableAccessLog() + enableAccessLog() + disableAccessLog() Note: accessLogDisabled("true") filter is deprecated in favor of "disableAccessLog" and "enableAccessLog" */ diff --git a/filters/apiusagemonitoring/doc.go b/filters/apiusagemonitoring/doc.go index 03063a0ee0..73d824800f 100644 --- a/filters/apiusagemonitoring/doc.go +++ b/filters/apiusagemonitoring/doc.go @@ -1,8 +1,6 @@ /* - Package apiusagemonitoring provides filters gathering metrics around API calls - Feature switch & Dependencies This feature is by default not enabled. To enable it, the flag `-enable-api-usage-monitoring` @@ -14,8 +12,7 @@ flavour. Per instance: This will enable the API monitoring filter through Prometheus metrics. - -Configuration +# Configuration Due to its structured configuration, the filter accepts one parameter of type string containing a JSON object. @@ -23,8 +20,7 @@ containing a JSON object. Details and examples can be found at https://opensource.zalando.com/skipper/reference/filters/#apiUsageMonitoring (or in this project, under `docs/reference/filters.md`). - -Development Helpers +# Development Helpers The spec and filter log detailed operation information at `DEBUG` level. The -application-log-level=DEBUG switch is desirable for debugging usage of the filter. @@ -40,6 +36,5 @@ Command line example for executing locally: -metrics-flavour prometheus \ -histogram-metric-buckets=".01,.025,.05,.075,.1,.2,.3,.4,.5,.75,1,2,3,4,5,7,10,15,20,30,60,120,300,600" \ -application-log-level=DEBUG - */ package apiusagemonitoring diff --git a/filters/auth/basic.go b/filters/auth/basic.go index 515a6e09a1..957e1a67bd 100644 --- a/filters/auth/basic.go +++ b/filters/auth/basic.go @@ -26,7 +26,7 @@ func NewBasicAuth() *basicSpec { return &basicSpec{} } -//We do not touch response at all +// We do not touch response at all func (a *basic) Response(filters.FilterContext) {} // check basic auth diff --git a/filters/auth/doc.go b/filters/auth/doc.go index 82e9e5654f..9692ffcc70 100644 --- a/filters/auth/doc.go +++ b/filters/auth/doc.go @@ -1,7 +1,7 @@ /* Package auth provides authentication related filters. -Basic - Check Basic Authentication +# Basic - Check Basic Authentication The filter accepts two parameters, the first mandatory one is the path to the htpasswd file usually used with Apache or nginx. The second one is the optional realm name that will be displayed in the browser. Each incoming @@ -18,7 +18,7 @@ Embedding the filter in routes: -> basicAuth("/path/to/htpasswd", "My Website") -> "https://my-internal.example.org"; -OAuth2 - Check Bearer Tokens +# OAuth2 - Check Bearer Tokens The auth filter takes the incoming request, and tries to extract the Bearer token from the Authorization header. Then it validates against @@ -28,7 +28,7 @@ check if it has at least one of the predefined scopes. If any of the expectations are not met, it doesn't forward the request to the target endpoint, but returns with status 401. -OAuth2 - Provider Configuration - Tokeninfo +# OAuth2 - Provider Configuration - Tokeninfo To enable OAuth2 tokeninfo filters you have to set the CLI argument -oauth2-tokeninfo-url=. Scopes and key value pairs @@ -45,40 +45,40 @@ tokeninfo timeout is 2s. Example json output of the tokeninfo response could be: - { - "access_token": "", - "client_id": "ztoken", - "cn": "Jane Doe", - "expires_in": "300", - "grant_type": "password", - "realm": "/employees", - "scope": [ - "uid", - "foo-r", - "bar-w", - "qux-rw" - ], - "token_type": "Bearer", - "uid": "jdoe" - } - -OAuth2 - oauthTokeninfoAnyScope filter + { + "access_token": "", + "client_id": "ztoken", + "cn": "Jane Doe", + "expires_in": "300", + "grant_type": "password", + "realm": "/employees", + "scope": [ + "uid", + "foo-r", + "bar-w", + "qux-rw" + ], + "token_type": "Bearer", + "uid": "jdoe" + } + +# OAuth2 - oauthTokeninfoAnyScope filter The filter oauthTokeninfoAnyScope allows access if one of the scopes is satisfied by the request. - a: Path("/a") -> oauthTokeninfoAnyScope("uid") -> "https://internal.example.org/"; - b: Path("/b") -> oauthTokeninfoAnyScope("uid", "bar") -> "https://internal.example.org/"; + a: Path("/a") -> oauthTokeninfoAnyScope("uid") -> "https://internal.example.org/"; + b: Path("/b") -> oauthTokeninfoAnyScope("uid", "bar") -> "https://internal.example.org/"; -OAuth - oauthTokeninfoAllScope() filter +# OAuth - oauthTokeninfoAllScope() filter The filter oauthTokeninfoAllScope allows access if all of the scopes are satisfied by the request: - a: Path("/a") -> oauthTokeninfoAllScope("uid") -> "https://internal.example.org/"; - b: Path("/b") -> oauthTokeninfoAllScope("uid", "bar") -> "https://internal.example.org/"; + a: Path("/a") -> oauthTokeninfoAllScope("uid") -> "https://internal.example.org/"; + b: Path("/b") -> oauthTokeninfoAllScope("uid", "bar") -> "https://internal.example.org/"; -OAuth - oauthTokeninfoAnyKV() filter +# OAuth - oauthTokeninfoAnyKV() filter The filter oauthTokeninfoAnyKV allows access if the token information returned by OAuthTokeninfoURL has the given key and the given @@ -88,34 +88,34 @@ The following route has a filter definition, that one of the keys "uid" or "foo" has the value "jdoe" or "bar". Additionally the second will check if there is a "realm" "/employees": - a: Path("/") -> oauthTokeninfoAnyKV("uid", "jdoe", "foo", "bar") -> "https://internal.example.org/"; + a: Path("/") -> oauthTokeninfoAnyKV("uid", "jdoe", "foo", "bar") -> "https://internal.example.org/"; - b: Path("/") -> oauthTokeninfoAnyKV("realm","/employees", "uid", "jdoe", "foo", "bar") -> "https://internal.example.org/"; + b: Path("/") -> oauthTokeninfoAnyKV("realm","/employees", "uid", "jdoe", "foo", "bar") -> "https://internal.example.org/"; The same as route `a` above, but you also allow "uid=mstar" to access: - a: Path("/") -> oauthTokeninfoAnyKV("uid", "jdoe", "uid", "mstar") -> "https://internal.example.org/"; + a: Path("/") -> oauthTokeninfoAnyKV("uid", "jdoe", "uid", "mstar") -> "https://internal.example.org/"; Example json output of this tokeninfo response: - { - "access_token": "", - "client_id": "ztoken", - "cn": "Jane Doe", - "expires_in": "300", - "grant_type": "password", - "realm": "/employees", - "scope": [ - "uid", - "foo-r", - "bar-w", - "qux-rw" - ], - "token_type": "Bearer", - "uid": "jdoe" - } - -OAuth - oauthTokeninfoAllKV() filter + { + "access_token": "", + "client_id": "ztoken", + "cn": "Jane Doe", + "expires_in": "300", + "grant_type": "password", + "realm": "/employees", + "scope": [ + "uid", + "foo-r", + "bar-w", + "qux-rw" + ], + "token_type": "Bearer", + "uid": "jdoe" + } + +# OAuth - oauthTokeninfoAllKV() filter The filter oauthTokeninfoAllKV allows access if the token information returned by OAuthTokeninfoURL has the given key and the given value. @@ -125,35 +125,34 @@ the key value pairs match. Here "uid" has to have the value "jdoe" and "foo" has to have the value "bar". Additionally the second will check if there is a "realm" "/employees": - a: Path("/") -> oauthTokeninfoAllKV("uid", "jdoe", "foo", "bar") -> "https://internal.example.org/"; - b: Path("/") -> oauthTokeninfoAllKV("realm", "/employees", "uid", "jdoe", "foo", "bar") -> "https://internal.example.org/"; + a: Path("/") -> oauthTokeninfoAllKV("uid", "jdoe", "foo", "bar") -> "https://internal.example.org/"; + b: Path("/") -> oauthTokeninfoAllKV("realm", "/employees", "uid", "jdoe", "foo", "bar") -> "https://internal.example.org/"; Example json output of this information response: - { - "access_token": "", - "client_id": "ztoken", - "cn": "John Doe", - "expires_in": "300", - "grant_type": "password", - "foo": "bar", - "realm": "/employees", - "scope": [ - "uid", - "foo-r", - "bar-w", - "qux-rw" - ], - "token_type": "Bearer", - "uid": "jdoe" - } - + { + "access_token": "", + "client_id": "ztoken", + "cn": "John Doe", + "expires_in": "300", + "grant_type": "password", + "foo": "bar", + "realm": "/employees", + "scope": [ + "uid", + "foo-r", + "bar-w", + "qux-rw" + ], + "token_type": "Bearer", + "uid": "jdoe" + } In case you are using any of the above 4 filters in your custom build, you can call the `Close()` method to close the `quit` channel and free up goroutines, to avoid goroutine leak -OAuth2 - Provider Configuration - Tokenintrospection +# OAuth2 - Provider Configuration - Tokenintrospection Provider configuration is dynamically done by https://tools.ietf.org/html/draft-ietf-oauth-discovery-06#section-5, @@ -164,58 +163,58 @@ filter configurations. Example response from the openid-configuration endpoint: - { - "issuer" : "https://issuer.example.com", - "token_endpoint" : "https://issuer.example.com/token", - "introspection_endpoint": "https://issuer.example.com/token/introspect", - "revocation_endpoint" : "https://issuer.example.com/token/revoke", - "authorization_endpoint": "https://issuer.example.com/login", - "userinfo_endpoint" : "https://issuer.example.com/userinfo", - "jwks_uri" : "https://issuer.example.com/token/certs", - "response_types_supported": [ - "code", - "token", - "id_token", - "code token", - "code id_token", - "token id_token", - "code token id_token", - "none" - ], - "subject_types_supported": [ - "public" - ], - "id_token_signing_alg_values_supported": [ - "RS256" - ], - "scopes_supported": [ - "openid", - "email", - "profile" - ], - "token_endpoint_auth_methods_supported": [ - "client_secret_post", - "client_secret_basic" - ], - "claims_supported": [ - "aud", - "email", - "email_verified", - "exp", - "family_name", - "given_name", - "iat", - "iss", - "locale", - "name", - "picture", - "sub" - ], - "code_challenge_methods_supported": [ - "plain", - "S256" - ] - } + { + "issuer" : "https://issuer.example.com", + "token_endpoint" : "https://issuer.example.com/token", + "introspection_endpoint": "https://issuer.example.com/token/introspect", + "revocation_endpoint" : "https://issuer.example.com/token/revoke", + "authorization_endpoint": "https://issuer.example.com/login", + "userinfo_endpoint" : "https://issuer.example.com/userinfo", + "jwks_uri" : "https://issuer.example.com/token/certs", + "response_types_supported": [ + "code", + "token", + "id_token", + "code token", + "code id_token", + "token id_token", + "code token id_token", + "none" + ], + "subject_types_supported": [ + "public" + ], + "id_token_signing_alg_values_supported": [ + "RS256" + ], + "scopes_supported": [ + "openid", + "email", + "profile" + ], + "token_endpoint_auth_methods_supported": [ + "client_secret_post", + "client_secret_basic" + ], + "claims_supported": [ + "aud", + "email", + "email_verified", + "exp", + "family_name", + "given_name", + "iat", + "iss", + "locale", + "name", + "picture", + "sub" + ], + "code_challenge_methods_supported": [ + "plain", + "S256" + ] + } Additionally, you can also pass CLI argument -oauth2-tokenintrospect-timeout= to control the @@ -226,30 +225,28 @@ All oauthTokenintrospection* filters will work on the tokenintrospect response. Example json output of the tokenintrospect response could be: - - { - "access_token": "", - "client_id": "ztoken", - "name": "Jane Doe", - "expires_in": "300", - "grant_type": "password", - "active": true, - "sub": "a-sub", - "iss": "https://issuer.example.com" - "realm": "/employees", - "claims": { - "uid": "jdoe", - "email": "jdoe@example.com" - }, - "scope": [ - "email", - "foo-r", - ], - "token_type": "Bearer", - } - - -OAuth2 - oauthTokenintrospectionAnyClaims filter + { + "access_token": "", + "client_id": "ztoken", + "name": "Jane Doe", + "expires_in": "300", + "grant_type": "password", + "active": true, + "sub": "a-sub", + "iss": "https://issuer.example.com" + "realm": "/employees", + "claims": { + "uid": "jdoe", + "email": "jdoe@example.com" + }, + "scope": [ + "email", + "foo-r", + ], + "token_type": "Bearer", + } + +# OAuth2 - oauthTokenintrospectionAnyClaims filter The filter oauthTokenintrospectionAnyClaims can be configured with claims validated from the openid-configuration `claims_supported` and @@ -263,9 +260,9 @@ in the filter. The following route has a filter definition, that will check if there is one of the following claims in the token: "uid" or "email": - a: Path("/") -> oauthTokenintrospectionAnyClaims("https://issuer.example.com", "uid", "email") -> "https://internal.example.org/"; + a: Path("/") -> oauthTokenintrospectionAnyClaims("https://issuer.example.com", "uid", "email") -> "https://internal.example.org/"; -OAuth2 - oauthTokenintrospectionAllClaims filter +# OAuth2 - oauthTokenintrospectionAllClaims filter The filter oauthTokenintrospectionAllClaims can be configured with claims validated from the openid-configuration `claims_supported` and @@ -279,9 +276,9 @@ in the filter. The following route has a filter definition, that will check if there all of the following claims in the token: "uid" and "email": - a: Path("/") -> oauthTokenintrospectionAllClaims("https://issuer.example.com", "uid", "email") -> "https://internal.example.org/"; + a: Path("/") -> oauthTokenintrospectionAllClaims("https://issuer.example.com", "uid", "email") -> "https://internal.example.org/"; -OAuth2 - oauthTokenintrospectionAnyKV filter +# OAuth2 - oauthTokenintrospectionAnyKV filter The filter oauthTokenintrospectionAnyKV will use the `introspection_endpoint` endpoint from the openid-configuration to @@ -295,13 +292,13 @@ The following route has a filter definition, that will check if there one of the following key-value pairs in the token: "uid=jdoe" or "iss=https://issuer.example.com": - a: Path("/") -> oauthTokenintrospectionAnyKV("https://issuer.example.com", "uid", "jdoe", "iss", "https://issuer.example.com") -> "https://internal.example.org/"; + a: Path("/") -> oauthTokenintrospectionAnyKV("https://issuer.example.com", "uid", "jdoe", "iss", "https://issuer.example.com") -> "https://internal.example.org/"; The same as route `a` above, but you also allow "uid=mstar" to access: - a: Path("/") -> oauthTokenintrospectionAnyKV("https://issuer.example.com", "uid", "jdoe", "uid", "mstar", "iss", "https://issuer.example.com") -> "https://internal.example.org/"; + a: Path("/") -> oauthTokenintrospectionAnyKV("https://issuer.example.com", "uid", "jdoe", "uid", "mstar", "iss", "https://issuer.example.com") -> "https://internal.example.org/"; -OAuth2 - oauthTokenintrospectionAllKV filter +# OAuth2 - oauthTokenintrospectionAllKV filter The filter oauthTokenintrospectionAllKV will use the `introspection_endpoint` endpoint from the openid-configuration to @@ -315,9 +312,9 @@ The following route has a filter definition, that will check if there are all of the following key-value pairs in the token: "uid=jdoe" or "iss=https://issuer.example.com": - a: Path("/") -> oauthTokenintrospectionAllKV("https://issuer.example.com", "uid", "jdoe", "iss", "https://issuer.example.com") -> "https://internal.example.org/"; + a: Path("/") -> oauthTokenintrospectionAllKV("https://issuer.example.com", "uid", "jdoe", "iss", "https://issuer.example.com") -> "https://internal.example.org/"; -OpenID - oauthOidcUserInfo filter +# OpenID - oauthOidcUserInfo filter The filter oauthOidcUserInfo is a filter for OAuth Implicit Flow authentication of users through OpenID Connect. It verifies that the token provided by the user upon authentication contains all the fields specified in the filter. @@ -325,7 +322,7 @@ It verifies that the token provided by the user upon authentication contains all a: Path("/") -> oauthOidcUserInfo("https://accounts.identity-provider.com", "some-client-id", "some-client-secret", "http://callback.com/auth/provider/callback", "scope1 scope2", "field1 field2") -> "https://internal.example.org"; -OpenID - oauthOidcAnyClaims filter +# OpenID - oauthOidcAnyClaims filter The filter oauthOidcAnyClaims is a filter for OAuth Implicit Flow authentication scheme for users through OpenID Connect. It verifies that the token provided by the user upon authentication with the authentication provider contains at @@ -342,32 +339,31 @@ of the claims specified in the filter. a: Path("/") -> oauthOidcAllClaims("https://accounts.identity-provider.com", "some-client-id", "some-client-secret", "http://callback.com/auth/provider/callback", "scope1 scope2", "claim1 claim2") -> "https://internal.example.org"; -OAuth - auditLog() filter +# OAuth - auditLog() filter The filter auditLog allows you to have an audit log for all requests. This filter should be always set, before checking with auth filters. To see only permitted access, you can set the auditLog() filter after the auth filter. - a: Path("/only-allowed-audit-log") -> oauthTokeninfoAnyScope("bar-w") -> auditLog() -> "https://internal.example.org/"; - b: Path("/all-access-requests-audit-log") -> auditLog() -> oauthTokeninfoAnyScope("foo-r") -> "https://internal.example.org/"; + a: Path("/only-allowed-audit-log") -> oauthTokeninfoAnyScope("bar-w") -> auditLog() -> "https://internal.example.org/"; + b: Path("/all-access-requests-audit-log") -> auditLog() -> oauthTokeninfoAnyScope("foo-r") -> "https://internal.example.org/"; -Webhook - webhook() filter +# Webhook - webhook() filter The filter webhook allows you to have a custom authentication and authorization endpoint for a route. Headers from the webhook response can be copyied into the continuing request by specifying the headers to copy as an optional second argument to the filter - a: Path("/only-allowed-by-webhook") -> webhook("https://custom-webhook.example.org/auth") -> "https://protected-backend.example.org/"; - b: Path("/copy-webhook-headers") -> webhook("https://custom-webhook.example.org/auth", "X-Copy-This-Header") -> "https://protected-backend.example.org/"; + a: Path("/only-allowed-by-webhook") -> webhook("https://custom-webhook.example.org/auth") -> "https://protected-backend.example.org/"; + b: Path("/copy-webhook-headers") -> webhook("https://custom-webhook.example.org/auth", "X-Copy-This-Header") -> "https://protected-backend.example.org/"; -Forward Token - forwardToken() filter +# Forward Token - forwardToken() filter The filter is used to forward the result of token introspection or token info to the backend. a: Path("/tokeninfo-protected") -> oauthTokeninfoAnyScope("uid") -> forwardToken("X-Tokeninfo-Forward") -> "https://internal.example.org"; b: Path("tokenintrospection-protected") -> oauthTokenintrospectionAnyKV("uid") -> forwardToken("X-Tokenintrospection-Forward") -> "http://internal.example.org"; - */ package auth diff --git a/filters/auth/jwt_validation.go b/filters/auth/jwt_validation.go index 664e62cef4..07a005cea8 100644 --- a/filters/auth/jwt_validation.go +++ b/filters/auth/jwt_validation.go @@ -33,7 +33,7 @@ var refreshRateLimit = time.Minute * 5 var refreshTimeout = time.Second * 10 var refreshUnknownKID = true -//the map of jwks keyfunctions stored per jwksUri +// the map of jwks keyfunctions stored per jwksUri var jwksMap map[string]*keyfunc.JWKS = make(map[string]*keyfunc.JWKS) func NewJwtValidationWithOptions(o TokenintrospectionOptions) filters.Spec { diff --git a/filters/auth/oidc.go b/filters/auth/oidc.go index d11828be7a..ed0b02b35c 100644 --- a/filters/auth/oidc.go +++ b/filters/auth/oidc.go @@ -71,9 +71,9 @@ type azureGraphGroups struct { // Filter parameter: // -// oauthOidc...("https://oidc-provider.example.com", "client_id", "client_secret", -// "http://target.example.com/subpath/callback", "email profile", "name email picture", -// "parameter=value", "X-Auth-Authorization:claims.email") +// oauthOidc...("https://oidc-provider.example.com", "client_id", "client_secret", +// "http://target.example.com/subpath/callback", "email profile", "name email picture", +// "parameter=value", "X-Auth-Authorization:claims.email") const ( paramIdpURL int = iota paramClientID @@ -170,12 +170,12 @@ func NewOAuthOidcAllClaims(secretsFile string, secretsRegistry secrets.Encrypter // CreateFilter creates an OpenID Connect authorization filter. // // first arg: a provider, for example "https://accounts.google.com", -// which has the path /.well-known/openid-configuration +// which has the path /.well-known/openid-configuration // // Example: // -// oauthOidcAllClaims("https://accounts.identity-provider.com", "some-client-id", "some-client-secret", -// "http://callback.com/auth/provider/callback", "scope1 scope2", "claim1 claim2", "", "", "") -> "https://internal.example.org"; +// oauthOidcAllClaims("https://accounts.identity-provider.com", "some-client-id", "some-client-secret", +// "http://callback.com/auth/provider/callback", "scope1 scope2", "claim1 claim2", "", "", "") -> "https://internal.example.org"; func (s *tokenOidcSpec) CreateFilter(args []interface{}) (filters.Filter, error) { sargs, err := getStrings(args) if err != nil { @@ -949,17 +949,16 @@ func (f *tokenOidcFilter) getTokenWithExchange(state *OauthState, ctx filters.Fi // https://openid.net/specs/openid-connect-core-1_0.html#AggregatedDistributedClaims // Example: // -// { -// "_claim_names": { -// "groups": "src1" -// }, -// "_claim_sources": { -// "src1": { -// "endpoint": "https://graph.windows.net/.../getMemberObjects" -// } -// } -// } -// +// { +// "_claim_names": { +// "groups": "src1" +// }, +// "_claim_sources": { +// "src1": { +// "endpoint": "https://graph.windows.net/.../getMemberObjects" +// } +// } +// } func (f *tokenOidcFilter) handleDistributedClaims(ctx context.Context, idToken *oidc.IDToken, oauth2Token *oauth2.Token, claimsMap map[string]interface{}) error { // https://github.com/coreos/go-oidc/issues/171#issuecomment-1044286153 var distClaims distributedClaims diff --git a/filters/auth/tokeninfo.go b/filters/auth/tokeninfo.go index e89bac2a53..44ecdfd014 100644 --- a/filters/auth/tokeninfo.go +++ b/filters/auth/tokeninfo.go @@ -134,7 +134,6 @@ func NewOAuthTokeninfoAnyKV(OAuthTokeninfoURL string, OAuthTokeninfoTimeout time // Use one of the base initializer functions as the first argument: // NewOAuthTokeninfoAllScope, NewOAuthTokeninfoAnyScope, // NewOAuthTokeninfoAllKV or NewOAuthTokeninfoAnyKV. -// func TokeninfoWithOptions(create func(string, time.Duration) filters.Spec, o TokeninfoOptions) filters.Spec { s := create(o.URL, o.Timeout) ts, ok := s.(*tokeninfoSpec) @@ -167,8 +166,7 @@ func (s *tokeninfoSpec) Name() string { // type. The shown example for checkOAuthTokeninfoAllScopes will grant // access only to tokens, that have scopes read-x and write-y: // -// s.CreateFilter("read-x", "write-y") -// +// s.CreateFilter("read-x", "write-y") func (s *tokeninfoSpec) CreateFilter(args []interface{}) (filters.Filter, error) { sargs, err := getStrings(args) if err != nil { diff --git a/filters/auth/tokenintrospection.go b/filters/auth/tokenintrospection.go index cfd070923f..4b995f20a3 100644 --- a/filters/auth/tokenintrospection.go +++ b/filters/auth/tokenintrospection.go @@ -149,7 +149,7 @@ func NewOAuthTokenintrospectionAllClaims(timeout time.Duration) filters.Spec { return newOAuthTokenintrospectionFilter(checkOAuthTokenintrospectionAllClaims, timeout) } -//Secure Introspection Point +// Secure Introspection Point func NewSecureOAuthTokenintrospectionAnyKV(timeout time.Duration) filters.Spec { return newSecureOAuthTokenintrospectionFilter(checkSecureOAuthTokenintrospectionAnyKV, timeout) } diff --git a/filters/auth/webhook.go b/filters/auth/webhook.go index 3a7374652c..acfa052db0 100644 --- a/filters/auth/webhook.go +++ b/filters/auth/webhook.go @@ -58,9 +58,8 @@ func (*webhookSpec) Name() string { // string. The second, optional, argument is a comma separated list of // headers to forward from from webhook response. // -// s.CreateFilter("https://my-auth-service.example.org/auth") -// s.CreateFilter("https://my-auth-service.example.org/auth", "X-Auth-User,X-Auth-User-Roles") -// +// s.CreateFilter("https://my-auth-service.example.org/auth") +// s.CreateFilter("https://my-auth-service.example.org/auth", "X-Auth-User,X-Auth-User-Roles") func (ws *webhookSpec) CreateFilter(args []interface{}) (filters.Filter, error) { if l := len(args); l == 0 || l > 2 { return nil, filters.ErrInvalidFilterParameters diff --git a/filters/block/matcher.go b/filters/block/matcher.go index f07f0b6b99..b64050328b 100644 --- a/filters/block/matcher.go +++ b/filters/block/matcher.go @@ -47,7 +47,6 @@ const ( // When the matcher is closed, it doesn't read anymore from the input or return any // buffered data. If the input implements io.Closer, closing the matcher closes the // input, too. -// type matcher struct { once sync.Once input io.ReadCloser diff --git a/filters/builtin/builtin.go b/filters/builtin/builtin.go index 879170886c..53e1ad800c 100644 --- a/filters/builtin/builtin.go +++ b/filters/builtin/builtin.go @@ -203,6 +203,7 @@ func MakeRegistry() filters.Registry { tracing.NewBaggageToTagFilter(), tracing.NewTag(), tracing.NewStateBagToTag(), + //lint:ignore SA1019 due to backward compatibility accesslog.NewAccessLogDisabled(), accesslog.NewDisableAccessLog(), accesslog.NewEnableAccessLog(), diff --git a/filters/builtin/compress.go b/filters/builtin/compress.go index 6ab4aeae90..f28f281921 100644 --- a/filters/builtin/compress.go +++ b/filters/builtin/compress.go @@ -99,7 +99,7 @@ func (e encodings) Swap(i, j int) { e[i], e[j] = e[j], e[i] } // // Example: // -// * -> compress() -> "https://www.example.org" +// r: * -> compress() -> "https://www.example.org"; // // The filter, when executed on the response path, checks if the response // entity can be compressed. To decide, it checks the Content-Encoding, the @@ -116,12 +116,12 @@ func (e encodings) Swap(i, j int) { e[i], e[j] = e[j], e[i] } // types as filter arguments. When extending the defaults, the first argument needs // to be "...". E.g. to compress tiff in addition to the defaults: // -// * -> compress("...", "image/tiff") -> "https://www.example.org" +// r: * -> compress("...", "image/tiff") -> "https://www.example.org"; // // To reset the supported types, e.g. to compress only HTML, the "..." argument // needs to be omitted: // -// * -> compress("text/html") -> "https://www.example.org" +// r: * -> compress("text/html") -> "https://www.example.org"; // // It is possible to control the compression level, by setting it as the first // filter argument, in front of the MIME types. The default compression level is @@ -129,7 +129,7 @@ func (e encodings) Swap(i, j int) { e[i], e[j] = e[j], e[i] } // 0 means no-compression, 1 means best-speed and 11 means best-compression. // Example: // -// * -> compress(9, "image/tiff") -> "https://www.example.org" +// r: * -> compress(9, "image/tiff") -> "https://www.example.org"; // // The filter also checks the incoming request, if it accepts the supported // encodings, explicitly stated in the Accept-Encoding header. The filter currently @@ -147,7 +147,6 @@ func (e encodings) Swap(i, j int) { e[i], e[j] = e[j], e[i] } // encoding and sets the Vary: Accept-Encoding header, if missing. // // The compression happens in a streaming way, using only a small internal buffer. -// func NewCompress() filters.Spec { c, err := NewCompressWithOptions(CompressOptions{supportedEncodings}) if err != nil { diff --git a/filters/builtin/decompress.go b/filters/builtin/decompress.go index 2888b8cf28..adfd41ac46 100644 --- a/filters/builtin/decompress.go +++ b/filters/builtin/decompress.go @@ -172,7 +172,6 @@ func (e decodingError) Error() string { // decompression may fail after all the filters were processed. // // The filter does not need any parameters. -// func NewDecompress() filters.Spec { return decompress{} } diff --git a/filters/builtin/headertoquery.go b/filters/builtin/headertoquery.go index df68fc3208..945a647fc1 100644 --- a/filters/builtin/headertoquery.go +++ b/filters/builtin/headertoquery.go @@ -17,7 +17,7 @@ type ( // NewHeaderToQuery creates a filter which converts the headers // from the incoming Request to query params // -// headerToQuery("X-Foo-Header", "foo-query-param") +// headerToQuery("X-Foo-Header", "foo-query-param") // // The above filter will set the "foo-query-param" query param // to the value of "X-Foo-Header" header, to the request diff --git a/filters/builtin/inlinecontent.go b/filters/builtin/inlinecontent.go index ae43e32207..61d52fa380 100644 --- a/filters/builtin/inlinecontent.go +++ b/filters/builtin/inlinecontent.go @@ -18,18 +18,17 @@ type inlineContent struct { // // Usage of the filter: // -// * -> status(420) -> inlineContent("Enhance Your Calm") -> +// r: * -> status(420) -> inlineContent("Enhance Your Calm") -> ; // // Or: // -// * -> inlineContent("{\"foo\": 42}", "application/json") -> +// r: * -> inlineContent("{\"foo\": 42}", "application/json") -> ; // // It accepts two arguments: the content and the optional content type. // When the content type is not set, it tries to detect it using // http.DetectContentType. // // The filter shunts the request with status code 200. -// func NewInlineContent() filters.Spec { return &inlineContent{} } diff --git a/filters/builtin/inlinecontentifstatus.go b/filters/builtin/inlinecontentifstatus.go index 0103c4743e..26034257a3 100644 --- a/filters/builtin/inlinecontentifstatus.go +++ b/filters/builtin/inlinecontentifstatus.go @@ -19,13 +19,12 @@ type inlineContentIfStatus struct { // Creates a filter spec for the inlineContent() filter. // -// * -> inlineContentIfStatus(401, "{\"foo\": 42}", "application/json") -> "https://www.example.org" +// r: * -> inlineContentIfStatus(401, "{\"foo\": 42}", "application/json") -> "https://www.example.org"; // // It accepts three arguments: the statusCode code to match, the content and the optional content type. // When the content type is not set, it tries to detect it using http.DetectContentType. // // The filter replaces the response coming from the backend or the following filters. -// func NewInlineContentIfStatus() filters.Spec { return &inlineContentIfStatus{} } diff --git a/filters/builtin/query.go b/filters/builtin/query.go index 2b741429ee..9d6c6e7a92 100644 --- a/filters/builtin/query.go +++ b/filters/builtin/query.go @@ -21,7 +21,7 @@ type modQuery struct { // Returns a new dropQuery filter Spec, whose instances drop a corresponding // query parameter. // -// Instances expect the name string or template parameter, see eskip.Template.ApplyContext +// # Instances expect the name string or template parameter, see eskip.Template.ApplyContext // // Name: "dropQuery". func NewDropQuery() filters.Spec { return &modQuery{behavior: drop} } diff --git a/filters/builtin/querytoheader.go b/filters/builtin/querytoheader.go index 288786d861..81084d44fe 100644 --- a/filters/builtin/querytoheader.go +++ b/filters/builtin/querytoheader.go @@ -20,7 +20,7 @@ type ( // NewQueryToHeader creates a filter which converts query params // from the incoming Request to headers // -// queryToHeader("foo-query-param", "X-Foo-Header") +// queryToHeader("foo-query-param", "X-Foo-Header") // // The above filter will set the value of "X-Foo-Header" header to the // value of "foo-query-param" query param , to the request and will @@ -28,9 +28,8 @@ type ( // // The header value can be created by a formatstring with an optional third parameter // -// queryToHeader("foo-query-param", "X-Foo-Header", "prefix %s postfix") -// queryToHeader("access_token", "Authorization", "Bearer %s") -// +// queryToHeader("foo-query-param", "X-Foo-Header", "prefix %s postfix") +// queryToHeader("access_token", "Authorization", "Bearer %s") func NewQueryToHeader() filters.Spec { return &queryToHeaderSpec{} } diff --git a/filters/circuit/breaker.go b/filters/circuit/breaker.go index a167518874..a32459ca8c 100644 --- a/filters/circuit/breaker.go +++ b/filters/circuit/breaker.go @@ -57,7 +57,7 @@ func getDurationArg(a interface{}) (time.Duration, error) { // These filters set a breaker for the current route that open if the backend failures for the route reach a // value of N, where N is a mandatory argument of the filter: // -// consecutiveBreaker(15) +// consecutiveBreaker(15) // // The filter accepts the following optional arguments: timeout (milliseconds or duration string), // half-open-requests (integer), idle-ttl (milliseconds or duration string). @@ -70,7 +70,7 @@ func NewConsecutiveBreaker() filters.Spec { // These filters set a breaker for the current route that open if the backend failures for the route reach a // value of N within a window of the last M requests, where N and M are mandatory arguments of the filter: // -// rateBreaker(30, 300) +// rateBreaker(30, 300) // // The filter accepts the following optional arguments: timeout (milliseconds or duration string), // half-open-requests (integer), idle-ttl (milliseconds or duration string). diff --git a/filters/cookie/cookie.go b/filters/cookie/cookie.go index 72c6179111..c566b8029e 100644 --- a/filters/cookie/cookie.go +++ b/filters/cookie/cookie.go @@ -23,14 +23,14 @@ accessible from JS code running in web browsers. Examples: - requestCookie("test-session", "abc") + requestCookie("test-session", "abc") - responseCookie("test-session", "abc", 31536000) + responseCookie("test-session", "abc", 31536000) - responseCookie("test-session", "abc", 31536000, "change-only") + responseCookie("test-session", "abc", 31536000, "change-only") - // response cookie without HttpOnly: - jsCookie("test-session-info", "abc-debug", 31536000, "change-only") + // response cookie without HttpOnly: + jsCookie("test-session-info", "abc-debug", 31536000, "change-only") */ package cookie diff --git a/filters/cors/doc.go b/filters/cors/doc.go index b0db55e636..1f19493bba 100644 --- a/filters/cors/doc.go +++ b/filters/cors/doc.go @@ -1,7 +1,7 @@ /* Package cors implements the origin header for CORS. -How It Works +# How It Works The filter accepts an optional variadic list of acceptable origin parameters. If the input argument list is empty, the header will always be set to '*' which means any origin is acceptable. Otherwise the header is only set if the request contains diff --git a/filters/diag/diag.go b/filters/diag/diag.go index a6a81e88d8..7d8862759b 100644 --- a/filters/diag/diag.go +++ b/filters/diag/diag.go @@ -99,8 +99,7 @@ func kbps2bpms(kbps float64) float64 { // the byte length of the random response to be generated as an argument. // Eskip example: // -// * -> randomContent(2048) -> ; -// +// r: * -> randomContent(2048) -> ; func NewRandom() filters.Spec { return &random{} } // NewRepeat creates a filter specification whose filter instances can be used @@ -108,8 +107,7 @@ func NewRandom() filters.Spec { return &random{} } // the byte length of the response body to be generated as arguments. // Eskip example: // -// * -> repeatContent("x", 100) -> ; -// +// r: * -> repeatContent("x", 100) -> ; func NewRepeat() filters.Spec { return &repeat{} } // NewLatency creates a filter specification whose filter instances can be used @@ -117,73 +115,63 @@ func NewRepeat() filters.Spec { return &repeat{} } // as an argument. It always adds this value in addition to the natural latency, // and does not do any adjustments. Eskip example: // -// * -> latency(120) -> "https://www.example.org"; -// +// r: * -> latency(120) -> "https://www.example.org"; func NewLatency() filters.Spec { return &throttle{typ: latency} } // NewBandwidth creates a filter specification whose filter instances can be used // to maximize the bandwidth of the responses. It expects the bandwidth in // kbyte/sec as an argument. // -// * -> bandwidth(30) -> "https://www.example.org"; -// +// r: * -> bandwidth(30) -> "https://www.example.org"; func NewBandwidth() filters.Spec { return &throttle{typ: bandwidth} } // NewChunks creates a filter specification whose filter instances can be used // set artificial delays in between response chunks. It expects the byte length // of the chunks and the delay milliseconds. // -// * -> chunks(1024, "120ms") -> "https://www.example.org"; -// +// r: * -> chunks(1024, "120ms") -> "https://www.example.org"; func NewChunks() filters.Spec { return &throttle{typ: chunks} } // NewBackendLatency is the equivalent of NewLatency but for outgoing backend // requests. Eskip example: // -// * -> backendLatency(120) -> "https://www.example.org"; -// +// r: * -> backendLatency(120) -> "https://www.example.org"; func NewBackendLatency() filters.Spec { return &throttle{typ: backendLatency} } // NewBackendBandwidth is the equivalent of NewBandwidth but for outgoing backend // requests. Eskip example: // -// * -> backendBandwidth(30) -> "https://www.example.org"; -// +// r: * -> backendBandwidth(30) -> "https://www.example.org"; func NewBackendBandwidth() filters.Spec { return &throttle{typ: backendBandwidth} } // NewBackendChunks is the equivalent of NewChunks but for outgoing backend // requests. Eskip example: // -// * -> backendChunks(1024, 120) -> "https://www.example.org"; -// +// r: * -> backendChunks(1024, 120) -> "https://www.example.org"; func NewBackendChunks() filters.Spec { return &throttle{typ: backendChunks} } // NewUniformRequestLatency creates a latency for requests with uniform // distribution. Example delay around 1s with +/-120ms. // -// * -> uniformRequestLatency("1s", "120ms") -> "https://www.example.org"; -// +// r: * -> uniformRequestLatency("1s", "120ms") -> "https://www.example.org"; func NewUniformRequestLatency() filters.Spec { return &jitter{typ: uniformRequestDistribution} } // NewNormalRequestLatency creates a latency for requests with normal // distribution. Example delay around 1s with +/-120ms. // -// * -> normalRequestLatency("1s", "120ms") -> "https://www.example.org"; -// +// r: * -> normalRequestLatency("1s", "120ms") -> "https://www.example.org"; func NewNormalRequestLatency() filters.Spec { return &jitter{typ: normalRequestDistribution} } // NewUniformResponseLatency creates a latency for responses with uniform // distribution. Example delay around 1s with +/-120ms. // -// * -> uniformRequestLatency("1s", "120ms") -> "https://www.example.org"; -// +// r: * -> uniformRequestLatency("1s", "120ms") -> "https://www.example.org"; func NewUniformResponseLatency() filters.Spec { return &jitter{typ: uniformResponseDistribution} } // NewNormalResponseLatency creates a latency for responses with normal // distribution. Example delay around 1s with +/-120ms. // -// * -> normalRequestLatency("1s", "120ms") -> "https://www.example.org"; -// +// r: * -> normalRequestLatency("1s", "120ms") -> "https://www.example.org"; func NewNormalResponseLatency() filters.Spec { return &jitter{typ: normalResponseDistribution} } func (r *random) Name() string { return filters.RandomContentName } diff --git a/filters/doc.go b/filters/doc.go index 7ddba95ac0..47f3c54f73 100644 --- a/filters/doc.go +++ b/filters/doc.go @@ -20,8 +20,7 @@ Filters are used to augment both the inbound request's attributes before forwarding it to the route endpoint, and the outbound response's attributes before returning it to the original client. - -Filter Specification and Filter Instances +# Filter Specification and Filter Instances Filter implementations are based on filter specifications that provide a filter name and a 'factory' method to create filter instances. The filter name @@ -31,8 +30,7 @@ route. Filter instances are created while the route definitions are parsed and initialized, based on the specifications stored in the filter registry. Different filter instances can be created with different parameters. - -Filtering and FilterContext +# Filtering and FilterContext Once a route is identified during request processing, a context object is created that is unique to the request, holding the current request, the @@ -43,8 +41,7 @@ Each filter in a route is called twice, once for the request in the order of their position in the route definition, and once for the response in reverse order. - -Handling Requests with Filters +# Handling Requests with Filters Filters can handle the requests themselves, meaning that they can set the response status, headers and send any particular response body. In this case, diff --git a/filters/flowid/doc.go b/filters/flowid/doc.go index 238c93200e..5d8cdd94a3 100644 --- a/filters/flowid/doc.go +++ b/filters/flowid/doc.go @@ -6,8 +6,7 @@ Flow Ids let you correlate router logs for a given request against the upstream If your upstream application makes other requests to other services it can provide the same Flow ID value so that all of those logs can be correlated. - -How It Works +# How It Works Skipper generates a unique Flow Id for every HTTP request that it receives. The Flow ID is then passed to your upstream application as an HTTP header called X-Flow-Id. @@ -18,24 +17,24 @@ not a valid flow id it is ignored and a new flow id is generated, overwriting th Any other string used for this parameter is ignored and trigger the same, default, behavior - to ignore any existing X-Flow-Id header. -Generators +# Generators The Flow ID generation can follow any format. Skipper provides two Generator implementations - Standard and ULID. They offer different performance and options and you can choose which one you prefer. -Standard Flow IDs +# Standard Flow IDs The Standard Generator uses a base 64 alphabet and can be configured to generate flow IDs with length between 8 and 64 chars. It is very fast for small length FlowIDs and uses a system shared entropy source. It is safe for concurrent use. -ULID Flow IDs +# ULID Flow IDs The ULID Generator relies on the great work from https://github.com/alizain/ulid and https://github.com/oklog/ulid. It generates 26 char long Universally Unique Lexicographically Sortable IDs. It is very fast and it's also safe for concurrent use. -Programmatic Usage +# Programmatic Usage To create a specification of the FlowID filter you can either create the default specification, which uses the Standard Generator with a length of 16 chars as default or provide your own Generator instance. @@ -54,7 +53,7 @@ Custom spec with your own Generator implementation myCustomGenerator := newCustomGenerator(arg1, arg2) NewWithGenerator(myCustomGenerator) -Routing Usage +# Routing Usage The filter can be used with many different combinations of parameters. It can also be used without any parameter, using defaults @@ -72,9 +71,9 @@ Reuse existing flow id With a single string parameter with the value "reuse", the filter will accept an existing X-Flow-Id header, if it's present in the request. If it's invalid, a new one is generated and the header is overwritten. -Some Benchmarks +# Some Benchmarks -Built-In Flow ID Generator +# Built-In Flow ID Generator To decide upon which hashing mechanism to use we tested some versions of UUID v1 - v4 and some other implementations. The results are as follow: @@ -134,11 +133,9 @@ following results: BenchmarkFlowIdStandardGeneratorInParallel/32-8 2000000 918 ns/op 64 B/op 2 allocs/op BenchmarkFlowIdStandardGeneratorInParallel/64-8 1000000 1576 ns/op 128 B/op 2 allocs/op - ULID Flow ID Generator BenchmarkFlowIdULIDGenerator/Std-8 10000000 194 ns/op 48 B/op 2 allocs/op BenchmarkFlowIdULIDGeneratorInParallel-8 5000000 380 ns/op 48 B/op 2 allocs/op - */ package flowid diff --git a/filters/log/log.go b/filters/log/log.go index 063ea8fb43..59995b5613 100644 --- a/filters/log/log.go +++ b/filters/log/log.go @@ -111,7 +111,7 @@ func (tb *teeBody) Write(b []byte) (int, error) { // maxAuditBody attribute to limit the size of the log. It will use // os.Stderr as writer for the output of the log entries. // -// spec := NewAuditLog(1024) +// spec := NewAuditLog(1024) func NewAuditLog(maxAuditBody int) filters.Spec { return &auditLog{ writer: os.Stderr, diff --git a/filters/ratelimit/leakybucket.go b/filters/ratelimit/leakybucket.go index e674899aeb..fe1278d4e3 100644 --- a/filters/ratelimit/leakybucket.go +++ b/filters/ratelimit/leakybucket.go @@ -35,8 +35,7 @@ type leakyBucketFilter struct { // // Example to allow each unique Authorization header once in five seconds: // -// clusterLeakyBucketRatelimit("auth-${request.header.Authorization}", 1, "5s", 2, 1) -// +// clusterLeakyBucketRatelimit("auth-${request.header.Authorization}", 1, "5s", 2, 1) func NewClusterLeakyBucketRatelimit(registry *ratelimit.Registry) filters.Spec { return &leakyBucketSpec{ create: func(capacity int, emission time.Duration) leakyBucket { diff --git a/filters/ratelimit/ratelimit.go b/filters/ratelimit/ratelimit.go index e5c9bcb979..e47549972b 100644 --- a/filters/ratelimit/ratelimit.go +++ b/filters/ratelimit/ratelimit.go @@ -77,15 +77,15 @@ func NewLocalRatelimit(provider RatelimitProvider) filters.Spec { // // Example: // -// backendHealthcheck: Path("/healthcheck") -// -> clientRatelimit(20, "1m") -// -> "https://foo.backend.net"; +// backendHealthcheck: Path("/healthcheck") +// -> clientRatelimit(20, "1m") +// -> "https://foo.backend.net"; // // Example rate limit per Authorization Header: // -// login: Path("/login") -// -> clientRatelimit(3, "1m", "Authorization") -// -> "https://login.backend.net"; +// login: Path("/login") +// -> clientRatelimit(3, "1m", "Authorization") +// -> "https://login.backend.net"; func NewClientRatelimit(provider RatelimitProvider) filters.Spec { return &spec{typ: ratelimit.ClientRatelimit, provider: provider, filterName: filters.ClientRatelimitName} } @@ -96,18 +96,17 @@ func NewClientRatelimit(provider RatelimitProvider) filters.Spec { // // Example: // -// backendHealthcheck: Path("/healthcheck") -// -> ratelimit(20, "1s") -// -> "https://foo.backend.net"; -// +// backendHealthcheck: Path("/healthcheck") +// -> ratelimit(20, "1s") +// -> "https://foo.backend.net"; // // Optionally a custom response status code can be provided as an argument (default is 429). // // Example: // -// backendHealthcheck: Path("/healthcheck") -// -> ratelimit(20, "1s", 503) -// -> "https://foo.backend.net"; +// backendHealthcheck: Path("/healthcheck") +// -> ratelimit(20, "1s", 503) +// -> "https://foo.backend.net"; func NewRatelimit(provider RatelimitProvider) filters.Spec { return &spec{typ: ratelimit.ServiceRatelimit, provider: provider, filterName: filters.RatelimitName} } @@ -119,18 +118,17 @@ func NewRatelimit(provider RatelimitProvider) filters.Spec { // // Example: // -// backendHealthcheck: Path("/healthcheck") -// -> clusterRatelimit("groupA", 200, "1m") -// -> "https://foo.backend.net"; -// +// backendHealthcheck: Path("/healthcheck") +// -> clusterRatelimit("groupA", 200, "1m") +// -> "https://foo.backend.net"; // // Optionally a custom response status code can be provided as an argument (default is 429). // // Example: // -// backendHealthcheck: Path("/healthcheck") -// -> clusterRatelimit("groupA", 200, "1m", 503) -// -> "https://foo.backend.net"; +// backendHealthcheck: Path("/healthcheck") +// -> clusterRatelimit("groupA", 200, "1m", 503) +// -> "https://foo.backend.net"; func NewClusterRateLimit(provider RatelimitProvider) filters.Spec { return NewShardedClusterRateLimit(provider, 1) } @@ -152,9 +150,9 @@ func NewShardedClusterRateLimit(provider RatelimitProvider, maxGroupShards int) // // Example: // -// backendHealthcheck: Path("/login") -// -> clusterClientRatelimit("groupB", 20, "1h") -// -> "https://foo.backend.net"; +// backendHealthcheck: Path("/login") +// -> clusterClientRatelimit("groupB", 20, "1h") +// -> "https://foo.backend.net"; // // The above example would limit access to "/login" if, the client did // more than 20 requests within the last hour to this route across all @@ -166,10 +164,9 @@ func NewShardedClusterRateLimit(provider RatelimitProvider, maxGroupShards int) // // Example: // -// backendHealthcheck: Path("/login") -// -> clusterClientRatelimit("groupC", 20, "1h", "Authorization") -// -> "https://foo.backend.net"; -// +// backendHealthcheck: Path("/login") +// -> clusterClientRatelimit("groupC", 20, "1h", "Authorization") +// -> "https://foo.backend.net"; func NewClusterClientRateLimit(provider RatelimitProvider) filters.Spec { return &spec{typ: ratelimit.ClusterClientRatelimit, provider: provider, filterName: filters.ClusterClientRatelimitName} } @@ -178,9 +175,9 @@ func NewClusterClientRateLimit(provider RatelimitProvider) filters.Spec { // // Example: // -// backendHealthcheck: Path("/healthcheck") -// -> disableRatelimit() -// -> "https://foo.backend.net"; +// backendHealthcheck: Path("/healthcheck") +// -> disableRatelimit() +// -> "https://foo.backend.net"; func NewDisableRatelimit(provider RatelimitProvider) filters.Spec { return &spec{typ: ratelimit.DisableRatelimit, provider: provider, filterName: filters.DisableRatelimitName} } diff --git a/filters/rfc/rfc.go b/filters/rfc/rfc.go index 501c4a6e99..3450c0b2ea 100644 --- a/filters/rfc/rfc.go +++ b/filters/rfc/rfc.go @@ -21,7 +21,6 @@ type path struct{} // that they are encoded in the raw path. // // See also the PatchPath documentation in the rfc package. -// func NewPath() filters.Spec { return path{} } func (p path) Name() string { return filters.RfcPathName } @@ -39,7 +38,6 @@ type host struct{} // removes a trailing dot in the host header. // // See also the PatchHost documentation in the rfc package. -// func NewHost() filters.Spec { return host{} } func (host) Name() string { return filters.RfcHostName } diff --git a/filters/scheduler/doc.go b/filters/scheduler/doc.go index 4ce4967b07..18c92e51ea 100644 --- a/filters/scheduler/doc.go +++ b/filters/scheduler/doc.go @@ -33,5 +33,4 @@ // // - 502, if it can not get a request from data structure fast enough // - 503, if the data structure is full and reached its boundary -// package scheduler diff --git a/filters/scheduler/lifo.go b/filters/scheduler/lifo.go index 59de5290e3..dadca42c5c 100644 --- a/filters/scheduler/lifo.go +++ b/filters/scheduler/lifo.go @@ -163,7 +163,6 @@ func (*lifoGroupSpec) Name() string { return filters.LifoGroupName } // one of them will be used as the source for the applied settings, if there // is accidentally a difference between the settings in the same group, a // warning will be logged. -// func (*lifoGroupSpec) CreateFilter(args []interface{}) (filters.Filter, error) { if len(args) < 1 || len(args) > 4 { return nil, filters.ErrInvalidFilterParameters diff --git a/filters/sed/doc.go b/filters/sed/doc.go index a1323407aa..7d20ca135b 100644 --- a/filters/sed/doc.go +++ b/filters/sed/doc.go @@ -1,7 +1,7 @@ /* Package sed provides stream editor filters for request and response payload. -Filter sed +# Filter sed Example: @@ -31,7 +31,7 @@ The filter uses the go regular expression implementation: https://github.com/google/re2/wiki/Syntax . Due to the streaming nature, matches with zero length are ignored. -Memory handling and limitations +# Memory handling and limitations In order to avoid unbound buffering of unprocessed data, the sed* filters need to apply some limitations. Some patterns, e.g. `.*` would allow to match the complete @@ -55,7 +55,7 @@ use the delimited variant of the filters, e.g. for line based editing. If the max buffer handling is set to "abort", then the stream editing is stopped and the rest of the payload is dropped. -Filter sedDelim +# Filter sedDelim Like sed(), but it expects an additional argument, before the optional max buffer size argument, that is used to delimit chunks to be processed at once. The pattern @@ -64,13 +64,13 @@ delimiter, and matches across the chunk boundaries are not considered. editorRoute: * -> sedDelim("foo", "bar", "\n") -> "https://www.example.org"; -Filter sedRequest +# Filter sedRequest Like sed(), but for the request content. editorRoute: * -> sedRequest("foo", "bar") -> "https://www.example.org"; -Filter sedRequestDelim +# Filter sedRequestDelim Like sedDelim(), but for the request content. diff --git a/filters/sed/editor.go b/filters/sed/editor.go index bde6f04b31..bd5c525216 100644 --- a/filters/sed/editor.go +++ b/filters/sed/editor.go @@ -50,7 +50,6 @@ const ( // When the editor is closed, it doesn't read anymore from the input or return any // buffered data. If the input implements io.Closer, closing the editor closes the // input, too. -// type editor struct { // init: input io.Reader diff --git a/filters/serve/serve.go b/filters/serve/serve.go index 0d3830e7e6..cac6ce75b4 100644 --- a/filters/serve/serve.go +++ b/filters/serve/serve.go @@ -28,12 +28,11 @@ type pipedResponse struct { // // Example, a simple file server: // -// var handler = http.StripPrefix(webRoot, http.FileServer(http.Dir(root))) -// -// func (f *myFilter) Request(ctx filters.FilterContext) { -// serve.ServeHTTP(ctx, handler) -// } +// var handler = http.StripPrefix(webRoot, http.FileServer(http.Dir(root))) // +// func (f *myFilter) Request(ctx filters.FilterContext) { +// serve.ServeHTTP(ctx, handler) +// } func ServeHTTP(ctx filters.FilterContext, h http.Handler) { rsp := &http.Response{Header: make(http.Header)} r, w := io.Pipe() diff --git a/filters/shedder/admission.go b/filters/shedder/admission.go index af14d48ea5..37e328df9e 100644 --- a/filters/shedder/admission.go +++ b/filters/shedder/admission.go @@ -214,14 +214,16 @@ func (*AdmissionControlSpec) Name() string { return filters.AdmissionControlName // CreateFilter creates a new admissionControl filter with passed configuration: // -// admissionControl(metricSuffix, mode, d, windowSize, minRps, successThreshold, maxRejectProbability, exponent) -// admissionControl("$app", "active", "1s", 5, 10, 0.1, 0.95, 0.5) +// admissionControl(metricSuffix, mode, d, windowSize, minRps, successThreshold, maxRejectProbability, exponent) +// admissionControl("$app", "active", "1s", 5, 10, 0.1, 0.95, 0.5) // // metricSuffix is the suffix key to expose reject counter, should be unique by filter instance // mode is one of "active", "inactive", "logInactive" -// active will reject traffic -// inactive will never reject traffic -// logInactive will not reject traffic, but log to debug filter settings +// +// active will reject traffic +// inactive will never reject traffic +// logInactive will not reject traffic, but log to debug filter settings +// // windowSize is within [minWindowSize, maxWindowSize] // minRps threshold that needs to be reached such that the filter will apply // successThreshold is within (0,1] and sets the lowest request success rate at which the filter will not reject requests. diff --git a/filters/tee/doc.go b/filters/tee/doc.go index ccfb66fe22..8ff198f8fd 100644 --- a/filters/tee/doc.go +++ b/filters/tee/doc.go @@ -6,7 +6,7 @@ backend of the route. Example: - * -> tee("https://audit-logging.example.org") -> "https://foo.example.org" + r: * -> tee("https://audit-logging.example.org") -> "https://foo.example.org"; This will send an identical request for foo.example.org to audit-logging.example.org. Another use case could be using it for benchmarking a new backend with some real traffic diff --git a/filters/tee/tee.go b/filters/tee/tee.go index 53fac8c18b..9d98a13557 100644 --- a/filters/tee/tee.go +++ b/filters/tee/tee.go @@ -113,7 +113,6 @@ func NewTeeNoFollow() filters.Spec { // Returns a new tee filter Spec, whose instances execute the exact same Request against a shadow backend with given // options. Available options are nofollow and Timeout for http client. // parameters: shadow backend url, optional - the path(as a regexp) to match and the replacement string. -// func WithOptions(o Options) filters.Spec { return &teeSpec{options: o} } @@ -141,7 +140,7 @@ func (tt *teeTie) Read(b []byte) (int, error) { func (tt *teeTie) Close() error { return nil } -//We do not touch response at all +// We do not touch response at all func (r *tee) Response(filters.FilterContext) {} // Request is copied and then modified to adopt changes in new backend diff --git a/filters/tracing/spanname.go b/filters/tracing/spanname.go index 5c56fbd5a3..5c7db0f217 100644 --- a/filters/tracing/spanname.go +++ b/filters/tracing/spanname.go @@ -23,8 +23,7 @@ type filter struct { // NewSpanName creates a filter spec for setting the name of the outgoing span. (By default "proxy".) // -// tracingSpanName("example-operation") -// +// tracingSpanName("example-operation") func NewSpanName() filters.Spec { return &spec{} } diff --git a/loadbalancer/doc.go b/loadbalancer/doc.go index cd63e2bc32..4c3387afd3 100644 --- a/loadbalancer/doc.go +++ b/loadbalancer/doc.go @@ -3,21 +3,21 @@ Package loadbalancer implements load balancer algorithms that are applied by the roundRobin Algorithm - The roundRobin algorithm does proxy requests round robin to - backend endpoints. It has a mutex to update the index and will - start at a random index + The roundRobin algorithm does proxy requests round robin to + backend endpoints. It has a mutex to update the index and will + start at a random index random Algorithm - The random algorithm does proxy requests to random backend - endpoints. + The random algorithm does proxy requests to random backend + endpoints. consistentHash Algorithm - The consistentHash algorithm choose backend endpoints by hashing - client data with hash function fnv.New32. The client data is the - client IP, which will be looked up from X-Forwarded-For header - with remote IP as the fallback. + The consistentHash algorithm choose backend endpoints by hashing + client data with hash function fnv.New32. The client data is the + client IP, which will be looked up from X-Forwarded-For header + with remote IP as the fallback. powerOfRandomNChoices Algorithm @@ -31,12 +31,10 @@ applications that require a certain amount of warm-up time. Eskip example: - - r1: * -> ; - r2: * -> ; - r3: * -> ; - r4: * -> ; - + r1: * -> ; + r2: * -> ; + r3: * -> ; + r4: * -> ; Package loadbalancer also implements health checking of pool members for a group of routes, if backend calls are reported to the loadbalancer. @@ -45,19 +43,19 @@ Based on https://landing.google.com/sre/book/chapters/load-balancing-datacenter. Healthy (healthy) - The backend task has initialized correctly and is processing - requests. + The backend task has initialized correctly and is processing + requests. Refusing connections (dead) - The backend task is unresponsive. This can happen because the - task is starting up or shutting down, or because the backend is - in an abnormal state (though it would be rare for a backend to - stop listening on its port if it is not shutting down). + The backend task is unresponsive. This can happen because the + task is starting up or shutting down, or because the backend is + in an abnormal state (though it would be rare for a backend to + stop listening on its port if it is not shutting down). Lame duck (unhealthy) - The backend task is listening on its port and can serve, but is - explicitly asking clients to stop sending requests. + The backend task is listening on its port and can serve, but is + explicitly asking clients to stop sending requests. */ package loadbalancer diff --git a/logging/doc.go b/logging/doc.go index 76f5cbae85..0c88a83eb3 100644 --- a/logging/doc.go +++ b/logging/doc.go @@ -2,7 +2,7 @@ Package logging implements application log instrumentation and Apache combined access log. -Application Log +# Application Log The application log uses the logrus package: @@ -11,11 +11,11 @@ https://github.com/sirupsen/logrus To send messages to the application log, import this package and use its methods. Example: - import log "github.com/sirupsen/logrus" + import log "github.com/sirupsen/logrus" - func doSomething() { - log.Errorf("nothing to do") - } + func doSomething() { + log.Errorf("nothing to do") + } During startup initialization, it is possible to redirect the log output from the default /dev/stderr to another file, and to set a common @@ -23,7 +23,7 @@ prefix for each log entry. Setting the prefix may be a good idea when the access log is enabled and its output is the same as the one of the application log, to make it easier to split the output for diagnostics. -Access Log +# Access Log The access log prints HTTP access information in the Apache combined access log format. To output entries, use the logging.Access method. @@ -39,7 +39,7 @@ so filters can add more data to the access log files. When using the feature, an contained in a map[string]interface{} in the StateBag's key will be passed to the logger. This is specially useful when more request/response information is needed when logging. -Output Files +# Output Files To set a custom file output for the application log or the access log is currently not recommended in production environment, because neither the diff --git a/metrics/doc.go b/metrics/doc.go index 6d9ee4045b..f814cb2cd8 100644 --- a/metrics/doc.go +++ b/metrics/doc.go @@ -19,7 +19,7 @@ The collected metrics include detailed information about Skipper's relevant proc looking up routes, filters (aggregate and individual), backend communication and forwarding the response to the client. -Options +# Options To enable metrics, it needs to be initialized with a Listener address. In this case, Skipper will start an additional http listener, where the current metrics values can be downloaded. @@ -30,7 +30,7 @@ metrics and other systems if you aggregate them later in some monitoring system. You can also enable some Go garbage collector and runtime metrics using EnableDebugGcMetrics and EnableRuntimeMetrics, respectively. -REST API +# REST API This listener accepts GET requests on the /metrics endpoint like any other REST api. A request to "/metrics" should return a JSON response including all the collected metrics if CodaHale format is used, or in Plain text if Prometheus diff --git a/net/net.go b/net/net.go index 7af882a810..d6af53b04c 100644 --- a/net/net.go +++ b/net/net.go @@ -30,7 +30,7 @@ func parse(addr string) net.IP { // // Example: // -// X-Forwarded-For: client, proxy1, proxy2 +// X-Forwarded-For: client, proxy1, proxy2 func RemoteHost(r *http.Request) net.IP { ffs := r.Header.Get("X-Forwarded-For") ff, _, _ := strings.Cut(ffs, ",") @@ -48,7 +48,7 @@ func RemoteHost(r *http.Request) net.IP { // // Example: // -// X-Forwarded-For: ip-address-1, ip-address-2, client-ip-address +// X-Forwarded-For: ip-address-1, ip-address-2, client-ip-address func RemoteHostFromLast(r *http.Request) net.IP { ffs := r.Header.Get("X-Forwarded-For") ffa := strings.Split(ffs, ",") diff --git a/oauth/oauth.go b/oauth/oauth.go index 536c8aeb82..a190b236a8 100644 --- a/oauth/oauth.go +++ b/oauth/oauth.go @@ -22,11 +22,11 @@ file system under the directory passed in with the credentialsDir argument. The structure of the client credentials document: - {"client_id": "testclientid", "client_secret": "testsecret"} + {"client_id": "testclientid", "client_secret": "testsecret"} The structure of the user credentials document: - {"application_username": "testusername", "application_password": "testpassword"} + {"application_username": "testusername", "application_password": "testpassword"} The GetToken method ignores the expiration date and makes a new request to the OAuth2 service on every call, so storing the token, if necessary, is the diff --git a/predicates/auth/jwt.go b/predicates/auth/jwt.go index 06e85c0c8a..8ede69dd98 100644 --- a/predicates/auth/jwt.go +++ b/predicates/auth/jwt.go @@ -7,12 +7,12 @@ part of a JWT token, for example based on the issuer. Examples: - // one key value pair has to match - example1: JWTPayloadAnyKV("iss", "https://accounts.google.com", "email", "skipper-router@googlegroups.com") - -> "http://example.org/"; - // all key value pairs have to match - example2: * && JWTPayloadAllKV("iss", "https://accounts.google.com", "email", "skipper-router@googlegroups.com") - -> "http://example.org/"; + // one key value pair has to match + example1: JWTPayloadAnyKV("iss", "https://accounts.google.com", "email", "skipper-router@googlegroups.com") + -> "http://example.org/"; + // all key value pairs have to match + example2: * && JWTPayloadAllKV("iss", "https://accounts.google.com", "email", "skipper-router@googlegroups.com") + -> "http://example.org/"; */ package auth diff --git a/predicates/cookie/cookie.go b/predicates/cookie/cookie.go index 734077d43e..281e4739ce 100644 --- a/predicates/cookie/cookie.go +++ b/predicates/cookie/cookie.go @@ -31,8 +31,7 @@ type ( // // Eskip example: // -// Cookie("tcial", /^enabled$/) -> "https://www.example.org"; -// +// Cookie("tcial", /^enabled$/) -> "https://www.example.org"; func New() routing.PredicateSpec { return &spec{} } func (s *spec) Name() string { return predicates.CookieName } diff --git a/predicates/forwarded/forwarded.go b/predicates/forwarded/forwarded.go index 796e3c0f8e..6c1489c340 100644 --- a/predicates/forwarded/forwarded.go +++ b/predicates/forwarded/forwarded.go @@ -7,14 +7,14 @@ https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded Examples: - // only match requests to "example.com" - example1: ForwardedHost("example.com") -> "http://example.org"; + // only match requests to "example.com" + example1: ForwardedHost("example.com") -> "http://example.org"; - // only match requests to http - example2: ForwardedProtocol("http") -> "http://example.org"; + // only match requests to http + example2: ForwardedProtocol("http") -> "http://example.org"; - // only match requests to https - example3: ForwardedProtocol("https") -> "http://example.org"; + // only match requests to https + example3: ForwardedProtocol("https") -> "http://example.org"; */ package forwarded diff --git a/predicates/interval/interval.go b/predicates/interval/interval.go index 96b3abc53d..a4371d60a4 100644 --- a/predicates/interval/interval.go +++ b/predicates/interval/interval.go @@ -4,10 +4,10 @@ only during some period of time. Package includes three predicates: Between, Before and After. All predicates can be created using the date represented as: -- a string in RFC3339 format (see https://golang.org/pkg/time/#pkg-constants) -- a string in RFC3339 format without numeric timezone offset and a location name (see https://golang.org/pkg/time/#LoadLocation) -- an int64 or float64 number corresponding to the given Unix time in seconds since January 1, 1970 UTC. - float64 number will be converted into int64 number. + - a string in RFC3339 format (see https://golang.org/pkg/time/#pkg-constants) + - a string in RFC3339 format without numeric timezone offset and a location name (see https://golang.org/pkg/time/#LoadLocation) + - an int64 or float64 number corresponding to the given Unix time in seconds since January 1, 1970 UTC. + float64 number will be converted into int64 number. Between predicate matches only if current date is inside the specified range of dates. Between predicate requires two dates to be constructed. @@ -34,7 +34,6 @@ Examples: example7: Path("/zalando") && Between("2021-02-18T00:00:00", "2021-02-18T01:00:00", "Europe/Berlin") -> "https://www.zalando.de"; example8: Path("/zalando") && Before("2021-02-18T00:00:00", "Europe/Berlin") -> "https://www.zalando.de"; example9: Path("/zalando") && After("2021-02-18T00:00:00", "Europe/Berlin") -> "https://www.zalando.de"; - */ package interval diff --git a/predicates/methods/methods.go b/predicates/methods/methods.go index 18a4914742..13df4d4797 100644 --- a/predicates/methods/methods.go +++ b/predicates/methods/methods.go @@ -1,18 +1,16 @@ /* - Package methods implements a custom predicate to match routes based on the http method in request -It supports multiple http methods, with case insensitive input +# It supports multiple http methods, with case insensitive input Examples: - // matches GET request - example1: Methods("GET") -> "http://example.org"; - - // matches GET or POST request - example1: Methods("GET", "post") -> "http://example.org"; + // matches GET request + example1: Methods("GET") -> "http://example.org"; + // matches GET or POST request + example1: Methods("GET", "post") -> "http://example.org"; */ package methods diff --git a/predicates/query/query.go b/predicates/query/query.go index ec047f56ad..c02c528e2b 100644 --- a/predicates/query/query.go +++ b/predicates/query/query.go @@ -7,22 +7,21 @@ query params value match to a given regular exp Examples: - // Checking existence of a query param - // matches http://example.org?bb=a&query=withvalue - example1: QueryParam("query") -> "http://example.org"; + // Checking existence of a query param + // matches http://example.org?bb=a&query=withvalue + example1: QueryParam("query") -> "http://example.org"; - // Even a query param without a value - // matches http://example.org?bb=a&query= - example1: QueryParam("query") -> "http://example.org"; + // Even a query param without a value + // matches http://example.org?bb=a&query= + example1: QueryParam("query") -> "http://example.org"; - // matches with regexp - // matches http://example.org?bb=a&query=example - example1: QueryParam("query", "^example$") -> "http://example.org"; - - // matches with regexp and multiple values of query param - // matches http://example.org?bb=a&query=testing&query=example - example1: QueryParam("query", "^example$") -> "http://example.org"; + // matches with regexp + // matches http://example.org?bb=a&query=example + example1: QueryParam("query", "^example$") -> "http://example.org"; + // matches with regexp and multiple values of query param + // matches http://example.org?bb=a&query=testing&query=example + example1: QueryParam("query", "^example$") -> "http://example.org"; */ package query diff --git a/predicates/source/source.go b/predicates/source/source.go index cff90e49df..736fe06208 100644 --- a/predicates/source/source.go +++ b/predicates/source/source.go @@ -25,17 +25,17 @@ the X-Forwarded-For header and SourceFromLast() as last entry. Examples: - // only match requests from 1.2.3.4 - example1: Source("1.2.3.4") -> "http://example.org"; + // only match requests from 1.2.3.4 + example1: Source("1.2.3.4") -> "http://example.org"; - // only match requests from 1.2.3.0 - 1.2.3.255 - example2: Source("1.2.3.0/24") -> "http://example.org"; + // only match requests from 1.2.3.0 - 1.2.3.255 + example2: Source("1.2.3.0/24") -> "http://example.org"; - // only match requests from 1.2.3.4 and the 2.2.2.0/24 network - example3: Source("1.2.3.4", "2.2.2.0/24") -> "http://example.org"; + // only match requests from 1.2.3.4 and the 2.2.2.0/24 network + example3: Source("1.2.3.4", "2.2.2.0/24") -> "http://example.org"; - // same as example3, only match requests from 1.2.3.4 and the 2.2.2.0/24 network - example4: SourceFromLast("1.2.3.4", "2.2.2.0/24") -> "http://example.org"; + // same as example3, only match requests from 1.2.3.4 and the 2.2.2.0/24 network + example4: SourceFromLast("1.2.3.4", "2.2.2.0/24") -> "http://example.org"; */ package source diff --git a/predicates/traffic/traffic.go b/predicates/traffic/traffic.go index 8bfb1a66ab..16f90ef826 100644 --- a/predicates/traffic/traffic.go +++ b/predicates/traffic/traffic.go @@ -24,48 +24,47 @@ backend service. The below example, shows a possible eskip document used for green-blue deployments of APIS, which usually don't require stickiness: - // hit by 10% percent chance - v2: - Traffic(.1) -> - "https://api-test-green"; + // hit by 10% percent chance + v2: + Traffic(.1) -> + "https://api-test-green"; - // hit by remaining chance - v1: - "https://api-test-blue"; + // hit by remaining chance + v1: + "https://api-test-blue"; The below example, shows a possible eskip document with two, independent traffic controlled route sets, which uses session stickiness: - // hit by 5% percent chance - cartTest: - Traffic(.05, "cart-test", "test") && Path("/cart") -> - responseCookie("cart-test", "test") -> - "https://cart-test"; - - // hit by remaining chance - cart: - Path("/cart") -> - responseCookie("cart-test", "default") -> - "https://cart"; - - // hit by 15% percent chance - catalogTestA: - Traffic(.15, "catalog-test", "A") -> - responseCookie("catalog-test", "A") -> - "https://catalog-test-a"; - - // hit by 30% percent chance - catalogTestB: - Traffic(.3, "catalog-test", "B") -> - responseCookie("catalog-test", "B") -> - "https://catalog-test-b"; - - // hit by remaining chance - catalog: - * -> - responseCookie("catalog-test", "default") -> - "https://catalog"; - + // hit by 5% percent chance + cartTest: + Traffic(.05, "cart-test", "test") && Path("/cart") -> + responseCookie("cart-test", "test") -> + "https://cart-test"; + + // hit by remaining chance + cart: + Path("/cart") -> + responseCookie("cart-test", "default") -> + "https://cart"; + + // hit by 15% percent chance + catalogTestA: + Traffic(.15, "catalog-test", "A") -> + responseCookie("catalog-test", "A") -> + "https://catalog-test-a"; + + // hit by 30% percent chance + catalogTestB: + Traffic(.3, "catalog-test", "B") -> + responseCookie("catalog-test", "B") -> + "https://catalog-test-b"; + + // hit by remaining chance + catalog: + * -> + responseCookie("catalog-test", "default") -> + "https://catalog"; */ package traffic diff --git a/proxy/doc.go b/proxy/doc.go index 604fde2ab9..9832eb53e2 100644 --- a/proxy/doc.go +++ b/proxy/doc.go @@ -23,8 +23,7 @@ forwarding it to the route endpoint. It may also mean to handle the request internally if it is a 'shunt' route or to continue routing further in case of 'loopback'. - -Proxy Mechanism +# Proxy Mechanism 1. route matching: @@ -33,7 +32,6 @@ in skipper/routing. The result may be a route, which will be used for forwarding or handling the request, or nil, in which case the proxy responds with a configured http status code (defaults to 404). - 2. upstream request augmentation: In case of a matched route, the request handling method of all filters @@ -49,7 +47,6 @@ will prevent the request from reaching the route endpoint. The filters that are defined in the route after the one that broke the chain will never handle the request. - 3.a upstream request: The incoming and augmented request is mapped to an outgoing request and @@ -57,7 +54,6 @@ executed, addressing the endpoint defined by the current route. If a filter chain was broken by some filter this step is skipped. - 3.b shunt: In case the route is a 'shunt', an empty response is created with @@ -74,7 +70,6 @@ limit is reached. In case of a `dynamic` route, the final target must be defined in a filter. - 4. downstream response augmentation: The response handling method of all the filters processed in step 2 @@ -87,7 +82,6 @@ request latest in this phase. It should set the status and response headers and write the response body, if any, to the writer in the filter context. - 5. response: In case none of the filters handled the request, the response @@ -95,8 +89,7 @@ properties, including the status and the headers, are mapped to the outgoing response writer, and the response body is streamed to it, with continuous flushing. - -Routing Rules +# Routing Rules The route matching is implemented in the skipper/routing package. The routing rules are not static, but they can be continuously updated by @@ -106,8 +99,7 @@ The only exceptions are the priority routes, that have not originated from the external data sources, and are tested against the requests before the general routing tree. - -Handling the Host header +# Handling the Host header The default behavior regarding the 'Host' header of the proxy requests is that the proxy ignores the value set in the incoming request. This @@ -126,8 +118,7 @@ To control the value of the outgoing 'Host' header, the `OutgoingHost()` and `SetOutgoingHost()` methods of the `FilterContext` need to be used instead of the `Request.Header` map. - -Circuit Breakers +# Circuit Breakers When configured, skipper can use circuit breakers for the backend requests. It asks the registry for a matching circuit breaker for @@ -138,8 +129,7 @@ current breaker, otherwise it reports a success. For details, see: https://godoc.org/github.com/zalando/skipper/circuit. - -Proxy Example +# Proxy Example The below example demonstrates creating a routing proxy as a standard http.Handler interface: diff --git a/ratelimit/doc.go b/ratelimit/doc.go index 96934b78dd..4e9e8f6fcb 100644 --- a/ratelimit/doc.go +++ b/ratelimit/doc.go @@ -5,24 +5,24 @@ It provides per process rate limiting. It can be configured globally, or based on routes. Rate limiting can be lookuped based on HTTP headers for example X-Forwarded-For or Authorization. -Lookuper Type - SameBucketLookuper +# Lookuper Type - SameBucketLookuper This lookuper will use a static string to point always to the same bucket. This means all requests are counted the same. -Lookuper Type - HeaderLookuper +# Lookuper Type - HeaderLookuper This lookuper will use the content of the the specified header to calculate rate limiting. -Lookuper Type - XForwardedForLookuper +# Lookuper Type - XForwardedForLookuper This lookuper will use the remote IP of the origin request to calculate rate limiting. If there is no such header it will use the remote IP of the request. This is the default Lookuper and may be the one most users want to use. -Usage +# Usage When imported as a package, the Registry can be used to hold the rate limiters and their settings. On a higher level, rate limiter settings @@ -33,37 +33,36 @@ The following command starts skipper with default X-Forwarded-For Lookuper, that will start to rate limit after 5 requests within 60s from the same client - % skipper -ratelimits type=client,max-hits=5,time-window=60s + % skipper -ratelimits type=client,max-hits=5,time-window=60s The following configuration will rate limit /foo after 2 requests within 90s from the same requester and all other requests after 20 requests within 60s from the same client - % cat ratelimit.eskip - foo: Path("/foo") -> clientRatelimit(2,"1m30s") -> "http://www.example.org/foo" - rest: * -> clientRatelimit(20,"1m") -> "http://www.example.net/" - % skipper -enable-ratelimits -routes-file=ratelimit.eskip + % cat ratelimit.eskip + foo: Path("/foo") -> clientRatelimit(2,"1m30s") -> "http://www.example.org/foo" + rest: * -> clientRatelimit(20,"1m") -> "http://www.example.net/" + % skipper -enable-ratelimits -routes-file=ratelimit.eskip The following configuration will rate limit requests after 100 requests within 1 minute with the same Authorization Header - % cat ratelimit-auth.eskip - all: * -> clientRatelimit(100,"1m","Authorization") -> "http://www.example.org/" - % skipper -enable-ratelimits -routes-file=ratelimit-auth.eskip + % cat ratelimit-auth.eskip + all: * -> clientRatelimit(100,"1m","Authorization") -> "http://www.example.org/" + % skipper -enable-ratelimits -routes-file=ratelimit-auth.eskip The following configuration will rate limit requests to /login after 10 requests summed across all skipper peers within one hour from the same requester. - % cat ratelimit.eskip - foo: Path("/login") -> clientRatelimit(10,"1h") -> "http://www.example.org/login" - rest: * -> "http://www.example.net/" - % skipper -enable-ratelimits -routes-file=ratelimit.eskip -enable-swarm - + % cat ratelimit.eskip + foo: Path("/login") -> clientRatelimit(10,"1h") -> "http://www.example.org/login" + rest: * -> "http://www.example.net/" + % skipper -enable-ratelimits -routes-file=ratelimit.eskip -enable-swarm Rate limiter settings can be applied globally via command line flags or within routing settings. -Settings - Type +# Settings - Type Defines the type of the rate limiter. There are types that only use local state information and others that use cluster information using @@ -82,23 +81,23 @@ tested, because of redis. Redis ring based cluster ratelimits should not create a significant memory footprint for skipper instances, but might create load to redis. -Settings - MaxHits +# Settings - MaxHits Defines the maximum number of requests per user within a TimeWindow. -Settings - TimeWindow +# Settings - TimeWindow Defines the time window until rate limits will be enforced, if maximum number of requests are exceeded. This is defined as a string representation of Go's time.Duration, e.g. 1m30s. -Settings - Lookuper +# Settings - Lookuper Defines an optional configuration to choose which Header should be used to group client requests. It accepts any header, for example "Authorization". -Settings - Group +# Settings - Group Defines the ratelimit group, which can be the same for different routes, if you want to have one ratelimiter spanning more than one @@ -106,7 +105,7 @@ route. Make sure your settings are the same for the whole group. In case of different settings for the same group the behavior is undefined and could toggle between different configurations. -HTTP Response +# HTTP Response In case of rate limiting, the HTTP response status will be 429 Too Many Requests and two headers will be set. @@ -122,13 +121,12 @@ request: Both are based on RFC 6585. -Registry +# Registry The active rate limiters are stored in a registry. They are created based on routes or command line flags. The registry synchronizes access to the shared rate limiters. A registry has default settings that it will apply and that it will use the disable rate limiter in case it's not defined in the configuration or not global enabled. - */ package ratelimit diff --git a/rfc/patchpath.go b/rfc/patchpath.go index 581238aadf..58d978f573 100644 --- a/rfc/patchpath.go +++ b/rfc/patchpath.go @@ -58,12 +58,11 @@ func unescape(seq []byte) (byte, bool) { // the stdlib url package, and there is no difference between the parsed and the raw path. // This basically means that the following code is correct: // -// req.URL.Path = rfc.PatchPath(req.URL.Path, req.URL.RawPath) +// req.URL.Path = rfc.PatchPath(req.URL.Path, req.URL.RawPath) // // Links: // - https://tools.ietf.org/html/rfc2616#section-3.2.3 and // - https://tools.ietf.org/html/rfc3986#section-2.2 -// func PatchPath(parsed, raw string) string { p, r := []byte(parsed), []byte(raw) patched := make([]byte, 0, len(r)) diff --git a/routing/doc.go b/routing/doc.go index 6f15aa8f8c..63e6e96c9e 100644 --- a/routing/doc.go +++ b/routing/doc.go @@ -2,8 +2,7 @@ Package routing implements matching of http requests to a continuously updatable set of skipper routes. - -Request Evaluation +# Request Evaluation 1. The path in the http request is used to find one or more matching route definitions in a lookup tree. @@ -21,8 +20,7 @@ expressions, use the go stdlib regexp, which uses re2: https://github.com/google/re2/wiki/Syntax - -Matching Conditions +# Matching Conditions The following types of conditions are supported in the route definitions. @@ -47,8 +45,7 @@ HeaderRegexp("Key", "^Value$"). must be present in the request and one of the associated values must match the expression. - -Wildcards +# Wildcards Path matching supports two kinds of wildcards: @@ -65,8 +62,7 @@ predicate, the name of this parameter will be "*". This makes the PathSubtree("/foo") predicate equivalent to having routes with Path("/foo"), Path("/foo/") and Path("/foo/**") predicates. - -Custom Predicates +# Custom Predicates It is possible to define custom route matching rules in the form of custom predicates. Custom predicates need to implement the PredicateSpec @@ -76,8 +72,7 @@ predicate is matched based on the path tree, the predicate receives the request object, and it returns true or false meaning that the request is a match or not. - -Data Clients +# Data Clients Routing definitions are not directly passed to the routing instance, but they are loaded from clients that implement the DataClient interface. diff --git a/routing/matcher.go b/routing/matcher.go index fb4fac2ed4..686fb8c40a 100644 --- a/routing/matcher.go +++ b/routing/matcher.go @@ -211,7 +211,6 @@ func normalizePath(r *Route) (string, error) { // Using a set of regular expressions shared in // the current generation to preserve the // compiled instances. -// func newLeaf(r *Route, rxs map[string]*regexp.Regexp) (*leafMatcher, error) { hostRxs, err := getCompiledRxs(rxs, r.HostRegexps) if err != nil { diff --git a/scheduler/scheduler.go b/scheduler/scheduler.go index 1be2a03634..dfd59a2693 100644 --- a/scheduler/scheduler.go +++ b/scheduler/scheduler.go @@ -201,7 +201,6 @@ type Options struct { // metrics. This goroutine is started when the first lifo filter is detected and returns // when the registry is closed. Individual metrics objects (keys) are used for each // lifo filter, and one for each lifo group defined by the lifoGroup filter. -// type Registry struct { options Options measuring bool diff --git a/secrets/readers.go b/secrets/readers.go index eee55d1594..72729f6aed 100644 --- a/secrets/readers.go +++ b/secrets/readers.go @@ -15,10 +15,10 @@ type SecretsReader interface { // StaticSecret implements SecretsReader interface. Example: // -// sec := []byte("mysecret") -// sss := StaticSecret(sec) -// b,_ := sss.GetSecret("") -// string(b) == sec // true +// sec := []byte("mysecret") +// sss := StaticSecret(sec) +// b,_ := sss.GetSecret("") +// string(b) == sec // true type StaticSecret []byte // GetSecret returns the static secret diff --git a/swarm/doc.go b/swarm/doc.go index 646e9481c6..c07c5057f1 100644 --- a/swarm/doc.go +++ b/swarm/doc.go @@ -22,9 +22,9 @@ http://www.cs.cornell.edu/~asdas/research/dsn02-SWIM.pdf. Quote from a nice overview https://prakhar.me/articles/swim/ - The SWIM or the Scalable Weakly-consistent Infection-style process - group Membership protocol is a protocol used for maintaining - membership amongst processes in a distributed system. + The SWIM or the Scalable Weakly-consistent Infection-style process + group Membership protocol is a protocol used for maintaining + membership amongst processes in a distributed system. While starting, Skipper will find its swarm peers through the Kubernetes API server. It will do that using a label selector query diff --git a/tracing/tracing.go b/tracing/tracing.go index 8d4831f63b..50b76c2c82 100644 --- a/tracing/tracing.go +++ b/tracing/tracing.go @@ -6,18 +6,18 @@ // The tracers, except for "noop", are built as Go Plugins. Note the warning from Go's // plugin.go: // -// // The plugin support is currently incomplete, only supports Linux, -// // and has known bugs. Please report any issues. +// // The plugin support is currently incomplete, only supports Linux, +// // and has known bugs. Please report any issues. // // All plugins must have a function named "InitTracer" with the following signature // -// func([]string) (opentracing.Tracer, error) +// func([]string) (opentracing.Tracer, error) // // The parameters passed are all arguments for the plugin, i.e. everything after the first // word from skipper's -opentracing parameter. E.g. when the -opentracing parameter is // "mytracer foo=bar token=xxx somename=bla:3" the "mytracer" plugin will receive // -// []string{"foo=bar", "token=xxx", "somename=bla:3"} +// []string{"foo=bar", "token=xxx", "somename=bla:3"} // // as arguments. // @@ -25,24 +25,24 @@ // // An example plugin looks like // -// package main +// package main // -// import ( -// basic "github.com/opentracing/basictracer-go" -// opentracing "github.com/opentracing/opentracing-go" -// ) +// import ( +// basic "github.com/opentracing/basictracer-go" +// opentracing "github.com/opentracing/opentracing-go" +// ) // -// func InitTracer(opts []string) (opentracing.Tracer, error) { -// return basic.NewTracerWithOptions(basic.Options{ -// Recorder: basic.NewInMemoryRecorder(), -// ShouldSample: func(traceID uint64) bool { return traceID%64 == 0 }, -// MaxLogsPerSpan: 25, -// }), nil -// } +// func InitTracer(opts []string) (opentracing.Tracer, error) { +// return basic.NewTracerWithOptions(basic.Options{ +// Recorder: basic.NewInMemoryRecorder(), +// ShouldSample: func(traceID uint64) bool { return traceID%64 == 0 }, +// MaxLogsPerSpan: 25, +// }), nil +// } // // This should be built with // -// go build -buildmode=plugin -o basic.so ./basic/basic.go +// go build -buildmode=plugin -o basic.so ./basic/basic.go // // and copied to the given as -plugindir (by default, "./plugins"). //