diff --git a/CHANGELOG.md b/CHANGELOG.md index 14370b215..d3ad9e1f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,26 @@ +## [v2.4.11](https://github.com/traefik/traefik/tree/v2.4.11) (2021-07-15) +[All Commits](https://github.com/traefik/traefik/compare/v2.4.9...v2.4.11) + +**Bug fixes:** +- **[k8s,k8s/crd,k8s/ingress]** Disable ExternalName Services by default on Kubernetes providers ([#8261](https://github.com/traefik/traefik/pull/8261) by [dtomcej](https://github.com/dtomcej)) +- **[k8s,k8s/crd,k8s/ingress]** Fix: malformed Kubernetes resource names and references in tests ([#8226](https://github.com/traefik/traefik/pull/8226) by [rtribotte](https://github.com/rtribotte)) +- **[k8s,k8s/crd]** Disable Cross-Namespace by default for IngressRoute provider ([#8260](https://github.com/traefik/traefik/pull/8260) by [dtomcej](https://github.com/dtomcej)) +- **[logs,middleware]** Accesslog: support multiple values for a given header ([#8258](https://github.com/traefik/traefik/pull/8258) by [ldez](https://github.com/ldez)) +- **[logs]** Ignore http 1.0 request host missing errors ([#8252](https://github.com/traefik/traefik/pull/8252) by [dtomcej](https://github.com/dtomcej)) +- **[middleware]** Headers Middleware: support http.CloseNotifier interface ([#8238](https://github.com/traefik/traefik/pull/8238) by [dtomcej](https://github.com/dtomcej)) +- **[tls]** Detect certificates content modifications ([#8243](https://github.com/traefik/traefik/pull/8243) by [jbdoumenjou](https://github.com/jbdoumenjou)) + +**Documentation:** +- **[middleware,k8s]** Fix invalid subdomain ([#8212](https://github.com/traefik/traefik/pull/8212) by [WLun001](https://github.com/WLun001)) +- Add the list of available provider names ([#8225](https://github.com/traefik/traefik/pull/8225) by [WLun001](https://github.com/WLun001)) +- Fix maintainers-guidelines page title ([#8216](https://github.com/traefik/traefik/pull/8216) by [kubopanda](https://github.com/kubopanda)) +- Typos in contributing section ([#8215](https://github.com/traefik/traefik/pull/8215) by [kubopanda](https://github.com/kubopanda)) + +## [v2.4.10](https://github.com/traefik/traefik/tree/v2.4.10) (2021-07-13) +[All Commits](https://github.com/traefik/traefik/compare/v2.4.9...v2.4.10) + +Release canceled. + ## [v2.5.0-rc2](https://github.com/traefik/traefik/tree/v2.5.0-rc2) (2021-06-28) [All Commits](https://github.com/traefik/traefik/compare/v2.4.0-rc1...v2.5.0-rc2) diff --git a/Makefile b/Makefile index 7bb8e73de..7cb590cc4 100644 --- a/Makefile +++ b/Makefile @@ -96,6 +96,15 @@ test-integration: $(PRE_TARGET) $(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK),TEST_CONTAINER=1) ./script/make.sh generate binary test-integration TEST_HOST=1 ./script/make.sh test-integration +## Run the container integration tests +test-integration-container: $(PRE_TARGET) + $(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK),TEST_CONTAINER=1) ./script/make.sh generate binary test-integration + +## Run the host integration tests +test-integration-host: $(PRE_TARGET) + $(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK),TEST_CONTAINER=1) ./script/make.sh generate binary + TEST_HOST=1 ./script/make.sh test-integration + ## Validate code and docs validate-files: $(PRE_TARGET) $(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate validate-lint validate-misspell diff --git a/cmd/internal/gen/centrifuge.go b/cmd/internal/gen/centrifuge.go index 87c09f272..ddef63429 100644 --- a/cmd/internal/gen/centrifuge.go +++ b/cmd/internal/gen/centrifuge.go @@ -12,6 +12,7 @@ import ( "os" "path" "path/filepath" + "reflect" "sort" "strings" @@ -183,15 +184,15 @@ func (c Centrifuge) writeStruct(name string, obj *types.Struct, rootPkg string, continue } + values, ok := lookupTagValue(obj.Tag(i), "json") + if len(values) > 0 && values[0] == "-" { + continue + } + b.WriteString(fmt.Sprintf("\t%s %s", field.Name(), fType)) - tags := obj.Tag(i) - if tags != "" { - tg := extractJSONTag(tags) - - if tg != `json:"-"` { - b.WriteString(fmt.Sprintf(" `%s`", tg)) - } + if ok { + b.WriteString(fmt.Sprintf(" `json:\"%s\"`", strings.Join(values, ","))) } b.WriteString("\n") @@ -202,16 +203,19 @@ func (c Centrifuge) writeStruct(name string, obj *types.Struct, rootPkg string, return b.String() } -func extractJSONTag(value string) string { - fields := strings.Fields(value) - - for _, field := range fields { - if strings.HasPrefix(field, `json:"`) { - return field - } +func lookupTagValue(raw, key string) ([]string, bool) { + value, ok := reflect.StructTag(raw).Lookup(key) + if !ok { + return nil, ok } - return "" + values := strings.Split(value, ",") + + if len(values) < 1 { + return nil, true + } + + return values, true } func extractPackage(t types.Type) string { diff --git a/docs/content/getting-started/faq.md b/docs/content/getting-started/faq.md index 66ae963af..74ad989bf 100644 --- a/docs/content/getting-started/faq.md +++ b/docs/content/getting-started/faq.md @@ -124,3 +124,16 @@ http: If there is a need for a response code other than a `503` and/or a custom message, the principle of the above example above (a catchall router) still stands, but the `unavailable` service should be adapted to fit such a need. + +## Why Is My TLS Certificate Not Reloaded When Its Contents Change ? + +With the file provider, +a configuration update is only triggered when one of the [watched](../providers/file.md#provider-configuration) configuration files is modified. + +Which is why, when a certificate is defined by path, +and the actual contents of this certificate change, +a configuration update is _not_ triggered. + +To take into account the new certificate contents, the update of the dynamic configuration must be forced. +One way to achieve that, is to trigger a file notification, +for example, by using the `touch` command on the configuration file. diff --git a/docs/content/migration/v2.md b/docs/content/migration/v2.md index 2293976ff..4aa9b6202 100644 --- a/docs/content/migration/v2.md +++ b/docs/content/migration/v2.md @@ -365,6 +365,17 @@ For more information, please read the [HTTP routers rule](../routing/routers/ind In `v2.4.9`, we changed span error to log only server errors (>= 500). +## v2.4.9 to v2.4.10 + +### K8S CrossNamespace + +In `v2.4.10`, the default value for `allowCrossNamespace` has been changed to `false`. + +### K8S ExternalName Service + +In `v2.4.10`, by default, it is no longer authorized to reference Kubernetes ExternalName services. +To allow it, the `allowExternalNameServices` option should be set to `true`. + ## v2.4 to v2.5 ### Kubernetes CRD @@ -387,8 +398,6 @@ The `extensions/v1beta1` API Version should now be replaced either by `networkin The support of the `networking.k8s.io/v1beta1` API Version will stop in Kubernetes v1.22. -## v2.5 to v2.6 - ### Headers middleware: ssl redirect options `sslRedirect`, `sslTemporaryRedirect`, `sslHost` and `sslForceHost` are deprecated in Traefik v2.5. @@ -399,4 +408,4 @@ For more advanced use cases, you can use either the [RedirectScheme middleware]( ### Headers middleware: accessControlAllowOrigin -`accessControlAllowOrigin` is no longer supported. +`accessControlAllowOrigin` is no longer supported in Traefik v2.5. diff --git a/docs/content/providers/consul-catalog.md b/docs/content/providers/consul-catalog.md index 23f71af86..31bb54d43 100644 --- a/docs/content/providers/consul-catalog.md +++ b/docs/content/providers/consul-catalog.md @@ -556,6 +556,81 @@ providers: # ... ``` +### `connectAware` + +_Optional, Default=false_ + +Enable Consul Connect support. +If set to `true`, Traefik will be enabled to communicate with Connect services. + +```toml tab="File (TOML)" +[providers.consulCatalog] + connectAware = true + # ... +``` + +```yaml tab="File (YAML)" +providers: + consulCatalog: + connectAware: true + # ... +``` + +```bash tab="CLI" +--providers.consulcatalog.connectAware=true +# ... +``` + +### `connectByDefault` + +_Optional, Default=false_ + +Consider every service as Connect capable by default. +If set to `true`, Traefik will consider every Consul Catalog service to be Connect capable by default. +The option can be overridden on an instance basis with the `traefik.consulcatalog.connect` tag. + +```toml tab="File (TOML)" +[providers.consulCatalog] + connectByDefault = true + # ... +``` + +```yaml tab="File (YAML)" +providers: + consulCatalog: + connectByDefault: true + # ... +``` + +```bash tab="CLI" +--providers.consulcatalog.connectByDefault=true +# ... +``` + +### `serviceName` + +_Optional, Default="traefik"_ + +Name of the Traefik service in Consul Catalog. + +```toml tab="File (TOML)" +[providers.consulCatalog] + serviceName = "test" + # ... +``` + +```yaml tab="File (YAML)" +providers: + consulCatalog: + serviceName: test + # ... +``` + +```bash tab="CLI" +--providers.consulcatalog.serviceName=test +# ... +``` + ### `constraints` _Optional, Default=""_ diff --git a/docs/content/providers/kubernetes-crd.md b/docs/content/providers/kubernetes-crd.md index 6f5d64fd7..7276e391d 100644 --- a/docs/content/providers/kubernetes-crd.md +++ b/docs/content/providers/kubernetes-crd.md @@ -266,29 +266,48 @@ providers: ### `allowCrossNamespace` -_Optional, Default: true_ +_Optional, Default: false_ -If the parameter is set to `false`, IngressRoutes are not able to reference any resources in other namespaces than theirs. - -!!! warning "Deprecation" - - Please note that the default value for this option will be set to `false` in a future version. +If the parameter is set to `true`, IngressRoutes are able to reference resources in other namespaces than theirs. ```yaml tab="File (YAML)" providers: kubernetesCRD: - allowCrossNamespace: false + allowCrossNamespace: true # ... ``` ```toml tab="File (TOML)" [providers.kubernetesCRD] - allowCrossNamespace = false + allowCrossNamespace = true # ... ``` ```bash tab="CLI" ---providers.kubernetescrd.allowCrossNamespace=false +--providers.kubernetescrd.allowCrossNamespace=true +``` + +### `allowExternalNameServices` + +_Optional, Default: false_ + +If the parameter is set to `true`, IngressRoutes are able to reference ExternalName services. + +```yaml tab="File (YAML)" +providers: + kubernetesCRD: + allowExternalNameServices: true + # ... +``` + +```toml tab="File (TOML)" +[providers.kubernetesCRD] + allowExternalNameServices = true + # ... +``` + +```bash tab="CLI" +--providers.kubernetescrd.allowexternalnameservices=true ``` ## Full Example diff --git a/docs/content/providers/kubernetes-gateway.md b/docs/content/providers/kubernetes-gateway.md index af931b572..1cd115f4e 100644 --- a/docs/content/providers/kubernetes-gateway.md +++ b/docs/content/providers/kubernetes-gateway.md @@ -9,7 +9,7 @@ The Gateway API project is part of Kubernetes, working under SIG-NETWORK. The Kubernetes Gateway provider is a Traefik implementation of the [Gateway API](https://gateway-api.sigs.k8s.io/) specifications from the Kubernetes Special Interest Groups (SIGs). -This provider is proposed as an experimental feature and partially supports the Gateway API [v0.2.0](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v0.2.0) specification. +This provider is proposed as an experimental feature and partially supports the Gateway API [v0.3.0](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v0.3.0) specification. !!! warning "Enabling The Experimental Kubernetes Gateway Provider" @@ -77,7 +77,7 @@ The [getting started guide](https://gateway-api.sigs.k8s.io/guides/getting-start !!! note "" - Keep in mind that the Traefik Gateway provider only supports the `v0.2.0`. + Keep in mind that the Traefik Gateway provider only supports the `v0.3.0`. For now, the Traefik Gateway Provider can be used while following the below guides: diff --git a/docs/content/providers/kubernetes-ingress.md b/docs/content/providers/kubernetes-ingress.md index 68d819418..c3a3a86c9 100644 --- a/docs/content/providers/kubernetes-ingress.md +++ b/docs/content/providers/kubernetes-ingress.md @@ -464,6 +464,29 @@ providers: Allow the creation of services if there are no endpoints available. This results in `503` http responses instead of `404`. +### `allowExternalNameServices` + +_Optional, Default: false_ + +If the parameter is set to `true`, Ingresses are able to reference ExternalName services. + +```yaml tab="File (YAML)" +providers: + kubernetesIngress: + allowExternalNameServices: true + # ... +``` + +```toml tab="File (TOML)" +[providers.kubernetesIngress] + allowExternalNameServices = true + # ... +``` + +```bash tab="CLI" +--providers.kubernetesingress.allowexternalnameservices=true +``` + ### Further To learn more about the various aspects of the Ingress specification that Traefik supports, diff --git a/docs/content/reference/dynamic-configuration/consul-catalog.yml b/docs/content/reference/dynamic-configuration/consul-catalog.yml index 23efc00c6..e125eba19 100644 --- a/docs/content/reference/dynamic-configuration/consul-catalog.yml +++ b/docs/content/reference/dynamic-configuration/consul-catalog.yml @@ -1 +1,2 @@ - "traefik.enable=true" +- "traefik.consulcatalog.connect=true" diff --git a/docs/content/reference/dynamic-configuration/file.toml b/docs/content/reference/dynamic-configuration/file.toml index c207be966..d531f81a1 100644 --- a/docs/content/reference/dynamic-configuration/file.toml +++ b/docs/content/reference/dynamic-configuration/file.toml @@ -278,6 +278,7 @@ rootCAs = ["foobar", "foobar"] maxIdleConnsPerHost = 42 disableHTTP2 = true + peerCertURI = "foobar" [[http.serversTransports.ServersTransport0.certificates]] certFile = "foobar" @@ -296,6 +297,7 @@ rootCAs = ["foobar", "foobar"] maxIdleConnsPerHost = 42 disableHTTP2 = true + peerCertURI = "foobar" [[http.serversTransports.ServersTransport1.certificates]] certFile = "foobar" diff --git a/docs/content/reference/dynamic-configuration/file.yaml b/docs/content/reference/dynamic-configuration/file.yaml index b060e6fb1..515dba07c 100644 --- a/docs/content/reference/dynamic-configuration/file.yaml +++ b/docs/content/reference/dynamic-configuration/file.yaml @@ -329,6 +329,7 @@ http: responseHeaderTimeout: 42s idleConnTimeout: 42s disableHTTP2: true + peerCertURI: foobar ServersTransport1: serverName: foobar insecureSkipVerify: true @@ -346,6 +347,7 @@ http: responseHeaderTimeout: 42s idleConnTimeout: 42s disableHTTP2: true + peerCertURI: foobar tcp: routers: TCPRouter0: diff --git a/docs/content/reference/dynamic-configuration/kv-ref.md b/docs/content/reference/dynamic-configuration/kv-ref.md index 2650202a7..b4c81d76b 100644 --- a/docs/content/reference/dynamic-configuration/kv-ref.md +++ b/docs/content/reference/dynamic-configuration/kv-ref.md @@ -174,6 +174,7 @@ | `traefik/http/serversTransports/ServersTransport0/forwardingTimeouts/responseHeaderTimeout` | `42s` | | `traefik/http/serversTransports/ServersTransport0/insecureSkipVerify` | `true` | | `traefik/http/serversTransports/ServersTransport0/maxIdleConnsPerHost` | `42` | +| `traefik/http/serversTransports/ServersTransport0/peerCertURI` | `foobar` | | `traefik/http/serversTransports/ServersTransport0/rootCAs/0` | `foobar` | | `traefik/http/serversTransports/ServersTransport0/rootCAs/1` | `foobar` | | `traefik/http/serversTransports/ServersTransport0/serverName` | `foobar` | @@ -187,6 +188,7 @@ | `traefik/http/serversTransports/ServersTransport1/forwardingTimeouts/responseHeaderTimeout` | `42s` | | `traefik/http/serversTransports/ServersTransport1/insecureSkipVerify` | `true` | | `traefik/http/serversTransports/ServersTransport1/maxIdleConnsPerHost` | `42` | +| `traefik/http/serversTransports/ServersTransport1/peerCertURI` | `foobar` | | `traefik/http/serversTransports/ServersTransport1/rootCAs/0` | `foobar` | | `traefik/http/serversTransports/ServersTransport1/rootCAs/1` | `foobar` | | `traefik/http/serversTransports/ServersTransport1/serverName` | `foobar` | diff --git a/docs/content/reference/dynamic-configuration/networking.x-k8s.io_gatewayclasses.yaml b/docs/content/reference/dynamic-configuration/networking.x-k8s.io_gatewayclasses.yaml index cf3b0f1aa..5872e7ec7 100644 --- a/docs/content/reference/dynamic-configuration/networking.x-k8s.io_gatewayclasses.yaml +++ b/docs/content/reference/dynamic-configuration/networking.x-k8s.io_gatewayclasses.yaml @@ -4,12 +4,14 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.4.1 + controller-gen.kubebuilder.io/version: v0.5.0 creationTimestamp: null name: gatewayclasses.networking.x-k8s.io spec: group: networking.x-k8s.io names: + categories: + - gateway-api kind: GatewayClass listKind: GatewayClassList plural: gatewayclasses @@ -22,16 +24,25 @@ spec: - jsonPath: .spec.controller name: Controller type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1alpha1 schema: openAPIV3Schema: - description: "GatewayClass describes a class of Gateways available to the user for creating Gateway resources. \n GatewayClass is a Cluster level resource." + description: "GatewayClass describes a class of Gateways available to the + user for creating Gateway resources. \n GatewayClass is a Cluster level + resource." properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object @@ -39,11 +50,23 @@ spec: description: Spec defines the desired state of GatewayClass. properties: controller: - description: "Controller is a domain/path string that indicates the controller that is managing Gateways of this class. \n Example: \"acme.io/gateway-controller\". \n This field is not mutable and cannot be empty. \n The format of this field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). \n Support: Core" + description: "Controller is a domain/path string that indicates the + controller that is managing Gateways of this class. \n Example: + \"acme.io/gateway-controller\". \n This field is not mutable and + cannot be empty. \n The format of this field is DOMAIN \"/\" PATH, + where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). + \n Support: Core" maxLength: 253 type: string parametersRef: - description: "ParametersRef is a reference to a resource that contains the configuration parameters corresponding to the GatewayClass. This is optional if the controller does not require any additional configuration. \n ParametersRef can reference a standard Kubernetes resource, i.e. ConfigMap, or an implementation-specific custom resource. The resource can be cluster-scoped or namespace-scoped. \n If the referent cannot be found, the GatewayClass's \"InvalidParameters\" status condition will be true. \n Support: Custom" + description: "ParametersRef is a reference to a resource that contains + the configuration parameters corresponding to the GatewayClass. + This is optional if the controller does not require any additional + configuration. \n ParametersRef can reference a standard Kubernetes + resource, i.e. ConfigMap, or an implementation-specific custom resource. + The resource can be cluster-scoped or namespace-scoped. \n If the + referent cannot be found, the GatewayClass's \"InvalidParameters\" + status condition will be true. \n Support: Custom" properties: group: description: Group is the group of the referent. @@ -61,13 +84,16 @@ spec: minLength: 1 type: string namespace: - description: Namespace is the namespace of the referent. This field is required when scope is set to "Namespace" and ignored when scope is set to "Cluster". + description: Namespace is the namespace of the referent. This + field is required when scope is set to "Namespace" and ignored + when scope is set to "Cluster". maxLength: 253 minLength: 1 type: string scope: default: Cluster - description: Scope represents if the referent is a Cluster or Namespace scoped resource. This may be set to "Cluster" or "Namespace". + description: Scope represents if the referent is a Cluster or + Namespace scoped resource. This may be set to "Cluster" or "Namespace". enum: - Cluster - Namespace @@ -97,25 +123,49 @@ spec: reason: Waiting status: "False" type: Admitted - description: "Conditions is the current status from the controller for this GatewayClass. \n Controllers should prefer to publish conditions using values of GatewayClassConditionType for the type of each Condition." + description: "Conditions is the current status from the controller + for this GatewayClass. \n Controllers should prefer to publish conditions + using values of GatewayClassConditionType for the type of each Condition." items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: + \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type + \ // +patchStrategy=merge // +listType=map // +listMapKey=type + \ Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` + \n // other fields }" properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. format: date-time type: string message: - description: message is a human readable message indicating details about the transition. This may be an empty string. + description: message is a human readable message indicating + details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ @@ -128,7 +178,11 @@ spec: - Unknown type: string type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string diff --git a/docs/content/reference/dynamic-configuration/networking.x-k8s.io_gateways.yaml b/docs/content/reference/dynamic-configuration/networking.x-k8s.io_gateways.yaml index 2ed53d5c1..25eca95a7 100644 --- a/docs/content/reference/dynamic-configuration/networking.x-k8s.io_gateways.yaml +++ b/docs/content/reference/dynamic-configuration/networking.x-k8s.io_gateways.yaml @@ -4,12 +4,14 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.4.1 + controller-gen.kubebuilder.io/version: v0.5.0 creationTimestamp: null name: gateways.networking.x-k8s.io spec: group: networking.x-k8s.io names: + categories: + - gateway-api kind: Gateway listKind: GatewayList plural: gateways @@ -22,16 +24,28 @@ spec: - jsonPath: .spec.gatewayClassName name: Class type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1alpha1 schema: openAPIV3Schema: - description: "Gateway represents an instantiation of a service-traffic handling infrastructure by binding Listeners to a set of IP addresses. \n Implementations should add the `gateway-exists-finalizer.networking.x-k8s.io` finalizer on the associated GatewayClass whenever Gateway(s) is running. This ensures that a GatewayClass associated with a Gateway(s) is not deleted while in use." + description: "Gateway represents an instantiation of a service-traffic handling + infrastructure by binding Listeners to a set of IP addresses. \n Implementations + should add the `gateway-exists-finalizer.networking.x-k8s.io` finalizer + on the associated GatewayClass whenever Gateway(s) is running. This ensures + that a GatewayClass associated with a Gateway(s) is not deleted while in + use." properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object @@ -39,9 +53,18 @@ spec: description: Spec defines the desired state of Gateway. properties: addresses: - description: "Addresses requested for this gateway. This is optional and behavior can depend on the GatewayClass. If a value is set in the spec and the requested address is invalid, the GatewayClass MUST indicate this in the associated entry in GatewayStatus.Addresses. \n If no Addresses are specified, the GatewayClass may schedule the Gateway in an implementation-defined manner, assigning an appropriate set of Addresses. \n The GatewayClass MUST bind all Listeners to every GatewayAddress that it assigns to the Gateway. \n Support: Core" + description: "Addresses requested for this gateway. This is optional + and behavior can depend on the GatewayClass. If a value is set in + the spec and the requested address is invalid, the GatewayClass + MUST indicate this in the associated entry in GatewayStatus.Addresses. + \n If no Addresses are specified, the GatewayClass may schedule + the Gateway in an implementation-defined manner, assigning an appropriate + set of Addresses. \n The GatewayClass MUST bind all Listeners to + every GatewayAddress that it assigns to the Gateway. \n Support: + Core" items: - description: GatewayAddress describes an address that can be bound to a Gateway. + description: GatewayAddress describes an address that can be bound + to a Gateway. properties: type: default: IPAddress @@ -51,7 +74,9 @@ spec: - NamedAddress type: string value: - description: "Value of the address. The validity of the values will depend on the type and support by the controller. \n Examples: `1.2.3.4`, `128::1`, `my-ip-address`." + description: "Value of the address. The validity of the values + will depend on the type and support by the controller. \n + Examples: `1.2.3.4`, `128::1`, `my-ip-address`." maxLength: 253 minLength: 1 type: string @@ -61,70 +86,186 @@ spec: maxItems: 16 type: array gatewayClassName: - description: GatewayClassName used for this Gateway. This is the name of a GatewayClass resource. + description: GatewayClassName used for this Gateway. This is the name + of a GatewayClass resource. maxLength: 253 minLength: 1 type: string listeners: - description: "Listeners associated with this Gateway. Listeners define logical endpoints that are bound on this Gateway's addresses. At least one Listener MUST be specified. \n An implementation MAY group Listeners by Port and then collapse each group of Listeners into a single Listener if the implementation determines that the Listeners in the group are \"compatible\". An implementation MAY also group together and collapse compatible Listeners belonging to different Gateways. \n For example, an implementation might consider Listeners to be compatible with each other if all of the following conditions are met: \n 1. Either each Listener within the group specifies the \"HTTP\" Protocol or each Listener within the group specifies either the \"HTTPS\" or \"TLS\" Protocol. \n 2. Each Listener within the group specifies a Hostname that is unique within the group. \n 3. As a special case, one Listener within a group may omit Hostname, in which case this Listener matches when no other Listener matches. \n If the implementation does collapse compatible Listeners, the hostname provided in the incoming client request MUST be matched to a Listener to find the correct set of Routes. The incoming hostname MUST be matched using the Hostname field for each Listener in order of most to least specific. That is, exact matches must be processed before wildcard matches. \n If this field specifies multiple Listeners that have the same Port value but are not compatible, the implementation must raise a \"Conflicted\" condition in the Listener status. \n Support: Core" + description: "Listeners associated with this Gateway. Listeners define + logical endpoints that are bound on this Gateway's addresses. At + least one Listener MUST be specified. \n An implementation MAY group + Listeners by Port and then collapse each group of Listeners into + a single Listener if the implementation determines that the Listeners + in the group are \"compatible\". An implementation MAY also group + together and collapse compatible Listeners belonging to different + Gateways. \n For example, an implementation might consider Listeners + to be compatible with each other if all of the following conditions + are met: \n 1. Either each Listener within the group specifies the + \"HTTP\" Protocol or each Listener within the group specifies + either the \"HTTPS\" or \"TLS\" Protocol. \n 2. Each Listener + within the group specifies a Hostname that is unique within the + group. \n 3. As a special case, one Listener within a group may + omit Hostname, in which case this Listener matches when no other + Listener matches. \n If the implementation does collapse compatible + Listeners, the hostname provided in the incoming client request + MUST be matched to a Listener to find the correct set of Routes. + The incoming hostname MUST be matched using the Hostname field for + each Listener in order of most to least specific. That is, exact + matches must be processed before wildcard matches. \n If this field + specifies multiple Listeners that have the same Port value but are + not compatible, the implementation must raise a \"Conflicted\" condition + in the Listener status. \n Support: Core" items: - description: Listener embodies the concept of a logical endpoint where a Gateway can accept network connections. Each listener in a Gateway must have a unique combination of Hostname, Port, and Protocol. This will be enforced by a validating webhook. + description: Listener embodies the concept of a logical endpoint + where a Gateway can accept network connections. Each listener + in a Gateway must have a unique combination of Hostname, Port, + and Protocol. This will be enforced by a validating webhook. properties: hostname: - description: "Hostname specifies the virtual hostname to match for protocol types that define this concept. When unspecified, \"\", or `*`, all hostnames are matched. This field can be omitted for protocols that don't require hostname based matching. \n Hostname is the fully qualified domain name of a network host, as defined by RFC 3986. Note the following deviations from the \"host\" part of the URI as defined in the RFC: \n 1. IP literals are not allowed. 2. The `:` delimiter is not respected because ports are not allowed. \n Hostname can be \"precise\" which is a domain name without the terminating dot of a network host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). The wildcard character `*` must appear by itself as the first DNS label and matches only a single label. \n Support: Core" + description: "Hostname specifies the virtual hostname to match + for protocol types that define this concept. When unspecified, + \"\", or `*`, all hostnames are matched. This field can be + omitted for protocols that don't require hostname based matching. + \n Hostname is the fully qualified domain name of a network + host, as defined by RFC 3986. Note the following deviations + from the \"host\" part of the URI as defined in the RFC: \n + 1. IP literals are not allowed. 2. The `:` delimiter is not + respected because ports are not allowed. \n Hostname can be + \"precise\" which is a domain name without the terminating + dot of a network host (e.g. \"foo.example.com\") or \"wildcard\", + which is a domain name prefixed with a single wildcard label + (e.g. `*.example.com`). The wildcard character `*` must appear + by itself as the first DNS label and matches only a single + label. \n Support: Core" maxLength: 253 minLength: 1 type: string port: - description: "Port is the network port. Multiple listeners may use the same port, subject to the Listener compatibility rules. \n Support: Core" + description: "Port is the network port. Multiple listeners may + use the same port, subject to the Listener compatibility rules. + \n Support: Core" format: int32 maximum: 65535 minimum: 1 type: integer protocol: - description: "Protocol specifies the network protocol this listener expects to receive. The GatewayClass MUST apply the Hostname match appropriately for each protocol: \n * For the \"TLS\" protocol, the Hostname match MUST be applied to the [SNI](https://tools.ietf.org/html/rfc6066#section-3) server name offered by the client. * For the \"HTTP\" protocol, the Hostname match MUST be applied to the host portion of the [effective request URI](https://tools.ietf.org/html/rfc7230#section-5.5) or the [:authority pseudo-header](https://tools.ietf.org/html/rfc7540#section-8.1.2.3) * For the \"HTTPS\" protocol, the Hostname match MUST be applied at both the TLS and HTTP protocol layers. \n Support: Core" + description: "Protocol specifies the network protocol this listener + expects to receive. The GatewayClass MUST apply the Hostname + match appropriately for each protocol: \n * For the \"TLS\" + protocol, the Hostname match MUST be applied to the [SNI](https://tools.ietf.org/html/rfc6066#section-3) + \ server name offered by the client. * For the \"HTTP\" protocol, + the Hostname match MUST be applied to the host portion of + the [effective request URI](https://tools.ietf.org/html/rfc7230#section-5.5) + \ or the [:authority pseudo-header](https://tools.ietf.org/html/rfc7540#section-8.1.2.3) + * For the \"HTTPS\" protocol, the Hostname match MUST be applied + at both the TLS and HTTP protocol layers. \n Support: Core" type: string routes: - description: "Routes specifies a schema for associating routes with the Listener using selectors. A Route is a resource capable of servicing a request and allows a cluster operator to expose a cluster resource (i.e. Service) by externally-reachable URL, load-balance traffic and terminate SSL/TLS. Typically, a route is a \"HTTPRoute\" or \"TCPRoute\" in group \"networking.x-k8s.io\", however, an implementation may support other types of resources. \n The Routes selector MUST select a set of objects that are compatible with the application protocol specified in the Protocol field. \n Although a client request may technically match multiple route rules, only one rule may ultimately receive the request. Matching precedence MUST be determined in order of the following criteria: \n * The most specific match. For example, the most specific HTTPRoute match is determined by the longest matching combination of hostname and path. * The oldest Route based on creation timestamp. For example, a Route with a creation timestamp of \"2020-09-08 01:02:03\" is given precedence over a Route with a creation timestamp of \"2020-09-08 01:02:04\". * If everything else is equivalent, the Route appearing first in alphabetical order (namespace/name) should be given precedence. For example, foo/bar is given precedence over foo/baz. \n All valid portions of a Route selected by this field should be supported. Invalid portions of a Route can be ignored (sometimes that will mean the full Route). If a portion of a Route transitions from valid to invalid, support for that portion of the Route should be dropped to ensure consistency. For example, even if a filter specified by a Route is invalid, the rest of the Route should still be supported. \n Support: Core" + description: "Routes specifies a schema for associating routes + with the Listener using selectors. A Route is a resource capable + of servicing a request and allows a cluster operator to expose + a cluster resource (i.e. Service) by externally-reachable + URL, load-balance traffic and terminate SSL/TLS. Typically, + a route is a \"HTTPRoute\" or \"TCPRoute\" in group \"networking.x-k8s.io\", + however, an implementation may support other types of resources. + \n The Routes selector MUST select a set of objects that are + compatible with the application protocol specified in the + Protocol field. \n Although a client request may technically + match multiple route rules, only one rule may ultimately receive + the request. Matching precedence MUST be determined in order + of the following criteria: \n * The most specific match. For + example, the most specific HTTPRoute match is determined + by the longest matching combination of hostname and path. + * The oldest Route based on creation timestamp. For example, + a Route with a creation timestamp of \"2020-09-08 01:02:03\" + is given precedence over a Route with a creation timestamp + of \"2020-09-08 01:02:04\". * If everything else is equivalent, + the Route appearing first in alphabetical order (namespace/name) + should be given precedence. For example, foo/bar is given + precedence over foo/baz. \n All valid portions of a Route + selected by this field should be supported. Invalid portions + of a Route can be ignored (sometimes that will mean the full + Route). If a portion of a Route transitions from valid to + invalid, support for that portion of the Route should be dropped + to ensure consistency. For example, even if a filter specified + by a Route is invalid, the rest of the Route should still + be supported. \n Support: Core" properties: group: default: networking.x-k8s.io - description: "Group is the group of the route resource to select. Omitting the value or specifying the empty string indicates the networking.x-k8s.io API group. For example, use the following to select an HTTPRoute: \n routes: kind: HTTPRoute \n Otherwise, if an alternative API group is desired, specify the desired group: \n routes: group: acme.io kind: FooRoute \n Support: Core" + description: "Group is the group of the route resource to + select. Omitting the value or specifying the empty string + indicates the networking.x-k8s.io API group. For example, + use the following to select an HTTPRoute: \n routes: kind: + HTTPRoute \n Otherwise, if an alternative API group is + desired, specify the desired group: \n routes: group: + acme.io kind: FooRoute \n Support: Core" maxLength: 253 minLength: 1 type: string kind: - description: "Kind is the kind of the route resource to select. \n Kind MUST correspond to kinds of routes that are compatible with the application protocol specified in the Listener's Protocol field. \n If an implementation does not support or recognize this resource type, it SHOULD set the \"ResolvedRefs\" condition to false for this listener with the \"InvalidRoutesRef\" reason. \n Support: Core" + description: "Kind is the kind of the route resource to + select. \n Kind MUST correspond to kinds of routes that + are compatible with the application protocol specified + in the Listener's Protocol field. \n If an implementation + does not support or recognize this resource type, it SHOULD + set the \"ResolvedRefs\" condition to false for this listener + with the \"InvalidRoutesRef\" reason. \n Support: Core" type: string namespaces: default: from: Same - description: "Namespaces indicates in which namespaces Routes should be selected for this Gateway. This is restricted to the namespace of this Gateway by default. \n Support: Core" + description: "Namespaces indicates in which namespaces Routes + should be selected for this Gateway. This is restricted + to the namespace of this Gateway by default. \n Support: + Core" properties: from: default: Same - description: "From indicates where Routes will be selected for this Gateway. Possible values are: * All: Routes in all namespaces may be used by this Gateway. * Selector: Routes in namespaces selected by the selector may be used by this Gateway. * Same: Only Routes in the same namespace may be used by this Gateway. \n Support: Core" + description: "From indicates where Routes will be selected + for this Gateway. Possible values are: * All: Routes + in all namespaces may be used by this Gateway. * Selector: + Routes in namespaces selected by the selector may + be used by this Gateway. * Same: Only Routes in + the same namespace may be used by this Gateway. \n + Support: Core" enum: - All - Selector - Same type: string selector: - description: "Selector must be specified when From is set to \"Selector\". In that case, only Routes in Namespaces matching this Selector will be selected by this Gateway. This field is ignored for other values of \"From\". \n Support: Core" + description: "Selector must be specified when From is + set to \"Selector\". In that case, only Routes in + Namespaces matching this Selector will be selected + by this Gateway. This field is ignored for other values + of \"From\". \n Support: Core" properties: matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. items: - description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. properties: key: - description: key is the label key that the selector applies to. + description: key is the label key that the + selector applies to. type: string operator: - description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. This array is + replaced during a strategic merge patch. items: type: string type: array @@ -136,26 +277,46 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". The + requirements are ANDed. type: object type: object type: object selector: - description: "Selector specifies a set of route labels used for selecting routes to associate with the Gateway. If this Selector is defined, only routes matching the Selector are associated with the Gateway. An empty Selector matches all routes. \n Support: Core" + description: "Selector specifies a set of route labels used + for selecting routes to associate with the Gateway. If + this Selector is defined, only routes matching the Selector + are associated with the Gateway. An empty Selector matches + all routes. \n Support: Core" properties: matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. items: - description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. properties: key: - description: key is the label key that the selector applies to. + description: key is the label key that the selector + applies to. type: string operator: - description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. type: string values: - description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. + description: values is an array of string values. + If the operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be empty. + This array is replaced during a strategic merge + patch. items: type: string type: array @@ -167,17 +328,37 @@ spec: matchLabels: additionalProperties: type: string - description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. type: object type: object required: - kind type: object tls: - description: "TLS is the TLS configuration for the Listener. This field is required if the Protocol field is \"HTTPS\" or \"TLS\" and ignored otherwise. \n The association of SNIs to Certificate defined in GatewayTLSConfig is defined based on the Hostname field for this listener. \n The GatewayClass MUST use the longest matching SNI out of all available certificates for any TLS handshake. \n Support: Core" + description: "TLS is the TLS configuration for the Listener. + This field is required if the Protocol field is \"HTTPS\" + or \"TLS\" and ignored otherwise. \n The association of SNIs + to Certificate defined in GatewayTLSConfig is defined based + on the Hostname field for this listener. \n The GatewayClass + MUST use the longest matching SNI out of all available certificates + for any TLS handshake. \n Support: Core" properties: certificateRef: - description: "CertificateRef is the reference to Kubernetes object that contain a TLS certificate and private key. This certificate MUST be used for TLS handshakes for the domain this GatewayTLSConfig is associated with. \n This field is required when mode is set to \"Terminate\" (default) and optional otherwise. \n If an entry in this list omits or specifies the empty string for both the group and the resource, the resource defaults to \"secrets\". An implementation may support other resources (for example, resource \"mycertificates\" in group \"networking.acme.io\"). \n Support: Core (Kubernetes Secrets) \n Support: Implementation-specific (Other resource types)" + description: "CertificateRef is a reference to a Kubernetes + object that contains a TLS certificate and private key. + This certificate is used to establish a TLS handshake + for requests that match the hostname of the associated + listener. The referenced object MUST reside in the same + namespace as Gateway. \n This field is required when mode + is set to \"Terminate\" (default) and optional otherwise. + \n CertificateRef can reference a standard Kubernetes + resource, i.e. Secret, or an implementation-specific custom + resource. \n Support: Core (Kubernetes Secrets) \n Support: + Implementation-specific (Other resource types)" properties: group: description: Group is the group of the referent. @@ -201,7 +382,16 @@ spec: type: object mode: default: Terminate - description: "Mode defines the TLS behavior for the TLS session initiated by the client. There are two possible modes: - Terminate: The TLS session between the downstream client and the Gateway is terminated at the Gateway. This mode requires certificateRef to be set. - Passthrough: The TLS session is NOT terminated by the Gateway. This implies that the Gateway can't decipher the TLS stream except for the ClientHello message of the TLS protocol. CertificateRef field is ignored in this mode. \n Support: Core" + description: "Mode defines the TLS behavior for the TLS + session initiated by the client. There are two possible + modes: - Terminate: The TLS session between the downstream + client and the Gateway is terminated at the Gateway. + This mode requires certificateRef to be set. - Passthrough: + The TLS session is NOT terminated by the Gateway. This + \ implies that the Gateway can't decipher the TLS stream + except for the ClientHello message of the TLS protocol. + \ CertificateRef field is ignored in this mode. \n Support: + Core" enum: - Terminate - Passthrough @@ -209,16 +399,29 @@ spec: options: additionalProperties: type: string - description: "Options are a list of key/value pairs to give extended options to the provider. \n There variation among providers as to how ciphersuites are expressed. If there is a common subset for expressing ciphers then it will make sense to loft that as a core API construct. \n Support: Implementation-specific" + description: "Options are a list of key/value pairs to give + extended options to the provider. \n There variation among + providers as to how ciphersuites are expressed. If there + is a common subset for expressing ciphers then it will + make sense to loft that as a core API construct. \n Support: + Implementation-specific" type: object routeOverride: default: certificate: Deny - description: "RouteOverride dictates if TLS settings can be configured via Routes or not. \n CertificateRef must be defined even if `routeOverride.certificate` is set to 'Allow' as it will be used as the default certificate for the listener. \n Support: Core" + description: "RouteOverride dictates if TLS settings can + be configured via Routes or not. \n CertificateRef must + be defined even if `routeOverride.certificate` is set + to 'Allow' as it will be used as the default certificate + for the listener. \n Support: Core" properties: certificate: default: Deny - description: "Certificate dictates if TLS certificates can be configured via Routes. If set to 'Allow', a TLS certificate for a hostname defined in a Route takes precedence over the certificate defined in Gateway. \n Support: Core" + description: "Certificate dictates if TLS certificates + can be configured via Routes. If set to 'Allow', a + TLS certificate for a hostname defined in a Route + takes precedence over the certificate defined in Gateway. + \n Support: Core" enum: - Allow - Deny @@ -248,9 +451,13 @@ spec: description: Status defines the current state of Gateway. properties: addresses: - description: "Addresses lists the IP addresses that have actually been bound to the Gateway. These addresses may differ from the addresses in the Spec, e.g. if the Gateway automatically assigns an address from a reserved pool. \n These addresses should all be of type \"IPAddress\"." + description: "Addresses lists the IP addresses that have actually + been bound to the Gateway. These addresses may differ from the addresses + in the Spec, e.g. if the Gateway automatically assigns an address + from a reserved pool. \n These addresses should all be of type \"IPAddress\"." items: - description: GatewayAddress describes an address that can be bound to a Gateway. + description: GatewayAddress describes an address that can be bound + to a Gateway. properties: type: default: IPAddress @@ -260,7 +467,9 @@ spec: - NamedAddress type: string value: - description: "Value of the address. The validity of the values will depend on the type and support by the controller. \n Examples: `1.2.3.4`, `128::1`, `my-ip-address`." + description: "Value of the address. The validity of the values + will depend on the type and support by the controller. \n + Examples: `1.2.3.4`, `128::1`, `my-ip-address`." maxLength: 253 minLength: 1 type: string @@ -276,25 +485,52 @@ spec: reason: NotReconciled status: "False" type: Scheduled - description: "Conditions describe the current conditions of the Gateway. \n Implementations should prefer to express Gateway conditions using the `GatewayConditionType` and `GatewayConditionReason` constants so that operators and tools can converge on a common vocabulary to describe Gateway state. \n Known condition types are: \n * \"Scheduled\" * \"Ready\"" + description: "Conditions describe the current conditions of the Gateway. + \n Implementations should prefer to express Gateway conditions using + the `GatewayConditionType` and `GatewayConditionReason` constants + so that operators and tools can converge on a common vocabulary + to describe Gateway state. \n Known condition types are: \n * \"Scheduled\" + * \"Ready\"" items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: + \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type + \ // +patchStrategy=merge // +listType=map // +listMapKey=type + \ Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` + \n // other fields }" properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. format: date-time type: string message: - description: message is a human readable message indicating details about the transition. This may be an empty string. + description: message is a human readable message indicating + details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ @@ -307,7 +543,11 @@ spec: - Unknown type: string type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -324,43 +564,75 @@ spec: - type x-kubernetes-list-type: map listeners: - description: Listeners provide status for each unique listener port defined in the Spec. + description: Listeners provide status for each unique listener port + defined in the Spec. items: description: ListenerStatus is the status associated with a Listener. properties: conditions: - description: Conditions describe the current condition of this listener. + description: Conditions describe the current condition of this + listener. items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + description: "Condition contains details for one aspect of + the current state of this API Resource. --- This struct + is intended for direct use as an array at the field path + .status.conditions. For example, type FooStatus struct{ + \ // Represents the observations of a foo's current state. + \ // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type + \ // +patchStrategy=merge // +listType=map // + +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` + \n // other fields }" properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should + be when the underlying condition changed. If that is + not known, then using the time when the API field changed + is acceptable. format: date-time type: string message: - description: message is a human readable message indicating details about the transition. This may be an empty string. + description: message is a human readable message indicating + details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, + if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the + current state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + description: reason contains a programmatic identifier + indicating the reason for the condition's last transition. + Producers of specific condition types may define expected + values and meanings for this field, and whether the + values are considered a guaranteed API. The value should + be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: - description: status of the condition, one of True, False, Unknown. + description: status of the condition, one of True, False, + Unknown. enum: - "True" - "False" - Unknown type: string type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across + resources like Available, but because arbitrary conditions + can be useful (see .node.status.conditions), the ability + to deconflict is important. The regex it matches is + (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -377,18 +649,21 @@ spec: - type x-kubernetes-list-type: map hostname: - description: Hostname is the Listener hostname value for which this message is reporting the status. + description: Hostname is the Listener hostname value for which + this message is reporting the status. maxLength: 253 minLength: 1 type: string port: - description: Port is the unique Listener port value for which this message is reporting the status. + description: Port is the unique Listener port value for which + this message is reporting the status. format: int32 maximum: 65535 minimum: 1 type: integer protocol: - description: Protocol is the Listener protocol value for which this message is reporting the status. + description: Protocol is the Listener protocol value for which + this message is reporting the status. type: string required: - conditions diff --git a/docs/content/reference/dynamic-configuration/networking.x-k8s.io_httproutes.yaml b/docs/content/reference/dynamic-configuration/networking.x-k8s.io_httproutes.yaml index 7e2392067..d64108b70 100644 --- a/docs/content/reference/dynamic-configuration/networking.x-k8s.io_httproutes.yaml +++ b/docs/content/reference/dynamic-configuration/networking.x-k8s.io_httproutes.yaml @@ -4,12 +4,14 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.4.1 + controller-gen.kubebuilder.io/version: v0.5.0 creationTimestamp: null name: httproutes.networking.x-k8s.io spec: group: networking.x-k8s.io names: + categories: + - gateway-api kind: HTTPRoute listKind: HTTPRouteList plural: httproutes @@ -20,16 +22,23 @@ spec: - jsonPath: .spec.hostnames name: Hostnames type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1alpha1 schema: openAPIV3Schema: description: HTTPRoute is the Schema for the HTTPRoute resource. properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object @@ -43,16 +52,24 @@ spec: properties: allow: default: SameNamespace - description: 'Allow indicates which Gateways will be allowed to use this route. Possible values are: * All: Gateways in any namespace can use this route. * FromList: Only Gateways specified in GatewayRefs may use this route. * SameNamespace: Only Gateways in the same namespace may use this route.' + description: 'Allow indicates which Gateways will be allowed to + use this route. Possible values are: * All: Gateways in any + namespace can use this route. * FromList: Only Gateways specified + in GatewayRefs may use this route. * SameNamespace: Only Gateways + in the same namespace may use this route.' enum: - All - FromList - SameNamespace type: string gatewayRefs: - description: GatewayRefs must be specified when Allow is set to "FromList". In that case, only Gateways referenced in this list will be allowed to use this route. This field is ignored for other values of "Allow". + description: GatewayRefs must be specified when Allow is set to + "FromList". In that case, only Gateways referenced in this list + will be allowed to use this route. This field is ignored for + other values of "Allow". items: - description: GatewayReference identifies a Gateway in a specified namespace. + description: GatewayReference identifies a Gateway in a specified + namespace. properties: name: description: Name is the name of the referent. @@ -71,9 +88,29 @@ spec: type: array type: object hostnames: - description: "Hostnames defines a set of hostname that should match against the HTTP Host header to select a HTTPRoute to process the request. Hostname is the fully qualified domain name of a network host, as defined by RFC 3986. Note the following deviations from the \"host\" part of the URI as defined in the RFC: \n 1. IPs are not allowed. 2. The `:` delimiter is not respected because ports are not allowed. \n Incoming requests are matched against the hostnames before the HTTPRoute rules. If no hostname is specified, traffic is routed based on the HTTPRouteRules. \n Hostname can be \"precise\" which is a domain name without the terminating dot of a network host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). The wildcard character `*` must appear by itself as the first DNS label and matches only a single label. You cannot have a wildcard label by itself (e.g. Host == `*`). Requests will be matched against the Host field in the following order: \n 1. If Host is precise, the request matches this rule if the HTTP Host header is equal to Host. 2. If Host is a wildcard, then the request matches this rule if the HTTP Host header is to equal to the suffix (removing the first label) of the wildcard rule. \n Support: Core" + description: "Hostnames defines a set of hostname that should match + against the HTTP Host header to select a HTTPRoute to process the + request. Hostname is the fully qualified domain name of a network + host, as defined by RFC 3986. Note the following deviations from + the \"host\" part of the URI as defined in the RFC: \n 1. IPs are + not allowed. 2. The `:` delimiter is not respected because ports + are not allowed. \n Incoming requests are matched against the hostnames + before the HTTPRoute rules. If no hostname is specified, traffic + is routed based on the HTTPRouteRules. \n Hostname can be \"precise\" + which is a domain name without the terminating dot of a network + host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain + name prefixed with a single wildcard label (e.g. `*.example.com`). + The wildcard character `*` must appear by itself as the first DNS + label and matches only a single label. You cannot have a wildcard + label by itself (e.g. Host == `*`). Requests will be matched against + the Host field in the following order: \n 1. If Host is precise, + the request matches this rule if the HTTP Host header is equal + to Host. 2. If Host is a wildcard, then the request matches this + rule if the HTTP Host header is to equal to the suffix (removing + the first label) of the wildcard rule. \n Support: Core" items: - description: Hostname is used to specify a hostname that should be matched. + description: Hostname is used to specify a hostname that should + be matched. maxLength: 253 minLength: 1 type: string @@ -87,15 +124,40 @@ spec: value: / description: Rules are a list of HTTP matchers, filters and actions. items: - description: HTTPRouteRule defines semantics for matching an HTTP request based on conditions, optionally executing additional processing steps, and forwarding the request to an API object. + description: HTTPRouteRule defines semantics for matching an HTTP + request based on conditions, optionally executing additional processing + steps, and forwarding the request to an API object. properties: filters: - description: "Filters define the filters that are applied to requests that match this rule. \n The effects of ordering of multiple behaviors are currently unspecified. This can change in the future based on feedback during the alpha stage. \n Conformance-levels at this level are defined based on the type of filter: \n - ALL core filters MUST be supported by all implementations. - Implementers are encouraged to support extended filters. - Implementation-specific custom filters have no API guarantees across implementations. \n Specifying a core filter multiple times has unspecified or custom conformance. \n Support: Core" + description: "Filters define the filters that are applied to + requests that match this rule. \n The effects of ordering + of multiple behaviors are currently unspecified. This can + change in the future based on feedback during the alpha stage. + \n Conformance-levels at this level are defined based on the + type of filter: \n - ALL core filters MUST be supported by + all implementations. - Implementers are encouraged to support + extended filters. - Implementation-specific custom filters + have no API guarantees across implementations. \n Specifying + a core filter multiple times has unspecified or custom conformance. + \n Support: Core" items: - description: 'HTTPRouteFilter defines additional processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express additional processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. TODO(hbagdi): re-render CRDs once controller-tools supports union tags: - https://github.com/kubernetes-sigs/controller-tools/pull/298 - https://github.com/kubernetes-sigs/controller-tools/issues/461' + description: 'HTTPRouteFilter defines additional processing + steps that must be completed during the request or response + lifecycle. HTTPRouteFilters are meant as an extension point + to express additional processing that may be done in Gateway + implementations. Some examples include request or response + modification, implementing authentication strategies, rate-limiting, + and traffic shaping. API guarantee/conformance is defined + based on the type of the filter. TODO(hbagdi): re-render + CRDs once controller-tools supports union tags: - https://github.com/kubernetes-sigs/controller-tools/pull/298 + - https://github.com/kubernetes-sigs/controller-tools/issues/461' properties: extensionRef: - description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.acme.io\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" + description: "ExtensionRef is an optional, implementation-specific + extension to the \"filter\" behavior. For example, + resource \"myroutefilter\" in group \"networking.acme.io\"). + ExtensionRef MUST NOT be used for core and extended + filters. \n Support: Implementation-specific" properties: group: description: Group is the group of the referent. @@ -118,15 +180,30 @@ spec: - name type: object requestHeaderModifier: - description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" + description: "RequestHeaderModifier defines a schema for + a filter that modifies request headers. \n Support: + Core" properties: add: additionalProperties: type: string - description: "Add adds the given header (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: {\"my-header\": \"bar\"} \n Output: GET /foo HTTP/1.1 my-header: foo my-header: bar \n Support: Extended" + description: "Add adds the given header (name, value) + to the request before the action. It appends to + any existing values associated with the header name. + \n Input: GET /foo HTTP/1.1 my-header: foo \n + Config: add: {\"my-header\": \"bar\"} \n Output: + \ GET /foo HTTP/1.1 my-header: foo my-header: + bar \n Support: Extended" type: object remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of RemoveHeader is a list of HTTP header names. Note that the header names are case-insensitive [RFC-2616 4.2]. \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar \n Support: Extended" + description: "Remove the given header(s) from the + HTTP request before the action. The value of RemoveHeader + is a list of HTTP header names. Note that the header + names are case-insensitive [RFC-2616 4.2]. \n Input: + \ GET /foo HTTP/1.1 my-header1: foo my-header2: + bar my-header3: baz \n Config: remove: [\"my-header1\", + \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: + bar \n Support: Extended" items: type: string maxItems: 16 @@ -134,14 +211,28 @@ spec: set: additionalProperties: type: string - description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: {\"my-header\": \"bar\"} \n Output: GET /foo HTTP/1.1 my-header: bar \n Support: Extended" + description: "Set overwrites the request with the + given header (name, value) before the action. \n + Input: GET /foo HTTP/1.1 my-header: foo \n Config: + \ set: {\"my-header\": \"bar\"} \n Output: GET + /foo HTTP/1.1 my-header: bar \n Support: Extended" type: object type: object requestMirror: - description: "RequestMirror defines a schema for a filter that mirrors requests. \n Support: Extended" + description: "RequestMirror defines a schema for a filter + that mirrors requests. \n Support: Extended" properties: backendRef: - description: "BackendRef is a local object reference to mirror matched requests to. If both BackendRef and ServiceName are specified, ServiceName will be given precedence. \n If the referent cannot be found, the rule is not included in the route. The controller should raise the \"ResolvedRefs\" condition on the Gateway with the \"DegradedRoutes\" reason. The gateway status for this route should be updated with a condition that describes the error more specifically. \n Support: Custom" + description: "BackendRef is a local object reference + to mirror matched requests to. If both BackendRef + and ServiceName are specified, ServiceName will + be given precedence. \n If the referent cannot be + found, the rule is not included in the route. The + controller should raise the \"ResolvedRefs\" condition + on the Gateway with the \"DegradedRoutes\" reason. + The gateway status for this route should be updated + with a condition that describes the error more specifically. + \n Support: Custom" properties: group: description: Group is the group of the referent. @@ -164,18 +255,51 @@ spec: - name type: object port: - description: "Port specifies the destination port number to use for the backend referenced by the ServiceName or BackendRef field. \n If unspecified, the destination port in the request is used when forwarding to a backendRef or serviceName." + description: "Port specifies the destination port + number to use for the backend referenced by the + ServiceName or BackendRef field. \n If unspecified, + the destination port in the request is used when + forwarding to a backendRef or serviceName." format: int32 maximum: 65535 minimum: 1 type: integer serviceName: - description: "ServiceName refers to the name of the Service to mirror matched requests to. When specified, this takes the place of BackendRef. If both BackendRef and ServiceName are specified, ServiceName will be given precedence. \n If the referent cannot be found, the rule is not included in the route. The controller should raise the \"ResolvedRefs\" condition on the Gateway with the \"DegradedRoutes\" reason. The gateway status for this route should be updated with a condition that describes the error more specifically. \n Support: Core" + description: "ServiceName refers to the name of the + Service to mirror matched requests to. When specified, + this takes the place of BackendRef. If both BackendRef + and ServiceName are specified, ServiceName will + be given precedence. \n If the referent cannot be + found, the rule is not included in the route. The + controller should raise the \"ResolvedRefs\" condition + on the Gateway with the \"DegradedRoutes\" reason. + The gateway status for this route should be updated + with a condition that describes the error more specifically. + \n Support: Core" maxLength: 253 type: string type: object type: - description: "Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations must support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - Custom: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` should be set to \"ExtensionRef\" for custom filters. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior." + description: "Type identifies the type of filter to apply. + As with other API fields, types are classified into + three conformance levels: \n - Core: Filter types and + their corresponding configuration defined by \"Support: + Core\" in this package, e.g. \"RequestHeaderModifier\". + All implementations must support core filters. \n + - Extended: Filter types and their corresponding configuration + defined by \"Support: Extended\" in this package, + e.g. \"RequestMirror\". Implementers are encouraged + to support extended filters. \n - Custom: Filters that + are defined and supported by specific vendors. In + the future, filters showing convergence in behavior + across multiple implementations will be considered + for inclusion in extended or core conformance levels. + Filter-specific configuration for such filters is + specified using the ExtensionRef field. `Type` should + be set to \"ExtensionRef\" for custom filters. \n + Implementers are encouraged to define custom implementation + types to extend the core API with implementation-specific + behavior." enum: - RequestHeaderModifier - RequestMirror @@ -187,12 +311,25 @@ spec: maxItems: 16 type: array forwardTo: - description: ForwardTo defines the backend(s) where matching requests should be sent. If unspecified, the rule performs no forwarding. If unspecified and no filters are specified that would result in a response being sent, a 503 error code is returned. + description: ForwardTo defines the backend(s) where matching + requests should be sent. If unspecified, the rule performs + no forwarding. If unspecified and no filters are specified + that would result in a response being sent, a 503 error code + is returned. items: - description: HTTPRouteForwardTo defines how a HTTPRoute should forward a request. + description: HTTPRouteForwardTo defines how a HTTPRoute should + forward a request. properties: backendRef: - description: "BackendRef is a reference to a backend to forward matched requests to. If both BackendRef and ServiceName are specified, ServiceName will be given precedence. \n If the referent cannot be found, the route must be dropped from the Gateway. The controller should raise the \"ResolvedRefs\" condition on the Gateway with the \"DegradedRoutes\" reason. The gateway status for this route should be updated with a condition that describes the error more specifically. \n Support: Custom" + description: "BackendRef is a reference to a backend to + forward matched requests to. If both BackendRef and + ServiceName are specified, ServiceName will be given + precedence. \n If the referent cannot be found, the + route must be dropped from the Gateway. The controller + should raise the \"ResolvedRefs\" condition on the Gateway + with the \"DegradedRoutes\" reason. The gateway status + for this route should be updated with a condition that + describes the error more specifically. \n Support: Custom" properties: group: description: Group is the group of the referent. @@ -215,12 +352,31 @@ spec: - name type: object filters: - description: "Filters defined at this-level should be executed if and only if the request is being forwarded to the backend defined here. \n Support: Custom (For broader support of filters, use the Filters field in HTTPRouteRule.)" + description: "Filters defined at this-level should be + executed if and only if the request is being forwarded + to the backend defined here. \n Support: Custom (For + broader support of filters, use the Filters field in + HTTPRouteRule.)" items: - description: 'HTTPRouteFilter defines additional processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express additional processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. TODO(hbagdi): re-render CRDs once controller-tools supports union tags: - https://github.com/kubernetes-sigs/controller-tools/pull/298 - https://github.com/kubernetes-sigs/controller-tools/issues/461' + description: 'HTTPRouteFilter defines additional processing + steps that must be completed during the request or + response lifecycle. HTTPRouteFilters are meant as + an extension point to express additional processing + that may be done in Gateway implementations. Some + examples include request or response modification, + implementing authentication strategies, rate-limiting, + and traffic shaping. API guarantee/conformance is + defined based on the type of the filter. TODO(hbagdi): + re-render CRDs once controller-tools supports union + tags: - https://github.com/kubernetes-sigs/controller-tools/pull/298 + - https://github.com/kubernetes-sigs/controller-tools/issues/461' properties: extensionRef: - description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.acme.io\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" + description: "ExtensionRef is an optional, implementation-specific + extension to the \"filter\" behavior. For example, + resource \"myroutefilter\" in group \"networking.acme.io\"). + ExtensionRef MUST NOT be used for core and extended + filters. \n Support: Implementation-specific" properties: group: description: Group is the group of the referent. @@ -243,15 +399,32 @@ spec: - name type: object requestHeaderModifier: - description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" + description: "RequestHeaderModifier defines a schema + for a filter that modifies request headers. \n + Support: Core" properties: add: additionalProperties: type: string - description: "Add adds the given header (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: {\"my-header\": \"bar\"} \n Output: GET /foo HTTP/1.1 my-header: foo my-header: bar \n Support: Extended" + description: "Add adds the given header (name, + value) to the request before the action. It + appends to any existing values associated + with the header name. \n Input: GET /foo + HTTP/1.1 my-header: foo \n Config: add: + {\"my-header\": \"bar\"} \n Output: GET + /foo HTTP/1.1 my-header: foo my-header: + bar \n Support: Extended" type: object remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of RemoveHeader is a list of HTTP header names. Note that the header names are case-insensitive [RFC-2616 4.2]. \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar \n Support: Extended" + description: "Remove the given header(s) from + the HTTP request before the action. The value + of RemoveHeader is a list of HTTP header names. + Note that the header names are case-insensitive + [RFC-2616 4.2]. \n Input: GET /foo HTTP/1.1 + \ my-header1: foo my-header2: bar my-header3: + baz \n Config: remove: [\"my-header1\", + \"my-header3\"] \n Output: GET /foo HTTP/1.1 + \ my-header2: bar \n Support: Extended" items: type: string maxItems: 16 @@ -259,14 +432,30 @@ spec: set: additionalProperties: type: string - description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: {\"my-header\": \"bar\"} \n Output: GET /foo HTTP/1.1 my-header: bar \n Support: Extended" + description: "Set overwrites the request with + the given header (name, value) before the + action. \n Input: GET /foo HTTP/1.1 my-header: + foo \n Config: set: {\"my-header\": \"bar\"} + \n Output: GET /foo HTTP/1.1 my-header: + bar \n Support: Extended" type: object type: object requestMirror: - description: "RequestMirror defines a schema for a filter that mirrors requests. \n Support: Extended" + description: "RequestMirror defines a schema for + a filter that mirrors requests. \n Support: Extended" properties: backendRef: - description: "BackendRef is a local object reference to mirror matched requests to. If both BackendRef and ServiceName are specified, ServiceName will be given precedence. \n If the referent cannot be found, the rule is not included in the route. The controller should raise the \"ResolvedRefs\" condition on the Gateway with the \"DegradedRoutes\" reason. The gateway status for this route should be updated with a condition that describes the error more specifically. \n Support: Custom" + description: "BackendRef is a local object reference + to mirror matched requests to. If both BackendRef + and ServiceName are specified, ServiceName + will be given precedence. \n If the referent + cannot be found, the rule is not included + in the route. The controller should raise + the \"ResolvedRefs\" condition on the Gateway + with the \"DegradedRoutes\" reason. The gateway + status for this route should be updated with + a condition that describes the error more + specifically. \n Support: Custom" properties: group: description: Group is the group of the referent. @@ -289,18 +478,54 @@ spec: - name type: object port: - description: "Port specifies the destination port number to use for the backend referenced by the ServiceName or BackendRef field. \n If unspecified, the destination port in the request is used when forwarding to a backendRef or serviceName." + description: "Port specifies the destination + port number to use for the backend referenced + by the ServiceName or BackendRef field. \n + If unspecified, the destination port in the + request is used when forwarding to a backendRef + or serviceName." format: int32 maximum: 65535 minimum: 1 type: integer serviceName: - description: "ServiceName refers to the name of the Service to mirror matched requests to. When specified, this takes the place of BackendRef. If both BackendRef and ServiceName are specified, ServiceName will be given precedence. \n If the referent cannot be found, the rule is not included in the route. The controller should raise the \"ResolvedRefs\" condition on the Gateway with the \"DegradedRoutes\" reason. The gateway status for this route should be updated with a condition that describes the error more specifically. \n Support: Core" + description: "ServiceName refers to the name + of the Service to mirror matched requests + to. When specified, this takes the place of + BackendRef. If both BackendRef and ServiceName + are specified, ServiceName will be given precedence. + \n If the referent cannot be found, the rule + is not included in the route. The controller + should raise the \"ResolvedRefs\" condition + on the Gateway with the \"DegradedRoutes\" + reason. The gateway status for this route + should be updated with a condition that describes + the error more specifically. \n Support: Core" maxLength: 253 type: string type: object type: - description: "Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations must support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - Custom: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` should be set to \"ExtensionRef\" for custom filters. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior." + description: "Type identifies the type of filter + to apply. As with other API fields, types are + classified into three conformance levels: \n - + Core: Filter types and their corresponding configuration + defined by \"Support: Core\" in this package, + e.g. \"RequestHeaderModifier\". All implementations + must support core filters. \n - Extended: Filter + types and their corresponding configuration defined + by \"Support: Extended\" in this package, e.g. + \"RequestMirror\". Implementers are encouraged + to support extended filters. \n - Custom: Filters + that are defined and supported by specific vendors. + \ In the future, filters showing convergence + in behavior across multiple implementations + will be considered for inclusion in extended or + core conformance levels. Filter-specific configuration + for such filters is specified using the ExtensionRef + field. `Type` should be set to \"ExtensionRef\" + for custom filters. \n Implementers are encouraged + to define custom implementation types to extend + the core API with implementation-specific behavior." enum: - RequestHeaderModifier - RequestMirror @@ -312,18 +537,53 @@ spec: maxItems: 16 type: array port: - description: "Port specifies the destination port number to use for the backend referenced by the ServiceName or BackendRef field. If unspecified, the destination port in the request is used when forwarding to a backendRef or serviceName. \n Support: Core" + description: "Port specifies the destination port number + to use for the backend referenced by the ServiceName + or BackendRef field. If unspecified, the destination + port in the request is used when forwarding to a backendRef + or serviceName. \n Support: Core" format: int32 maximum: 65535 minimum: 1 type: integer serviceName: - description: "ServiceName refers to the name of the Service to forward matched requests to. When specified, this takes the place of BackendRef. If both BackendRef and ServiceName are specified, ServiceName will be given precedence. \n If the referent cannot be found, the route must be dropped from the Gateway. The controller should raise the \"ResolvedRefs\" condition on the Gateway with the \"DegradedRoutes\" reason. The gateway status for this route should be updated with a condition that describes the error more specifically. \n The protocol to use should be specified with the AppProtocol field on Service resources. This field was introduced in Kubernetes 1.18. If using an earlier version of Kubernetes, a `networking.x-k8s.io/app-protocol` annotation on the BackendPolicy resource may be used to define the protocol. If the AppProtocol field is available, this annotation should not be used. The AppProtocol field, when populated, takes precedence over the annotation in the BackendPolicy resource. For custom backends, it is encouraged to add a semantically-equivalent field in the Custom Resource Definition. \n Support: Core" + description: "ServiceName refers to the name of the Service + to forward matched requests to. When specified, this + takes the place of BackendRef. If both BackendRef and + ServiceName are specified, ServiceName will be given + precedence. \n If the referent cannot be found, the + route must be dropped from the Gateway. The controller + should raise the \"ResolvedRefs\" condition on the Gateway + with the \"DegradedRoutes\" reason. The gateway status + for this route should be updated with a condition that + describes the error more specifically. \n The protocol + to use should be specified with the AppProtocol field + on Service resources. This field was introduced in Kubernetes + 1.18. If using an earlier version of Kubernetes, a `networking.x-k8s.io/app-protocol` + annotation on the BackendPolicy resource may be used + to define the protocol. If the AppProtocol field is + available, this annotation should not be used. The AppProtocol + field, when populated, takes precedence over the annotation + in the BackendPolicy resource. For custom backends, + it is encouraged to add a semantically-equivalent field + in the Custom Resource Definition. \n Support: Core" maxLength: 253 type: string weight: default: 1 - description: "Weight specifies the proportion of HTTP requests forwarded to the backend referenced by the ServiceName or BackendRef field. This is computed as weight/(sum of all weights in this ForwardTo list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support: Core" + description: "Weight specifies the proportion of HTTP + requests forwarded to the backend referenced by the + ServiceName or BackendRef field. This is computed as + weight/(sum of all weights in this ForwardTo list). + For non-zero values, there may be some epsilon from + the exact proportion defined here depending on the precision + an implementation supports. Weight is not a percentage + and the sum of weights does not need to equal 100. \n + If only one backend is specified and it has a weight + greater than 0, 100% of the traffic is forwarded to + that backend. If weight is set to 0, no traffic should + be forwarded for this entry. If unspecified, weight + defaults to 1. \n Support: Core" format: int32 maximum: 1000000 minimum: 0 @@ -336,12 +596,55 @@ spec: - path: type: Prefix value: / - description: "Matches define conditions used for matching the rule against incoming HTTP requests. Each match is independent, i.e. this rule will be matched if **any** one of the matches is satisfied. \n For example, take the following matches configuration: \n ``` matches: - path: value: \"/foo\" headers: values: version: \"2\" - path: value: \"/v2/foo\" ``` \n For a request to match against this rule, a request should satisfy EITHER of the two conditions: \n - path prefixed with `/foo` AND contains the header `version: \"2\"` - path prefix of `/v2/foo` \n See the documentation for HTTPRouteMatch on how to specify multiple match conditions that should be ANDed together. \n If no matches are specified, the default is a prefix path match on \"/\", which has the effect of matching every HTTP request. \n A client request may match multiple HTTP route rules. Matching precedence MUST be determined in order of the following criteria, continuing on ties: \n * The longest matching hostname. * The longest matching path. * The largest number of header matches * The oldest Route based on creation timestamp. For example, a Route with a creation timestamp of \"2020-09-08 01:02:03\" is given precedence over a Route with a creation timestamp of \"2020-09-08 01:02:04\". * The Route appearing first in alphabetical order (namespace/name) for example, foo/bar is given precedence over foo/baz." + description: "Matches define conditions used for matching the + rule against incoming HTTP requests. Each match is independent, + i.e. this rule will be matched if **any** one of the matches + is satisfied. \n For example, take the following matches configuration: + \n ``` matches: - path: value: \"/foo\" headers: values: + \ version: \"2\" - path: value: \"/v2/foo\" ``` \n + For a request to match against this rule, a request should + satisfy EITHER of the two conditions: \n - path prefixed with + `/foo` AND contains the header `version: \"2\"` - path prefix + of `/v2/foo` \n See the documentation for HTTPRouteMatch on + how to specify multiple match conditions that should be ANDed + together. \n If no matches are specified, the default is a + prefix path match on \"/\", which has the effect of matching + every HTTP request. \n Each client request MUST map to a maximum + of one route rule. If a request matches multiple rules, matching + precedence MUST be determined in order of the following criteria, + continuing on ties: \n * The longest matching hostname. * + The longest matching path. * The largest number of header + matches. \n If ties still exist across multiple Routes, matching + precedence MUST be determined in order of the following criteria, + continuing on ties: \n * The oldest Route based on creation + timestamp. For example, a Route with a creation timestamp + of \"2020-09-08 01:02:03\" is given precedence over a Route + with a creation timestamp of \"2020-09-08 01:02:04\". * The + Route appearing first in alphabetical order by \"/\". + For example, foo/bar is given precedence over foo/baz. \n + If ties still exist within the Route that has been given precedence, + matching precedence MUST be granted to the first matching + rule meeting the above criteria." items: - description: "HTTPRouteMatch defines the predicate used to match requests to a given action. Multiple match types are ANDed together, i.e. the match will evaluate to true only if all conditions are satisfied. \n For example, the match below will match a HTTP request only if its path starts with `/foo` AND it contains the `version: \"1\"` header: \n ``` match: path: value: \"/foo\" headers: values: version: \"1\" ```" + description: "HTTPRouteMatch defines the predicate used to + match requests to a given action. Multiple match types are + ANDed together, i.e. the match will evaluate to true only + if all conditions are satisfied. \n For example, the match + below will match a HTTP request only if its path starts + with `/foo` AND it contains the `version: \"1\"` header: + \n ``` match: path: value: \"/foo\" headers: values: + \ version: \"1\" ```" properties: extensionRef: - description: "ExtensionRef is an optional, implementation-specific extension to the \"match\" behavior. For example, resource \"myroutematcher\" in group \"networking.acme.io\". If the referent cannot be found, the rule is not included in the route. The controller should raise the \"ResolvedRefs\" condition on the Gateway with the \"DegradedRoutes\" reason. The gateway status for this route should be updated with a condition that describes the error more specifically. \n Support: Custom" + description: "ExtensionRef is an optional, implementation-specific + extension to the \"match\" behavior. For example, resource + \"myroutematcher\" in group \"networking.acme.io\". + If the referent cannot be found, the rule is not included + in the route. The controller should raise the \"ResolvedRefs\" + condition on the Gateway with the \"DegradedRoutes\" + reason. The gateway status for this route should be + updated with a condition that describes the error more + specifically. \n Support: Custom" properties: group: description: Group is the group of the referent. @@ -368,7 +671,15 @@ spec: properties: type: default: Exact - description: "Type specifies how to match against the value of the header. \n Support: Core (Exact) \n Support: Custom (RegularExpression, ImplementationSpecific) \n Since RegularExpression PathType has custom conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect. \n HTTP Header name matching MUST be case-insensitive (RFC 2616 - section 4.2)." + description: "Type specifies how to match against + the value of the header. \n Support: Core (Exact) + \n Support: Custom (RegularExpression, ImplementationSpecific) + \n Since RegularExpression PathType has custom conformance, + implementations can support POSIX, PCRE or any other + dialects of regular expressions. Please read the + implementation's documentation to determine the + supported dialect. \n HTTP Header name matching + MUST be case-insensitive (RFC 2616 - section 4.2)." enum: - Exact - RegularExpression @@ -377,7 +688,14 @@ spec: values: additionalProperties: type: string - description: "Values is a map of HTTP Headers to be matched. It MUST contain at least one entry. \n The HTTP header field name to match is the map key, and the value of the HTTP header is the map value. HTTP header field name matching MUST be case-insensitive. \n Multiple match values are ANDed together, meaning, a request must match all the specified headers to select the route." + description: "Values is a map of HTTP Headers to be + matched. It MUST contain at least one entry. \n + The HTTP header field name to match is the map key, + and the value of the HTTP header is the map value. + HTTP header field name matching MUST be case-insensitive. + \n Multiple match values are ANDed together, meaning, + a request must match all the specified headers to + select the route." type: object required: - values @@ -386,11 +704,20 @@ spec: default: type: Prefix value: / - description: Path specifies a HTTP request path matcher. If this field is not specified, a default prefix match on the "/" path is provided. + description: Path specifies a HTTP request path matcher. + If this field is not specified, a default prefix match + on the "/" path is provided. properties: type: default: Prefix - description: "Type specifies how to match against the path Value. \n Support: Core (Exact, Prefix) \n Support: Custom (RegularExpression, ImplementationSpecific) \n Since RegularExpression PathType has custom conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect." + description: "Type specifies how to match against + the path Value. \n Support: Core (Exact, Prefix) + \n Support: Custom (RegularExpression, ImplementationSpecific) + \n Since RegularExpression PathType has custom conformance, + implementations can support POSIX, PCRE or any other + dialects of regular expressions. Please read the + implementation's documentation to determine the + supported dialect." enum: - Exact - Prefix @@ -398,11 +725,46 @@ spec: - ImplementationSpecific type: string value: + default: / description: Value of the HTTP path to match against. - minLength: 1 type: string + type: object + queryParams: + description: QueryParams specifies a HTTP query parameter + matcher. + properties: + type: + default: Exact + description: "Type specifies how to match against + the value of the query parameter. \n Support: Extended + (Exact) \n Support: Custom (RegularExpression, ImplementationSpecific) + \n Since RegularExpression QueryParamMatchType has + custom conformance, implementations can support + POSIX, PCRE or any other dialects of regular expressions. + Please read the implementation's documentation to + determine the supported dialect." + enum: + - Exact + - RegularExpression + - ImplementationSpecific + type: string + values: + additionalProperties: + type: string + description: "Values is a map of HTTP query parameters + to be matched. It MUST contain at least one entry. + \n The query parameter name to match is the map + key, and the value of the query parameter is the + map value. \n Multiple match values are ANDed together, + meaning, a request must match all the specified + query parameters to select the route. \n HTTP query + parameter matching MUST be case-sensitive for both + keys and values. (See https://tools.ietf.org/html/rfc7230#section-2.7.3). + \n Note that the query parameter key MUST always + be an exact match by string comparison." + type: object required: - - value + - values type: object type: object maxItems: 8 @@ -411,10 +773,31 @@ spec: maxItems: 16 type: array tls: - description: "TLS defines the TLS certificate to use for Hostnames defined in this Route. This configuration only takes effect if the AllowRouteOverride field is set to true in the associated Gateway resource. \n Collisions can happen if multiple HTTPRoutes define a TLS certificate for the same hostname. In such a case, conflict resolution guiding principles apply, specifically, if hostnames are same and two different certificates are specified then the certificate in the oldest resource wins. \n Please note that HTTP Route-selection takes place after the TLS Handshake (ClientHello). Due to this, TLS certificate defined here will take precedence even if the request has the potential to match multiple routes (in case multiple HTTPRoutes share the same hostname). \n Support: Core" + description: "TLS defines the TLS certificate to use for Hostnames + defined in this Route. This configuration only takes effect if the + AllowRouteOverride field is set to true in the associated Gateway + resource. \n Collisions can happen if multiple HTTPRoutes define + a TLS certificate for the same hostname. In such a case, conflict + resolution guiding principles apply, specifically, if hostnames + are same and two different certificates are specified then the certificate + in the oldest resource wins. \n Please note that HTTP Route-selection + takes place after the TLS Handshake (ClientHello). Due to this, + TLS certificate defined here will take precedence even if the request + has the potential to match multiple routes (in case multiple HTTPRoutes + share the same hostname). \n Support: Core" properties: certificateRef: - description: "CertificateRef refers to a Kubernetes object that contains a TLS certificate and private key. This certificate MUST be used for TLS handshakes for the domain this RouteTLSConfig is associated with. If an entry in this list omits or specifies the empty string for both the group and kind, the resource defaults to \"secrets\". An implementation may support other resources (for example, resource \"mycertificates\" in group \"networking.acme.io\"). \n Support: Core (Kubernetes Secrets) \n Support: Implementation-specific (Other resource types)" + description: "CertificateRef is a reference to a Kubernetes object + that contains a TLS certificate and private key. This certificate + is used to establish a TLS handshake for requests that match + the hostname of the associated HTTPRoute. The referenced object + MUST reside in the same namespace as HTTPRoute. \n This field + is required when the TLS configuration mode of the associated + Gateway listener is set to \"Passthrough\". \n CertificateRef + can reference a standard Kubernetes resource, i.e. Secret, or + an implementation-specific custom resource. \n Support: Core + (Kubernetes Secrets) \n Support: Implementation-specific (Other + resource types)" properties: group: description: Group is the group of the referent. @@ -444,43 +827,87 @@ spec: description: Status defines the current state of HTTPRoute. properties: gateways: - description: "Gateways is a list of Gateways that are associated with the route, and the status of the route with respect to each Gateway. When a Gateway selects this route, the controller that manages the Gateway must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route is modified. \n A maximum of 100 Gateways will be represented in this list. If this list is full, there may be additional Gateways using this Route that are not included in the list. An empty list means the route has not been admitted by any Gateway." + description: "Gateways is a list of Gateways that are associated with + the route, and the status of the route with respect to each Gateway. + When a Gateway selects this route, the controller that manages the + Gateway must add an entry to this list when the controller first + sees the route and should update the entry as appropriate when the + route is modified. \n A maximum of 100 Gateways will be represented + in this list. If this list is full, there may be additional Gateways + using this Route that are not included in the list. An empty list + means the route has not been admitted by any Gateway." items: - description: RouteGatewayStatus describes the status of a route with respect to an associated Gateway. + description: RouteGatewayStatus describes the status of a route + with respect to an associated Gateway. properties: conditions: - description: Conditions describes the status of the route with respect to the Gateway. The "Admitted" condition must always be specified by controllers to indicate whether the route has been admitted or rejected by the Gateway, and why. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. + description: Conditions describes the status of the route with + respect to the Gateway. The "Admitted" condition must always + be specified by controllers to indicate whether the route + has been admitted or rejected by the Gateway, and why. Note + that the route's availability is also subject to the Gateway's + own status conditions and listener status. items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + description: "Condition contains details for one aspect of + the current state of this API Resource. --- This struct + is intended for direct use as an array at the field path + .status.conditions. For example, type FooStatus struct{ + \ // Represents the observations of a foo's current state. + \ // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type + \ // +patchStrategy=merge // +listType=map // + +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` + \n // other fields }" properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should + be when the underlying condition changed. If that is + not known, then using the time when the API field changed + is acceptable. format: date-time type: string message: - description: message is a human readable message indicating details about the transition. This may be an empty string. + description: message is a human readable message indicating + details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, + if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the + current state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + description: reason contains a programmatic identifier + indicating the reason for the condition's last transition. + Producers of specific condition types may define expected + values and meanings for this field, and whether the + values are considered a guaranteed API. The value should + be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: - description: status of the condition, one of True, False, Unknown. + description: status of the condition, one of True, False, + Unknown. enum: - "True" - "False" - Unknown type: string type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across + resources like Available, but because arbitrary conditions + can be useful (see .node.status.conditions), the ability + to deconflict is important. The regex it matches is + (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -497,8 +924,18 @@ spec: - type x-kubernetes-list-type: map gatewayRef: - description: GatewayRef is a reference to a Gateway object that is associated with the route. + description: GatewayRef is a reference to a Gateway object that + is associated with the route. properties: + controller: + description: "Controller is a domain/path string that indicates + the controller implementing the Gateway. This corresponds + with the controller field on GatewayClass. \n Example: + \"acme.io/gateway-controller\". \n The format of this + field is DOMAIN \"/\" PATH, where DOMAIN and PATH are + valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names)." + maxLength: 253 + type: string name: description: Name is the name of the referent. maxLength: 253 diff --git a/docs/content/reference/dynamic-configuration/networking.x-k8s.io_tcproutes.yaml b/docs/content/reference/dynamic-configuration/networking.x-k8s.io_tcproutes.yaml index 9a7631420..c9b62c458 100644 --- a/docs/content/reference/dynamic-configuration/networking.x-k8s.io_tcproutes.yaml +++ b/docs/content/reference/dynamic-configuration/networking.x-k8s.io_tcproutes.yaml @@ -4,28 +4,38 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.4.1 + controller-gen.kubebuilder.io/version: v0.5.0 creationTimestamp: null name: tcproutes.networking.x-k8s.io spec: group: networking.x-k8s.io names: + categories: + - gateway-api kind: TCPRoute listKind: TCPRouteList plural: tcproutes singular: tcproute scope: Namespaced versions: - - name: v1alpha1 + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 schema: openAPIV3Schema: description: TCPRoute is the Schema for the TCPRoute resource. properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object @@ -39,16 +49,24 @@ spec: properties: allow: default: SameNamespace - description: 'Allow indicates which Gateways will be allowed to use this route. Possible values are: * All: Gateways in any namespace can use this route. * FromList: Only Gateways specified in GatewayRefs may use this route. * SameNamespace: Only Gateways in the same namespace may use this route.' + description: 'Allow indicates which Gateways will be allowed to + use this route. Possible values are: * All: Gateways in any + namespace can use this route. * FromList: Only Gateways specified + in GatewayRefs may use this route. * SameNamespace: Only Gateways + in the same namespace may use this route.' enum: - All - FromList - SameNamespace type: string gatewayRefs: - description: GatewayRefs must be specified when Allow is set to "FromList". In that case, only Gateways referenced in this list will be allowed to use this route. This field is ignored for other values of "Allow". + description: GatewayRefs must be specified when Allow is set to + "FromList". In that case, only Gateways referenced in this list + will be allowed to use this route. This field is ignored for + other values of "Allow". items: - description: GatewayReference identifies a Gateway in a specified namespace. + description: GatewayReference identifies a Gateway in a specified + namespace. properties: name: description: Name is the name of the referent. @@ -72,12 +90,22 @@ spec: description: TCPRouteRule is the configuration for a given rule. properties: forwardTo: - description: ForwardTo defines the backend(s) where matching requests should be sent. + description: ForwardTo defines the backend(s) where matching + requests should be sent. items: - description: RouteForwardTo defines how a Route should forward a request. + description: RouteForwardTo defines how a Route should forward + a request. properties: backendRef: - description: "BackendRef is a reference to a backend to forward matched requests to. If both BackendRef and ServiceName are specified, ServiceName will be given precedence. \n If the referent cannot be found, the rule is not included in the route. The controller should raise the \"ResolvedRefs\" condition on the Gateway with the \"DegradedRoutes\" reason. The gateway status for this route should be updated with a condition that describes the error more specifically. \n Support: Custom" + description: "BackendRef is a reference to a backend to + forward matched requests to. If both BackendRef and + ServiceName are specified, ServiceName will be given + precedence. \n If the referent cannot be found, the + rule is not included in the route. The controller should + raise the \"ResolvedRefs\" condition on the Gateway + with the \"DegradedRoutes\" reason. The gateway status + for this route should be updated with a condition that + describes the error more specifically. \n Support: Custom" properties: group: description: Group is the group of the referent. @@ -100,18 +128,53 @@ spec: - name type: object port: - description: "Port specifies the destination port number to use for the backend referenced by the ServiceName or BackendRef field. If unspecified, the destination port in the request is used when forwarding to a backendRef or serviceName. \n Support: Core" + description: "Port specifies the destination port number + to use for the backend referenced by the ServiceName + or BackendRef field. If unspecified, the destination + port in the request is used when forwarding to a backendRef + or serviceName. \n Support: Core" format: int32 maximum: 65535 minimum: 1 type: integer serviceName: - description: "ServiceName refers to the name of the Service to forward matched requests to. When specified, this takes the place of BackendRef. If both BackendRef and ServiceName are specified, ServiceName will be given precedence. \n If the referent cannot be found, the rule is not included in the route. The controller should raise the \"ResolvedRefs\" condition on the Gateway with the \"DegradedRoutes\" reason. The gateway status for this route should be updated with a condition that describes the error more specifically. \n The protocol to use is defined using AppProtocol field (introduced in Kubernetes 1.18) in the Service resource. In the absence of the AppProtocol field a `networking.x-k8s.io/app-protocol` annotation on the BackendPolicy resource may be used to define the protocol. If the AppProtocol field is available, this annotation should not be used. The AppProtocol field, when populated, takes precedence over the annotation in the BackendPolicy resource. For custom backends, it is encouraged to add a semantically-equivalent field in the Custom Resource Definition. \n Support: Core" + description: "ServiceName refers to the name of the Service + to forward matched requests to. When specified, this + takes the place of BackendRef. If both BackendRef and + ServiceName are specified, ServiceName will be given + precedence. \n If the referent cannot be found, the + rule is not included in the route. The controller should + raise the \"ResolvedRefs\" condition on the Gateway + with the \"DegradedRoutes\" reason. The gateway status + for this route should be updated with a condition that + describes the error more specifically. \n The protocol + to use is defined using AppProtocol field (introduced + in Kubernetes 1.18) in the Service resource. In the + absence of the AppProtocol field a `networking.x-k8s.io/app-protocol` + annotation on the BackendPolicy resource may be used + to define the protocol. If the AppProtocol field is + available, this annotation should not be used. The AppProtocol + field, when populated, takes precedence over the annotation + in the BackendPolicy resource. For custom backends, + it is encouraged to add a semantically-equivalent field + in the Custom Resource Definition. \n Support: Core" maxLength: 253 type: string weight: default: 1 - description: "Weight specifies the proportion of HTTP requests forwarded to the backend referenced by the ServiceName or BackendRef field. This is computed as weight/(sum of all weights in this ForwardTo list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support: Extended" + description: "Weight specifies the proportion of HTTP + requests forwarded to the backend referenced by the + ServiceName or BackendRef field. This is computed as + weight/(sum of all weights in this ForwardTo list). + For non-zero values, there may be some epsilon from + the exact proportion defined here depending on the precision + an implementation supports. Weight is not a percentage + and the sum of weights does not need to equal 100. \n + If only one backend is specified and it has a weight + greater than 0, 100% of the traffic is forwarded to + that backend. If weight is set to 0, no traffic should + be forwarded for this entry. If unspecified, weight + defaults to 1. \n Support: Extended" format: int32 maximum: 1000000 minimum: 0 @@ -121,12 +184,42 @@ spec: minItems: 1 type: array matches: - description: Matches define conditions used for matching the rule against incoming TCP connections. Each match is independent, i.e. this rule will be matched if **any** one of the matches is satisfied. If unspecified, all requests from the associated gateway TCP listener will match. + description: "Matches define conditions used for matching the + rule against incoming TCP connections. Each match is independent, + i.e. this rule will be matched if **any** one of the matches + is satisfied. If unspecified (i.e. empty), this Rule will + match all requests for the associated Listener. \n Each client + request MUST map to a maximum of one route rule. If a request + matches multiple rules, matching precedence MUST be determined + in order of the following criteria, continuing on ties: \n + * The most specific match specified by ExtensionRef. Each + implementation that supports ExtensionRef may have different + ways of determining the specificity of the referenced extension. + \n If ties still exist across multiple Routes, matching precedence + MUST be determined in order of the following criteria, continuing + on ties: \n * The oldest Route based on creation timestamp. + For example, a Route with a creation timestamp of \"2020-09-08 + 01:02:03\" is given precedence over a Route with a creation + timestamp of \"2020-09-08 01:02:04\". * The Route appearing + first in alphabetical order by \"/\". For + example, foo/bar is given precedence over foo/baz. \n If + ties still exist within the Route that has been given precedence, + matching precedence MUST be granted to the first matching + rule meeting the above criteria." items: - description: TCPRouteMatch defines the predicate used to match connections to a given action. + description: TCPRouteMatch defines the predicate used to match + connections to a given action. properties: extensionRef: - description: "ExtensionRef is an optional, implementation-specific extension to the \"match\" behavior. For example, resource \"mytcproutematcher\" in group \"networking.acme.io\". If the referent cannot be found, the rule is not included in the route. The controller should raise the \"ResolvedRefs\" condition on the Gateway with the \"DegradedRoutes\" reason. The gateway status for this route should be updated with a condition that describes the error more specifically. \n Support: Custom" + description: "ExtensionRef is an optional, implementation-specific + extension to the \"match\" behavior. For example, resource + \"mytcproutematcher\" in group \"networking.acme.io\". + If the referent cannot be found, the rule is not included + in the route. The controller should raise the \"ResolvedRefs\" + condition on the Gateway with the \"DegradedRoutes\" + reason. The gateway status for this route should be + updated with a condition that describes the error more + specifically. \n Support: Custom" properties: group: description: Group is the group of the referent. @@ -164,43 +257,87 @@ spec: description: Status defines the current state of TCPRoute. properties: gateways: - description: "Gateways is a list of Gateways that are associated with the route, and the status of the route with respect to each Gateway. When a Gateway selects this route, the controller that manages the Gateway must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route is modified. \n A maximum of 100 Gateways will be represented in this list. If this list is full, there may be additional Gateways using this Route that are not included in the list. An empty list means the route has not been admitted by any Gateway." + description: "Gateways is a list of Gateways that are associated with + the route, and the status of the route with respect to each Gateway. + When a Gateway selects this route, the controller that manages the + Gateway must add an entry to this list when the controller first + sees the route and should update the entry as appropriate when the + route is modified. \n A maximum of 100 Gateways will be represented + in this list. If this list is full, there may be additional Gateways + using this Route that are not included in the list. An empty list + means the route has not been admitted by any Gateway." items: - description: RouteGatewayStatus describes the status of a route with respect to an associated Gateway. + description: RouteGatewayStatus describes the status of a route + with respect to an associated Gateway. properties: conditions: - description: Conditions describes the status of the route with respect to the Gateway. The "Admitted" condition must always be specified by controllers to indicate whether the route has been admitted or rejected by the Gateway, and why. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. + description: Conditions describes the status of the route with + respect to the Gateway. The "Admitted" condition must always + be specified by controllers to indicate whether the route + has been admitted or rejected by the Gateway, and why. Note + that the route's availability is also subject to the Gateway's + own status conditions and listener status. items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + description: "Condition contains details for one aspect of + the current state of this API Resource. --- This struct + is intended for direct use as an array at the field path + .status.conditions. For example, type FooStatus struct{ + \ // Represents the observations of a foo's current state. + \ // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type + \ // +patchStrategy=merge // +listType=map // + +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` + \n // other fields }" properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should + be when the underlying condition changed. If that is + not known, then using the time when the API field changed + is acceptable. format: date-time type: string message: - description: message is a human readable message indicating details about the transition. This may be an empty string. + description: message is a human readable message indicating + details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, + if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the + current state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + description: reason contains a programmatic identifier + indicating the reason for the condition's last transition. + Producers of specific condition types may define expected + values and meanings for this field, and whether the + values are considered a guaranteed API. The value should + be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: - description: status of the condition, one of True, False, Unknown. + description: status of the condition, one of True, False, + Unknown. enum: - "True" - "False" - Unknown type: string type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across + resources like Available, but because arbitrary conditions + can be useful (see .node.status.conditions), the ability + to deconflict is important. The regex it matches is + (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -217,8 +354,18 @@ spec: - type x-kubernetes-list-type: map gatewayRef: - description: GatewayRef is a reference to a Gateway object that is associated with the route. + description: GatewayRef is a reference to a Gateway object that + is associated with the route. properties: + controller: + description: "Controller is a domain/path string that indicates + the controller implementing the Gateway. This corresponds + with the controller field on GatewayClass. \n Example: + \"acme.io/gateway-controller\". \n The format of this + field is DOMAIN \"/\" PATH, where DOMAIN and PATH are + valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names)." + maxLength: 253 + type: string name: description: Name is the name of the referent. maxLength: 253 diff --git a/docs/content/reference/dynamic-configuration/networking.x-k8s.io_tlsroutes.yaml b/docs/content/reference/dynamic-configuration/networking.x-k8s.io_tlsroutes.yaml index 17cffbb80..31d10f2f6 100644 --- a/docs/content/reference/dynamic-configuration/networking.x-k8s.io_tlsroutes.yaml +++ b/docs/content/reference/dynamic-configuration/networking.x-k8s.io_tlsroutes.yaml @@ -4,28 +4,42 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.4.1 + controller-gen.kubebuilder.io/version: v0.5.0 creationTimestamp: null name: tlsroutes.networking.x-k8s.io spec: group: networking.x-k8s.io names: + categories: + - gateway-api kind: TLSRoute listKind: TLSRouteList plural: tlsroutes singular: tlsroute scope: Namespaced versions: - - name: v1alpha1 + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 schema: openAPIV3Schema: - description: "The TLSRoute resource is similar to TCPRoute, but can be configured to match against TLS-specific metadata. This allows more flexibility in matching streams for a given TLS listener. \n If you need to forward traffic to a single target for a TLS listener, you could choose to use a TCPRoute with a TLS listener." + description: "The TLSRoute resource is similar to TCPRoute, but can be configured + to match against TLS-specific metadata. This allows more flexibility in + matching streams for a given TLS listener. \n If you need to forward traffic + to a single target for a TLS listener, you could choose to use a TCPRoute + with a TLS listener." properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object @@ -39,16 +53,24 @@ spec: properties: allow: default: SameNamespace - description: 'Allow indicates which Gateways will be allowed to use this route. Possible values are: * All: Gateways in any namespace can use this route. * FromList: Only Gateways specified in GatewayRefs may use this route. * SameNamespace: Only Gateways in the same namespace may use this route.' + description: 'Allow indicates which Gateways will be allowed to + use this route. Possible values are: * All: Gateways in any + namespace can use this route. * FromList: Only Gateways specified + in GatewayRefs may use this route. * SameNamespace: Only Gateways + in the same namespace may use this route.' enum: - All - FromList - SameNamespace type: string gatewayRefs: - description: GatewayRefs must be specified when Allow is set to "FromList". In that case, only Gateways referenced in this list will be allowed to use this route. This field is ignored for other values of "Allow". + description: GatewayRefs must be specified when Allow is set to + "FromList". In that case, only Gateways referenced in this list + will be allowed to use this route. This field is ignored for + other values of "Allow". items: - description: GatewayReference identifies a Gateway in a specified namespace. + description: GatewayReference identifies a Gateway in a specified + namespace. properties: name: description: Name is the name of the referent. @@ -72,12 +94,22 @@ spec: description: TLSRouteRule is the configuration for a given rule. properties: forwardTo: - description: ForwardTo defines the backend(s) where matching requests should be sent. + description: ForwardTo defines the backend(s) where matching + requests should be sent. items: - description: RouteForwardTo defines how a Route should forward a request. + description: RouteForwardTo defines how a Route should forward + a request. properties: backendRef: - description: "BackendRef is a reference to a backend to forward matched requests to. If both BackendRef and ServiceName are specified, ServiceName will be given precedence. \n If the referent cannot be found, the rule is not included in the route. The controller should raise the \"ResolvedRefs\" condition on the Gateway with the \"DegradedRoutes\" reason. The gateway status for this route should be updated with a condition that describes the error more specifically. \n Support: Custom" + description: "BackendRef is a reference to a backend to + forward matched requests to. If both BackendRef and + ServiceName are specified, ServiceName will be given + precedence. \n If the referent cannot be found, the + rule is not included in the route. The controller should + raise the \"ResolvedRefs\" condition on the Gateway + with the \"DegradedRoutes\" reason. The gateway status + for this route should be updated with a condition that + describes the error more specifically. \n Support: Custom" properties: group: description: Group is the group of the referent. @@ -100,18 +132,53 @@ spec: - name type: object port: - description: "Port specifies the destination port number to use for the backend referenced by the ServiceName or BackendRef field. If unspecified, the destination port in the request is used when forwarding to a backendRef or serviceName. \n Support: Core" + description: "Port specifies the destination port number + to use for the backend referenced by the ServiceName + or BackendRef field. If unspecified, the destination + port in the request is used when forwarding to a backendRef + or serviceName. \n Support: Core" format: int32 maximum: 65535 minimum: 1 type: integer serviceName: - description: "ServiceName refers to the name of the Service to forward matched requests to. When specified, this takes the place of BackendRef. If both BackendRef and ServiceName are specified, ServiceName will be given precedence. \n If the referent cannot be found, the rule is not included in the route. The controller should raise the \"ResolvedRefs\" condition on the Gateway with the \"DegradedRoutes\" reason. The gateway status for this route should be updated with a condition that describes the error more specifically. \n The protocol to use is defined using AppProtocol field (introduced in Kubernetes 1.18) in the Service resource. In the absence of the AppProtocol field a `networking.x-k8s.io/app-protocol` annotation on the BackendPolicy resource may be used to define the protocol. If the AppProtocol field is available, this annotation should not be used. The AppProtocol field, when populated, takes precedence over the annotation in the BackendPolicy resource. For custom backends, it is encouraged to add a semantically-equivalent field in the Custom Resource Definition. \n Support: Core" + description: "ServiceName refers to the name of the Service + to forward matched requests to. When specified, this + takes the place of BackendRef. If both BackendRef and + ServiceName are specified, ServiceName will be given + precedence. \n If the referent cannot be found, the + rule is not included in the route. The controller should + raise the \"ResolvedRefs\" condition on the Gateway + with the \"DegradedRoutes\" reason. The gateway status + for this route should be updated with a condition that + describes the error more specifically. \n The protocol + to use is defined using AppProtocol field (introduced + in Kubernetes 1.18) in the Service resource. In the + absence of the AppProtocol field a `networking.x-k8s.io/app-protocol` + annotation on the BackendPolicy resource may be used + to define the protocol. If the AppProtocol field is + available, this annotation should not be used. The AppProtocol + field, when populated, takes precedence over the annotation + in the BackendPolicy resource. For custom backends, + it is encouraged to add a semantically-equivalent field + in the Custom Resource Definition. \n Support: Core" maxLength: 253 type: string weight: default: 1 - description: "Weight specifies the proportion of HTTP requests forwarded to the backend referenced by the ServiceName or BackendRef field. This is computed as weight/(sum of all weights in this ForwardTo list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support: Extended" + description: "Weight specifies the proportion of HTTP + requests forwarded to the backend referenced by the + ServiceName or BackendRef field. This is computed as + weight/(sum of all weights in this ForwardTo list). + For non-zero values, there may be some epsilon from + the exact proportion defined here depending on the precision + an implementation supports. Weight is not a percentage + and the sum of weights does not need to equal 100. \n + If only one backend is specified and it has a weight + greater than 0, 100% of the traffic is forwarded to + that backend. If weight is set to 0, no traffic should + be forwarded for this entry. If unspecified, weight + defaults to 1. \n Support: Extended" format: int32 maximum: 1000000 minimum: 0 @@ -121,12 +188,45 @@ spec: minItems: 1 type: array matches: - description: Matches define conditions used for matching the rule against an incoming TLS handshake. Each match is independent, i.e. this rule will be matched if **any** one of the matches is satisfied. If unspecified, all requests from the associated gateway TLS listener will match. + description: "Matches define conditions used for matching the + rule against incoming TLS connections. Each match is independent, + i.e. this rule will be matched if **any** one of the matches + is satisfied. If unspecified (i.e. empty), this Rule will + match all requests for the associated Listener. \n Each client + request MUST map to a maximum of one route rule. If a request + matches multiple rules, matching precedence MUST be determined + in order of the following criteria, continuing on ties: \n + * The longest matching SNI. * The longest matching precise + SNI (without a wildcard). This means that \"b.example.com\" + should be given precedence over \"*.example.com\". * The most + specific match specified by ExtensionRef. Each implementation + \ that supports ExtensionRef may have different ways of determining + the specificity of the referenced extension. \n If ties + still exist across multiple Routes, matching precedence MUST + be determined in order of the following criteria, continuing + on ties: \n * The oldest Route based on creation timestamp. + For example, a Route with a creation timestamp of \"2020-09-08 + 01:02:03\" is given precedence over a Route with a creation + timestamp of \"2020-09-08 01:02:04\". * The Route appearing + first in alphabetical order by \"/\". For + example, foo/bar is given precedence over foo/baz. \n If + ties still exist within the Route that has been given precedence, + matching precedence MUST be granted to the first matching + rule meeting the above criteria." items: - description: TLSRouteMatch defines the predicate used to match connections to a given action. + description: TLSRouteMatch defines the predicate used to match + connections to a given action. properties: extensionRef: - description: "ExtensionRef is an optional, implementation-specific extension to the \"match\" behavior. For example, resource \"mytlsroutematcher\" in group \"networking.acme.io\". If the referent cannot be found, the rule is not included in the route. The controller should raise the \"ResolvedRefs\" condition on the Gateway with the \"DegradedRoutes\" reason. The gateway status for this route should be updated with a condition that describes the error more specifically. \n Support: Custom" + description: "ExtensionRef is an optional, implementation-specific + extension to the \"match\" behavior. For example, resource + \"mytlsroutematcher\" in group \"networking.acme.io\". + If the referent cannot be found, the rule is not included + in the route. The controller should raise the \"ResolvedRefs\" + condition on the Gateway with the \"DegradedRoutes\" + reason. The gateway status for this route should be + updated with a condition that describes the error more + specifically. \n Support: Custom" properties: group: description: Group is the group of the referent. @@ -149,9 +249,29 @@ spec: - name type: object snis: - description: "SNIs defines a set of SNI names that should match against the SNI attribute of TLS ClientHello message in TLS handshake. \n SNI can be \"precise\" which is a domain name without the terminating dot of a network host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). The wildcard character `*` must appear by itself as the first DNS label and matches only a single label. You cannot have a wildcard label by itself (e.g. Host == `*`). \n Requests will be matched against the Host field in the following order: \n 1. If SNI is precise, the request matches this rule if the SNI in ClientHello is equal to one of the defined SNIs. 2. If SNI is a wildcard, then the request matches this rule if the SNI is to equal to the suffix (removing the first label) of the wildcard rule. 3. If SNIs is unspecified, all requests associated with the gateway TLS listener will match. This can be used to define a default backend for a TLS listener. \n Support: Core" + description: "SNIs defines a set of SNI names that should + match against the SNI attribute of TLS ClientHello message + in TLS handshake. \n SNI can be \"precise\" which is + a domain name without the terminating dot of a network + host (e.g. \"foo.example.com\") or \"wildcard\", which + is a domain name prefixed with a single wildcard label + (e.g. `*.example.com`). The wildcard character `*` must + appear by itself as the first DNS label and matches + only a single label. You cannot have a wildcard label + by itself (e.g. Host == `*`). \n Requests will be matched + against the Host field in the following order: \n 1. + If SNI is precise, the request matches this rule if + the SNI in ClientHello is equal to one of the defined + SNIs. 2. If SNI is a wildcard, then the request matches + this rule if the SNI is to equal to the suffix (removing + the first label) of the wildcard rule. 3. If SNIs + is unspecified, all requests associated with the gateway + TLS listener will match. This can be used to define + a default backend for a TLS listener. \n Support: + Core" items: - description: Hostname is used to specify a hostname that should be matched. + description: Hostname is used to specify a hostname + that should be matched. maxLength: 253 minLength: 1 type: string @@ -173,43 +293,87 @@ spec: description: Status defines the current state of TLSRoute. properties: gateways: - description: "Gateways is a list of Gateways that are associated with the route, and the status of the route with respect to each Gateway. When a Gateway selects this route, the controller that manages the Gateway must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route is modified. \n A maximum of 100 Gateways will be represented in this list. If this list is full, there may be additional Gateways using this Route that are not included in the list. An empty list means the route has not been admitted by any Gateway." + description: "Gateways is a list of Gateways that are associated with + the route, and the status of the route with respect to each Gateway. + When a Gateway selects this route, the controller that manages the + Gateway must add an entry to this list when the controller first + sees the route and should update the entry as appropriate when the + route is modified. \n A maximum of 100 Gateways will be represented + in this list. If this list is full, there may be additional Gateways + using this Route that are not included in the list. An empty list + means the route has not been admitted by any Gateway." items: - description: RouteGatewayStatus describes the status of a route with respect to an associated Gateway. + description: RouteGatewayStatus describes the status of a route + with respect to an associated Gateway. properties: conditions: - description: Conditions describes the status of the route with respect to the Gateway. The "Admitted" condition must always be specified by controllers to indicate whether the route has been admitted or rejected by the Gateway, and why. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. + description: Conditions describes the status of the route with + respect to the Gateway. The "Admitted" condition must always + be specified by controllers to indicate whether the route + has been admitted or rejected by the Gateway, and why. Note + that the route's availability is also subject to the Gateway's + own status conditions and listener status. items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + description: "Condition contains details for one aspect of + the current state of this API Resource. --- This struct + is intended for direct use as an array at the field path + .status.conditions. For example, type FooStatus struct{ + \ // Represents the observations of a foo's current state. + \ // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type + \ // +patchStrategy=merge // +listType=map // + +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` + \n // other fields }" properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should + be when the underlying condition changed. If that is + not known, then using the time when the API field changed + is acceptable. format: date-time type: string message: - description: message is a human readable message indicating details about the transition. This may be an empty string. + description: message is a human readable message indicating + details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, + if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the + current state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + description: reason contains a programmatic identifier + indicating the reason for the condition's last transition. + Producers of specific condition types may define expected + values and meanings for this field, and whether the + values are considered a guaranteed API. The value should + be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: - description: status of the condition, one of True, False, Unknown. + description: status of the condition, one of True, False, + Unknown. enum: - "True" - "False" - Unknown type: string type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across + resources like Available, but because arbitrary conditions + can be useful (see .node.status.conditions), the ability + to deconflict is important. The regex it matches is + (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -226,8 +390,18 @@ spec: - type x-kubernetes-list-type: map gatewayRef: - description: GatewayRef is a reference to a Gateway object that is associated with the route. + description: GatewayRef is a reference to a Gateway object that + is associated with the route. properties: + controller: + description: "Controller is a domain/path string that indicates + the controller implementing the Gateway. This corresponds + with the controller field on GatewayClass. \n Example: + \"acme.io/gateway-controller\". \n The format of this + field is DOMAIN \"/\" PATH, where DOMAIN and PATH are + valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names)." + maxLength: 253 + type: string name: description: Name is the name of the referent. maxLength: 253 diff --git a/docs/content/reference/dynamic-configuration/traefik.containo.us_middlewares.yaml b/docs/content/reference/dynamic-configuration/traefik.containo.us_middlewares.yaml index bb2e737fe..4ce827116 100644 --- a/docs/content/reference/dynamic-configuration/traefik.containo.us_middlewares.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.containo.us_middlewares.yaml @@ -299,6 +299,7 @@ spec: type: string type: object featurePolicy: + description: 'Deprecated: use PermissionsPolicy instead.' type: string forceSTSHeader: type: boolean @@ -310,6 +311,8 @@ spec: type: array isDevelopment: type: boolean + permissionsPolicy: + type: string publicKey: type: string referrerPolicy: diff --git a/docs/content/reference/dynamic-configuration/traefik.containo.us_serverstransports.yaml b/docs/content/reference/dynamic-configuration/traefik.containo.us_serverstransports.yaml index 7bae05b2a..b6da0997b 100644 --- a/docs/content/reference/dynamic-configuration/traefik.containo.us_serverstransports.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.containo.us_serverstransports.yaml @@ -78,6 +78,10 @@ spec: description: If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used. type: integer + peerCertURI: + description: URI used to match against SAN URI during the peer certificate + verification. + type: string rootCAsSecrets: description: Add cert file for self-signed certificate. items: diff --git a/docs/content/reference/static-configuration/cli-ref.md b/docs/content/reference/static-configuration/cli-ref.md index f8a2aa8d3..e5068eb57 100644 --- a/docs/content/reference/static-configuration/cli-ref.md +++ b/docs/content/reference/static-configuration/cli-ref.md @@ -369,6 +369,12 @@ Enable ConsulCatalog backend with default settings. (Default: ```false```) `--providers.consulcatalog.cache`: Use local agent caching for catalog reads. (Default: ```false```) +`--providers.consulcatalog.connectaware`: +Enable Consul Connect support. (Default: ```false```) + +`--providers.consulcatalog.connectbydefault`: +Consider every service as Connect capable by default. (Default: ```false```) + `--providers.consulcatalog.constraints`: Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container. @@ -376,7 +382,7 @@ Constraints is an expression that Traefik matches against the container's labels Default rule. (Default: ```Host(`{{ normalize .Name }}`)```) `--providers.consulcatalog.endpoint.address`: -The address of the Consul server (Default: ```127.0.0.1:8500```) +The address of the Consul server `--providers.consulcatalog.endpoint.datacenter`: Data center to use. If not provided, the default agent data center is used @@ -423,6 +429,9 @@ Interval for check Consul API. Default 15s (Default: ```15```) `--providers.consulcatalog.requireconsistent`: Forces the read to be fully consistent. (Default: ```false```) +`--providers.consulcatalog.servicename`: +Name of the Traefik service in Consul Catalog (needs to be registered via the orchestrator or manually). (Default: ```traefik```) + `--providers.consulcatalog.stale`: Use stale consistency for catalog reads. (Default: ```false```) @@ -577,7 +586,10 @@ TLS key Enable Kubernetes backend with default settings. (Default: ```false```) `--providers.kubernetescrd.allowcrossnamespace`: -Allow cross namespace resource reference. (Default: ```true```) +Allow cross namespace resource reference. (Default: ```false```) + +`--providers.kubernetescrd.allowexternalnameservices`: +Allow ExternalName services. (Default: ```false```) `--providers.kubernetescrd.certauthfilepath`: Kubernetes certificate authority file path (not needed for in-cluster client). @@ -627,6 +639,9 @@ Enable Kubernetes backend with default settings. (Default: ```false```) `--providers.kubernetesingress.allowemptyservices`: Allow creation of services without endpoints. (Default: ```false```) +`--providers.kubernetesingress.allowexternalnameservices`: +Allow ExternalName services. (Default: ```false```) + `--providers.kubernetesingress.certauthfilepath`: Kubernetes certificate authority file path (not needed for in-cluster client). diff --git a/docs/content/reference/static-configuration/env-ref.md b/docs/content/reference/static-configuration/env-ref.md index a1f82ca13..525f65f66 100644 --- a/docs/content/reference/static-configuration/env-ref.md +++ b/docs/content/reference/static-configuration/env-ref.md @@ -342,6 +342,12 @@ Enable ConsulCatalog backend with default settings. (Default: ```false```) `TRAEFIK_PROVIDERS_CONSULCATALOG_CACHE`: Use local agent caching for catalog reads. (Default: ```false```) +`TRAEFIK_PROVIDERS_CONSULCATALOG_CONNECTAWARE`: +Enable Consul Connect support. (Default: ```false```) + +`TRAEFIK_PROVIDERS_CONSULCATALOG_CONNECTBYDEFAULT`: +Consider every service as Connect capable by default. (Default: ```false```) + `TRAEFIK_PROVIDERS_CONSULCATALOG_CONSTRAINTS`: Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container. @@ -349,7 +355,7 @@ Constraints is an expression that Traefik matches against the container's labels Default rule. (Default: ```Host(`{{ normalize .Name }}`)```) `TRAEFIK_PROVIDERS_CONSULCATALOG_ENDPOINT_ADDRESS`: -The address of the Consul server (Default: ```127.0.0.1:8500```) +The address of the Consul server `TRAEFIK_PROVIDERS_CONSULCATALOG_ENDPOINT_DATACENTER`: Data center to use. If not provided, the default agent data center is used @@ -396,6 +402,9 @@ Interval for check Consul API. Default 15s (Default: ```15```) `TRAEFIK_PROVIDERS_CONSULCATALOG_REQUIRECONSISTENT`: Forces the read to be fully consistent. (Default: ```false```) +`TRAEFIK_PROVIDERS_CONSULCATALOG_SERVICENAME`: +Name of the Traefik service in Consul Catalog (needs to be registered via the orchestrator or manually). (Default: ```traefik```) + `TRAEFIK_PROVIDERS_CONSULCATALOG_STALE`: Use stale consistency for catalog reads. (Default: ```false```) @@ -577,7 +586,10 @@ TLS key Enable Kubernetes backend with default settings. (Default: ```false```) `TRAEFIK_PROVIDERS_KUBERNETESCRD_ALLOWCROSSNAMESPACE`: -Allow cross namespace resource reference. (Default: ```true```) +Allow cross namespace resource reference. (Default: ```false```) + +`TRAEFIK_PROVIDERS_KUBERNETESCRD_ALLOWEXTERNALNAMESERVICES`: +Allow ExternalName services. (Default: ```false```) `TRAEFIK_PROVIDERS_KUBERNETESCRD_CERTAUTHFILEPATH`: Kubernetes certificate authority file path (not needed for in-cluster client). @@ -627,6 +639,9 @@ Enable Kubernetes backend with default settings. (Default: ```false```) `TRAEFIK_PROVIDERS_KUBERNETESINGRESS_ALLOWEMPTYSERVICES`: Allow creation of services without endpoints. (Default: ```false```) +`TRAEFIK_PROVIDERS_KUBERNETESINGRESS_ALLOWEXTERNALNAMESERVICES`: +Allow ExternalName services. (Default: ```false```) + `TRAEFIK_PROVIDERS_KUBERNETESINGRESS_CERTAUTHFILEPATH`: Kubernetes certificate authority file path (not needed for in-cluster client). diff --git a/docs/content/routing/providers/consul-catalog.md b/docs/content/routing/providers/consul-catalog.md index 4a577133e..3bd44e04e 100644 --- a/docs/content/routing/providers/consul-catalog.md +++ b/docs/content/routing/providers/consul-catalog.md @@ -454,6 +454,16 @@ You can tell Traefik to consider (or not) the service by setting `traefik.enable This option overrides the value of `exposedByDefault`. +#### `traefik.consulcatalog.connect` + +```yaml +traefik.consulcatalog.connect=true +``` + +You can tell Traefik to consider (or not) the service as a Connect capable one by setting `traefik.consulcatalog.connect` to true or false. + +This option overrides the value of `connectByDefault`. + #### Port Lookup Traefik is capable of detecting the port to use, by following the default consul Catalog flow. diff --git a/docs/content/routing/providers/kubernetes-crd.md b/docs/content/routing/providers/kubernetes-crd.md index f5bf0174b..965f9db0f 100644 --- a/docs/content/routing/providers/kubernetes-crd.md +++ b/docs/content/routing/providers/kubernetes-crd.md @@ -1648,19 +1648,21 @@ or referencing TLS stores in the [`IngressRoute`](#kind-ingressroute) / [`Ingres dialTimeout: 42s # [7] responseHeaderTimeout: 42s # [8] idleConnTimeout: 42s # [9] + peerCertURI: foobar # [10] ``` -| Ref | Attribute | Purpose | -|-----|-------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------| -| [1] | `serverName` | ServerName used to contact the server. | -| [2] | `insecureSkipVerify` | Disable SSL certificate verification. | -| [3] | `rootCAsSecrets` | Add cert file for self-signed certificate. The secret must contain a certificate under either a tls.ca or a ca.crt key. | -| [4] | `certificatesSecrets` | Certificates for mTLS. | -| [5] | `maxIdleConnsPerHost` | If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, `defaultMaxIdleConnsPerHost` is used. | -| [6] | `forwardingTimeouts` | Timeouts for requests forwarded to the backend servers. | -| [7] | `dialTimeout` | The amount of time to wait until a connection to a backend server can be established. If zero, no timeout exists. | -| [8] | `responseHeaderTimeout` | The amount of time to wait for a server's response headers after fully writing the request (including its body, if any). If zero, no timeout exists. | -| [9] | `idleConnTimeout` | The maximum period for which an idle HTTP keep-alive connection will remain open before closing itself. | +| Ref | Attribute | Purpose | +|------|-------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------| +| [1] | `serverName` | ServerName used to contact the server. | +| [2] | `insecureSkipVerify` | Disable SSL certificate verification. | +| [3] | `rootCAsSecrets` | Add cert file for self-signed certificate. The secret must contain a certificate under either a tls.ca or a ca.crt key. | +| [4] | `certificatesSecrets` | Certificates for mTLS. | +| [5] | `maxIdleConnsPerHost` | If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, `defaultMaxIdleConnsPerHost` is used. | +| [6] | `forwardingTimeouts` | Timeouts for requests forwarded to the backend servers. | +| [7] | `dialTimeout` | The amount of time to wait until a connection to a backend server can be established. If zero, no timeout exists. | +| [8] | `responseHeaderTimeout` | The amount of time to wait for a server's response headers after fully writing the request (including its body, if any). If zero, no timeout exists. | +| [9] | `idleConnTimeout` | The maximum period for which an idle HTTP keep-alive connection will remain open before closing itself. | +| [10] | `peerCertURI` | URI used to match with service certificate. | !!! info "CA Secret" diff --git a/docs/content/routing/services/index.md b/docs/content/routing/services/index.md index c11e8cdd3..ef3b0978b 100644 --- a/docs/content/routing/services/index.md +++ b/docs/content/routing/services/index.md @@ -738,6 +738,37 @@ spec: disableHTTP2: true ``` +#### `peerCertURI` + +_Optional, Default=false_ + +`peerCertURI` defines the URI used to match against SAN URI during the peer certificate verification. + +```toml tab="File (TOML)" +## Dynamic configuration +[http.serversTransports.mytransport] + peerCertURI = "foobar" +``` + +```yaml tab="File (YAML)" +## Dynamic configuration +http: + serversTransports: + mytransport: + peerCertURI: foobar +``` + +```yaml tab="Kubernetes" +apiVersion: traefik.containo.us/v1alpha1 +kind: ServersTransport +metadata: + name: mytransport + namespace: default + +spec: + peerCertURI: foobar +``` + #### `forwardingTimeouts` `forwardingTimeouts` is about a number of timeouts relevant to when forwarding requests to the backend servers. diff --git a/docs/scripts/verify.sh b/docs/scripts/verify.sh index 8dd7c3cfb..03ddaefea 100755 --- a/docs/scripts/verify.sh +++ b/docs/scripts/verify.sh @@ -22,7 +22,7 @@ find "${PATH_TO_SITE}" -type f -not -path "/app/site/theme/*" \ --alt_ignore="/traefikproxy-vertical-logo-color.svg/" \ --http_status_ignore="0,500,501,503" \ --file_ignore="/404.html/" \ - --url_ignore="/https://groups.google.com/a/traefik.io/forum/#!forum/security/,/localhost:/,/127.0.0.1:/,/fonts.gstatic.com/,/.minikube/,/github.com\/traefik\/traefik\/*edit*/,/github.com\/traefik\/traefik/,/doc.traefik.io/,/github\.com\/golang\/oauth2\/blob\/36a7019397c4c86cf59eeab3bc0d188bac444277\/.+/,/www.akamai.com/,/pilot.traefik.io\/profile/,/traefik.io/,/doc.traefik.io\/traefik-mesh/,/www.mkdocs.org/,/squidfunk.github.io/,/ietf.org/,/www.namesilo.com/,/www.youtube.com/,/www.linode.com/" \ + --url_ignore="/https://groups.google.com/a/traefik.io/forum/#!forum/security/,/localhost:/,/127.0.0.1:/,/fonts.gstatic.com/,/.minikube/,/github.com\/traefik\/traefik\/*edit*/,/github.com\/traefik\/traefik/,/doc.traefik.io/,/github\.com\/golang\/oauth2\/blob\/36a7019397c4c86cf59eeab3bc0d188bac444277\/.+/,/www.akamai.com/,/pilot.traefik.io\/profile/,/traefik.io/,/doc.traefik.io\/traefik-mesh/,/www.mkdocs.org/,/squidfunk.github.io/,/ietf.org/,/www.namesilo.com/,/www.youtube.com/,/www.linode.com/,/www.alibabacloud.com/" \ '{}' 1>/dev/null ## HTML-proofer options at https://github.com/gjtorikian/html-proofer#configuration diff --git a/go.mod b/go.mod index 2b8c1619b..44c2a25a6 100644 --- a/go.mod +++ b/go.mod @@ -37,20 +37,23 @@ require ( github.com/google/go-github/v28 v28.1.1 github.com/gorilla/mux v1.7.3 github.com/gorilla/websocket v1.4.2 - github.com/hashicorp/consul/api v1.3.0 - github.com/hashicorp/go-multierror v1.0.0 - github.com/hashicorp/go-version v1.2.0 + github.com/hashicorp/consul v1.10.0 + github.com/hashicorp/consul/api v1.9.1 + github.com/hashicorp/go-hclog v0.16.1 + github.com/hashicorp/go-multierror v1.1.1 + github.com/hashicorp/go-version v1.2.1 github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d github.com/instana/go-sensor v1.5.1 + github.com/klauspost/compress v1.13.0 github.com/libkermit/compose v0.0.0-20171122111507-c04e39c026ad github.com/libkermit/docker v0.0.0-20171122101128-e6674d32b807 github.com/libkermit/docker-check v0.0.0-20171122104347-1113af38e591 github.com/lucas-clemente/quic-go v0.20.1 github.com/mailgun/ttlmap v0.0.0-20170619185759-c1c17f74874f - github.com/miekg/dns v1.1.40 + github.com/miekg/dns v1.1.43 github.com/mitchellh/copystructure v1.0.0 github.com/mitchellh/hashstructure v1.0.0 - github.com/mitchellh/mapstructure v1.3.3 + github.com/mitchellh/mapstructure v1.4.1 github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect github.com/opencontainers/go-digest v1.0.0-rc1 // indirect github.com/opencontainers/image-spec v1.0.1 // indirect @@ -69,9 +72,8 @@ require ( github.com/stretchr/testify v1.7.0 github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154 github.com/tinylib/msgp v1.0.2 // indirect - github.com/traefik/gziphandler v1.1.2-0.20210212101304-175e0fad6888 github.com/traefik/paerser v0.1.4 - github.com/traefik/yaegi v0.9.19 + github.com/traefik/yaegi v0.9.21 github.com/uber/jaeger-client-go v2.29.1+incompatible github.com/uber/jaeger-lib v2.2.0+incompatible github.com/unrolled/render v1.0.2 @@ -82,21 +84,22 @@ require ( go.elastic.co/apm v1.11.0 go.elastic.co/apm/module/apmot v1.11.0 golang.org/x/mod v0.4.2 - golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d + golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1 golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba - golang.org/x/tools v0.0.0-20200904185747-39188db58858 + golang.org/x/tools v0.1.0 google.golang.org/grpc v1.27.1 gopkg.in/DataDog/dd-trace-go.v1 v1.19.0 gopkg.in/fsnotify.v1 v1.4.7 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b - k8s.io/api v0.20.2 - k8s.io/apiextensions-apiserver v0.20.1 - k8s.io/apimachinery v0.20.2 - k8s.io/client-go v0.20.2 - k8s.io/code-generator v0.20.2 + k8s.io/api v0.21.0 + k8s.io/apiextensions-apiserver v0.20.2 + k8s.io/apimachinery v0.21.0 + k8s.io/client-go v0.21.0 + k8s.io/code-generator v0.21.0 + k8s.io/utils v0.0.0-20210709001253-0e1f9d693477 mvdan.cc/xurls/v2 v2.1.0 - sigs.k8s.io/controller-tools v0.4.1 - sigs.k8s.io/gateway-api v0.2.0 + sigs.k8s.io/controller-tools v0.5.0 + sigs.k8s.io/gateway-api v0.3.0 ) // Containous forks diff --git a/go.sum b/go.sum index 9d2caf0a0..806eb5978 100644 --- a/go.sum +++ b/go.sum @@ -31,37 +31,50 @@ dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBr dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= -github.com/Azure/azure-sdk-for-go v32.4.0+incompatible h1:1JP8SKfroEakYiQU2ZyPDosh8w2Tg9UopKt88VyQPt4= github.com/Azure/azure-sdk-for-go v32.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v40.3.0+incompatible h1:NthZg3psrLxvQLN6rVm07pZ9mv2wvGNaBNGQ3fnPvLE= +github.com/Azure/azure-sdk-for-go v40.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.1.0/go.mod h1:AKyIcETwSUFxIcs/Wnq/C+kwCtlEYGUVd7FPNb2slmg= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= -github.com/Azure/go-autorest/autorest v0.11.1 h1:eVvIXUKiTgv++6YnWb42DUA1YL7qDugnKP0HljexdnQ= +github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0= +github.com/Azure/go-autorest/autorest v0.10.0/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= +github.com/Azure/go-autorest/autorest v0.11.12 h1:gI8ytXbxMfI+IVbI9mP2JGCTXIuhHLgRlvQ9X4PsnHE= +github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= github.com/Azure/go-autorest/autorest/adal v0.1.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E= github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= +github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= +github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= github.com/Azure/go-autorest/autorest/adal v0.9.5 h1:Y3bBUV4rTuxenJJs41HU3qmqsb+auo+a3Lz+PlJPpL0= github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= -github.com/Azure/go-autorest/autorest/azure/auth v0.1.0 h1:YgO/vSnJEc76NLw2ecIXvXa8bDWiqf1pOJzARAoZsYU= github.com/Azure/go-autorest/autorest/azure/auth v0.1.0/go.mod h1:Gf7/i2FUpyb/sGBLIFxTBzrNzBo7aPXXE3ZVeDRwdpM= -github.com/Azure/go-autorest/autorest/azure/cli v0.1.0 h1:YTtBrcb6mhA+PoSW8WxFDoIIyjp13XqJeX80ssQtri4= +github.com/Azure/go-autorest/autorest/azure/auth v0.4.2 h1:iM6UAvjR97ZIeR93qTcwpKNMpV+/FTWjwEbuPD495Tk= +github.com/Azure/go-autorest/autorest/azure/auth v0.4.2/go.mod h1:90gmfKdlmKgfjUpnCEpOJzsUEjrWDSLwHIG73tSXddM= github.com/Azure/go-autorest/autorest/azure/cli v0.1.0/go.mod h1:Dk8CUAt/b/PzkfeRsWzVG9Yj3ps8mS8ECztu43rdU8U= +github.com/Azure/go-autorest/autorest/azure/cli v0.3.1 h1:LXl088ZQlP0SBppGFsRZonW6hSvwgL5gRByMbvUbx8U= +github.com/Azure/go-autorest/autorest/azure/cli v0.3.1/go.mod h1:ZG5p860J94/0kI9mNJVoIoLgXcirM2gF5i2kWloofxw= github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/to v0.2.0 h1:nQOZzFCudTh+TvquAtCRjM01VEYx85e9qbwt5ncW4L8= github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc= -github.com/Azure/go-autorest/autorest/validation v0.1.0 h1:ISSNzGUh+ZSzizJWOWzs8bwpXIePbGLW4z/AmUFGH5A= +github.com/Azure/go-autorest/autorest/to v0.3.0 h1:zebkZaadz7+wIQYgC7GXaz3Wb28yKYfVkkBKwc38VF8= +github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA= github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8= +github.com/Azure/go-autorest/autorest/validation v0.2.0 h1:15vMO4y76dehZSq7pAaOLQxC6dZYsSrj2GQpflyM/L4= +github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI= github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= github.com/Azure/go-autorest/logger v0.2.0 h1:e4RVHVZKC5p6UANLJHkM4OfR1UKZPj8Wt8Pcx+3oqrE= github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= @@ -72,8 +85,9 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/DataDog/datadog-go v2.2.0+incompatible h1:V5BKkxACZLjzHjSgBbr2gvLA2Ae49yhc6CSY7MLy5k4= github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/DataDog/datadog-go v3.2.0+incompatible h1:qSG2N4FghB1He/r2mFrWKCaL7dXCilEuNEeAn20fdD4= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.3.6-0.20190409195224-796139022798 h1:2T/jmrHeTezcCM58lvEQXs0UpQJCo5SoGAcg+mbSTIg= github.com/DataDog/zstd v1.3.6-0.20190409195224-796139022798/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/ExpediaDotCom/haystack-client-go v0.0.0-20190315171017-e7edbdf53a61 h1:1NIUJ+MAMpqDr4LWIfNsoJR+G7zg/8GZVwuRkmJxtTc= @@ -85,16 +99,17 @@ github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030I github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= +github.com/Microsoft/go-winio v0.4.3/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 h1:ygIc8M6trr62pF5DucadTWGdEB4mEyvzi0e2nbcmcyA= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/hcsshim v0.8.7 h1:ptnOoufxGSzauVTsdE+wMYnCWA301PdoN4xg5oRdZpg= github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/NYTimes/gziphandler v1.0.1/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 h1:xPMsUicZ3iosVPSIP7bW5EcGUzjiiMl1OYTe14y/R24= github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= @@ -104,12 +119,13 @@ github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWX github.com/Shopify/sarama v1.23.1 h1:XxJBCZEoWJtoWjf/xRbmGUpAmTZGnuuF0ON0EvxxBrs= github.com/Shopify/sarama v1.23.1/go.mod h1:XLH1GYJnLVE0XCr6KdJGVJRTwY30moWNJ4sERjXX6fs= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw= github.com/abronan/valkeyrie v0.0.0-20200127174252-ef4277a138cd h1:UlQRt3CZdeD+WfDamDtdDDOu84CYbGIh9/B28TgzCZk= github.com/abronan/valkeyrie v0.0.0-20200127174252-ef4277a138cd/go.mod h1:2RUNONRAQ8bS1QcVJF3dYO/faiEro6NAAIQ6CqBkpD0= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= -github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/ahmetb/gen-crd-api-reference-docs v0.2.1-0.20201224172655-df869c1245d4/go.mod h1:TdjdkYhlOifCQWPs1UdTma97kQQMozf5h26hTuG70u8= github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.0 h1:NNsy0ugDFZbu6S11NlsR4Kmko9hJ00gv9U2JHbwpf3g= github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.0/go.mod h1:kX6YddBkXqqywAe8c9LyvgTCyFuZCTMF4cRPQhc3Fy8= @@ -119,23 +135,25 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/aliyun/alibaba-cloud-sdk-go v1.61.976 h1:I9fs4eZbZqimF3TstEqEwK66R2b7QKd6D6OCxibSD60= github.com/aliyun/alibaba-cloud-sdk-go v1.61.976/go.mod h1:pUKYbK5JQ+1Dfxk80P0qxGqe5dkxDoabbZS7zOcouyA= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 h1:EFSB7Zo9Eg91v7MJPVsifUysc/wPdN+NOnVe6bWbdBM= github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg= +github.com/armon/go-metrics v0.3.0/go.mod h1:zXjbSimjXTd7vOpY8B0/2LpvNvDoXBuplAD+gJD3GYs= +github.com/armon/go-metrics v0.3.8 h1:oOxq3KPj0WhCuy50EhzwiyMyG2ovRQZpZLXQuOh2a/M= +github.com/armon/go-metrics v0.3.8/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= -github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.16.23/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.25.41/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.37.27 h1:zh+56ktUEUjA5lMsolBnjVDaYOWadwCwx1DMF7tFM8Y= github.com/aws/aws-sdk-go v1.37.27/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= @@ -147,8 +165,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= @@ -170,12 +188,16 @@ github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wX github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible h1:C29Ae4G5GtYyYMm1aztcyj/J5ckgJm2zwdDajFbx1NY= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3 h1:TJH+oke8D16535+jHExHj4nQvzlZrj7ug5D7I/orNUA= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/cloudflare-go v0.14.0 h1:gFqGlGl/5f9UGXAaKapCGUfaTCgRKKnzu2VvzMZlOFA= github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304= +github.com/cncf/udpa/go v0.0.0-20200313221541-5f7e5dd04533/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= @@ -202,6 +224,7 @@ github.com/containous/multibuf v0.0.0-20190809014333-8b6c9a7e6bba h1:PhR03pep+5e github.com/containous/multibuf v0.0.0-20190809014333-8b6c9a7e6bba/go.mod h1:zkWcASFUJEst6QwCrxLdkuw1gvaKqmflEipm+iecV5M= github.com/containous/mux v0.0.0-20181024131434-c33f32e26898 h1:1srn9voikJGofblBhWy3WuZWqo14Ou7NaswNG/I2yWc= github.com/containous/mux v0.0.0-20181024131434-c33f32e26898/go.mod h1:z8WW7n06n8/1xF9Jl9WmuDeZuHAhfL+bwarNjsciwwg= +github.com/coredns/coredns v1.1.2/go.mod h1:zASH/MVDgR6XZTbxvOnsZfffS+31vg6Ackf/wo1+AM0= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY= github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -220,7 +243,6 @@ github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7 github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU= github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpu/goacmedns v0.1.1 h1:DM3H2NiN2oam7QljgGY5ygy4yDXhK5Z4JUnqaugs2C4= @@ -229,17 +251,22 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cucumber/godog v0.8.1 h1:lVb+X41I4YDreE+ibZ50bdXmySxgRviYFgKY6Aw4XE8= github.com/cucumber/godog v0.8.1/go.mod h1:vSh3r/lM+psC1BPXvdkSEuNjmXfpVqrMGYAElF6hxnA= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= +github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/deepmap/oapi-codegen v1.3.11 h1:Nd3tDQfqgquLmCzyRONHzs5SJEwPPoQcFZxT8MKt1Hs= github.com/deepmap/oapi-codegen v1.3.11/go.mod h1:suMvK7+rKlx3+tpa8ByptmvoXbAV70wERKTOGH3hLp0= +github.com/denverdino/aliyungo v0.0.0-20170926055100-d3308649c661/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/digitalocean/godo v1.1.1/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= +github.com/digitalocean/godo v1.10.0/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= @@ -249,16 +276,15 @@ github.com/docker/cli v0.0.0-20200221155518-740919cc7fc0 h1:hlGHcYGaaHs/yffSubcU github.com/docker/cli v0.0.0-20200221155518-740919cc7fc0/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v17.12.0-ce-rc1.0.20200204220554-5f6d6f3f2203+incompatible h1:VrzTIgiSCSkQxvYPqS92HHDuSrSeniab44r/BruI7qM= github.com/docker/docker v17.12.0-ce-rc1.0.20200204220554-5f6d6f3f2203+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ= github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= +github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82 h1:X0fj836zx99zFu83v/M79DuBn84IL/Syx1SY6Y5ZEMA= github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= -github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libcompose v0.0.0-20190805081528-eac9fe1b8b03 h1:kodxuqd2aTzaoldemGNQLrnYGpYLvm1kRdzngQa6hPc= @@ -286,14 +312,18 @@ github.com/elastic/go-sysinfo v1.1.1 h1:ZVlaLDyhVkDfjwPGU55CQRCRolNpc7P0BbyhhQZQ github.com/elastic/go-sysinfo v1.1.1/go.mod h1:i1ZYdU10oLNfRzq4vq62BEwD2fH8KaWh6eh0ikPT9F0= github.com/elastic/go-windows v1.0.0 h1:qLURgZFkkrYyTTkvYpsZIgf83AUsdIHfvlJaqaZ7aSY= github.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6CcloAxnPgU= +github.com/elazarl/go-bindata-assetfs v0.0.0-20160803192304-e1a2a7ec64b0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk= github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.5/go.mod h1:OXl5to++W0ctG+EHWTFUjiypVxC/Y4VLc/KFU+al13s= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -301,8 +331,9 @@ github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQo github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/exoscale/egoscale v0.46.0 h1:i1Ut7oqFaOV8opNP9CHcwTnHryIAcTvL3pwZusQEGrA= github.com/exoscale/egoscale v0.46.0/go.mod h1:mpEXBpROAa/2i5GC0r33rfxG+TxSEka11g1PIXt9+zc= -github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/httpsnoop v1.0.0 h1:gh8fMGz0rlOv/1WmRZm7OgncIOTsAj21iNJot48omJQ= @@ -314,6 +345,7 @@ github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoD github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/frankban/quicktest v1.11.0/go.mod h1:K+q6oSqb0W0Ininfk863uOk1lMy69l/P6txr3mVT54s= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= @@ -324,10 +356,9 @@ github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2H github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-acme/lego/v4 v4.4.0 h1:uHhU5LpOYQOdp3aDU+XY2bajseu8fuExphTL1Ss6/Fc= github.com/go-acme/lego/v4 v4.4.0/go.mod h1:l3+tFUFZb590dWcqhWZegynUthtaHJbG2fevUpoOOE0= +github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= @@ -339,71 +370,45 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.1-0.20200915143503-439c4d2ed3ea h1:CnEQOUv4ilElSwFB9g/lVmz206oLE4aNZDYngIY1Gvg= github.com/go-kit/kit v0.10.1-0.20200915143503-439c4d2ed3ea/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-ldap/ldap/v3 v3.1.3/go.mod h1:3rbOH3jRS2u6jg2rJnKAMLE/xQyCKIveG2Sa/Cohzb8= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v0.3.0 h1:q4c+kbcR0d5rSurhBR8dIgieOaYpXtsdTYfx22Cu6rs= github.com/go-logr/logr v0.3.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc= +github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/zapr v0.2.0/go.mod h1:qhKdvif7YF5GI9NWEpyxTSSBdGmzkNguibrdCNVPunU= -github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= -github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= -github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= -github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= -github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= -github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= -github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= -github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= -github.com/go-openapi/spec v0.19.3 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= -github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= +github.com/go-openapi/spec v0.19.5 h1:Xm0Ao53uqnk9QE/LlYV5DEU09UAgpliA85QoT9LzqPw= +github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= -github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= -github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48 h1:JVrqSeQfdhYRFk24TvhTZWU0q8lfCojxZQFi3Ou7+uY= github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b h1:/vQ+oYKu+JoyaMPDsv5FzwuL2wwWBgBbtj/YLCi4LuA= github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b/go.mod h1:Xo4aNUOrJnVruqWQJBtW6+bTBDTniY8yZum5rF3b5jw= -github.com/gobuffalo/flect v0.2.0 h1:EWCvMGGxOjsgwlWaP+f4+Hh6yrrte7JeFL2S6b+0hdM= -github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80= +github.com/gobuffalo/flect v0.2.2 h1:PAVD7sp0KOdfswjAw9BpLCU9hXo7wFSzgpQ+zNeks/A= +github.com/gobuffalo/flect v0.2.2/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc= github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -411,8 +416,10 @@ github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFG github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -437,6 +444,7 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -447,8 +455,9 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= @@ -465,18 +474,23 @@ github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+u github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo= github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM= github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI= +github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/tcpproxy v0.0.0-20180808230851-dfa16c61dad2/go.mod h1:DavVbd41y+b7ukKDmlnPR4nGYmkWXR6vHUkjQNiHPBs= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= @@ -488,7 +502,7 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+ github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= github.com/googleapis/gnostic v0.5.1 h1:A8Yhf6EtqTv9RMsU6MQTyrtV1TjWlR6xU9BsZIwuTCM= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= @@ -521,63 +535,113 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1 github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= +github.com/hashicorp/consul v1.10.0 h1:c5sPUgf9NaL80m1Hgrzt7di2AK0wm59klN7mDbsb+gk= +github.com/hashicorp/consul v1.10.0/go.mod h1:tqliCKERPJ9yk8Q8diLhqZi8G394AVrxBm5Gvdb6i1U= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.3.0 h1:HXNYlRkkM/t+Y/Yhxtwcy02dlYwIaoxzvxPnS+cqy78= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/api v1.9.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= +github.com/hashicorp/consul/api v1.9.1 h1:SngrdG2L62qqLsUz85qcPhFZ78rPf8tcD5qjMgs6MME= +github.com/hashicorp/consul/api v1.9.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/consul/sdk v0.3.0 h1:UOxjlb4xVNF93jak1mzzoBatyFju9nrkxpVwIp/QqxQ= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/consul/sdk v0.8.0 h1:OJtKBtEjboEZvG6AOUdh4Z1Zbyu0WcxQ0qatRrZHTVU= +github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-bexpr v0.1.2 h1:ijMXI4qERbzxbCnkxmfUtwMyjrrk3y+Vt0MxojNCbBs= +github.com/hashicorp/go-bexpr v0.1.2/go.mod h1:ANbpTX1oAql27TZkKVeW8p1w8NTdnyzPe/0qqPCKohU= +github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= +github.com/hashicorp/go-connlimit v0.3.0/go.mod h1:OUj9FGL1tPIhl/2RCfzYHrIiWj+VVPGNyVPnUX8AqS0= +github.com/hashicorp/go-discover v0.0.0-20200501174627-ad1e96bde088/go.mod h1:vZu6Opqf49xX5lsFAu7iFNewkcVF1sn/wyapZh5ytlg= +github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= +github.com/hashicorp/go-hclog v0.9.1/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v0.16.1 h1:IVQwpTGNRRIHafnTs2dQLIk4ENtneRIEEJWOVDqz99o= +github.com/hashicorp/go-hclog v0.16.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.0 h1:8exGP7ego3OmkfksihtSouGMZ+hQrhxx+FVELeXpVPE= +github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-kms-wrapping/entropy v0.1.0/go.mod h1:d1g9WGtAunDNpek8jUIEJnBlbgKS1N2Q61QkHiZyR1g= +github.com/hashicorp/go-memdb v1.3.1/go.mod h1:Mluclgwib3R93Hk5fxEfiRhB+6Dar64wWh71LpNSe3g= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= -github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= +github.com/hashicorp/go-raftchunking v0.6.1/go.mod h1:cGlg3JtDy7qy6c/3Bu660Mic1JF+7lWqIwCFSb08fX0= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM= github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= -github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI= +github.com/hashicorp/go-retryablehttp v0.6.7 h1:8/CAEZt/+F7kR7GevNHulKkUjLht3CPmn7egmhieNKo= +github.com/hashicorp/go-retryablehttp v0.6.7/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= +github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= +github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/hil v0.0.0-20200423225030-a18a1cd20038/go.mod h1:n2TSygSNwsLJ76m8qFXTSc7beTb+auJxYdqrnoqwZWE= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= +github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/memberlist v0.1.4 h1:gkyML/r71w3FL8gUi74Vk76avkj/9lYAY9lvg0OcoGs= github.com/hashicorp/memberlist v0.1.4/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0= +github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= +github.com/hashicorp/memberlist v0.2.4 h1:OOhYzSvFnkFQXm1ysE8RjXTHsqSRDyP4emusC9K7DYg= +github.com/hashicorp/memberlist v0.2.4/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= +github.com/hashicorp/net-rpc-msgpackrpc v0.0.0-20151116020338-a14192a58a69/go.mod h1:/z+jUGRBlwVpUZfjute9jWaF6/HuhjuFQuL1YXzVD1Q= +github.com/hashicorp/raft v1.1.1/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8= +github.com/hashicorp/raft v1.2.0/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8= +github.com/hashicorp/raft v1.3.1 h1:zDT8ke8y2aP4wf9zPTB2uSIeavJ3Hx/ceY4jxI2JxuY= +github.com/hashicorp/raft v1.3.1/go.mod h1:4Ak7FSPnuvmb0GV6vgIAJ4vYT4bek9bb6Q+7HVbyzqM= +github.com/hashicorp/raft-autopilot v0.1.5 h1:onEfMH5uHVdXQqtas36zXUHEZxLdsJVu/nXHLcLdL1I= +github.com/hashicorp/raft-autopilot v0.1.5/go.mod h1:Af4jZBwaNOI+tXfIqIdbcAnh/UyyqIMj/pOISIfhArw= +github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea/go.mod h1:pNv7Wc3ycL6F5oOWn+tPGo2gWD4a5X+yp/ntwdKLjRk= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/serf v0.9.5 h1:EBWvyu9tcRszt3Bxp3KNssBMP1KuHWyO51lz9+786iM= +github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= +github.com/hashicorp/vault/api v1.0.5-0.20200717191844-f687267c8086/go.mod h1:R3Umvhlxi2TN7Ex2hzOowyeNb+SfbVWI973N+ctaFMk= +github.com/hashicorp/vault/sdk v0.1.14-0.20200519221838-e0cfd64bc267/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10= +github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443/go.mod h1:bEpDU35nTu0ey1EXjwNwPjI9xErAsoOCmcMb9GKvyxo= +github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hashicorp/yamux v0.0.0-20200609203250-aecfd211c9ce h1:7UnVY3T/ZnHUrfviiAgIUjg2PXxsQfs5bphsG8F7Keo= +github.com/hashicorp/yamux v0.0.0-20200609203250-aecfd211c9ce/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df h1:MZf03xP9WdakyXhOWuAD5uPK3wHh96wCsqe3hCMKh8E= github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= @@ -589,6 +653,9 @@ github.com/infobloxopen/infoblox-go-client v1.1.1 h1:728A6LbLjptj/7kZjHyIxQnm768 github.com/infobloxopen/infoblox-go-client v1.1.1/go.mod h1:BXiw7S2b9qJoM8MS40vfgCNB2NLHGusk1DtO16BD9zI= github.com/instana/go-sensor v1.5.1 h1:GLxYsYiDWD15RSXDHS70VvTVU/CbwUimWrK6/e4eBPQ= github.com/instana/go-sensor v1.5.1/go.mod h1:5dEieTqu59XZr2/X53xF2Px4v83aSRRZa/47VbxAVa4= +github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= +github.com/jackc/pgx v3.3.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= +github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4= github.com/jarcoal/httpmock v1.0.6 h1:e81vOSexXU3mJuJ4l//geOmKIt+Vkxerk1feQBC8D0g= github.com/jarcoal/httpmock v1.0.6/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= github.com/jcmturner/gofork v0.0.0-20190328161633-dc7c13fece03 h1:FUwcHNlEqkqLjLBdCp5PRlCFijNjvcYANOZXzCfXwCM= @@ -604,10 +671,14 @@ github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8 github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak= github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA= +github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f/go.mod h1:KDSfL7qe5ZfQqvlDMkVjCztbmcpp/c8M77vhQP8ZPvk= +github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= @@ -619,7 +690,10 @@ github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcM github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.13.0 h1:2T7tUoQrQT+fQWdaY5rjWztFGAFwbGD04iPJg90ZiOs= +github.com/klauspost/compress v1.13.0/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b h1:DzHy0GlWeF0KAglaTMY7Q+khIFoG8toHP+wLFBVBQJc= github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -628,19 +702,21 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/labbsr0x/bindman-dns-webhook v1.0.2 h1:I7ITbmQPAVwrDdhd6dHKi+MYJTJqPCK0jE6YNBAevnk= github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA= github.com/labbsr0x/goh v1.0.1 h1:97aBJkDjpyBZGPbQuOK5/gHcSFbcr5aRsq3RSRJFpPk= github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w= github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= +github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/libkermit/compose v0.0.0-20171122111507-c04e39c026ad h1:nTyRWZ864mnHUnusBCVA628AZFgfGHwRUpbHqGhRQr8= github.com/libkermit/compose v0.0.0-20171122111507-c04e39c026ad/go.mod h1:GyCk/ifDcqsU1tsRMMWqXANnTtxzcwEWscb7j5qmblM= github.com/libkermit/docker v0.0.0-20171122101128-e6674d32b807 h1:/7J1WDQd6Xn1Pr8KtE2I/7/cKw66AV3hBUOyxqyXo84= @@ -649,6 +725,7 @@ github.com/libkermit/docker-check v0.0.0-20171122104347-1113af38e591 h1:+zkZyvOy github.com/libkermit/docker-check v0.0.0-20171122104347-1113af38e591/go.mod h1:EBQ0jeOrBpOTkquwjmJl4W6z5xqlf5oA2LZfTqRNcO0= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/linode/linodego v0.7.1/go.mod h1:ga11n3ivecUrPCHN0rANxKmfWBJVkOXfLMZinAbj2sY= github.com/linode/linodego v0.25.3 h1:OKUepYBitakHsnI75maYefrkBtOz6CGt/4o4CKnMlnM= github.com/linode/linodego v0.25.3/go.mod h1:GSBKPpjoQfxEfryoCRcgkuUOCuVtGHWhzI8OMdycNTE= github.com/liquidweb/go-lwApi v0.0.0-20190605172801-52a4864d2738/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs= @@ -658,6 +735,7 @@ github.com/liquidweb/liquidweb-cli v0.6.9 h1:acbIvdRauiwbxIsOCEMXGwF75aSJDbDiyAW github.com/liquidweb/liquidweb-cli v0.6.9/go.mod h1:cE1uvQ+x24NGUL75D0QagOFCG8Wdvmwu8aL9TLmA/eQ= github.com/liquidweb/liquidweb-go v1.6.3 h1:NVHvcnX3eb3BltiIoA+gLYn15nOpkYkdizOEYGSKrk4= github.com/liquidweb/liquidweb-go v1.6.3/go.mod h1:SuXXp+thr28LnjEw18AYtWwIbWMHSUiajPQs8T9c/Rc= +github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/looplab/fsm v0.1.0 h1:Qte7Zdn/5hBNbXzP7yxVU4OIFHWXBovyTT2LaBTyC20= github.com/looplab/fsm v0.1.0/go.mod h1:m2VaOfDHxqXBBMgc26m6yUOwkFn8H2AlJDE+jd/uafI= github.com/lucas-clemente/quic-go v0.20.1 h1:hb5m76V8QS/8Nw/suHvXqo3BMHAozvIkcnzpJdpanSk= @@ -673,7 +751,6 @@ github.com/mailgun/timetools v0.0.0-20141028012446-7e6055773c51/go.mod h1:RYmqHb github.com/mailgun/ttlmap v0.0.0-20170619185759-c1c17f74874f h1:ZZYhg16XocqSKPGNQAe0aeweNtFxuedbwwb4fSlg7h4= github.com/mailgun/ttlmap v0.0.0-20170619185759-c1c17f74874f/go.mod h1:8heskWJ5c0v5J9WH89ADhyal1DOZcayll8fSbhB+/9A= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -689,6 +766,7 @@ github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIG github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -697,9 +775,11 @@ github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= @@ -709,31 +789,47 @@ github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182aff github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.40 h1:pyyPFfGMnciYUk/mXpKkVmeMQjfXqt3FAJ2hy7tPiLA= +github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= +github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= +github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.14.0 h1:/x0XQ6h+3U3nAyk1yx+bHPURrKa9sVVvYbuqZ7pIAtI= +github.com/mitchellh/go-testing-interface v1.14.0/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= github.com/mitchellh/hashstructure v1.0.0 h1:ZkRJX1CyOoTkar7p/mLS5TZU4nJ1Rn/F8u9dGS02Q3Y= github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8= +github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= +github.com/mitchellh/mapstructure v1.4.1-0.20210112042008-8ebf2d61a8b4/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/pointerstructure v1.0.0 h1:ATSdz4NWrmWPOF1CeCBU4sMCno2hgqdbSrRPFWQSVZI= +github.com/mitchellh/pointerstructure v1.0.0/go.mod h1:k4XwG94++jLVsSiTxo7qdIfXA9pj9EAeo0QsNNJOLZ8= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE= +github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= @@ -755,6 +851,9 @@ github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OS github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2/go.mod h1:TLb2Sg7HQcgGdloNxkrmtgDNR9uVYF3lfdFIN4Ro6Sk= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nrdcg/auroradns v1.0.1 h1:m/kBq83Xvy3cU261MOknd8BdnOk12q4lAWM+kOdsC2Y= github.com/nrdcg/auroradns v1.0.1/go.mod h1:y4pc0i9QXYlFCWrhWrUSIETnZgrf4KuwjDIWmmXo3JI= github.com/nrdcg/desec v0.5.0 h1:foL7hqivYOMlv0qDhHXJtuuEXkqf0wW9EQMqyrt228g= @@ -773,6 +872,7 @@ github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtb github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.0-20180130162743-b8a9be070da4/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -788,7 +888,6 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.2 h1:aY/nuoWlKJud2J6U0E3NWsjlg+0GtwXxgEqthRdzlcs= github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= @@ -821,6 +920,7 @@ github.com/oracle/oci-go-sdk v24.3.0+incompatible h1:x4mcfb4agelf1O4/1/auGlZ1lr9 github.com/oracle/oci-go-sdk v24.3.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= github.com/ovh/go-ovh v1.1.0 h1:bHXZmw8nTgZin4Nv7JuaLs0KG5x54EQR7migYTd1zrk= github.com/ovh/go-ovh v1.1.0/go.mod h1:AxitLZ5HBRPyUd+Zl60Ajaag+rNTdVXWIkzfrVuTXWA= +github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c/go.mod h1:otzZQXgoO96RTzDB/Hycg0qZcXZsWJGJRSXbmEIJ+4M= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= @@ -836,8 +936,9 @@ github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pierrec/lz4 v0.0.0-20190327172049-315a67e90e41/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= -github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= +github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pires/go-proxyproto v0.5.0 h1:A4Jv4ZCaV3AFJeGh5mGwkz4iuWUYMlQ7IoO/GTuSuLo= github.com/pires/go-proxyproto v0.5.0/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -847,10 +948,13 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/pquerna/otp v1.3.0 h1:oJV/SkzR33anKXwQU3Of42rL4wbrffP4uvUf1SvS5Xs= github.com/pquerna/otp v1.3.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -861,6 +965,7 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -878,6 +983,7 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -897,14 +1003,19 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA= github.com/rancher/go-rancher-metadata v0.0.0-20200311180630-7f4c936a06ac h1:wBGhHdXKICZmvAPWS8gQoMyOWDH7QAi9bU4Z1nDWnFU= github.com/rancher/go-rancher-metadata v0.0.0-20200311180630-7f4c936a06ac/go.mod h1:67sLWL17mVlO1HFROaTBmU71NB4R8UNCesFHhg0f6LQ= +github.com/rboyer/safeio v0.2.1/go.mod h1:Cq/cEPK+YXFn622lsQ0K4KsPZSPtaptHHEldsy7Fmig= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03/go.mod h1:gRAiPF5C5Nd0eyyRdqIu9qTiFSoZzpTq727b5B8fkkU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/zerolog v1.4.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/sacloud/libsacloud v1.36.2 h1:aosI7clbQ9IU0Hj+3rpk3SKJop5nLPpLThnWCivPqjI= github.com/sacloud/libsacloud v1.36.2/go.mod h1:P7YAOVmnIn3DKHqCZcUKYUXmSwGBm3yS7IBEjKVSrjg= github.com/samuel/go-zookeeper v0.0.0-20180130194729-c4fab1ac1bec/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= @@ -912,13 +1023,18 @@ github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da h1:p3Vo3i64TCL github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/santhosh-tekuri/jsonschema v1.2.4 h1:hNhW8e7t+H1vgY+1QeEQpveR6D4+OwKPXCfD2aieJis= github.com/santhosh-tekuri/jsonschema v1.2.4/go.mod h1:TEAUOeZSmIxTTuHatJzrvARHiuO9LYd+cIxzgEHCQI4= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f h1:WSnaD0/cvbKJgSTYbjAPf4RJXVvNNDAwVm+W8wEmnGE= github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8= +github.com/sean-/conswriter v0.0.0-20180208195008-f5ae3917a627/go.mod h1:7zjs06qF79/FKAJpBvFx3P8Ww4UTIMAe+lpNXDHziac= +github.com/sean-/pager v0.0.0-20180208200047-666be9bf53b5/go.mod h1:BeybITEsBEg6qbIiqJ6/Bqeq25bCLbL7YFmpaFfJDuM= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM= github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shirou/gopsutil/v3 v3.20.10/go.mod h1:igHnfak0qnw1biGeI2qKQvu0ZkwvEkUcCLlYhZzdr/4= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= @@ -944,6 +1060,7 @@ github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go. github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= +github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -961,6 +1078,7 @@ github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIK github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/gunit v1.0.4 h1:tpTjnuH7MLlqhoD21vRoMZbMIi5GmBsAJDFyF67GhZA= github.com/smartystreets/gunit v1.0.4/go.mod h1:EH5qMBab2UclzXUcpR8b93eHsIlp9u+pDQIRp5DZNzQ= +github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d/go.mod h1:Cw4GTlQccdRGSEf6KiMju767x0NEHE0YIVPJSaXjlsw= github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= @@ -968,6 +1086,7 @@ github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:Udh github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -975,7 +1094,6 @@ github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= @@ -998,6 +1116,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As= github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -1010,20 +1129,20 @@ github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154/go.mod h1:7jxm github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4= +github.com/tent/http-link-go v0.0.0-20130702225549-ac974c61c2f9/go.mod h1:RHkNRtSLfOK7qBTHaeSX1D6BNpI3qw7NTxsmNr4RvN8= github.com/tinylib/msgp v1.0.2 h1:DfdQrzQa7Yh2es9SuLkixqxuXS2SxsdYn0KbdrOGWD8= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/traefik/gziphandler v1.1.2-0.20210212101304-175e0fad6888 h1:GMY0C+M/w8xO+/NP3Kq6sroMd+z2KbbdVr1K8o2NLHk= -github.com/traefik/gziphandler v1.1.2-0.20210212101304-175e0fad6888/go.mod h1:sLqwoN03tkluITKL+lPEZbfsJQU2suYoKbrR/HeV9aM= github.com/traefik/paerser v0.1.4 h1:/IXjV04Gf6di51H8Jl7jyS3OylsLjIasrwXIIwj1aT8= github.com/traefik/paerser v0.1.4/go.mod h1:FIdQ4Y92ulQUGSeZgxchtBKEcLw1o551PMNg9PoIq/4= -github.com/traefik/yaegi v0.9.19 h1:ze01+pVtKmxSogy0wlAPSvm2LoDYuZj2LdH3S6GxHcQ= -github.com/traefik/yaegi v0.9.19/go.mod h1:FAYnRlZyuVlEkvnkHq3bvJ1lW5be6XuwgLdkYgYG6Lk= +github.com/traefik/yaegi v0.9.21 h1:Ar123+dawjSKTUqkhWF5q7pCeR3Ei0V5070teAZxnQ0= +github.com/traefik/yaegi v0.9.21/go.mod h1:FAYnRlZyuVlEkvnkHq3bvJ1lW5be6XuwgLdkYgYG6Lk= github.com/transip/gotransip/v6 v6.2.0 h1:0Z+qVsyeiQdWfcAUeJyF0IEKAPvhJwwpwPi2WGtBIiE= github.com/transip/gotransip/v6 v6.2.0/go.mod h1:pQZ36hWWRahCUXkFWlx9Hs711gLd8J4qdgLdRzmtY+g= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 h1:G3dpKMzFDjgEh2q1Z7zUUtKa8ViPtH+ocF0bE0g00O8= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/uber-go/atomic v1.3.2 h1:Azu9lPBWRNKzYXSIwRfgRuDuS0YKsK4NFhiQv98gkxo= github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= @@ -1049,11 +1168,11 @@ github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPU github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/vdemeester/shakers v0.1.0 h1:K+n9sSyUCg2ywmZkv+3c7vsYZfivcfKhMh8kRxCrONM= github.com/vdemeester/shakers v0.1.0/go.mod h1:IZ1HHynUOQt32iQ3rvAeVddXLd19h/6LWiKsh9RZtAQ= -github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/vinyldns/go-vinyldns v0.0.0-20200917153823-148a5f6b8f14 h1:TFXGGMHmml4rs29PdPisC/aaCzOxUu1Vsh9on/IpUfE= github.com/vinyldns/go-vinyldns v0.0.0-20200917153823-148a5f6b8f14/go.mod h1:RWc47jtnVuQv6+lY3c768WtXCas/Xi+U5UFc5xULmYg= +github.com/vmware/govmomi v0.18.0/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= github.com/vulcand/oxy v1.3.0 h1:358BVHmJNLjhOrhbjq2EVJX5NQ3HxrP0d5OyHLRliX0= github.com/vulcand/oxy v1.3.0/go.mod h1:hN/gw/jg+GH4A+bqvznsW26Izd4jNGV6h1z3s7drRzs= github.com/vulcand/predicate v1.1.0 h1:Gq/uWopa4rx/tnZu2opOSBqHK63Yqlou/SzrbwdJiNg= @@ -1090,9 +1209,6 @@ go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mI go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= go.etcd.io/etcd v3.3.13+incompatible h1:jCejD5EMnlGxFvcGRyEV4VGlENZc7oPQX6o0t7n3xbw= go.etcd.io/etcd v3.3.13+incompatible/go.mod h1:yaeTdrJi5lOmYerz05bd8+V7KubZs8YSFZfzsF9A6aI= -go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= @@ -1106,6 +1222,7 @@ go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= @@ -1129,17 +1246,17 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200317142112-1b76d66859c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -1147,8 +1264,10 @@ golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1181,13 +1300,13 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1199,7 +1318,6 @@ golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -1222,11 +1340,15 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d h1:1aflnvSoWWLI2k/dMUAl5lvU1YO4Mb4hz0gh+1rjcxU= golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1 h1:4qWs8cYYH6PoEFy4dfhDFgoMGkwAcETd+MmPdCPMzUc= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1242,8 +1364,9 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1256,18 +1379,20 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190523142557-0e01d883c5c5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1277,11 +1402,11 @@ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191025021431-6c3a3bfe00ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1293,6 +1418,7 @@ golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1303,26 +1429,34 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201024232916-9f70ab9862d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201110211018-35f3e6cf4a65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201231184435-2d18734c6014 h1:joucsQqXmyBVxViHCPFjG3hx8JzIFSaym3l3MM/Jsdg= golang.org/x/sys v0.0.0-20201231184435-2d18734c6014/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 h1:Bli41pIlzTzf3KEY06n+xnzK/BESIg2ze4Pgfh/aI8c= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE= @@ -1334,7 +1468,6 @@ golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -1345,13 +1478,12 @@ golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1376,10 +1508,12 @@ golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200513154647-78b527d18275/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200616195046-dc31b401abb5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200904185747-39188db58858 h1:xLt+iB5ksWcZVxqc+g9K41ZHy+6MKWfXCDsjSThnsPA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1406,6 +1540,7 @@ google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= @@ -1445,9 +1580,11 @@ google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLD google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk= @@ -1465,16 +1602,19 @@ google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/DataDog/dd-trace-go.v1 v1.19.0 h1:aFSFd6oDMdvPYiToGqTv7/ERA6QrPhGaXSuueRCaM88= gopkg.in/DataDog/dd-trace-go.v1 v1.19.0/go.mod h1:DVp8HmDh8PuTu2Z0fVVlBsyWaC++fzwVCaGWylTe3tg= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/h2non/gock.v1 v1.0.15 h1:SzLqcIlb/fDfg7UvukMpNcWsu7sI5tWwL+KCATZqks0= gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= @@ -1515,7 +1655,6 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= @@ -1533,31 +1672,33 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= howett.net/plist v0.0.0-20181124034731-591f970eefbb h1:jhnBjNi9UFpfpl8YZhA9CrOqpnJdvzuiHsl/dnxl11M= howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= -k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78= +k8s.io/api v0.16.9/go.mod h1:Y7dZNHs1Xy0mSwSlzL9QShi6qkljnN41yR8oWCRTDe8= k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= -k8s.io/api v0.20.2 h1:y/HR22XDZY3pniu9hIFDLpUCPq2w5eQ6aV/VFQ7uJMw= k8s.io/api v0.20.2/go.mod h1:d7n6Ehyzx+S+cE3VhTGfVNNqtGc/oL9DCdYYahlurV8= -k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY= -k8s.io/apiextensions-apiserver v0.20.1 h1:ZrXQeslal+6zKM/HjDXLzThlz/vPSxrfK3OqL8txgVQ= +k8s.io/api v0.21.0 h1:gu5iGF4V6tfVCQ/R+8Hc0h7H1JuEhzyEi9S4R5LM8+Y= +k8s.io/api v0.21.0/go.mod h1:+YbrhBBGgsxbF6o6Kj4KJPJnBmAKuXDeS3E18bgHNVU= k8s.io/apiextensions-apiserver v0.20.1/go.mod h1:ntnrZV+6a3dB504qwC5PN/Yg9PBiDNt1EVqbW2kORVk= -k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= +k8s.io/apiextensions-apiserver v0.20.2 h1:rfrMWQ87lhd8EzQWRnbQ4gXrniL/yTRBgYH1x1+BLlo= +k8s.io/apiextensions-apiserver v0.20.2/go.mod h1:F6TXp389Xntt+LUq3vw6HFOLttPa0V8821ogLGwb6Zs= +k8s.io/apimachinery v0.16.9/go.mod h1:Xk2vD2TRRpuWYLQNM6lT9R7DSFZUYG03SarNkbGrnKE= k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.2 h1:hFx6Sbt1oG0n6DZ+g4bFt5f6BoMkOjKWsQFu077M3Vg= k8s.io/apimachinery v0.20.2/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apiserver v0.18.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw= +k8s.io/apimachinery v0.21.0 h1:3Fx+41if+IRavNcKOz09FwEXDBG6ORh6iMsTSelhkMA= +k8s.io/apimachinery v0.21.0/go.mod h1:jbreFvJo3ov9rj7eWT7+sYiRx+qZuCYXwWT1bcDswPY= k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= -k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU= +k8s.io/apiserver v0.20.2/go.mod h1:2nKd93WyMhZx4Hp3RfgH2K5PhwyTrprrkWYnI7id7jA= +k8s.io/client-go v0.16.9/go.mod h1:ThjPlh7Kx+XoBFOCt775vx5J7atwY7F/zaFzTco5gL0= k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= -k8s.io/client-go v0.20.2 h1:uuf+iIAbfnCSw8IGAv/Rg0giM+2bOzHLOsbbrwrdhNQ= k8s.io/client-go v0.20.2/go.mod h1:kH5brqWqp7HDxUFKoEgiI4v8G1xzbe9giaCenUWJzgE= -k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= +k8s.io/client-go v0.21.0 h1:n0zzzJsAQmJngpC0IhgFcApZyoGXPrDIAD601HD09ag= +k8s.io/client-go v0.21.0/go.mod h1:nNBytTF9qPFDEhoqgEPaarobC8QPae13bElIVHzIglA= k8s.io/code-generator v0.20.1/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg= -k8s.io/code-generator v0.20.2 h1:SQaysped4EtUDk3u1zphnUJiOAwFdhHx9xS3WKAE0x8= k8s.io/code-generator v0.20.2/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg= -k8s.io/component-base v0.18.2/go.mod h1:kqLlMuhJNHQ9lz8Z7V5bxUUtjFZnrypArGl58gmDfUM= +k8s.io/code-generator v0.21.0 h1:LGWJOvkbBNpuRBqBRXUjzfvymUh7F/iR2KDpwLnqCM4= +k8s.io/code-generator v0.21.0/go.mod h1:hUlps5+9QaTrKx+jiM4rmq7YmH8wPOIko64uZCHDh6Q= k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= +k8s.io/component-base v0.20.2/go.mod h1:pzFtCiwe/ASD0iV7ySMu8SYVJjCapNM9bjvk7ptpKh0= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/gengo v0.0.0-20201203183100-97869a43a9d9/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= @@ -1570,32 +1711,37 @@ k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.4.0 h1:7+X0fUguPyrKEC4WjH8iGDg3laWgMo5tMnRTIGTTxGQ= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= -k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd h1:sOHNzJIkytDF6qadMNKhhDRpc6ODik8lVC6nOur7B2c= +k8s.io/klog/v2 v2.8.0 h1:Q3gmuM9hKEjefWFFYF0Mat+YyFJvsUyYuwyNNJ5C9Ts= +k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= +k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= +k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7 h1:vEx13qjvaZ4yfObSSXW7BrMc/KQBBT/Jyee8XtLf4x0= +k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= -k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw= +k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20210111153108-fddb29f9d009/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20210305010621-2afb4311ab10/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20210709001253-0e1f9d693477 h1:AuP1OP6ExD2U8pP9w85xHtmgBn/Ob3pngZwWMe12GBQ= +k8s.io/utils v0.0.0-20210709001253-0e1f9d693477/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM= mvdan.cc/xurls/v2 v2.1.0 h1:KaMb5GLhlcSX+e+qhbRJODnUUBvlw01jt4yrjFIHAuA= mvdan.cc/xurls/v2 v2.1.0/go.mod h1:5GrSd9rOnKOpZaji1OZLYL/yeAAtGDlo/cFe+8K5n8E= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/controller-runtime v0.8.0/go.mod h1:v9Lbj5oX443uR7GXYY46E0EE2o7k2YxQ58GxVNeXSW4= -sigs.k8s.io/controller-tools v0.4.1 h1:VkuV0MxlRPmRu5iTgBZU4UxUX2LiR99n3sdQGRxZF4w= -sigs.k8s.io/controller-tools v0.4.1/go.mod h1:G9rHdZMVlBDocIxGkK3jHLWqcTMNvveypYJwrvYKjWU= -sigs.k8s.io/gateway-api v0.2.0 h1:7cHyUed8LLFXPyzUl/mGylimx3E1CWHJYUK0/AHfEyg= -sigs.k8s.io/gateway-api v0.2.0/go.mod h1:IUbl4vAjUFoa2nt2gER8NsUrAu84x2edpWXbXBvcNis= -sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= -sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2 h1:YHQV7Dajm86OuqnIR6zAelnDWBRjo+YhYV9PmGrh1s8= +sigs.k8s.io/controller-runtime v0.8.3/go.mod h1:U/l+DUopBc1ecfRZ5aviA9JDmGFQKvLf5YkZNx2e0sU= +sigs.k8s.io/controller-tools v0.5.0 h1:3u2RCwOlp0cjCALAigpOcbAf50pE+kHSdueUosrC/AE= +sigs.k8s.io/controller-tools v0.5.0/go.mod h1:JTsstrMpxs+9BUj6eGuAaEb6SDSPTeVtUyp0jmnAM/I= +sigs.k8s.io/gateway-api v0.3.0 h1:mKbQRlRIIY3dsCCbNF9Jv30V9vvOf6SRG82l0MfJQ9U= +sigs.k8s.io/gateway-api v0.3.0/go.mod h1:Wb8bx7QhGVZxOSEU3i9vw/JqTB5Nlai9MLMYVZeDmRQ= +sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e h1:4Z09Hglb792X0kfOBBJUPFEyvVfQWrYT/l8h5EKA6JQ= +sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.1.0 h1:C4r9BgJ98vrKnnVCjwCSXcWjWe0NKcUQkmzDXZXGwH8= +sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/integration/consul_catalog_test.go b/integration/consul_catalog_test.go index 6aaa03619..df033e1bb 100644 --- a/integration/consul_catalog_test.go +++ b/integration/consul_catalog_test.go @@ -54,6 +54,18 @@ func (s *ConsulCatalogSuite) waitToElectConsulLeader() error { }) } +func (s *ConsulCatalogSuite) waitForConnectCA() error { + return try.Do(15*time.Second, func() error { + caroots, _, err := s.consulClient.Connect().CARoots(nil) + + if err != nil || len(caroots.Roots) == 0 { + return fmt.Errorf("connect CA not fully initialized. %w", err) + } + + return nil + }) +} + func (s *ConsulCatalogSuite) TearDownSuite(c *check.C) { // shutdown and delete compose project if s.composeProject != nil { @@ -611,3 +623,221 @@ func (s *ConsulCatalogSuite) TestConsulServiceWithHealthCheck(c *check.C) { err = s.deregisterService("whoami2", false) c.Assert(err, checker.IsNil) } + +func (s *ConsulCatalogSuite) TestConsulConnect(c *check.C) { + // Wait for consul to fully initialize connect CA + err := s.waitForConnectCA() + c.Assert(err, checker.IsNil) + + connectIP := s.composeProject.Container(c, "connect").NetworkSettings.IPAddress + reg := &api.AgentServiceRegistration{ + ID: "uuid-api1", + Name: "uuid-api", + Tags: []string{ + "traefik.enable=true", + "traefik.consulcatalog.connect=true", + "traefik.http.routers.router1.rule=Path(`/`)", + "traefik.http.routers.router1.service=service1", + "traefik.http.services.service1.loadBalancer.server.url=https://" + connectIP, + }, + Connect: &api.AgentServiceConnect{ + Native: true, + }, + Port: 443, + Address: connectIP, + } + err = s.registerService(reg, false) + c.Assert(err, checker.IsNil) + + whoamiIP := s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress + regWhoami := &api.AgentServiceRegistration{ + ID: "whoami1", + Name: "whoami", + Tags: []string{ + "traefik.enable=true", + "traefik.http.routers.router2.rule=Path(`/whoami`)", + "traefik.http.routers.router2.service=whoami", + }, + Port: 80, + Address: whoamiIP, + } + err = s.registerService(regWhoami, false) + c.Assert(err, checker.IsNil) + + tempObjects := struct { + ConsulAddress string + }{ + ConsulAddress: s.consulAddress, + } + file := s.adaptFile(c, "fixtures/consul_catalog/connect.toml", tempObjects) + defer os.Remove(file) + + cmd, display := s.traefikCmd(withConfigFile(file)) + defer display(c) + err = cmd.Start() + c.Assert(err, checker.IsNil) + defer s.killCmd(cmd) + + err = try.GetRequest("http://127.0.0.1:8000/", 10*time.Second, try.StatusCodeIs(http.StatusOK)) + c.Assert(err, checker.IsNil) + + err = try.GetRequest("http://127.0.0.1:8000/whoami", 10*time.Second, try.StatusCodeIs(http.StatusOK)) + c.Assert(err, checker.IsNil) + + err = s.deregisterService("uuid-api1", false) + c.Assert(err, checker.IsNil) + err = s.deregisterService("whoami1", false) + c.Assert(err, checker.IsNil) +} + +func (s *ConsulCatalogSuite) TestConsulConnect_ByDefault(c *check.C) { + // Wait for consul to fully initialize connect CA + err := s.waitForConnectCA() + c.Assert(err, checker.IsNil) + + connectIP := s.composeProject.Container(c, "connect").NetworkSettings.IPAddress + reg := &api.AgentServiceRegistration{ + ID: "uuid-api1", + Name: "uuid-api", + Tags: []string{ + "traefik.enable=true", + "traefik.http.routers.router1.rule=Path(`/`)", + "traefik.http.routers.router1.service=service1", + "traefik.http.services.service1.loadBalancer.server.url=https://" + connectIP, + }, + Connect: &api.AgentServiceConnect{ + Native: true, + }, + Port: 443, + Address: connectIP, + } + err = s.registerService(reg, false) + c.Assert(err, checker.IsNil) + + whoamiIP := s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress + regWhoami := &api.AgentServiceRegistration{ + ID: "whoami1", + Name: "whoami1", + Tags: []string{ + "traefik.enable=true", + "traefik.http.routers.router2.rule=Path(`/whoami`)", + "traefik.http.routers.router2.service=whoami", + }, + Port: 80, + Address: whoamiIP, + } + err = s.registerService(regWhoami, false) + c.Assert(err, checker.IsNil) + + whoami2IP := s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress + regWhoami2 := &api.AgentServiceRegistration{ + ID: "whoami2", + Name: "whoami2", + Tags: []string{ + "traefik.enable=true", + "traefik.consulcatalog.connect=false", + "traefik.http.routers.router2.rule=Path(`/whoami2`)", + "traefik.http.routers.router2.service=whoami2", + }, + Port: 80, + Address: whoami2IP, + } + err = s.registerService(regWhoami2, false) + c.Assert(err, checker.IsNil) + + tempObjects := struct { + ConsulAddress string + }{ + ConsulAddress: s.consulAddress, + } + file := s.adaptFile(c, "fixtures/consul_catalog/connect_by_default.toml", tempObjects) + defer os.Remove(file) + + cmd, display := s.traefikCmd(withConfigFile(file)) + defer display(c) + err = cmd.Start() + c.Assert(err, checker.IsNil) + defer s.killCmd(cmd) + + err = try.GetRequest("http://127.0.0.1:8000/", 10*time.Second, try.StatusCodeIs(http.StatusOK)) + c.Assert(err, checker.IsNil) + + err = try.GetRequest("http://127.0.0.1:8000/whoami", 10*time.Second, try.StatusCodeIs(http.StatusNotFound)) + c.Assert(err, checker.IsNil) + + err = try.GetRequest("http://127.0.0.1:8000/whoami2", 10*time.Second, try.StatusCodeIs(http.StatusOK)) + c.Assert(err, checker.IsNil) + + err = s.deregisterService("uuid-api1", false) + c.Assert(err, checker.IsNil) + err = s.deregisterService("whoami1", false) + c.Assert(err, checker.IsNil) + err = s.deregisterService("whoami2", false) + c.Assert(err, checker.IsNil) +} + +func (s *ConsulCatalogSuite) TestConsulConnect_NotAware(c *check.C) { + // Wait for consul to fully initialize connect CA + err := s.waitForConnectCA() + c.Assert(err, checker.IsNil) + + connectIP := s.composeProject.Container(c, "connect").NetworkSettings.IPAddress + reg := &api.AgentServiceRegistration{ + ID: "uuid-api1", + Name: "uuid-api", + Tags: []string{ + "traefik.enable=true", + "traefik.consulcatalog.connect=true", + "traefik.http.routers.router1.rule=Path(`/`)", + "traefik.http.routers.router1.service=service1", + "traefik.http.services.service1.loadBalancer.server.url=https://" + connectIP, + }, + Connect: &api.AgentServiceConnect{ + Native: true, + }, + Port: 443, + Address: connectIP, + } + err = s.registerService(reg, false) + c.Assert(err, checker.IsNil) + + whoamiIP := s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress + regWhoami := &api.AgentServiceRegistration{ + ID: "whoami1", + Name: "whoami", + Tags: []string{ + "traefik.enable=true", + "traefik.http.routers.router2.rule=Path(`/whoami`)", + "traefik.http.routers.router2.service=whoami", + }, + Port: 80, + Address: whoamiIP, + } + err = s.registerService(regWhoami, false) + c.Assert(err, checker.IsNil) + + tempObjects := struct { + ConsulAddress string + }{ + ConsulAddress: s.consulAddress, + } + file := s.adaptFile(c, "fixtures/consul_catalog/connect_not_aware.toml", tempObjects) + defer os.Remove(file) + + cmd, display := s.traefikCmd(withConfigFile(file)) + defer display(c) + err = cmd.Start() + c.Assert(err, checker.IsNil) + defer s.killCmd(cmd) + + err = try.GetRequest("http://127.0.0.1:8000/", 10*time.Second, try.StatusCodeIs(http.StatusNotFound)) + c.Assert(err, checker.IsNil) + + err = try.GetRequest("http://127.0.0.1:8000/whoami", 10*time.Second, try.StatusCodeIs(http.StatusOK)) + c.Assert(err, checker.IsNil) + + err = s.deregisterService("uuid-api1", false) + c.Assert(err, checker.IsNil) + err = s.deregisterService("whoami1", false) + c.Assert(err, checker.IsNil) +} diff --git a/integration/fixtures/consul_catalog/connect.toml b/integration/fixtures/consul_catalog/connect.toml new file mode 100644 index 000000000..f093c499a --- /dev/null +++ b/integration/fixtures/consul_catalog/connect.toml @@ -0,0 +1,21 @@ +[global] + checkNewVersion = false + sendAnonymousUsage = false + +[log] + level = "DEBUG" + +[entryPoints] + [entryPoints.web] + address = ":8000" + +[api] + insecure = true + +[providers] + [providers.consulCatalog] + exposedByDefault = false + refreshInterval = "500ms" + connectAware = true + [providers.consulCatalog.endpoint] + address = "{{ .ConsulAddress }}" diff --git a/integration/fixtures/consul_catalog/connect_by_default.toml b/integration/fixtures/consul_catalog/connect_by_default.toml new file mode 100644 index 000000000..67f823ad4 --- /dev/null +++ b/integration/fixtures/consul_catalog/connect_by_default.toml @@ -0,0 +1,22 @@ +[global] + checkNewVersion = false + sendAnonymousUsage = false + +[log] + level = "DEBUG" + +[entryPoints] + [entryPoints.web] + address = ":8000" + +[api] + insecure = true + +[providers] + [providers.consulCatalog] + exposedByDefault = false + refreshInterval = "500ms" + connectAware = true + connectByDefault = true + [providers.consulCatalog.endpoint] + address = "{{ .ConsulAddress }}" diff --git a/integration/fixtures/consul_catalog/connect_not_aware.toml b/integration/fixtures/consul_catalog/connect_not_aware.toml new file mode 100644 index 000000000..6a6c10d94 --- /dev/null +++ b/integration/fixtures/consul_catalog/connect_not_aware.toml @@ -0,0 +1,21 @@ +[global] + checkNewVersion = false + sendAnonymousUsage = false + +[log] + level = "DEBUG" + +[entryPoints] + [entryPoints.web] + address = ":8000" + +[api] + insecure = true + +[providers] + [providers.consulCatalog] + exposedByDefault = false + refreshInterval = "500ms" + connectAware = false + [providers.consulCatalog.endpoint] + address = "{{ .ConsulAddress }}" diff --git a/integration/fixtures/k8s/01-gateway-api.yml b/integration/fixtures/k8s/01-gateway-api.yml index fc9e02ccc..8a7da2c7d 100644 --- a/integration/fixtures/k8s/01-gateway-api.yml +++ b/integration/fixtures/k8s/01-gateway-api.yml @@ -4,152 +4,206 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.4.1 + controller-gen.kubebuilder.io/version: v0.5.0 creationTimestamp: null name: gatewayclasses.networking.x-k8s.io spec: group: networking.x-k8s.io names: + categories: + - gateway-api kind: GatewayClass listKind: GatewayClassList plural: gatewayclasses shortNames: - - gc + - gc singular: gatewayclass scope: Cluster versions: - - additionalPrinterColumns: - - jsonPath: .spec.controller - name: Controller - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: "GatewayClass describes a class of Gateways available to the user for creating Gateway resources. \n GatewayClass is a Cluster level resource." - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of GatewayClass. - properties: - controller: - description: "Controller is a domain/path string that indicates the controller that is managing Gateways of this class. \n Example: \"acme.io/gateway-controller\". \n This field is not mutable and cannot be empty. \n The format of this field is DOMAIN \"/\" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). \n Support: Core" - maxLength: 253 - type: string - parametersRef: - description: "ParametersRef is a reference to a resource that contains the configuration parameters corresponding to the GatewayClass. This is optional if the controller does not require any additional configuration. \n ParametersRef can reference a standard Kubernetes resource, i.e. ConfigMap, or an implementation-specific custom resource. The resource can be cluster-scoped or namespace-scoped. \n If the referent cannot be found, the GatewayClass's \"InvalidParameters\" status condition will be true. \n Support: Custom" - properties: - group: - description: Group is the group of the referent. - maxLength: 253 - minLength: 1 - type: string - kind: - description: Kind is kind of the referent. - maxLength: 253 - minLength: 1 - type: string - name: - description: Name is the name of the referent. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: Namespace is the namespace of the referent. This field is required when scope is set to "Namespace" and ignored when scope is set to "Cluster". - maxLength: 253 - minLength: 1 - type: string - scope: - default: Cluster - description: Scope represents if the referent is a Cluster or Namespace scoped resource. This may be set to "Cluster" or "Namespace". - enum: - - Cluster - - Namespace - type: string - required: - - group - - kind - - name - type: object - required: - - controller - type: object - status: - default: - conditions: - - lastTransitionTime: "1970-01-01T00:00:00Z" - message: Waiting for controller - reason: Waiting - status: "False" - type: Admitted - description: Status defines the current state of GatewayClass. - properties: - conditions: - default: - - lastTransitionTime: "1970-01-01T00:00:00Z" - message: Waiting for controller - reason: Waiting - status: "False" - type: Admitted - description: "Conditions is the current status from the controller for this GatewayClass. \n Controllers should prefer to publish conditions using values of GatewayClassConditionType for the type of each Condition." - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + - additionalPrinterColumns: + - jsonPath: .spec.controller + name: Controller + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: "GatewayClass describes a class of Gateways available to the + user for creating Gateway resources. \n GatewayClass is a Cluster level + resource." + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of GatewayClass. + properties: + controller: + description: "Controller is a domain/path string that indicates the + controller that is managing Gateways of this class. \n Example: + \"acme.io/gateway-controller\". \n This field is not mutable and + cannot be empty. \n The format of this field is DOMAIN \"/\" PATH, + where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). + \n Support: Core" + maxLength: 253 + type: string + parametersRef: + description: "ParametersRef is a reference to a resource that contains + the configuration parameters corresponding to the GatewayClass. + This is optional if the controller does not require any additional + configuration. \n ParametersRef can reference a standard Kubernetes + resource, i.e. ConfigMap, or an implementation-specific custom resource. + The resource can be cluster-scoped or namespace-scoped. \n If the + referent cannot be found, the GatewayClass's \"InvalidParameters\" + status condition will be true. \n Support: Custom" properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 + group: + description: Group is the group of the referent. + maxLength: 253 minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string - status: - description: status of the condition, one of True, False, Unknown. + kind: + description: Kind is kind of the referent. + maxLength: 253 + minLength: 1 + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: Namespace is the namespace of the referent. This + field is required when scope is set to "Namespace" and ignored + when scope is set to "Cluster". + maxLength: 253 + minLength: 1 + type: string + scope: + default: Cluster + description: Scope represents if the referent is a Cluster or + Namespace scoped resource. This may be set to "Cluster" or "Namespace". enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + - Cluster + - Namespace type: string required: - - lastTransitionTime - - message - - reason - - status - - type + - group + - kind + - name type: object - maxItems: 8 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - type: object - type: object - served: true - storage: true - subresources: - status: {} + required: + - controller + type: object + status: + default: + conditions: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Waiting + status: "False" + type: Admitted + description: Status defines the current state of GatewayClass. + properties: + conditions: + default: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Waiting + status: "False" + type: Admitted + description: "Conditions is the current status from the controller + for this GatewayClass. \n Controllers should prefer to publish conditions + using values of GatewayClassConditionType for the type of each Condition." + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: + \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type + \ // +patchStrategy=merge // +listType=map // +listMapKey=type + \ Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` + \n // other fields }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + type: object + served: true + storage: true + subresources: + status: {} status: acceptedNames: kind: "" @@ -162,408 +216,683 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.4.1 + controller-gen.kubebuilder.io/version: v0.5.0 creationTimestamp: null name: gateways.networking.x-k8s.io spec: group: networking.x-k8s.io names: + categories: + - gateway-api kind: Gateway listKind: GatewayList plural: gateways shortNames: - - gtw + - gtw singular: gateway scope: Namespaced versions: - - additionalPrinterColumns: - - jsonPath: .spec.gatewayClassName - name: Class - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: "Gateway represents an instantiation of a service-traffic handling infrastructure by binding Listeners to a set of IP addresses. \n Implementations should add the `gateway-exists-finalizer.networking.x-k8s.io` finalizer on the associated GatewayClass whenever Gateway(s) is running. This ensures that a GatewayClass associated with a Gateway(s) is not deleted while in use." - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of Gateway. - properties: - addresses: - description: "Addresses requested for this gateway. This is optional and behavior can depend on the GatewayClass. If a value is set in the spec and the requested address is invalid, the GatewayClass MUST indicate this in the associated entry in GatewayStatus.Addresses. \n If no Addresses are specified, the GatewayClass may schedule the Gateway in an implementation-defined manner, assigning an appropriate set of Addresses. \n The GatewayClass MUST bind all Listeners to every GatewayAddress that it assigns to the Gateway. \n Support: Core" - items: - description: GatewayAddress describes an address that can be bound to a Gateway. - properties: - type: - default: IPAddress - description: "Type of the address. \n Support: Extended" - enum: - - IPAddress - - NamedAddress - type: string - value: - description: "Value of the address. The validity of the values will depend on the type and support by the controller. \n Examples: `1.2.3.4`, `128::1`, `my-ip-address`." - maxLength: 253 - minLength: 1 - type: string - required: - - value - type: object - maxItems: 16 - type: array - gatewayClassName: - description: GatewayClassName used for this Gateway. This is the name of a GatewayClass resource. - maxLength: 253 - minLength: 1 - type: string - listeners: - description: "Listeners associated with this Gateway. Listeners define logical endpoints that are bound on this Gateway's addresses. At least one Listener MUST be specified. \n An implementation MAY group Listeners by Port and then collapse each group of Listeners into a single Listener if the implementation determines that the Listeners in the group are \"compatible\". An implementation MAY also group together and collapse compatible Listeners belonging to different Gateways. \n For example, an implementation might consider Listeners to be compatible with each other if all of the following conditions are met: \n 1. Either each Listener within the group specifies the \"HTTP\" Protocol or each Listener within the group specifies either the \"HTTPS\" or \"TLS\" Protocol. \n 2. Each Listener within the group specifies a Hostname that is unique within the group. \n 3. As a special case, one Listener within a group may omit Hostname, in which case this Listener matches when no other Listener matches. \n If the implementation does collapse compatible Listeners, the hostname provided in the incoming client request MUST be matched to a Listener to find the correct set of Routes. The incoming hostname MUST be matched using the Hostname field for each Listener in order of most to least specific. That is, exact matches must be processed before wildcard matches. \n If this field specifies multiple Listeners that have the same Port value but are not compatible, the implementation must raise a \"Conflicted\" condition in the Listener status. \n Support: Core" - items: - description: Listener embodies the concept of a logical endpoint where a Gateway can accept network connections. Each listener in a Gateway must have a unique combination of Hostname, Port, and Protocol. This will be enforced by a validating webhook. - properties: - hostname: - description: "Hostname specifies the virtual hostname to match for protocol types that define this concept. When unspecified, \"\", or `*`, all hostnames are matched. This field can be omitted for protocols that don't require hostname based matching. \n Hostname is the fully qualified domain name of a network host, as defined by RFC 3986. Note the following deviations from the \"host\" part of the URI as defined in the RFC: \n 1. IP literals are not allowed. 2. The `:` delimiter is not respected because ports are not allowed. \n Hostname can be \"precise\" which is a domain name without the terminating dot of a network host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). The wildcard character `*` must appear by itself as the first DNS label and matches only a single label. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - port: - description: "Port is the network port. Multiple listeners may use the same port, subject to the Listener compatibility rules. \n Support: Core" - format: int32 - maximum: 65535 - minimum: 1 - type: integer - protocol: - description: "Protocol specifies the network protocol this listener expects to receive. The GatewayClass MUST apply the Hostname match appropriately for each protocol: \n * For the \"TLS\" protocol, the Hostname match MUST be applied to the [SNI](https://tools.ietf.org/html/rfc6066#section-3) server name offered by the client. * For the \"HTTP\" protocol, the Hostname match MUST be applied to the host portion of the [effective request URI](https://tools.ietf.org/html/rfc7230#section-5.5) or the [:authority pseudo-header](https://tools.ietf.org/html/rfc7540#section-8.1.2.3) * For the \"HTTPS\" protocol, the Hostname match MUST be applied at both the TLS and HTTP protocol layers. \n Support: Core" - type: string - routes: - description: "Routes specifies a schema for associating routes with the Listener using selectors. A Route is a resource capable of servicing a request and allows a cluster operator to expose a cluster resource (i.e. Service) by externally-reachable URL, load-balance traffic and terminate SSL/TLS. Typically, a route is a \"HTTPRoute\" or \"TCPRoute\" in group \"networking.x-k8s.io\", however, an implementation may support other types of resources. \n The Routes selector MUST select a set of objects that are compatible with the application protocol specified in the Protocol field. \n Although a client request may technically match multiple route rules, only one rule may ultimately receive the request. Matching precedence MUST be determined in order of the following criteria: \n * The most specific match. For example, the most specific HTTPRoute match is determined by the longest matching combination of hostname and path. * The oldest Route based on creation timestamp. For example, a Route with a creation timestamp of \"2020-09-08 01:02:03\" is given precedence over a Route with a creation timestamp of \"2020-09-08 01:02:04\". * If everything else is equivalent, the Route appearing first in alphabetical order (namespace/name) should be given precedence. For example, foo/bar is given precedence over foo/baz. \n All valid portions of a Route selected by this field should be supported. Invalid portions of a Route can be ignored (sometimes that will mean the full Route). If a portion of a Route transitions from valid to invalid, support for that portion of the Route should be dropped to ensure consistency. For example, even if a filter specified by a Route is invalid, the rest of the Route should still be supported. \n Support: Core" - properties: - group: - default: networking.x-k8s.io - description: "Group is the group of the route resource to select. Omitting the value or specifying the empty string indicates the networking.x-k8s.io API group. For example, use the following to select an HTTPRoute: \n routes: kind: HTTPRoute \n Otherwise, if an alternative API group is desired, specify the desired group: \n routes: group: acme.io kind: FooRoute \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - kind: - description: "Kind is the kind of the route resource to select. \n Kind MUST correspond to kinds of routes that are compatible with the application protocol specified in the Listener's Protocol field. \n If an implementation does not support or recognize this resource type, it SHOULD set the \"ResolvedRefs\" condition to false for this listener with the \"InvalidRoutesRef\" reason. \n Support: Core" - type: string - namespaces: - default: - from: Same - description: "Namespaces indicates in which namespaces Routes should be selected for this Gateway. This is restricted to the namespace of this Gateway by default. \n Support: Core" - properties: - from: - default: Same - description: "From indicates where Routes will be selected for this Gateway. Possible values are: * All: Routes in all namespaces may be used by this Gateway. * Selector: Routes in namespaces selected by the selector may be used by this Gateway. * Same: Only Routes in the same namespace may be used by this Gateway. \n Support: Core" - enum: - - All - - Selector - - Same - type: string - selector: - description: "Selector must be specified when From is set to \"Selector\". In that case, only Routes in Namespaces matching this Selector will be selected by this Gateway. This field is ignored for other values of \"From\". \n Support: Core" - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - items: - description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. - items: + - additionalPrinterColumns: + - jsonPath: .spec.gatewayClassName + name: Class + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: "Gateway represents an instantiation of a service-traffic handling + infrastructure by binding Listeners to a set of IP addresses. \n Implementations + should add the `gateway-exists-finalizer.networking.x-k8s.io` finalizer + on the associated GatewayClass whenever Gateway(s) is running. This ensures + that a GatewayClass associated with a Gateway(s) is not deleted while in + use." + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of Gateway. + properties: + addresses: + description: "Addresses requested for this gateway. This is optional + and behavior can depend on the GatewayClass. If a value is set in + the spec and the requested address is invalid, the GatewayClass + MUST indicate this in the associated entry in GatewayStatus.Addresses. + \n If no Addresses are specified, the GatewayClass may schedule + the Gateway in an implementation-defined manner, assigning an appropriate + set of Addresses. \n The GatewayClass MUST bind all Listeners to + every GatewayAddress that it assigns to the Gateway. \n Support: + Core" + items: + description: GatewayAddress describes an address that can be bound + to a Gateway. + properties: + type: + default: IPAddress + description: "Type of the address. \n Support: Extended" + enum: + - IPAddress + - NamedAddress + type: string + value: + description: "Value of the address. The validity of the values + will depend on the type and support by the controller. \n + Examples: `1.2.3.4`, `128::1`, `my-ip-address`." + maxLength: 253 + minLength: 1 + type: string + required: + - value + type: object + maxItems: 16 + type: array + gatewayClassName: + description: GatewayClassName used for this Gateway. This is the name + of a GatewayClass resource. + maxLength: 253 + minLength: 1 + type: string + listeners: + description: "Listeners associated with this Gateway. Listeners define + logical endpoints that are bound on this Gateway's addresses. At + least one Listener MUST be specified. \n An implementation MAY group + Listeners by Port and then collapse each group of Listeners into + a single Listener if the implementation determines that the Listeners + in the group are \"compatible\". An implementation MAY also group + together and collapse compatible Listeners belonging to different + Gateways. \n For example, an implementation might consider Listeners + to be compatible with each other if all of the following conditions + are met: \n 1. Either each Listener within the group specifies the + \"HTTP\" Protocol or each Listener within the group specifies + either the \"HTTPS\" or \"TLS\" Protocol. \n 2. Each Listener + within the group specifies a Hostname that is unique within the + group. \n 3. As a special case, one Listener within a group may + omit Hostname, in which case this Listener matches when no other + Listener matches. \n If the implementation does collapse compatible + Listeners, the hostname provided in the incoming client request + MUST be matched to a Listener to find the correct set of Routes. + The incoming hostname MUST be matched using the Hostname field for + each Listener in order of most to least specific. That is, exact + matches must be processed before wildcard matches. \n If this field + specifies multiple Listeners that have the same Port value but are + not compatible, the implementation must raise a \"Conflicted\" condition + in the Listener status. \n Support: Core" + items: + description: Listener embodies the concept of a logical endpoint + where a Gateway can accept network connections. Each listener + in a Gateway must have a unique combination of Hostname, Port, + and Protocol. This will be enforced by a validating webhook. + properties: + hostname: + description: "Hostname specifies the virtual hostname to match + for protocol types that define this concept. When unspecified, + \"\", or `*`, all hostnames are matched. This field can be + omitted for protocols that don't require hostname based matching. + \n Hostname is the fully qualified domain name of a network + host, as defined by RFC 3986. Note the following deviations + from the \"host\" part of the URI as defined in the RFC: \n + 1. IP literals are not allowed. 2. The `:` delimiter is not + respected because ports are not allowed. \n Hostname can be + \"precise\" which is a domain name without the terminating + dot of a network host (e.g. \"foo.example.com\") or \"wildcard\", + which is a domain name prefixed with a single wildcard label + (e.g. `*.example.com`). The wildcard character `*` must appear + by itself as the first DNS label and matches only a single + label. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + port: + description: "Port is the network port. Multiple listeners may + use the same port, subject to the Listener compatibility rules. + \n Support: Core" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + protocol: + description: "Protocol specifies the network protocol this listener + expects to receive. The GatewayClass MUST apply the Hostname + match appropriately for each protocol: \n * For the \"TLS\" + protocol, the Hostname match MUST be applied to the [SNI](https://tools.ietf.org/html/rfc6066#section-3) + \ server name offered by the client. * For the \"HTTP\" protocol, + the Hostname match MUST be applied to the host portion of + the [effective request URI](https://tools.ietf.org/html/rfc7230#section-5.5) + \ or the [:authority pseudo-header](https://tools.ietf.org/html/rfc7540#section-8.1.2.3) + * For the \"HTTPS\" protocol, the Hostname match MUST be applied + at both the TLS and HTTP protocol layers. \n Support: Core" + type: string + routes: + description: "Routes specifies a schema for associating routes + with the Listener using selectors. A Route is a resource capable + of servicing a request and allows a cluster operator to expose + a cluster resource (i.e. Service) by externally-reachable + URL, load-balance traffic and terminate SSL/TLS. Typically, + a route is a \"HTTPRoute\" or \"TCPRoute\" in group \"networking.x-k8s.io\", + however, an implementation may support other types of resources. + \n The Routes selector MUST select a set of objects that are + compatible with the application protocol specified in the + Protocol field. \n Although a client request may technically + match multiple route rules, only one rule may ultimately receive + the request. Matching precedence MUST be determined in order + of the following criteria: \n * The most specific match. For + example, the most specific HTTPRoute match is determined + by the longest matching combination of hostname and path. + * The oldest Route based on creation timestamp. For example, + a Route with a creation timestamp of \"2020-09-08 01:02:03\" + is given precedence over a Route with a creation timestamp + of \"2020-09-08 01:02:04\". * If everything else is equivalent, + the Route appearing first in alphabetical order (namespace/name) + should be given precedence. For example, foo/bar is given + precedence over foo/baz. \n All valid portions of a Route + selected by this field should be supported. Invalid portions + of a Route can be ignored (sometimes that will mean the full + Route). If a portion of a Route transitions from valid to + invalid, support for that portion of the Route should be dropped + to ensure consistency. For example, even if a filter specified + by a Route is invalid, the rest of the Route should still + be supported. \n Support: Core" + properties: + group: + default: networking.x-k8s.io + description: "Group is the group of the route resource to + select. Omitting the value or specifying the empty string + indicates the networking.x-k8s.io API group. For example, + use the following to select an HTTPRoute: \n routes: kind: + HTTPRoute \n Otherwise, if an alternative API group is + desired, specify the desired group: \n routes: group: + acme.io kind: FooRoute \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + kind: + description: "Kind is the kind of the route resource to + select. \n Kind MUST correspond to kinds of routes that + are compatible with the application protocol specified + in the Listener's Protocol field. \n If an implementation + does not support or recognize this resource type, it SHOULD + set the \"ResolvedRefs\" condition to false for this listener + with the \"InvalidRoutesRef\" reason. \n Support: Core" + type: string + namespaces: + default: + from: Same + description: "Namespaces indicates in which namespaces Routes + should be selected for this Gateway. This is restricted + to the namespace of this Gateway by default. \n Support: + Core" + properties: + from: + default: Same + description: "From indicates where Routes will be selected + for this Gateway. Possible values are: * All: Routes + in all namespaces may be used by this Gateway. * Selector: + Routes in namespaces selected by the selector may + be used by this Gateway. * Same: Only Routes in + the same namespace may be used by this Gateway. \n + Support: Core" + enum: + - All + - Selector + - Same + type: string + selector: + description: "Selector must be specified when From is + set to \"Selector\". In that case, only Routes in + Namespaces matching this Selector will be selected + by this Gateway. This field is ignored for other values + of \"From\". \n Support: Core" + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. type: string - type: array - required: + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". The + requirements are ANDed. + type: object + type: object + type: object + selector: + description: "Selector specifies a set of route labels used + for selecting routes to associate with the Gateway. If + this Selector is defined, only routes matching the Selector + are associated with the Gateway. An empty Selector matches + all routes. \n Support: Core" + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be empty. + This array is replaced during a strategic merge + patch. + items: + type: string + type: array + required: - key - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object - type: object - type: object - selector: - description: "Selector specifies a set of route labels used for selecting routes to associate with the Gateway. If this Selector is defined, only routes matching the Selector are associated with the Gateway. An empty Selector matches all routes. \n Support: Core" - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - items: - description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. type: object - type: array - matchLabels: - additionalProperties: + type: object + required: + - kind + type: object + tls: + description: "TLS is the TLS configuration for the Listener. + This field is required if the Protocol field is \"HTTPS\" + or \"TLS\" and ignored otherwise. \n The association of SNIs + to Certificate defined in GatewayTLSConfig is defined based + on the Hostname field for this listener. \n The GatewayClass + MUST use the longest matching SNI out of all available certificates + for any TLS handshake. \n Support: Core" + properties: + certificateRef: + description: "CertificateRef is a reference to a Kubernetes + object that contains a TLS certificate and private key. + This certificate is used to establish a TLS handshake + for requests that match the hostname of the associated + listener. The referenced object MUST reside in the same + namespace as Gateway. \n This field is required when mode + is set to \"Terminate\" (default) and optional otherwise. + \n CertificateRef can reference a standard Kubernetes + resource, i.e. Secret, or an implementation-specific custom + resource. \n Support: Core (Kubernetes Secrets) \n Support: + Implementation-specific (Other resource types)" + properties: + group: + description: Group is the group of the referent. + maxLength: 253 + minLength: 1 type: string - description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - required: - - kind - type: object - tls: - description: "TLS is the TLS configuration for the Listener. This field is required if the Protocol field is \"HTTPS\" or \"TLS\" and ignored otherwise. \n The association of SNIs to Certificate defined in GatewayTLSConfig is defined based on the Hostname field for this listener. \n The GatewayClass MUST use the longest matching SNI out of all available certificates for any TLS handshake. \n Support: Core" - properties: - certificateRef: - description: "CertificateRef is the reference to Kubernetes object that contain a TLS certificate and private key. This certificate MUST be used for TLS handshakes for the domain this GatewayTLSConfig is associated with. \n This field is required when mode is set to \"Terminate\" (default) and optional otherwise. \n If an entry in this list omits or specifies the empty string for both the group and the resource, the resource defaults to \"secrets\". An implementation may support other resources (for example, resource \"mycertificates\" in group \"networking.acme.io\"). \n Support: Core (Kubernetes Secrets) \n Support: Implementation-specific (Other resource types)" + kind: + description: Kind is kind of the referent. + maxLength: 253 + minLength: 1 + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + mode: + default: Terminate + description: "Mode defines the TLS behavior for the TLS + session initiated by the client. There are two possible + modes: - Terminate: The TLS session between the downstream + client and the Gateway is terminated at the Gateway. + This mode requires certificateRef to be set. - Passthrough: + The TLS session is NOT terminated by the Gateway. This + \ implies that the Gateway can't decipher the TLS stream + except for the ClientHello message of the TLS protocol. + \ CertificateRef field is ignored in this mode. \n Support: + Core" + enum: + - Terminate + - Passthrough + type: string + options: + additionalProperties: + type: string + description: "Options are a list of key/value pairs to give + extended options to the provider. \n There variation among + providers as to how ciphersuites are expressed. If there + is a common subset for expressing ciphers then it will + make sense to loft that as a core API construct. \n Support: + Implementation-specific" + type: object + routeOverride: + default: + certificate: Deny + description: "RouteOverride dictates if TLS settings can + be configured via Routes or not. \n CertificateRef must + be defined even if `routeOverride.certificate` is set + to 'Allow' as it will be used as the default certificate + for the listener. \n Support: Core" + properties: + certificate: + default: Deny + description: "Certificate dictates if TLS certificates + can be configured via Routes. If set to 'Allow', a + TLS certificate for a hostname defined in a Route + takes precedence over the certificate defined in Gateway. + \n Support: Core" + enum: + - Allow + - Deny + type: string + type: object + type: object + required: + - port + - protocol + - routes + type: object + maxItems: 64 + minItems: 1 + type: array + required: + - gatewayClassName + - listeners + type: object + status: + default: + conditions: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: NotReconciled + status: "False" + type: Scheduled + description: Status defines the current state of Gateway. + properties: + addresses: + description: "Addresses lists the IP addresses that have actually + been bound to the Gateway. These addresses may differ from the addresses + in the Spec, e.g. if the Gateway automatically assigns an address + from a reserved pool. \n These addresses should all be of type \"IPAddress\"." + items: + description: GatewayAddress describes an address that can be bound + to a Gateway. + properties: + type: + default: IPAddress + description: "Type of the address. \n Support: Extended" + enum: + - IPAddress + - NamedAddress + type: string + value: + description: "Value of the address. The validity of the values + will depend on the type and support by the controller. \n + Examples: `1.2.3.4`, `128::1`, `my-ip-address`." + maxLength: 253 + minLength: 1 + type: string + required: + - value + type: object + maxItems: 16 + type: array + conditions: + default: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: NotReconciled + status: "False" + type: Scheduled + description: "Conditions describe the current conditions of the Gateway. + \n Implementations should prefer to express Gateway conditions using + the `GatewayConditionType` and `GatewayConditionReason` constants + so that operators and tools can converge on a common vocabulary + to describe Gateway state. \n Known condition types are: \n * \"Scheduled\" + * \"Ready\"" + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: + \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type + \ // +patchStrategy=merge // +listType=map // +listMapKey=type + \ Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` + \n // other fields }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + listeners: + description: Listeners provide status for each unique listener port + defined in the Spec. + items: + description: ListenerStatus is the status associated with a Listener. + properties: + conditions: + description: Conditions describe the current condition of this + listener. + items: + description: "Condition contains details for one aspect of + the current state of this API Resource. --- This struct + is intended for direct use as an array at the field path + .status.conditions. For example, type FooStatus struct{ + \ // Represents the observations of a foo's current state. + \ // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type + \ // +patchStrategy=merge // +listType=map // + +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` + \n // other fields }" properties: - group: - description: Group is the group of the referent. - maxLength: 253 - minLength: 1 + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should + be when the underlying condition changed. If that is + not known, then using the time when the API field changed + is acceptable. + format: date-time type: string - kind: - description: Kind is kind of the referent. - maxLength: 253 - minLength: 1 + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 type: string - name: - description: Name is the name of the referent. - maxLength: 253 + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, + if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the + current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier + indicating the reason for the condition's last transition. + Producers of specific condition types may define expected + values and meanings for this field, and whether the + values are considered a guaranteed API. The value should + be a CamelCase string. This field may not be empty. + maxLength: 1024 minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across + resources like Available, but because arbitrary conditions + can be useful (see .node.status.conditions), the ability + to deconflict is important. The regex it matches is + (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - - group - - kind - - name + - lastTransitionTime + - message + - reason + - status + - type type: object - mode: - default: Terminate - description: "Mode defines the TLS behavior for the TLS session initiated by the client. There are two possible modes: - Terminate: The TLS session between the downstream client and the Gateway is terminated at the Gateway. This mode requires certificateRef to be set. - Passthrough: The TLS session is NOT terminated by the Gateway. This implies that the Gateway can't decipher the TLS stream except for the ClientHello message of the TLS protocol. CertificateRef field is ignored in this mode. \n Support: Core" - enum: - - Terminate - - Passthrough - type: string - options: - additionalProperties: - type: string - description: "Options are a list of key/value pairs to give extended options to the provider. \n There variation among providers as to how ciphersuites are expressed. If there is a common subset for expressing ciphers then it will make sense to loft that as a core API construct. \n Support: Implementation-specific" - type: object - routeOverride: - default: - certificate: Deny - description: "RouteOverride dictates if TLS settings can be configured via Routes or not. \n CertificateRef must be defined even if `routeOverride.certificate` is set to 'Allow' as it will be used as the default certificate for the listener. \n Support: Core" - properties: - certificate: - default: Deny - description: "Certificate dictates if TLS certificates can be configured via Routes. If set to 'Allow', a TLS certificate for a hostname defined in a Route takes precedence over the certificate defined in Gateway. \n Support: Core" - enum: - - Allow - - Deny - type: string - type: object - type: object - required: - - port - - protocol - - routes - type: object - maxItems: 64 - minItems: 1 - type: array - required: - - gatewayClassName - - listeners - type: object - status: - default: - conditions: - - lastTransitionTime: "1970-01-01T00:00:00Z" - message: Waiting for controller - reason: NotReconciled - status: "False" - type: Scheduled - description: Status defines the current state of Gateway. - properties: - addresses: - description: "Addresses lists the IP addresses that have actually been bound to the Gateway. These addresses may differ from the addresses in the Spec, e.g. if the Gateway automatically assigns an address from a reserved pool. \n These addresses should all be of type \"IPAddress\"." - items: - description: GatewayAddress describes an address that can be bound to a Gateway. - properties: - type: - default: IPAddress - description: "Type of the address. \n Support: Extended" - enum: - - IPAddress - - NamedAddress - type: string - value: - description: "Value of the address. The validity of the values will depend on the type and support by the controller. \n Examples: `1.2.3.4`, `128::1`, `my-ip-address`." - maxLength: 253 - minLength: 1 - type: string - required: - - value - type: object - maxItems: 16 - type: array - conditions: - default: - - lastTransitionTime: "1970-01-01T00:00:00Z" - message: Waiting for controller - reason: NotReconciled - status: "False" - type: Scheduled - description: "Conditions describe the current conditions of the Gateway. \n Implementations should prefer to express Gateway conditions using the `GatewayConditionType` and `GatewayConditionReason` constants so that operators and tools can converge on a common vocabulary to describe Gateway state. \n Known condition types are: \n * \"Scheduled\" * \"Ready\"" - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - listeners: - description: Listeners provide status for each unique listener port defined in the Spec. - items: - description: ListenerStatus is the status associated with a Listener. - properties: - conditions: - description: Conditions describe the current condition of this listener. - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - hostname: - description: Hostname is the Listener hostname value for which this message is reporting the status. - maxLength: 253 - minLength: 1 - type: string - port: - description: Port is the unique Listener port value for which this message is reporting the status. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - protocol: - description: Protocol is the Listener protocol value for which this message is reporting the status. - type: string - required: - - conditions - - port - - protocol - type: object - maxItems: 64 - type: array - x-kubernetes-list-map-keys: - - port - x-kubernetes-list-type: map - type: object - type: object - served: true - storage: true - subresources: - status: {} + maxItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + hostname: + description: Hostname is the Listener hostname value for which + this message is reporting the status. + maxLength: 253 + minLength: 1 + type: string + port: + description: Port is the unique Listener port value for which + this message is reporting the status. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + protocol: + description: Protocol is the Listener protocol value for which + this message is reporting the status. + type: string + required: + - conditions + - port + - protocol + type: object + maxItems: 64 + type: array + x-kubernetes-list-map-keys: + - port + x-kubernetes-list-type: map + type: object + type: object + served: true + storage: true + subresources: + status: {} status: acceptedNames: kind: "" @@ -576,223 +905,235 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.4.1 + controller-gen.kubebuilder.io/version: v0.5.0 creationTimestamp: null name: httproutes.networking.x-k8s.io spec: group: networking.x-k8s.io names: + categories: + - gateway-api kind: HTTPRoute listKind: HTTPRouteList plural: httproutes singular: httproute scope: Namespaced versions: - - additionalPrinterColumns: - - jsonPath: .spec.hostnames - name: Hostnames - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: HTTPRoute is the Schema for the HTTPRoute resource. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of HTTPRoute. - properties: - gateways: - default: - allow: SameNamespace - description: Gateways defines which Gateways can use this Route. - properties: - allow: - default: SameNamespace - description: 'Allow indicates which Gateways will be allowed to use this route. Possible values are: * All: Gateways in any namespace can use this route. * FromList: Only Gateways specified in GatewayRefs may use this route. * SameNamespace: Only Gateways in the same namespace may use this route.' - enum: - - All - - FromList - - SameNamespace - type: string - gatewayRefs: - description: GatewayRefs must be specified when Allow is set to "FromList". In that case, only Gateways referenced in this list will be allowed to use this route. This field is ignored for other values of "Allow". - items: - description: GatewayReference identifies a Gateway in a specified namespace. - properties: - name: - description: Name is the name of the referent. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: Namespace is the namespace of the referent. - maxLength: 253 - minLength: 1 - type: string - required: - - name - - namespace - type: object - type: array - type: object - hostnames: - description: "Hostnames defines a set of hostname that should match against the HTTP Host header to select a HTTPRoute to process the request. Hostname is the fully qualified domain name of a network host, as defined by RFC 3986. Note the following deviations from the \"host\" part of the URI as defined in the RFC: \n 1. IPs are not allowed. 2. The `:` delimiter is not respected because ports are not allowed. \n Incoming requests are matched against the hostnames before the HTTPRoute rules. If no hostname is specified, traffic is routed based on the HTTPRouteRules. \n Hostname can be \"precise\" which is a domain name without the terminating dot of a network host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). The wildcard character `*` must appear by itself as the first DNS label and matches only a single label. You cannot have a wildcard label by itself (e.g. Host == `*`). Requests will be matched against the Host field in the following order: \n 1. If Host is precise, the request matches this rule if the HTTP Host header is equal to Host. 2. If Host is a wildcard, then the request matches this rule if the HTTP Host header is to equal to the suffix (removing the first label) of the wildcard rule. \n Support: Core" - items: - description: Hostname is used to specify a hostname that should be matched. - maxLength: 253 - minLength: 1 - type: string - maxItems: 16 - type: array - rules: - default: - - matches: - - path: - type: Prefix - value: / - description: Rules are a list of HTTP matchers, filters and actions. - items: - description: HTTPRouteRule defines semantics for matching an HTTP request based on conditions, optionally executing additional processing steps, and forwarding the request to an API object. + - additionalPrinterColumns: + - jsonPath: .spec.hostnames + name: Hostnames + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: HTTPRoute is the Schema for the HTTPRoute resource. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of HTTPRoute. + properties: + gateways: + default: + allow: SameNamespace + description: Gateways defines which Gateways can use this Route. properties: - filters: - description: "Filters define the filters that are applied to requests that match this rule. \n The effects of ordering of multiple behaviors are currently unspecified. This can change in the future based on feedback during the alpha stage. \n Conformance-levels at this level are defined based on the type of filter: \n - ALL core filters MUST be supported by all implementations. - Implementers are encouraged to support extended filters. - Implementation-specific custom filters have no API guarantees across implementations. \n Specifying a core filter multiple times has unspecified or custom conformance. \n Support: Core" + allow: + default: SameNamespace + description: 'Allow indicates which Gateways will be allowed to + use this route. Possible values are: * All: Gateways in any + namespace can use this route. * FromList: Only Gateways specified + in GatewayRefs may use this route. * SameNamespace: Only Gateways + in the same namespace may use this route.' + enum: + - All + - FromList + - SameNamespace + type: string + gatewayRefs: + description: GatewayRefs must be specified when Allow is set to + "FromList". In that case, only Gateways referenced in this list + will be allowed to use this route. This field is ignored for + other values of "Allow". items: - description: 'HTTPRouteFilter defines additional processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express additional processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. TODO(hbagdi): re-render CRDs once controller-tools supports union tags: - https://github.com/kubernetes-sigs/controller-tools/pull/298 - https://github.com/kubernetes-sigs/controller-tools/issues/461' + description: GatewayReference identifies a Gateway in a specified + namespace. properties: - extensionRef: - description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.acme.io\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" - properties: - group: - description: Group is the group of the referent. - maxLength: 253 - minLength: 1 - type: string - kind: - description: Kind is kind of the referent. - maxLength: 253 - minLength: 1 - type: string - name: - description: Name is the name of the referent. - maxLength: 253 - minLength: 1 - type: string - required: - - group - - kind - - name - type: object - requestHeaderModifier: - description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" - properties: - add: - additionalProperties: + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: Namespace is the namespace of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - name + - namespace + type: object + type: array + type: object + hostnames: + description: "Hostnames defines a set of hostname that should match + against the HTTP Host header to select a HTTPRoute to process the + request. Hostname is the fully qualified domain name of a network + host, as defined by RFC 3986. Note the following deviations from + the \"host\" part of the URI as defined in the RFC: \n 1. IPs are + not allowed. 2. The `:` delimiter is not respected because ports + are not allowed. \n Incoming requests are matched against the hostnames + before the HTTPRoute rules. If no hostname is specified, traffic + is routed based on the HTTPRouteRules. \n Hostname can be \"precise\" + which is a domain name without the terminating dot of a network + host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain + name prefixed with a single wildcard label (e.g. `*.example.com`). + The wildcard character `*` must appear by itself as the first DNS + label and matches only a single label. You cannot have a wildcard + label by itself (e.g. Host == `*`). Requests will be matched against + the Host field in the following order: \n 1. If Host is precise, + the request matches this rule if the HTTP Host header is equal + to Host. 2. If Host is a wildcard, then the request matches this + rule if the HTTP Host header is to equal to the suffix (removing + the first label) of the wildcard rule. \n Support: Core" + items: + description: Hostname is used to specify a hostname that should + be matched. + maxLength: 253 + minLength: 1 + type: string + maxItems: 16 + type: array + rules: + default: + - matches: + - path: + type: Prefix + value: / + description: Rules are a list of HTTP matchers, filters and actions. + items: + description: HTTPRouteRule defines semantics for matching an HTTP + request based on conditions, optionally executing additional processing + steps, and forwarding the request to an API object. + properties: + filters: + description: "Filters define the filters that are applied to + requests that match this rule. \n The effects of ordering + of multiple behaviors are currently unspecified. This can + change in the future based on feedback during the alpha stage. + \n Conformance-levels at this level are defined based on the + type of filter: \n - ALL core filters MUST be supported by + all implementations. - Implementers are encouraged to support + extended filters. - Implementation-specific custom filters + have no API guarantees across implementations. \n Specifying + a core filter multiple times has unspecified or custom conformance. + \n Support: Core" + items: + description: 'HTTPRouteFilter defines additional processing + steps that must be completed during the request or response + lifecycle. HTTPRouteFilters are meant as an extension point + to express additional processing that may be done in Gateway + implementations. Some examples include request or response + modification, implementing authentication strategies, rate-limiting, + and traffic shaping. API guarantee/conformance is defined + based on the type of the filter. TODO(hbagdi): re-render + CRDs once controller-tools supports union tags: - https://github.com/kubernetes-sigs/controller-tools/pull/298 + - https://github.com/kubernetes-sigs/controller-tools/issues/461' + properties: + extensionRef: + description: "ExtensionRef is an optional, implementation-specific + extension to the \"filter\" behavior. For example, + resource \"myroutefilter\" in group \"networking.acme.io\"). + ExtensionRef MUST NOT be used for core and extended + filters. \n Support: Implementation-specific" + properties: + group: + description: Group is the group of the referent. + maxLength: 253 + minLength: 1 type: string - description: "Add adds the given header (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: {\"my-header\": \"bar\"} \n Output: GET /foo HTTP/1.1 my-header: foo my-header: bar \n Support: Extended" - type: object - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of RemoveHeader is a list of HTTP header names. Note that the header names are case-insensitive [RFC-2616 4.2]. \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar \n Support: Extended" - items: + kind: + description: Kind is kind of the referent. + maxLength: 253 + minLength: 1 type: string - maxItems: 16 - type: array - set: - additionalProperties: + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 type: string - description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: {\"my-header\": \"bar\"} \n Output: GET /foo HTTP/1.1 my-header: bar \n Support: Extended" - type: object - type: object - requestMirror: - description: "RequestMirror defines a schema for a filter that mirrors requests. \n Support: Extended" - properties: - backendRef: - description: "BackendRef is a local object reference to mirror matched requests to. If both BackendRef and ServiceName are specified, ServiceName will be given precedence. \n If the referent cannot be found, the rule is not included in the route. The controller should raise the \"ResolvedRefs\" condition on the Gateway with the \"DegradedRoutes\" reason. The gateway status for this route should be updated with a condition that describes the error more specifically. \n Support: Custom" - properties: - group: - description: Group is the group of the referent. - maxLength: 253 - minLength: 1 - type: string - kind: - description: Kind is kind of the referent. - maxLength: 253 - minLength: 1 - type: string - name: - description: Name is the name of the referent. - maxLength: 253 - minLength: 1 - type: string - required: + required: - group - kind - name - type: object - port: - description: "Port specifies the destination port number to use for the backend referenced by the ServiceName or BackendRef field. \n If unspecified, the destination port in the request is used when forwarding to a backendRef or serviceName." - format: int32 - maximum: 65535 - minimum: 1 - type: integer - serviceName: - description: "ServiceName refers to the name of the Service to mirror matched requests to. When specified, this takes the place of BackendRef. If both BackendRef and ServiceName are specified, ServiceName will be given precedence. \n If the referent cannot be found, the rule is not included in the route. The controller should raise the \"ResolvedRefs\" condition on the Gateway with the \"DegradedRoutes\" reason. The gateway status for this route should be updated with a condition that describes the error more specifically. \n Support: Core" - maxLength: 253 - type: string - type: object - type: - description: "Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations must support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - Custom: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` should be set to \"ExtensionRef\" for custom filters. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior." - enum: - - RequestHeaderModifier - - RequestMirror - - ExtensionRef - type: string - required: - - type - type: object - maxItems: 16 - type: array - forwardTo: - description: ForwardTo defines the backend(s) where matching requests should be sent. If unspecified, the rule performs no forwarding. If unspecified and no filters are specified that would result in a response being sent, a 503 error code is returned. - items: - description: HTTPRouteForwardTo defines how a HTTPRoute should forward a request. - properties: - backendRef: - description: "BackendRef is a reference to a backend to forward matched requests to. If both BackendRef and ServiceName are specified, ServiceName will be given precedence. \n If the referent cannot be found, the route must be dropped from the Gateway. The controller should raise the \"ResolvedRefs\" condition on the Gateway with the \"DegradedRoutes\" reason. The gateway status for this route should be updated with a condition that describes the error more specifically. \n Support: Custom" - properties: - group: - description: Group is the group of the referent. - maxLength: 253 - minLength: 1 - type: string - kind: - description: Kind is kind of the referent. - maxLength: 253 - minLength: 1 - type: string - name: - description: Name is the name of the referent. - maxLength: 253 - minLength: 1 - type: string - required: - - group - - kind - - name - type: object - filters: - description: "Filters defined at this-level should be executed if and only if the request is being forwarded to the backend defined here. \n Support: Custom (For broader support of filters, use the Filters field in HTTPRouteRule.)" - items: - description: 'HTTPRouteFilter defines additional processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express additional processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. TODO(hbagdi): re-render CRDs once controller-tools supports union tags: - https://github.com/kubernetes-sigs/controller-tools/pull/298 - https://github.com/kubernetes-sigs/controller-tools/issues/461' + type: object + requestHeaderModifier: + description: "RequestHeaderModifier defines a schema for + a filter that modifies request headers. \n Support: + Core" properties: - extensionRef: - description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.acme.io\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" + add: + additionalProperties: + type: string + description: "Add adds the given header (name, value) + to the request before the action. It appends to + any existing values associated with the header name. + \n Input: GET /foo HTTP/1.1 my-header: foo \n + Config: add: {\"my-header\": \"bar\"} \n Output: + \ GET /foo HTTP/1.1 my-header: foo my-header: + bar \n Support: Extended" + type: object + remove: + description: "Remove the given header(s) from the + HTTP request before the action. The value of RemoveHeader + is a list of HTTP header names. Note that the header + names are case-insensitive [RFC-2616 4.2]. \n Input: + \ GET /foo HTTP/1.1 my-header1: foo my-header2: + bar my-header3: baz \n Config: remove: [\"my-header1\", + \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: + bar \n Support: Extended" + items: + type: string + maxItems: 16 + type: array + set: + additionalProperties: + type: string + description: "Set overwrites the request with the + given header (name, value) before the action. \n + Input: GET /foo HTTP/1.1 my-header: foo \n Config: + \ set: {\"my-header\": \"bar\"} \n Output: GET + /foo HTTP/1.1 my-header: bar \n Support: Extended" + type: object + type: object + requestMirror: + description: "RequestMirror defines a schema for a filter + that mirrors requests. \n Support: Extended" + properties: + backendRef: + description: "BackendRef is a local object reference + to mirror matched requests to. If both BackendRef + and ServiceName are specified, ServiceName will + be given precedence. \n If the referent cannot be + found, the rule is not included in the route. The + controller should raise the \"ResolvedRefs\" condition + on the Gateway with the \"DegradedRoutes\" reason. + The gateway status for this route should be updated + with a condition that describes the error more specifically. + \n Support: Custom" properties: group: description: Group is the group of the referent. @@ -810,327 +1151,763 @@ spec: minLength: 1 type: string required: - - group - - kind - - name + - group + - kind + - name type: object - requestHeaderModifier: - description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" - properties: - add: - additionalProperties: + port: + description: "Port specifies the destination port + number to use for the backend referenced by the + ServiceName or BackendRef field. \n If unspecified, + the destination port in the request is used when + forwarding to a backendRef or serviceName." + format: int32 + maximum: 65535 + minimum: 1 + type: integer + serviceName: + description: "ServiceName refers to the name of the + Service to mirror matched requests to. When specified, + this takes the place of BackendRef. If both BackendRef + and ServiceName are specified, ServiceName will + be given precedence. \n If the referent cannot be + found, the rule is not included in the route. The + controller should raise the \"ResolvedRefs\" condition + on the Gateway with the \"DegradedRoutes\" reason. + The gateway status for this route should be updated + with a condition that describes the error more specifically. + \n Support: Core" + maxLength: 253 + type: string + type: object + type: + description: "Type identifies the type of filter to apply. + As with other API fields, types are classified into + three conformance levels: \n - Core: Filter types and + their corresponding configuration defined by \"Support: + Core\" in this package, e.g. \"RequestHeaderModifier\". + All implementations must support core filters. \n + - Extended: Filter types and their corresponding configuration + defined by \"Support: Extended\" in this package, + e.g. \"RequestMirror\". Implementers are encouraged + to support extended filters. \n - Custom: Filters that + are defined and supported by specific vendors. In + the future, filters showing convergence in behavior + across multiple implementations will be considered + for inclusion in extended or core conformance levels. + Filter-specific configuration for such filters is + specified using the ExtensionRef field. `Type` should + be set to \"ExtensionRef\" for custom filters. \n + Implementers are encouraged to define custom implementation + types to extend the core API with implementation-specific + behavior." + enum: + - RequestHeaderModifier + - RequestMirror + - ExtensionRef + type: string + required: + - type + type: object + maxItems: 16 + type: array + forwardTo: + description: ForwardTo defines the backend(s) where matching + requests should be sent. If unspecified, the rule performs + no forwarding. If unspecified and no filters are specified + that would result in a response being sent, a 503 error code + is returned. + items: + description: HTTPRouteForwardTo defines how a HTTPRoute should + forward a request. + properties: + backendRef: + description: "BackendRef is a reference to a backend to + forward matched requests to. If both BackendRef and + ServiceName are specified, ServiceName will be given + precedence. \n If the referent cannot be found, the + route must be dropped from the Gateway. The controller + should raise the \"ResolvedRefs\" condition on the Gateway + with the \"DegradedRoutes\" reason. The gateway status + for this route should be updated with a condition that + describes the error more specifically. \n Support: Custom" + properties: + group: + description: Group is the group of the referent. + maxLength: 253 + minLength: 1 + type: string + kind: + description: Kind is kind of the referent. + maxLength: 253 + minLength: 1 + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + filters: + description: "Filters defined at this-level should be + executed if and only if the request is being forwarded + to the backend defined here. \n Support: Custom (For + broader support of filters, use the Filters field in + HTTPRouteRule.)" + items: + description: 'HTTPRouteFilter defines additional processing + steps that must be completed during the request or + response lifecycle. HTTPRouteFilters are meant as + an extension point to express additional processing + that may be done in Gateway implementations. Some + examples include request or response modification, + implementing authentication strategies, rate-limiting, + and traffic shaping. API guarantee/conformance is + defined based on the type of the filter. TODO(hbagdi): + re-render CRDs once controller-tools supports union + tags: - https://github.com/kubernetes-sigs/controller-tools/pull/298 + - https://github.com/kubernetes-sigs/controller-tools/issues/461' + properties: + extensionRef: + description: "ExtensionRef is an optional, implementation-specific + extension to the \"filter\" behavior. For example, + resource \"myroutefilter\" in group \"networking.acme.io\"). + ExtensionRef MUST NOT be used for core and extended + filters. \n Support: Implementation-specific" + properties: + group: + description: Group is the group of the referent. + maxLength: 253 + minLength: 1 type: string - description: "Add adds the given header (name, value) to the request before the action. It appends to any existing values associated with the header name. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: add: {\"my-header\": \"bar\"} \n Output: GET /foo HTTP/1.1 my-header: foo my-header: bar \n Support: Extended" - type: object - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of RemoveHeader is a list of HTTP header names. Note that the header names are case-insensitive [RFC-2616 4.2]. \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz \n Config: remove: [\"my-header1\", \"my-header3\"] \n Output: GET /foo HTTP/1.1 my-header2: bar \n Support: Extended" - items: + kind: + description: Kind is kind of the referent. + maxLength: 253 + minLength: 1 type: string - maxItems: 16 - type: array - set: - additionalProperties: + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 type: string - description: "Set overwrites the request with the given header (name, value) before the action. \n Input: GET /foo HTTP/1.1 my-header: foo \n Config: set: {\"my-header\": \"bar\"} \n Output: GET /foo HTTP/1.1 my-header: bar \n Support: Extended" - type: object - type: object - requestMirror: - description: "RequestMirror defines a schema for a filter that mirrors requests. \n Support: Extended" - properties: - backendRef: - description: "BackendRef is a local object reference to mirror matched requests to. If both BackendRef and ServiceName are specified, ServiceName will be given precedence. \n If the referent cannot be found, the rule is not included in the route. The controller should raise the \"ResolvedRefs\" condition on the Gateway with the \"DegradedRoutes\" reason. The gateway status for this route should be updated with a condition that describes the error more specifically. \n Support: Custom" - properties: - group: - description: Group is the group of the referent. - maxLength: 253 - minLength: 1 - type: string - kind: - description: Kind is kind of the referent. - maxLength: 253 - minLength: 1 - type: string - name: - description: Name is the name of the referent. - maxLength: 253 - minLength: 1 - type: string - required: + required: - group - kind - name - type: object - port: - description: "Port specifies the destination port number to use for the backend referenced by the ServiceName or BackendRef field. \n If unspecified, the destination port in the request is used when forwarding to a backendRef or serviceName." - format: int32 - maximum: 65535 - minimum: 1 - type: integer - serviceName: - description: "ServiceName refers to the name of the Service to mirror matched requests to. When specified, this takes the place of BackendRef. If both BackendRef and ServiceName are specified, ServiceName will be given precedence. \n If the referent cannot be found, the rule is not included in the route. The controller should raise the \"ResolvedRefs\" condition on the Gateway with the \"DegradedRoutes\" reason. The gateway status for this route should be updated with a condition that describes the error more specifically. \n Support: Core" - maxLength: 253 - type: string - type: object - type: - description: "Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: \n - Core: Filter types and their corresponding configuration defined by \"Support: Core\" in this package, e.g. \"RequestHeaderModifier\". All implementations must support core filters. \n - Extended: Filter types and their corresponding configuration defined by \"Support: Extended\" in this package, e.g. \"RequestMirror\". Implementers are encouraged to support extended filters. \n - Custom: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` should be set to \"ExtensionRef\" for custom filters. \n Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior." - enum: - - RequestHeaderModifier - - RequestMirror - - ExtensionRef - type: string - required: - - type - type: object - maxItems: 16 - type: array - port: - description: "Port specifies the destination port number to use for the backend referenced by the ServiceName or BackendRef field. If unspecified, the destination port in the request is used when forwarding to a backendRef or serviceName. \n Support: Core" - format: int32 - maximum: 65535 - minimum: 1 - type: integer - serviceName: - description: "ServiceName refers to the name of the Service to forward matched requests to. When specified, this takes the place of BackendRef. If both BackendRef and ServiceName are specified, ServiceName will be given precedence. \n If the referent cannot be found, the route must be dropped from the Gateway. The controller should raise the \"ResolvedRefs\" condition on the Gateway with the \"DegradedRoutes\" reason. The gateway status for this route should be updated with a condition that describes the error more specifically. \n The protocol to use should be specified with the AppProtocol field on Service resources. This field was introduced in Kubernetes 1.18. If using an earlier version of Kubernetes, a `networking.x-k8s.io/app-protocol` annotation on the BackendPolicy resource may be used to define the protocol. If the AppProtocol field is available, this annotation should not be used. The AppProtocol field, when populated, takes precedence over the annotation in the BackendPolicy resource. For custom backends, it is encouraged to add a semantically-equivalent field in the Custom Resource Definition. \n Support: Core" - maxLength: 253 - type: string - weight: - default: 1 - description: "Weight specifies the proportion of HTTP requests forwarded to the backend referenced by the ServiceName or BackendRef field. This is computed as weight/(sum of all weights in this ForwardTo list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support: Core" - format: int32 - maximum: 1000000 - minimum: 0 - type: integer - type: object - maxItems: 16 - type: array - matches: - default: - - path: - type: Prefix - value: / - description: "Matches define conditions used for matching the rule against incoming HTTP requests. Each match is independent, i.e. this rule will be matched if **any** one of the matches is satisfied. \n For example, take the following matches configuration: \n ``` matches: - path: value: \"/foo\" headers: values: version: \"2\" - path: value: \"/v2/foo\" ``` \n For a request to match against this rule, a request should satisfy EITHER of the two conditions: \n - path prefixed with `/foo` AND contains the header `version: \"2\"` - path prefix of `/v2/foo` \n See the documentation for HTTPRouteMatch on how to specify multiple match conditions that should be ANDed together. \n If no matches are specified, the default is a prefix path match on \"/\", which has the effect of matching every HTTP request. \n A client request may match multiple HTTP route rules. Matching precedence MUST be determined in order of the following criteria, continuing on ties: \n * The longest matching hostname. * The longest matching path. * The largest number of header matches * The oldest Route based on creation timestamp. For example, a Route with a creation timestamp of \"2020-09-08 01:02:03\" is given precedence over a Route with a creation timestamp of \"2020-09-08 01:02:04\". * The Route appearing first in alphabetical order (namespace/name) for example, foo/bar is given precedence over foo/baz." - items: - description: "HTTPRouteMatch defines the predicate used to match requests to a given action. Multiple match types are ANDed together, i.e. the match will evaluate to true only if all conditions are satisfied. \n For example, the match below will match a HTTP request only if its path starts with `/foo` AND it contains the `version: \"1\"` header: \n ``` match: path: value: \"/foo\" headers: values: version: \"1\" ```" - properties: - extensionRef: - description: "ExtensionRef is an optional, implementation-specific extension to the \"match\" behavior. For example, resource \"myroutematcher\" in group \"networking.acme.io\". If the referent cannot be found, the rule is not included in the route. The controller should raise the \"ResolvedRefs\" condition on the Gateway with the \"DegradedRoutes\" reason. The gateway status for this route should be updated with a condition that describes the error more specifically. \n Support: Custom" - properties: - group: - description: Group is the group of the referent. - maxLength: 253 - minLength: 1 - type: string - kind: - description: Kind is kind of the referent. - maxLength: 253 - minLength: 1 - type: string - name: - description: Name is the name of the referent. - maxLength: 253 - minLength: 1 - type: string - required: - - group - - kind - - name - type: object - headers: - description: Headers specifies a HTTP request header matcher. - properties: - type: - default: Exact - description: "Type specifies how to match against the value of the header. \n Support: Core (Exact) \n Support: Custom (RegularExpression, ImplementationSpecific) \n Since RegularExpression PathType has custom conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect. \n HTTP Header name matching MUST be case-insensitive (RFC 2616 - section 4.2)." - enum: - - Exact - - RegularExpression - - ImplementationSpecific - type: string - values: - additionalProperties: - type: string - description: "Values is a map of HTTP Headers to be matched. It MUST contain at least one entry. \n The HTTP header field name to match is the map key, and the value of the HTTP header is the map value. HTTP header field name matching MUST be case-insensitive. \n Multiple match values are ANDed together, meaning, a request must match all the specified headers to select the route." + type: object + requestHeaderModifier: + description: "RequestHeaderModifier defines a schema + for a filter that modifies request headers. \n + Support: Core" + properties: + add: + additionalProperties: + type: string + description: "Add adds the given header (name, + value) to the request before the action. It + appends to any existing values associated + with the header name. \n Input: GET /foo + HTTP/1.1 my-header: foo \n Config: add: + {\"my-header\": \"bar\"} \n Output: GET + /foo HTTP/1.1 my-header: foo my-header: + bar \n Support: Extended" + type: object + remove: + description: "Remove the given header(s) from + the HTTP request before the action. The value + of RemoveHeader is a list of HTTP header names. + Note that the header names are case-insensitive + [RFC-2616 4.2]. \n Input: GET /foo HTTP/1.1 + \ my-header1: foo my-header2: bar my-header3: + baz \n Config: remove: [\"my-header1\", + \"my-header3\"] \n Output: GET /foo HTTP/1.1 + \ my-header2: bar \n Support: Extended" + items: + type: string + maxItems: 16 + type: array + set: + additionalProperties: + type: string + description: "Set overwrites the request with + the given header (name, value) before the + action. \n Input: GET /foo HTTP/1.1 my-header: + foo \n Config: set: {\"my-header\": \"bar\"} + \n Output: GET /foo HTTP/1.1 my-header: + bar \n Support: Extended" + type: object + type: object + requestMirror: + description: "RequestMirror defines a schema for + a filter that mirrors requests. \n Support: Extended" + properties: + backendRef: + description: "BackendRef is a local object reference + to mirror matched requests to. If both BackendRef + and ServiceName are specified, ServiceName + will be given precedence. \n If the referent + cannot be found, the rule is not included + in the route. The controller should raise + the \"ResolvedRefs\" condition on the Gateway + with the \"DegradedRoutes\" reason. The gateway + status for this route should be updated with + a condition that describes the error more + specifically. \n Support: Custom" + properties: + group: + description: Group is the group of the referent. + maxLength: 253 + minLength: 1 + type: string + kind: + description: Kind is kind of the referent. + maxLength: 253 + minLength: 1 + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + port: + description: "Port specifies the destination + port number to use for the backend referenced + by the ServiceName or BackendRef field. \n + If unspecified, the destination port in the + request is used when forwarding to a backendRef + or serviceName." + format: int32 + maximum: 65535 + minimum: 1 + type: integer + serviceName: + description: "ServiceName refers to the name + of the Service to mirror matched requests + to. When specified, this takes the place of + BackendRef. If both BackendRef and ServiceName + are specified, ServiceName will be given precedence. + \n If the referent cannot be found, the rule + is not included in the route. The controller + should raise the \"ResolvedRefs\" condition + on the Gateway with the \"DegradedRoutes\" + reason. The gateway status for this route + should be updated with a condition that describes + the error more specifically. \n Support: Core" + maxLength: 253 + type: string + type: object + type: + description: "Type identifies the type of filter + to apply. As with other API fields, types are + classified into three conformance levels: \n - + Core: Filter types and their corresponding configuration + defined by \"Support: Core\" in this package, + e.g. \"RequestHeaderModifier\". All implementations + must support core filters. \n - Extended: Filter + types and their corresponding configuration defined + by \"Support: Extended\" in this package, e.g. + \"RequestMirror\". Implementers are encouraged + to support extended filters. \n - Custom: Filters + that are defined and supported by specific vendors. + \ In the future, filters showing convergence + in behavior across multiple implementations + will be considered for inclusion in extended or + core conformance levels. Filter-specific configuration + for such filters is specified using the ExtensionRef + field. `Type` should be set to \"ExtensionRef\" + for custom filters. \n Implementers are encouraged + to define custom implementation types to extend + the core API with implementation-specific behavior." + enum: + - RequestHeaderModifier + - RequestMirror + - ExtensionRef + type: string + required: + - type type: object - required: - - values - type: object - path: - default: + maxItems: 16 + type: array + port: + description: "Port specifies the destination port number + to use for the backend referenced by the ServiceName + or BackendRef field. If unspecified, the destination + port in the request is used when forwarding to a backendRef + or serviceName. \n Support: Core" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + serviceName: + description: "ServiceName refers to the name of the Service + to forward matched requests to. When specified, this + takes the place of BackendRef. If both BackendRef and + ServiceName are specified, ServiceName will be given + precedence. \n If the referent cannot be found, the + route must be dropped from the Gateway. The controller + should raise the \"ResolvedRefs\" condition on the Gateway + with the \"DegradedRoutes\" reason. The gateway status + for this route should be updated with a condition that + describes the error more specifically. \n The protocol + to use should be specified with the AppProtocol field + on Service resources. This field was introduced in Kubernetes + 1.18. If using an earlier version of Kubernetes, a `networking.x-k8s.io/app-protocol` + annotation on the BackendPolicy resource may be used + to define the protocol. If the AppProtocol field is + available, this annotation should not be used. The AppProtocol + field, when populated, takes precedence over the annotation + in the BackendPolicy resource. For custom backends, + it is encouraged to add a semantically-equivalent field + in the Custom Resource Definition. \n Support: Core" + maxLength: 253 + type: string + weight: + default: 1 + description: "Weight specifies the proportion of HTTP + requests forwarded to the backend referenced by the + ServiceName or BackendRef field. This is computed as + weight/(sum of all weights in this ForwardTo list). + For non-zero values, there may be some epsilon from + the exact proportion defined here depending on the precision + an implementation supports. Weight is not a percentage + and the sum of weights does not need to equal 100. \n + If only one backend is specified and it has a weight + greater than 0, 100% of the traffic is forwarded to + that backend. If weight is set to 0, no traffic should + be forwarded for this entry. If unspecified, weight + defaults to 1. \n Support: Core" + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + type: object + maxItems: 16 + type: array + matches: + default: + - path: type: Prefix value: / - description: Path specifies a HTTP request path matcher. If this field is not specified, a default prefix match on the "/" path is provided. - properties: - type: - default: Prefix - description: "Type specifies how to match against the path Value. \n Support: Core (Exact, Prefix) \n Support: Custom (RegularExpression, ImplementationSpecific) \n Since RegularExpression PathType has custom conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect." - enum: - - Exact - - Prefix - - RegularExpression - - ImplementationSpecific - type: string - value: - description: Value of the HTTP path to match against. - minLength: 1 - type: string - required: - - value - type: object - type: object - maxItems: 8 - type: array - type: object - maxItems: 16 - type: array - tls: - description: "TLS defines the TLS certificate to use for Hostnames defined in this Route. This configuration only takes effect if the AllowRouteOverride field is set to true in the associated Gateway resource. \n Collisions can happen if multiple HTTPRoutes define a TLS certificate for the same hostname. In such a case, conflict resolution guiding principles apply, specifically, if hostnames are same and two different certificates are specified then the certificate in the oldest resource wins. \n Please note that HTTP Route-selection takes place after the TLS Handshake (ClientHello). Due to this, TLS certificate defined here will take precedence even if the request has the potential to match multiple routes (in case multiple HTTPRoutes share the same hostname). \n Support: Core" - properties: - certificateRef: - description: "CertificateRef refers to a Kubernetes object that contains a TLS certificate and private key. This certificate MUST be used for TLS handshakes for the domain this RouteTLSConfig is associated with. If an entry in this list omits or specifies the empty string for both the group and kind, the resource defaults to \"secrets\". An implementation may support other resources (for example, resource \"mycertificates\" in group \"networking.acme.io\"). \n Support: Core (Kubernetes Secrets) \n Support: Implementation-specific (Other resource types)" - properties: - group: - description: Group is the group of the referent. - maxLength: 253 - minLength: 1 - type: string - kind: - description: Kind is kind of the referent. - maxLength: 253 - minLength: 1 - type: string - name: - description: Name is the name of the referent. - maxLength: 253 - minLength: 1 - type: string - required: - - group - - kind - - name + description: "Matches define conditions used for matching the + rule against incoming HTTP requests. Each match is independent, + i.e. this rule will be matched if **any** one of the matches + is satisfied. \n For example, take the following matches configuration: + \n ``` matches: - path: value: \"/foo\" headers: values: + \ version: \"2\" - path: value: \"/v2/foo\" ``` \n + For a request to match against this rule, a request should + satisfy EITHER of the two conditions: \n - path prefixed with + `/foo` AND contains the header `version: \"2\"` - path prefix + of `/v2/foo` \n See the documentation for HTTPRouteMatch on + how to specify multiple match conditions that should be ANDed + together. \n If no matches are specified, the default is a + prefix path match on \"/\", which has the effect of matching + every HTTP request. \n Each client request MUST map to a maximum + of one route rule. If a request matches multiple rules, matching + precedence MUST be determined in order of the following criteria, + continuing on ties: \n * The longest matching hostname. * + The longest matching path. * The largest number of header + matches. \n If ties still exist across multiple Routes, matching + precedence MUST be determined in order of the following criteria, + continuing on ties: \n * The oldest Route based on creation + timestamp. For example, a Route with a creation timestamp + of \"2020-09-08 01:02:03\" is given precedence over a Route + with a creation timestamp of \"2020-09-08 01:02:04\". * The + Route appearing first in alphabetical order by \"/\". + For example, foo/bar is given precedence over foo/baz. \n + If ties still exist within the Route that has been given precedence, + matching precedence MUST be granted to the first matching + rule meeting the above criteria." + items: + description: "HTTPRouteMatch defines the predicate used to + match requests to a given action. Multiple match types are + ANDed together, i.e. the match will evaluate to true only + if all conditions are satisfied. \n For example, the match + below will match a HTTP request only if its path starts + with `/foo` AND it contains the `version: \"1\"` header: + \n ``` match: path: value: \"/foo\" headers: values: + \ version: \"1\" ```" + properties: + extensionRef: + description: "ExtensionRef is an optional, implementation-specific + extension to the \"match\" behavior. For example, resource + \"myroutematcher\" in group \"networking.acme.io\". + If the referent cannot be found, the rule is not included + in the route. The controller should raise the \"ResolvedRefs\" + condition on the Gateway with the \"DegradedRoutes\" + reason. The gateway status for this route should be + updated with a condition that describes the error more + specifically. \n Support: Custom" + properties: + group: + description: Group is the group of the referent. + maxLength: 253 + minLength: 1 + type: string + kind: + description: Kind is kind of the referent. + maxLength: 253 + minLength: 1 + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + headers: + description: Headers specifies a HTTP request header matcher. + properties: + type: + default: Exact + description: "Type specifies how to match against + the value of the header. \n Support: Core (Exact) + \n Support: Custom (RegularExpression, ImplementationSpecific) + \n Since RegularExpression PathType has custom conformance, + implementations can support POSIX, PCRE or any other + dialects of regular expressions. Please read the + implementation's documentation to determine the + supported dialect. \n HTTP Header name matching + MUST be case-insensitive (RFC 2616 - section 4.2)." + enum: + - Exact + - RegularExpression + - ImplementationSpecific + type: string + values: + additionalProperties: + type: string + description: "Values is a map of HTTP Headers to be + matched. It MUST contain at least one entry. \n + The HTTP header field name to match is the map key, + and the value of the HTTP header is the map value. + HTTP header field name matching MUST be case-insensitive. + \n Multiple match values are ANDed together, meaning, + a request must match all the specified headers to + select the route." + type: object + required: + - values + type: object + path: + default: + type: Prefix + value: / + description: Path specifies a HTTP request path matcher. + If this field is not specified, a default prefix match + on the "/" path is provided. + properties: + type: + default: Prefix + description: "Type specifies how to match against + the path Value. \n Support: Core (Exact, Prefix) + \n Support: Custom (RegularExpression, ImplementationSpecific) + \n Since RegularExpression PathType has custom conformance, + implementations can support POSIX, PCRE or any other + dialects of regular expressions. Please read the + implementation's documentation to determine the + supported dialect." + enum: + - Exact + - Prefix + - RegularExpression + - ImplementationSpecific + type: string + value: + default: / + description: Value of the HTTP path to match against. + type: string + type: object + queryParams: + description: QueryParams specifies a HTTP query parameter + matcher. + properties: + type: + default: Exact + description: "Type specifies how to match against + the value of the query parameter. \n Support: Extended + (Exact) \n Support: Custom (RegularExpression, ImplementationSpecific) + \n Since RegularExpression QueryParamMatchType has + custom conformance, implementations can support + POSIX, PCRE or any other dialects of regular expressions. + Please read the implementation's documentation to + determine the supported dialect." + enum: + - Exact + - RegularExpression + - ImplementationSpecific + type: string + values: + additionalProperties: + type: string + description: "Values is a map of HTTP query parameters + to be matched. It MUST contain at least one entry. + \n The query parameter name to match is the map + key, and the value of the query parameter is the + map value. \n Multiple match values are ANDed together, + meaning, a request must match all the specified + query parameters to select the route. \n HTTP query + parameter matching MUST be case-sensitive for both + keys and values. (See https://tools.ietf.org/html/rfc7230#section-2.7.3). + \n Note that the query parameter key MUST always + be an exact match by string comparison." + type: object + required: + - values + type: object + type: object + maxItems: 8 + type: array type: object - required: - - certificateRef - type: object - type: object - status: - description: Status defines the current state of HTTPRoute. - properties: - gateways: - description: "Gateways is a list of Gateways that are associated with the route, and the status of the route with respect to each Gateway. When a Gateway selects this route, the controller that manages the Gateway must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route is modified. \n A maximum of 100 Gateways will be represented in this list. If this list is full, there may be additional Gateways using this Route that are not included in the list. An empty list means the route has not been admitted by any Gateway." - items: - description: RouteGatewayStatus describes the status of a route with respect to an associated Gateway. + maxItems: 16 + type: array + tls: + description: "TLS defines the TLS certificate to use for Hostnames + defined in this Route. This configuration only takes effect if the + AllowRouteOverride field is set to true in the associated Gateway + resource. \n Collisions can happen if multiple HTTPRoutes define + a TLS certificate for the same hostname. In such a case, conflict + resolution guiding principles apply, specifically, if hostnames + are same and two different certificates are specified then the certificate + in the oldest resource wins. \n Please note that HTTP Route-selection + takes place after the TLS Handshake (ClientHello). Due to this, + TLS certificate defined here will take precedence even if the request + has the potential to match multiple routes (in case multiple HTTPRoutes + share the same hostname). \n Support: Core" properties: - conditions: - description: Conditions describes the status of the route with respect to the Gateway. The "Admitted" condition must always be specified by controllers to indicate whether the route has been admitted or rejected by the Gateway, and why. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - gatewayRef: - description: GatewayRef is a reference to a Gateway object that is associated with the route. + certificateRef: + description: "CertificateRef is a reference to a Kubernetes object + that contains a TLS certificate and private key. This certificate + is used to establish a TLS handshake for requests that match + the hostname of the associated HTTPRoute. The referenced object + MUST reside in the same namespace as HTTPRoute. \n This field + is required when the TLS configuration mode of the associated + Gateway listener is set to \"Passthrough\". \n CertificateRef + can reference a standard Kubernetes resource, i.e. Secret, or + an implementation-specific custom resource. \n Support: Core + (Kubernetes Secrets) \n Support: Implementation-specific (Other + resource types)" properties: + group: + description: Group is the group of the referent. + maxLength: 253 + minLength: 1 + type: string + kind: + description: Kind is kind of the referent. + maxLength: 253 + minLength: 1 + type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string - namespace: - description: Namespace is the namespace of the referent. - maxLength: 253 - minLength: 1 - type: string required: - - name - - namespace + - group + - kind + - name type: object required: - - gatewayRef + - certificateRef type: object - maxItems: 100 - type: array - required: - - gateways - type: object - type: object - served: true - storage: true - subresources: - status: {} + type: object + status: + description: Status defines the current state of HTTPRoute. + properties: + gateways: + description: "Gateways is a list of Gateways that are associated with + the route, and the status of the route with respect to each Gateway. + When a Gateway selects this route, the controller that manages the + Gateway must add an entry to this list when the controller first + sees the route and should update the entry as appropriate when the + route is modified. \n A maximum of 100 Gateways will be represented + in this list. If this list is full, there may be additional Gateways + using this Route that are not included in the list. An empty list + means the route has not been admitted by any Gateway." + items: + description: RouteGatewayStatus describes the status of a route + with respect to an associated Gateway. + properties: + conditions: + description: Conditions describes the status of the route with + respect to the Gateway. The "Admitted" condition must always + be specified by controllers to indicate whether the route + has been admitted or rejected by the Gateway, and why. Note + that the route's availability is also subject to the Gateway's + own status conditions and listener status. + items: + description: "Condition contains details for one aspect of + the current state of this API Resource. --- This struct + is intended for direct use as an array at the field path + .status.conditions. For example, type FooStatus struct{ + \ // Represents the observations of a foo's current state. + \ // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type + \ // +patchStrategy=merge // +listType=map // + +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` + \n // other fields }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should + be when the underlying condition changed. If that is + not known, then using the time when the API field changed + is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, + if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the + current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier + indicating the reason for the condition's last transition. + Producers of specific condition types may define expected + values and meanings for this field, and whether the + values are considered a guaranteed API. The value should + be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across + resources like Available, but because arbitrary conditions + can be useful (see .node.status.conditions), the ability + to deconflict is important. The regex it matches is + (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + gatewayRef: + description: GatewayRef is a reference to a Gateway object that + is associated with the route. + properties: + controller: + description: "Controller is a domain/path string that indicates + the controller implementing the Gateway. This corresponds + with the controller field on GatewayClass. \n Example: + \"acme.io/gateway-controller\". \n The format of this + field is DOMAIN \"/\" PATH, where DOMAIN and PATH are + valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names)." + maxLength: 253 + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: Namespace is the namespace of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - name + - namespace + type: object + required: + - gatewayRef + type: object + maxItems: 100 + type: array + required: + - gateways + type: object + type: object + served: true + storage: true + subresources: + status: {} status: acceptedNames: kind: "" plural: "" conditions: [] storedVersions: [] + --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.4.1 + controller-gen.kubebuilder.io/version: v0.5.0 creationTimestamp: null name: tcproutes.networking.x-k8s.io spec: group: networking.x-k8s.io names: + categories: + - gateway-api kind: TCPRoute listKind: TCPRouteList plural: tcproutes singular: tcproute scope: Namespaced versions: - - name: v1alpha1 + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 schema: openAPIV3Schema: description: TCPRoute is the Schema for the TCPRoute resource. properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object @@ -1144,16 +1921,24 @@ spec: properties: allow: default: SameNamespace - description: 'Allow indicates which Gateways will be allowed to use this route. Possible values are: * All: Gateways in any namespace can use this route. * FromList: Only Gateways specified in GatewayRefs may use this route. * SameNamespace: Only Gateways in the same namespace may use this route.' + description: 'Allow indicates which Gateways will be allowed to + use this route. Possible values are: * All: Gateways in any + namespace can use this route. * FromList: Only Gateways specified + in GatewayRefs may use this route. * SameNamespace: Only Gateways + in the same namespace may use this route.' enum: - All - FromList - SameNamespace type: string gatewayRefs: - description: GatewayRefs must be specified when Allow is set to "FromList". In that case, only Gateways referenced in this list will be allowed to use this route. This field is ignored for other values of "Allow". + description: GatewayRefs must be specified when Allow is set to + "FromList". In that case, only Gateways referenced in this list + will be allowed to use this route. This field is ignored for + other values of "Allow". items: - description: GatewayReference identifies a Gateway in a specified namespace. + description: GatewayReference identifies a Gateway in a specified + namespace. properties: name: description: Name is the name of the referent. @@ -1177,12 +1962,22 @@ spec: description: TCPRouteRule is the configuration for a given rule. properties: forwardTo: - description: ForwardTo defines the backend(s) where matching requests should be sent. + description: ForwardTo defines the backend(s) where matching + requests should be sent. items: - description: RouteForwardTo defines how a Route should forward a request. + description: RouteForwardTo defines how a Route should forward + a request. properties: backendRef: - description: "BackendRef is a reference to a backend to forward matched requests to. If both BackendRef and ServiceName are specified, ServiceName will be given precedence. \n If the referent cannot be found, the rule is not included in the route. The controller should raise the \"ResolvedRefs\" condition on the Gateway with the \"DegradedRoutes\" reason. The gateway status for this route should be updated with a condition that describes the error more specifically. \n Support: Custom" + description: "BackendRef is a reference to a backend to + forward matched requests to. If both BackendRef and + ServiceName are specified, ServiceName will be given + precedence. \n If the referent cannot be found, the + rule is not included in the route. The controller should + raise the \"ResolvedRefs\" condition on the Gateway + with the \"DegradedRoutes\" reason. The gateway status + for this route should be updated with a condition that + describes the error more specifically. \n Support: Custom" properties: group: description: Group is the group of the referent. @@ -1205,18 +2000,53 @@ spec: - name type: object port: - description: "Port specifies the destination port number to use for the backend referenced by the ServiceName or BackendRef field. If unspecified, the destination port in the request is used when forwarding to a backendRef or serviceName. \n Support: Core" + description: "Port specifies the destination port number + to use for the backend referenced by the ServiceName + or BackendRef field. If unspecified, the destination + port in the request is used when forwarding to a backendRef + or serviceName. \n Support: Core" format: int32 maximum: 65535 minimum: 1 type: integer serviceName: - description: "ServiceName refers to the name of the Service to forward matched requests to. When specified, this takes the place of BackendRef. If both BackendRef and ServiceName are specified, ServiceName will be given precedence. \n If the referent cannot be found, the rule is not included in the route. The controller should raise the \"ResolvedRefs\" condition on the Gateway with the \"DegradedRoutes\" reason. The gateway status for this route should be updated with a condition that describes the error more specifically. \n The protocol to use is defined using AppProtocol field (introduced in Kubernetes 1.18) in the Service resource. In the absence of the AppProtocol field a `networking.x-k8s.io/app-protocol` annotation on the BackendPolicy resource may be used to define the protocol. If the AppProtocol field is available, this annotation should not be used. The AppProtocol field, when populated, takes precedence over the annotation in the BackendPolicy resource. For custom backends, it is encouraged to add a semantically-equivalent field in the Custom Resource Definition. \n Support: Core" + description: "ServiceName refers to the name of the Service + to forward matched requests to. When specified, this + takes the place of BackendRef. If both BackendRef and + ServiceName are specified, ServiceName will be given + precedence. \n If the referent cannot be found, the + rule is not included in the route. The controller should + raise the \"ResolvedRefs\" condition on the Gateway + with the \"DegradedRoutes\" reason. The gateway status + for this route should be updated with a condition that + describes the error more specifically. \n The protocol + to use is defined using AppProtocol field (introduced + in Kubernetes 1.18) in the Service resource. In the + absence of the AppProtocol field a `networking.x-k8s.io/app-protocol` + annotation on the BackendPolicy resource may be used + to define the protocol. If the AppProtocol field is + available, this annotation should not be used. The AppProtocol + field, when populated, takes precedence over the annotation + in the BackendPolicy resource. For custom backends, + it is encouraged to add a semantically-equivalent field + in the Custom Resource Definition. \n Support: Core" maxLength: 253 type: string weight: default: 1 - description: "Weight specifies the proportion of HTTP requests forwarded to the backend referenced by the ServiceName or BackendRef field. This is computed as weight/(sum of all weights in this ForwardTo list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support: Extended" + description: "Weight specifies the proportion of HTTP + requests forwarded to the backend referenced by the + ServiceName or BackendRef field. This is computed as + weight/(sum of all weights in this ForwardTo list). + For non-zero values, there may be some epsilon from + the exact proportion defined here depending on the precision + an implementation supports. Weight is not a percentage + and the sum of weights does not need to equal 100. \n + If only one backend is specified and it has a weight + greater than 0, 100% of the traffic is forwarded to + that backend. If weight is set to 0, no traffic should + be forwarded for this entry. If unspecified, weight + defaults to 1. \n Support: Extended" format: int32 maximum: 1000000 minimum: 0 @@ -1226,12 +2056,42 @@ spec: minItems: 1 type: array matches: - description: Matches define conditions used for matching the rule against incoming TCP connections. Each match is independent, i.e. this rule will be matched if **any** one of the matches is satisfied. If unspecified, all requests from the associated gateway TCP listener will match. + description: "Matches define conditions used for matching the + rule against incoming TCP connections. Each match is independent, + i.e. this rule will be matched if **any** one of the matches + is satisfied. If unspecified (i.e. empty), this Rule will + match all requests for the associated Listener. \n Each client + request MUST map to a maximum of one route rule. If a request + matches multiple rules, matching precedence MUST be determined + in order of the following criteria, continuing on ties: \n + * The most specific match specified by ExtensionRef. Each + implementation that supports ExtensionRef may have different + ways of determining the specificity of the referenced extension. + \n If ties still exist across multiple Routes, matching precedence + MUST be determined in order of the following criteria, continuing + on ties: \n * The oldest Route based on creation timestamp. + For example, a Route with a creation timestamp of \"2020-09-08 + 01:02:03\" is given precedence over a Route with a creation + timestamp of \"2020-09-08 01:02:04\". * The Route appearing + first in alphabetical order by \"/\". For + example, foo/bar is given precedence over foo/baz. \n If + ties still exist within the Route that has been given precedence, + matching precedence MUST be granted to the first matching + rule meeting the above criteria." items: - description: TCPRouteMatch defines the predicate used to match connections to a given action. + description: TCPRouteMatch defines the predicate used to match + connections to a given action. properties: extensionRef: - description: "ExtensionRef is an optional, implementation-specific extension to the \"match\" behavior. For example, resource \"mytcproutematcher\" in group \"networking.acme.io\". If the referent cannot be found, the rule is not included in the route. The controller should raise the \"ResolvedRefs\" condition on the Gateway with the \"DegradedRoutes\" reason. The gateway status for this route should be updated with a condition that describes the error more specifically. \n Support: Custom" + description: "ExtensionRef is an optional, implementation-specific + extension to the \"match\" behavior. For example, resource + \"mytcproutematcher\" in group \"networking.acme.io\". + If the referent cannot be found, the rule is not included + in the route. The controller should raise the \"ResolvedRefs\" + condition on the Gateway with the \"DegradedRoutes\" + reason. The gateway status for this route should be + updated with a condition that describes the error more + specifically. \n Support: Custom" properties: group: description: Group is the group of the referent. @@ -1269,43 +2129,87 @@ spec: description: Status defines the current state of TCPRoute. properties: gateways: - description: "Gateways is a list of Gateways that are associated with the route, and the status of the route with respect to each Gateway. When a Gateway selects this route, the controller that manages the Gateway must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route is modified. \n A maximum of 100 Gateways will be represented in this list. If this list is full, there may be additional Gateways using this Route that are not included in the list. An empty list means the route has not been admitted by any Gateway." + description: "Gateways is a list of Gateways that are associated with + the route, and the status of the route with respect to each Gateway. + When a Gateway selects this route, the controller that manages the + Gateway must add an entry to this list when the controller first + sees the route and should update the entry as appropriate when the + route is modified. \n A maximum of 100 Gateways will be represented + in this list. If this list is full, there may be additional Gateways + using this Route that are not included in the list. An empty list + means the route has not been admitted by any Gateway." items: - description: RouteGatewayStatus describes the status of a route with respect to an associated Gateway. + description: RouteGatewayStatus describes the status of a route + with respect to an associated Gateway. properties: conditions: - description: Conditions describes the status of the route with respect to the Gateway. The "Admitted" condition must always be specified by controllers to indicate whether the route has been admitted or rejected by the Gateway, and why. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. + description: Conditions describes the status of the route with + respect to the Gateway. The "Admitted" condition must always + be specified by controllers to indicate whether the route + has been admitted or rejected by the Gateway, and why. Note + that the route's availability is also subject to the Gateway's + own status conditions and listener status. items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + description: "Condition contains details for one aspect of + the current state of this API Resource. --- This struct + is intended for direct use as an array at the field path + .status.conditions. For example, type FooStatus struct{ + \ // Represents the observations of a foo's current state. + \ // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type + \ // +patchStrategy=merge // +listType=map // + +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` + \n // other fields }" properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should + be when the underlying condition changed. If that is + not known, then using the time when the API field changed + is acceptable. format: date-time type: string message: - description: message is a human readable message indicating details about the transition. This may be an empty string. + description: message is a human readable message indicating + details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, + if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the + current state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + description: reason contains a programmatic identifier + indicating the reason for the condition's last transition. + Producers of specific condition types may define expected + values and meanings for this field, and whether the + values are considered a guaranteed API. The value should + be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: - description: status of the condition, one of True, False, Unknown. + description: status of the condition, one of True, False, + Unknown. enum: - "True" - "False" - Unknown type: string type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across + resources like Available, but because arbitrary conditions + can be useful (see .node.status.conditions), the ability + to deconflict is important. The regex it matches is + (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -1322,8 +2226,18 @@ spec: - type x-kubernetes-list-type: map gatewayRef: - description: GatewayRef is a reference to a Gateway object that is associated with the route. + description: GatewayRef is a reference to a Gateway object that + is associated with the route. properties: + controller: + description: "Controller is a domain/path string that indicates + the controller implementing the Gateway. This corresponds + with the controller field on GatewayClass. \n Example: + \"acme.io/gateway-controller\". \n The format of this + field is DOMAIN \"/\" PATH, where DOMAIN and PATH are + valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names)." + maxLength: 253 + type: string name: description: Name is the name of the referent. maxLength: 253 @@ -1363,28 +2277,42 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.4.1 + controller-gen.kubebuilder.io/version: v0.5.0 creationTimestamp: null name: tlsroutes.networking.x-k8s.io spec: group: networking.x-k8s.io names: + categories: + - gateway-api kind: TLSRoute listKind: TLSRouteList plural: tlsroutes singular: tlsroute scope: Namespaced versions: - - name: v1alpha1 + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 schema: openAPIV3Schema: - description: "The TLSRoute resource is similar to TCPRoute, but can be configured to match against TLS-specific metadata. This allows more flexibility in matching streams for a given TLS listener. \n If you need to forward traffic to a single target for a TLS listener, you could choose to use a TCPRoute with a TLS listener." + description: "The TLSRoute resource is similar to TCPRoute, but can be configured + to match against TLS-specific metadata. This allows more flexibility in + matching streams for a given TLS listener. \n If you need to forward traffic + to a single target for a TLS listener, you could choose to use a TCPRoute + with a TLS listener." properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object @@ -1398,16 +2326,24 @@ spec: properties: allow: default: SameNamespace - description: 'Allow indicates which Gateways will be allowed to use this route. Possible values are: * All: Gateways in any namespace can use this route. * FromList: Only Gateways specified in GatewayRefs may use this route. * SameNamespace: Only Gateways in the same namespace may use this route.' + description: 'Allow indicates which Gateways will be allowed to + use this route. Possible values are: * All: Gateways in any + namespace can use this route. * FromList: Only Gateways specified + in GatewayRefs may use this route. * SameNamespace: Only Gateways + in the same namespace may use this route.' enum: - All - FromList - SameNamespace type: string gatewayRefs: - description: GatewayRefs must be specified when Allow is set to "FromList". In that case, only Gateways referenced in this list will be allowed to use this route. This field is ignored for other values of "Allow". + description: GatewayRefs must be specified when Allow is set to + "FromList". In that case, only Gateways referenced in this list + will be allowed to use this route. This field is ignored for + other values of "Allow". items: - description: GatewayReference identifies a Gateway in a specified namespace. + description: GatewayReference identifies a Gateway in a specified + namespace. properties: name: description: Name is the name of the referent. @@ -1431,12 +2367,22 @@ spec: description: TLSRouteRule is the configuration for a given rule. properties: forwardTo: - description: ForwardTo defines the backend(s) where matching requests should be sent. + description: ForwardTo defines the backend(s) where matching + requests should be sent. items: - description: RouteForwardTo defines how a Route should forward a request. + description: RouteForwardTo defines how a Route should forward + a request. properties: backendRef: - description: "BackendRef is a reference to a backend to forward matched requests to. If both BackendRef and ServiceName are specified, ServiceName will be given precedence. \n If the referent cannot be found, the rule is not included in the route. The controller should raise the \"ResolvedRefs\" condition on the Gateway with the \"DegradedRoutes\" reason. The gateway status for this route should be updated with a condition that describes the error more specifically. \n Support: Custom" + description: "BackendRef is a reference to a backend to + forward matched requests to. If both BackendRef and + ServiceName are specified, ServiceName will be given + precedence. \n If the referent cannot be found, the + rule is not included in the route. The controller should + raise the \"ResolvedRefs\" condition on the Gateway + with the \"DegradedRoutes\" reason. The gateway status + for this route should be updated with a condition that + describes the error more specifically. \n Support: Custom" properties: group: description: Group is the group of the referent. @@ -1459,18 +2405,53 @@ spec: - name type: object port: - description: "Port specifies the destination port number to use for the backend referenced by the ServiceName or BackendRef field. If unspecified, the destination port in the request is used when forwarding to a backendRef or serviceName. \n Support: Core" + description: "Port specifies the destination port number + to use for the backend referenced by the ServiceName + or BackendRef field. If unspecified, the destination + port in the request is used when forwarding to a backendRef + or serviceName. \n Support: Core" format: int32 maximum: 65535 minimum: 1 type: integer serviceName: - description: "ServiceName refers to the name of the Service to forward matched requests to. When specified, this takes the place of BackendRef. If both BackendRef and ServiceName are specified, ServiceName will be given precedence. \n If the referent cannot be found, the rule is not included in the route. The controller should raise the \"ResolvedRefs\" condition on the Gateway with the \"DegradedRoutes\" reason. The gateway status for this route should be updated with a condition that describes the error more specifically. \n The protocol to use is defined using AppProtocol field (introduced in Kubernetes 1.18) in the Service resource. In the absence of the AppProtocol field a `networking.x-k8s.io/app-protocol` annotation on the BackendPolicy resource may be used to define the protocol. If the AppProtocol field is available, this annotation should not be used. The AppProtocol field, when populated, takes precedence over the annotation in the BackendPolicy resource. For custom backends, it is encouraged to add a semantically-equivalent field in the Custom Resource Definition. \n Support: Core" + description: "ServiceName refers to the name of the Service + to forward matched requests to. When specified, this + takes the place of BackendRef. If both BackendRef and + ServiceName are specified, ServiceName will be given + precedence. \n If the referent cannot be found, the + rule is not included in the route. The controller should + raise the \"ResolvedRefs\" condition on the Gateway + with the \"DegradedRoutes\" reason. The gateway status + for this route should be updated with a condition that + describes the error more specifically. \n The protocol + to use is defined using AppProtocol field (introduced + in Kubernetes 1.18) in the Service resource. In the + absence of the AppProtocol field a `networking.x-k8s.io/app-protocol` + annotation on the BackendPolicy resource may be used + to define the protocol. If the AppProtocol field is + available, this annotation should not be used. The AppProtocol + field, when populated, takes precedence over the annotation + in the BackendPolicy resource. For custom backends, + it is encouraged to add a semantically-equivalent field + in the Custom Resource Definition. \n Support: Core" maxLength: 253 type: string weight: default: 1 - description: "Weight specifies the proportion of HTTP requests forwarded to the backend referenced by the ServiceName or BackendRef field. This is computed as weight/(sum of all weights in this ForwardTo list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. \n If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. \n Support: Extended" + description: "Weight specifies the proportion of HTTP + requests forwarded to the backend referenced by the + ServiceName or BackendRef field. This is computed as + weight/(sum of all weights in this ForwardTo list). + For non-zero values, there may be some epsilon from + the exact proportion defined here depending on the precision + an implementation supports. Weight is not a percentage + and the sum of weights does not need to equal 100. \n + If only one backend is specified and it has a weight + greater than 0, 100% of the traffic is forwarded to + that backend. If weight is set to 0, no traffic should + be forwarded for this entry. If unspecified, weight + defaults to 1. \n Support: Extended" format: int32 maximum: 1000000 minimum: 0 @@ -1480,12 +2461,45 @@ spec: minItems: 1 type: array matches: - description: Matches define conditions used for matching the rule against an incoming TLS handshake. Each match is independent, i.e. this rule will be matched if **any** one of the matches is satisfied. If unspecified, all requests from the associated gateway TLS listener will match. + description: "Matches define conditions used for matching the + rule against incoming TLS connections. Each match is independent, + i.e. this rule will be matched if **any** one of the matches + is satisfied. If unspecified (i.e. empty), this Rule will + match all requests for the associated Listener. \n Each client + request MUST map to a maximum of one route rule. If a request + matches multiple rules, matching precedence MUST be determined + in order of the following criteria, continuing on ties: \n + * The longest matching SNI. * The longest matching precise + SNI (without a wildcard). This means that \"b.example.com\" + should be given precedence over \"*.example.com\". * The most + specific match specified by ExtensionRef. Each implementation + \ that supports ExtensionRef may have different ways of determining + the specificity of the referenced extension. \n If ties + still exist across multiple Routes, matching precedence MUST + be determined in order of the following criteria, continuing + on ties: \n * The oldest Route based on creation timestamp. + For example, a Route with a creation timestamp of \"2020-09-08 + 01:02:03\" is given precedence over a Route with a creation + timestamp of \"2020-09-08 01:02:04\". * The Route appearing + first in alphabetical order by \"/\". For + example, foo/bar is given precedence over foo/baz. \n If + ties still exist within the Route that has been given precedence, + matching precedence MUST be granted to the first matching + rule meeting the above criteria." items: - description: TLSRouteMatch defines the predicate used to match connections to a given action. + description: TLSRouteMatch defines the predicate used to match + connections to a given action. properties: extensionRef: - description: "ExtensionRef is an optional, implementation-specific extension to the \"match\" behavior. For example, resource \"mytlsroutematcher\" in group \"networking.acme.io\". If the referent cannot be found, the rule is not included in the route. The controller should raise the \"ResolvedRefs\" condition on the Gateway with the \"DegradedRoutes\" reason. The gateway status for this route should be updated with a condition that describes the error more specifically. \n Support: Custom" + description: "ExtensionRef is an optional, implementation-specific + extension to the \"match\" behavior. For example, resource + \"mytlsroutematcher\" in group \"networking.acme.io\". + If the referent cannot be found, the rule is not included + in the route. The controller should raise the \"ResolvedRefs\" + condition on the Gateway with the \"DegradedRoutes\" + reason. The gateway status for this route should be + updated with a condition that describes the error more + specifically. \n Support: Custom" properties: group: description: Group is the group of the referent. @@ -1508,9 +2522,29 @@ spec: - name type: object snis: - description: "SNIs defines a set of SNI names that should match against the SNI attribute of TLS ClientHello message in TLS handshake. \n SNI can be \"precise\" which is a domain name without the terminating dot of a network host (e.g. \"foo.example.com\") or \"wildcard\", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). The wildcard character `*` must appear by itself as the first DNS label and matches only a single label. You cannot have a wildcard label by itself (e.g. Host == `*`). \n Requests will be matched against the Host field in the following order: \n 1. If SNI is precise, the request matches this rule if the SNI in ClientHello is equal to one of the defined SNIs. 2. If SNI is a wildcard, then the request matches this rule if the SNI is to equal to the suffix (removing the first label) of the wildcard rule. 3. If SNIs is unspecified, all requests associated with the gateway TLS listener will match. This can be used to define a default backend for a TLS listener. \n Support: Core" + description: "SNIs defines a set of SNI names that should + match against the SNI attribute of TLS ClientHello message + in TLS handshake. \n SNI can be \"precise\" which is + a domain name without the terminating dot of a network + host (e.g. \"foo.example.com\") or \"wildcard\", which + is a domain name prefixed with a single wildcard label + (e.g. `*.example.com`). The wildcard character `*` must + appear by itself as the first DNS label and matches + only a single label. You cannot have a wildcard label + by itself (e.g. Host == `*`). \n Requests will be matched + against the Host field in the following order: \n 1. + If SNI is precise, the request matches this rule if + the SNI in ClientHello is equal to one of the defined + SNIs. 2. If SNI is a wildcard, then the request matches + this rule if the SNI is to equal to the suffix (removing + the first label) of the wildcard rule. 3. If SNIs + is unspecified, all requests associated with the gateway + TLS listener will match. This can be used to define + a default backend for a TLS listener. \n Support: + Core" items: - description: Hostname is used to specify a hostname that should be matched. + description: Hostname is used to specify a hostname + that should be matched. maxLength: 253 minLength: 1 type: string @@ -1532,43 +2566,87 @@ spec: description: Status defines the current state of TLSRoute. properties: gateways: - description: "Gateways is a list of Gateways that are associated with the route, and the status of the route with respect to each Gateway. When a Gateway selects this route, the controller that manages the Gateway must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route is modified. \n A maximum of 100 Gateways will be represented in this list. If this list is full, there may be additional Gateways using this Route that are not included in the list. An empty list means the route has not been admitted by any Gateway." + description: "Gateways is a list of Gateways that are associated with + the route, and the status of the route with respect to each Gateway. + When a Gateway selects this route, the controller that manages the + Gateway must add an entry to this list when the controller first + sees the route and should update the entry as appropriate when the + route is modified. \n A maximum of 100 Gateways will be represented + in this list. If this list is full, there may be additional Gateways + using this Route that are not included in the list. An empty list + means the route has not been admitted by any Gateway." items: - description: RouteGatewayStatus describes the status of a route with respect to an associated Gateway. + description: RouteGatewayStatus describes the status of a route + with respect to an associated Gateway. properties: conditions: - description: Conditions describes the status of the route with respect to the Gateway. The "Admitted" condition must always be specified by controllers to indicate whether the route has been admitted or rejected by the Gateway, and why. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. + description: Conditions describes the status of the route with + respect to the Gateway. The "Admitted" condition must always + be specified by controllers to indicate whether the route + has been admitted or rejected by the Gateway, and why. Note + that the route's availability is also subject to the Gateway's + own status conditions and listener status. items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + description: "Condition contains details for one aspect of + the current state of this API Resource. --- This struct + is intended for direct use as an array at the field path + .status.conditions. For example, type FooStatus struct{ + \ // Represents the observations of a foo's current state. + \ // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type + \ // +patchStrategy=merge // +listType=map // + +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` + \n // other fields }" properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should + be when the underlying condition changed. If that is + not known, then using the time when the API field changed + is acceptable. format: date-time type: string message: - description: message is a human readable message indicating details about the transition. This may be an empty string. + description: message is a human readable message indicating + details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, + if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the + current state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + description: reason contains a programmatic identifier + indicating the reason for the condition's last transition. + Producers of specific condition types may define expected + values and meanings for this field, and whether the + values are considered a guaranteed API. The value should + be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: - description: status of the condition, one of True, False, Unknown. + description: status of the condition, one of True, False, + Unknown. enum: - "True" - "False" - Unknown type: string type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across + resources like Available, but because arbitrary conditions + can be useful (see .node.status.conditions), the ability + to deconflict is important. The regex it matches is + (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string @@ -1585,8 +2663,18 @@ spec: - type x-kubernetes-list-type: map gatewayRef: - description: GatewayRef is a reference to a Gateway object that is associated with the route. + description: GatewayRef is a reference to a Gateway object that + is associated with the route. properties: + controller: + description: "Controller is a domain/path string that indicates + the controller implementing the Gateway. This corresponds + with the controller field on GatewayClass. \n Example: + \"acme.io/gateway-controller\". \n The format of this + field is DOMAIN \"/\" PATH, where DOMAIN and PATH are + valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names)." + maxLength: 253 + type: string name: description: Name is the name of the referent. maxLength: 253 diff --git a/integration/fixtures/k8s/01-traefik-crd.yml b/integration/fixtures/k8s/01-traefik-crd.yml index fdfcdc39e..84ca0e756 100644 --- a/integration/fixtures/k8s/01-traefik-crd.yml +++ b/integration/fixtures/k8s/01-traefik-crd.yml @@ -741,6 +741,7 @@ spec: type: string type: object featurePolicy: + description: 'Deprecated: use PermissionsPolicy instead.' type: string forceSTSHeader: type: boolean @@ -752,6 +753,8 @@ spec: type: array isDevelopment: type: boolean + permissionsPolicy: + type: string publicKey: type: string referrerPolicy: @@ -1142,6 +1145,10 @@ spec: description: If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used. type: integer + peerCertURI: + description: URI used to match against SAN URI during the peer certificate + verification. + type: string rootCAsSecrets: description: Add cert file for self-signed certificate. items: diff --git a/integration/fixtures/k8s_crd.toml b/integration/fixtures/k8s_crd.toml index 3015b6093..86e85ee91 100644 --- a/integration/fixtures/k8s_crd.toml +++ b/integration/fixtures/k8s_crd.toml @@ -17,3 +17,4 @@ [providers.kubernetesCRD] allowCrossNamespace = false + allowExternalNameServices = true diff --git a/integration/resources/compose/consul_catalog.yml b/integration/resources/compose/consul_catalog.yml index 6c2d8901d..ba31ef65b 100644 --- a/integration/resources/compose/consul_catalog.yml +++ b/integration/resources/compose/consul_catalog.yml @@ -2,7 +2,7 @@ consul: image: consul:1.6.2 ports: - 8500:8500 - command: "agent -server -bootstrap -ui -client 0.0.0.0" + command: "agent -server -bootstrap -ui -client 0.0.0.0 -hcl 'connect { enabled = true }'" consul-agent: image: consul:1.6.2 ports: @@ -22,3 +22,11 @@ whoami3: whoamitcp: image: traefik/whoamitcp hostname: whoamitcp +connect: + image: hashicorpnomad/uuid-api:v5 + links: + - consul + environment: + PORT: 443 + BIND: 0.0.0.0 + CONSUL_HTTP_ADDR: http://consul:8500 diff --git a/pkg/config/dynamic/http_config.go b/pkg/config/dynamic/http_config.go index c2f335e9a..edeaf83e0 100644 --- a/pkg/config/dynamic/http_config.go +++ b/pkg/config/dynamic/http_config.go @@ -5,7 +5,7 @@ import ( "time" ptypes "github.com/traefik/paerser/types" - "github.com/traefik/traefik/v2/pkg/tls" + traefiktls "github.com/traefik/traefik/v2/pkg/tls" "github.com/traefik/traefik/v2/pkg/types" ) @@ -218,13 +218,14 @@ type HealthCheck struct{} // ServersTransport options to configure communication between Traefik and the servers. type ServersTransport struct { - ServerName string `description:"ServerName used to contact the server" json:"serverName,omitempty" toml:"serverName,omitempty" yaml:"serverName,omitempty"` - InsecureSkipVerify bool `description:"Disable SSL certificate verification." json:"insecureSkipVerify,omitempty" toml:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify,omitempty" export:"true"` - RootCAs []tls.FileOrContent `description:"Add cert file for self-signed certificate." json:"rootCAs,omitempty" toml:"rootCAs,omitempty" yaml:"rootCAs,omitempty"` - Certificates tls.Certificates `description:"Certificates for mTLS." json:"certificates,omitempty" toml:"certificates,omitempty" yaml:"certificates,omitempty" export:"true"` - MaxIdleConnsPerHost int `description:"If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used" json:"maxIdleConnsPerHost,omitempty" toml:"maxIdleConnsPerHost,omitempty" yaml:"maxIdleConnsPerHost,omitempty" export:"true"` - ForwardingTimeouts *ForwardingTimeouts `description:"Timeouts for requests forwarded to the backend servers." json:"forwardingTimeouts,omitempty" toml:"forwardingTimeouts,omitempty" yaml:"forwardingTimeouts,omitempty" export:"true"` - DisableHTTP2 bool `description:"Disable HTTP/2 for connections with backend servers." json:"disableHTTP2,omitempty" toml:"disableHTTP2,omitempty" yaml:"disableHTTP2,omitempty" export:"true"` + ServerName string `description:"ServerName used to contact the server" json:"serverName,omitempty" toml:"serverName,omitempty" yaml:"serverName,omitempty"` + InsecureSkipVerify bool `description:"Disable SSL certificate verification." json:"insecureSkipVerify,omitempty" toml:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify,omitempty" export:"true"` + RootCAs []traefiktls.FileOrContent `description:"Add cert file for self-signed certificate." json:"rootCAs,omitempty" toml:"rootCAs,omitempty" yaml:"rootCAs,omitempty"` + Certificates traefiktls.Certificates `description:"Certificates for mTLS." json:"certificates,omitempty" toml:"certificates,omitempty" yaml:"certificates,omitempty" export:"true"` + MaxIdleConnsPerHost int `description:"If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used" json:"maxIdleConnsPerHost,omitempty" toml:"maxIdleConnsPerHost,omitempty" yaml:"maxIdleConnsPerHost,omitempty" export:"true"` + ForwardingTimeouts *ForwardingTimeouts `description:"Timeouts for requests forwarded to the backend servers." json:"forwardingTimeouts,omitempty" toml:"forwardingTimeouts,omitempty" yaml:"forwardingTimeouts,omitempty" export:"true"` + DisableHTTP2 bool `description:"Disable HTTP/2 for connections with backend servers." json:"disableHTTP2,omitempty" toml:"disableHTTP2,omitempty" yaml:"disableHTTP2,omitempty" export:"true"` + PeerCertURI string `description:"URI used to match against SAN URI during the peer certificate verification." json:"peerCertURI,omitempty" toml:"peerCertURI,omitempty" yaml:"peerCertURI,omitempty" export:"true"` } // +k8s:deepcopy-gen=true diff --git a/pkg/config/label/label_test.go b/pkg/config/label/label_test.go index fca311d8e..3abbd57ea 100644 --- a/pkg/config/label/label_test.go +++ b/pkg/config/label/label_test.go @@ -675,6 +675,7 @@ func TestDecodeConfiguration(t *testing.T) { }, } + assert.Nil(t, configuration.HTTP.ServersTransports) assert.Equal(t, expected, configuration) } diff --git a/pkg/middlewares/accesslog/logger.go b/pkg/middlewares/accesslog/logger.go index b3657b67a..a6cfea67d 100644 --- a/pkg/middlewares/accesslog/logger.go +++ b/pkg/middlewares/accesslog/logger.go @@ -10,6 +10,7 @@ import ( "net/url" "os" "path/filepath" + "strings" "sync" "sync/atomic" "time" @@ -347,7 +348,7 @@ func (h *Handler) redactHeaders(headers http.Header, fields logrus.Fields, prefi for k := range headers { v := h.config.Fields.KeepHeader(k) if v == types.AccessLogKeep { - fields[prefix+k] = headers.Get(k) + fields[prefix+k] = strings.Join(headers.Values(k), ",") } else if v == types.AccessLogRedact { fields[prefix+k] = "REDACTED" } diff --git a/pkg/middlewares/accesslog/logger_test.go b/pkg/middlewares/accesslog/logger_test.go index 02f4e6989..751380018 100644 --- a/pkg/middlewares/accesslog/logger_test.go +++ b/pkg/middlewares/accesslog/logger_test.go @@ -114,7 +114,7 @@ func lineCount(t *testing.T, fileName string) int { } func TestLoggerHeaderFields(t *testing.T) { - expectedValue := "expectedValue" + expectedValues := []string{"AAA", "BBB"} testCases := []struct { desc string @@ -191,7 +191,10 @@ func TestLoggerHeaderFields(t *testing.T) { Path: testPath, }, } - req.Header.Set(test.header, expectedValue) + + for _, s := range expectedValues { + req.Header.Add(test.header, s) + } logger.ServeHTTP(httptest.NewRecorder(), req, http.HandlerFunc(func(writer http.ResponseWriter, r *http.Request) { writer.WriteHeader(http.StatusOK) @@ -201,9 +204,9 @@ func TestLoggerHeaderFields(t *testing.T) { require.NoError(t, err) if test.expected == types.AccessLogDrop { - assert.NotContains(t, string(logData), expectedValue) + assert.NotContains(t, string(logData), strings.Join(expectedValues, ",")) } else { - assert.Contains(t, string(logData), expectedValue) + assert.Contains(t, string(logData), strings.Join(expectedValues, ",")) } }) } diff --git a/pkg/middlewares/compress/compress.go b/pkg/middlewares/compress/compress.go index 42b0d01ea..e6ddeb75d 100644 --- a/pkg/middlewares/compress/compress.go +++ b/pkg/middlewares/compress/compress.go @@ -6,8 +6,8 @@ import ( "mime" "net/http" + "github.com/klauspost/compress/gzhttp" "github.com/opentracing/opentracing-go/ext" - "github.com/traefik/gziphandler" "github.com/traefik/traefik/v2/pkg/config/dynamic" "github.com/traefik/traefik/v2/pkg/log" "github.com/traefik/traefik/v2/pkg/middlewares" @@ -61,10 +61,10 @@ func (c *compress) GetTracingInformation() (string, ext.SpanKindEnum) { } func (c *compress) gzipHandler(ctx context.Context) http.Handler { - wrapper, err := gziphandler.GzipHandlerWithOpts( - gziphandler.ContentTypeExceptions(c.excludes), - gziphandler.CompressionLevel(gzip.DefaultCompression), - gziphandler.MinSize(gziphandler.DefaultMinSize)) + wrapper, err := gzhttp.NewWrapper( + gzhttp.ExceptContentTypes(c.excludes), + gzhttp.CompressionLevel(gzip.DefaultCompression), + gzhttp.MinSize(gzhttp.DefaultMinSize)) if err != nil { log.FromContext(ctx).Error(err) } diff --git a/pkg/middlewares/compress/compress_test.go b/pkg/middlewares/compress/compress_test.go index f3b01ed0e..9dd4c8124 100644 --- a/pkg/middlewares/compress/compress_test.go +++ b/pkg/middlewares/compress/compress_test.go @@ -7,9 +7,9 @@ import ( "net/http/httptest" "testing" + "github.com/klauspost/compress/gzhttp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/traefik/gziphandler" "github.com/traefik/traefik/v2/pkg/config/dynamic" "github.com/traefik/traefik/v2/pkg/testhelpers" ) @@ -26,13 +26,14 @@ func TestShouldCompressWhenNoContentEncodingHeader(t *testing.T) { req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost", nil) req.Header.Add(acceptEncodingHeader, gzipValue) - baseBody := generateBytes(gziphandler.DefaultMinSize) + baseBody := generateBytes(gzhttp.DefaultMinSize) next := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { _, err := rw.Write(baseBody) assert.NoError(t, err) }) - handler := &compress{next: next} + handler, err := New(context.Background(), next, dynamic.Compress{}, "testing") + require.NoError(t, err) rw := httptest.NewRecorder() handler.ServeHTTP(rw, req) @@ -49,7 +50,7 @@ func TestShouldNotCompressWhenContentEncodingHeader(t *testing.T) { req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost", nil) req.Header.Add(acceptEncodingHeader, gzipValue) - fakeCompressedBody := generateBytes(gziphandler.DefaultMinSize) + fakeCompressedBody := generateBytes(gzhttp.DefaultMinSize) next := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { rw.Header().Add(contentEncodingHeader, gzipValue) rw.Header().Add(varyHeader, acceptEncodingHeader) @@ -58,7 +59,8 @@ func TestShouldNotCompressWhenContentEncodingHeader(t *testing.T) { http.Error(rw, err.Error(), http.StatusInternalServerError) } }) - handler := &compress{next: next} + handler, err := New(context.Background(), next, dynamic.Compress{}, "testing") + require.NoError(t, err) rw := httptest.NewRecorder() handler.ServeHTTP(rw, req) @@ -72,14 +74,15 @@ func TestShouldNotCompressWhenContentEncodingHeader(t *testing.T) { func TestShouldNotCompressWhenNoAcceptEncodingHeader(t *testing.T) { req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost", nil) - fakeBody := generateBytes(gziphandler.DefaultMinSize) + fakeBody := generateBytes(gzhttp.DefaultMinSize) next := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { _, err := rw.Write(fakeBody) if err != nil { http.Error(rw, err.Error(), http.StatusInternalServerError) } }) - handler := &compress{next: next} + handler, err := New(context.Background(), next, dynamic.Compress{}, "testing") + require.NoError(t, err) rw := httptest.NewRecorder() handler.ServeHTTP(rw, req) @@ -89,7 +92,7 @@ func TestShouldNotCompressWhenNoAcceptEncodingHeader(t *testing.T) { } func TestShouldNotCompressWhenSpecificContentType(t *testing.T) { - baseBody := generateBytes(gziphandler.DefaultMinSize) + baseBody := generateBytes(gzhttp.DefaultMinSize) testCases := []struct { desc string @@ -190,7 +193,9 @@ func TestIntegrationShouldNotCompress(t *testing.T) { for _, test := range testCases { t.Run(test.name, func(t *testing.T) { - compress := &compress{next: test.handler} + compress, err := New(context.Background(), test.handler, dynamic.Compress{}, "testing") + require.NoError(t, err) + ts := httptest.NewServer(compress) defer ts.Close() @@ -223,7 +228,9 @@ func TestShouldWriteHeaderWhenFlush(t *testing.T) { http.Error(rw, err.Error(), http.StatusInternalServerError) } }) - handler := &compress{next: next} + handler, err := New(context.Background(), next, dynamic.Compress{}, "testing") + require.NoError(t, err) + ts := httptest.NewServer(handler) defer ts.Close() @@ -272,7 +279,9 @@ func TestIntegrationShouldCompress(t *testing.T) { for _, test := range testCases { t.Run(test.name, func(t *testing.T) { - compress := &compress{next: test.handler} + compress, err := New(context.Background(), test.handler, dynamic.Compress{}, "testing") + require.NoError(t, err) + ts := httptest.NewServer(compress) defer ts.Close() @@ -296,6 +305,86 @@ func TestIntegrationShouldCompress(t *testing.T) { } } +func BenchmarkCompress(b *testing.B) { + testCases := []struct { + name string + parallel bool + size int + }{ + { + name: "2k", + size: 2048, + }, + { + name: "20k", + size: 20480, + }, + { + name: "100k", + size: 102400, + }, + { + name: "2k parallel", + parallel: true, + size: 2048, + }, + { + name: "20k parallel", + parallel: true, + size: 20480, + }, + { + name: "100k parallel", + parallel: true, + size: 102400, + }, + } + + for _, test := range testCases { + b.Run(test.name, func(b *testing.B) { + baseBody := generateBytes(test.size) + + next := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + _, err := rw.Write(baseBody) + assert.NoError(b, err) + }) + handler, _ := New(context.Background(), next, dynamic.Compress{}, "testing") + + req, _ := http.NewRequest("GET", "/whatever", nil) + req.Header.Set("Accept-Encoding", "gzip") + + b.ReportAllocs() + b.SetBytes(int64(test.size)) + if test.parallel { + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + runBenchmark(b, req, handler) + } + }) + return + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + runBenchmark(b, req, handler) + } + }) + } +} + +func runBenchmark(b *testing.B, req *http.Request, handler http.Handler) { + b.Helper() + + res := httptest.NewRecorder() + handler.ServeHTTP(res, req) + if code := res.Code; code != 200 { + b.Fatalf("Expected 200 but got %d", code) + } + + assert.Equal(b, gzipValue, res.Header().Get(contentEncodingHeader)) +} + func generateBytes(length int) []byte { var value []byte for i := 0; i < length; i++ { diff --git a/pkg/middlewares/headers/responsewriter.go b/pkg/middlewares/headers/responsewriter.go index a50643eae..39a171dc2 100644 --- a/pkg/middlewares/headers/responsewriter.go +++ b/pkg/middlewares/headers/responsewriter.go @@ -10,8 +10,8 @@ import ( ) type responseModifier struct { - r *http.Request - w http.ResponseWriter + req *http.Request + rw http.ResponseWriter headersSent bool // whether headers have already been sent code int // status code, must default to 200 @@ -24,71 +24,76 @@ type responseModifier struct { // modifier can be nil. func newResponseModifier(w http.ResponseWriter, r *http.Request, modifier func(*http.Response) error) *responseModifier { return &responseModifier{ - r: r, - w: w, + req: r, + rw: w, modifier: modifier, code: http.StatusOK, } } -func (w *responseModifier) WriteHeader(code int) { - if w.headersSent { +func (r *responseModifier) WriteHeader(code int) { + if r.headersSent { return } defer func() { - w.code = code - w.headersSent = true + r.code = code + r.headersSent = true }() - if w.modifier == nil || w.modified { - w.w.WriteHeader(code) + if r.modifier == nil || r.modified { + r.rw.WriteHeader(code) return } resp := http.Response{ - Header: w.w.Header(), - Request: w.r, + Header: r.rw.Header(), + Request: r.req, } - if err := w.modifier(&resp); err != nil { - w.modifierErr = err + if err := r.modifier(&resp); err != nil { + r.modifierErr = err // we are propagating when we are called in Write, but we're logging anyway, // because we could be called from another place which does not take care of // checking w.modifierErr. log.WithoutContext().Errorf("Error when applying response modifier: %v", err) - w.w.WriteHeader(http.StatusInternalServerError) + r.rw.WriteHeader(http.StatusInternalServerError) return } - w.modified = true - w.w.WriteHeader(code) + r.modified = true + r.rw.WriteHeader(code) } -func (w *responseModifier) Header() http.Header { - return w.w.Header() +func (r *responseModifier) Header() http.Header { + return r.rw.Header() } -func (w *responseModifier) Write(b []byte) (int, error) { - w.WriteHeader(w.code) - if w.modifierErr != nil { - return 0, w.modifierErr +func (r *responseModifier) Write(b []byte) (int, error) { + r.WriteHeader(r.code) + if r.modifierErr != nil { + return 0, r.modifierErr } - return w.w.Write(b) + return r.rw.Write(b) } // Hijack hijacks the connection. -func (w *responseModifier) Hijack() (net.Conn, *bufio.ReadWriter, error) { - if h, ok := w.w.(http.Hijacker); ok { +func (r *responseModifier) Hijack() (net.Conn, *bufio.ReadWriter, error) { + if h, ok := r.rw.(http.Hijacker); ok { return h.Hijack() } - return nil, nil, fmt.Errorf("not a hijacker: %T", w.w) + return nil, nil, fmt.Errorf("not a hijacker: %T", r.rw) } // Flush sends any buffered data to the client. -func (w *responseModifier) Flush() { - if flusher, ok := w.w.(http.Flusher); ok { +func (r *responseModifier) Flush() { + if flusher, ok := r.rw.(http.Flusher); ok { flusher.Flush() } } + +// CloseNotify implements http.CloseNotifier. +func (r *responseModifier) CloseNotify() <-chan bool { + return r.rw.(http.CloseNotifier).CloseNotify() +} diff --git a/pkg/plugins/providers.go b/pkg/plugins/providers.go index 0cb95935f..1b2c3cce0 100644 --- a/pkg/plugins/providers.go +++ b/pkg/plugins/providers.go @@ -23,6 +23,7 @@ type PP interface { } type _PP struct { + IValue interface{} WInit func() error WProvide func(cfgChan chan<- json.Marshaler) error WStop func() error @@ -42,7 +43,7 @@ func (p _PP) Stop() error { func ppSymbols() map[string]map[string]reflect.Value { return map[string]map[string]reflect.Value{ - "github.com/traefik/traefik/v2/pkg/plugins": { + "github.com/traefik/traefik/v2/pkg/plugins/plugins": { "PP": reflect.ValueOf((*PP)(nil)), "_PP": reflect.ValueOf((*_PP)(nil)), }, diff --git a/pkg/provider/configuration.go b/pkg/provider/configuration.go index 361e8f025..1000e696f 100644 --- a/pkg/provider/configuration.go +++ b/pkg/provider/configuration.go @@ -20,9 +20,10 @@ func Merge(ctx context.Context, configurations map[string]*dynamic.Configuration configuration := &dynamic.Configuration{ HTTP: &dynamic.HTTPConfiguration{ - Routers: make(map[string]*dynamic.Router), - Middlewares: make(map[string]*dynamic.Middleware), - Services: make(map[string]*dynamic.Service), + Routers: make(map[string]*dynamic.Router), + Middlewares: make(map[string]*dynamic.Middleware), + Services: make(map[string]*dynamic.Service), + ServersTransports: make(map[string]*dynamic.ServersTransport), }, TCP: &dynamic.TCPConfiguration{ Routers: make(map[string]*dynamic.TCPRouter), @@ -59,6 +60,9 @@ func Merge(ctx context.Context, configurations map[string]*dynamic.Configuration middlewaresTCPToDelete := map[string]struct{}{} middlewaresTCP := map[string][]string{} + transportsToDelete := map[string]struct{}{} + transports := map[string][]string{} + var sortedKeys []string for key := range configurations { sortedKeys = append(sortedKeys, key) @@ -81,6 +85,13 @@ func Merge(ctx context.Context, configurations map[string]*dynamic.Configuration } } + for transportName, transport := range conf.HTTP.ServersTransports { + transports[transportName] = append(transports[transportName], root) + if !AddTransport(configuration.HTTP, transportName, transport) { + transportsToDelete[transportName] = struct{}{} + } + } + for serviceName, service := range conf.TCP.Services { servicesTCP[serviceName] = append(servicesTCP[serviceName], root) if !AddServiceTCP(configuration.TCP, serviceName, service) { @@ -136,6 +147,12 @@ func Merge(ctx context.Context, configurations map[string]*dynamic.Configuration delete(configuration.HTTP.Routers, routerName) } + for transportName := range transportsToDelete { + logger.WithField(log.ServersTransportName, transportName). + Errorf("ServersTransport defined multiple times with different configurations in %v", transports[transportName]) + delete(configuration.HTTP.ServersTransports, transportName) + } + for serviceName := range servicesTCPToDelete { logger.WithField(log.ServiceName, serviceName). Errorf("Service TCP defined multiple times with different configurations in %v", servicesTCP[serviceName]) @@ -290,6 +307,16 @@ func AddRouter(configuration *dynamic.HTTPConfiguration, routerName string, rout return reflect.DeepEqual(configuration.Routers[routerName], router) } +// AddTransport Adds a transport to a configurations. +func AddTransport(configuration *dynamic.HTTPConfiguration, transportName string, transport *dynamic.ServersTransport) bool { + if _, ok := configuration.ServersTransports[transportName]; !ok { + configuration.ServersTransports[transportName] = transport + return true + } + + return reflect.DeepEqual(configuration.ServersTransports[transportName], transport) +} + // AddMiddleware Adds a middleware to a configurations. func AddMiddleware(configuration *dynamic.HTTPConfiguration, middlewareName string, middleware *dynamic.Middleware) bool { if _, ok := configuration.Middlewares[middlewareName]; !ok { diff --git a/pkg/provider/consulcatalog/config.go b/pkg/provider/consulcatalog/config.go index 66f43dd22..19fe33084 100644 --- a/pkg/provider/consulcatalog/config.go +++ b/pkg/provider/consulcatalog/config.go @@ -14,7 +14,7 @@ import ( "github.com/traefik/traefik/v2/pkg/provider/constraints" ) -func (p *Provider) buildConfiguration(ctx context.Context, items []itemData) *dynamic.Configuration { +func (p *Provider) buildConfiguration(ctx context.Context, items []itemData, certInfo *connectCert) *dynamic.Configuration { configurations := make(map[string]*dynamic.Configuration) for _, item := range items { @@ -42,6 +42,7 @@ func (p *Provider) buildConfiguration(ctx context.Context, items []itemData) *dy logger.Error(err) continue } + provider.BuildTCPRouterConfiguration(ctxSvc, confFromLabel.TCP) } @@ -63,6 +64,17 @@ func (p *Provider) buildConfiguration(ctx context.Context, items []itemData) *dy continue } + if item.ExtraConf.ConsulCatalog.Connect { + if confFromLabel.HTTP.ServersTransports == nil { + confFromLabel.HTTP.ServersTransports = make(map[string]*dynamic.ServersTransport) + } + + serversTransportKey := itemServersTransportKey(item) + if confFromLabel.HTTP.ServersTransports[serversTransportKey] == nil { + confFromLabel.HTTP.ServersTransports[serversTransportKey] = certInfo.serversTransport(item) + } + } + err = p.buildServiceConfiguration(ctxSvc, item, confFromLabel.HTTP) if err != nil { logger.Error(err) @@ -93,13 +105,18 @@ func (p *Provider) keepContainer(ctx context.Context, item itemData) bool { return false } + if !p.ConnectAware && item.ExtraConf.ConsulCatalog.Connect { + logger.Debugf("Filtering out Connect aware item, Connect support is not enabled") + return false + } + matches, err := constraints.MatchTags(item.Tags, p.Constraints) if err != nil { - logger.Errorf("Error matching constraints expression: %v", err) + logger.Errorf("Error matching constraint expressions: %v", err) return false } if !matches { - logger.Debugf("Container pruned by constraint expression: %q", p.Constraints) + logger.Debugf("Container pruned by constraint expressions: %q", p.Constraints) return false } @@ -267,8 +284,19 @@ func (p *Provider) addServer(ctx context.Context, item itemData, loadBalancer *d return errors.New("address is missing") } - loadBalancer.Servers[0].URL = fmt.Sprintf("%s://%s", loadBalancer.Servers[0].Scheme, net.JoinHostPort(item.Address, port)) + scheme := loadBalancer.Servers[0].Scheme loadBalancer.Servers[0].Scheme = "" + if item.ExtraConf.ConsulCatalog.Connect { + loadBalancer.ServersTransport = itemServersTransportKey(item) + scheme = "https" + } + + loadBalancer.Servers[0].URL = fmt.Sprintf("%s://%s", scheme, net.JoinHostPort(item.Address, port)) + return nil } + +func itemServersTransportKey(item itemData) string { + return provider.Normalize("tls-" + item.Namespace + "-" + item.Datacenter + "-" + item.Name) +} diff --git a/pkg/provider/consulcatalog/config_test.go b/pkg/provider/consulcatalog/config_test.go index 1526134f5..ca3d75d14 100644 --- a/pkg/provider/consulcatalog/config_test.go +++ b/pkg/provider/consulcatalog/config_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/traefik/traefik/v2/pkg/config/dynamic" + "github.com/traefik/traefik/v2/pkg/tls" ) func Int(v int) *int { return &v } @@ -65,6 +66,7 @@ func TestDefaultRule(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -114,6 +116,7 @@ func TestDefaultRule(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -156,6 +159,7 @@ func TestDefaultRule(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -198,6 +202,7 @@ func TestDefaultRule(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -245,6 +250,7 @@ func TestDefaultRule(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -265,11 +271,11 @@ func TestDefaultRule(t *testing.T) { for i := 0; i < len(test.items); i++ { var err error - test.items[i].ExtraConf, err = p.getConfiguration(test.items[i]) + test.items[i].ExtraConf, err = p.getConfiguration(test.items[i].Labels) require.NoError(t, err) } - configuration := p.buildConfiguration(context.Background(), test.items) + configuration := p.buildConfiguration(context.Background(), test.items, nil) assert.Equal(t, test.expected, configuration) }) @@ -278,10 +284,11 @@ func TestDefaultRule(t *testing.T) { func Test_buildConfiguration(t *testing.T) { testCases := []struct { - desc string - items []itemData - constraints string - expected *dynamic.Configuration + desc string + items []itemData + constraints string + ConnectAware bool + expected *dynamic.Configuration }{ { desc: "one container no label", @@ -326,6 +333,162 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + }, + }, + { + desc: "one connect container", + ConnectAware: true, + items: []itemData{ + { + ID: "Test", + Node: "Node1", + Datacenter: "dc1", + Name: "dev/Test", + Namespace: "ns", + Address: "127.0.0.1", + Port: "443", + Status: api.HealthPassing, + Labels: map[string]string{ + "traefik.consulcatalog.connect": "true", + }, + Tags: nil, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + }, + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "dev-Test": { + Service: "dev-Test", + Rule: "Host(`dev-Test.traefik.wtf`)", + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "dev-Test": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "https://127.0.0.1:443", + }, + }, + PassHostHeader: Bool(true), + ServersTransport: "tls-ns-dc1-dev-Test", + }, + }, + }, + ServersTransports: map[string]*dynamic.ServersTransport{ + "tls-ns-dc1-dev-Test": { + ServerName: "ns-dc1-dev/Test", + InsecureSkipVerify: true, + RootCAs: []tls.FileOrContent{ + "root", + }, + Certificates: []tls.Certificate{ + { + CertFile: "cert", + KeyFile: "key", + }, + }, + PeerCertURI: "spiffe:///ns/ns/dc/dc1/svc/dev/Test", + }, + }, + }, + }, + }, + { + desc: "two connect containers on same service", + ConnectAware: true, + items: []itemData{ + { + ID: "Test1", + Node: "Node1", + Datacenter: "dc1", + Name: "dev/Test", + Namespace: "ns", + Address: "127.0.0.1", + Port: "443", + Status: api.HealthPassing, + Labels: map[string]string{ + "traefik.consulcatalog.connect": "true", + }, + Tags: nil, + }, + { + ID: "Test2", + Node: "Node2", + Datacenter: "dc1", + Name: "dev/Test", + Namespace: "ns", + Address: "127.0.0.2", + Port: "444", + Status: api.HealthPassing, + Labels: map[string]string{ + "traefik.consulcatalog.connect": "true", + }, + Tags: nil, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + }, + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "dev-Test": { + Service: "dev-Test", + Rule: "Host(`dev-Test.traefik.wtf`)", + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "dev-Test": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "https://127.0.0.1:443", + }, + { + URL: "https://127.0.0.2:444", + }, + }, + PassHostHeader: Bool(true), + ServersTransport: "tls-ns-dc1-dev-Test", + }, + }, + }, + ServersTransports: map[string]*dynamic.ServersTransport{ + "tls-ns-dc1-dev-Test": { + ServerName: "ns-dc1-dev/Test", + InsecureSkipVerify: true, + RootCAs: []tls.FileOrContent{ + "root", + }, + Certificates: []tls.Certificate{ + { + CertFile: "cert", + KeyFile: "key", + }, + }, + PeerCertURI: "spiffe:///ns/ns/dc/dc1/svc/dev/Test", + }, + }, }, }, }, @@ -395,6 +558,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -453,6 +617,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -508,6 +673,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -566,6 +732,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -613,6 +780,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -662,6 +830,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -709,6 +878,7 @@ func Test_buildConfiguration(t *testing.T) { Rule: "Host(`foo.com`)", }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -757,6 +927,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -811,6 +982,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -854,8 +1026,9 @@ func Test_buildConfiguration(t *testing.T) { Rule: "Host(`Test.traefik.wtf`)", }, }, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -910,8 +1083,9 @@ func Test_buildConfiguration(t *testing.T) { Rule: "Host(`Test.traefik.wtf`)", }, }, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -972,6 +1146,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1025,6 +1200,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1090,6 +1266,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1149,6 +1326,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1224,6 +1402,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1280,6 +1459,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1350,6 +1530,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1412,6 +1593,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1459,6 +1641,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1507,6 +1690,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1560,6 +1744,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1586,9 +1771,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1616,9 +1802,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1646,9 +1833,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1676,9 +1864,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1708,9 +1897,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1759,6 +1949,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1817,6 +2008,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1870,9 +2062,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1919,9 +2112,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1965,9 +2159,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2007,9 +2202,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2059,9 +2255,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2106,9 +2303,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2195,6 +2393,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2277,6 +2476,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2316,9 +2516,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2357,9 +2558,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2400,9 +2602,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2417,15 +2620,16 @@ func Test_buildConfiguration(t *testing.T) { p := Provider{ ExposedByDefault: true, DefaultRule: "Host(`{{ normalize .Name }}.traefik.wtf`)", + ConnectAware: test.ConnectAware, + Constraints: test.constraints, } - p.Constraints = test.constraints err := p.Init() require.NoError(t, err) for i := 0; i < len(test.items); i++ { var err error - test.items[i].ExtraConf, err = p.getConfiguration(test.items[i]) + test.items[i].ExtraConf, err = p.getConfiguration(test.items[i].Labels) require.NoError(t, err) var tags []string @@ -2435,7 +2639,13 @@ func Test_buildConfiguration(t *testing.T) { test.items[i].Tags = tags } - configuration := p.buildConfiguration(context.Background(), test.items) + configuration := p.buildConfiguration(context.Background(), test.items, &connectCert{ + root: []string{"root"}, + leaf: keyPair{ + cert: "cert", + key: "key", + }, + }) assert.Equal(t, test.expected, configuration) }) diff --git a/pkg/provider/consulcatalog/connect_tls.go b/pkg/provider/consulcatalog/connect_tls.go new file mode 100644 index 000000000..4c0e437e0 --- /dev/null +++ b/pkg/provider/consulcatalog/connect_tls.go @@ -0,0 +1,74 @@ +package consulcatalog + +import ( + "fmt" + + "github.com/hashicorp/consul/agent/connect" + "github.com/traefik/traefik/v2/pkg/config/dynamic" + traefiktls "github.com/traefik/traefik/v2/pkg/tls" +) + +// connectCert holds our certificates as a client of the Consul Connect protocol. +type connectCert struct { + root []string + leaf keyPair + // err is used to propagate to the caller (Provide) any error occurring within the certificate watcher goroutines. + err error +} + +func (c *connectCert) getRoot() []traefiktls.FileOrContent { + var result []traefiktls.FileOrContent + for _, r := range c.root { + result = append(result, traefiktls.FileOrContent(r)) + } + return result +} + +func (c *connectCert) getLeaf() traefiktls.Certificate { + return traefiktls.Certificate{ + CertFile: traefiktls.FileOrContent(c.leaf.cert), + KeyFile: traefiktls.FileOrContent(c.leaf.key), + } +} + +func (c *connectCert) isReady() bool { + return c != nil && len(c.root) > 0 && c.leaf.cert != "" && c.leaf.key != "" +} + +func (c *connectCert) equals(other *connectCert) bool { + if c == nil && other == nil { + return true + } + if c == nil || other == nil { + return false + } + if len(c.root) != len(other.root) { + return false + } + for i, v := range c.root { + if v != other.root[i] { + return false + } + } + return c.leaf == other.leaf +} + +func (c *connectCert) serversTransport(item itemData) *dynamic.ServersTransport { + spiffeIDService := connect.SpiffeIDService{ + Namespace: item.Namespace, + Datacenter: item.Datacenter, + Service: item.Name, + } + + return &dynamic.ServersTransport{ + // This ensures that the config changes whenever the verifier function changes + ServerName: fmt.Sprintf("%s-%s-%s", item.Namespace, item.Datacenter, item.Name), + // InsecureSkipVerify is needed because Go wants to verify a hostname otherwise + InsecureSkipVerify: true, + RootCAs: c.getRoot(), + Certificates: traefiktls.Certificates{ + c.getLeaf(), + }, + PeerCertURI: spiffeIDService.URI().String(), + } +} diff --git a/pkg/provider/consulcatalog/consul_catalog.go b/pkg/provider/consulcatalog/consul_catalog.go index c6937f10a..b931e730b 100644 --- a/pkg/provider/consulcatalog/consul_catalog.go +++ b/pkg/provider/consulcatalog/consul_catalog.go @@ -4,12 +4,14 @@ import ( "context" "fmt" "strconv" - "strings" "text/template" "time" "github.com/cenkalti/backoff/v4" "github.com/hashicorp/consul/api" + "github.com/hashicorp/consul/api/watch" + "github.com/hashicorp/go-hclog" + "github.com/sirupsen/logrus" ptypes "github.com/traefik/paerser/types" "github.com/traefik/traefik/v2/pkg/config/dynamic" "github.com/traefik/traefik/v2/pkg/job" @@ -26,15 +28,17 @@ const DefaultTemplateRule = "Host(`{{ normalize .Name }}`)" var _ provider.Provider = (*Provider)(nil) type itemData struct { - ID string - Node string - Name string - Address string - Port string - Status string - Labels map[string]string - Tags []string - ExtraConf configuration + ID string + Node string + Datacenter string + Name string + Namespace string + Address string + Port string + Status string + Labels map[string]string + Tags []string + ExtraConf configuration } // Provider holds configurations of the provider. @@ -48,9 +52,13 @@ type Provider struct { Cache bool `description:"Use local agent caching for catalog reads." json:"cache,omitempty" toml:"cache,omitempty" yaml:"cache,omitempty" export:"true"` ExposedByDefault bool `description:"Expose containers by default." json:"exposedByDefault,omitempty" toml:"exposedByDefault,omitempty" yaml:"exposedByDefault,omitempty" export:"true"` DefaultRule string `description:"Default rule." json:"defaultRule,omitempty" toml:"defaultRule,omitempty" yaml:"defaultRule,omitempty"` + ConnectAware bool `description:"Enable Consul Connect support." json:"connectAware,omitempty" toml:"connectAware,omitempty" yaml:"connectAware,omitempty" export:"true"` + ConnectByDefault bool `description:"Consider every service as Connect capable by default." json:"connectByDefault,omitempty" toml:"connectByDefault,omitempty" yaml:"connectByDefault,omitempty" export:"true"` + ServiceName string `description:"Name of the Traefik service in Consul Catalog (needs to be registered via the orchestrator or manually)." json:"serviceName,omitempty" toml:"serviceName,omitempty" yaml:"serviceName,omitempty" export:"true"` client *api.Client defaultRuleTpl *template.Template + certChan chan *connectCert } // EndpointConfig holds configurations of the endpoint. @@ -64,11 +72,6 @@ type EndpointConfig struct { EndpointWaitTime ptypes.Duration `description:"WaitTime limits how long a Watch will block. If not provided, the agent default values will be used" json:"endpointWaitTime,omitempty" toml:"endpointWaitTime,omitempty" yaml:"endpointWaitTime,omitempty" export:"true"` } -// SetDefaults sets the default values. -func (c *EndpointConfig) SetDefaults() { - c.Address = "127.0.0.1:8500" -} - // EndpointHTTPAuthConfig holds configurations of the authentication. type EndpointHTTPAuthConfig struct { Username string `description:"Basic Auth username" json:"username,omitempty" toml:"username,omitempty" yaml:"username,omitempty"` @@ -78,12 +81,13 @@ type EndpointHTTPAuthConfig struct { // SetDefaults sets the default values. func (p *Provider) SetDefaults() { endpoint := &EndpointConfig{} - endpoint.SetDefaults() p.Endpoint = endpoint p.RefreshInterval = ptypes.Duration(15 * time.Second) p.Prefix = "traefik" p.ExposedByDefault = true p.DefaultRule = DefaultTemplateRule + p.ServiceName = "traefik" + p.certChan = make(chan *connectCert) } // Init the provider. @@ -99,6 +103,24 @@ func (p *Provider) Init() error { // Provide allows the consul catalog provider to provide configurations to traefik using the given configuration channel. func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error { + var err error + p.client, err = createClient(p.Endpoint) + if err != nil { + return fmt.Errorf("unable to create consul client: %w", err) + } + + if p.ConnectAware { + leafWatcher, rootWatcher, err := p.createConnectTLSWatchers() + if err != nil { + return fmt.Errorf("unable to create consul watch plans: %w", err) + } + pool.GoCtx(func(routineCtx context.Context) { + p.watchConnectTLS(routineCtx, leafWatcher, rootWatcher) + }) + } + + var certInfo *connectCert + pool.GoCtx(func(routineCtx context.Context) { ctxLog := log.With(routineCtx, log.Str(log.ProviderName, "consulcatalog")) logger := log.FromContext(ctxLog) @@ -106,13 +128,25 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe. operation := func() error { var err error - p.client, err = createClient(p.Endpoint) - if err != nil { - return fmt.Errorf("unable to create consul client: %w", err) + // If we are running in connect aware mode then we need to + // make sure that we obtain the certificates before starting + // the service watcher, otherwise a connect enabled service + // that gets resolved before the certificates are available + // will cause an error condition. + if p.ConnectAware && !certInfo.isReady() { + logger.Infof("Waiting for Connect certificate before building first configuration") + select { + case <-routineCtx.Done(): + return nil + case certInfo = <-p.certChan: + if certInfo.err != nil { + return backoff.Permanent(err) + } + } } // get configuration at the provider's startup. - err = p.loadConfiguration(routineCtx, configurationChan) + err = p.loadConfiguration(ctxLog, certInfo, configurationChan) if err != nil { return fmt.Errorf("failed to get consul catalog data: %w", err) } @@ -123,14 +157,17 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe. for { select { - case <-ticker.C: - err = p.loadConfiguration(routineCtx, configurationChan) - if err != nil { - return fmt.Errorf("failed to refresh consul catalog data: %w", err) - } - case <-routineCtx.Done(): return nil + case <-ticker.C: + case certInfo = <-p.certChan: + if certInfo.err != nil { + return backoff.Permanent(err) + } + } + err = p.loadConfiguration(ctxLog, certInfo, configurationChan) + if err != nil { + return fmt.Errorf("failed to refresh consul catalog data: %w", err) } } } @@ -148,7 +185,7 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe. return nil } -func (p *Provider) loadConfiguration(ctx context.Context, configurationChan chan<- dynamic.Message) error { +func (p *Provider) loadConfiguration(ctx context.Context, certInfo *connectCert, configurationChan chan<- dynamic.Message) error { data, err := p.getConsulServicesData(ctx) if err != nil { return err @@ -156,21 +193,53 @@ func (p *Provider) loadConfiguration(ctx context.Context, configurationChan chan configurationChan <- dynamic.Message{ ProviderName: "consulcatalog", - Configuration: p.buildConfiguration(ctx, data), + Configuration: p.buildConfiguration(ctx, data, certInfo), } return nil } func (p *Provider) getConsulServicesData(ctx context.Context) ([]itemData, error) { - consulServiceNames, err := p.fetchServices(ctx) + // The query option "Filter" is not supported by /catalog/services. + // https://www.consul.io/api/catalog.html#list-services + opts := &api.QueryOptions{AllowStale: p.Stale, RequireConsistent: p.RequireConsistent, UseCache: p.Cache} + serviceNames, _, err := p.client.Catalog().Services(opts) if err != nil { return nil, err } var data []itemData - for _, name := range consulServiceNames { - consulServices, statuses, err := p.fetchService(ctx, name) + for name, tags := range serviceNames { + logger := log.FromContext(log.With(ctx, log.Str("serviceName", name))) + + svcCfg, err := p.getConfiguration(tagsToNeutralLabels(tags, p.Prefix)) + if err != nil { + logger.Errorf("Skip service: %v", err) + continue + } + + if !svcCfg.Enable { + logger.Debug("Filtering disabled item") + continue + } + + matches, err := constraints.MatchTags(tags, p.Constraints) + if err != nil { + logger.Errorf("Error matching constraint expressions: %v", err) + continue + } + + if !matches { + logger.Debugf("Container pruned by constraint expressions: %q", p.Constraints) + continue + } + + if !p.ConnectAware && svcCfg.ConsulCatalog.Connect { + logger.Debugf("Filtering out Connect aware item, Connect support is not enabled") + continue + } + + consulServices, statuses, err := p.fetchService(ctx, name, svcCfg.ConsulCatalog.Connect) if err != nil { return nil, err } @@ -181,23 +250,30 @@ func (p *Provider) getConsulServicesData(ctx context.Context) ([]itemData, error address = consulService.Address } + namespace := consulService.Namespace + if namespace == "" { + namespace = "default" + } + status, exists := statuses[consulService.ID+consulService.ServiceID] if !exists { status = api.HealthAny } item := itemData{ - ID: consulService.ServiceID, - Node: consulService.Node, - Name: consulService.ServiceName, - Address: address, - Port: strconv.Itoa(consulService.ServicePort), - Labels: tagsToNeutralLabels(consulService.ServiceTags, p.Prefix), - Tags: consulService.ServiceTags, - Status: status, + ID: consulService.ServiceID, + Node: consulService.Node, + Datacenter: consulService.Datacenter, + Namespace: namespace, + Name: name, + Address: address, + Port: strconv.Itoa(consulService.ServicePort), + Labels: tagsToNeutralLabels(consulService.ServiceTags, p.Prefix), + Tags: consulService.ServiceTags, + Status: status, } - extraConf, err := p.getConfiguration(item) + extraConf, err := p.getConfiguration(item.Labels) if err != nil { log.FromContext(ctx).Errorf("Skip item %s: %v", item.Name, err) continue @@ -207,10 +283,11 @@ func (p *Provider) getConsulServicesData(ctx context.Context) ([]itemData, error data = append(data, item) } } + return data, nil } -func (p *Provider) fetchService(ctx context.Context, name string) ([]*api.CatalogService, map[string]string, error) { +func (p *Provider) fetchService(ctx context.Context, name string, connectEnabled bool) ([]*api.CatalogService, map[string]string, error) { var tagFilter string if !p.ExposedByDefault { tagFilter = p.Prefix + ".enable=true" @@ -219,12 +296,19 @@ func (p *Provider) fetchService(ctx context.Context, name string) ([]*api.Catalo opts := &api.QueryOptions{AllowStale: p.Stale, RequireConsistent: p.RequireConsistent, UseCache: p.Cache} opts = opts.WithContext(ctx) - consulServices, _, err := p.client.Catalog().Service(name, tagFilter, opts) + catalogFunc := p.client.Catalog().Service + healthFunc := p.client.Health().Service + if connectEnabled { + catalogFunc = p.client.Catalog().Connect + healthFunc = p.client.Health().Connect + } + + consulServices, _, err := catalogFunc(name, tagFilter, opts) if err != nil { return nil, nil, err } - healthServices, _, err := p.client.Health().Service(name, tagFilter, false, opts) + healthServices, _, err := healthFunc(name, tagFilter, false, opts) if err != nil { return nil, nil, err } @@ -243,55 +327,132 @@ func (p *Provider) fetchService(ctx context.Context, name string) ([]*api.Catalo return consulServices, statuses, err } -func (p *Provider) fetchServices(ctx context.Context) ([]string, error) { - // The query option "Filter" is not supported by /catalog/services. - // https://www.consul.io/api/catalog.html#list-services - opts := &api.QueryOptions{AllowStale: p.Stale, RequireConsistent: p.RequireConsistent, UseCache: p.Cache} - serviceNames, _, err := p.client.Catalog().Services(opts) - if err != nil { - return nil, err +func rootsWatchHandler(ctx context.Context, dest chan<- []string) func(watch.BlockingParamVal, interface{}) { + return func(_ watch.BlockingParamVal, raw interface{}) { + if raw == nil { + log.FromContext(ctx).Errorf("Root certificate watcher called with nil") + return + } + + v, ok := raw.(*api.CARootList) + if !ok || v == nil { + log.FromContext(ctx).Errorf("Invalid result for root certificate watcher") + return + } + + roots := make([]string, 0, len(v.Roots)) + for _, root := range v.Roots { + roots = append(roots, root.RootCertPEM) + } + + dest <- roots } - - // The keys are the service names, and the array values provide all known tags for a given service. - // https://www.consul.io/api/catalog.html#list-services - var filtered []string - for svcName, tags := range serviceNames { - logger := log.FromContext(log.With(ctx, log.Str("serviceName", svcName))) - - if !p.ExposedByDefault && !contains(tags, p.Prefix+".enable=true") { - logger.Debug("Filtering disabled item") - continue - } - - if contains(tags, p.Prefix+".enable=false") { - logger.Debug("Filtering disabled item") - continue - } - - matches, err := constraints.MatchTags(tags, p.Constraints) - if err != nil { - logger.Errorf("Error matching constraints expression: %v", err) - continue - } - - if !matches { - logger.Debugf("Container pruned by constraint expression: %q", p.Constraints) - continue - } - - filtered = append(filtered, svcName) - } - - return filtered, err } -func contains(values []string, val string) bool { - for _, value := range values { - if strings.EqualFold(value, val) { - return true +type keyPair struct { + cert string + key string +} + +func leafWatcherHandler(ctx context.Context, dest chan<- keyPair) func(watch.BlockingParamVal, interface{}) { + return func(_ watch.BlockingParamVal, raw interface{}) { + if raw == nil { + log.FromContext(ctx).Errorf("Leaf certificate watcher called with nil") + return + } + + v, ok := raw.(*api.LeafCert) + if !ok || v == nil { + log.FromContext(ctx).Errorf("Invalid result for leaf certificate watcher") + return + } + + dest <- keyPair{ + cert: v.CertPEM, + key: v.PrivateKeyPEM, + } + } +} + +func (p *Provider) createConnectTLSWatchers() (*watch.Plan, *watch.Plan, error) { + leafWatcher, err := watch.Parse(map[string]interface{}{ + "type": "connect_leaf", + "service": p.ServiceName, + }) + if err != nil { + return nil, nil, fmt.Errorf("failed to create leaf cert watcher plan: %w", err) + } + + rootWatcher, err := watch.Parse(map[string]interface{}{ + "type": "connect_roots", + }) + if err != nil { + return nil, nil, fmt.Errorf("failed to create root cert watcher plan: %w", err) + } + + return leafWatcher, rootWatcher, nil +} + +// watchConnectTLS watches for updates of the root certificate or the leaf +// certificate, and transmits them to the caller via p.certChan. Any error is also +// propagated up through p.certChan, in connectCert.err. +func (p *Provider) watchConnectTLS(ctx context.Context, leafWatcher *watch.Plan, rootWatcher *watch.Plan) { + ctxLog := log.With(ctx, log.Str(log.ProviderName, "consulcatalog")) + logger := log.FromContext(ctxLog) + + leafChan := make(chan keyPair) + rootChan := make(chan []string) + + leafWatcher.HybridHandler = leafWatcherHandler(ctx, leafChan) + rootWatcher.HybridHandler = rootsWatchHandler(ctx, rootChan) + + logOpts := &hclog.LoggerOptions{ + Name: "consulcatalog", + Level: hclog.LevelFromString(logrus.GetLevel().String()), + JSONFormat: true, + } + + hclogger := hclog.New(logOpts) + + go func() { + err := leafWatcher.RunWithClientAndHclog(p.client, hclogger) + if err != nil { + p.certChan <- &connectCert{err: err} + } + }() + + go func() { + err := rootWatcher.RunWithClientAndHclog(p.client, hclogger) + if err != nil { + p.certChan <- &connectCert{err: err} + } + }() + + var ( + certInfo *connectCert + leafCerts keyPair + rootCerts []string + ) + + for { + select { + case <-ctx.Done(): + leafWatcher.Stop() + rootWatcher.Stop() + return + case rootCerts = <-rootChan: + case leafCerts = <-leafChan: + } + newCertInfo := &connectCert{ + root: rootCerts, + leaf: leafCerts, + } + if newCertInfo.isReady() && !newCertInfo.equals(certInfo) { + logger.Debugf("Updating connect certs for service %s", p.ServiceName) + certInfo = newCertInfo + p.certChan <- newCertInfo } } - return false } func createClient(cfg *EndpointConfig) (*api.Client, error) { diff --git a/pkg/provider/consulcatalog/label.go b/pkg/provider/consulcatalog/label.go index 45e2ebc32..40e67920a 100644 --- a/pkg/provider/consulcatalog/label.go +++ b/pkg/provider/consulcatalog/label.go @@ -6,15 +6,21 @@ import ( // configuration Contains information from the labels that are globals (not related to the dynamic configuration) or specific to the provider. type configuration struct { - Enable bool + Enable bool + ConsulCatalog specificConfiguration } -func (p *Provider) getConfiguration(item itemData) (configuration, error) { +type specificConfiguration struct { + Connect bool +} + +func (p *Provider) getConfiguration(labels map[string]string) (configuration, error) { conf := configuration{ - Enable: p.ExposedByDefault, + Enable: p.ExposedByDefault, + ConsulCatalog: specificConfiguration{Connect: p.ConnectByDefault}, } - err := label.Decode(item.Labels, &conf, "traefik.consulcatalog.", "traefik.enable") + err := label.Decode(labels, &conf, "traefik.consulcatalog.", "traefik.enable") if err != nil { return configuration{}, err } diff --git a/pkg/provider/docker/config_test.go b/pkg/provider/docker/config_test.go index 843c8735a..c02e31683 100644 --- a/pkg/provider/docker/config_test.go +++ b/pkg/provider/docker/config_test.go @@ -74,6 +74,7 @@ func TestDefaultRule(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -128,6 +129,7 @@ func TestDefaultRule(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -184,6 +186,7 @@ func TestDefaultRule(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -233,6 +236,7 @@ func TestDefaultRule(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -282,6 +286,7 @@ func TestDefaultRule(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -336,6 +341,7 @@ func TestDefaultRule(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -408,9 +414,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -447,9 +454,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -486,9 +494,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -542,6 +551,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -625,6 +635,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -699,6 +710,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -754,6 +766,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -811,6 +824,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -866,6 +880,7 @@ func Test_buildConfiguration(t *testing.T) { Rule: "Host(`foo.com`)", }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -922,6 +937,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -984,6 +1000,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1040,6 +1057,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1102,8 +1120,9 @@ func Test_buildConfiguration(t *testing.T) { Rule: "Host(`Test.traefik.wtf`)", }, }, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1185,8 +1204,9 @@ func Test_buildConfiguration(t *testing.T) { Rule: "Host(`Test.traefik.wtf`)", }, }, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1265,6 +1285,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1326,6 +1347,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1410,6 +1432,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1488,6 +1511,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1588,6 +1612,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1661,6 +1686,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1756,6 +1782,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1834,6 +1861,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1912,6 +1940,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1967,6 +1996,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2023,6 +2053,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2084,6 +2115,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2116,9 +2148,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2153,9 +2186,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2192,9 +2226,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2230,9 +2265,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2270,9 +2306,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2329,6 +2366,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2395,6 +2433,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2456,9 +2495,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2513,9 +2553,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2567,9 +2608,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2617,9 +2659,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2677,9 +2720,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2732,9 +2776,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2834,6 +2879,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2880,9 +2926,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2931,9 +2978,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2997,6 +3045,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, diff --git a/pkg/provider/ecs/config_test.go b/pkg/provider/ecs/config_test.go index d627598a6..a34a53766 100644 --- a/pkg/provider/ecs/config_test.go +++ b/pkg/provider/ecs/config_test.go @@ -67,6 +67,7 @@ func TestDefaultRule(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -116,6 +117,7 @@ func TestDefaultRule(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -167,6 +169,7 @@ func TestDefaultRule(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -211,6 +214,7 @@ func TestDefaultRule(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -255,6 +259,7 @@ func TestDefaultRule(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -304,6 +309,7 @@ func TestDefaultRule(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -371,9 +377,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -405,9 +412,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -439,9 +447,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -490,6 +499,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -563,6 +573,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -627,6 +638,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -677,6 +689,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -729,6 +742,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -779,6 +793,7 @@ func Test_buildConfiguration(t *testing.T) { Rule: "Host(`foo.com`)", }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -830,6 +845,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -887,6 +903,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -938,6 +955,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -990,8 +1008,9 @@ func Test_buildConfiguration(t *testing.T) { Rule: "Host(`Test.traefik.wtf`)", }, }, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1058,8 +1077,9 @@ func Test_buildConfiguration(t *testing.T) { Rule: "Host(`Test.traefik.wtf`)", }, }, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1128,6 +1148,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1184,6 +1205,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1258,6 +1280,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1326,6 +1349,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1411,6 +1435,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1474,6 +1499,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1554,6 +1580,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1623,6 +1650,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1691,6 +1719,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1741,6 +1770,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1792,6 +1822,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1843,6 +1874,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1913,6 +1945,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1969,6 +2002,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1996,9 +2030,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2028,9 +2063,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2062,9 +2098,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2097,9 +2134,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2131,9 +2169,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2166,9 +2205,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2220,6 +2260,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2281,6 +2322,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2337,9 +2379,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2389,9 +2432,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2438,9 +2482,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2483,9 +2528,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2538,9 +2584,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2588,9 +2635,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2680,6 +2728,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2721,9 +2770,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2767,9 +2817,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, diff --git a/pkg/provider/file/file.go b/pkg/provider/file/file.go index c8f915d1d..08ee0896d 100644 --- a/pkg/provider/file/file.go +++ b/pkg/provider/file/file.go @@ -167,6 +167,90 @@ func (p *Provider) loadFileConfig(ctx context.Context, filename string, parseTem if configuration.TLS != nil { configuration.TLS.Certificates = flattenCertificates(ctx, configuration.TLS) + + // TLS Options + if configuration.TLS.Options != nil { + for name, options := range configuration.TLS.Options { + var caCerts []tls.FileOrContent + + for _, caFile := range options.ClientAuth.CAFiles { + content, err := caFile.Read() + if err != nil { + log.FromContext(ctx).Error(err) + continue + } + + caCerts = append(caCerts, tls.FileOrContent(content)) + } + options.ClientAuth.CAFiles = caCerts + + configuration.TLS.Options[name] = options + } + } + + // TLS stores + if len(configuration.TLS.Stores) > 0 { + for name, store := range configuration.TLS.Stores { + if store.DefaultCertificate == nil { + continue + } + + content, err := store.DefaultCertificate.CertFile.Read() + if err != nil { + log.FromContext(ctx).Error(err) + continue + } + store.DefaultCertificate.CertFile = tls.FileOrContent(content) + + content, err = store.DefaultCertificate.KeyFile.Read() + if err != nil { + log.FromContext(ctx).Error(err) + continue + } + store.DefaultCertificate.KeyFile = tls.FileOrContent(content) + + configuration.TLS.Stores[name] = store + } + } + } + + // ServersTransport + if configuration.HTTP != nil && len(configuration.HTTP.ServersTransports) > 0 { + for name, st := range configuration.HTTP.ServersTransports { + var certificates []tls.Certificate + for _, cert := range st.Certificates { + content, err := cert.CertFile.Read() + if err != nil { + log.FromContext(ctx).Error(err) + continue + } + cert.CertFile = tls.FileOrContent(content) + + content, err = cert.KeyFile.Read() + if err != nil { + log.FromContext(ctx).Error(err) + continue + } + cert.KeyFile = tls.FileOrContent(content) + + certificates = append(certificates, cert) + } + + configuration.HTTP.ServersTransports[name].Certificates = certificates + + var rootCAs []tls.FileOrContent + for _, rootCA := range st.RootCAs { + content, err := rootCA.Read() + if err != nil { + log.FromContext(ctx).Error(err) + continue + } + + rootCAs = append(rootCAs, tls.FileOrContent(content)) + } + + st.RootCAs = rootCAs + } } return configuration, nil diff --git a/pkg/provider/file/file_test.go b/pkg/provider/file/file_test.go index 6dde44321..76fb49282 100644 --- a/pkg/provider/file/file_test.go +++ b/pkg/provider/file/file_test.go @@ -25,19 +25,35 @@ type ProvideTestCase struct { expectedNumTLSOptions int } -func TestTLSContent(t *testing.T) { +func TestTLSCertificateContent(t *testing.T) { tempDir := t.TempDir() fileTLS, err := createTempFile("./fixtures/toml/tls_file.cert", tempDir) require.NoError(t, err) + fileTLSKey, err := createTempFile("./fixtures/toml/tls_file_key.cert", tempDir) + require.NoError(t, err) + fileConfig, err := os.CreateTemp(tempDir, "temp*.toml") require.NoError(t, err) content := ` [[tls.certificates]] certFile = "` + fileTLS.Name() + `" - keyFile = "` + fileTLS.Name() + `" + keyFile = "` + fileTLSKey.Name() + `" + +[tls.options.default.clientAuth] + caFiles = ["` + fileTLS.Name() + `"] + +[tls.stores.default.defaultCertificate] + certFile = "` + fileTLS.Name() + `" + keyFile = "` + fileTLSKey.Name() + `" + +[http.serversTransports.default] + rootCAs = ["` + fileTLS.Name() + `"] + [[http.serversTransports.default.certificates]] + certFile = "` + fileTLS.Name() + `" + keyFile = "` + fileTLSKey.Name() + `" ` _, err = fileConfig.Write([]byte(content)) @@ -48,7 +64,16 @@ func TestTLSContent(t *testing.T) { require.NoError(t, err) require.Equal(t, "CONTENT", configuration.TLS.Certificates[0].Certificate.CertFile.String()) - require.Equal(t, "CONTENT", configuration.TLS.Certificates[0].Certificate.KeyFile.String()) + require.Equal(t, "CONTENTKEY", configuration.TLS.Certificates[0].Certificate.KeyFile.String()) + + require.Equal(t, "CONTENT", configuration.TLS.Options["default"].ClientAuth.CAFiles[0].String()) + + require.Equal(t, "CONTENT", configuration.TLS.Stores["default"].DefaultCertificate.CertFile.String()) + require.Equal(t, "CONTENTKEY", configuration.TLS.Stores["default"].DefaultCertificate.KeyFile.String()) + + require.Equal(t, "CONTENT", configuration.HTTP.ServersTransports["default"].Certificates[0].CertFile.String()) + require.Equal(t, "CONTENTKEY", configuration.HTTP.ServersTransports["default"].Certificates[0].KeyFile.String()) + require.Equal(t, "CONTENT", configuration.HTTP.ServersTransports["default"].RootCAs[0].String()) } func TestErrorWhenEmptyConfig(t *testing.T) { @@ -237,6 +262,13 @@ func getTestCases() []ProvideTestCase { expectedNumRouter: 20, expectedNumService: 20, }, + { + desc: "simple file with empty store yaml", + filePath: "./fixtures/yaml/simple_empty_store.yml", + expectedNumRouter: 0, + expectedNumService: 0, + expectedNumTLSConf: 0, + }, } } diff --git a/pkg/provider/file/fixtures/toml/tls_file_key.cert b/pkg/provider/file/fixtures/toml/tls_file_key.cert new file mode 100644 index 000000000..f196b32ee --- /dev/null +++ b/pkg/provider/file/fixtures/toml/tls_file_key.cert @@ -0,0 +1 @@ +CONTENTKEY \ No newline at end of file diff --git a/pkg/provider/file/fixtures/yaml/simple_empty_store.yml b/pkg/provider/file/fixtures/yaml/simple_empty_store.yml new file mode 100644 index 000000000..b9c8e3242 --- /dev/null +++ b/pkg/provider/file/fixtures/yaml/simple_empty_store.yml @@ -0,0 +1,3 @@ +tls: + stores: + default: {} diff --git a/pkg/provider/kubernetes/crd/fixtures/udp/with_externalname_service.yml b/pkg/provider/kubernetes/crd/fixtures/udp/with_externalname_service.yml new file mode 100644 index 000000000..962178ed5 --- /dev/null +++ b/pkg/provider/kubernetes/crd/fixtures/udp/with_externalname_service.yml @@ -0,0 +1,14 @@ +apiVersion: traefik.containo.us/v1alpha1 +kind: IngressRouteUDP +metadata: + name: test.route + namespace: default + +spec: + entryPoints: + - foo + + routes: + - services: + - name: external.service.with.port + port: 80 diff --git a/pkg/provider/kubernetes/crd/kubernetes.go b/pkg/provider/kubernetes/crd/kubernetes.go index cd55a18a7..dcb37ae8a 100644 --- a/pkg/provider/kubernetes/crd/kubernetes.go +++ b/pkg/provider/kubernetes/crd/kubernetes.go @@ -41,20 +41,16 @@ const ( // Provider holds configurations of the provider. type Provider struct { - Endpoint string `description:"Kubernetes server endpoint (required for external cluster client)." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"` - Token string `description:"Kubernetes bearer token (not needed for in-cluster client)." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty"` - CertAuthFilePath string `description:"Kubernetes certificate authority file path (not needed for in-cluster client)." json:"certAuthFilePath,omitempty" toml:"certAuthFilePath,omitempty" yaml:"certAuthFilePath,omitempty"` - Namespaces []string `description:"Kubernetes namespaces." json:"namespaces,omitempty" toml:"namespaces,omitempty" yaml:"namespaces,omitempty" export:"true"` - AllowCrossNamespace *bool `description:"Allow cross namespace resource reference." json:"allowCrossNamespace,omitempty" toml:"allowCrossNamespace,omitempty" yaml:"allowCrossNamespace,omitempty" export:"true"` - LabelSelector string `description:"Kubernetes label selector to use." json:"labelSelector,omitempty" toml:"labelSelector,omitempty" yaml:"labelSelector,omitempty" export:"true"` - IngressClass string `description:"Value of kubernetes.io/ingress.class annotation to watch for." json:"ingressClass,omitempty" toml:"ingressClass,omitempty" yaml:"ingressClass,omitempty" export:"true"` - ThrottleDuration ptypes.Duration `description:"Ingress refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty" export:"true"` - lastConfiguration safe.Safe -} - -// SetDefaults sets the default values. -func (p *Provider) SetDefaults() { - p.AllowCrossNamespace = func(b bool) *bool { return &b }(true) + Endpoint string `description:"Kubernetes server endpoint (required for external cluster client)." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"` + Token string `description:"Kubernetes bearer token (not needed for in-cluster client)." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty"` + CertAuthFilePath string `description:"Kubernetes certificate authority file path (not needed for in-cluster client)." json:"certAuthFilePath,omitempty" toml:"certAuthFilePath,omitempty" yaml:"certAuthFilePath,omitempty"` + Namespaces []string `description:"Kubernetes namespaces." json:"namespaces,omitempty" toml:"namespaces,omitempty" yaml:"namespaces,omitempty" export:"true"` + AllowCrossNamespace bool `description:"Allow cross namespace resource reference." json:"allowCrossNamespace,omitempty" toml:"allowCrossNamespace,omitempty" yaml:"allowCrossNamespace,omitempty" export:"true"` + AllowExternalNameServices bool `description:"Allow ExternalName services." json:"allowExternalNameServices,omitempty" toml:"allowExternalNameServices,omitempty" yaml:"allowExternalNameServices,omitempty" export:"true"` + LabelSelector string `description:"Kubernetes label selector to use." json:"labelSelector,omitempty" toml:"labelSelector,omitempty" yaml:"labelSelector,omitempty" export:"true"` + IngressClass string `description:"Value of kubernetes.io/ingress.class annotation to watch for." json:"ingressClass,omitempty" toml:"ingressClass,omitempty" yaml:"ingressClass,omitempty" export:"true"` + ThrottleDuration ptypes.Duration `description:"Ingress refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty" export:"true"` + lastConfiguration safe.Safe } func (p *Provider) newK8sClient(ctx context.Context) (*clientWrapper, error) { @@ -106,10 +102,14 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe. return err } - if p.AllowCrossNamespace == nil || *p.AllowCrossNamespace { + if p.AllowCrossNamespace { logger.Warn("Cross-namespace reference between IngressRoutes and resources is enabled, please ensure that this is expected (see AllowCrossNamespace option)") } + if p.AllowExternalNameServices { + logger.Warn("ExternalName service loading is enabled, please ensure that this is expected (see AllowExternalNameServices option)") + } + pool.GoCtx(func(ctxPool context.Context) { operation := func() error { eventsChan, err := k8sClient.WatchAll(p.Namespaces, ctxPool.Done()) @@ -274,7 +274,7 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client) } } - cb := configBuilder{client, p.AllowCrossNamespace} + cb := configBuilder{client: client, allowCrossNamespace: p.AllowCrossNamespace, allowExternalNameServices: p.AllowExternalNameServices} for _, service := range client.GetTraefikServices() { err := cb.buildTraefikService(ctx, service, conf.HTTP.Services) @@ -450,7 +450,7 @@ func (p *Provider) createErrorPageMiddleware(client Client, namespace string, er Query: errorPage.Query, } - balancerServerHTTP, err := configBuilder{client, p.AllowCrossNamespace}.buildServersLB(namespace, errorPage.Service.LoadBalancerSpec) + balancerServerHTTP, err := configBuilder{client: client, allowCrossNamespace: p.AllowCrossNamespace, allowExternalNameServices: p.AllowExternalNameServices}.buildServersLB(namespace, errorPage.Service.LoadBalancerSpec) if err != nil { return nil, nil, err } @@ -919,7 +919,7 @@ func throttleEvents(ctx context.Context, throttleDuration time.Duration, pool *s return eventsChanBuffered } -func isNamespaceAllowed(allowCrossNamespace *bool, parentNamespace, namespace string) bool { +func isNamespaceAllowed(allowCrossNamespace bool, parentNamespace, namespace string) bool { // If allowCrossNamespace option is not defined the default behavior is to allow cross namespace references. - return allowCrossNamespace == nil || *allowCrossNamespace || parentNamespace == namespace + return allowCrossNamespace || parentNamespace == namespace } diff --git a/pkg/provider/kubernetes/crd/kubernetes_http.go b/pkg/provider/kubernetes/crd/kubernetes_http.go index 79f848035..3f969d7e8 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_http.go +++ b/pkg/provider/kubernetes/crd/kubernetes_http.go @@ -50,7 +50,7 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli ingressName = ingressRoute.GenerateName } - cb := configBuilder{client, p.AllowCrossNamespace} + cb := configBuilder{client: client, allowCrossNamespace: p.AllowCrossNamespace, allowExternalNameServices: p.AllowExternalNameServices} for _, route := range ingressRoute.Spec.Routes { if route.Kind != "Rule" { @@ -173,8 +173,9 @@ func (p *Provider) makeMiddlewareKeys(ctx context.Context, ingRouteNamespace str } type configBuilder struct { - client Client - allowCrossNamespace *bool + client Client + allowCrossNamespace bool + allowExternalNameServices bool } // buildTraefikService creates the configuration for the traefik service defined in tService, @@ -323,6 +324,10 @@ func (c configBuilder) loadServers(parentNamespace string, svc v1alpha1.LoadBala var servers []dynamic.Server if service.Spec.Type == corev1.ServiceTypeExternalName { + if !c.allowExternalNameServices { + return nil, fmt.Errorf("externalName services not allowed: %s/%s", namespace, sanitizedName) + } + protocol, err := parseServiceProtocol(svc.Scheme, svcPort.Name, svcPort.Port) if err != nil { return nil, err diff --git a/pkg/provider/kubernetes/crd/kubernetes_tcp.go b/pkg/provider/kubernetes/crd/kubernetes_tcp.go index e283ecbdf..181baa7d5 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_tcp.go +++ b/pkg/provider/kubernetes/crd/kubernetes_tcp.go @@ -172,7 +172,7 @@ func (p *Provider) createLoadBalancerServerTCP(client Client, parentNamespace st ns = service.Namespace } - servers, err := loadTCPServers(client, ns, service) + servers, err := p.loadTCPServers(client, ns, service) if err != nil { return nil, err } @@ -199,7 +199,7 @@ func (p *Provider) createLoadBalancerServerTCP(client Client, parentNamespace st return tcpService, nil } -func loadTCPServers(client Client, namespace string, svc v1alpha1.ServiceTCP) ([]dynamic.TCPServer, error) { +func (p *Provider) loadTCPServers(client Client, namespace string, svc v1alpha1.ServiceTCP) ([]dynamic.TCPServer, error) { service, exists, err := client.GetService(namespace, svc.Name) if err != nil { return nil, err @@ -209,6 +209,10 @@ func loadTCPServers(client Client, namespace string, svc v1alpha1.ServiceTCP) ([ return nil, errors.New("service not found") } + if service.Spec.Type == corev1.ServiceTypeExternalName && !p.AllowExternalNameServices { + return nil, fmt.Errorf("externalName services not allowed: %s/%s", namespace, svc.Name) + } + svcPort, err := getServicePort(service, svc.Port) if err != nil { return nil, err diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go index aea40d19c..504e317da 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_test.go +++ b/pkg/provider/kubernetes/crd/kubernetes_test.go @@ -47,10 +47,10 @@ func TestLoadIngressRouteTCPs(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -64,10 +64,10 @@ func TestLoadIngressRouteTCPs(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ @@ -105,10 +105,10 @@ func TestLoadIngressRouteTCPs(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ @@ -158,10 +158,10 @@ func TestLoadIngressRouteTCPs(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ @@ -252,10 +252,10 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -293,10 +293,10 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -360,10 +360,10 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -443,10 +443,10 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -466,10 +466,10 @@ func TestLoadIngressRouteTCPs(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -488,10 +488,10 @@ func TestLoadIngressRouteTCPs(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -540,10 +540,10 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -583,10 +583,10 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -647,10 +647,10 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -709,10 +709,10 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -770,10 +770,10 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -820,10 +820,10 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -870,10 +870,10 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -911,10 +911,10 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -954,10 +954,10 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1005,10 +1005,10 @@ func TestLoadIngressRouteTCPs(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1042,10 +1042,10 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -1080,10 +1080,10 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -1108,10 +1108,10 @@ func TestLoadIngressRouteTCPs(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -1191,7 +1191,6 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-route-6b204d94623b3df4370c": { EntryPoints: []string{"foo"}, @@ -1236,6 +1235,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -1275,10 +1275,10 @@ func TestLoadIngressRouteTCPs(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1294,8 +1294,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) { return } - p := Provider{IngressClass: test.ingressClass} - p.SetDefaults() + p := Provider{IngressClass: test.ingressClass, AllowCrossNamespace: true, AllowExternalNameServices: true} clientMock := newClientMock(test.paths...) conf := p.loadConfigurationFromCRD(context.Background(), clientMock) @@ -1324,10 +1323,10 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -1346,7 +1345,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-route-6b204d94623b3df4370c": { EntryPoints: []string{"foo"}, @@ -1371,6 +1369,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -1389,7 +1388,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test2-route-23c7f4c450289ee29016": { EntryPoints: []string{"web"}, @@ -1426,6 +1424,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -1445,7 +1444,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test2-route-23c7f4c450289ee29016": { EntryPoints: []string{"web"}, @@ -1482,6 +1480,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1499,7 +1498,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-route-6b204d94623b3df4370c": { EntryPoints: []string{"web"}, @@ -1543,6 +1541,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -1562,7 +1561,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-route-77c62dfe9517144aeeaa": { EntryPoints: []string{"web"}, @@ -1614,6 +1612,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1632,7 +1631,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-route-77c62dfe9517144aeeaa": { EntryPoints: []string{"web"}, @@ -1667,6 +1665,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1685,9 +1684,8 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ "default-wrr1": { Weighted: &dynamic.WeightedRoundRobin{ @@ -1713,6 +1711,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1731,7 +1730,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-route-77c62dfe9517144aeeaa": { EntryPoints: []string{"web"}, @@ -1837,6 +1835,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1855,7 +1854,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-route-77c62dfe9517144aeeaa": { EntryPoints: []string{"web"}, @@ -1904,6 +1902,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1922,7 +1921,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-route-77c62dfe9517144aeeaa": { EntryPoints: []string{"web"}, @@ -1996,6 +1994,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2014,7 +2013,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-route-77c62dfe9517144aeeaa": { EntryPoints: []string{"web"}, @@ -2174,6 +2172,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2192,7 +2191,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-route-77c62dfe9517144aeeaa": { EntryPoints: []string{"web"}, @@ -2238,6 +2236,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2256,7 +2255,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-route-77c62dfe9517144aeeaa": { EntryPoints: []string{"web"}, @@ -2322,6 +2320,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2340,7 +2339,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-route-77c62dfe9517144aeeaa": { EntryPoints: []string{"web"}, @@ -2392,6 +2390,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2411,10 +2410,10 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2433,10 +2432,10 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2455,10 +2454,10 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2486,7 +2485,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-route-6b204d94623b3df4370c": { EntryPoints: []string{"web"}, @@ -2512,6 +2510,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2549,7 +2548,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-route-6b204d94623b3df4370c": { EntryPoints: []string{"web"}, @@ -2577,6 +2575,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2597,7 +2596,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-route-6b204d94623b3df4370c": { EntryPoints: []string{"web"}, @@ -2625,6 +2623,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2662,7 +2661,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-route-6b204d94623b3df4370c": { EntryPoints: []string{"web"}, @@ -2690,6 +2688,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2726,7 +2725,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-route-6b204d94623b3df4370c": { EntryPoints: []string{"web"}, @@ -2754,6 +2752,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2789,7 +2788,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-route-6b204d94623b3df4370c": { EntryPoints: []string{"web"}, @@ -2817,6 +2815,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2841,7 +2840,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-route-6b204d94623b3df4370c": { EntryPoints: []string{"web"}, @@ -2869,6 +2867,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2893,7 +2892,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-route-6b204d94623b3df4370c": { EntryPoints: []string{"web"}, @@ -2921,6 +2919,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2939,7 +2938,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-route-6b204d94623b3df4370c": { EntryPoints: []string{"web"}, @@ -2965,6 +2963,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -2983,7 +2982,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-route-6b204d94623b3df4370c": { EntryPoints: []string{"foo"}, @@ -3008,6 +3006,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -3026,7 +3025,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-route-6b204d94623b3df4370c": { EntryPoints: []string{"foo"}, @@ -3051,6 +3049,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -3069,8 +3068,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, - Routers: map[string]*dynamic.Router{}, + Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{ "default-basicauth": { BasicAuth: &dynamic.BasicAuth{ @@ -3093,7 +3091,8 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, - Services: map[string]*dynamic.Service{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -3112,8 +3111,7 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, - Routers: map[string]*dynamic.Router{}, + Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{ "default-errorpage": { Errors: &dynamic.ErrorPage{ @@ -3138,6 +3136,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -3155,7 +3154,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-route-6b204d94623b3df4370c": { EntryPoints: []string{"foo"}, @@ -3181,6 +3179,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -3209,7 +3208,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-route-6b204d94623b3df4370c": { EntryPoints: []string{"web"}, @@ -3235,6 +3233,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -3255,7 +3254,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-route-6b204d94623b3df4370c": { EntryPoints: []string{"web"}, @@ -3281,6 +3279,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -3301,7 +3300,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-route-6f97418635c7e18853da": { EntryPoints: []string{"foo"}, @@ -3322,6 +3320,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -3340,7 +3339,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-route-6f97418635c7e18853da": { EntryPoints: []string{"foo"}, @@ -3361,6 +3359,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -3379,7 +3378,6 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-route-6f97418635c7e18853da": { EntryPoints: []string{"foo"}, @@ -3400,6 +3398,7 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -3418,10 +3417,10 @@ func TestLoadIngressRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -3476,8 +3475,7 @@ func TestLoadIngressRoutes(t *testing.T) { return } - p := Provider{IngressClass: test.ingressClass} - p.SetDefaults() + p := Provider{IngressClass: test.ingressClass, AllowCrossNamespace: true, AllowExternalNameServices: true} clientMock := newClientMock(test.paths...) conf := p.loadConfigurationFromCRD(context.Background(), clientMock) @@ -3506,10 +3504,10 @@ func TestLoadIngressRouteUDPs(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -3541,10 +3539,10 @@ func TestLoadIngressRouteUDPs(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{}, @@ -3602,10 +3600,10 @@ func TestLoadIngressRouteUDPs(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -3668,10 +3666,10 @@ func TestLoadIngressRouteUDPs(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -3750,10 +3748,10 @@ func TestLoadIngressRouteUDPs(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -3787,10 +3785,10 @@ func TestLoadIngressRouteUDPs(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -3824,10 +3822,10 @@ func TestLoadIngressRouteUDPs(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -3851,10 +3849,10 @@ func TestLoadIngressRouteUDPs(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -3874,10 +3872,10 @@ func TestLoadIngressRouteUDPs(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -3894,8 +3892,7 @@ func TestLoadIngressRouteUDPs(t *testing.T) { return } - p := Provider{IngressClass: test.ingressClass} - p.SetDefaults() + p := Provider{IngressClass: test.ingressClass, AllowCrossNamespace: true, AllowExternalNameServices: true} clientMock := newClientMock(test.paths...) conf := p.loadConfigurationFromCRD(context.Background(), clientMock) @@ -4247,7 +4244,6 @@ func TestCrossNamespace(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-crossnamespace-route-9313b71dbe6a649d5049": { EntryPoints: []string{"foo"}, @@ -4280,6 +4276,7 @@ func TestCrossNamespace(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -4299,7 +4296,6 @@ func TestCrossNamespace(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-test-crossnamespace-route-6b204d94623b3df4370c": { EntryPoints: []string{"foo"}, @@ -4374,6 +4370,7 @@ func TestCrossNamespace(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -4393,7 +4390,6 @@ func TestCrossNamespace(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{ "default-cross-ns-route-6b204d94623b3df4370c": { EntryPoints: []string{"foo"}, @@ -4499,6 +4495,7 @@ func TestCrossNamespace(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -4517,9 +4514,8 @@ func TestCrossNamespace(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ "cross-ns-tr-svc-wrr2": { Weighted: &dynamic.WeightedRoundRobin{ @@ -4569,6 +4565,7 @@ func TestCrossNamespace(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -4618,10 +4615,10 @@ func TestCrossNamespace(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -4690,10 +4687,10 @@ func TestCrossNamespace(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -4708,10 +4705,10 @@ func TestCrossNamespace(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{ @@ -4761,10 +4758,10 @@ func TestCrossNamespace(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -4797,10 +4794,10 @@ func TestCrossNamespace(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TCP: &dynamic.TCPConfiguration{ Routers: map[string]*dynamic.TCPRouter{}, @@ -4830,10 +4827,10 @@ func TestCrossNamespace(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, Routers: map[string]*dynamic.Router{}, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -4895,10 +4892,299 @@ func TestCrossNamespace(t *testing.T) { <-eventCh } - p := Provider{} - p.SetDefaults() + p := Provider{AllowCrossNamespace: test.allowCrossNamespace} + + conf := p.loadConfigurationFromCRD(context.Background(), client) + assert.Equal(t, test.expected, conf) + }) + } +} + +func TestExternalNameService(t *testing.T) { + testCases := []struct { + desc string + allowExternalNameService bool + ingressClass string + paths []string + expected *dynamic.Configuration + }{ + { + desc: "Empty", + expected: &dynamic.Configuration{ + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + ServersTransports: map[string]*dynamic.ServersTransport{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + }, + TLS: &dynamic.TLSConfiguration{}, + }, + }, + { + desc: "HTTP ExternalName services allowed", + paths: []string{"services.yml", "with_externalname_with_http.yml"}, + allowExternalNameService: true, + expected: &dynamic.Configuration{ + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + ServersTransports: map[string]*dynamic.ServersTransport{}, + Routers: map[string]*dynamic.Router{ + "default-test-route-6f97418635c7e18853da": { + EntryPoints: []string{"foo"}, + Service: "default-test-route-6f97418635c7e18853da", + Rule: "Host(`foo.com`)", + Priority: 0, + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "default-test-route-6f97418635c7e18853da": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://external.domain:80", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + }, + TLS: &dynamic.TLSConfiguration{}, + }, + }, + { + desc: "HTTP Externalname services disallowed", + paths: []string{"services.yml", "with_externalname_with_http.yml"}, + expected: &dynamic.Configuration{ + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + ServersTransports: map[string]*dynamic.ServersTransport{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + }, + TLS: &dynamic.TLSConfiguration{}, + }, + }, + { + desc: "TCP ExternalName services allowed", + paths: []string{"tcp/services.yml", "tcp/with_externalname_with_port.yml"}, + allowExternalNameService: true, + expected: &dynamic.Configuration{ + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + ServersTransports: map[string]*dynamic.ServersTransport{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + }, + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{ + "default-test.route-fdd3e9338e47a45efefc": { + EntryPoints: []string{"foo"}, + Service: "default-test.route-fdd3e9338e47a45efefc", + Rule: "HostSNI(`foo.com`)", + }, + }, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{ + "default-test.route-fdd3e9338e47a45efefc": { + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "external.domain:80", + Port: "", + }, + }, + }, + }, + }, + }, + TLS: &dynamic.TLSConfiguration{}, + }, + }, + { + desc: "TCP ExternalName services disallowed", + paths: []string{"tcp/services.yml", "tcp/with_externalname_with_port.yml"}, + expected: &dynamic.Configuration{ + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + TCP: &dynamic.TCPConfiguration{ + // The router that references the invalid service will be discarded. + Routers: map[string]*dynamic.TCPRouter{ + "default-test.route-fdd3e9338e47a45efefc": { + EntryPoints: []string{"foo"}, + Service: "default-test.route-fdd3e9338e47a45efefc", + Rule: "HostSNI(`foo.com`)", + }, + }, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + ServersTransports: map[string]*dynamic.ServersTransport{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + }, + TLS: &dynamic.TLSConfiguration{}, + }, + }, + { + desc: "UDP ExternalName services allowed", + paths: []string{"udp/services.yml", "udp/with_externalname_service.yml"}, + allowExternalNameService: true, + expected: &dynamic.Configuration{ + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{ + "default-test.route-0": { + EntryPoints: []string{"foo"}, + Service: "default-test.route-0", + }, + }, + Services: map[string]*dynamic.UDPService{ + "default-test.route-0": { + LoadBalancer: &dynamic.UDPServersLoadBalancer{ + Servers: []dynamic.UDPServer{ + { + Address: "external.domain:80", + Port: "", + }, + }, + }, + }, + }, + }, + HTTP: &dynamic.HTTPConfiguration{ + ServersTransports: map[string]*dynamic.ServersTransport{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + }, + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + }, + TLS: &dynamic.TLSConfiguration{}, + }, + }, + { + desc: "UDP ExternalName service disallowed", + paths: []string{"udp/services.yml", "udp/with_externalname_service.yml"}, + expected: &dynamic.Configuration{ + // The router that references the invalid service will be discarded. + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{ + "default-test.route-0": { + EntryPoints: []string{"foo"}, + Service: "default-test.route-0", + }, + }, + Services: map[string]*dynamic.UDPService{}, + }, + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + ServersTransports: map[string]*dynamic.ServersTransport{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + }, + TLS: &dynamic.TLSConfiguration{}, + }, + }, + } + + for _, test := range testCases { + test := test + + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + var k8sObjects []runtime.Object + var crdObjects []runtime.Object + for _, path := range test.paths { + yamlContent, err := os.ReadFile(filepath.FromSlash("./fixtures/" + path)) + if err != nil { + panic(err) + } + + objects := k8s.MustParseYaml(yamlContent) + for _, obj := range objects { + switch o := obj.(type) { + case *corev1.Service, *corev1.Endpoints, *corev1.Secret: + k8sObjects = append(k8sObjects, o) + case *v1alpha1.IngressRoute: + crdObjects = append(crdObjects, o) + case *v1alpha1.IngressRouteTCP: + crdObjects = append(crdObjects, o) + case *v1alpha1.IngressRouteUDP: + crdObjects = append(crdObjects, o) + case *v1alpha1.Middleware: + crdObjects = append(crdObjects, o) + case *v1alpha1.TraefikService: + crdObjects = append(crdObjects, o) + case *v1alpha1.TLSOption: + crdObjects = append(crdObjects, o) + case *v1alpha1.TLSStore: + crdObjects = append(crdObjects, o) + default: + } + } + } + + kubeClient := kubefake.NewSimpleClientset(k8sObjects...) + crdClient := crdfake.NewSimpleClientset(crdObjects...) + + client := newClientImpl(kubeClient, crdClient) + + stopCh := make(chan struct{}) + + eventCh, err := client.WatchAll([]string{"default", "cross-ns"}, stopCh) + require.NoError(t, err) + + if k8sObjects != nil || crdObjects != nil { + // just wait for the first event + <-eventCh + } + + p := Provider{AllowExternalNameServices: test.allowExternalNameService} - p.AllowCrossNamespace = Bool(test.allowCrossNamespace) conf := p.loadConfigurationFromCRD(context.Background(), client) assert.Equal(t, test.expected, conf) }) diff --git a/pkg/provider/kubernetes/crd/kubernetes_udp.go b/pkg/provider/kubernetes/crd/kubernetes_udp.go index 808b6bff0..ad0c22dc6 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_udp.go +++ b/pkg/provider/kubernetes/crd/kubernetes_udp.go @@ -87,7 +87,7 @@ func (p *Provider) createLoadBalancerServerUDP(client Client, parentNamespace st ns = service.Namespace } - servers, err := loadUDPServers(client, ns, service) + servers, err := p.loadUDPServers(client, ns, service) if err != nil { return nil, err } @@ -101,7 +101,7 @@ func (p *Provider) createLoadBalancerServerUDP(client Client, parentNamespace st return udpService, nil } -func loadUDPServers(client Client, namespace string, svc v1alpha1.ServiceUDP) ([]dynamic.UDPServer, error) { +func (p *Provider) loadUDPServers(client Client, namespace string, svc v1alpha1.ServiceUDP) ([]dynamic.UDPServer, error) { service, exists, err := client.GetService(namespace, svc.Name) if err != nil { return nil, err @@ -111,6 +111,10 @@ func loadUDPServers(client Client, namespace string, svc v1alpha1.ServiceUDP) ([ return nil, errors.New("service not found") } + if service.Spec.Type == corev1.ServiceTypeExternalName && !p.AllowExternalNameServices { + return nil, fmt.Errorf("externalName services not allowed: %s/%s", namespace, svc.Name) + } + svcPort, err := getServicePort(service, svc.Port) if err != nil { return nil, err diff --git a/pkg/provider/kubernetes/crd/traefik/v1alpha1/serverstransport.go b/pkg/provider/kubernetes/crd/traefik/v1alpha1/serverstransport.go index dd8b00ff5..d30adf012 100644 --- a/pkg/provider/kubernetes/crd/traefik/v1alpha1/serverstransport.go +++ b/pkg/provider/kubernetes/crd/traefik/v1alpha1/serverstransport.go @@ -35,6 +35,8 @@ type ServersTransportSpec struct { ForwardingTimeouts *ForwardingTimeouts `json:"forwardingTimeouts,omitempty"` // Disable HTTP/2 for connections with backend servers. DisableHTTP2 bool `json:"disableHTTP2,omitempty"` + // URI used to match against SAN URI during the peer certificate verification. + PeerCertURI string `json:"peerCertURI,omitempty"` } // +k8s:deepcopy-gen=true diff --git a/pkg/provider/kubernetes/gateway/kubernetes.go b/pkg/provider/kubernetes/gateway/kubernetes.go index b3ff2a1c7..5a9bd0f9b 100644 --- a/pkg/provider/kubernetes/gateway/kubernetes.go +++ b/pkg/provider/kubernetes/gateway/kubernetes.go @@ -25,6 +25,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" + "k8s.io/utils/pointer" "sigs.k8s.io/gateway-api/apis/v1alpha1" ) @@ -62,7 +63,9 @@ func (p *Provider) newK8sClient(ctx context.Context) (*clientWrapper, error) { if err != nil { return nil, fmt.Errorf("invalid label selector: %q", p.LabelSelector) } - log.FromContext(ctx).Infof("label selector is: %q", p.LabelSelector) + + logger := log.FromContext(ctx) + logger.Infof("label selector is: %q", p.LabelSelector) withEndpoint := "" if p.Endpoint != "" { @@ -72,19 +75,20 @@ func (p *Provider) newK8sClient(ctx context.Context) (*clientWrapper, error) { var client *clientWrapper switch { case os.Getenv("KUBERNETES_SERVICE_HOST") != "" && os.Getenv("KUBERNETES_SERVICE_PORT") != "": - log.FromContext(ctx).Infof("Creating in-cluster Provider client%s", withEndpoint) + logger.Infof("Creating in-cluster Provider client%s", withEndpoint) client, err = newInClusterClient(p.Endpoint) case os.Getenv("KUBECONFIG") != "": - log.FromContext(ctx).Infof("Creating cluster-external Provider client from KUBECONFIG %s", os.Getenv("KUBECONFIG")) + logger.Infof("Creating cluster-external Provider client from KUBECONFIG %s", os.Getenv("KUBECONFIG")) client, err = newExternalClusterClientFromFile(os.Getenv("KUBECONFIG")) default: - log.FromContext(ctx).Infof("Creating cluster-external Provider client%s", withEndpoint) + logger.Infof("Creating cluster-external Provider client%s", withEndpoint) client, err = newExternalClusterClient(p.Endpoint, p.Token, p.CertAuthFilePath) } if err != nil { return nil, err } + client.labelSelector = p.LabelSelector return client, nil @@ -394,7 +398,7 @@ func (p *Provider) fillGatewayConf(ctx context.Context, client Client, gateway * // TLS if listener.Protocol == v1alpha1.HTTPSProtocolType || listener.Protocol == v1alpha1.TLSProtocolType { - if listener.TLS == nil || (listener.TLS.CertificateRef == nil && listener.TLS.Mode != v1alpha1.TLSModePassthrough) { + if listener.TLS == nil || (listener.TLS.CertificateRef == nil && listener.TLS.Mode != nil && *listener.TLS.Mode != v1alpha1.TLSModePassthrough) { // update "Detached" status with "UnsupportedProtocol" reason listenerStatuses[i].Conditions = append(listenerStatuses[i].Conditions, metav1.Condition{ Type: string(v1alpha1.ListenerConditionDetached), @@ -407,12 +411,17 @@ func (p *Provider) fillGatewayConf(ctx context.Context, client Client, gateway * continue } - if listener.TLS.Mode == v1alpha1.TLSModePassthrough && listener.TLS.CertificateRef != nil { + var tlsModeType v1alpha1.TLSModeType + if listener.TLS.Mode != nil { + tlsModeType = *listener.TLS.Mode + } + + if tlsModeType == v1alpha1.TLSModePassthrough && listener.TLS.CertificateRef != nil { // https://gateway-api.sigs.k8s.io/guides/tls/ logger.Warnf("In case of Passthrough TLS mode, no TLS settings take effect as the TLS session from the client is NOT terminated at the Gateway") } - isTLSPassthrough := listener.TLS.Mode == v1alpha1.TLSModePassthrough + isTLSPassthrough := tlsModeType == v1alpha1.TLSModePassthrough // Allowed configurations: // Protocol TLS -> Passthrough -> TLSRoute @@ -428,7 +437,7 @@ func (p *Provider) fillGatewayConf(ctx context.Context, client Client, gateway * LastTransitionTime: metav1.Now(), Reason: string(v1alpha1.ListenerReasonUnsupportedProtocol), Message: fmt.Sprintf("Unsupported route kind %q with %q", - listener.Routes.Kind, listener.TLS.Mode), + listener.Routes.Kind, tlsModeType), }) continue @@ -484,7 +493,11 @@ func (p *Provider) fillGatewayConf(ctx context.Context, client Client, gateway * func gatewayHTTPRouteToHTTPConf(ctx context.Context, ep string, listener v1alpha1.Listener, gateway *v1alpha1.Gateway, client Client, conf *dynamic.Configuration) []metav1.Condition { // TODO: support RouteNamespaces - selector := labels.SelectorFromSet(listener.Routes.Selector.MatchLabels) + selector := labels.Everything() + if listener.Routes.Selector != nil { + selector = labels.SelectorFromSet(listener.Routes.Selector.MatchLabels) + } + httpRoutes, err := client.GetHTTPRoutes(gateway.Namespace, selector) if err != nil { // update "ResolvedRefs" status true with "InvalidRoutesRef" reason @@ -599,7 +612,11 @@ func gatewayHTTPRouteToHTTPConf(ctx context.Context, ep string, listener v1alpha func gatewayTCPRouteToTCPConf(ctx context.Context, ep string, listener v1alpha1.Listener, gateway *v1alpha1.Gateway, client Client, conf *dynamic.Configuration) []metav1.Condition { // TODO: support RouteNamespaces - selector := labels.SelectorFromSet(listener.Routes.Selector.MatchLabels) + selector := labels.Everything() + if listener.Routes.Selector != nil { + selector = labels.SelectorFromSet(listener.Routes.Selector.MatchLabels) + } + tcpRoutes, err := client.GetTCPRoutes(gateway.Namespace, selector) if err != nil { // update "ResolvedRefs" status true with "InvalidRoutesRef" reason @@ -698,7 +715,11 @@ func gatewayTCPRouteToTCPConf(ctx context.Context, ep string, listener v1alpha1. func gatewayTLSRouteToTCPConf(ctx context.Context, ep string, listener v1alpha1.Listener, gateway *v1alpha1.Gateway, client Client, conf *dynamic.Configuration) []metav1.Condition { // TODO: support RouteNamespaces - selector := labels.SelectorFromSet(listener.Routes.Selector.MatchLabels) + selector := labels.Everything() + if listener.Routes.Selector != nil { + selector = labels.SelectorFromSet(listener.Routes.Selector.MatchLabels) + } + tlsRoutes, err := client.GetTLSRoutes(gateway.Namespace, selector) if err != nil { // update "ResolvedRefs" status true with "InvalidRoutesRef" reason @@ -736,13 +757,10 @@ func gatewayTLSRouteToTCPConf(ctx context.Context, ep string, listener v1alpha1. router := dynamic.TCPRouter{ Rule: rule, EntryPoints: []string{ep}, - } - - if listener.TLS != nil { - // TODO support let's encrypt - router.TLS = &dynamic.RouterTCPTLSConfig{ - Passthrough: listener.TLS.Mode == v1alpha1.TLSModePassthrough, - } + // The TLS Passthrough is the only TLS mode supported by a Gateway TLSRoute. + TLS: &dynamic.RouterTCPTLSConfig{ + Passthrough: true, + }, } // Adding the gateway name and the entryPoint name prevents overlapping of routers build from the same routes. @@ -945,37 +963,39 @@ func extractRule(routeRule v1alpha1.HTTPRouteRule, hostRule string) (string, err var matchesRules []string for _, match := range routeRule.Matches { - if len(match.Path.Type) == 0 && match.Headers == nil { + if (match.Path == nil || match.Path.Type == nil) && match.Headers == nil { continue } var matchRules []string // TODO handle other path types - if len(match.Path.Type) > 0 { - switch match.Path.Type { + if match.Path != nil && match.Path.Type != nil && match.Path.Value != nil { + val := pointer.StringDeref(match.Path.Value, "") + + switch *match.Path.Type { case v1alpha1.PathMatchExact: - matchRules = append(matchRules, "Path(`"+match.Path.Value+"`)") + matchRules = append(matchRules, fmt.Sprintf("Path(`%s`)", val)) case v1alpha1.PathMatchPrefix: - matchRules = append(matchRules, "PathPrefix(`"+match.Path.Value+"`)") + matchRules = append(matchRules, fmt.Sprintf("PathPrefix(`%s`)", val)) default: - return "", fmt.Errorf("unsupported path match %s", match.Path.Type) + return "", fmt.Errorf("unsupported path match %s", *match.Path.Type) } } // TODO handle other headers types - if match.Headers != nil { - switch match.Headers.Type { + if match.Headers != nil && match.Headers.Type != nil { + switch *match.Headers.Type { case v1alpha1.HeaderMatchExact: var headerRules []string for headerName, headerValue := range match.Headers.Values { - headerRules = append(headerRules, "Headers(`"+headerName+"`,`"+headerValue+"`)") + headerRules = append(headerRules, fmt.Sprintf("Headers(`%s`,`%s`)", headerName, headerValue)) } // to have a consistent order sort.Strings(headerRules) matchRules = append(matchRules, headerRules...) default: - return "", fmt.Errorf("unsupported header match type %s", match.Headers.Type) + return "", fmt.Errorf("unsupported header match type %s", *match.Headers.Type) } } @@ -1128,7 +1148,7 @@ func loadServices(client Client, namespace string, targets []v1alpha1.HTTPRouteF } for _, forwardTo := range targets { - weight := int(forwardTo.Weight) + weight := int(pointer.Int32Deref(forwardTo.Weight, 1)) if forwardTo.ServiceName == nil && forwardTo.BackendRef != nil { if !(forwardTo.BackendRef.Group == traefikServiceGroupName && forwardTo.BackendRef.Kind == traefikServiceKind) { @@ -1149,7 +1169,7 @@ func loadServices(client Client, namespace string, targets []v1alpha1.HTTPRouteF svc := dynamic.Service{ LoadBalancer: &dynamic.ServersLoadBalancer{ - PassHostHeader: func(v bool) *bool { return &v }(true), + PassHostHeader: pointer.Bool(true), }, } @@ -1189,7 +1209,7 @@ func loadServices(client Client, namespace string, targets []v1alpha1.HTTPRouteF return nil, nil, errors.New("service port not found") } - endpoints, endpointsExists, endpointsErr := client.GetEndpoints(namespace, *forwardTo.ServiceName) + endpoints, endpointsExists, endpointsErr := client.GetEndpoints(namespace, pointer.StringDeref(forwardTo.ServiceName, "")) if endpointsErr != nil { return nil, nil, endpointsErr } @@ -1250,7 +1270,7 @@ func loadTCPServices(client Client, namespace string, targets []v1alpha1.RouteFo } for _, forwardTo := range targets { - weight := int(forwardTo.Weight) + weight := int(pointer.Int32Deref(forwardTo.Weight, 1)) if forwardTo.ServiceName == nil && forwardTo.BackendRef != nil { if !(forwardTo.BackendRef.Group == traefikServiceGroupName && forwardTo.BackendRef.Kind == traefikServiceKind) { diff --git a/pkg/provider/kubernetes/gateway/kubernetes_test.go b/pkg/provider/kubernetes/gateway/kubernetes_test.go index 13c17bc02..33e882ef5 100644 --- a/pkg/provider/kubernetes/gateway/kubernetes_test.go +++ b/pkg/provider/kubernetes/gateway/kubernetes_test.go @@ -9,12 +9,15 @@ import ( "github.com/traefik/traefik/v2/pkg/config/dynamic" "github.com/traefik/traefik/v2/pkg/provider" "github.com/traefik/traefik/v2/pkg/tls" + "k8s.io/utils/pointer" "sigs.k8s.io/gateway-api/apis/v1alpha1" ) var _ provider.Provider = (*Provider)(nil) -func Bool(v bool) *bool { return &v } +func PMT(p v1alpha1.PathMatchType) *v1alpha1.PathMatchType { return &p } + +func HMT(h v1alpha1.HeaderMatchType) *v1alpha1.HeaderMatchType { return &h } func TestLoadHTTPRoutes(t *testing.T) { testCases := []struct { @@ -37,9 +40,10 @@ func TestLoadHTTPRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -61,9 +65,10 @@ func TestLoadHTTPRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -85,9 +90,10 @@ func TestLoadHTTPRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -109,9 +115,10 @@ func TestLoadHTTPRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -133,9 +140,10 @@ func TestLoadHTTPRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -157,9 +165,10 @@ func TestLoadHTTPRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -181,9 +190,10 @@ func TestLoadHTTPRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -205,9 +215,10 @@ func TestLoadHTTPRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -229,9 +240,10 @@ func TestLoadHTTPRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -253,9 +265,10 @@ func TestLoadHTTPRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -277,9 +290,10 @@ func TestLoadHTTPRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -301,9 +315,10 @@ func TestLoadHTTPRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -325,9 +340,10 @@ func TestLoadHTTPRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -349,9 +365,10 @@ func TestLoadHTTPRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -373,9 +390,10 @@ func TestLoadHTTPRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -398,9 +416,10 @@ func TestLoadHTTPRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -422,9 +441,10 @@ func TestLoadHTTPRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -446,9 +466,10 @@ func TestLoadHTTPRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{ Certificates: []*tls.CertAndStores{ @@ -508,10 +529,11 @@ func TestLoadHTTPRoutes(t *testing.T) { URL: "http://10.10.0.2:80", }, }, - PassHostHeader: Bool(true), + PassHostHeader: pointer.Bool(true), }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -540,8 +562,9 @@ func TestLoadHTTPRoutes(t *testing.T) { Rule: "Host(`foo.com`) && Path(`/bar`)", }, }, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -596,10 +619,11 @@ func TestLoadHTTPRoutes(t *testing.T) { URL: "http://10.10.0.2:80", }, }, - PassHostHeader: Bool(true), + PassHostHeader: pointer.Bool(true), }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -651,10 +675,11 @@ func TestLoadHTTPRoutes(t *testing.T) { URL: "http://10.10.0.2:80", }, }, - PassHostHeader: Bool(true), + PassHostHeader: pointer.Bool(true), }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{ Certificates: []*tls.CertAndStores{ @@ -714,10 +739,11 @@ func TestLoadHTTPRoutes(t *testing.T) { URL: "http://10.10.0.2:80", }, }, - PassHostHeader: Bool(true), + PassHostHeader: pointer.Bool(true), }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -768,10 +794,11 @@ func TestLoadHTTPRoutes(t *testing.T) { URL: "http://10.10.0.2:80", }, }, - PassHostHeader: Bool(true), + PassHostHeader: pointer.Bool(true), }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -822,10 +849,11 @@ func TestLoadHTTPRoutes(t *testing.T) { URL: "http://10.10.0.2:80", }, }, - PassHostHeader: Bool(true), + PassHostHeader: pointer.Bool(true), }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -891,7 +919,7 @@ func TestLoadHTTPRoutes(t *testing.T) { URL: "http://10.10.0.2:80", }, }, - PassHostHeader: Bool(true), + PassHostHeader: pointer.Bool(true), }, }, "default-whoami2-8080": { @@ -904,10 +932,11 @@ func TestLoadHTTPRoutes(t *testing.T) { URL: "http://10.10.0.4:8080", }, }, - PassHostHeader: Bool(true), + PassHostHeader: pointer.Bool(true), }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -962,7 +991,7 @@ func TestLoadHTTPRoutes(t *testing.T) { URL: "http://10.10.0.2:80", }, }, - PassHostHeader: Bool(true), + PassHostHeader: pointer.Bool(true), }, }, "default-whoami2-8080": { @@ -975,10 +1004,11 @@ func TestLoadHTTPRoutes(t *testing.T) { URL: "http://10.10.0.4:8080", }, }, - PassHostHeader: Bool(true), + PassHostHeader: pointer.Bool(true), }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -1050,10 +1080,11 @@ func TestLoadHTTPRoutes(t *testing.T) { URL: "http://10.10.0.2:80", }, }, - PassHostHeader: Bool(true), + PassHostHeader: pointer.Bool(true), }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{ Certificates: []*tls.CertAndStores{ @@ -1134,10 +1165,11 @@ func TestLoadHTTPRoutes(t *testing.T) { URL: "http://10.10.0.2:80", }, }, - PassHostHeader: Bool(true), + PassHostHeader: pointer.Bool(true), }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{ Certificates: []*tls.CertAndStores{ @@ -1212,10 +1244,11 @@ func TestLoadHTTPRoutes(t *testing.T) { URL: "http://10.10.0.2:80", }, }, - PassHostHeader: Bool(true), + PassHostHeader: pointer.Bool(true), }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -1260,9 +1293,10 @@ func TestLoadTCPRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -1284,9 +1318,10 @@ func TestLoadTCPRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -1308,9 +1343,10 @@ func TestLoadTCPRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -1332,9 +1368,10 @@ func TestLoadTCPRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -1356,9 +1393,10 @@ func TestLoadTCPRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -1380,9 +1418,10 @@ func TestLoadTCPRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -1433,9 +1472,10 @@ func TestLoadTCPRoutes(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -1515,9 +1555,10 @@ func TestLoadTCPRoutes(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -1541,9 +1582,10 @@ func TestLoadTCPRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -1598,9 +1640,10 @@ func TestLoadTCPRoutes(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -1650,9 +1693,10 @@ func TestLoadTCPRoutes(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{ Certificates: []*tls.CertAndStores{ @@ -1706,9 +1750,10 @@ func TestLoadTLSRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -1730,9 +1775,10 @@ func TestLoadTLSRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -1754,9 +1800,10 @@ func TestLoadTLSRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -1778,9 +1825,10 @@ func TestLoadTLSRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -1802,9 +1850,10 @@ func TestLoadTLSRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -1826,9 +1875,10 @@ func TestLoadTLSRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -1858,9 +1908,10 @@ func TestLoadTLSRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -1882,9 +1933,10 @@ func TestLoadTLSRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -1906,9 +1958,10 @@ func TestLoadTLSRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -1960,9 +2013,10 @@ func TestLoadTLSRoutes(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{ Certificates: []*tls.CertAndStores{ @@ -2025,9 +2079,10 @@ func TestLoadTLSRoutes(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -2110,9 +2165,10 @@ func TestLoadTLSRoutes(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{ Certificates: []*tls.CertAndStores{ @@ -2177,9 +2233,10 @@ func TestLoadTLSRoutes(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{ Certificates: []*tls.CertAndStores{ @@ -2242,9 +2299,10 @@ func TestLoadTLSRoutes(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -2298,9 +2356,10 @@ func TestLoadTLSRoutes(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -2354,9 +2413,10 @@ func TestLoadTLSRoutes(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -2410,9 +2470,10 @@ func TestLoadTLSRoutes(t *testing.T) { }, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -2457,9 +2518,10 @@ func TestLoadMixedRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -2481,9 +2543,10 @@ func TestLoadMixedRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -2505,9 +2568,10 @@ func TestLoadMixedRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -2529,9 +2593,10 @@ func TestLoadMixedRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -2665,10 +2730,11 @@ func TestLoadMixedRoutes(t *testing.T) { URL: "http://10.10.0.2:80", }, }, - PassHostHeader: Bool(true), + PassHostHeader: pointer.Bool(true), }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{ Certificates: []*tls.CertAndStores{ @@ -2701,9 +2767,10 @@ func TestLoadMixedRoutes(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, TLS: &dynamic.TLSConfiguration{}, }, @@ -2872,14 +2939,88 @@ func TestExtractRule(t *testing.T) { hostRule: "Host(`foo.com`)", expectedRule: "Host(`foo.com`) && PathPrefix(`/`)", }, + { + desc: "One HTTPRouteMatch with nil HTTPHeaderMatch", + routeRule: v1alpha1.HTTPRouteRule{ + Matches: []v1alpha1.HTTPRouteMatch{ + {Headers: nil}, + }, + }, + expectedRule: "", + }, + { + desc: "One HTTPRouteMatch with nil HTTPHeaderMatch Type", + routeRule: v1alpha1.HTTPRouteRule{ + Matches: []v1alpha1.HTTPRouteMatch{ + { + Headers: &v1alpha1.HTTPHeaderMatch{ + Type: nil, + Values: map[string]string{"foo": "bar"}, + }, + }, + }, + }, + expectedRule: "", + }, + { + desc: "One HTTPRouteMatch with nil HTTPHeaderMatch Values", + routeRule: v1alpha1.HTTPRouteRule{ + Matches: []v1alpha1.HTTPRouteMatch{ + { + Headers: &v1alpha1.HTTPHeaderMatch{ + Type: HMT(v1alpha1.HeaderMatchExact), + Values: nil, + }, + }, + }, + }, + expectedRule: "", + }, + { + desc: "One HTTPRouteMatch with nil HTTPPathMatch", + routeRule: v1alpha1.HTTPRouteRule{ + Matches: []v1alpha1.HTTPRouteMatch{ + {Path: nil}, + }, + }, + expectedRule: "", + }, + { + desc: "One HTTPRouteMatch with nil HTTPPathMatch Type", + routeRule: v1alpha1.HTTPRouteRule{ + Matches: []v1alpha1.HTTPRouteMatch{ + { + Path: &v1alpha1.HTTPPathMatch{ + Type: nil, + Value: pointer.String("/foo/"), + }, + }, + }, + }, + expectedRule: "", + }, + { + desc: "One HTTPRouteMatch with nil HTTPPathMatch Values", + routeRule: v1alpha1.HTTPRouteRule{ + Matches: []v1alpha1.HTTPRouteMatch{ + { + Path: &v1alpha1.HTTPPathMatch{ + Type: PMT(v1alpha1.PathMatchExact), + Value: nil, + }, + }, + }, + }, + expectedRule: "", + }, { desc: "One Path in matches", routeRule: v1alpha1.HTTPRouteRule{ Matches: []v1alpha1.HTTPRouteMatch{ { - Path: v1alpha1.HTTPPathMatch{ - Type: v1alpha1.PathMatchExact, - Value: "/foo/", + Path: &v1alpha1.HTTPPathMatch{ + Type: PMT(v1alpha1.PathMatchExact), + Value: pointer.String("/foo/"), }, }, }, @@ -2891,15 +3032,15 @@ func TestExtractRule(t *testing.T) { routeRule: v1alpha1.HTTPRouteRule{ Matches: []v1alpha1.HTTPRouteMatch{ { - Path: v1alpha1.HTTPPathMatch{ - Type: v1alpha1.PathMatchExact, - Value: "/foo/", + Path: &v1alpha1.HTTPPathMatch{ + Type: PMT(v1alpha1.PathMatchExact), + Value: pointer.String("/foo/"), }, }, { - Path: v1alpha1.HTTPPathMatch{ - Type: "unknown", - Value: "/foo/", + Path: &v1alpha1.HTTPPathMatch{ + Type: PMT("unknown"), + Value: pointer.String("/foo/"), }, }, }, @@ -2911,9 +3052,9 @@ func TestExtractRule(t *testing.T) { routeRule: v1alpha1.HTTPRouteRule{ Matches: []v1alpha1.HTTPRouteMatch{ { - Path: v1alpha1.HTTPPathMatch{ - Type: v1alpha1.PathMatchExact, - Value: "/foo/", + Path: &v1alpha1.HTTPPathMatch{ + Type: PMT(v1alpha1.PathMatchExact), + Value: pointer.String("/foo/"), }, }, {}, @@ -2926,14 +3067,14 @@ func TestExtractRule(t *testing.T) { routeRule: v1alpha1.HTTPRouteRule{ Matches: []v1alpha1.HTTPRouteMatch{ { - Path: v1alpha1.HTTPPathMatch{ - Type: v1alpha1.PathMatchExact, - Value: "/foo/", + Path: &v1alpha1.HTTPPathMatch{ + Type: PMT(v1alpha1.PathMatchExact), + Value: pointer.String("/foo/"), }, }, { Headers: &v1alpha1.HTTPHeaderMatch{ - Type: v1alpha1.HeaderMatchExact, + Type: HMT(v1alpha1.HeaderMatchExact), Values: map[string]string{ "my-header": "foo", }, @@ -2948,12 +3089,12 @@ func TestExtractRule(t *testing.T) { routeRule: v1alpha1.HTTPRouteRule{ Matches: []v1alpha1.HTTPRouteMatch{ { - Path: v1alpha1.HTTPPathMatch{ - Type: v1alpha1.PathMatchExact, - Value: "/foo/", + Path: &v1alpha1.HTTPPathMatch{ + Type: PMT(v1alpha1.PathMatchExact), + Value: pointer.String("/foo/"), }, Headers: &v1alpha1.HTTPHeaderMatch{ - Type: v1alpha1.HeaderMatchExact, + Type: HMT(v1alpha1.HeaderMatchExact), Values: map[string]string{ "my-header": "foo", }, @@ -2969,12 +3110,12 @@ func TestExtractRule(t *testing.T) { routeRule: v1alpha1.HTTPRouteRule{ Matches: []v1alpha1.HTTPRouteMatch{ { - Path: v1alpha1.HTTPPathMatch{ - Type: v1alpha1.PathMatchExact, - Value: "/foo/", + Path: &v1alpha1.HTTPPathMatch{ + Type: PMT(v1alpha1.PathMatchExact), + Value: pointer.String("/foo/"), }, Headers: &v1alpha1.HTTPHeaderMatch{ - Type: v1alpha1.HeaderMatchExact, + Type: HMT(v1alpha1.HeaderMatchExact), Values: map[string]string{ "my-header": "foo", }, @@ -2990,14 +3131,14 @@ func TestExtractRule(t *testing.T) { routeRule: v1alpha1.HTTPRouteRule{ Matches: []v1alpha1.HTTPRouteMatch{ { - Path: v1alpha1.HTTPPathMatch{ - Type: v1alpha1.PathMatchExact, - Value: "/foo/", + Path: &v1alpha1.HTTPPathMatch{ + Type: PMT(v1alpha1.PathMatchExact), + Value: pointer.String("/foo/"), }, }, { Headers: &v1alpha1.HTTPHeaderMatch{ - Type: v1alpha1.HeaderMatchExact, + Type: HMT(v1alpha1.HeaderMatchExact), Values: map[string]string{ "my-header": "foo", }, diff --git a/pkg/provider/kubernetes/ingress/client.go b/pkg/provider/kubernetes/ingress/client.go index b827e52a5..7dddb8cda 100644 --- a/pkg/provider/kubernetes/ingress/client.go +++ b/pkg/provider/kubernetes/ingress/client.go @@ -46,7 +46,7 @@ type Client interface { GetSecret(namespace, name string) (*corev1.Secret, bool, error) GetEndpoints(namespace, name string) (*corev1.Endpoints, bool, error) UpdateIngressStatus(ing *networkingv1.Ingress, ingStatus []corev1.LoadBalancerIngress) error - GetServerVersion() (*version.Version, error) + GetServerVersion() *version.Version } type clientWrapper struct { @@ -58,6 +58,7 @@ type clientWrapper struct { ingressLabelSelector string isNamespaceAll bool watchedNamespaces []string + serverVersion *version.Version } // newInClusterClient returns a new Provider client that is expected to run @@ -135,6 +136,19 @@ func newClientImpl(clientset kubernetes.Interface) *clientWrapper { // WatchAll starts namespace-specific controllers for all relevant kinds. func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<-chan interface{}, error) { + // Get and store the serverVersion for future use. + serverVersionInfo, err := c.clientset.Discovery().ServerVersion() + if err != nil { + return nil, fmt.Errorf("could not retrieve server version: %w", err) + } + + serverVersion, err := version.NewVersion(serverVersionInfo.GitVersion) + if err != nil { + return nil, fmt.Errorf("could not parse server version: %w", err) + } + + c.serverVersion = serverVersion + eventCh := make(chan interface{}, 1) eventHandler := &k8s.ResourceEventHandler{Ev: eventCh} @@ -153,11 +167,6 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (< opts.LabelSelector = c.ingressLabelSelector } - serverVersion, err := c.GetServerVersion() - if err != nil { - return nil, fmt.Errorf("failed to get server version: %w", err) - } - for _, ns := range namespaces { factoryIngress := informers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod, informers.WithNamespace(ns), informers.WithTweakListOptions(matchesLabelSelector)) @@ -230,13 +239,7 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (< func (c *clientWrapper) GetIngresses() []*networkingv1.Ingress { var results []*networkingv1.Ingress - serverVersion, err := c.GetServerVersion() - if err != nil { - log.Errorf("Failed to get server version: %v", err) - return results - } - - isNetworkingV1Supported := supportsNetworkingV1Ingress(serverVersion) + isNetworkingV1Supported := supportsNetworkingV1Ingress(c.serverVersion) for ns, factory := range c.factoriesIngress { if isNetworkingV1Supported { @@ -354,13 +357,7 @@ func (c *clientWrapper) UpdateIngressStatus(src *networkingv1.Ingress, ingStatus return fmt.Errorf("failed to get ingress %s/%s: namespace is not within watched namespaces", src.Namespace, src.Name) } - serverVersion, err := c.GetServerVersion() - if err != nil { - log.WithoutContext().Errorf("Failed to get server version: %v", err) - return err - } - - if !supportsNetworkingV1Ingress(serverVersion) { + if !supportsNetworkingV1Ingress(c.serverVersion) { return c.updateIngressStatusOld(src, ingStatus) } @@ -472,18 +469,12 @@ func (c *clientWrapper) GetSecret(namespace, name string) (*corev1.Secret, bool, } func (c *clientWrapper) GetIngressClasses() ([]*networkingv1.IngressClass, error) { - serverVersion, err := c.GetServerVersion() - if err != nil { - log.WithoutContext().Errorf("Failed to get server version: %v", err) - return nil, err - } - if c.clusterFactory == nil { return nil, errors.New("cluster factory not loaded") } var ics []*networkingv1.IngressClass - if !supportsNetworkingV1Ingress(serverVersion) { + if !supportsNetworkingV1Ingress(c.serverVersion) { ingressClasses, err := c.clusterFactory.Networking().V1beta1().IngressClasses().Lister().List(labels.Everything()) if err != nil { return nil, err @@ -531,13 +522,8 @@ func (c *clientWrapper) lookupNamespace(ns string) string { } // GetServerVersion returns the cluster server version, or an error. -func (c *clientWrapper) GetServerVersion() (*version.Version, error) { - serverVersion, err := c.clientset.Discovery().ServerVersion() - if err != nil { - return nil, fmt.Errorf("could not retrieve server version: %w", err) - } - - return version.NewVersion(serverVersion.GitVersion) +func (c *clientWrapper) GetServerVersion() *version.Version { + return c.serverVersion } // translateNotFoundError will translate a "not found" error to a boolean return diff --git a/pkg/provider/kubernetes/ingress/client_mock_test.go b/pkg/provider/kubernetes/ingress/client_mock_test.go index 195580692..6cc6ef138 100644 --- a/pkg/provider/kubernetes/ingress/client_mock_test.go +++ b/pkg/provider/kubernetes/ingress/client_mock_test.go @@ -80,8 +80,8 @@ func (c clientMock) GetIngresses() []*networkingv1.Ingress { return c.ingresses } -func (c clientMock) GetServerVersion() (*version.Version, error) { - return c.serverVersion, nil +func (c clientMock) GetServerVersion() *version.Version { + return c.serverVersion } func (c clientMock) GetService(namespace, name string) (*corev1.Service, bool, error) { diff --git a/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-IPv6-endpoints-externalname-enabled_ingress.yml b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-IPv6-endpoints-externalname-enabled_ingress.yml new file mode 100644 index 000000000..e41dde42f --- /dev/null +++ b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-IPv6-endpoints-externalname-enabled_ingress.yml @@ -0,0 +1,14 @@ +kind: Ingress +apiVersion: networking.k8s.io/v1beta1 +metadata: + name: example.com + namespace: testing + +spec: + rules: + - http: + paths: + - path: /foo + backend: + serviceName: service-foo + servicePort: 8080 diff --git a/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-IPv6-endpoints-externalname-enabled_service.yml b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-IPv6-endpoints-externalname-enabled_service.yml new file mode 100644 index 000000000..67c193cfa --- /dev/null +++ b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-IPv6-endpoints-externalname-enabled_service.yml @@ -0,0 +1,13 @@ +--- +kind: Service +apiVersion: v1 +metadata: + name: service-foo + namespace: testing + +spec: + ports: + - name: http + port: 8080 + type: ExternalName + externalName: "2001:0db8:3c4d:0015:0000:0000:1a2f:2a3b" diff --git a/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-service-with-externalName-enabled_ingress.yml b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-service-with-externalName-enabled_ingress.yml new file mode 100644 index 000000000..f9645ad09 --- /dev/null +++ b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-service-with-externalName-enabled_ingress.yml @@ -0,0 +1,15 @@ +kind: Ingress +apiVersion: networking.k8s.io/v1beta1 +metadata: + name: "" + namespace: testing + +spec: + rules: + - host: traefik.tchouk + http: + paths: + - path: /bar + backend: + serviceName: service1 + servicePort: 8080 diff --git a/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-service-with-externalName-enabled_service.yml b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-service-with-externalName-enabled_service.yml new file mode 100644 index 000000000..972e4cdbc --- /dev/null +++ b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-service-with-externalName-enabled_service.yml @@ -0,0 +1,13 @@ +kind: Service +apiVersion: v1 +metadata: + name: service1 + namespace: testing + +spec: + ports: + - port: 8080 + clusterIP: 10.0.0.1 + type: ExternalName + externalName: traefik.wtf + diff --git a/pkg/provider/kubernetes/ingress/kubernetes.go b/pkg/provider/kubernetes/ingress/kubernetes.go index 0a2240640..2e1869fa8 100644 --- a/pkg/provider/kubernetes/ingress/kubernetes.go +++ b/pkg/provider/kubernetes/ingress/kubernetes.go @@ -36,16 +36,17 @@ const ( // Provider holds configurations of the provider. type Provider struct { - Endpoint string `description:"Kubernetes server endpoint (required for external cluster client)." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"` - Token string `description:"Kubernetes bearer token (not needed for in-cluster client)." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty"` - CertAuthFilePath string `description:"Kubernetes certificate authority file path (not needed for in-cluster client)." json:"certAuthFilePath,omitempty" toml:"certAuthFilePath,omitempty" yaml:"certAuthFilePath,omitempty"` - Namespaces []string `description:"Kubernetes namespaces." json:"namespaces,omitempty" toml:"namespaces,omitempty" yaml:"namespaces,omitempty" export:"true"` - LabelSelector string `description:"Kubernetes Ingress label selector to use." json:"labelSelector,omitempty" toml:"labelSelector,omitempty" yaml:"labelSelector,omitempty" export:"true"` - IngressClass string `description:"Value of kubernetes.io/ingress.class annotation or IngressClass name to watch for." json:"ingressClass,omitempty" toml:"ingressClass,omitempty" yaml:"ingressClass,omitempty" export:"true"` - IngressEndpoint *EndpointIngress `description:"Kubernetes Ingress Endpoint." json:"ingressEndpoint,omitempty" toml:"ingressEndpoint,omitempty" yaml:"ingressEndpoint,omitempty" export:"true"` - ThrottleDuration ptypes.Duration `description:"Ingress refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty" export:"true"` - AllowEmptyServices bool `description:"Allow creation of services without endpoints." json:"allowEmptyServices,omitempty" toml:"allowEmptyServices,omitempty" yaml:"allowEmptyServices,omitempty" export:"true"` - lastConfiguration safe.Safe + Endpoint string `description:"Kubernetes server endpoint (required for external cluster client)." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"` + Token string `description:"Kubernetes bearer token (not needed for in-cluster client)." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty"` + CertAuthFilePath string `description:"Kubernetes certificate authority file path (not needed for in-cluster client)." json:"certAuthFilePath,omitempty" toml:"certAuthFilePath,omitempty" yaml:"certAuthFilePath,omitempty"` + Namespaces []string `description:"Kubernetes namespaces." json:"namespaces,omitempty" toml:"namespaces,omitempty" yaml:"namespaces,omitempty" export:"true"` + LabelSelector string `description:"Kubernetes Ingress label selector to use." json:"labelSelector,omitempty" toml:"labelSelector,omitempty" yaml:"labelSelector,omitempty" export:"true"` + IngressClass string `description:"Value of kubernetes.io/ingress.class annotation or IngressClass name to watch for." json:"ingressClass,omitempty" toml:"ingressClass,omitempty" yaml:"ingressClass,omitempty" export:"true"` + IngressEndpoint *EndpointIngress `description:"Kubernetes Ingress Endpoint." json:"ingressEndpoint,omitempty" toml:"ingressEndpoint,omitempty" yaml:"ingressEndpoint,omitempty" export:"true"` + ThrottleDuration ptypes.Duration `description:"Ingress refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty" export:"true"` + AllowEmptyServices bool `description:"Allow creation of services without endpoints." json:"allowEmptyServices,omitempty" toml:"allowEmptyServices,omitempty" yaml:"allowEmptyServices,omitempty" export:"true"` + AllowExternalNameServices bool `description:"Allow ExternalName services." json:"allowExternalNameServices,omitempty" toml:"allowExternalNameServices,omitempty" yaml:"allowExternalNameServices,omitempty" export:"true"` + lastConfiguration safe.Safe } // EndpointIngress holds the endpoint information for the Kubernetes provider. @@ -107,6 +108,10 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe. return err } + if p.AllowExternalNameServices { + logger.Warn("ExternalName service loading is enabled, please ensure that this is expected (see AllowExternalNameServices option)") + } + pool.GoCtx(func(ctxPool context.Context) { operation := func() error { eventsChan, err := k8sClient.WatchAll(p.Namespaces, ctxPool.Done()) @@ -184,11 +189,7 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl TCP: &dynamic.TCPConfiguration{}, } - serverVersion, err := client.GetServerVersion() - if err != nil { - log.FromContext(ctx).Errorf("Failed to get server version: %v", err) - return conf - } + serverVersion := client.GetServerVersion() var ingressClasses []*networkingv1.IngressClass @@ -232,7 +233,7 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl continue } - service, err := loadService(client, ingress.Namespace, *ingress.Spec.DefaultBackend) + service, err := p.loadService(client, ingress.Namespace, *ingress.Spec.DefaultBackend) if err != nil { log.FromContext(ctx). WithField("serviceName", ingress.Spec.DefaultBackend.Service.Name). @@ -277,7 +278,7 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl } for _, pa := range rule.HTTP.Paths { - service, err := loadService(client, ingress.Namespace, pa.Backend) + service, err := p.loadService(client, ingress.Namespace, pa.Backend) if err != nil { log.FromContext(ctx). WithField("serviceName", pa.Backend.Service.Name). @@ -486,7 +487,7 @@ func getTLSConfig(tlsConfigs map[string]*tls.CertAndStores) []*tls.CertAndStores return configs } -func loadService(client Client, namespace string, backend networkingv1.IngressBackend) (*dynamic.Service, error) { +func (p *Provider) loadService(client Client, namespace string, backend networkingv1.IngressBackend) (*dynamic.Service, error) { service, exists, err := client.GetService(namespace, backend.Service.Name) if err != nil { return nil, err @@ -496,6 +497,10 @@ func loadService(client Client, namespace string, backend networkingv1.IngressBa return nil, errors.New("service not found") } + if !p.AllowExternalNameServices && service.Spec.Type == corev1.ServiceTypeExternalName { + return nil, fmt.Errorf("externalName services not allowed: %s/%s", namespace, backend.Service.Name) + } + var portName string var portSpec corev1.ServicePort var match bool diff --git a/pkg/provider/kubernetes/ingress/kubernetes_test.go b/pkg/provider/kubernetes/ingress/kubernetes_test.go index 67ba7e063..58d8226a9 100644 --- a/pkg/provider/kubernetes/ingress/kubernetes_test.go +++ b/pkg/provider/kubernetes/ingress/kubernetes_test.go @@ -746,33 +746,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { }, }, }, - { - desc: "Ingress with service with externalName", - expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, - HTTP: &dynamic.HTTPConfiguration{ - Middlewares: map[string]*dynamic.Middleware{}, - Routers: map[string]*dynamic.Router{ - "testing-traefik-tchouk-bar": { - Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", - Service: "testing-service1-8080", - }, - }, - Services: map[string]*dynamic.Service{ - "testing-service1-8080": { - LoadBalancer: &dynamic.ServersLoadBalancer{ - PassHostHeader: Bool(true), - Servers: []dynamic.Server{ - { - URL: "http://traefik.wtf:8080", - }, - }, - }, - }, - }, - }, - }, - }, { desc: "Ingress with port invalid for one service", expected: &dynamic.Configuration{ @@ -800,47 +773,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { }, }, }, - { - desc: "Ingress with IPv6 endpoints", - expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, - HTTP: &dynamic.HTTPConfiguration{ - Middlewares: map[string]*dynamic.Middleware{}, - Routers: map[string]*dynamic.Router{ - "example-com-testing-bar": { - Rule: "PathPrefix(`/bar`)", - Service: "testing-service-bar-8080", - }, - "example-com-testing-foo": { - Rule: "PathPrefix(`/foo`)", - Service: "testing-service-foo-8080", - }, - }, - Services: map[string]*dynamic.Service{ - "testing-service-bar-8080": { - LoadBalancer: &dynamic.ServersLoadBalancer{ - Servers: []dynamic.Server{ - { - URL: "http://[2001:0db8:3c4d:0015:0000:0000:1a2f:1a2b]:8080", - }, - }, - PassHostHeader: Bool(true), - }, - }, - "testing-service-foo-8080": { - LoadBalancer: &dynamic.ServersLoadBalancer{ - Servers: []dynamic.Server{ - { - URL: "http://[2001:0db8:3c4d:0015:0000:0000:1a2f:2a3b]:8080", - }, - }, - PassHostHeader: Bool(true), - }, - }, - }, - }, - }, - }, { desc: "TLS support", expected: &dynamic.Configuration{ @@ -1702,7 +1634,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { } clientMock := newClientMock(serverVersion, paths...) - p := Provider{IngressClass: test.ingressClass, AllowEmptyServices: test.allowEmptyServices} conf := p.loadConfigurationFromIngresses(context.Background(), clientMock) @@ -1711,6 +1642,154 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { } } +func TestLoadConfigurationFromIngressesWithExternalNameServices(t *testing.T) { + testCases := []struct { + desc string + ingressClass string + serverVersion string + allowExternalNameServices bool + expected *dynamic.Configuration + }{ + { + desc: "Ingress with service with externalName", + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{}, + HTTP: &dynamic.HTTPConfiguration{ + Middlewares: map[string]*dynamic.Middleware{}, + Routers: map[string]*dynamic.Router{}, + Services: map[string]*dynamic.Service{}, + }, + }, + }, + { + desc: "Ingress with service with externalName enabled", + allowExternalNameServices: true, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{}, + HTTP: &dynamic.HTTPConfiguration{ + Middlewares: map[string]*dynamic.Middleware{}, + Routers: map[string]*dynamic.Router{ + "testing-traefik-tchouk-bar": { + Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", + Service: "testing-service1-8080", + }, + }, + Services: map[string]*dynamic.Service{ + "testing-service1-8080": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + PassHostHeader: Bool(true), + Servers: []dynamic.Server{ + { + URL: "http://traefik.wtf:8080", + }, + }, + }, + }, + }, + }, + }, + }, + { + desc: "Ingress with IPv6 endpoints", + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{}, + HTTP: &dynamic.HTTPConfiguration{ + Middlewares: map[string]*dynamic.Middleware{}, + Routers: map[string]*dynamic.Router{ + "example-com-testing-bar": { + Rule: "PathPrefix(`/bar`)", + Service: "testing-service-bar-8080", + }, + }, + Services: map[string]*dynamic.Service{ + "testing-service-bar-8080": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://[2001:0db8:3c4d:0015:0000:0000:1a2f:1a2b]:8080", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + }, + }, + }, + { + desc: "Ingress with IPv6 endpoints externalname enabled", + allowExternalNameServices: true, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{}, + HTTP: &dynamic.HTTPConfiguration{ + Middlewares: map[string]*dynamic.Middleware{}, + Routers: map[string]*dynamic.Router{ + "example-com-testing-foo": { + Rule: "PathPrefix(`/foo`)", + Service: "testing-service-foo-8080", + }, + }, + Services: map[string]*dynamic.Service{ + "testing-service-foo-8080": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://[2001:0db8:3c4d:0015:0000:0000:1a2f:2a3b]:8080", + }, + }, + PassHostHeader: Bool(true), + }, + }, + }, + }, + }, + }, + } + + for _, test := range testCases { + test := test + + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + var paths []string + _, err := os.Stat(generateTestFilename("_ingress", test.desc)) + if err == nil { + paths = append(paths, generateTestFilename("_ingress", test.desc)) + } + _, err = os.Stat(generateTestFilename("_endpoint", test.desc)) + if err == nil { + paths = append(paths, generateTestFilename("_endpoint", test.desc)) + } + _, err = os.Stat(generateTestFilename("_service", test.desc)) + if err == nil { + paths = append(paths, generateTestFilename("_service", test.desc)) + } + _, err = os.Stat(generateTestFilename("_secret", test.desc)) + if err == nil { + paths = append(paths, generateTestFilename("_secret", test.desc)) + } + _, err = os.Stat(generateTestFilename("_ingressclass", test.desc)) + if err == nil { + paths = append(paths, generateTestFilename("_ingressclass", test.desc)) + } + + serverVersion := test.serverVersion + if serverVersion == "" { + serverVersion = "v1.17" + } + + clientMock := newClientMock(serverVersion, paths...) + + p := Provider{IngressClass: test.ingressClass} + p.AllowExternalNameServices = test.allowExternalNameServices + conf := p.loadConfigurationFromIngresses(context.Background(), clientMock) + + assert.Equal(t, test.expected, conf) + }) + } +} + func generateTestFilename(suffix, desc string) string { return "./fixtures/" + strings.ReplaceAll(desc, " ", "-") + suffix + ".yml" } diff --git a/pkg/provider/marathon/config_test.go b/pkg/provider/marathon/config_test.go index 1f00c7ad6..f49559ab9 100644 --- a/pkg/provider/marathon/config_test.go +++ b/pkg/provider/marathon/config_test.go @@ -73,6 +73,7 @@ func TestBuildConfiguration(t *testing.T) { PassHostHeader: Bool(true), }}, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -95,9 +96,10 @@ func TestBuildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -137,6 +139,7 @@ func TestBuildConfiguration(t *testing.T) { PassHostHeader: Bool(true), }}, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -188,6 +191,7 @@ func TestBuildConfiguration(t *testing.T) { PassHostHeader: Bool(true), }}, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -237,9 +241,10 @@ func TestBuildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -292,6 +297,7 @@ func TestBuildConfiguration(t *testing.T) { PassHostHeader: Bool(true), }}, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -352,6 +358,7 @@ func TestBuildConfiguration(t *testing.T) { PassHostHeader: Bool(true), }}, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -407,6 +414,7 @@ func TestBuildConfiguration(t *testing.T) { PassHostHeader: Bool(true), }}, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -451,6 +459,7 @@ func TestBuildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -491,6 +500,7 @@ func TestBuildConfiguration(t *testing.T) { PassHostHeader: Bool(true), }}, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -535,6 +545,7 @@ func TestBuildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -577,6 +588,7 @@ func TestBuildConfiguration(t *testing.T) { Rule: "Host(`foo.com`)", }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -620,6 +632,7 @@ func TestBuildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -669,6 +682,7 @@ func TestBuildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -708,8 +722,9 @@ func TestBuildConfiguration(t *testing.T) { Rule: "Host(`app2.marathon.localhost`)", }, }, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -778,6 +793,7 @@ func TestBuildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -840,6 +856,7 @@ func TestBuildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -893,6 +910,7 @@ func TestBuildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -943,6 +961,7 @@ func TestBuildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -996,6 +1015,7 @@ func TestBuildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1038,6 +1058,7 @@ func TestBuildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1081,6 +1102,7 @@ func TestBuildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1129,6 +1151,7 @@ func TestBuildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1151,9 +1174,10 @@ func TestBuildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1177,9 +1201,10 @@ func TestBuildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1203,9 +1228,10 @@ func TestBuildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1229,9 +1255,10 @@ func TestBuildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1256,9 +1283,10 @@ func TestBuildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1283,9 +1311,10 @@ func TestBuildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1329,6 +1358,7 @@ func TestBuildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1372,6 +1402,7 @@ func TestBuildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1414,6 +1445,7 @@ func TestBuildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1455,9 +1487,10 @@ func TestBuildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1496,9 +1529,10 @@ func TestBuildConfiguration(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1533,9 +1567,10 @@ func TestBuildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1578,9 +1613,10 @@ func TestBuildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1620,9 +1656,10 @@ func TestBuildConfiguration(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1666,9 +1703,10 @@ func TestBuildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1731,6 +1769,7 @@ func TestBuildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1790,6 +1829,7 @@ func TestBuildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, diff --git a/pkg/provider/rancher/config_test.go b/pkg/provider/rancher/config_test.go index bac70649f..2f94c559e 100644 --- a/pkg/provider/rancher/config_test.go +++ b/pkg/provider/rancher/config_test.go @@ -61,6 +61,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -128,6 +129,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -198,6 +200,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -247,6 +250,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -272,9 +276,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -300,9 +305,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -350,6 +356,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -379,9 +386,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -430,6 +438,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -488,6 +497,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -541,9 +551,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -591,6 +602,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -637,9 +649,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -683,9 +696,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -725,9 +739,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -773,9 +788,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -820,9 +836,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -896,6 +913,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -966,6 +984,7 @@ func Test_buildConfiguration(t *testing.T) { }, }, }, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1005,9 +1024,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1046,9 +1066,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.TCPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, @@ -1089,9 +1110,10 @@ func Test_buildConfiguration(t *testing.T) { Services: map[string]*dynamic.UDPService{}, }, HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, }, }, }, diff --git a/pkg/rules/rules.go b/pkg/rules/rules.go index 2eeb3ee3b..3f709eae7 100644 --- a/pkg/rules/rules.go +++ b/pkg/rules/rules.go @@ -116,7 +116,11 @@ func host(route *mux.Route, hosts ...string) error { route.MatcherFunc(func(req *http.Request, _ *mux.RouteMatch) bool { reqHost := requestdecorator.GetCanonizedHost(req.Context()) if len(reqHost) == 0 { - log.FromContext(req.Context()).Warnf("Could not retrieve CanonizedHost, rejecting %s", req.Host) + // If the request is an HTTP/1.0 request, then a Host may not be defined. + if req.ProtoAtLeast(1, 1) { + log.FromContext(req.Context()).Warnf("Could not retrieve CanonizedHost, rejecting %s", req.Host) + } + return false } diff --git a/pkg/server/configurationwatcher.go b/pkg/server/configurationwatcher.go index c7541de80..c46ac5785 100644 --- a/pkg/server/configurationwatcher.go +++ b/pkg/server/configurationwatcher.go @@ -12,6 +12,7 @@ import ( "github.com/traefik/traefik/v2/pkg/log" "github.com/traefik/traefik/v2/pkg/provider" "github.com/traefik/traefik/v2/pkg/safe" + "github.com/traefik/traefik/v2/pkg/tls" ) // ConfigurationWatcher watches configuration changes. @@ -164,6 +165,16 @@ func (c *ConfigurationWatcher) preLoadConfiguration(configMsg dynamic.Message) { if copyConf.TLS != nil { copyConf.TLS.Certificates = nil + if copyConf.TLS.Options != nil { + cleanedOptions := make(map[string]tls.Options, len(copyConf.TLS.Options)) + for name, option := range copyConf.TLS.Options { + option.ClientAuth.CAFiles = []tls.FileOrContent{} + cleanedOptions[name] = option + } + + copyConf.TLS.Options = cleanedOptions + } + for k := range copyConf.TLS.Stores { st := copyConf.TLS.Stores[k] st.DefaultCertificate = nil @@ -171,6 +182,13 @@ func (c *ConfigurationWatcher) preLoadConfiguration(configMsg dynamic.Message) { } } + if copyConf.HTTP != nil { + for _, transport := range copyConf.HTTP.ServersTransports { + transport.Certificates = tls.Certificates{} + transport.RootCAs = []tls.FileOrContent{} + } + } + jsonConf, err := json.Marshal(copyConf) if err != nil { logger.Errorf("Could not marshal dynamic configuration: %v", err) diff --git a/pkg/server/service/roundtripper.go b/pkg/server/service/roundtripper.go index 364cd9e69..ee1bdd768 100644 --- a/pkg/server/service/roundtripper.go +++ b/pkg/server/service/roundtripper.go @@ -132,13 +132,19 @@ func createRoundTripper(cfg *dynamic.ServersTransport) (http.RoundTripper, error transport.IdleConnTimeout = time.Duration(cfg.ForwardingTimeouts.IdleConnTimeout) } - if cfg.InsecureSkipVerify || len(cfg.RootCAs) > 0 || len(cfg.ServerName) > 0 || len(cfg.Certificates) > 0 { + if cfg.InsecureSkipVerify || len(cfg.RootCAs) > 0 || len(cfg.ServerName) > 0 || len(cfg.Certificates) > 0 || cfg.PeerCertURI != "" { transport.TLSClientConfig = &tls.Config{ ServerName: cfg.ServerName, InsecureSkipVerify: cfg.InsecureSkipVerify, RootCAs: createRootCACertPool(cfg.RootCAs), Certificates: cfg.Certificates.GetCertificates(), } + + if cfg.PeerCertURI != "" { + transport.TLSClientConfig.VerifyPeerCertificate = func(rawCerts [][]byte, _ [][]*x509.Certificate) error { + return traefiktls.VerifyPeerCertificate(cfg.PeerCertURI, transport.TLSClientConfig, rawCerts) + } + } } // Return directly HTTP/1.1 transport when HTTP/2 is disabled diff --git a/pkg/tls/certificate.go b/pkg/tls/certificate.go index b18f867e3..6862145a2 100644 --- a/pkg/tls/certificate.go +++ b/pkg/tls/certificate.go @@ -3,7 +3,9 @@ package tls import ( "crypto/tls" "crypto/x509" + "errors" "fmt" + "net/url" "os" "sort" "strings" @@ -273,3 +275,86 @@ func (c *Certificates) Set(value string) error { func (c *Certificates) Type() string { return "certificates" } + +// VerifyPeerCertificate verifies the chain certificates and their URI. +func VerifyPeerCertificate(uri string, cfg *tls.Config, rawCerts [][]byte) error { + // TODO: Refactor to avoid useless verifyChain (ex: when insecureskipverify is false) + cert, err := verifyChain(cfg.RootCAs, rawCerts) + if err != nil { + return err + } + + if len(uri) > 0 { + return verifyServerCertMatchesURI(uri, cert) + } + + return nil +} + +// verifyServerCertMatchesURI is used on tls connections dialed to a server +// to ensure that the certificate it presented has the correct URI. +func verifyServerCertMatchesURI(uri string, cert *x509.Certificate) error { + if cert == nil { + return errors.New("peer certificate mismatch: no peer certificate presented") + } + + // Our certs will only ever have a single URI for now so only check that + if len(cert.URIs) < 1 { + return errors.New("peer certificate mismatch: peer certificate invalid") + } + + gotURI := cert.URIs[0] + + // Override the hostname since we rely on x509 constraints to limit ability to spoof the trust domain if needed + // (i.e. because a root is shared with other PKI or Consul clusters). + // This allows for seamless migrations between trust domains. + + expectURI := &url.URL{} + id, err := url.Parse(uri) + if err != nil { + return fmt.Errorf("%q is not a valid URI", uri) + } + *expectURI = *id + expectURI.Host = gotURI.Host + + if strings.EqualFold(gotURI.String(), expectURI.String()) { + return nil + } + + return fmt.Errorf("peer certificate mismatch got %s, want %s", gotURI, uri) +} + +// verifyChain performs standard TLS verification without enforcing remote hostname matching. +func verifyChain(rootCAs *x509.CertPool, rawCerts [][]byte) (*x509.Certificate, error) { + // Fetch leaf and intermediates. This is based on code form tls handshake. + if len(rawCerts) < 1 { + return nil, errors.New("tls: no certificates from peer") + } + + certs := make([]*x509.Certificate, len(rawCerts)) + for i, asn1Data := range rawCerts { + cert, err := x509.ParseCertificate(asn1Data) + if err != nil { + return nil, fmt.Errorf("tls: failed to parse certificate from peer: %w", err) + } + + certs[i] = cert + } + + opts := x509.VerifyOptions{ + Roots: rootCAs, + Intermediates: x509.NewCertPool(), + } + + // All but the first cert are intermediates + for _, cert := range certs[1:] { + opts.Intermediates.AddCert(cert) + } + + _, err := certs[0].Verify(opts) + if err != nil { + return nil, err + } + + return certs[0], nil +} diff --git a/script/gcg/traefik-bugfix.toml b/script/gcg/traefik-bugfix.toml index 8d51a360c..4d1cc91d3 100644 --- a/script/gcg/traefik-bugfix.toml +++ b/script/gcg/traefik-bugfix.toml @@ -4,11 +4,11 @@ RepositoryName = "traefik" OutputType = "file" FileName = "traefik_changelog.md" -# example new bugfix v2.4.9 +# example new bugfix v2.4.11 CurrentRef = "v2.4" -PreviousRef = "v2.4.8" +PreviousRef = "v2.4.9" BaseBranch = "v2.4" -FutureCurrentRefName = "v2.4.9" +FutureCurrentRefName = "v2.4.11" ThresholdPreviousRef = 10 ThresholdCurrentRef = 10 diff --git a/webui/src/components/_commons/PanelMiddlewares.vue b/webui/src/components/_commons/PanelMiddlewares.vue index 63714ff29..17de1d43e 100644 --- a/webui/src/components/_commons/PanelMiddlewares.vue +++ b/webui/src/components/_commons/PanelMiddlewares.vue @@ -1131,7 +1131,7 @@ export default { return exData }, getProviderLogoPath (provider) { - const name = provider.name.toLowerCase() + const name = provider.toLowerCase() if (name.includes('plugin-')) { return 'statics/providers/plugin.svg'