diff --git a/Makefile b/Makefile index 5767218e25..a1c4e01834 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,9 @@ gen-helm-docs: ## Generate Helm reference docs from values.yaml and update Consu copy-crds-to-chart: ## Copy generated CRD YAML into charts/consul. Usage: make copy-crds-to-chart @cd hack/copy-crds-to-chart; go run ./... +camel-crds: ## Convert snake_case keys in yaml to camelCase. Usage: make camel-crds + @cd hack/camel-crds; go run ./... + generate-external-crds: ## Generate CRDs for externally defined CRDs and copy them to charts/consul. Usage: make generate-external-crds @cd ./control-plane/config/crd/external; \ kustomize build | yq --split-exp '.metadata.name + ".yaml"' --no-doc @@ -174,6 +177,7 @@ lint: cni-plugin-lint ## Run linter in the control-plane, cli, and acceptance di ctrl-manifests: get-controller-gen ## Generate CRD manifests. make ensure-controller-gen-version cd control-plane; $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases + make camel-crds make copy-crds-to-chart make generate-external-crds make add-copyright-header @@ -185,7 +189,7 @@ ifeq (, $(shell which controller-gen)) CONTROLLER_GEN_TMP_DIR=$$(mktemp -d) ;\ cd $$CONTROLLER_GEN_TMP_DIR ;\ go mod init tmp ;\ - go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.8.0 ;\ + go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.12.1 ;\ rm -rf $$CONTROLLER_GEN_TMP_DIR ;\ } CONTROLLER_GEN=$(shell go env GOPATH)/bin/controller-gen @@ -193,9 +197,9 @@ else CONTROLLER_GEN=$(shell which controller-gen) endif -ensure-controller-gen-version: ## Ensure controller-gen version is v0.8.0. -ifeq (, $(shell $(CONTROLLER_GEN) --version | grep v0.8.0)) - @echo "controller-gen version is not v0.8.0, uninstall the binary and install the correct version with 'make get-controller-gen'." +ensure-controller-gen-version: ## Ensure controller-gen version is v0.12.1. +ifeq (, $(shell $(CONTROLLER_GEN) --version | grep v0.12.1)) + @echo "controller-gen version is not v0.12.1, uninstall the binary and install the correct version with 'make get-controller-gen'." @echo "Found version: $(shell $(CONTROLLER_GEN) --version)" @exit 1 else @@ -314,4 +318,4 @@ DOCKER_HUB_USER=$(shell cat $(HOME)/.dockerhub) GIT_COMMIT?=$(shell git rev-parse --short HEAD) GIT_DIRTY?=$(shell test -n "`git status --porcelain`" && echo "+CHANGES" || true) GIT_DESCRIBE?=$(shell git describe --tags --always) -CRD_OPTIONS ?= "crd:allowDangerousTypes=true" +CRD_OPTIONS ?= "crd:ignoreUnexportedFields=true,allowDangerousTypes=true" diff --git a/acceptance/tests/cloud/load/remote.go b/acceptance/tests/cloud/load/remote.go index 906c050f70..5ce2ffa286 100644 --- a/acceptance/tests/cloud/load/remote.go +++ b/acceptance/tests/cloud/load/remote.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package load import ( diff --git a/acceptance/tests/fixtures/bases/trafficpermissions/trafficpermissions.yaml b/acceptance/tests/fixtures/bases/trafficpermissions/trafficpermissions.yaml index f43bd2d62f..ed5c0436ed 100644 --- a/acceptance/tests/fixtures/bases/trafficpermissions/trafficpermissions.yaml +++ b/acceptance/tests/fixtures/bases/trafficpermissions/trafficpermissions.yaml @@ -8,7 +8,7 @@ metadata: spec: destination: identityName: multiport - action: allow + action: ACTION_ALLOW permissions: - sources: - identityName: static-client diff --git a/acceptance/tests/fixtures/cases/trafficpermissions-deny/patch.yaml b/acceptance/tests/fixtures/cases/trafficpermissions-deny/patch.yaml index 037859194f..e1220bcba5 100644 --- a/acceptance/tests/fixtures/cases/trafficpermissions-deny/patch.yaml +++ b/acceptance/tests/fixtures/cases/trafficpermissions-deny/patch.yaml @@ -6,4 +6,4 @@ kind: TrafficPermissions metadata: name: client-to-server spec: - action: deny + action: ACTION_DENY diff --git a/charts/consul/templates/connect-inject-clusterrole.yaml b/charts/consul/templates/connect-inject-clusterrole.yaml index f99d82409f..2506637949 100644 --- a/charts/consul/templates/connect-inject-clusterrole.yaml +++ b/charts/consul/templates/connect-inject-clusterrole.yaml @@ -93,6 +93,32 @@ rules: - get - patch - update +- apiGroups: + - mesh.consul.hashicorp.com + resources: + - grpcroutes + - httproutes + - tcproutes + - proxyconfigurations + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - mesh.consul.hashicorp.com + resources: + - grpcroutes/status + - httproutes/status + - tcproutes/status + - proxyconfigurations/status + verbs: + - get + - patch + - update {{- end }} - apiGroups: [ "" ] resources: [ "secrets", "serviceaccounts", "endpoints", "services", "namespaces", "nodes" ] diff --git a/charts/consul/templates/crd-controlplanerequestlimits.yaml b/charts/consul/templates/crd-controlplanerequestlimits.yaml index dceaea1ece..1939a8d373 100644 --- a/charts/consul/templates/crd-controlplanerequestlimits.yaml +++ b/charts/consul/templates/crd-controlplanerequestlimits.yaml @@ -1,18 +1,16 @@ {{- if .Values.connectInject.enabled }} ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: controlplanerequestlimits.consul.hashicorp.com + controller-gen.kubebuilder.io/version: v0.12.1 labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd + name: controlplanerequestlimits.consul.hashicorp.com spec: group: consul.hashicorp.com names: @@ -194,10 +192,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-exportedservices.yaml b/charts/consul/templates/crd-exportedservices.yaml index 591500cb12..081a2b0cf0 100644 --- a/charts/consul/templates/crd-exportedservices.yaml +++ b/charts/consul/templates/crd-exportedservices.yaml @@ -1,18 +1,16 @@ {{- if .Values.connectInject.enabled }} ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: exportedservices.consul.hashicorp.com + controller-gen.kubebuilder.io/version: v0.12.1 labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd + name: exportedservices.consul.hashicorp.com spec: group: consul.hashicorp.com names: @@ -138,10 +136,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-gatewayclassconfigs.yaml b/charts/consul/templates/crd-gatewayclassconfigs.yaml index 67eb30944f..130db72a22 100644 --- a/charts/consul/templates/crd-gatewayclassconfigs.yaml +++ b/charts/consul/templates/crd-gatewayclassconfigs.yaml @@ -1,18 +1,16 @@ {{- if .Values.connectInject.enabled }} ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: gatewayclassconfigs.consul.hashicorp.com + controller-gen.kubebuilder.io/version: v0.12.1 labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd + name: gatewayclassconfigs.consul.hashicorp.com spec: group: consul.hashicorp.com names: @@ -200,10 +198,4 @@ spec: type: object served: true storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-gatewayclasses.yaml b/charts/consul/templates/crd-gatewayclasses-external.yaml similarity index 100% rename from charts/consul/templates/crd-gatewayclasses.yaml rename to charts/consul/templates/crd-gatewayclasses-external.yaml diff --git a/charts/consul/templates/crd-gatewaypolicies.yaml b/charts/consul/templates/crd-gatewaypolicies.yaml index 54779f4356..1cdfa331f5 100644 --- a/charts/consul/templates/crd-gatewaypolicies.yaml +++ b/charts/consul/templates/crd-gatewaypolicies.yaml @@ -1,18 +1,16 @@ {{- if .Values.connectInject.enabled }} ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: gatewaypolicies.consul.hashicorp.com + controller-gen.kubebuilder.io/version: v0.12.1 labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd + name: gatewaypolicies.consul.hashicorp.com spec: group: consul.hashicorp.com names: @@ -281,10 +279,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-gateways.yaml b/charts/consul/templates/crd-gateways-external.yaml similarity index 100% rename from charts/consul/templates/crd-gateways.yaml rename to charts/consul/templates/crd-gateways-external.yaml diff --git a/charts/consul/templates/crd-grpcroutes-external.yaml b/charts/consul/templates/crd-grpcroutes-external.yaml new file mode 100644 index 0000000000..3e4aa75853 --- /dev/null +++ b/charts/consul/templates/crd-grpcroutes-external.yaml @@ -0,0 +1,769 @@ +{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + gateway.networking.k8s.io/bundle-version: v0.6.2 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + name: grpcroutes.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: GRPCRoute + listKind: GRPCRouteList + plural: grpcroutes + singular: grpcroute + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.hostnames + name: Hostnames + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha2 + schema: + openAPIV3Schema: + description: "GRPCRoute provides a way to route gRPC requests. This includes the capability to match requests by hostname, gRPC service, gRPC method, or HTTP/2 header. Filters can be used to specify additional processing steps. Backends specify where matching requests will be routed. \n GRPCRoute falls under extended support within the Gateway API. Within the following specification, the word \"MUST\" indicates that an implementation supporting GRPCRoute must conform to the indicated requirement, but an implementation not supporting this route type need not follow the requirement unless explicitly indicated. \n Implementations supporting `GRPCRoute` with the `HTTPS` `ProtocolType` MUST accept HTTP/2 connections without an initial upgrade from HTTP/1.1, i.e. via ALPN. If the implementation does not support this, then it MUST set the \"Accepted\" condition to \"False\" for the affected listener with a reason of \"UnsupportedProtocol\". Implementations MAY also accept HTTP/2 connections with an upgrade from HTTP/1. \n Implementations supporting `GRPCRoute` with the `HTTP` `ProtocolType` MUST support HTTP/2 over cleartext TCP (h2c, https://www.rfc-editor.org/rfc/rfc7540#section-3.1) without an initial upgrade from HTTP/1.1, i.e. with prior knowledge (https://www.rfc-editor.org/rfc/rfc7540#section-3.4). If the implementation does not support this, then it MUST set the \"Accepted\" condition to \"False\" for the affected listener with a reason of \"UnsupportedProtocol\". Implementations MAY also accept HTTP/2 connections with an upgrade from HTTP/1, i.e. without prior knowledge. \n Support: Extended" + 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 GRPCRoute. + properties: + hostnames: + description: "Hostnames defines a set of hostnames to match against the GRPC Host header to select a GRPCRoute to process the request. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label MUST appear by itself as the first label. \n If a hostname is specified by both the Listener and GRPCRoute, there MUST be at least one intersecting hostname for the GRPCRoute to be attached to the Listener. For example: \n * A Listener with `test.example.com` as the hostname matches GRPCRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches GRPCRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `test.example.com` and `*.example.com` would both match. On the other hand, `example.com` and `test.example.net` would not match. \n Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. \n If both the Listener and GRPCRoute have specified hostnames, any GRPCRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the GRPCRoute specified `test.example.com` and `test.example.net`, `test.example.net` MUST NOT be considered for a match. \n If both the Listener and GRPCRoute have specified hostnames, and none match with the criteria above, then the GRPCRoute MUST NOT be accepted by the implementation. The implementation MUST raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. \n If a Route (A) of type HTTPRoute or GRPCRoute is attached to a Listener and that listener already has another Route (B) of the other type attached and the intersection of the hostnames of A and B is non-empty, then the implementation MUST accept exactly one of these two routes, determined by the following criteria, in order: \n * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by \"{namespace}/{name}\". \n The rejected Route MUST raise an 'Accepted' condition with a status of 'False' in the corresponding RouteParentStatus. \n Support: Core" + items: + description: "Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \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`). \n Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed." + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + maxItems: 16 + type: array + parentRefs: + description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." + items: + description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: "Name is the name of the referent. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + rules: + default: + - matches: + - method: + type: Exact + description: Rules are a list of GRPC matchers, filters and actions. + items: + description: GRPCRouteRule defines the semantics for matching an gRPC request based on conditions (matches), processing it (filters), and forwarding the request to an API object (backendRefs). + properties: + backendRefs: + description: "BackendRefs defines the backend(s) where matching requests should be sent. \n Failure behavior here depends on how many BackendRefs are specified and how many are invalid. \n If *all* entries in BackendRefs are invalid, and there are also no filters specified in this route rule, *all* traffic which matches this rule MUST receive an `UNAVAILABLE` status. \n See the GRPCBackendRef definition for the rules about what makes a single GRPCBackendRef invalid. \n When a GRPCBackendRef is invalid, `UNAVAILABLE` statuses MUST be returned for requests that would have otherwise been routed to an invalid backend. If multiple backends are specified, and some are invalid, the proportion of requests that would otherwise have been routed to an invalid backend MUST receive an `UNAVAILABLE` status. \n For example, if two backends are specified with equal weights, and one is invalid, 50 percent of traffic MUST receive an `UNAVAILABLE` status. Implementations may choose how that 50 percent is determined. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Core" + items: + description: GRPCBackendRef defines how a GRPCRoute forwards a gRPC request. + properties: + filters: + description: "Filters defined at this level MUST be executed if and only if the request is being forwarded to the backend defined here. \n Support: Implementation-specific (For broader support of filters, use the Filters field in GRPCRouteRule.)" + items: + description: GRPCRouteFilter defines processing steps that must be completed during the request or response lifecycle. GRPCRouteFilters are meant as an extension point to express 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. + properties: + extensionRef: + description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" + properties: + group: + description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + 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: + description: "Add adds the given header(s) (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: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-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" + items: + type: string + maxItems: 16 + type: array + set: + 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: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" + properties: + group: + default: "" + description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + 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 backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " + properties: + add: + description: "Add adds the given header(s) (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: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-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" + items: + type: string + maxItems: 16 + type: array + set: + 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: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + 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 supporting GRPCRoute 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 - Implementation-specific: 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` MUST 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. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n " + enum: + - ResponseHeaderModifier + - RequestHeaderModifier + - RequestMirror + - ExtensionRef + type: string + required: + - type + type: object + maxItems: 16 + type: array + group: + default: "" + description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + 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 backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs 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 for this field varies based on the context where used." + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + maxItems: 16 + type: array + 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 that support GRPCRoute. - 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 implementation-specific conformance. Support: Core" + items: + description: GRPCRouteFilter defines processing steps that must be completed during the request or response lifecycle. GRPCRouteFilters are meant as an extension point to express 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. + properties: + extensionRef: + description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" + properties: + group: + description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + 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: + description: "Add adds the given header(s) (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: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-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" + items: + type: string + maxItems: 16 + type: array + set: + 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: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" + properties: + group: + default: "" + description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + 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 backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " + properties: + add: + description: "Add adds the given header(s) (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: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-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" + items: + type: string + maxItems: 16 + type: array + set: + 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: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + 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 supporting GRPCRoute 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 - Implementation-specific: 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` MUST 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. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n " + enum: + - ResponseHeaderModifier + - RequestHeaderModifier + - RequestMirror + - ExtensionRef + type: string + required: + - type + type: object + maxItems: 16 + type: array + matches: + default: + - method: + type: Exact + description: "Matches define conditions used for matching the rule against incoming gRPC 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: - method: service: foo.bar headers: values: version: 2 - method: service: foo.bar.v2 ``` \n For a request to match against this rule, it MUST satisfy EITHER of the two conditions: \n - service of foo.bar AND contains the header `version: 2` - service of foo.bar.v2 \n See the documentation for GRPCRouteMatch on how to specify multiple match conditions to be ANDed together. \n If no matches are specified, the implementation MUST match every gRPC request. \n Proxy or Load Balancer routing configuration generated from GRPCRoutes MUST prioritize rules based on the following criteria, continuing on ties. Merging MUST not be done between GRPCRoutes and HTTPRoutes. Precedence MUST be given to the rule with the largest number of: \n * Characters in a matching non-wildcard hostname. * Characters in a matching hostname. * Characters in a matching service. * Characters in a matching method. * 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. * The Route appearing first in alphabetical order by \"{namespace}/{name}\". \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: "GRPCRouteMatch 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 gRPC request only if its service is `foo` AND it contains the `version: v1` header: \n ``` matches: - method: type: Exact service: \"foo\" headers: - name: \"version\" value \"v1\" \n ```" + properties: + headers: + description: Headers specifies gRPC request header matchers. Multiple match values are ANDed together, meaning, a request MUST match all the specified headers to select the route. + items: + description: GRPCHeaderMatch describes how to select a gRPC route by matching gRPC request headers. + properties: + name: + description: "Name is the name of the gRPC Header to be matched. \n If multiple entries specify equivalent header names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: Type specifies how to match against the value of the header. + enum: + - Exact + - RegularExpression + type: string + value: + description: Value is the value of the gRPC Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + method: + default: + type: Exact + description: Method specifies a gRPC request service/method matcher. If this field is not specified, all services and methods will match. + properties: + method: + description: "Value of the method to match against. If left empty or omitted, will match all services. \n At least one of Service and Method MUST be a non-empty string. \n A GRPC Method must be a valid Protobuf Method (https://protobuf.com/docs/language-spec#methods)." + maxLength: 1024 + pattern: ^[A-Za-z_][A-Za-z_0-9]*$ + type: string + service: + description: "Value of the service to match against. If left empty or omitted, will match any service. \n At least one of Service and Method MUST be a non-empty string. \n A GRPC Service must be a valid Protobuf Type Name (https://protobuf.com/docs/language-spec#type-references)." + maxLength: 1024 + pattern: ^(?i)\.?[a-z_][a-z_0-9]*(\.[a-z_][a-z_0-9]*)*$ + type: string + type: + default: Exact + description: "Type specifies how to match against the service and/or method. Support: Core (Exact with service and method specified) \n Support: Implementation-specific (Exact with method specified but no service specified) \n Support: Implementation-specific (RegularExpression)" + enum: + - Exact + - RegularExpression + type: string + type: object + type: object + maxItems: 8 + type: array + type: object + maxItems: 16 + type: array + type: object + status: + description: Status defines the current state of GRPCRoute. + properties: + parents: + description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." + items: + description: RouteParentStatus describes the status of a route with respect to an associated Parent. + properties: + conditions: + description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." + 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, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + 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 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/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). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: "Name is the name of the referent. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +{{- end }} diff --git a/charts/consul/templates/crd-grpcroutes.yaml b/charts/consul/templates/crd-grpcroutes.yaml index 3e4aa75853..31812fff35 100644 --- a/charts/consul/templates/crd-grpcroutes.yaml +++ b/charts/consul/templates/crd-grpcroutes.yaml @@ -1,769 +1,617 @@ -{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - +{{- if .Values.connectInject.enabled }} apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + controller-gen.kubebuilder.io/version: v0.12.1 labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd - gateway.networking.k8s.io/bundle-version: v0.6.2 - gateway.networking.k8s.io/channel: experimental - creationTimestamp: null - name: grpcroutes.gateway.networking.k8s.io + name: grpcroutes.mesh.consul.hashicorp.com spec: - group: gateway.networking.k8s.io + group: mesh.consul.hashicorp.com names: - categories: - - gateway-api kind: GRPCRoute listKind: GRPCRouteList plural: grpcroutes + shortNames: + - grpc-route singular: grpcroute scope: Namespaced versions: - - additionalPrinterColumns: - - jsonPath: .spec.hostnames - name: Hostnames - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha2 - schema: - openAPIV3Schema: - description: "GRPCRoute provides a way to route gRPC requests. This includes the capability to match requests by hostname, gRPC service, gRPC method, or HTTP/2 header. Filters can be used to specify additional processing steps. Backends specify where matching requests will be routed. \n GRPCRoute falls under extended support within the Gateway API. Within the following specification, the word \"MUST\" indicates that an implementation supporting GRPCRoute must conform to the indicated requirement, but an implementation not supporting this route type need not follow the requirement unless explicitly indicated. \n Implementations supporting `GRPCRoute` with the `HTTPS` `ProtocolType` MUST accept HTTP/2 connections without an initial upgrade from HTTP/1.1, i.e. via ALPN. If the implementation does not support this, then it MUST set the \"Accepted\" condition to \"False\" for the affected listener with a reason of \"UnsupportedProtocol\". Implementations MAY also accept HTTP/2 connections with an upgrade from HTTP/1. \n Implementations supporting `GRPCRoute` with the `HTTP` `ProtocolType` MUST support HTTP/2 over cleartext TCP (h2c, https://www.rfc-editor.org/rfc/rfc7540#section-3.1) without an initial upgrade from HTTP/1.1, i.e. with prior knowledge (https://www.rfc-editor.org/rfc/rfc7540#section-3.4). If the implementation does not support this, then it MUST set the \"Accepted\" condition to \"False\" for the affected listener with a reason of \"UnsupportedProtocol\". Implementations MAY also accept HTTP/2 connections with an upgrade from HTTP/1, i.e. without prior knowledge. \n Support: Extended" - 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 GRPCRoute. - properties: - hostnames: - description: "Hostnames defines a set of hostnames to match against the GRPC Host header to select a GRPCRoute to process the request. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label MUST appear by itself as the first label. \n If a hostname is specified by both the Listener and GRPCRoute, there MUST be at least one intersecting hostname for the GRPCRoute to be attached to the Listener. For example: \n * A Listener with `test.example.com` as the hostname matches GRPCRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches GRPCRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `test.example.com` and `*.example.com` would both match. On the other hand, `example.com` and `test.example.net` would not match. \n Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. \n If both the Listener and GRPCRoute have specified hostnames, any GRPCRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the GRPCRoute specified `test.example.com` and `test.example.net`, `test.example.net` MUST NOT be considered for a match. \n If both the Listener and GRPCRoute have specified hostnames, and none match with the criteria above, then the GRPCRoute MUST NOT be accepted by the implementation. The implementation MUST raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. \n If a Route (A) of type HTTPRoute or GRPCRoute is attached to a Listener and that listener already has another Route (B) of the other type attached and the intersection of the hostnames of A and B is non-empty, then the implementation MUST accept exactly one of these two routes, determined by the following criteria, in order: \n * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by \"{namespace}/{name}\". \n The rejected Route MUST raise an 'Accepted' condition with a status of 'False' in the corresponding RouteParentStatus. \n Support: Core" - items: - description: "Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \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`). \n Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed." - maxLength: 253 - minLength: 1 - pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - maxItems: 16 - type: array - parentRefs: - description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." - items: - description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - name: - description: "Name is the name of the referent. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - maxItems: 32 - type: array - rules: - default: - - matches: - - method: - type: Exact - description: Rules are a list of GRPC matchers, filters and actions. - items: - description: GRPCRouteRule defines the semantics for matching an gRPC request based on conditions (matches), processing it (filters), and forwarding the request to an API object (backendRefs). - properties: - backendRefs: - description: "BackendRefs defines the backend(s) where matching requests should be sent. \n Failure behavior here depends on how many BackendRefs are specified and how many are invalid. \n If *all* entries in BackendRefs are invalid, and there are also no filters specified in this route rule, *all* traffic which matches this rule MUST receive an `UNAVAILABLE` status. \n See the GRPCBackendRef definition for the rules about what makes a single GRPCBackendRef invalid. \n When a GRPCBackendRef is invalid, `UNAVAILABLE` statuses MUST be returned for requests that would have otherwise been routed to an invalid backend. If multiple backends are specified, and some are invalid, the proportion of requests that would otherwise have been routed to an invalid backend MUST receive an `UNAVAILABLE` status. \n For example, if two backends are specified with equal weights, and one is invalid, 50 percent of traffic MUST receive an `UNAVAILABLE` status. Implementations may choose how that 50 percent is determined. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Core" - items: - description: GRPCBackendRef defines how a GRPCRoute forwards a gRPC request. + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v2beta1 + schema: + openAPIV3Schema: + description: GRPCRoute is the Schema for the GRPC Route API + 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: "NOTE: this should align to the GAMMA/gateway-api version, + or at least be easily translatable. \n https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRoute + \n This is a Resource type." + properties: + hostnames: + description: "Hostnames are the hostnames for which this GRPCRoute + should respond to requests. \n This is only valid for north/south." + items: + type: string + type: array + parentRefs: + description: "ParentRefs references the resources (usually Services) + that a Route wants to be attached to. \n It is invalid to reference + an identical parent more than once. It is valid to reference multiple + distinct sections within the same parent resource." + items: + description: 'NOTE: roughly equivalent to structs.ResourceReference' + properties: + port: + description: For east/west this is the name of the Consul Service + port to direct traffic to or empty to imply all. For north/south + this is TBD. + type: string + ref: + description: For east/west configuration, this should point + to a Service. For north/south it should point to a Gateway. + properties: + name: + description: Name is the user-given name of the resource + (e.g. the "billing" service). + type: string + section: + description: Section identifies which part of the resource + the condition relates to. + type: string + tenancy: + description: Tenancy identifies the tenancy units (i.e. + partition, namespace) in which the resource resides. properties: - filters: - description: "Filters defined at this level MUST be executed if and only if the request is being forwarded to the backend defined here. \n Support: Implementation-specific (For broader support of filters, use the Filters field in GRPCRouteRule.)" - items: - description: GRPCRouteFilter defines processing steps that must be completed during the request or response lifecycle. GRPCRouteFilters are meant as an extension point to express 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. + namespace: + description: "Namespace further isolates resources within + a partition. https://developer.hashicorp.com/consul/docs/enterprise/namespaces + \n When using the List and WatchList endpoints, provide + the wildcard value \"*\" to list resources across + all namespaces." + type: string + partition: + description: "Partition is the topmost administrative + boundary within a cluster. https://developer.hashicorp.com/consul/docs/enterprise/admin-partitions + \n When using the List and WatchList endpoints, provide + the wildcard value \"*\" to list resources across + all partitions." + type: string + peerName: + description: "PeerName identifies which peer the resource + is imported from. https://developer.hashicorp.com/consul/docs/connect/cluster-peering + \n When using the List and WatchList endpoints, provide + the wildcard value \"*\" to list resources across + all peers." + type: string + type: object + type: + description: Type identifies the resource's type. + properties: + group: + description: Group describes the area of functionality + to which this resource type relates (e.g. "catalog", + "authorization"). + type: string + groupVersion: + description: GroupVersion is incremented when sweeping + or backward-incompatible changes are made to the group's + resource types. + type: string + kind: + description: Kind identifies the specific resource type + within the group. + type: string + type: object + type: object + type: object + type: array + rules: + description: Rules are a list of GRPC matchers, filters and actions. + items: + properties: + backendRefs: + description: "BackendRefs defines the backend(s) where matching + requests should be sent. Failure behavior here depends on + how many BackendRefs are specified and how many are invalid. + \n If all entries in BackendRefs are invalid, and there are + also no filters specified in this route rule, all traffic + which matches this rule MUST receive a 500 status code. \n + See the GRPCBackendRef definition for the rules about what + makes a single GRPCBackendRef invalid. \n When a GRPCBackendRef + is invalid, 500 status codes MUST be returned for requests + that would have otherwise been routed to an invalid backend. + If multiple backends are specified, and some are invalid, + the proportion of requests that would otherwise have been + routed to an invalid backend MUST receive a 500 status code. + \n For example, if two backends are specified with equal weights, + and one is invalid, 50 percent of traffic must receive a 500. + Implementations may choose how that 50 percent is determined." + items: + properties: + backendRef: + properties: + datacenter: + type: string + port: + description: "For east/west this is the name of the + Consul Service port to direct traffic to or empty + to imply using the same value as the parent ref. + \n For north/south this is TBD." + type: string + ref: + description: For east/west configuration, this should + point to a Service. properties: - extensionRef: - description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" + name: + description: Name is the user-given name of the + resource (e.g. the "billing" service). + type: string + section: + description: Section identifies which part of + the resource the condition relates to. + type: string + tenancy: + description: Tenancy identifies the tenancy units + (i.e. partition, namespace) in which the resource + resides. properties: - group: - description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + namespace: + description: "Namespace further isolates resources + within a partition. https://developer.hashicorp.com/consul/docs/enterprise/namespaces + \n When using the List and WatchList endpoints, + provide the wildcard value \"*\" to list + resources across all namespaces." type: string - kind: - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + partition: + description: "Partition is the topmost administrative + boundary within a cluster. https://developer.hashicorp.com/consul/docs/enterprise/admin-partitions + \n When using the List and WatchList endpoints, + provide the wildcard value \"*\" to list + resources across all partitions." type: string - name: - description: Name is the name of the referent. - maxLength: 253 - minLength: 1 + peerName: + description: "PeerName identifies which peer + the resource is imported from. https://developer.hashicorp.com/consul/docs/connect/cluster-peering + \n When using the List and WatchList endpoints, + provide the wildcard value \"*\" to list + resources across all peers." type: string - required: - - group - - kind - - name type: object - requestHeaderModifier: - description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" + type: + description: Type identifies the resource's type. properties: - add: - description: "Add adds the given header(s) (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: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-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" - items: - type: string - maxItems: 16 - type: array - set: - 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: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map + group: + description: Group describes the area of functionality + to which this resource type relates (e.g. + "catalog", "authorization"). + type: string + groupVersion: + description: GroupVersion is incremented when + sweeping or backward-incompatible changes + are made to the group's resource types. + type: string + kind: + description: Kind identifies the specific + resource type within the group. + type: string type: object - requestMirror: - description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" - properties: - backendRef: - description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" + type: object + 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. + items: + properties: + requestHeaderModifier: + description: RequestHeaderModifier defines a schema + for a filter that modifies request headers. + properties: + add: + description: Add adds the given header(s) (name, + value) to the request before the action. It + appends to any existing values associated + with the header name. + items: properties: - group: - default: "" - description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + name: type: string - kind: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + value: type: string + type: object + type: array + remove: + description: Remove the given header(s) from + the HTTP request before the action. The value + of Remove is a list of HTTP header names. + Note that the header names are case-insensitive + (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + items: + type: string + type: array + set: + description: Set overwrites the request with + the given header (name, value) before the + action. + items: + properties: name: - description: Name is the name of the referent. - maxLength: 253 - minLength: 1 type: string - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + value: type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - required: - - name type: object - required: - - backendRef - type: object - responseHeaderModifier: - description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " - properties: - add: - description: "Add adds the given header(s) (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: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-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" - items: - type: string - maxItems: 16 - type: array - set: - 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: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - 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 supporting GRPCRoute 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 - Implementation-specific: 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` MUST 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. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n " - enum: - - ResponseHeaderModifier - - RequestHeaderModifier - - RequestMirror - - ExtensionRef - type: string - required: - - type - type: object - maxItems: 16 - type: array - group: - default: "" - description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - 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 backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - weight: - default: 1 - description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs 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 for this field varies based on the context where used." - format: int32 - maximum: 1000000 - minimum: 0 - type: integer - required: - - name - type: object - maxItems: 16 - type: array - 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 that support GRPCRoute. - 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 implementation-specific conformance. Support: Core" - items: - description: GRPCRouteFilter defines processing steps that must be completed during the request or response lifecycle. GRPCRouteFilters are meant as an extension point to express 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. - properties: - extensionRef: - description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" - properties: - group: - description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - 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: - description: "Add adds the given header(s) (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: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-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" - items: - type: string - maxItems: 16 - type: array - set: - 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: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 + type: array + type: object + responseHeaderModifier: + description: ResponseHeaderModifier defines a schema + for a filter that modifies response headers. + properties: + add: + description: Add adds the given header(s) (name, + value) to the request before the action. It + appends to any existing values associated + with the header name. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + remove: + description: Remove the given header(s) from + the HTTP request before the action. The value + of Remove is a list of HTTP header names. + Note that the header names are case-insensitive + (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + items: type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map + type: array + set: + description: Set overwrites the request with + the given header (name, value) before the + action. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + type: object + urlRewrite: + description: URLRewrite defines a schema for a filter + that modifies a request during forwarding. + properties: + pathPrefix: + type: string + type: object type: object - requestMirror: - description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" - properties: - backendRef: - description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" + type: array + weight: + description: "Weight specifies the proportion of requests + forwarded to the referenced backend. This is computed + as weight/(sum of all weights in this BackendRefs 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." + format: int32 + type: integer + type: object + type: array + filters: + items: + properties: + requestHeaderModifier: + description: RequestHeaderModifier defines a schema for + a filter that modifies request headers. + properties: + add: + description: Add adds the given header(s) (name, value) + to the request before the action. It appends to + any existing values associated with the header name. + items: properties: - group: - default: "" - description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + name: type: string - kind: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + value: type: string + type: object + type: array + remove: + description: Remove the given header(s) from the HTTP + request before the action. The value of Remove is + a list of HTTP header names. Note that the header + names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + items: + type: string + type: array + set: + description: Set overwrites the request with the given + header (name, value) before the action. + items: + properties: name: - description: Name is the name of the referent. - maxLength: 253 - minLength: 1 type: string - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + value: type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - required: - - name type: object - required: - - backendRef - type: object - responseHeaderModifier: - description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " - properties: - add: - description: "Add adds the given header(s) (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: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-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" - items: - type: string - maxItems: 16 - type: array - set: - 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: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - 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 supporting GRPCRoute 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 - Implementation-specific: 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` MUST 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. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n " - enum: - - ResponseHeaderModifier - - RequestHeaderModifier - - RequestMirror - - ExtensionRef - type: string - required: - - type - type: object - maxItems: 16 - type: array - matches: - default: - - method: - type: Exact - description: "Matches define conditions used for matching the rule against incoming gRPC 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: - method: service: foo.bar headers: values: version: 2 - method: service: foo.bar.v2 ``` \n For a request to match against this rule, it MUST satisfy EITHER of the two conditions: \n - service of foo.bar AND contains the header `version: 2` - service of foo.bar.v2 \n See the documentation for GRPCRouteMatch on how to specify multiple match conditions to be ANDed together. \n If no matches are specified, the implementation MUST match every gRPC request. \n Proxy or Load Balancer routing configuration generated from GRPCRoutes MUST prioritize rules based on the following criteria, continuing on ties. Merging MUST not be done between GRPCRoutes and HTTPRoutes. Precedence MUST be given to the rule with the largest number of: \n * Characters in a matching non-wildcard hostname. * Characters in a matching hostname. * Characters in a matching service. * Characters in a matching method. * 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. * The Route appearing first in alphabetical order by \"{namespace}/{name}\". \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: "GRPCRouteMatch 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 gRPC request only if its service is `foo` AND it contains the `version: v1` header: \n ``` matches: - method: type: Exact service: \"foo\" headers: - name: \"version\" value \"v1\" \n ```" - properties: - headers: - description: Headers specifies gRPC request header matchers. Multiple match values are ANDed together, meaning, a request MUST match all the specified headers to select the route. - items: - description: GRPCHeaderMatch describes how to select a gRPC route by matching gRPC request headers. - properties: - name: - description: "Name is the name of the gRPC Header to be matched. \n If multiple entries specify equivalent header names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - type: - default: Exact - description: Type specifies how to match against the value of the header. - enum: - - Exact - - RegularExpression - type: string - value: - description: Value is the value of the gRPC Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - method: - default: - type: Exact - description: Method specifies a gRPC request service/method matcher. If this field is not specified, all services and methods will match. - properties: - method: - description: "Value of the method to match against. If left empty or omitted, will match all services. \n At least one of Service and Method MUST be a non-empty string. \n A GRPC Method must be a valid Protobuf Method (https://protobuf.com/docs/language-spec#methods)." - maxLength: 1024 - pattern: ^[A-Za-z_][A-Za-z_0-9]*$ + type: array + type: object + responseHeaderModifier: + description: ResponseHeaderModifier defines a schema for + a filter that modifies response headers. + properties: + add: + description: Add adds the given header(s) (name, value) + to the request before the action. It appends to + any existing values associated with the header name. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + remove: + description: Remove the given header(s) from the HTTP + request before the action. The value of Remove is + a list of HTTP header names. Note that the header + names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + items: type: string - service: - description: "Value of the service to match against. If left empty or omitted, will match any service. \n At least one of Service and Method MUST be a non-empty string. \n A GRPC Service must be a valid Protobuf Type Name (https://protobuf.com/docs/language-spec#type-references)." - maxLength: 1024 - pattern: ^(?i)\.?[a-z_][a-z_0-9]*(\.[a-z_][a-z_0-9]*)*$ + type: array + set: + description: Set overwrites the request with the given + header (name, value) before the action. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + type: object + urlRewrite: + description: URLRewrite defines a schema for a filter + that modifies a request during forwarding. + properties: + pathPrefix: + type: string + type: object + type: object + type: array + matches: + items: + properties: + headers: + description: Headers specifies gRPC request header matchers. + Multiple match values are ANDed together, meaning, a + request MUST match all the specified headers to select + the route. + items: + properties: + name: type: string type: - default: Exact - description: "Type specifies how to match against the service and/or method. Support: Core (Exact with service and method specified) \n Support: Implementation-specific (Exact with method specified but no service specified) \n Support: Implementation-specific (RegularExpression)" + description: "HeaderMatchType specifies the semantics + of how HTTP header values should be compared. + Valid HeaderMatchType values, along with their + conformance levels, are: \n Note that values may + be added to this enum, implementations must ensure + that unknown values will not cause a crash. \n + Unknown values here must result in the implementation + setting the Accepted Condition for the Route to + status: False, with a Reason of UnsupportedValue." enum: - - Exact - - RegularExpression + - HEADER_MATCH_TYPE_UNSPECIFIED + - HEADER_MATCH_TYPE_EXACT + - HEADER_MATCH_TYPE_REGEX + - HEADER_MATCH_TYPE_PRESENT + - HEADER_MATCH_TYPE_PREFIX + - HEADER_MATCH_TYPE_SUFFIX + format: int32 + type: string + value: type: string type: object - type: object - maxItems: 8 - type: array - type: object - maxItems: 16 - type: array - type: object - status: - description: Status defines the current state of GRPCRoute. - properties: - parents: - description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." - items: - description: RouteParentStatus describes the status of a route with respect to an associated Parent. - properties: - conditions: - description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." - 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, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + type: array + method: + description: Method specifies a gRPC request service/method + matcher. If this field is not specified, all services + and methods will match. + properties: + method: + description: "Value of the method to match against. + If left empty or omitted, will match all services. + \n At least one of Service and Method MUST be a + non-empty string.}" + type: string + service: + description: "Value of the service to match against. + If left empty or omitted, will match any service. + \n At least one of Service and Method MUST be a + non-empty string." + type: string + type: + description: 'Type specifies how to match against + the service and/or method. Support: Core (Exact + with service and method specified)' + enum: + - GRPC_METHOD_MATCH_TYPE_UNSPECIFIED + - GRPC_METHOD_MATCH_TYPE_EXACT + - GRPC_METHOD_MATCH_TYPE_REGEX + format: int32 + type: string + type: object + type: object + type: array + retries: + properties: + number: + description: Number is the number of times to retry the + request when a retryable result occurs. 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 + value: + description: The uint32 value. + format: int32 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 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - controllerName: - description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/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). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - parentRef: - description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - name: - description: "Name is the name of the referent. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + onConditions: + description: RetryOn allows setting envoy specific conditions + when a request should be automatically retried. + items: type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + type: array + onConnectFailure: + description: RetryOnConnectFailure allows for connection + failure errors to trigger a retry. + type: boolean + onStatusCodes: + description: RetryOnStatusCodes is a flat list of http response + status codes that are eligible for retry. This again should + be feasible in any reasonable proxy. + items: format: int32 - maximum: 65535 - minimum: 1 type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - required: - - controllerName - - parentRef - type: object - maxItems: 32 - type: array - required: - - parents - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] + type: array + type: object + timeouts: + description: HTTPRouteTimeouts defines timeouts that can be + configured for an HTTPRoute or GRPCRoute. + properties: + idle: + description: Idle specifies the total amount of time permitted + for the request stream to be idle. + format: duration + properties: + nanos: + description: Signed fractions of a second at nanosecond + resolution of the span of time. Durations less than + one second are represented with a 0 `seconds` field + and a positive or negative `nanos` field. For durations + of one second or more, a non-zero value for the `nanos` + field must be of the same sign as the `seconds` field. + Must be from -999,999,999 to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the span of time. Must + be from -315,576,000,000 to +315,576,000,000 inclusive. + Note: these bounds are computed from: 60 sec/min * + 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years' + format: int64 + type: integer + type: object + request: + description: RequestTimeout is the total amount of time + permitted for the entire downstream request (and retries) + to be processed. + format: duration + properties: + nanos: + description: Signed fractions of a second at nanosecond + resolution of the span of time. Durations less than + one second are represented with a 0 `seconds` field + and a positive or negative `nanos` field. For durations + of one second or more, a non-zero value for the `nanos` + field must be of the same sign as the `seconds` field. + Must be from -999,999,999 to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the span of time. Must + be from -315,576,000,000 to +315,576,000,000 inclusive. + Note: these bounds are computed from: 60 sec/min * + 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years' + format: int64 + type: integer + type: object + type: object + type: object + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} {{- end }} diff --git a/charts/consul/templates/crd-httproutes-external.yaml b/charts/consul/templates/crd-httproutes-external.yaml new file mode 100644 index 0000000000..c89591376a --- /dev/null +++ b/charts/consul/templates/crd-httproutes-external.yaml @@ -0,0 +1,1917 @@ +{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + gateway.networking.k8s.io/bundle-version: v0.6.2 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + name: httproutes.gateway.networking.k8s.io +spec: + group: gateway.networking.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 + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + deprecated: true + deprecationWarning: The v1alpha2 version of HTTPRoute has been deprecated and will be removed in a future release of the API. Please upgrade to v1beta1. + name: v1alpha2 + schema: + openAPIV3Schema: + description: HTTPRoute provides a way to route HTTP requests. This includes the capability to match requests by hostname, path, header, or query param. Filters can be used to specify additional processing steps. Backends specify where matching requests should be routed. + 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: + hostnames: + description: "Hostnames defines a set of hostname that should match against the HTTP Host header to select a HTTPRoute to process the request. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n If a hostname is specified by both the Listener and HTTPRoute, there must be at least one intersecting hostname for the HTTPRoute to be attached to the Listener. For example: \n * A Listener with `test.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `*.example.com`, `test.example.com`, and `foo.test.example.com` would all match. On the other hand, `example.com` and `test.example.net` would not match. \n Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. \n If both the Listener and HTTPRoute have specified hostnames, any HTTPRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the HTTPRoute specified `test.example.com` and `test.example.net`, `test.example.net` must not be considered for a match. \n If both the Listener and HTTPRoute have specified hostnames, and none match with the criteria above, then the HTTPRoute is not accepted. The implementation must raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. \n In the event that multiple HTTPRoutes specify intersecting hostnames (e.g. overlapping wildcard matching and exact matching hostnames), precedence must be given to rules from the HTTPRoute with the largest number of: \n * Characters in a matching non-wildcard hostname. * Characters in a matching hostname. \n If ties exist across multiple Routes, the matching precedence rules for HTTPRouteMatches takes over. \n Support: Core" + items: + description: "Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \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`). \n Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed." + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + maxItems: 16 + type: array + parentRefs: + description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." + items: + description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: "Name is the name of the referent. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + rules: + default: + - matches: + - path: + type: PathPrefix + 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 (matches), processing it (filters), and forwarding the request to an API object (backendRefs). + properties: + backendRefs: + description: "BackendRefs defines the backend(s) where matching requests should be sent. \n Failure behavior here depends on how many BackendRefs are specified and how many are invalid. \n If *all* entries in BackendRefs are invalid, and there are also no filters specified in this route rule, *all* traffic which matches this rule MUST receive a 500 status code. \n See the HTTPBackendRef definition for the rules about what makes a single HTTPBackendRef invalid. \n When a HTTPBackendRef is invalid, 500 status codes MUST be returned for requests that would have otherwise been routed to an invalid backend. If multiple backends are specified, and some are invalid, the proportion of requests that would otherwise have been routed to an invalid backend MUST receive a 500 status code. \n For example, if two backends are specified with equal weights, and one is invalid, 50 percent of traffic must receive a 500. Implementations may choose how that 50 percent is determined. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Core" + items: + description: HTTPBackendRef defines how a HTTPRoute should forward an HTTP request. + properties: + 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: Implementation-specific (For broader support of filters, use the Filters field in HTTPRouteRule.)" + items: + description: HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express 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. + properties: + extensionRef: + description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" + properties: + group: + description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + 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: + description: "Add adds the given header(s) (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: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-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" + items: + type: string + maxItems: 16 + type: array + set: + 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: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" + properties: + group: + default: "" + description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + 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 backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + requestRedirect: + description: "RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. \n Support: Core" + properties: + hostname: + description: "Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname of the request is used. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " + maxLength: 1024 + type: string + type: + description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: "Port is the port to be used in the value of the `Location` header in the response. When empty, port (if specified) of the request is used. \n Support: Extended" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: "Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status code to be used in response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Core" + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " + properties: + add: + description: "Add adds the given header(s) (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: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-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" + items: + type: string + maxItems: 16 + type: array + set: + 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: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + 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 - Implementation-specific: 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. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: "URLRewrite defines a schema for a filter that modifies a request during forwarding. \n Support: Extended \n " + properties: + hostname: + description: "Hostname is the value to be used to replace the Host header value during forwarding. \n Support: Extended \n " + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines a path rewrite. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " + maxLength: 1024 + type: string + type: + description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + group: + default: "" + description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + 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 backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs 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 for this field varies based on the context where used." + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + maxItems: 16 + type: array + 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 implementation-specific conformance. \n All filters are expected to be compatible with each other except for the URLRewrite and RequestRedirect filters, which may not be combined. If an implementation can not support other combinations of filters, they must clearly document that limitation. In all cases where incompatible or unsupported filters are specified, implementations MUST add a warning condition to status. \n Support: Core" + items: + description: HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express 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. + properties: + extensionRef: + description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" + properties: + group: + description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + 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: + description: "Add adds the given header(s) (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: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-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" + items: + type: string + maxItems: 16 + type: array + set: + 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: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" + properties: + group: + default: "" + description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + 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 backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + requestRedirect: + description: "RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. \n Support: Core" + properties: + hostname: + description: "Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname of the request is used. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " + maxLength: 1024 + type: string + type: + description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: "Port is the port to be used in the value of the `Location` header in the response. When empty, port (if specified) of the request is used. \n Support: Extended" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: "Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status code to be used in response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Core" + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " + properties: + add: + description: "Add adds the given header(s) (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: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-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" + items: + type: string + maxItems: 16 + type: array + set: + 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: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + 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 - Implementation-specific: 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. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: "URLRewrite defines a schema for a filter that modifies a request during forwarding. \n Support: Extended \n " + properties: + hostname: + description: "Hostname is the value to be used to replace the Host header value during forwarding. \n Support: Extended \n " + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines a path rewrite. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " + maxLength: 1024 + type: string + type: + description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + matches: + default: + - path: + type: PathPrefix + 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: - name: \"version\" value: \"v2\" - path: value: \"/v2/foo\" ``` \n For a request to match against this rule, a request must satisfy EITHER of the two conditions: \n - path prefixed with `/foo` AND contains the header `version: v2` - 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 Proxy or Load Balancer routing configuration generated from HTTPRoutes MUST prioritize matches based on the following criteria, continuing on ties. Across all rules specified on applicable Routes, precedence must be given to the match with the largest number of: \n * Characters in a matching path. * Header matches. * Query param 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. * The Route appearing first in alphabetical order by \"{namespace}/{name}\". \n If ties still exist within an HTTPRoute, matching precedence MUST be granted to the FIRST matching rule (in list order) with a match meeting the above criteria. \n When no rules matching a request have been successfully attached to the parent a request is coming from, a HTTP 404 status code MUST be returned." + 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: v1` header: \n ``` match: \n \tpath: \t value: \"/foo\" \theaders: \t- name: \"version\" \t value \"v1\" \n ```" + properties: + headers: + description: Headers specifies HTTP request header matchers. Multiple match values are ANDed together, meaning, a request must match all the specified headers to select the route. + items: + description: HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request headers. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent. \n When a header is repeated in an HTTP request, it is implementation-specific behavior as to how this is represented. Generally, proxies should follow the guidance from the RFC: https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding processing a repeated header, with special handling for \"Set-Cookie\"." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: "Type specifies how to match against the value of the header. \n Support: Core (Exact) \n Support: Implementation-specific (RegularExpression) \n Since RegularExpression HeaderMatchType has implementation-specific 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 + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + method: + description: "Method specifies HTTP method matcher. When specified, this route will be matched only if the request has the specified method. \n Support: Extended" + enum: + - GET + - HEAD + - POST + - PUT + - DELETE + - CONNECT + - OPTIONS + - TRACE + - PATCH + type: string + path: + default: + type: PathPrefix + 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: PathPrefix + description: "Type specifies how to match against the path Value. \n Support: Core (Exact, PathPrefix) \n Support: Implementation-specific (RegularExpression)" + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + queryParams: + description: "QueryParams specifies HTTP query parameter matchers. Multiple match values are ANDed together, meaning, a request must match all the specified query parameters to select the route. \n Support: Extended" + items: + description: HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP query parameters. + properties: + name: + description: "Name is the name of the HTTP query param to be matched. This must be an exact string match. (See https://tools.ietf.org/html/rfc7230#section-2.7.3). \n If multiple entries specify equivalent query param names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent query param name MUST be ignored. \n If a query param is repeated in an HTTP request, the behavior is purposely left undefined, since different data planes have different capabilities. However, it is *recommended* that implementations should match against the first value of the param if the data plane supports it, as this behavior is expected in other load balancing contexts outside of the Gateway API. \n Users SHOULD NOT route traffic based on repeated query params to guard themselves against potential differences in the implementations." + maxLength: 256 + minLength: 1 + type: string + type: + default: Exact + description: "Type specifies how to match against the value of the query parameter. \n Support: Extended (Exact) \n Support: Implementation-specific (RegularExpression) \n Since RegularExpression QueryParamMatchType has Implementation-specific 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 + type: string + value: + description: Value is the value of HTTP query param to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + type: object + maxItems: 16 + type: array + type: object + status: + description: Status defines the current state of HTTPRoute. + properties: + parents: + description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." + items: + description: RouteParentStatus describes the status of a route with respect to an associated Parent. + properties: + conditions: + description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." + 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, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + 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 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/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). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: "Name is the name of the referent. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + required: + - spec + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.hostnames + name: Hostnames + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: HTTPRoute provides a way to route HTTP requests. This includes the capability to match requests by hostname, path, header, or query param. Filters can be used to specify additional processing steps. Backends specify where matching requests should be routed. + 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: + hostnames: + description: "Hostnames defines a set of hostname that should match against the HTTP Host header to select a HTTPRoute to process the request. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n If a hostname is specified by both the Listener and HTTPRoute, there must be at least one intersecting hostname for the HTTPRoute to be attached to the Listener. For example: \n * A Listener with `test.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `*.example.com`, `test.example.com`, and `foo.test.example.com` would all match. On the other hand, `example.com` and `test.example.net` would not match. \n Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. \n If both the Listener and HTTPRoute have specified hostnames, any HTTPRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the HTTPRoute specified `test.example.com` and `test.example.net`, `test.example.net` must not be considered for a match. \n If both the Listener and HTTPRoute have specified hostnames, and none match with the criteria above, then the HTTPRoute is not accepted. The implementation must raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. \n In the event that multiple HTTPRoutes specify intersecting hostnames (e.g. overlapping wildcard matching and exact matching hostnames), precedence must be given to rules from the HTTPRoute with the largest number of: \n * Characters in a matching non-wildcard hostname. * Characters in a matching hostname. \n If ties exist across multiple Routes, the matching precedence rules for HTTPRouteMatches takes over. \n Support: Core" + items: + description: "Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \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`). \n Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed." + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + maxItems: 16 + type: array + parentRefs: + description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." + items: + description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: "Name is the name of the referent. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + rules: + default: + - matches: + - path: + type: PathPrefix + 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 (matches), processing it (filters), and forwarding the request to an API object (backendRefs). + properties: + backendRefs: + description: "BackendRefs defines the backend(s) where matching requests should be sent. \n Failure behavior here depends on how many BackendRefs are specified and how many are invalid. \n If *all* entries in BackendRefs are invalid, and there are also no filters specified in this route rule, *all* traffic which matches this rule MUST receive a 500 status code. \n See the HTTPBackendRef definition for the rules about what makes a single HTTPBackendRef invalid. \n When a HTTPBackendRef is invalid, 500 status codes MUST be returned for requests that would have otherwise been routed to an invalid backend. If multiple backends are specified, and some are invalid, the proportion of requests that would otherwise have been routed to an invalid backend MUST receive a 500 status code. \n For example, if two backends are specified with equal weights, and one is invalid, 50 percent of traffic must receive a 500. Implementations may choose how that 50 percent is determined. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Core" + items: + description: HTTPBackendRef defines how a HTTPRoute should forward an HTTP request. + properties: + 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: Implementation-specific (For broader support of filters, use the Filters field in HTTPRouteRule.)" + items: + description: HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express 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. + properties: + extensionRef: + description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" + properties: + group: + description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + 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: + description: "Add adds the given header(s) (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: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-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" + items: + type: string + maxItems: 16 + type: array + set: + 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: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" + properties: + group: + default: "" + description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + 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 backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + requestRedirect: + description: "RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. \n Support: Core" + properties: + hostname: + description: "Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname of the request is used. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " + maxLength: 1024 + type: string + type: + description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: "Port is the port to be used in the value of the `Location` header in the response. When empty, port (if specified) of the request is used. \n Support: Extended" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: "Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status code to be used in response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Core" + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " + properties: + add: + description: "Add adds the given header(s) (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: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-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" + items: + type: string + maxItems: 16 + type: array + set: + 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: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + 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 - Implementation-specific: 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. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: "URLRewrite defines a schema for a filter that modifies a request during forwarding. \n Support: Extended \n " + properties: + hostname: + description: "Hostname is the value to be used to replace the Host header value during forwarding. \n Support: Extended \n " + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines a path rewrite. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " + maxLength: 1024 + type: string + type: + description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + group: + default: "" + description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + 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 backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs 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 for this field varies based on the context where used." + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + maxItems: 16 + type: array + 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 implementation-specific conformance. \n All filters are expected to be compatible with each other except for the URLRewrite and RequestRedirect filters, which may not be combined. If an implementation can not support other combinations of filters, they must clearly document that limitation. In all cases where incompatible or unsupported filters are specified, implementations MUST add a warning condition to status. \n Support: Core" + items: + description: HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express 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. + properties: + extensionRef: + description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" + properties: + group: + description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + 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: + description: "Add adds the given header(s) (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: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-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" + items: + type: string + maxItems: 16 + type: array + set: + 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: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + requestMirror: + description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" + properties: + backendRef: + description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" + properties: + group: + default: "" + description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + 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 backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + required: + - backendRef + type: object + requestRedirect: + description: "RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. \n Support: Core" + properties: + hostname: + description: "Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname of the request is used. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " + maxLength: 1024 + type: string + type: + description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + port: + description: "Port is the port to be used in the value of the `Location` header in the response. When empty, port (if specified) of the request is used. \n Support: Extended" + format: int32 + maximum: 65535 + minimum: 1 + type: integer + scheme: + description: "Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Extended" + enum: + - http + - https + type: string + statusCode: + default: 302 + description: "StatusCode is the HTTP status code to be used in response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Core" + enum: + - 301 + - 302 + type: integer + type: object + responseHeaderModifier: + description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " + properties: + add: + description: "Add adds the given header(s) (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: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + remove: + description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-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" + items: + type: string + maxItems: 16 + type: array + set: + 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: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" + items: + description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + 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 - Implementation-specific: 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. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - RequestHeaderModifier + - ResponseHeaderModifier + - RequestMirror + - RequestRedirect + - URLRewrite + - ExtensionRef + type: string + urlRewrite: + description: "URLRewrite defines a schema for a filter that modifies a request during forwarding. \n Support: Extended \n " + properties: + hostname: + description: "Hostname is the value to be used to replace the Host header value during forwarding. \n Support: Extended \n " + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + path: + description: "Path defines a path rewrite. \n Support: Extended \n " + properties: + replaceFullPath: + description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " + maxLength: 1024 + type: string + replacePrefixMatch: + description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " + maxLength: 1024 + type: string + type: + description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " + enum: + - ReplaceFullPath + - ReplacePrefixMatch + type: string + required: + - type + type: object + type: object + required: + - type + type: object + maxItems: 16 + type: array + matches: + default: + - path: + type: PathPrefix + 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: - name: \"version\" value: \"v2\" - path: value: \"/v2/foo\" ``` \n For a request to match against this rule, a request must satisfy EITHER of the two conditions: \n - path prefixed with `/foo` AND contains the header `version: v2` - 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 Proxy or Load Balancer routing configuration generated from HTTPRoutes MUST prioritize matches based on the following criteria, continuing on ties. Across all rules specified on applicable Routes, precedence must be given to the match with the largest number of: \n * Characters in a matching path. * Header matches. * Query param 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. * The Route appearing first in alphabetical order by \"{namespace}/{name}\". \n If ties still exist within an HTTPRoute, matching precedence MUST be granted to the FIRST matching rule (in list order) with a match meeting the above criteria. \n When no rules matching a request have been successfully attached to the parent a request is coming from, a HTTP 404 status code MUST be returned." + 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: v1` header: \n ``` match: \n \tpath: \t value: \"/foo\" \theaders: \t- name: \"version\" \t value \"v1\" \n ```" + properties: + headers: + description: Headers specifies HTTP request header matchers. Multiple match values are ANDed together, meaning, a request must match all the specified headers to select the route. + items: + description: HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request headers. + properties: + name: + description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent. \n When a header is repeated in an HTTP request, it is implementation-specific behavior as to how this is represented. Generally, proxies should follow the guidance from the RFC: https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding processing a repeated header, with special handling for \"Set-Cookie\"." + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + type: + default: Exact + description: "Type specifies how to match against the value of the header. \n Support: Core (Exact) \n Support: Implementation-specific (RegularExpression) \n Since RegularExpression HeaderMatchType has implementation-specific 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 + type: string + value: + description: Value is the value of HTTP Header to be matched. + maxLength: 4096 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + method: + description: "Method specifies HTTP method matcher. When specified, this route will be matched only if the request has the specified method. \n Support: Extended" + enum: + - GET + - HEAD + - POST + - PUT + - DELETE + - CONNECT + - OPTIONS + - TRACE + - PATCH + type: string + path: + default: + type: PathPrefix + 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: PathPrefix + description: "Type specifies how to match against the path Value. \n Support: Core (Exact, PathPrefix) \n Support: Implementation-specific (RegularExpression)" + enum: + - Exact + - PathPrefix + - RegularExpression + type: string + value: + default: / + description: Value of the HTTP path to match against. + maxLength: 1024 + type: string + type: object + queryParams: + description: "QueryParams specifies HTTP query parameter matchers. Multiple match values are ANDed together, meaning, a request must match all the specified query parameters to select the route. \n Support: Extended" + items: + description: HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP query parameters. + properties: + name: + description: "Name is the name of the HTTP query param to be matched. This must be an exact string match. (See https://tools.ietf.org/html/rfc7230#section-2.7.3). \n If multiple entries specify equivalent query param names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent query param name MUST be ignored. \n If a query param is repeated in an HTTP request, the behavior is purposely left undefined, since different data planes have different capabilities. However, it is *recommended* that implementations should match against the first value of the param if the data plane supports it, as this behavior is expected in other load balancing contexts outside of the Gateway API. \n Users SHOULD NOT route traffic based on repeated query params to guard themselves against potential differences in the implementations." + maxLength: 256 + minLength: 1 + type: string + type: + default: Exact + description: "Type specifies how to match against the value of the query parameter. \n Support: Extended (Exact) \n Support: Implementation-specific (RegularExpression) \n Since RegularExpression QueryParamMatchType has Implementation-specific 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 + type: string + value: + description: Value is the value of HTTP query param to be matched. + maxLength: 1024 + minLength: 1 + type: string + required: + - name + - value + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + maxItems: 8 + type: array + type: object + maxItems: 16 + type: array + type: object + status: + description: Status defines the current state of HTTPRoute. + properties: + parents: + description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." + items: + description: RouteParentStatus describes the status of a route with respect to an associated Parent. + properties: + conditions: + description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." + 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, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + 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 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/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). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: "Name is the name of the referent. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +{{- end }} diff --git a/charts/consul/templates/crd-httproutes.yaml b/charts/consul/templates/crd-httproutes.yaml index c89591376a..3da6e1e637 100644 --- a/charts/consul/templates/crd-httproutes.yaml +++ b/charts/consul/templates/crd-httproutes.yaml @@ -1,1917 +1,673 @@ -{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - +{{- if .Values.connectInject.enabled }} apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + controller-gen.kubebuilder.io/version: v0.12.1 labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd - gateway.networking.k8s.io/bundle-version: v0.6.2 - gateway.networking.k8s.io/channel: experimental - creationTimestamp: null - name: httproutes.gateway.networking.k8s.io + name: httproutes.mesh.consul.hashicorp.com spec: - group: gateway.networking.k8s.io + group: mesh.consul.hashicorp.com names: - categories: - - gateway-api kind: HTTPRoute listKind: HTTPRouteList plural: httproutes + shortNames: + - http-route singular: httproute scope: Namespaced versions: - - additionalPrinterColumns: - - jsonPath: .spec.hostnames - name: Hostnames - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - deprecated: true - deprecationWarning: The v1alpha2 version of HTTPRoute has been deprecated and will be removed in a future release of the API. Please upgrade to v1beta1. - name: v1alpha2 - schema: - openAPIV3Schema: - description: HTTPRoute provides a way to route HTTP requests. This includes the capability to match requests by hostname, path, header, or query param. Filters can be used to specify additional processing steps. Backends specify where matching requests should be routed. - 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: - hostnames: - description: "Hostnames defines a set of hostname that should match against the HTTP Host header to select a HTTPRoute to process the request. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n If a hostname is specified by both the Listener and HTTPRoute, there must be at least one intersecting hostname for the HTTPRoute to be attached to the Listener. For example: \n * A Listener with `test.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `*.example.com`, `test.example.com`, and `foo.test.example.com` would all match. On the other hand, `example.com` and `test.example.net` would not match. \n Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. \n If both the Listener and HTTPRoute have specified hostnames, any HTTPRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the HTTPRoute specified `test.example.com` and `test.example.net`, `test.example.net` must not be considered for a match. \n If both the Listener and HTTPRoute have specified hostnames, and none match with the criteria above, then the HTTPRoute is not accepted. The implementation must raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. \n In the event that multiple HTTPRoutes specify intersecting hostnames (e.g. overlapping wildcard matching and exact matching hostnames), precedence must be given to rules from the HTTPRoute with the largest number of: \n * Characters in a matching non-wildcard hostname. * Characters in a matching hostname. \n If ties exist across multiple Routes, the matching precedence rules for HTTPRouteMatches takes over. \n Support: Core" - items: - description: "Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \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`). \n Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed." - maxLength: 253 - minLength: 1 - pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - maxItems: 16 - type: array - parentRefs: - description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." - items: - description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - name: - description: "Name is the name of the referent. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - maxItems: 32 - type: array - rules: - default: - - matches: - - path: - type: PathPrefix - 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 (matches), processing it (filters), and forwarding the request to an API object (backendRefs). - properties: - backendRefs: - description: "BackendRefs defines the backend(s) where matching requests should be sent. \n Failure behavior here depends on how many BackendRefs are specified and how many are invalid. \n If *all* entries in BackendRefs are invalid, and there are also no filters specified in this route rule, *all* traffic which matches this rule MUST receive a 500 status code. \n See the HTTPBackendRef definition for the rules about what makes a single HTTPBackendRef invalid. \n When a HTTPBackendRef is invalid, 500 status codes MUST be returned for requests that would have otherwise been routed to an invalid backend. If multiple backends are specified, and some are invalid, the proportion of requests that would otherwise have been routed to an invalid backend MUST receive a 500 status code. \n For example, if two backends are specified with equal weights, and one is invalid, 50 percent of traffic must receive a 500. Implementations may choose how that 50 percent is determined. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Core" - items: - description: HTTPBackendRef defines how a HTTPRoute should forward an HTTP request. + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v2beta1 + schema: + openAPIV3Schema: + description: HTTPRoute is the Schema for the HTTP Route API + 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: "NOTE: this should align to the GAMMA/gateway-api version, + or at least be easily translatable. \n https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.HTTPRoute + \n This is a Resource type." + properties: + hostnames: + description: "Hostnames are the hostnames for which this HTTPRoute + should respond to requests. \n This is only valid for north/south." + items: + type: string + type: array + parentRefs: + description: "ParentRefs references the resources (usually Services) + that a Route wants to be attached to. \n It is invalid to reference + an identical parent more than once. It is valid to reference multiple + distinct sections within the same parent resource." + items: + description: 'NOTE: roughly equivalent to structs.ResourceReference' + properties: + port: + description: For east/west this is the name of the Consul Service + port to direct traffic to or empty to imply all. For north/south + this is TBD. + type: string + ref: + description: For east/west configuration, this should point + to a Service. For north/south it should point to a Gateway. + properties: + name: + description: Name is the user-given name of the resource + (e.g. the "billing" service). + type: string + section: + description: Section identifies which part of the resource + the condition relates to. + type: string + tenancy: + description: Tenancy identifies the tenancy units (i.e. + partition, namespace) in which the resource resides. properties: - 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: Implementation-specific (For broader support of filters, use the Filters field in HTTPRouteRule.)" - items: - description: HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express 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. - properties: - extensionRef: - description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" - properties: - group: - description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - 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: - description: "Add adds the given header(s) (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: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-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" - items: - type: string - maxItems: 16 - type: array - set: - 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: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - requestMirror: - description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" - properties: - backendRef: - description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" - properties: - group: - default: "" - description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - 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 backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - required: - - name - type: object - required: - - backendRef - type: object - requestRedirect: - description: "RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. \n Support: Core" - properties: - hostname: - description: "Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname of the request is used. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - path: - description: "Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. \n Support: Extended \n " - properties: - replaceFullPath: - description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " - maxLength: 1024 - type: string - replacePrefixMatch: - description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " - maxLength: 1024 - type: string - type: - description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - port: - description: "Port is the port to be used in the value of the `Location` header in the response. When empty, port (if specified) of the request is used. \n Support: Extended" - format: int32 - maximum: 65535 - minimum: 1 - type: integer - scheme: - description: "Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Extended" - enum: - - http - - https - type: string - statusCode: - default: 302 - description: "StatusCode is the HTTP status code to be used in response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Core" - enum: - - 301 - - 302 - type: integer - type: object - responseHeaderModifier: - description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " - properties: - add: - description: "Add adds the given header(s) (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: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-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" - items: - type: string - maxItems: 16 - type: array - set: - 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: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - 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 - Implementation-specific: 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. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - RequestHeaderModifier - - ResponseHeaderModifier - - RequestMirror - - RequestRedirect - - URLRewrite - - ExtensionRef - type: string - urlRewrite: - description: "URLRewrite defines a schema for a filter that modifies a request during forwarding. \n Support: Extended \n " - properties: - hostname: - description: "Hostname is the value to be used to replace the Host header value during forwarding. \n Support: Extended \n " - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - path: - description: "Path defines a path rewrite. \n Support: Extended \n " - properties: - replaceFullPath: - description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " - maxLength: 1024 - type: string - replacePrefixMatch: - description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " - maxLength: 1024 - type: string - type: - description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - type: object - required: - - type - type: object - maxItems: 16 - type: array - group: - default: "" - description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - 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 backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + description: "Namespace further isolates resources within + a partition. https://developer.hashicorp.com/consul/docs/enterprise/namespaces + \n When using the List and WatchList endpoints, provide + the wildcard value \"*\" to list resources across + all namespaces." type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - weight: - default: 1 - description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs 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 for this field varies based on the context where used." - format: int32 - maximum: 1000000 - minimum: 0 - type: integer - required: - - name - type: object - maxItems: 16 - type: array - 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 implementation-specific conformance. \n All filters are expected to be compatible with each other except for the URLRewrite and RequestRedirect filters, which may not be combined. If an implementation can not support other combinations of filters, they must clearly document that limitation. In all cases where incompatible or unsupported filters are specified, implementations MUST add a warning condition to status. \n Support: Core" - items: - description: HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express 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. - properties: - extensionRef: - description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" - properties: - group: - description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - 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: - description: "Add adds the given header(s) (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: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-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" - items: - type: string - maxItems: 16 - type: array - set: - 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: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - requestMirror: - description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" - properties: - backendRef: - description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" - properties: - group: - default: "" - description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - 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 backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - required: - - name - type: object - required: - - backendRef - type: object - requestRedirect: - description: "RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. \n Support: Core" - properties: - hostname: - description: "Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname of the request is used. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - path: - description: "Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. \n Support: Extended \n " - properties: - replaceFullPath: - description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " - maxLength: 1024 - type: string - replacePrefixMatch: - description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " - maxLength: 1024 - type: string - type: - description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - port: - description: "Port is the port to be used in the value of the `Location` header in the response. When empty, port (if specified) of the request is used. \n Support: Extended" - format: int32 - maximum: 65535 - minimum: 1 - type: integer - scheme: - description: "Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Extended" - enum: - - http - - https - type: string - statusCode: - default: 302 - description: "StatusCode is the HTTP status code to be used in response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Core" - enum: - - 301 - - 302 - type: integer - type: object - responseHeaderModifier: - description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " - properties: - add: - description: "Add adds the given header(s) (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: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-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" - items: - type: string - maxItems: 16 - type: array - set: - 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: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - 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 - Implementation-specific: 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. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - RequestHeaderModifier - - ResponseHeaderModifier - - RequestMirror - - RequestRedirect - - URLRewrite - - ExtensionRef + partition: + description: "Partition is the topmost administrative + boundary within a cluster. https://developer.hashicorp.com/consul/docs/enterprise/admin-partitions + \n When using the List and WatchList endpoints, provide + the wildcard value \"*\" to list resources across + all partitions." type: string - urlRewrite: - description: "URLRewrite defines a schema for a filter that modifies a request during forwarding. \n Support: Extended \n " - properties: - hostname: - description: "Hostname is the value to be used to replace the Host header value during forwarding. \n Support: Extended \n " - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - path: - description: "Path defines a path rewrite. \n Support: Extended \n " - properties: - replaceFullPath: - description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " - maxLength: 1024 - type: string - replacePrefixMatch: - description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " - maxLength: 1024 - type: string - type: - description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - ReplaceFullPath - - ReplacePrefixMatch - type: string - required: - - type - type: object - type: object - required: - - type - type: object - maxItems: 16 - type: array - matches: - default: - - path: - type: PathPrefix - 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: - name: \"version\" value: \"v2\" - path: value: \"/v2/foo\" ``` \n For a request to match against this rule, a request must satisfy EITHER of the two conditions: \n - path prefixed with `/foo` AND contains the header `version: v2` - 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 Proxy or Load Balancer routing configuration generated from HTTPRoutes MUST prioritize matches based on the following criteria, continuing on ties. Across all rules specified on applicable Routes, precedence must be given to the match with the largest number of: \n * Characters in a matching path. * Header matches. * Query param 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. * The Route appearing first in alphabetical order by \"{namespace}/{name}\". \n If ties still exist within an HTTPRoute, matching precedence MUST be granted to the FIRST matching rule (in list order) with a match meeting the above criteria. \n When no rules matching a request have been successfully attached to the parent a request is coming from, a HTTP 404 status code MUST be returned." - 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: v1` header: \n ``` match: \n \tpath: \t value: \"/foo\" \theaders: \t- name: \"version\" \t value \"v1\" \n ```" - properties: - headers: - description: Headers specifies HTTP request header matchers. Multiple match values are ANDed together, meaning, a request must match all the specified headers to select the route. - items: - description: HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request headers. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent. \n When a header is repeated in an HTTP request, it is implementation-specific behavior as to how this is represented. Generally, proxies should follow the guidance from the RFC: https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding processing a repeated header, with special handling for \"Set-Cookie\"." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - type: - default: Exact - description: "Type specifies how to match against the value of the header. \n Support: Core (Exact) \n Support: Implementation-specific (RegularExpression) \n Since RegularExpression HeaderMatchType has implementation-specific 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 - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - method: - description: "Method specifies HTTP method matcher. When specified, this route will be matched only if the request has the specified method. \n Support: Extended" - enum: - - GET - - HEAD - - POST - - PUT - - DELETE - - CONNECT - - OPTIONS - - TRACE - - PATCH + peerName: + description: "PeerName identifies which peer the resource + is imported from. https://developer.hashicorp.com/consul/docs/connect/cluster-peering + \n When using the List and WatchList endpoints, provide + the wildcard value \"*\" to list resources across + all peers." type: string - path: - default: - type: PathPrefix - 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: PathPrefix - description: "Type specifies how to match against the path Value. \n Support: Core (Exact, PathPrefix) \n Support: Implementation-specific (RegularExpression)" - enum: - - Exact - - PathPrefix - - RegularExpression - type: string - value: - default: / - description: Value of the HTTP path to match against. - maxLength: 1024 - type: string - type: object - queryParams: - description: "QueryParams specifies HTTP query parameter matchers. Multiple match values are ANDed together, meaning, a request must match all the specified query parameters to select the route. \n Support: Extended" - items: - description: HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP query parameters. - properties: - name: - description: "Name is the name of the HTTP query param to be matched. This must be an exact string match. (See https://tools.ietf.org/html/rfc7230#section-2.7.3). \n If multiple entries specify equivalent query param names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent query param name MUST be ignored. \n If a query param is repeated in an HTTP request, the behavior is purposely left undefined, since different data planes have different capabilities. However, it is *recommended* that implementations should match against the first value of the param if the data plane supports it, as this behavior is expected in other load balancing contexts outside of the Gateway API. \n Users SHOULD NOT route traffic based on repeated query params to guard themselves against potential differences in the implementations." - maxLength: 256 - minLength: 1 - type: string - type: - default: Exact - description: "Type specifies how to match against the value of the query parameter. \n Support: Extended (Exact) \n Support: Implementation-specific (RegularExpression) \n Since RegularExpression QueryParamMatchType has Implementation-specific 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 - type: string - value: - description: Value is the value of HTTP query param to be matched. - maxLength: 1024 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map type: object - maxItems: 8 - type: array - type: object - maxItems: 16 - type: array - type: object - status: - description: Status defines the current state of HTTPRoute. - properties: - parents: - description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." - items: - description: RouteParentStatus describes the status of a route with respect to an associated Parent. - properties: - conditions: - description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." - 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, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + type: + description: Type identifies the resource's type. 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_])?$ + group: + description: Group describes the area of functionality + to which this resource type relates (e.g. "catalog", + "authorization"). type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown + groupVersion: + description: GroupVersion is incremented when sweeping + or backward-incompatible changes are made to the group's + resource types. 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])$ + kind: + description: Kind identifies the specific resource type + within the group. type: string - required: - - lastTransitionTime - - message - - reason - - status - - type type: object - maxItems: 8 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - controllerName: - description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/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). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - parentRef: - description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. + type: object + type: object + type: array + rules: + description: Rules are a list of HTTP-based routing rules that this + route should use for constructing a routing table. + items: + description: HTTPRouteRule specifies the routing rules used to determine + what upstream service an HTTP request is routed to. + properties: + backendRefs: + description: "BackendRefs defines the backend(s) where matching + requests should be sent. \n Failure behavior here depends + on how many BackendRefs are specified and how many are invalid. + \n If all entries in BackendRefs are invalid, and there are + also no filters specified in this route rule, all traffic + which matches this rule MUST receive a 500 status code. \n + See the HTTPBackendRef definition for the rules about what + makes a single HTTPBackendRef invalid. \n When a HTTPBackendRef + is invalid, 500 status codes MUST be returned for requests + that would have otherwise been routed to an invalid backend. + If multiple backends are specified, and some are invalid, + the proportion of requests that would otherwise have been + routed to an invalid backend MUST receive a 500 status code. + \n For example, if two backends are specified with equal weights, + and one is invalid, 50 percent of traffic must receive a 500. + Implementations may choose how that 50 percent is determined." + items: properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - name: - description: "Name is the name of the referent. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - required: - - controllerName - - parentRef - type: object - maxItems: 32 - type: array - required: - - parents - type: object - required: - - spec - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .spec.hostnames - name: Hostnames - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - description: HTTPRoute provides a way to route HTTP requests. This includes the capability to match requests by hostname, path, header, or query param. Filters can be used to specify additional processing steps. Backends specify where matching requests should be routed. - 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: - hostnames: - description: "Hostnames defines a set of hostname that should match against the HTTP Host header to select a HTTPRoute to process the request. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \n If a hostname is specified by both the Listener and HTTPRoute, there must be at least one intersecting hostname for the HTTPRoute to be attached to the Listener. For example: \n * A Listener with `test.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `*.example.com`, `test.example.com`, and `foo.test.example.com` would all match. On the other hand, `example.com` and `test.example.net` would not match. \n Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. \n If both the Listener and HTTPRoute have specified hostnames, any HTTPRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the HTTPRoute specified `test.example.com` and `test.example.net`, `test.example.net` must not be considered for a match. \n If both the Listener and HTTPRoute have specified hostnames, and none match with the criteria above, then the HTTPRoute is not accepted. The implementation must raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. \n In the event that multiple HTTPRoutes specify intersecting hostnames (e.g. overlapping wildcard matching and exact matching hostnames), precedence must be given to rules from the HTTPRoute with the largest number of: \n * Characters in a matching non-wildcard hostname. * Characters in a matching hostname. \n If ties exist across multiple Routes, the matching precedence rules for HTTPRouteMatches takes over. \n Support: Core" - items: - description: "Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: \n 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. \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`). \n Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed." - maxLength: 253 - minLength: 1 - pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - maxItems: 16 - type: array - parentRefs: - description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." - items: - description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - name: - description: "Name is the name of the referent. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - maxItems: 32 - type: array - rules: - default: - - matches: - - path: - type: PathPrefix - 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 (matches), processing it (filters), and forwarding the request to an API object (backendRefs). - properties: - backendRefs: - description: "BackendRefs defines the backend(s) where matching requests should be sent. \n Failure behavior here depends on how many BackendRefs are specified and how many are invalid. \n If *all* entries in BackendRefs are invalid, and there are also no filters specified in this route rule, *all* traffic which matches this rule MUST receive a 500 status code. \n See the HTTPBackendRef definition for the rules about what makes a single HTTPBackendRef invalid. \n When a HTTPBackendRef is invalid, 500 status codes MUST be returned for requests that would have otherwise been routed to an invalid backend. If multiple backends are specified, and some are invalid, the proportion of requests that would otherwise have been routed to an invalid backend MUST receive a 500 status code. \n For example, if two backends are specified with equal weights, and one is invalid, 50 percent of traffic must receive a 500. Implementations may choose how that 50 percent is determined. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Core" - items: - description: HTTPBackendRef defines how a HTTPRoute should forward an HTTP request. - properties: - 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: Implementation-specific (For broader support of filters, use the Filters field in HTTPRouteRule.)" - items: - description: HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express 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. + backendRef: + properties: + datacenter: + type: string + port: + description: "For east/west this is the name of the + Consul Service port to direct traffic to or empty + to imply using the same value as the parent ref. + \n For north/south this is TBD." + type: string + ref: + description: For east/west configuration, this should + point to a Service. properties: - extensionRef: - description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" + name: + description: Name is the user-given name of the + resource (e.g. the "billing" service). + type: string + section: + description: Section identifies which part of + the resource the condition relates to. + type: string + tenancy: + description: Tenancy identifies the tenancy units + (i.e. partition, namespace) in which the resource + resides. properties: - group: - description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + namespace: + description: "Namespace further isolates resources + within a partition. https://developer.hashicorp.com/consul/docs/enterprise/namespaces + \n When using the List and WatchList endpoints, + provide the wildcard value \"*\" to list + resources across all namespaces." type: string - kind: - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + partition: + description: "Partition is the topmost administrative + boundary within a cluster. https://developer.hashicorp.com/consul/docs/enterprise/admin-partitions + \n When using the List and WatchList endpoints, + provide the wildcard value \"*\" to list + resources across all partitions." type: string - name: - description: Name is the name of the referent. - maxLength: 253 - minLength: 1 + peerName: + description: "PeerName identifies which peer + the resource is imported from. https://developer.hashicorp.com/consul/docs/connect/cluster-peering + \n When using the List and WatchList endpoints, + provide the wildcard value \"*\" to list + resources across all peers." type: string - required: - - group - - kind - - name type: object - requestHeaderModifier: - description: "RequestHeaderModifier defines a schema for a filter that modifies request headers. \n Support: Core" + type: + description: Type identifies the resource's type. properties: - add: - description: "Add adds the given header(s) (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: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-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" - items: - type: string - maxItems: 16 - type: array - set: - 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: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map + group: + description: Group describes the area of functionality + to which this resource type relates (e.g. + "catalog", "authorization"). + type: string + groupVersion: + description: GroupVersion is incremented when + sweeping or backward-incompatible changes + are made to the group's resource types. + type: string + kind: + description: Kind identifies the specific + resource type within the group. + type: string type: object - requestMirror: - description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" - properties: - backendRef: - description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" + type: object + 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. + items: + properties: + requestHeaderModifier: + description: RequestHeaderModifier defines a schema + for a filter that modifies request headers. + properties: + add: + description: Add adds the given header(s) (name, + value) to the request before the action. It + appends to any existing values associated + with the header name. + items: properties: - group: - default: "" - description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - 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 backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + value: type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - required: - - name type: object - required: - - backendRef - type: object - requestRedirect: - description: "RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. \n Support: Core" - properties: - hostname: - description: "Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname of the request is used. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: array + remove: + description: Remove the given header(s) from + the HTTP request before the action. The value + of Remove is a list of HTTP header names. + Note that the header names are case-insensitive + (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + items: type: string - path: - description: "Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. \n Support: Extended \n " + type: array + set: + description: Set overwrites the request with + the given header (name, value) before the + action. + items: properties: - replaceFullPath: - description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " - maxLength: 1024 + name: + type: string + value: type: string - replacePrefixMatch: - description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " - maxLength: 1024 + type: object + type: array + type: object + responseHeaderModifier: + description: ResponseHeaderModifier defines a schema + for a filter that modifies response headers. + properties: + add: + description: Add adds the given header(s) (name, + value) to the request before the action. It + appends to any existing values associated + with the header name. + items: + properties: + name: type: string - type: - description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - ReplaceFullPath - - ReplacePrefixMatch + value: type: string - required: - - type type: object - port: - description: "Port is the port to be used in the value of the `Location` header in the response. When empty, port (if specified) of the request is used. \n Support: Extended" - format: int32 - maximum: 65535 - minimum: 1 - type: integer - scheme: - description: "Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Extended" - enum: - - http - - https + type: array + remove: + description: Remove the given header(s) from + the HTTP request before the action. The value + of Remove is a list of HTTP header names. + Note that the header names are case-insensitive + (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + items: type: string - statusCode: - default: 302 - description: "StatusCode is the HTTP status code to be used in response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Core" - enum: - - 301 - - 302 - type: integer - type: object - responseHeaderModifier: - description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " - properties: - add: - description: "Add adds the given header(s) (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: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-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" - items: - type: string - maxItems: 16 - type: array - set: - 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: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - 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 - Implementation-specific: 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. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - RequestHeaderModifier - - ResponseHeaderModifier - - RequestMirror - - RequestRedirect - - URLRewrite - - ExtensionRef - type: string - urlRewrite: - description: "URLRewrite defines a schema for a filter that modifies a request during forwarding. \n Support: Extended \n " - properties: - hostname: - description: "Hostname is the value to be used to replace the Host header value during forwarding. \n Support: Extended \n " - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - path: - description: "Path defines a path rewrite. \n Support: Extended \n " + type: array + set: + description: Set overwrites the request with + the given header (name, value) before the + action. + items: properties: - replaceFullPath: - description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " - maxLength: 1024 - type: string - replacePrefixMatch: - description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " - maxLength: 1024 + name: type: string - type: - description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - ReplaceFullPath - - ReplacePrefixMatch + value: type: string - required: - - type type: object - type: object - required: - - type - type: object - maxItems: 16 - type: array - group: - default: "" - description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - 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 backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - weight: - default: 1 - description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs 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 for this field varies based on the context where used." - format: int32 - maximum: 1000000 - minimum: 0 - type: integer - required: - - name - type: object - maxItems: 16 - type: array - 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 implementation-specific conformance. \n All filters are expected to be compatible with each other except for the URLRewrite and RequestRedirect filters, which may not be combined. If an implementation can not support other combinations of filters, they must clearly document that limitation. In all cases where incompatible or unsupported filters are specified, implementations MUST add a warning condition to status. \n Support: Core" - items: - description: HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express 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. - properties: - extensionRef: - description: "ExtensionRef is an optional, implementation-specific extension to the \"filter\" behavior. For example, resource \"myroutefilter\" in group \"networking.example.net\"). ExtensionRef MUST NOT be used for core and extended filters. \n Support: Implementation-specific" - properties: - group: - description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - 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: - description: "Add adds the given header(s) (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: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-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" - items: - type: string - maxItems: 16 - type: array - set: - 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: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - requestMirror: - description: "RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. \n Support: Extended" - properties: - backendRef: - description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the \"ResolvedRefs\" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. \n If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the \"ResolvedRefs\" condition on the Route is set to `status: False`, with the \"RefNotPermitted\" reason and not configure this backend in the underlying implementation. \n In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. \n Support: Extended for Kubernetes Service \n Support: Implementation-specific for any other resource" + type: array + type: object + urlRewrite: + description: URLRewrite defines a schema for a filter + that modifies a request during forwarding. properties: - group: - default: "" - description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + pathPrefix: type: string + type: object + type: object + type: array + weight: + description: "Weight specifies the proportion of requests + forwarded to the referenced backend. This is computed + as weight/(sum of all weights in this BackendRefs 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." + format: int32 + type: integer + type: object + type: array + filters: + items: + properties: + requestHeaderModifier: + description: RequestHeaderModifier defines a schema for + a filter that modifies request headers. + properties: + add: + description: Add adds the given header(s) (name, value) + to the request before the action. It appends to + any existing values associated with the header name. + items: + properties: name: - description: Name is the name of the referent. - maxLength: 253 - minLength: 1 type: string - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + value: type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - required: - - name type: object - required: - - backendRef - type: object - requestRedirect: - description: "RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. \n Support: Core" - properties: - hostname: - description: "Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname of the request is used. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: array + remove: + description: Remove the given header(s) from the HTTP + request before the action. The value of Remove is + a list of HTTP header names. Note that the header + names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + items: type: string - path: - description: "Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. \n Support: Extended \n " + type: array + set: + description: Set overwrites the request with the given + header (name, value) before the action. + items: properties: - replaceFullPath: - description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " - maxLength: 1024 + name: type: string - replacePrefixMatch: - description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " - maxLength: 1024 + value: type: string - type: - description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - ReplaceFullPath - - ReplacePrefixMatch + type: object + type: array + type: object + responseHeaderModifier: + description: ResponseHeaderModifier defines a schema for + a filter that modifies response headers. + properties: + add: + description: Add adds the given header(s) (name, value) + to the request before the action. It appends to + any existing values associated with the header name. + items: + properties: + name: + type: string + value: type: string - required: - - type type: object - port: - description: "Port is the port to be used in the value of the `Location` header in the response. When empty, port (if specified) of the request is used. \n Support: Extended" - format: int32 - maximum: 65535 - minimum: 1 - type: integer - scheme: - description: "Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Extended" - enum: - - http - - https + type: array + remove: + description: Remove the given header(s) from the HTTP + request before the action. The value of Remove is + a list of HTTP header names. Note that the header + names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + items: type: string - statusCode: - default: 302 - description: "StatusCode is the HTTP status code to be used in response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n Support: Core" - enum: - - 301 - - 302 - type: integer - type: object - responseHeaderModifier: - description: "ResponseHeaderModifier defines a schema for a filter that modifies response headers. \n Support: Extended \n " - properties: - add: - description: "Add adds the given header(s) (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: - name: \"my-header\" value: \"bar,baz\" \n Output: GET /foo HTTP/1.1 my-header: foo,bar,baz" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - remove: - description: "Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-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" - items: - type: string - maxItems: 16 - type: array - set: - 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: - name: \"my-header\" value: \"bar\" \n Output: GET /foo HTTP/1.1 my-header: bar" - items: - description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - 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 - Implementation-specific: 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. \n If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - RequestHeaderModifier - - ResponseHeaderModifier - - RequestMirror - - RequestRedirect - - URLRewrite - - ExtensionRef - type: string - urlRewrite: - description: "URLRewrite defines a schema for a filter that modifies a request during forwarding. \n Support: Extended \n " - properties: - hostname: - description: "Hostname is the value to be used to replace the Host header value during forwarding. \n Support: Extended \n " - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - path: - description: "Path defines a path rewrite. \n Support: Extended \n " + type: array + set: + description: Set overwrites the request with the given + header (name, value) before the action. + items: properties: - replaceFullPath: - description: "ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. \n " - maxLength: 1024 - type: string - replacePrefixMatch: - description: "ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to \"/foo/bar\" with a prefix match of \"/foo\" would be modified to \"/bar\". \n Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. \n " - maxLength: 1024 + name: type: string - type: - description: "Type defines the type of path modifier. Additional types may be added in a future release of the API. \n Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. \n Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. \n " - enum: - - ReplaceFullPath - - ReplacePrefixMatch + value: type: string - required: - - type type: object + type: array + type: object + urlRewrite: + description: URLRewrite defines a schema for a filter + that modifies a request during forwarding. + properties: + pathPrefix: + type: string + type: object + type: object + type: array + matches: + items: + properties: + headers: + description: Headers specifies HTTP request header matchers. + Multiple match values are ANDed together, meaning, a + request must match all the specified headers to select + the route. + items: + properties: + invert: + description: 'NOTE: not in gamma; service-router + compat' + type: boolean + name: + description: "Name is the name of the HTTP Header + to be matched. Name matching MUST be case insensitive. + (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent header + names, only the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be + ignored. Due to the case-insensitivity of header + names, “foo” and “Foo” are considered equivalent. + \n When a header is repeated in an HTTP request, + it is implementation-specific behavior as to how + this is represented. Generally, proxies should + follow the guidance from the RFC: https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 + regarding processing a repeated header, with special + handling for “Set-Cookie”." + type: string + type: + description: Type specifies how to match against + the value of the header. + enum: + - HEADER_MATCH_TYPE_UNSPECIFIED + - HEADER_MATCH_TYPE_EXACT + - HEADER_MATCH_TYPE_REGEX + - HEADER_MATCH_TYPE_PRESENT + - HEADER_MATCH_TYPE_PREFIX + - HEADER_MATCH_TYPE_SUFFIX + format: int32 + type: string + value: + description: Value is the value of HTTP Header to + be matched. + type: string type: object - required: - - type - type: object - maxItems: 16 - type: array - matches: - default: - - path: - type: PathPrefix - 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: - name: \"version\" value: \"v2\" - path: value: \"/v2/foo\" ``` \n For a request to match against this rule, a request must satisfy EITHER of the two conditions: \n - path prefixed with `/foo` AND contains the header `version: v2` - 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 Proxy or Load Balancer routing configuration generated from HTTPRoutes MUST prioritize matches based on the following criteria, continuing on ties. Across all rules specified on applicable Routes, precedence must be given to the match with the largest number of: \n * Characters in a matching path. * Header matches. * Query param 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. * The Route appearing first in alphabetical order by \"{namespace}/{name}\". \n If ties still exist within an HTTPRoute, matching precedence MUST be granted to the FIRST matching rule (in list order) with a match meeting the above criteria. \n When no rules matching a request have been successfully attached to the parent a request is coming from, a HTTP 404 status code MUST be returned." - 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: v1` header: \n ``` match: \n \tpath: \t value: \"/foo\" \theaders: \t- name: \"version\" \t value \"v1\" \n ```" - properties: - headers: - description: Headers specifies HTTP request header matchers. Multiple match values are ANDed together, meaning, a request must match all the specified headers to select the route. - items: - description: HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request headers. - properties: - name: - description: "Name is the name of the HTTP Header to be matched. Name matching MUST be case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). \n If multiple entries specify equivalent header names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, \"foo\" and \"Foo\" are considered equivalent. \n When a header is repeated in an HTTP request, it is implementation-specific behavior as to how this is represented. Generally, proxies should follow the guidance from the RFC: https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding processing a repeated header, with special handling for \"Set-Cookie\"." - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - type: - default: Exact - description: "Type specifies how to match against the value of the header. \n Support: Core (Exact) \n Support: Implementation-specific (RegularExpression) \n Since RegularExpression HeaderMatchType has implementation-specific 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 - type: string - value: - description: Value is the value of HTTP Header to be matched. - maxLength: 4096 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - method: - description: "Method specifies HTTP method matcher. When specified, this route will be matched only if the request has the specified method. \n Support: Extended" - enum: - - GET - - HEAD - - POST - - PUT - - DELETE - - CONNECT - - OPTIONS - - TRACE - - PATCH - type: string - path: - default: - type: PathPrefix - value: / - description: Path specifies a HTTP request path matcher. If this field is not specified, a default prefix match on the "/" path is provided. + type: array + method: + description: Method specifies HTTP method matcher. When + specified, this route will be matched only if the request + has the specified method. + type: string + path: + 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: + description: Type specifies how to match against the + path Value. + enum: + - PATH_MATCH_TYPE_UNSPECIFIED + - PATH_MATCH_TYPE_EXACT + - PATH_MATCH_TYPE_PREFIX + - PATH_MATCH_TYPE_REGEX + format: int32 + type: string + value: + description: Value of the HTTP path to match against. + type: string + type: object + queryParams: + description: QueryParams specifies HTTP query parameter + matchers. Multiple match values are ANDed together, + meaning, a request must match all the specified query + parameters to select the route. + items: properties: + name: + description: "Name is the name of the HTTP query + param to be matched. This must be an exact string + match. (See https://tools.ietf.org/html/rfc7230#section-2.7.3). + \n If multiple entries specify equivalent query + param names, only the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent query param name MUST + be ignored. \n If a query param is repeated in + an HTTP request, the behavior is purposely left + undefined, since different data planes have different + capabilities. However, it is recommended that + implementations should match against the first + value of the param if the data plane supports + it, as this behavior is expected in other load + balancing contexts outside of the Gateway API. + \n Users SHOULD NOT route traffic based on repeated + query params to guard themselves against potential + differences in the implementations." + type: string type: - default: PathPrefix - description: "Type specifies how to match against the path Value. \n Support: Core (Exact, PathPrefix) \n Support: Implementation-specific (RegularExpression)" + description: Type specifies how to match against + the value of the query parameter. enum: - - Exact - - PathPrefix - - RegularExpression + - QUERY_PARAM_MATCH_TYPE_UNSPECIFIED + - QUERY_PARAM_MATCH_TYPE_EXACT + - QUERY_PARAM_MATCH_TYPE_REGEX + - QUERY_PARAM_MATCH_TYPE_PRESENT + format: int32 type: string value: - default: / - description: Value of the HTTP path to match against. - maxLength: 1024 + description: Value is the value of HTTP query param + to be matched. type: string type: object - queryParams: - description: "QueryParams specifies HTTP query parameter matchers. Multiple match values are ANDed together, meaning, a request must match all the specified query parameters to select the route. \n Support: Extended" - items: - description: HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP query parameters. - properties: - name: - description: "Name is the name of the HTTP query param to be matched. This must be an exact string match. (See https://tools.ietf.org/html/rfc7230#section-2.7.3). \n If multiple entries specify equivalent query param names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent query param name MUST be ignored. \n If a query param is repeated in an HTTP request, the behavior is purposely left undefined, since different data planes have different capabilities. However, it is *recommended* that implementations should match against the first value of the param if the data plane supports it, as this behavior is expected in other load balancing contexts outside of the Gateway API. \n Users SHOULD NOT route traffic based on repeated query params to guard themselves against potential differences in the implementations." - maxLength: 256 - minLength: 1 - type: string - type: - default: Exact - description: "Type specifies how to match against the value of the query parameter. \n Support: Extended (Exact) \n Support: Implementation-specific (RegularExpression) \n Since RegularExpression QueryParamMatchType has Implementation-specific 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 - type: string - value: - description: Value is the value of HTTP query param to be matched. - maxLength: 1024 - minLength: 1 - type: string - required: - - name - - value - type: object - maxItems: 16 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - maxItems: 8 - type: array - type: object - maxItems: 16 - type: array - type: object - status: - description: Status defines the current state of HTTPRoute. - properties: - parents: - description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." - items: - description: RouteParentStatus describes the status of a route with respect to an associated Parent. - properties: - conditions: - description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." - 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, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + type: array + type: object + type: array + retries: + properties: + number: + description: Number is the number of times to retry the + request when a retryable result occurs. 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 + value: + description: The uint32 value. + format: int32 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 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - controllerName: - description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/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). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - parentRef: - description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - name: - description: "Name is the name of the referent. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + onConditions: + description: RetryOn allows setting envoy specific conditions + when a request should be automatically retried. + items: type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + type: array + onConnectFailure: + description: RetryOnConnectFailure allows for connection + failure errors to trigger a retry. + type: boolean + onStatusCodes: + description: RetryOnStatusCodes is a flat list of http response + status codes that are eligible for retry. This again should + be feasible in any reasonable proxy. + items: format: int32 - maximum: 65535 - minimum: 1 type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - required: - - controllerName - - parentRef - type: object - maxItems: 32 - type: array - required: - - parents - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] + type: array + type: object + timeouts: + description: HTTPRouteTimeouts defines timeouts that can be + configured for an HTTPRoute or GRPCRoute. + properties: + idle: + description: Idle specifies the total amount of time permitted + for the request stream to be idle. + format: duration + properties: + nanos: + description: Signed fractions of a second at nanosecond + resolution of the span of time. Durations less than + one second are represented with a 0 `seconds` field + and a positive or negative `nanos` field. For durations + of one second or more, a non-zero value for the `nanos` + field must be of the same sign as the `seconds` field. + Must be from -999,999,999 to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the span of time. Must + be from -315,576,000,000 to +315,576,000,000 inclusive. + Note: these bounds are computed from: 60 sec/min * + 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years' + format: int64 + type: integer + type: object + request: + description: RequestTimeout is the total amount of time + permitted for the entire downstream request (and retries) + to be processed. + format: duration + properties: + nanos: + description: Signed fractions of a second at nanosecond + resolution of the span of time. Durations less than + one second are represented with a 0 `seconds` field + and a positive or negative `nanos` field. For durations + of one second or more, a non-zero value for the `nanos` + field must be of the same sign as the `seconds` field. + Must be from -999,999,999 to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the span of time. Must + be from -315,576,000,000 to +315,576,000,000 inclusive. + Note: these bounds are computed from: 60 sec/min * + 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years' + format: int64 + type: integer + type: object + type: object + type: object + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} {{- end }} diff --git a/charts/consul/templates/crd-ingressgateways.yaml b/charts/consul/templates/crd-ingressgateways.yaml index 9fa5ef7edd..dcbc543525 100644 --- a/charts/consul/templates/crd-ingressgateways.yaml +++ b/charts/consul/templates/crd-ingressgateways.yaml @@ -1,18 +1,16 @@ {{- if .Values.connectInject.enabled }} ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: ingressgateways.consul.hashicorp.com + controller-gen.kubebuilder.io/version: v0.12.1 labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd + name: ingressgateways.consul.hashicorp.com spec: group: consul.hashicorp.com names: @@ -446,10 +444,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-jwtproviders.yaml b/charts/consul/templates/crd-jwtproviders.yaml index e5726cefe3..94c9697b33 100644 --- a/charts/consul/templates/crd-jwtproviders.yaml +++ b/charts/consul/templates/crd-jwtproviders.yaml @@ -1,18 +1,16 @@ {{- if .Values.connectInject.enabled }} ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: jwtproviders.consul.hashicorp.com + controller-gen.kubebuilder.io/version: v0.12.1 labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd + name: jwtproviders.consul.hashicorp.com spec: group: consul.hashicorp.com names: @@ -111,8 +109,7 @@ spec: cacheDuration: description: "CacheDuration is the duration after which cached keys should be expired. \n Default value is 5 minutes." - format: int64 - type: integer + type: string fetchAsynchronously: description: "FetchAsynchronously indicates that the JWKS should be fetched when a client request arrives. Client @@ -128,8 +125,7 @@ spec: description: The timeout for new network connections to hosts in the cluster. If not set, a default value of 5s will be used. - format: int64 - type: integer + type: string discoveryType: description: "DiscoveryType refers to the service discovery type to use for resolving the cluster. \n This defaults @@ -202,15 +198,13 @@ spec: description: "BaseInterval to be used for the next back off computation. \n The default value from envoy is 1s." - format: int64 - type: integer + type: string maxInterval: description: "MaxInternal to be used to specify the maximum interval between retries. Optional but should be greater or equal to BaseInterval. \n Defaults to 10 times BaseInterval." - format: int64 - type: integer + type: string type: object type: object uri: @@ -316,10 +310,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-meshes.yaml b/charts/consul/templates/crd-meshes.yaml index 0710d41280..f8ce4fc12e 100644 --- a/charts/consul/templates/crd-meshes.yaml +++ b/charts/consul/templates/crd-meshes.yaml @@ -1,18 +1,16 @@ {{- if .Values.connectInject.enabled }} ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: meshes.consul.hashicorp.com + controller-gen.kubebuilder.io/version: v0.12.1 labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd + name: meshes.consul.hashicorp.com spec: group: consul.hashicorp.com names: @@ -206,10 +204,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-meshservices.yaml b/charts/consul/templates/crd-meshservices.yaml index df8f673bdc..a5d36fb966 100644 --- a/charts/consul/templates/crd-meshservices.yaml +++ b/charts/consul/templates/crd-meshservices.yaml @@ -1,18 +1,16 @@ {{- if .Values.connectInject.enabled }} ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: meshservices.consul.hashicorp.com + controller-gen.kubebuilder.io/version: v0.12.1 labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd + name: meshservices.consul.hashicorp.com spec: group: consul.hashicorp.com names: @@ -55,10 +53,4 @@ spec: type: object served: true storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-peeringacceptors.yaml b/charts/consul/templates/crd-peeringacceptors.yaml index e06e830f04..2352ba7ad3 100644 --- a/charts/consul/templates/crd-peeringacceptors.yaml +++ b/charts/consul/templates/crd-peeringacceptors.yaml @@ -1,18 +1,16 @@ {{- if and .Values.connectInject.enabled .Values.global.peering.enabled }} ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: peeringacceptors.consul.hashicorp.com + controller-gen.kubebuilder.io/version: v0.12.1 labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd + name: peeringacceptors.consul.hashicorp.com spec: group: consul.hashicorp.com names: @@ -145,10 +143,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-peeringdialers.yaml b/charts/consul/templates/crd-peeringdialers.yaml index e24401e761..09991d2091 100644 --- a/charts/consul/templates/crd-peeringdialers.yaml +++ b/charts/consul/templates/crd-peeringdialers.yaml @@ -1,18 +1,16 @@ {{- if and .Values.connectInject.enabled .Values.global.peering.enabled }} ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: peeringdialers.consul.hashicorp.com + controller-gen.kubebuilder.io/version: v0.12.1 labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd + name: peeringdialers.consul.hashicorp.com spec: group: consul.hashicorp.com names: @@ -145,10 +143,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-proxyconfigurations.yaml b/charts/consul/templates/crd-proxyconfigurations.yaml new file mode 100644 index 0000000000..9a33bd2bab --- /dev/null +++ b/charts/consul/templates/crd-proxyconfigurations.yaml @@ -0,0 +1,423 @@ +{{- if .Values.connectInject.enabled }} +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.12.1 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + name: proxyconfigurations.mesh.consul.hashicorp.com +spec: + group: mesh.consul.hashicorp.com + names: + kind: ProxyConfiguration + listKind: ProxyConfigurationList + plural: proxyconfigurations + shortNames: + - proxy-configuration + singular: proxyconfiguration + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v2beta1 + schema: + openAPIV3Schema: + description: ProxyConfiguration is the Schema for the TCP Routes API + 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: This is a Resource type. + properties: + bootstrapConfig: + description: bootstrap_config is the configuration that requires proxies + to be restarted to be applied. + properties: + dogstatsdUrl: + type: string + overrideJsonTpl: + type: string + prometheusBindAddr: + type: string + readyBindAddr: + type: string + staticClustersJson: + type: string + staticListenersJson: + type: string + statsBindAddr: + type: string + statsConfigJson: + type: string + statsFlushInterval: + type: string + statsSinksJson: + type: string + statsTags: + items: + type: string + type: array + statsdUrl: + type: string + telemetryCollectorBindSocketDir: + type: string + tracingConfigJson: + type: string + type: object + dynamicConfig: + description: dynamic_config is the configuration that could be changed + dynamically (i.e. without needing restart). + properties: + accessLogs: + description: AccessLogs configures the output and format of Envoy + access logs + properties: + disableListenerLogs: + description: DisableListenerLogs turns off just listener logs + for connections rejected by Envoy because they don't have + a matching listener filter. + type: boolean + enabled: + description: Enabled turns off all access logging + type: boolean + jsonFormat: + description: The presence of one format string or the other + implies the access log string encoding. Defining both is + invalid. + type: string + path: + description: Path is the output file to write logs + type: string + textFormat: + type: string + type: + description: 'Type selects the output for logs: "file", "stderr". + "stdout"' + enum: + - LOG_SINK_TYPE_DEFAULT + - LOG_SINK_TYPE_FILE + - LOG_SINK_TYPE_STDERR + - LOG_SINK_TYPE_STDOUT + format: int32 + type: string + type: object + envoyExtensions: + items: + description: EnvoyExtension has configuration for an extension + that patches Envoy resources. + properties: + arguments: + type: object + x-kubernetes-preserve-unknown-fields: true + consulVersion: + type: string + envoyVersion: + type: string + name: + type: string + required: + type: boolean + type: object + type: array + exposeConfig: + properties: + exposePaths: + items: + properties: + listenerPort: + format: int32 + type: integer + localPathPort: + format: int32 + type: integer + path: + type: string + protocol: + enum: + - EXPOSE_PATH_PROTOCOL_HTTP + - EXPOSE_PATH_PROTOCOL_HTTP2 + format: int32 + type: string + type: object + type: array + type: object + inboundConnections: + description: inbound_connections configures inbound connections + to the proxy. + properties: + balanceInboundConnections: + enum: + - BALANCE_CONNECTIONS_DEFAULT + - BALANCE_CONNECTIONS_EXACT + format: int32 + type: string + maxInboundConnections: + format: int64 + type: integer + type: object + listenerTracingJson: + type: string + localClusterJson: + type: string + localConnection: + additionalProperties: + description: Referenced by ProxyConfiguration + properties: + connectTimeout: + description: "A Duration represents a signed, fixed-length + span of time represented as a count of seconds and fractions + of seconds at nanosecond resolution. It is independent + of any calendar and concepts like \"day\" or \"month\". + It is related to Timestamp in that the difference between + two Timestamp values is a Duration and it can be added + or subtracted from a Timestamp. Range is approximately + +-10,000 years. \n # Examples \n Example 1: Compute Duration + from two Timestamps in pseudo code. \n Timestamp start + = ...; Timestamp end = ...; Duration duration = ...; \n + duration.seconds = end.seconds - start.seconds; duration.nanos + = end.nanos - start.nanos; \n if (duration.seconds < 0 + && duration.nanos > 0) { duration.seconds += 1; duration.nanos + -= 1000000000; } else if (duration.seconds > 0 && duration.nanos + < 0) { duration.seconds -= 1; duration.nanos += 1000000000; + } \n Example 2: Compute Timestamp from Timestamp + Duration + in pseudo code. \n Timestamp start = ...; Duration duration + = ...; Timestamp end = ...; \n end.seconds = start.seconds + + duration.seconds; end.nanos = start.nanos + duration.nanos; + \n if (end.nanos < 0) { end.seconds -= 1; end.nanos += + 1000000000; } else if (end.nanos >= 1000000000) { end.seconds + += 1; end.nanos -= 1000000000; } \n Example 3: Compute + Duration from datetime.timedelta in Python. \n td = datetime.timedelta(days=3, + minutes=10) duration = Duration() duration.FromTimedelta(td) + \n # JSON Mapping \n In JSON format, the Duration type + is encoded as a string rather than an object, where the + string ends in the suffix \"s\" (indicating seconds) and + is preceded by the number of seconds, with nanoseconds + expressed as fractional seconds. For example, 3 seconds + with 0 nanoseconds should be encoded in JSON format as + \"3s\", while 3 seconds and 1 nanosecond should be expressed + in JSON format as \"3.000000001s\", and 3 seconds and + 1 microsecond should be expressed in JSON format as \"3.000001s\"." + format: duration + properties: + nanos: + description: Signed fractions of a second at nanosecond + resolution of the span of time. Durations less than + one second are represented with a 0 `seconds` field + and a positive or negative `nanos` field. For durations + of one second or more, a non-zero value for the `nanos` + field must be of the same sign as the `seconds` field. + Must be from -999,999,999 to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the span of time. Must + be from -315,576,000,000 to +315,576,000,000 inclusive. + Note: these bounds are computed from: 60 sec/min * + 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years' + format: int64 + type: integer + type: object + requestTimeout: + description: "A Duration represents a signed, fixed-length + span of time represented as a count of seconds and fractions + of seconds at nanosecond resolution. It is independent + of any calendar and concepts like \"day\" or \"month\". + It is related to Timestamp in that the difference between + two Timestamp values is a Duration and it can be added + or subtracted from a Timestamp. Range is approximately + +-10,000 years. \n # Examples \n Example 1: Compute Duration + from two Timestamps in pseudo code. \n Timestamp start + = ...; Timestamp end = ...; Duration duration = ...; \n + duration.seconds = end.seconds - start.seconds; duration.nanos + = end.nanos - start.nanos; \n if (duration.seconds < 0 + && duration.nanos > 0) { duration.seconds += 1; duration.nanos + -= 1000000000; } else if (duration.seconds > 0 && duration.nanos + < 0) { duration.seconds -= 1; duration.nanos += 1000000000; + } \n Example 2: Compute Timestamp from Timestamp + Duration + in pseudo code. \n Timestamp start = ...; Duration duration + = ...; Timestamp end = ...; \n end.seconds = start.seconds + + duration.seconds; end.nanos = start.nanos + duration.nanos; + \n if (end.nanos < 0) { end.seconds -= 1; end.nanos += + 1000000000; } else if (end.nanos >= 1000000000) { end.seconds + += 1; end.nanos -= 1000000000; } \n Example 3: Compute + Duration from datetime.timedelta in Python. \n td = datetime.timedelta(days=3, + minutes=10) duration = Duration() duration.FromTimedelta(td) + \n # JSON Mapping \n In JSON format, the Duration type + is encoded as a string rather than an object, where the + string ends in the suffix \"s\" (indicating seconds) and + is preceded by the number of seconds, with nanoseconds + expressed as fractional seconds. For example, 3 seconds + with 0 nanoseconds should be encoded in JSON format as + \"3s\", while 3 seconds and 1 nanosecond should be expressed + in JSON format as \"3.000000001s\", and 3 seconds and + 1 microsecond should be expressed in JSON format as \"3.000001s\"." + format: duration + properties: + nanos: + description: Signed fractions of a second at nanosecond + resolution of the span of time. Durations less than + one second are represented with a 0 `seconds` field + and a positive or negative `nanos` field. For durations + of one second or more, a non-zero value for the `nanos` + field must be of the same sign as the `seconds` field. + Must be from -999,999,999 to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the span of time. Must + be from -315,576,000,000 to +315,576,000,000 inclusive. + Note: these bounds are computed from: 60 sec/min * + 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years' + format: int64 + type: integer + type: object + type: object + description: local_connection is the configuration that should + be used to connect to the local application provided per-port. + The map keys should correspond to port names on the workload. + type: object + localWorkloadAddress: + description: "deprecated: local_workload_address, local_workload_port, + and local_workload_socket_path are deprecated and are only needed + for migration of existing resources. \n Deprecated: Marked as + deprecated in pbmesh/v2beta1/proxy_configuration.proto." + type: string + localWorkloadPort: + description: 'Deprecated: Marked as deprecated in pbmesh/v2beta1/proxy_configuration.proto.' + format: int32 + type: integer + localWorkloadSocketPath: + description: 'Deprecated: Marked as deprecated in pbmesh/v2beta1/proxy_configuration.proto.' + type: string + meshGatewayMode: + enum: + - MESH_GATEWAY_MODE_UNSPECIFIED + - MESH_GATEWAY_MODE_NONE + - MESH_GATEWAY_MODE_LOCAL + - MESH_GATEWAY_MODE_REMOTE + format: int32 + type: string + mode: + description: mode indicates the proxy's mode. This will default + to 'transparent'. + enum: + - PROXY_MODE_DEFAULT + - PROXY_MODE_TRANSPARENT + - PROXY_MODE_DIRECT + format: int32 + type: string + mutualTlsMode: + enum: + - MUTUAL_TLS_MODE_DEFAULT + - MUTUAL_TLS_MODE_STRICT + - MUTUAL_TLS_MODE_PERMISSIVE + format: int32 + type: string + publicListenerJson: + type: string + transparentProxy: + properties: + dialedDirectly: + description: dialed_directly indicates whether this proxy + should be dialed using original destination IP in the connection + rather than load balance between all endpoints. + type: boolean + outboundListenerPort: + description: outbound_listener_port is the port for the proxy's + outbound listener. This defaults to 15001. + format: int32 + type: integer + type: object + type: object + opaqueConfig: + description: "deprecated: prevent usage when using v2 APIs directly. + needed for backwards compatibility \n Deprecated: Marked as deprecated + in pbmesh/v2beta1/proxy_configuration.proto." + type: object + x-kubernetes-preserve-unknown-fields: true + workloads: + description: Selection of workloads this proxy configuration should + apply to. These can be prefixes or specific workload names. + properties: + filter: + type: string + names: + items: + type: string + type: array + prefixes: + items: + type: string + type: array + type: object + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +{{- end }} diff --git a/charts/consul/templates/crd-proxydefaults.yaml b/charts/consul/templates/crd-proxydefaults.yaml index 3d2df52a7e..ce49c9149a 100644 --- a/charts/consul/templates/crd-proxydefaults.yaml +++ b/charts/consul/templates/crd-proxydefaults.yaml @@ -1,18 +1,16 @@ {{- if .Values.connectInject.enabled }} ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: proxydefaults.consul.hashicorp.com + controller-gen.kubebuilder.io/version: v0.12.1 labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd + name: proxydefaults.consul.hashicorp.com spec: group: consul.hashicorp.com names: @@ -264,10 +262,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-referencegrants.yaml b/charts/consul/templates/crd-referencegrants-external.yaml similarity index 100% rename from charts/consul/templates/crd-referencegrants.yaml rename to charts/consul/templates/crd-referencegrants-external.yaml diff --git a/charts/consul/templates/crd-routeauthfilters.yaml b/charts/consul/templates/crd-routeauthfilters.yaml index dcea7192a4..a51bf226cd 100644 --- a/charts/consul/templates/crd-routeauthfilters.yaml +++ b/charts/consul/templates/crd-routeauthfilters.yaml @@ -1,18 +1,16 @@ {{- if .Values.connectInject.enabled }} ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: routeauthfilters.consul.hashicorp.com + controller-gen.kubebuilder.io/version: v0.12.1 labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd + name: routeauthfilters.consul.hashicorp.com spec: group: consul.hashicorp.com names: @@ -198,10 +196,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-routeretryfilters.yaml b/charts/consul/templates/crd-routeretryfilters.yaml index 1d538aa8d6..14b6062f60 100644 --- a/charts/consul/templates/crd-routeretryfilters.yaml +++ b/charts/consul/templates/crd-routeretryfilters.yaml @@ -1,18 +1,16 @@ {{- if .Values.connectInject.enabled }} ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: routeretryfilters.consul.hashicorp.com + controller-gen.kubebuilder.io/version: v0.12.1 labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd + name: routeretryfilters.consul.hashicorp.com spec: group: consul.hashicorp.com names: @@ -114,10 +112,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-routetimeoutfilters.yaml b/charts/consul/templates/crd-routetimeoutfilters.yaml index 28e4ec3ffb..95ab50320d 100644 --- a/charts/consul/templates/crd-routetimeoutfilters.yaml +++ b/charts/consul/templates/crd-routetimeoutfilters.yaml @@ -1,18 +1,16 @@ {{- if .Values.connectInject.enabled }} ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: routetimeoutfilters.consul.hashicorp.com + controller-gen.kubebuilder.io/version: v0.12.1 labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd + name: routetimeoutfilters.consul.hashicorp.com spec: group: consul.hashicorp.com names: @@ -57,17 +55,9 @@ spec: description: RouteTimeoutFilterSpec defines the desired state of RouteTimeoutFilter. properties: idleTimeout: - description: A Duration represents the elapsed time between two instants - as an int64 nanosecond count. The representation limits the largest - representable duration to approximately 290 years. - format: int64 - type: integer + type: string requestTimeout: - description: A Duration represents the elapsed time between two instants - as an int64 nanosecond count. The representation limits the largest - representable duration to approximately 290 years. - format: int64 - type: integer + type: string type: object status: properties: @@ -112,10 +102,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-samenessgroups.yaml b/charts/consul/templates/crd-samenessgroups.yaml index 60beb5662c..ea0ad7c8a0 100644 --- a/charts/consul/templates/crd-samenessgroups.yaml +++ b/charts/consul/templates/crd-samenessgroups.yaml @@ -1,18 +1,16 @@ {{- if .Values.connectInject.enabled }} ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: samenessgroups.consul.hashicorp.com + controller-gen.kubebuilder.io/version: v0.12.1 labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd + name: samenessgroups.consul.hashicorp.com spec: group: consul.hashicorp.com names: @@ -128,10 +126,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-servicedefaults.yaml b/charts/consul/templates/crd-servicedefaults.yaml index ddff40ced2..c7e2b5bb2b 100644 --- a/charts/consul/templates/crd-servicedefaults.yaml +++ b/charts/consul/templates/crd-servicedefaults.yaml @@ -1,18 +1,16 @@ {{- if .Values.connectInject.enabled }} ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: servicedefaults.consul.hashicorp.com + controller-gen.kubebuilder.io/version: v0.12.1 labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd + name: servicedefaults.consul.hashicorp.com spec: group: consul.hashicorp.com names: @@ -564,10 +562,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-serviceintentions.yaml b/charts/consul/templates/crd-serviceintentions.yaml index c4d2b5f20d..75299f016e 100644 --- a/charts/consul/templates/crd-serviceintentions.yaml +++ b/charts/consul/templates/crd-serviceintentions.yaml @@ -1,18 +1,16 @@ {{- if .Values.connectInject.enabled }} ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: serviceintentions.consul.hashicorp.com + controller-gen.kubebuilder.io/version: v0.12.1 labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd + name: serviceintentions.consul.hashicorp.com spec: group: consul.hashicorp.com names: @@ -310,10 +308,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-serviceresolvers.yaml b/charts/consul/templates/crd-serviceresolvers.yaml index eb5643fe49..6d89125216 100644 --- a/charts/consul/templates/crd-serviceresolvers.yaml +++ b/charts/consul/templates/crd-serviceresolvers.yaml @@ -1,18 +1,16 @@ {{- if .Values.connectInject.enabled }} ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: serviceresolvers.consul.hashicorp.com + controller-gen.kubebuilder.io/version: v0.12.1 labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd + name: serviceresolvers.consul.hashicorp.com spec: group: consul.hashicorp.com names: @@ -347,10 +345,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-servicerouters.yaml b/charts/consul/templates/crd-servicerouters.yaml index f28da9e7c1..72690c60e4 100644 --- a/charts/consul/templates/crd-servicerouters.yaml +++ b/charts/consul/templates/crd-servicerouters.yaml @@ -1,18 +1,16 @@ {{- if .Values.connectInject.enabled }} ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: servicerouters.consul.hashicorp.com + controller-gen.kubebuilder.io/version: v0.12.1 labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd + name: servicerouters.consul.hashicorp.com spec: group: consul.hashicorp.com names: @@ -311,10 +309,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-servicesplitters.yaml b/charts/consul/templates/crd-servicesplitters.yaml index a2af050c3d..8d5ed58023 100644 --- a/charts/consul/templates/crd-servicesplitters.yaml +++ b/charts/consul/templates/crd-servicesplitters.yaml @@ -1,18 +1,16 @@ {{- if .Values.connectInject.enabled }} ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: servicesplitters.consul.hashicorp.com + controller-gen.kubebuilder.io/version: v0.12.1 labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd + name: servicesplitters.consul.hashicorp.com spec: group: consul.hashicorp.com names: @@ -185,10 +183,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-tcproutes-external.yaml b/charts/consul/templates/crd-tcproutes-external.yaml new file mode 100644 index 0000000000..91989135e2 --- /dev/null +++ b/charts/consul/templates/crd-tcproutes-external.yaml @@ -0,0 +1,284 @@ +{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: crd + gateway.networking.k8s.io/bundle-version: v0.6.2 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + name: tcproutes.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: TCPRoute + listKind: TCPRouteList + plural: tcproutes + singular: tcproute + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha2 + schema: + openAPIV3Schema: + description: TCPRoute provides a way to route TCP requests. When combined with a Gateway listener, it can be used to forward connections on the port specified by the listener to a set of backends specified by the TCPRoute. + 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 TCPRoute. + properties: + parentRefs: + description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." + items: + description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: "Name is the name of the referent. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + rules: + description: Rules are a list of TCP matchers and actions. + items: + description: TCPRouteRule is the configuration for a given rule. + properties: + backendRefs: + description: "BackendRefs defines the backend(s) where matching requests should be sent. If unspecified or invalid (refers to a non-existent resource or a Service with no endpoints), the underlying implementation MUST actively reject connection attempts to this backend. Connection rejections must respect weight; if an invalid backend is requested to have 80% of connections, then 80% of connections must be rejected instead. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Extended" + items: + description: "BackendRef defines how a Route should forward a request to a Kubernetes resource. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details." + properties: + group: + default: "" + description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + 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 backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs 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 for this field varies based on the context where used." + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + maxItems: 16 + minItems: 1 + type: array + type: object + maxItems: 16 + minItems: 1 + type: array + required: + - rules + type: object + status: + description: Status defines the current state of TCPRoute. + properties: + parents: + description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." + items: + description: RouteParentStatus describes the status of a route with respect to an associated Parent. + properties: + conditions: + description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." + 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, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + 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 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/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). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: "Name is the name of the referent. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + required: + - parents + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +{{- end }} diff --git a/charts/consul/templates/crd-tcproutes.yaml b/charts/consul/templates/crd-tcproutes.yaml index 91989135e2..ae9d2cd080 100644 --- a/charts/consul/templates/crd-tcproutes.yaml +++ b/charts/consul/templates/crd-tcproutes.yaml @@ -1,284 +1,278 @@ -{{- if and .Values.connectInject.enabled .Values.connectInject.apiGateway.manageExternalCRDs }} -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - +{{- if .Values.connectInject.enabled }} apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538 + controller-gen.kubebuilder.io/version: v0.12.1 labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd - gateway.networking.k8s.io/bundle-version: v0.6.2 - gateway.networking.k8s.io/channel: experimental - creationTimestamp: null - name: tcproutes.gateway.networking.k8s.io + name: tcproutes.mesh.consul.hashicorp.com spec: - group: gateway.networking.k8s.io + group: mesh.consul.hashicorp.com names: - categories: - - gateway-api kind: TCPRoute listKind: TCPRouteList plural: tcproutes + shortNames: + - tcp-route singular: tcproute scope: Namespaced versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha2 - schema: - openAPIV3Schema: - description: TCPRoute provides a way to route TCP requests. When combined with a Gateway listener, it can be used to forward connections on the port specified by the listener to a set of backends specified by the TCPRoute. - 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 TCPRoute. - properties: - parentRefs: - description: "ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. \n The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources such as one of the route kinds. \n It is invalid to reference an identical parent more than once. It is valid to reference multiple distinct sections within the same parent resource, such as 2 Listeners within a Gateway. \n It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. \n Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference." - items: - description: "ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). The only kind of parent resource with \"Core\" support is Gateway. This API may be extended in the future to support additional kinds of parent resources, such as HTTPRoute. \n The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid." - properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - name: - description: "Name is the name of the referent. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - maxItems: 32 - type: array - rules: - description: Rules are a list of TCP matchers and actions. - items: - description: TCPRouteRule is the configuration for a given rule. - properties: - backendRefs: - description: "BackendRefs defines the backend(s) where matching requests should be sent. If unspecified or invalid (refers to a non-existent resource or a Service with no endpoints), the underlying implementation MUST actively reject connection attempts to this backend. Connection rejections must respect weight; if an invalid backend is requested to have 80% of connections, then 80% of connections must be rejected instead. \n Support: Core for Kubernetes Service \n Support: Implementation-specific for any other resource \n Support for weight: Extended" - items: - description: "BackendRef defines how a Route should forward a request to a Kubernetes resource. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details." + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v2beta1 + schema: + openAPIV3Schema: + description: TCPRoute is the Schema for the TCP Route API + 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: "NOTE: this should align to the GAMMA/gateway-api version, + or at least be easily translatable. \n https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.TCPRoute + \n This is a Resource type." + properties: + parentRefs: + description: "ParentRefs references the resources (usually Services) + that a Route wants to be attached to. \n It is invalid to reference + an identical parent more than once. It is valid to reference multiple + distinct sections within the same parent resource." + items: + description: 'NOTE: roughly equivalent to structs.ResourceReference' + properties: + port: + description: For east/west this is the name of the Consul Service + port to direct traffic to or empty to imply all. For north/south + this is TBD. + type: string + ref: + description: For east/west configuration, this should point + to a Service. For north/south it should point to a Gateway. + properties: + name: + description: Name is the user-given name of the resource + (e.g. the "billing" service). + type: string + section: + description: Section identifies which part of the resource + the condition relates to. + type: string + tenancy: + description: Tenancy identifies the tenancy units (i.e. + partition, namespace) in which the resource resides. properties: - group: - default: "" - description: Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Service - description: Kind is kind of the referent. For example "HTTPRoute" or "Service". Defaults to "Service" when not specified. - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + namespace: + description: "Namespace further isolates resources within + a partition. https://developer.hashicorp.com/consul/docs/enterprise/namespaces + \n When using the List and WatchList endpoints, provide + the wildcard value \"*\" to list resources across + all namespaces." type: string - name: - description: Name is the name of the referent. - maxLength: 253 - minLength: 1 + partition: + description: "Partition is the topmost administrative + boundary within a cluster. https://developer.hashicorp.com/consul/docs/enterprise/admin-partitions + \n When using the List and WatchList endpoints, provide + the wildcard value \"*\" to list resources across + all partitions." type: string - namespace: - description: "Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. \n Note that when a namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + peerName: + description: "PeerName identifies which peer the resource + is imported from. https://developer.hashicorp.com/consul/docs/connect/cluster-peering + \n When using the List and WatchList endpoints, provide + the wildcard value \"*\" to list resources across + all peers." type: string - port: - description: Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - weight: - default: 1 - description: "Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs 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 for this field varies based on the context where used." - format: int32 - maximum: 1000000 - minimum: 0 - type: integer - required: - - name type: object - maxItems: 16 - minItems: 1 - type: array - type: object - maxItems: 16 - minItems: 1 - type: array - required: - - rules - type: object - status: - description: Status defines the current state of TCPRoute. - properties: - parents: - description: "Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. \n Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. \n A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway." - items: - description: RouteParentStatus describes the status of a route with respect to an associated Parent. - properties: - conditions: - description: "Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. \n If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the \"Accepted\" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. \n A Route MUST be considered \"Accepted\" if at least one of the Route's rules is implemented by the Gateway. \n There are a number of cases where the \"Accepted\" condition may not be set due to lack of controller visibility, that includes when: \n * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to." - 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, \n \ttype FooStatus struct{ \t // Represents the observations of a foo's current state. \t // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map \t // +listMapKey=type \t Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields \t}" + type: + description: Type identifies the resource's type. 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_])?$ + group: + description: Group describes the area of functionality + to which this resource type relates (e.g. "catalog", + "authorization"). type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown + groupVersion: + description: GroupVersion is incremented when sweeping + or backward-incompatible changes are made to the group's + resource types. 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])$ + kind: + description: Kind identifies the specific resource type + within the group. type: string - required: - - lastTransitionTime - - message - - reason - - status - - type type: object - maxItems: 8 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - controllerName: - description: "ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. \n Example: \"example.net/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). \n Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary." - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - parentRef: - description: ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. + type: object + type: object + type: array + rules: + description: Rules are a list of TCP matchers and actions. + items: + properties: + backendRefs: + description: BackendRefs defines the backend(s) where matching + requests should be sent. If unspecified or invalid (refers + to a non-existent resource or a Service with no endpoints), + the underlying implementation MUST actively reject connection + attempts to this backend. Connection rejections must respect + weight; if an invalid backend is requested to have 80% of + connections, then 80% of connections must be rejected instead. + items: properties: - group: - default: gateway.networking.k8s.io - description: "Group is the group of the referent. When unspecified, \"gateway.networking.k8s.io\" is inferred. To set the core API group (such as for a \"Service\" kind referent), Group must be explicitly set to \"\" (empty string). \n Support: Core" - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: "Kind is kind of the referent. \n Support: Core (Gateway) \n Support: Implementation-specific (Other Resources)" - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - name: - description: "Name is the name of the referent. \n Support: Core" - maxLength: 253 - minLength: 1 - type: string - namespace: - description: "Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. \n Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. \n Support: Core" - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: "Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. \n When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. \n For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Extended \n " + backendRef: + properties: + datacenter: + type: string + port: + description: "For east/west this is the name of the + Consul Service port to direct traffic to or empty + to imply using the same value as the parent ref. + \n For north/south this is TBD." + type: string + ref: + description: For east/west configuration, this should + point to a Service. + properties: + name: + description: Name is the user-given name of the + resource (e.g. the "billing" service). + type: string + section: + description: Section identifies which part of + the resource the condition relates to. + type: string + tenancy: + description: Tenancy identifies the tenancy units + (i.e. partition, namespace) in which the resource + resides. + properties: + namespace: + description: "Namespace further isolates resources + within a partition. https://developer.hashicorp.com/consul/docs/enterprise/namespaces + \n When using the List and WatchList endpoints, + provide the wildcard value \"*\" to list + resources across all namespaces." + type: string + partition: + description: "Partition is the topmost administrative + boundary within a cluster. https://developer.hashicorp.com/consul/docs/enterprise/admin-partitions + \n When using the List and WatchList endpoints, + provide the wildcard value \"*\" to list + resources across all partitions." + type: string + peerName: + description: "PeerName identifies which peer + the resource is imported from. https://developer.hashicorp.com/consul/docs/connect/cluster-peering + \n When using the List and WatchList endpoints, + provide the wildcard value \"*\" to list + resources across all peers." + type: string + type: object + type: + description: Type identifies the resource's type. + properties: + group: + description: Group describes the area of functionality + to which this resource type relates (e.g. + "catalog", "authorization"). + type: string + groupVersion: + description: GroupVersion is incremented when + sweeping or backward-incompatible changes + are made to the group's resource types. + type: string + kind: + description: Kind identifies the specific + resource type within the group. + type: string + type: object + type: object + type: object + weight: + description: "Weight specifies the proportion of requests + forwarded to the referenced backend. This is computed + as weight/(sum of all weights in this BackendRefs 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." format: int32 - maximum: 65535 - minimum: 1 type: integer - sectionName: - description: "SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: \n * Gateway: Listener Name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. \n Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. \n When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n Support: Core" - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name type: object - required: - - controllerName - - parentRef - type: object - maxItems: 32 - type: array - required: - - parents - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] + type: array + type: object + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} {{- end }} diff --git a/charts/consul/templates/crd-terminatinggateways.yaml b/charts/consul/templates/crd-terminatinggateways.yaml index 583c218be8..565aa63381 100644 --- a/charts/consul/templates/crd-terminatinggateways.yaml +++ b/charts/consul/templates/crd-terminatinggateways.yaml @@ -1,18 +1,16 @@ {{- if .Values.connectInject.enabled }} ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: terminatinggateways.consul.hashicorp.com + controller-gen.kubebuilder.io/version: v0.12.1 labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd + name: terminatinggateways.consul.hashicorp.com spec: group: consul.hashicorp.com names: @@ -136,10 +134,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-tlsroutes.yaml b/charts/consul/templates/crd-tlsroutes-external.yaml similarity index 100% rename from charts/consul/templates/crd-tlsroutes.yaml rename to charts/consul/templates/crd-tlsroutes-external.yaml diff --git a/charts/consul/templates/crd-trafficpermissions.yaml b/charts/consul/templates/crd-trafficpermissions.yaml index ef8e8a73ca..27ab6f5e3d 100644 --- a/charts/consul/templates/crd-trafficpermissions.yaml +++ b/charts/consul/templates/crd-trafficpermissions.yaml @@ -1,18 +1,16 @@ {{- if .Values.connectInject.enabled }} ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - name: trafficpermissions.auth.consul.hashicorp.com + controller-gen.kubebuilder.io/version: v0.12.1 labels: app: {{ template "consul.name" . }} chart: {{ template "consul.chart" . }} heritage: {{ .Release.Service }} release: {{ .Release.Name }} component: crd + name: trafficpermissions.auth.consul.hashicorp.com spec: group: auth.consul.hashicorp.com names: @@ -56,7 +54,6 @@ spec: metadata: type: object spec: - description: TrafficPermissionsSpec defines the desired state of TrafficPermissions. properties: action: description: "Action can be either allow or deny for the entire object. @@ -71,34 +68,36 @@ spec: deny permissions have no effect without an allow permission as everything is denied by default. \n Action unspecified is reserved for compatibility with the addition of future actions." + enum: + - ACTION_ALLOW + - ACTION_DENY + - ACTION_UNKNOWN + format: int32 type: string destination: description: Destination is a configuration of the destination proxies where these traffic permissions should apply. properties: identityName: - description: Name is the destination of all intentions defined - in this config entry. This may be set to the wildcard character - (*) to match all services that don't otherwise have intentions - defined. type: string type: object permissions: description: Permissions is a list of permissions to match on. They are applied using OR semantics. items: + description: Permissions is a list of permissions to match on. properties: destinationRules: - description: destinationRules is a list of rules to apply for + description: DestinationRules is a list of rules to apply for matching sources in this Permission. These rules are specific to the request or connection that is going to the destination(s) selected by the TrafficPermissions resource. items: - description: DestinationRule contains rules to apply to the - incoming connection. + description: DestinationRule contains rules rules to apply + to the incoming connection. properties: exclude: - description: exclude contains a list of rules to exclude + description: Exclude contains a list of rules to exclude when evaluating rules for the incoming connection. items: properties: @@ -120,7 +119,7 @@ spec: type: string type: object methods: - description: methods is the list of HTTP methods. + description: Methods is the list of HTTP methods. items: type: string type: array @@ -131,7 +130,7 @@ spec: pathRegex: type: string portNames: - description: portNames is a list of workload ports + description: PortNames is a list of workload ports to apply this rule to. The ports specified here must be the ports used in the connection. items: @@ -157,7 +156,7 @@ spec: type: string type: object methods: - description: methods is the list of HTTP methods. If no + description: Methods is the list of HTTP methods. If no methods are specified, this rule will apply to all methods. items: type: string @@ -175,19 +174,19 @@ spec: type: object type: array sources: - description: sources is a list of sources in this traffic permission. + description: Sources is a list of sources in this traffic permission. items: description: Source represents the source identity. To specify any of the wildcard sources, the specific fields need to - be omitted. For example, for a wildcard namespace, identityName + be omitted. For example, for a wildcard namespace, identity_name should be omitted. properties: exclude: - description: exclude is a list of sources to exclude from + description: Exclude is a list of sources to exclude from this source. items: description: ExcludeSource is almost the same as source - but it prevents the addition of matchiing sources. + but it prevents the addition of matching sources. properties: identityName: type: string @@ -259,10 +258,4 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] {{- end }} diff --git a/charts/consul/templates/crd-udproutes.yaml b/charts/consul/templates/crd-udproutes-external.yaml similarity index 100% rename from charts/consul/templates/crd-udproutes.yaml rename to charts/consul/templates/crd-udproutes-external.yaml diff --git a/charts/consul/test/unit/crd-gatewayclasses.bats b/charts/consul/test/unit/crd-gatewayclasses-external.bats similarity index 80% rename from charts/consul/test/unit/crd-gatewayclasses.bats rename to charts/consul/test/unit/crd-gatewayclasses-external.bats index 8400590606..a1a845a249 100644 --- a/charts/consul/test/unit/crd-gatewayclasses.bats +++ b/charts/consul/test/unit/crd-gatewayclasses-external.bats @@ -5,7 +5,7 @@ load _helpers @test "gatewayclasses/CustomResourceDefinition: enabled by default" { cd `chart_dir` local actual=$(helm template \ - -s templates/crd-gatewayclasses.yaml \ + -s templates/crd-gatewayclasses-external.yaml \ . | tee /dev/stderr | yq 'length > 0' | tee /dev/stderr) [ "$actual" = "true" ] @@ -14,7 +14,7 @@ load _helpers @test "gatewayclasses/CustomResourceDefinition: disabled with connectInject.enabled=false" { cd `chart_dir` assert_empty helm template \ - -s templates/crd-gatewayclasses.yaml \ + -s templates/crd-gatewayclasses-external.yaml \ --set 'connectInject.enabled=false' \ . } @@ -22,7 +22,7 @@ load _helpers @test "gatewayclasses/CustomResourceDefinition: disabled with connectInject.apiGateway.manageExternalCRDs=false" { cd `chart_dir` assert_empty helm template \ - -s templates/crd-gatewayclasses.yaml \ + -s templates/crd-gatewayclasses-external.yaml \ --set 'connectInject.apiGateway.manageExternalCRDs=false' \ . } diff --git a/charts/consul/test/unit/crd-gateways.bats b/charts/consul/test/unit/crd-gateways-external.bats similarity index 82% rename from charts/consul/test/unit/crd-gateways.bats rename to charts/consul/test/unit/crd-gateways-external.bats index 8a7f0284e2..30b6d71630 100644 --- a/charts/consul/test/unit/crd-gateways.bats +++ b/charts/consul/test/unit/crd-gateways-external.bats @@ -5,7 +5,7 @@ load _helpers @test "gateways/CustomResourceDefinition: enabled by default" { cd `chart_dir` local actual=$(helm template \ - -s templates/crd-gateways.yaml \ + -s templates/crd-gateways-external.yaml \ . | tee /dev/stderr | yq 'length > 0' | tee /dev/stderr) [ "$actual" = "true" ] @@ -14,7 +14,7 @@ load _helpers @test "gateways/CustomResourceDefinition: disabled with connectInject.enabled=false" { cd `chart_dir` assert_empty helm template \ - -s templates/crd-gateways.yaml \ + -s templates/crd-gateways-external.yaml \ --set 'connectInject.enabled=false' \ . } @@ -22,7 +22,7 @@ load _helpers @test "gateways/CustomResourceDefinition: disabled with connectInject.apiGateway.manageExternalCRDs=false" { cd `chart_dir` assert_empty helm template \ - -s templates/crd-gateways.yaml \ + -s templates/crd-gateways-external.yaml \ --set 'connectInject.apiGateway.manageExternalCRDs=false' \ . } diff --git a/charts/consul/test/unit/crd-grpcroutes.bats b/charts/consul/test/unit/crd-grpcroutes-external.bats similarity index 81% rename from charts/consul/test/unit/crd-grpcroutes.bats rename to charts/consul/test/unit/crd-grpcroutes-external.bats index d5e3e298d7..625648e326 100644 --- a/charts/consul/test/unit/crd-grpcroutes.bats +++ b/charts/consul/test/unit/crd-grpcroutes-external.bats @@ -5,7 +5,7 @@ load _helpers @test "grpcroutes/CustomResourceDefinition: enabled by default" { cd `chart_dir` local actual=$(helm template \ - -s templates/crd-grpcroutes.yaml \ + -s templates/crd-grpcroutes-external.yaml \ . | tee /dev/stderr | yq 'length > 0' | tee /dev/stderr) [ "$actual" = "true" ] @@ -14,7 +14,7 @@ load _helpers @test "grpcroutes/CustomResourceDefinition: disabled with connectInject.enabled=false" { cd `chart_dir` assert_empty helm template \ - -s templates/crd-grpcroutes.yaml \ + -s templates/crd-grpcroutes-external.yaml \ --set 'connectInject.enabled=false' \ . } @@ -22,7 +22,7 @@ load _helpers @test "grpcroutes/CustomResourceDefinition: disabled with connectInject.apiGateway.manageExternalCRDs=false" { cd `chart_dir` assert_empty helm template \ - -s templates/crd-grpcroutes.yaml \ + -s templates/crd-grpcroutes-external.yaml \ --set 'connectInject.apiGateway.manageExternalCRDs=false' \ . } diff --git a/charts/consul/test/unit/crd-httproutes.bats b/charts/consul/test/unit/crd-httproutes-external.bats similarity index 81% rename from charts/consul/test/unit/crd-httproutes.bats rename to charts/consul/test/unit/crd-httproutes-external.bats index 99c58e0492..e35bebc3e4 100644 --- a/charts/consul/test/unit/crd-httproutes.bats +++ b/charts/consul/test/unit/crd-httproutes-external.bats @@ -5,7 +5,7 @@ load _helpers @test "httproutes/CustomResourceDefinition: enabled by default" { cd `chart_dir` local actual=$(helm template \ - -s templates/crd-httproutes.yaml \ + -s templates/crd-httproutes-external.yaml \ . | tee /dev/stderr | yq 'length > 0' | tee /dev/stderr) [ "$actual" = "true" ] @@ -14,7 +14,7 @@ load _helpers @test "httproutes/CustomResourceDefinition: disabled with connectInject.enabled=false" { cd `chart_dir` assert_empty helm template \ - -s templates/crd-httproutes.yaml \ + -s templates/crd-httproutes-external.yaml \ --set 'connectInject.enabled=false' \ . } @@ -22,7 +22,7 @@ load _helpers @test "httproutes/CustomResourceDefinition: disabled with connectInject.apiGateway.manageExternalCRDs=false" { cd `chart_dir` assert_empty helm template \ - -s templates/crd-httproutes.yaml \ + -s templates/crd-httproutes-external.yaml \ --set 'connectInject.apiGateway.manageExternalCRDs=false' \ . } diff --git a/charts/consul/test/unit/crd-tcproutes.bats b/charts/consul/test/unit/crd-tcproutes-external.bats similarity index 84% rename from charts/consul/test/unit/crd-tcproutes.bats rename to charts/consul/test/unit/crd-tcproutes-external.bats index 9cfdd182e7..c91eb15e6b 100644 --- a/charts/consul/test/unit/crd-tcproutes.bats +++ b/charts/consul/test/unit/crd-tcproutes-external.bats @@ -5,7 +5,7 @@ load _helpers @test "tcproutes/CustomResourceDefinition: enabled by default" { cd `chart_dir` local actual=$(helm template \ - -s templates/crd-tcproutes.yaml \ + -s templates/crd-tcproutes-external.yaml \ . | tee /dev/stderr | yq 'length > 0' | tee /dev/stderr) [ "$actual" = "true" ] @@ -14,7 +14,7 @@ load _helpers @test "tcproutes/CustomResourceDefinition: disabled with connectInject.enabled=false" { cd `chart_dir` assert_empty helm template \ - -s templates/crd-tcproutes.yaml \ + -s templates/crd-tcproutes-external.yaml \ --set 'connectInject.enabled=false' \ . } @@ -22,7 +22,7 @@ load _helpers @test "tcproutes/CustomResourceDefinition: disabled with connectInject.apiGateway.manageExternalCRDs=false" { cd `chart_dir` assert_empty helm template \ - -s templates/crd-tcproutes.yaml \ + -s templates/crd-tcproutes-external.yaml \ --set 'connectInject.apiGateway.manageExternalCRDs=false' \ . } @@ -30,7 +30,7 @@ load _helpers @test "tcproutes/CustomResourceDefinition: disabled with connectInject.apiGateway.manageExternalCRDs=false and connectInject.apiGateway.manageNonStandardCRDs=false" { cd `chart_dir` assert_empty helm template \ - -s templates/crd-tcproutes.yaml \ + -s templates/crd-tcproutes-external.yaml \ --set 'connectInject.apiGateway.manageExternalCRDs=false' \ --set 'connectInject.apiGateway.manageNonStandardCRDs=false' \ . @@ -39,7 +39,7 @@ load _helpers @test "tcproutes/CustomResourceDefinition: enabled with connectInject.apiGateway.manageNonStandardCRDs=true" { cd `chart_dir` local actual=$(helm template \ - -s templates/crd-tcproutes.yaml \ + -s templates/crd-tcproutes-external.yaml \ --set 'connectInject.apiGateway.manageNonStandardCRDs=true' \ . | tee /dev/stderr | yq -s 'length > 0' | tee /dev/stderr) diff --git a/charts/consul/test/unit/crd-tlsroutes.bats b/charts/consul/test/unit/crd-tlsroutes-external.bats similarity index 82% rename from charts/consul/test/unit/crd-tlsroutes.bats rename to charts/consul/test/unit/crd-tlsroutes-external.bats index 7e1d5c471f..88b37521f2 100644 --- a/charts/consul/test/unit/crd-tlsroutes.bats +++ b/charts/consul/test/unit/crd-tlsroutes-external.bats @@ -5,7 +5,7 @@ load _helpers @test "tlsroutes/CustomResourceDefinition: enabled by default" { cd `chart_dir` local actual=$(helm template \ - -s templates/crd-tlsroutes.yaml \ + -s templates/crd-tlsroutes-external.yaml \ . | tee /dev/stderr | yq 'length > 0' | tee /dev/stderr) [ "$actual" = "true" ] @@ -14,7 +14,7 @@ load _helpers @test "tlsroutes/CustomResourceDefinition: disabled with connectInject.enabled=false" { cd `chart_dir` assert_empty helm template \ - -s templates/crd-tlsroutes.yaml \ + -s templates/crd-tlsroutes-external.yaml \ --set 'connectInject.enabled=false' \ . } @@ -22,7 +22,7 @@ load _helpers @test "tlsroutes/CustomResourceDefinition: disabled with connectInject.apiGateway.manageExternalCRDs=false" { cd `chart_dir` assert_empty helm template \ - -s templates/crd-tlsroutes.yaml \ + -s templates/crd-tlsroutes-external.yaml \ --set 'connectInject.apiGateway.manageExternalCRDs=false' \ . } diff --git a/charts/consul/test/unit/crd-udproutes.bats b/charts/consul/test/unit/crd-udproutes-external.bats similarity index 82% rename from charts/consul/test/unit/crd-udproutes.bats rename to charts/consul/test/unit/crd-udproutes-external.bats index 407592a770..6693e67b2d 100644 --- a/charts/consul/test/unit/crd-udproutes.bats +++ b/charts/consul/test/unit/crd-udproutes-external.bats @@ -5,7 +5,7 @@ load _helpers @test "udproutes/CustomResourceDefinition: enabled by default" { cd `chart_dir` local actual=$(helm template \ - -s templates/crd-udproutes.yaml \ + -s templates/crd-udproutes-external.yaml \ . | tee /dev/stderr | yq 'length > 0' | tee /dev/stderr) [ "$actual" = "true" ] @@ -14,7 +14,7 @@ load _helpers @test "udproutes/CustomResourceDefinition: disabled with connectInject.enabled=false" { cd `chart_dir` assert_empty helm template \ - -s templates/crd-udproutes.yaml \ + -s templates/crd-udproutes-external.yaml \ --set 'connectInject.enabled=false' \ . } @@ -22,7 +22,7 @@ load _helpers @test "udproutes/CustomResourceDefinition: disabled with connectInject.apiGateway.manageExternalCRDs=false" { cd `chart_dir` assert_empty helm template \ - -s templates/crd-udproutes.yaml \ + -s templates/crd-udproutes-external.yaml \ --set 'connectInject.apiGateway.manageExternalCRDs=false' \ . } diff --git a/control-plane/PROJECT b/control-plane/PROJECT index b77d2be9a9..7146070824 100644 --- a/control-plane/PROJECT +++ b/control-plane/PROJECT @@ -149,6 +149,6 @@ resources: domain: consul.hashicorp.com group: auth kind: TrafficPermissions - path: github.com/hashicorp/consul-k8s/control-plane/api/v2beta1 + path: github.com/hashicorp/consul-k8s/control-plane/api/auth/v2beta1 version: v2beta1 version: "3" diff --git a/control-plane/api-gateway/common/translation.go b/control-plane/api-gateway/common/translation.go index 07c526be4d..ed949b1ade 100644 --- a/control-plane/api-gateway/common/translation.go +++ b/control-plane/api-gateway/common/translation.go @@ -159,8 +159,8 @@ func (t ResourceTranslator) translateRouteRetryFilter(routeRetryFilter *v1alpha1 func (t ResourceTranslator) translateRouteTimeoutFilter(routeTimeoutFilter *v1alpha1.RouteTimeoutFilter) *api.TimeoutFilter { return &api.TimeoutFilter{ - RequestTimeout: routeTimeoutFilter.Spec.RequestTimeout, - IdleTimeout: routeTimeoutFilter.Spec.IdleTimeout, + RequestTimeout: routeTimeoutFilter.Spec.RequestTimeout.Duration, + IdleTimeout: routeTimeoutFilter.Spec.IdleTimeout.Duration, } } diff --git a/control-plane/api-gateway/common/translation_test.go b/control-plane/api-gateway/common/translation_test.go index 4ce3805cff..8f02f6eca1 100644 --- a/control-plane/api-gateway/common/translation_test.go +++ b/control-plane/api-gateway/common/translation_test.go @@ -1387,8 +1387,8 @@ func TestTranslator_ToHTTPRoute(t *testing.T) { Namespace: "k8s-ns", }, Spec: v1alpha1.RouteTimeoutFilterSpec{ - RequestTimeout: 10, - IdleTimeout: 30, + RequestTimeout: metav1.Duration{Duration: 10}, + IdleTimeout: metav1.Duration{Duration: 30}, }, }, diff --git a/control-plane/api/v2beta1/groupversion_info.go b/control-plane/api/auth/v2beta1/auth_groupversion_info.go similarity index 94% rename from control-plane/api/v2beta1/groupversion_info.go rename to control-plane/api/auth/v2beta1/auth_groupversion_info.go index 35e6d2056b..3329d86855 100644 --- a/control-plane/api/v2beta1/groupversion_info.go +++ b/control-plane/api/auth/v2beta1/auth_groupversion_info.go @@ -13,8 +13,7 @@ import ( var ( - // AUTH group. - + // AuthGroup is a collection of auth resources. AuthGroup = "auth.consul.hashicorp.com" // AuthGroupVersion is group version used to register these objects. diff --git a/control-plane/api/v2beta1/shared_types.go b/control-plane/api/auth/v2beta1/shared_types.go similarity index 100% rename from control-plane/api/v2beta1/shared_types.go rename to control-plane/api/auth/v2beta1/shared_types.go diff --git a/control-plane/api/v2beta1/status.go b/control-plane/api/auth/v2beta1/status.go similarity index 100% rename from control-plane/api/v2beta1/status.go rename to control-plane/api/auth/v2beta1/status.go diff --git a/control-plane/api/auth/v2beta1/traffic_permissions_types.go b/control-plane/api/auth/v2beta1/traffic_permissions_types.go new file mode 100644 index 0000000000..e3a0d32f1a --- /dev/null +++ b/control-plane/api/auth/v2beta1/traffic_permissions_types.go @@ -0,0 +1,242 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package v2beta1 + +import ( + "fmt" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + pbauth "github.com/hashicorp/consul/proto-public/pbauth/v2beta1" + "github.com/hashicorp/consul/proto-public/pbresource" + "google.golang.org/protobuf/testing/protocmp" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + + "github.com/hashicorp/consul-k8s/control-plane/api/common" + inject "github.com/hashicorp/consul-k8s/control-plane/connect-inject/common" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" +) + +const ( + trafficpermissionsKubeKind = "trafficpermissions" +) + +func init() { + AuthSchemeBuilder.Register(&TrafficPermissions{}, &TrafficPermissionsList{}) +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status + +// TrafficPermissions is the Schema for the traffic-permissions API +// +kubebuilder:printcolumn:name="Synced",type="string",JSONPath=".status.conditions[?(@.type==\"Synced\")].status",description="The sync status of the resource with Consul" +// +kubebuilder:printcolumn:name="Last Synced",type="date",JSONPath=".status.lastSyncedTime",description="The last successful synced time of the resource with Consul" +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="The age of the resource" +// +kubebuilder:resource:shortName="traffic-permissions" +type TrafficPermissions struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec pbauth.TrafficPermissions `json:"spec,omitempty"` + Status `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// TrafficPermissionsList contains a list of TrafficPermissions. +type TrafficPermissionsList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []*TrafficPermissions `json:"items"` +} + +func (in *TrafficPermissions) ResourceID(namespace, partition string) *pbresource.ID { + return &pbresource.ID{ + Name: in.Name, + Type: pbauth.TrafficPermissionsType, + Tenancy: &pbresource.Tenancy{ + Partition: partition, + Namespace: namespace, + + // Because we are explicitly defining NS/partition, this will not default and must be explicit. + // At a future point, this will move out of the Tenancy block. + PeerName: constants.DefaultConsulPeer, + }, + } +} + +func (in *TrafficPermissions) Resource(namespace, partition string) *pbresource.Resource { + return &pbresource.Resource{ + Id: in.ResourceID(namespace, partition), + Data: inject.ToProtoAny(&in.Spec), + Metadata: meshConfigMeta(), + } +} + +func (in *TrafficPermissions) MatchesConsul(candidate *pbresource.Resource, namespace, partition string) bool { + return cmp.Equal( + in.Resource(namespace, partition), + candidate, + protocmp.IgnoreFields(&pbresource.Resource{}, "status", "generation", "version"), + protocmp.IgnoreFields(&pbresource.ID{}, "uid"), + protocmp.Transform(), + cmpopts.SortSlices(func(a, b any) bool { return fmt.Sprintf("%v", a) < fmt.Sprintf("%v", b) }), + ) +} + +func (in *TrafficPermissions) AddFinalizer(f string) { + in.ObjectMeta.Finalizers = append(in.Finalizers(), f) +} + +func (in *TrafficPermissions) RemoveFinalizer(f string) { + var newFinalizers []string + for _, oldF := range in.Finalizers() { + if oldF != f { + newFinalizers = append(newFinalizers, oldF) + } + } + in.ObjectMeta.Finalizers = newFinalizers +} + +func (in *TrafficPermissions) Finalizers() []string { + return in.ObjectMeta.Finalizers +} + +func (in *TrafficPermissions) KubeKind() string { + return trafficpermissionsKubeKind +} + +func (in *TrafficPermissions) KubernetesName() string { + return in.ObjectMeta.Name +} + +func (in *TrafficPermissions) SetSyncedCondition(status corev1.ConditionStatus, reason, message string) { + in.Status.Conditions = Conditions{ + { + Type: ConditionSynced, + Status: status, + LastTransitionTime: metav1.Now(), + Reason: reason, + Message: message, + }, + } +} + +func (in *TrafficPermissions) SetLastSyncedTime(time *metav1.Time) { + in.Status.LastSyncedTime = time +} + +func (in *TrafficPermissions) SyncedCondition() (status corev1.ConditionStatus, reason, message string) { + cond := in.Status.GetCondition(ConditionSynced) + if cond == nil { + return corev1.ConditionUnknown, "", "" + } + return cond.Status, cond.Reason, cond.Message +} + +func (in *TrafficPermissions) SyncedConditionStatus() corev1.ConditionStatus { + condition := in.Status.GetCondition(ConditionSynced) + if condition == nil { + return corev1.ConditionUnknown + } + return condition.Status +} + +func (in *TrafficPermissions) Validate(tenancy common.ConsulTenancyConfig) error { + var errs field.ErrorList + path := field.NewPath("spec") + var tp pbauth.TrafficPermissions + res := in.Resource(tenancy.ConsulDestinationNamespace, tenancy.ConsulPartition) + if err := res.Data.UnmarshalTo(&tp); err != nil { + return fmt.Errorf("error parsing resource data as type %q: %s", &tp, err) + } + + switch tp.Action { + case pbauth.Action_ACTION_ALLOW: + case pbauth.Action_ACTION_DENY: + case pbauth.Action_ACTION_UNSPECIFIED: + fallthrough + default: + errs = append(errs, field.Invalid(path.Child("action"), tp.Action, "action must be either allow or deny")) + } + + if tp.Destination == nil || (len(tp.Destination.IdentityName) == 0) { + errs = append(errs, field.Invalid(path.Child("destination"), tp.Destination, "cannot be empty")) + } + // Validate permissions + for i, permission := range tp.Permissions { + if err := validatePermission(permission, path.Child("permissions").Index(i)); err != nil { + errs = append(errs, err...) + } + } + if len(errs) > 0 { + return apierrors.NewInvalid( + schema.GroupKind{Group: AuthGroup, Kind: common.TrafficPermissions}, + in.KubernetesName(), errs) + } + return nil +} + +func validatePermission(p *pbauth.Permission, path *field.Path) field.ErrorList { + var errs field.ErrorList + + for s, src := range p.Sources { + if sourceHasIncompatibleTenancies(src) { + errs = append(errs, field.Invalid(path.Child("sources").Index(s), src, "permission sources may not specify partitions, peers, and sameness_groups together")) + } + + if src.Namespace == "" && src.IdentityName != "" { + errs = append(errs, field.Invalid(path.Child("sources").Index(s), src, "permission sources may not have wildcard namespaces and explicit names")) + } + + // Excludes are only valid for wildcard sources. + if src.IdentityName != "" && len(src.Exclude) > 0 { + errs = append(errs, field.Invalid(path.Child("sources").Index(s), src, "must be defined on wildcard sources")) + continue + } + + for e, d := range src.Exclude { + if sourceHasIncompatibleTenancies(d) { + errs = append(errs, field.Invalid(path.Child("sources").Index(s).Child("exclude").Index(e), d, "permissions sources may not specify partitions, peers, and sameness_groups together")) + } + + if d.Namespace == "" && d.IdentityName != "" { + errs = append(errs, field.Invalid(path.Child("sources").Index(s).Child("exclude").Index(e), d, "permission sources may not have wildcard namespaces and explicit names")) + } + } + } + for d, dest := range p.DestinationRules { + if (len(dest.PathExact) > 0 && len(dest.PathPrefix) > 0) || + (len(dest.PathRegex) > 0 && len(dest.PathExact) > 0) || + (len(dest.PathRegex) > 0 && len(dest.PathPrefix) > 0) { + errs = append(errs, field.Invalid(path.Child("destinationRules").Index(d), dest, "prefix values, regex values, and explicit names must not combined")) + } + if len(dest.Exclude) > 0 { + for e, excl := range dest.Exclude { + if (len(excl.PathExact) > 0 && len(excl.PathPrefix) > 0) || + (len(excl.PathRegex) > 0 && len(excl.PathExact) > 0) || + (len(excl.PathRegex) > 0 && len(excl.PathPrefix) > 0) { + errs = append(errs, field.Invalid(path.Child("destinationRules").Index(d).Child("exclude").Index(e), excl, "prefix values, regex values, and explicit names must not combined")) + } + } + } + } + + return errs +} + +func sourceHasIncompatibleTenancies(src pbauth.SourceToSpiffe) bool { + peerSet := src.GetPeer() != common.DefaultPeerName && src.GetPeer() != "" + apSet := src.GetPartition() != common.DefaultPartitionName && src.GetPartition() != "" + sgSet := src.GetSamenessGroup() != "" + + return (apSet && peerSet) || (apSet && sgSet) || (peerSet && sgSet) +} + +// DefaultNamespaceFields is required as part of the common.MeshConfig interface. +func (in *TrafficPermissions) DefaultNamespaceFields(tenancy common.ConsulTenancyConfig) {} diff --git a/control-plane/api/v2beta1/traffic_permissions_types_test.go b/control-plane/api/auth/v2beta1/traffic_permissions_types_test.go similarity index 55% rename from control-plane/api/v2beta1/traffic_permissions_types_test.go rename to control-plane/api/auth/v2beta1/traffic_permissions_types_test.go index f48740e496..79b5e71b85 100644 --- a/control-plane/api/v2beta1/traffic_permissions_types_test.go +++ b/control-plane/api/auth/v2beta1/traffic_permissions_types_test.go @@ -27,7 +27,7 @@ func TestTrafficPermissions_MatchesConsul(t *testing.T) { cases := map[string]struct { OurConsulNamespace string OurConsulPartition string - OurData TrafficPermissions + OurData *TrafficPermissions TheirName string TheirConsulNamespace string @@ -40,11 +40,11 @@ func TestTrafficPermissions_MatchesConsul(t *testing.T) { "empty fields matches": { OurConsulNamespace: constants.DefaultConsulNS, OurConsulPartition: constants.DefaultConsulPartition, - OurData: TrafficPermissions{ + OurData: &TrafficPermissions{ ObjectMeta: metav1.ObjectMeta{ Name: "name", }, - Spec: TrafficPermissionsSpec{}, + Spec: pbauth.TrafficPermissions{}, }, TheirName: "name", TheirConsulNamespace: constants.DefaultConsulNS, @@ -59,19 +59,19 @@ func TestTrafficPermissions_MatchesConsul(t *testing.T) { "source namespaces and partitions are compared": { OurConsulNamespace: "consul-ns", OurConsulPartition: "consul-partition", - OurData: TrafficPermissions{ + OurData: &TrafficPermissions{ ObjectMeta: metav1.ObjectMeta{ Name: "foo", Namespace: "kube-ns", }, - Spec: TrafficPermissionsSpec{ - Destination: &Destination{ + Spec: pbauth.TrafficPermissions{ + Destination: &pbauth.Destination{ IdentityName: "destination-identity", }, - Action: ActionAllow, - Permissions: Permissions{ + Action: pbauth.Action_ACTION_ALLOW, + Permissions: []*pbauth.Permission{ { - Sources: []*Source{ + Sources: []*pbauth.Source{ { IdentityName: "source-identity", Namespace: "the space namespace space", @@ -105,19 +105,19 @@ func TestTrafficPermissions_MatchesConsul(t *testing.T) { "destination namespaces and partitions are compared": { OurConsulNamespace: "not-consul-ns", OurConsulPartition: "not-consul-partition", - OurData: TrafficPermissions{ + OurData: &TrafficPermissions{ ObjectMeta: metav1.ObjectMeta{ Name: "foo", Namespace: "kube-ns", }, - Spec: TrafficPermissionsSpec{ - Destination: &Destination{ + Spec: pbauth.TrafficPermissions{ + Destination: &pbauth.Destination{ IdentityName: "destination-identity", }, - Action: ActionAllow, - Permissions: Permissions{ + Action: pbauth.Action_ACTION_DENY, + Permissions: []*pbauth.Permission{ { - Sources: []*Source{ + Sources: []*pbauth.Source{ { IdentityName: "source-identity", }, @@ -149,25 +149,25 @@ func TestTrafficPermissions_MatchesConsul(t *testing.T) { "all fields set matches": { OurConsulNamespace: "consul-ns", OurConsulPartition: "consul-partition", - OurData: TrafficPermissions{ + OurData: &TrafficPermissions{ ObjectMeta: metav1.ObjectMeta{ Name: "foo", Namespace: "kube-ns", }, - Spec: TrafficPermissionsSpec{ - Destination: &Destination{ + Spec: pbauth.TrafficPermissions{ + Destination: &pbauth.Destination{ IdentityName: "destination-identity", }, - Action: ActionAllow, - Permissions: Permissions{ + Action: pbauth.Action_ACTION_ALLOW, + Permissions: []*pbauth.Permission{ { - Sources: []*Source{ + Sources: []*pbauth.Source{ { Namespace: "the space namespace space", Partition: "space-partition", Peer: "space-peer", SamenessGroup: "space-group", - Exclude: Exclude{ + Exclude: []*pbauth.ExcludeSource{ { IdentityName: "not-source-identity", Namespace: "the space namespace space", @@ -181,12 +181,12 @@ func TestTrafficPermissions_MatchesConsul(t *testing.T) { IdentityName: "source-identity", }, }, - DestinationRules: DestinationRules{ + DestinationRules: []*pbauth.DestinationRule{ { PathExact: "/hello", PathPrefix: "/world", PathRegex: "/.*/foo", - Header: &DestinationRuleHeader{ + Header: &pbauth.DestinationRuleHeader{ Name: "x-consul-test", Present: true, Exact: "true", @@ -196,12 +196,12 @@ func TestTrafficPermissions_MatchesConsul(t *testing.T) { Invert: true, }, Methods: []string{"GET", "POST"}, - Exclude: ExcludePermissions{ + Exclude: []*pbauth.ExcludePermissionRule{ { PathExact: "/hello", PathPrefix: "/world", PathRegex: "/.*/foo", - Header: &DestinationRuleHeader{ + Header: &pbauth.DestinationRuleHeader{ Name: "x-consul-not-test", Present: true, Exact: "false", @@ -296,11 +296,11 @@ func TestTrafficPermissions_MatchesConsul(t *testing.T) { "different types does not match": { OurConsulNamespace: constants.DefaultConsulNS, OurConsulPartition: constants.DefaultConsulPartition, - OurData: TrafficPermissions{ + OurData: &TrafficPermissions{ ObjectMeta: metav1.ObjectMeta{ Name: "name", }, - Spec: TrafficPermissionsSpec{}, + Spec: pbauth.TrafficPermissions{}, }, ResourceOverride: &pbresource.Resource{ Id: &pbresource.ID{ @@ -335,18 +335,18 @@ func TestTrafficPermissions_MatchesConsul(t *testing.T) { // TestTrafficPermissions_Resource also includes test to verify ResourceID(). func TestTrafficPermissions_Resource(t *testing.T) { cases := map[string]struct { - Ours TrafficPermissions + Ours *TrafficPermissions ConsulNamespace string ConsulPartition string ExpectedName string ExpectedData *pbauth.TrafficPermissions }{ "empty fields": { - Ours: TrafficPermissions{ + Ours: &TrafficPermissions{ ObjectMeta: metav1.ObjectMeta{ Name: "foo", }, - Spec: TrafficPermissionsSpec{}, + Spec: pbauth.TrafficPermissions{}, }, ConsulNamespace: constants.DefaultConsulNS, ConsulPartition: constants.DefaultConsulPartition, @@ -354,25 +354,25 @@ func TestTrafficPermissions_Resource(t *testing.T) { ExpectedData: &pbauth.TrafficPermissions{}, }, "every field set": { - Ours: TrafficPermissions{ + Ours: &TrafficPermissions{ ObjectMeta: metav1.ObjectMeta{ Name: "foo", Namespace: "kube-ns", }, - Spec: TrafficPermissionsSpec{ - Destination: &Destination{ + Spec: pbauth.TrafficPermissions{ + Destination: &pbauth.Destination{ IdentityName: "destination-identity", }, - Action: ActionAllow, - Permissions: Permissions{ + Action: pbauth.Action_ACTION_ALLOW, + Permissions: []*pbauth.Permission{ { - Sources: []*Source{ + Sources: []*pbauth.Source{ { Namespace: "the space namespace space", Partition: "space-partition", Peer: "space-peer", SamenessGroup: "space-group", - Exclude: Exclude{ + Exclude: []*pbauth.ExcludeSource{ { IdentityName: "not-source-identity", Namespace: "the space namespace space", @@ -386,12 +386,12 @@ func TestTrafficPermissions_Resource(t *testing.T) { IdentityName: "source-identity", }, }, - DestinationRules: DestinationRules{ + DestinationRules: []*pbauth.DestinationRule{ { PathExact: "/hello", PathPrefix: "/world", PathRegex: "/.*/foo", - Header: &DestinationRuleHeader{ + Header: &pbauth.DestinationRuleHeader{ Name: "x-consul-test", Present: true, Exact: "true", @@ -401,12 +401,12 @@ func TestTrafficPermissions_Resource(t *testing.T) { Invert: true, }, Methods: []string{"GET", "POST"}, - Exclude: ExcludePermissions{ + Exclude: []*pbauth.ExcludePermissionRule{ { PathExact: "/hello", PathPrefix: "/world", PathRegex: "/.*/foo", - Header: &DestinationRuleHeader{ + Header: &pbauth.DestinationRuleHeader{ Name: "x-consul-not-test", Present: true, Exact: "false", @@ -579,8 +579,8 @@ func TestTrafficPermissions_KubernetesName(t *testing.T) { Name: "test", Namespace: "bar", }, - Spec: TrafficPermissionsSpec{ - Destination: &Destination{ + Spec: pbauth.TrafficPermissions{ + Destination: &pbauth.Destination{ IdentityName: "foo", }, }, @@ -615,39 +615,34 @@ func TestTrafficPermissions_Validate(t *testing.T) { Name: "foo", Namespace: "kube-ns", }, - Spec: TrafficPermissionsSpec{ - Destination: &Destination{ + Spec: pbauth.TrafficPermissions{ + Destination: &pbauth.Destination{ IdentityName: "destination-identity", }, - Action: ActionAllow, - Permissions: Permissions{ + Action: pbauth.Action_ACTION_ALLOW, + Permissions: []*pbauth.Permission{ { - Sources: []*Source{ + Sources: []*pbauth.Source{ { - Namespace: "the space namespace space", - Partition: "space-partition", - Peer: "space-peer", - SamenessGroup: "space-group", - Exclude: Exclude{ + Namespace: "the space namespace space", + Partition: "space-partition", + Exclude: []*pbauth.ExcludeSource{ { IdentityName: "not-source-identity", Namespace: "the space namespace space", - Partition: "space-partition", - Peer: "space-peer", SamenessGroup: "space-group", }, }, }, { IdentityName: "source-identity", + Namespace: "another-namespace", }, }, - DestinationRules: DestinationRules{ + DestinationRules: []*pbauth.DestinationRule{ { - PathExact: "/hello", - PathPrefix: "/world", - PathRegex: "/.*/foo", - Header: &DestinationRuleHeader{ + PathExact: "/hello", + Header: &pbauth.DestinationRuleHeader{ Name: "x-consul-test", Present: true, Exact: "true", @@ -657,12 +652,10 @@ func TestTrafficPermissions_Validate(t *testing.T) { Invert: true, }, Methods: []string{"GET", "POST"}, - Exclude: ExcludePermissions{ + Exclude: []*pbauth.ExcludePermissionRule{ { - PathExact: "/hello", PathPrefix: "/world", - PathRegex: "/.*/foo", - Header: &DestinationRuleHeader{ + Header: &pbauth.DestinationRuleHeader{ Name: "x-consul-not-test", Present: true, Exact: "false", @@ -691,64 +684,304 @@ func TestTrafficPermissions_Validate(t *testing.T) { Name: "does-not-matter", Namespace: "not-default-ns", }, - Spec: TrafficPermissionsSpec{ - Destination: &Destination{ + Spec: pbauth.TrafficPermissions{ + Destination: &pbauth.Destination{ IdentityName: "dest-service", }, }, }, expectedErrMsgs: []string{ - "spec.action: Required value: action is required", - "spec.action: Invalid value: \"\": must be one of \"allow\" or \"deny\"", + `trafficpermissions.auth.consul.hashicorp.com "does-not-matter" is invalid: spec.action: Invalid value: ACTION_UNSPECIFIED: action must be either allow or deny`, }, }, { - name: "action must be valid", + name: "destination is required", input: &TrafficPermissions{ ObjectMeta: metav1.ObjectMeta{ Name: "does-not-matter", Namespace: "not-default-ns", }, - Spec: TrafficPermissionsSpec{ - Destination: &Destination{ - IdentityName: "dest-service", + Spec: pbauth.TrafficPermissions{ + Action: pbauth.Action_ACTION_ALLOW, + }, + }, + expectedErrMsgs: []string{ + `trafficpermissions.auth.consul.hashicorp.com "does-not-matter" is invalid: spec.destination: Invalid value: "null": cannot be empty`, + }, + }, + { + name: "destination.identityName is required", + input: &TrafficPermissions{ + ObjectMeta: metav1.ObjectMeta{ + Name: "does-not-matter", + Namespace: "not-default-ns", + }, + Spec: pbauth.TrafficPermissions{ + Action: pbauth.Action_ACTION_ALLOW, + Destination: &pbauth.Destination{}, + }, + }, + expectedErrMsgs: []string{ + `trafficpermissions.auth.consul.hashicorp.com "does-not-matter" is invalid: spec.destination: Invalid value: authv2beta1.Destination{state:impl.MessageState{NoUnkeyedLiterals:pragma.NoUnkeyedLiterals{}, DoNotCompare:pragma.DoNotCompare{}, DoNotCopy:pragma.DoNotCopy{}, atomicMessageInfo:(*impl.MessageInfo)(nil)}, sizeCache:0, unknownFields:[]uint8(nil), IdentityName:""}: cannot be empty`, + }, + }, + { + name: "permission.sources: partitions, peers, and sameness_groups", + input: &TrafficPermissions{ + ObjectMeta: metav1.ObjectMeta{ + Name: "does-not-matter", + Namespace: "not-default-ns", + }, + Spec: pbauth.TrafficPermissions{ + Destination: &pbauth.Destination{ + IdentityName: "destination-identity", + }, + Action: pbauth.Action_ACTION_ALLOW, + Permissions: []*pbauth.Permission{ + { + Sources: []*pbauth.Source{ + { + Namespace: "the space namespace space", + Partition: "space-partition", + Peer: "space-peer", + }, + { + Namespace: "the space namespace space", + Partition: "space-partition", + SamenessGroup: "space-sameness", + }, + { + Namespace: "the space namespace space", + Peer: "space-peer", + SamenessGroup: "space-sameness", + }, + }, + }, }, - Action: "blurg", }, }, expectedErrMsgs: []string{ - "spec.action: Invalid value: \"blurg\": must be one of \"allow\" or \"deny\"", + `spec.permissions[0].sources[0]: Invalid value: authv2beta1.Source{state:impl.MessageState{NoUnkeyedLiterals:pragma.NoUnkeyedLiterals{}, DoNotCompare:pragma.DoNotCompare{}, DoNotCopy:pragma.DoNotCopy{}, atomicMessageInfo:(*impl.MessageInfo)(nil)}, sizeCache:0, unknownFields:[]uint8(nil), IdentityName:"", Namespace:"the space namespace space", Partition:"space-partition", Peer:"space-peer", SamenessGroup:"", Exclude:[]*authv2beta1.ExcludeSource(nil)}: permission sources may not specify partitions, peers, and sameness_groups together`, + `spec.permissions[0].sources[1]: Invalid value: authv2beta1.Source{state:impl.MessageState{NoUnkeyedLiterals:pragma.NoUnkeyedLiterals{}, DoNotCompare:pragma.DoNotCompare{}, DoNotCopy:pragma.DoNotCopy{}, atomicMessageInfo:(*impl.MessageInfo)(nil)}, sizeCache:0, unknownFields:[]uint8(nil), IdentityName:"", Namespace:"the space namespace space", Partition:"space-partition", Peer:"", SamenessGroup:"space-sameness", Exclude:[]*authv2beta1.ExcludeSource(nil)}: permission sources may not specify partitions, peers, and sameness_groups together`, + `spec.permissions[0].sources[2]: Invalid value: authv2beta1.Source{state:impl.MessageState{NoUnkeyedLiterals:pragma.NoUnkeyedLiterals{}, DoNotCompare:pragma.DoNotCompare{}, DoNotCopy:pragma.DoNotCopy{}, atomicMessageInfo:(*impl.MessageInfo)(nil)}, sizeCache:0, unknownFields:[]uint8(nil), IdentityName:"", Namespace:"the space namespace space", Partition:"", Peer:"space-peer", SamenessGroup:"space-sameness", Exclude:[]*authv2beta1.ExcludeSource(nil)}: permission sources may not specify partitions, peers, and sameness_groups together`, }, }, { - name: "destination is required", + name: "permission.sources: identity name without namespace", input: &TrafficPermissions{ ObjectMeta: metav1.ObjectMeta{ Name: "does-not-matter", Namespace: "not-default-ns", }, - Spec: TrafficPermissionsSpec{ - Action: "allow", + Spec: pbauth.TrafficPermissions{ + Destination: &pbauth.Destination{ + IdentityName: "destination-identity", + }, + Action: pbauth.Action_ACTION_ALLOW, + Permissions: []*pbauth.Permission{ + { + Sources: []*pbauth.Source{ + { + IdentityName: "false-identity", + }, + }, + }, + }, }, }, expectedErrMsgs: []string{ - "spec.destination: Required value: destination and destination.identityName are required", + `spec.permissions[0].sources[0]: Invalid value: authv2beta1.Source{state:impl.MessageState{NoUnkeyedLiterals:pragma.NoUnkeyedLiterals{}, DoNotCompare:pragma.DoNotCompare{}, DoNotCopy:pragma.DoNotCopy{}, atomicMessageInfo:(*impl.MessageInfo)(nil)}, sizeCache:0, unknownFields:[]uint8(nil), IdentityName:"false-identity", Namespace:"", Partition:"", Peer:"", SamenessGroup:"", Exclude:[]*authv2beta1.ExcludeSource(nil)}: permission sources may not have wildcard namespaces and explicit names`, }, }, { - name: "destination.identityName is required", + name: "permission.sources: identity name with excludes", + input: &TrafficPermissions{ + ObjectMeta: metav1.ObjectMeta{ + Name: "does-not-matter", + Namespace: "not-default-ns", + }, + Spec: pbauth.TrafficPermissions{ + Destination: &pbauth.Destination{ + IdentityName: "destination-identity", + }, + Action: pbauth.Action_ACTION_ALLOW, + Permissions: []*pbauth.Permission{ + { + Sources: []*pbauth.Source{ + { + Namespace: "default-namespace", + IdentityName: "false-identity", + Exclude: []*pbauth.ExcludeSource{ + { + IdentityName: "not-source-identity", + }, + }, + }, + }, + }, + }, + }, + }, + expectedErrMsgs: []string{ + `must be defined on wildcard sources`, + }, + }, + { + name: "permission.sources.exclude: incompatible tenancies", input: &TrafficPermissions{ ObjectMeta: metav1.ObjectMeta{ Name: "does-not-matter", Namespace: "not-default-ns", }, - Spec: TrafficPermissionsSpec{ - Action: "allow", - Destination: &Destination{}, + Spec: pbauth.TrafficPermissions{ + Destination: &pbauth.Destination{ + IdentityName: "destination-identity", + }, + Action: pbauth.Action_ACTION_ALLOW, + Permissions: []*pbauth.Permission{ + { + Sources: []*pbauth.Source{ + { + Namespace: "default-namespace", + Exclude: []*pbauth.ExcludeSource{ + { + Namespace: "the space namespace space", + Partition: "space-partition", + Peer: "space-peer", + }, + { + Namespace: "the space namespace space", + Partition: "space-partition", + SamenessGroup: "space-sameness", + }, + { + Namespace: "the space namespace space", + Peer: "space-peer", + SamenessGroup: "space-sameness", + }, + }, + }, + }, + }, + }, + }, + }, + expectedErrMsgs: []string{ + `spec.permissions[0].sources[0].exclude[0]: Invalid value: authv2beta1.ExcludeSource{state:impl.MessageState{NoUnkeyedLiterals:pragma.NoUnkeyedLiterals{}, DoNotCompare:pragma.DoNotCompare{}, DoNotCopy:pragma.DoNotCopy{}, atomicMessageInfo:(*impl.MessageInfo)(nil)}, sizeCache:0, unknownFields:[]uint8(nil), IdentityName:"", Namespace:"the space namespace space", Partition:"space-partition", Peer:"space-peer", SamenessGroup:""}: permissions sources may not specify partitions, peers, and sameness_groups together`, + `spec.permissions[0].sources[0].exclude[1]: Invalid value: authv2beta1.ExcludeSource{state:impl.MessageState{NoUnkeyedLiterals:pragma.NoUnkeyedLiterals{}, DoNotCompare:pragma.DoNotCompare{}, DoNotCopy:pragma.DoNotCopy{}, atomicMessageInfo:(*impl.MessageInfo)(nil)}, sizeCache:0, unknownFields:[]uint8(nil), IdentityName:"", Namespace:"the space namespace space", Partition:"space-partition", Peer:"", SamenessGroup:"space-sameness"}: permissions sources may not specify partitions, peers, and sameness_groups together`, + `spec.permissions[0].sources[0].exclude[2]: Invalid value: authv2beta1.ExcludeSource{state:impl.MessageState{NoUnkeyedLiterals:pragma.NoUnkeyedLiterals{}, DoNotCompare:pragma.DoNotCompare{}, DoNotCopy:pragma.DoNotCopy{}, atomicMessageInfo:(*impl.MessageInfo)(nil)}, sizeCache:0, unknownFields:[]uint8(nil), IdentityName:"", Namespace:"the space namespace space", Partition:"", Peer:"space-peer", SamenessGroup:"space-sameness"}: permissions sources may not specify partitions, peers, and sameness_groups together`, + }, + }, + { + name: "permission.sources.exclude: identity name without namespace", + input: &TrafficPermissions{ + ObjectMeta: metav1.ObjectMeta{ + Name: "does-not-matter", + Namespace: "not-default-ns", + }, + Spec: pbauth.TrafficPermissions{ + Destination: &pbauth.Destination{ + IdentityName: "destination-identity", + }, + Action: pbauth.Action_ACTION_ALLOW, + Permissions: []*pbauth.Permission{ + { + Sources: []*pbauth.Source{ + { + Namespace: "default-namespace", + Exclude: []*pbauth.ExcludeSource{ + { + IdentityName: "false-identity", + }, + }, + }, + }, + }, + }, + }, + }, + expectedErrMsgs: []string{ + `spec.permissions[0].sources[0].exclude[0]: Invalid value: authv2beta1.ExcludeSource{state:impl.MessageState{NoUnkeyedLiterals:pragma.NoUnkeyedLiterals{}, DoNotCompare:pragma.DoNotCompare{}, DoNotCopy:pragma.DoNotCopy{}, atomicMessageInfo:(*impl.MessageInfo)(nil)}, sizeCache:0, unknownFields:[]uint8(nil), IdentityName:"false-identity", Namespace:"", Partition:"", Peer:"", SamenessGroup:""}: permission sources may not have wildcard namespaces and explicit names`, + }, + }, + { + name: "permission.destinationRules: incompatible destination rules", + input: &TrafficPermissions{ + ObjectMeta: metav1.ObjectMeta{ + Name: "does-not-matter", + Namespace: "not-default-ns", + }, + Spec: pbauth.TrafficPermissions{ + Destination: &pbauth.Destination{ + IdentityName: "destination-identity", + }, + Action: pbauth.Action_ACTION_ALLOW, + Permissions: []*pbauth.Permission{ + { + DestinationRules: []*pbauth.DestinationRule{ + { + PathExact: "/hello", + PathPrefix: "foobar", + }, + { + PathExact: "/hello", + PathRegex: "path-regex", + }, + { + PathPrefix: "foobar", + PathRegex: "path-regex", + }, + }, + }, + }, + }, + }, + expectedErrMsgs: []string{ + `spec.permissions[0].destinationRules[0]: Invalid value: authv2beta1.DestinationRule{state:impl.MessageState{NoUnkeyedLiterals:pragma.NoUnkeyedLiterals{}, DoNotCompare:pragma.DoNotCompare{}, DoNotCopy:pragma.DoNotCopy{}, atomicMessageInfo:(*impl.MessageInfo)(nil)}, sizeCache:0, unknownFields:[]uint8(nil), PathExact:"/hello", PathPrefix:"foobar", PathRegex:"", Methods:[]string(nil), Header:(*authv2beta1.DestinationRuleHeader)(nil), PortNames:[]string(nil), Exclude:[]*authv2beta1.ExcludePermissionRule(nil)}: prefix values, regex values, and explicit names must not combined`, + `spec.permissions[0].destinationRules[1]: Invalid value: authv2beta1.DestinationRule{state:impl.MessageState{NoUnkeyedLiterals:pragma.NoUnkeyedLiterals{}, DoNotCompare:pragma.DoNotCompare{}, DoNotCopy:pragma.DoNotCopy{}, atomicMessageInfo:(*impl.MessageInfo)(nil)}, sizeCache:0, unknownFields:[]uint8(nil), PathExact:"/hello", PathPrefix:"", PathRegex:"path-regex", Methods:[]string(nil), Header:(*authv2beta1.DestinationRuleHeader)(nil), PortNames:[]string(nil), Exclude:[]*authv2beta1.ExcludePermissionRule(nil)}: prefix values, regex values, and explicit names must not combined`, + `spec.permissions[0].destinationRules[2]: Invalid value: authv2beta1.DestinationRule{state:impl.MessageState{NoUnkeyedLiterals:pragma.NoUnkeyedLiterals{}, DoNotCompare:pragma.DoNotCompare{}, DoNotCopy:pragma.DoNotCopy{}, atomicMessageInfo:(*impl.MessageInfo)(nil)}, sizeCache:0, unknownFields:[]uint8(nil), PathExact:"", PathPrefix:"foobar", PathRegex:"path-regex", Methods:[]string(nil), Header:(*authv2beta1.DestinationRuleHeader)(nil), PortNames:[]string(nil), Exclude:[]*authv2beta1.ExcludePermissionRule(nil)}: prefix values, regex values, and explicit names must not combined`, + }, + }, + { + name: "permission.destinationRules.exclude: incompatible destination rules", + input: &TrafficPermissions{ + ObjectMeta: metav1.ObjectMeta{ + Name: "does-not-matter", + Namespace: "not-default-ns", + }, + Spec: pbauth.TrafficPermissions{ + Destination: &pbauth.Destination{ + IdentityName: "destination-identity", + }, + Action: pbauth.Action_ACTION_ALLOW, + Permissions: []*pbauth.Permission{ + { + DestinationRules: []*pbauth.DestinationRule{ + { + Exclude: []*pbauth.ExcludePermissionRule{ + { + PathExact: "/hello", + PathPrefix: "foobar", + }, + { + PathExact: "/hello", + PathRegex: "path-regex", + }, + { + PathPrefix: "foobar", + PathRegex: "path-regex", + }, + }, + }, + }, + }, + }, }, }, expectedErrMsgs: []string{ - "spec.destination.identityName: Required value: identityName is required", + `spec.permissions[0].destinationRules[0].exclude[0]: Invalid value: authv2beta1.ExcludePermissionRule{state:impl.MessageState{NoUnkeyedLiterals:pragma.NoUnkeyedLiterals{}, DoNotCompare:pragma.DoNotCompare{}, DoNotCopy:pragma.DoNotCopy{}, atomicMessageInfo:(*impl.MessageInfo)(nil)}, sizeCache:0, unknownFields:[]uint8(nil), PathExact:"/hello", PathPrefix:"foobar", PathRegex:"", Methods:[]string(nil), Header:(*authv2beta1.DestinationRuleHeader)(nil), PortNames:[]string(nil)}: prefix values, regex values, and explicit names must not combined`, + `spec.permissions[0].destinationRules[0].exclude[1]: Invalid value: authv2beta1.ExcludePermissionRule{state:impl.MessageState{NoUnkeyedLiterals:pragma.NoUnkeyedLiterals{}, DoNotCompare:pragma.DoNotCompare{}, DoNotCopy:pragma.DoNotCopy{}, atomicMessageInfo:(*impl.MessageInfo)(nil)}, sizeCache:0, unknownFields:[]uint8(nil), PathExact:"/hello", PathPrefix:"", PathRegex:"path-regex", Methods:[]string(nil), Header:(*authv2beta1.DestinationRuleHeader)(nil), PortNames:[]string(nil)}: prefix values, regex values, and explicit names must not combined`, + `spec.permissions[0].destinationRules[0].exclude[2]: Invalid value: authv2beta1.ExcludePermissionRule{state:impl.MessageState{NoUnkeyedLiterals:pragma.NoUnkeyedLiterals{}, DoNotCompare:pragma.DoNotCompare{}, DoNotCopy:pragma.DoNotCopy{}, atomicMessageInfo:(*impl.MessageInfo)(nil)}, sizeCache:0, unknownFields:[]uint8(nil), PathExact:"", PathPrefix:"foobar", PathRegex:"path-regex", Methods:[]string(nil), Header:(*authv2beta1.DestinationRuleHeader)(nil), PortNames:[]string(nil)}: prefix values, regex values, and explicit names must not combined`, }, }, } diff --git a/control-plane/api/v2beta1/trafficpermissions_webhook.go b/control-plane/api/auth/v2beta1/trafficpermissions_webhook.go similarity index 97% rename from control-plane/api/v2beta1/trafficpermissions_webhook.go rename to control-plane/api/auth/v2beta1/trafficpermissions_webhook.go index a3371468d2..c7d8d0189d 100644 --- a/control-plane/api/v2beta1/trafficpermissions_webhook.go +++ b/control-plane/api/auth/v2beta1/trafficpermissions_webhook.go @@ -54,7 +54,7 @@ func (v *TrafficPermissionsWebhook) List(ctx context.Context) ([]common.MeshConf } var entries []common.MeshConfig for _, item := range resourceList.Items { - entries = append(entries, common.MeshConfig(&item)) + entries = append(entries, common.MeshConfig(item)) } return entries, nil } diff --git a/control-plane/api/auth/v2beta1/zz_generated.deepcopy.go b/control-plane/api/auth/v2beta1/zz_generated.deepcopy.go new file mode 100644 index 0000000000..3aa46646cb --- /dev/null +++ b/control-plane/api/auth/v2beta1/zz_generated.deepcopy.go @@ -0,0 +1,136 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +// Code generated by controller-gen. DO NOT EDIT. + +package v2beta1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Condition) DeepCopyInto(out *Condition) { + *out = *in + in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Condition. +func (in *Condition) DeepCopy() *Condition { + if in == nil { + return nil + } + out := new(Condition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in Conditions) DeepCopyInto(out *Conditions) { + { + in := &in + *out = make(Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Conditions. +func (in Conditions) DeepCopy() Conditions { + if in == nil { + return nil + } + out := new(Conditions) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Status) DeepCopyInto(out *Status) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.LastSyncedTime != nil { + in, out := &in.LastSyncedTime, &out.LastSyncedTime + *out = (*in).DeepCopy() + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Status. +func (in *Status) DeepCopy() *Status { + if in == nil { + return nil + } + out := new(Status) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TrafficPermissions) DeepCopyInto(out *TrafficPermissions) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficPermissions. +func (in *TrafficPermissions) DeepCopy() *TrafficPermissions { + if in == nil { + return nil + } + out := new(TrafficPermissions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TrafficPermissions) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TrafficPermissionsList) DeepCopyInto(out *TrafficPermissionsList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]*TrafficPermissions, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(TrafficPermissions) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficPermissionsList. +func (in *TrafficPermissionsList) DeepCopy() *TrafficPermissionsList { + if in == nil { + return nil + } + out := new(TrafficPermissionsList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TrafficPermissionsList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} diff --git a/control-plane/api/common/common.go b/control-plane/api/common/common.go index 8d05e8e46b..2cf32fdc0b 100644 --- a/control-plane/api/common/common.go +++ b/control-plane/api/common/common.go @@ -25,6 +25,10 @@ const ( // V2 config entries. TrafficPermissions string = "trafficpermissions" + GRPCRoute string = "grpcroute" + HTTPRoute string = "httproute" + TCPRoute string = "tcproute" + ProxyConfiguration string = "proxyconfiguration" Global string = "global" Mesh string = "mesh" @@ -37,6 +41,10 @@ const ( MigrateEntryKey string = "consul.hashicorp.com/migrate-entry" MigrateEntryTrue string = "true" SourceValue string = "kubernetes" + + DefaultPartitionName = "default" + DefaultNamespaceName = "default" + DefaultPeerName = "local" ) // ConsulTenancyConfig manages settings related to Consul namespaces and partitions. diff --git a/control-plane/api/mesh/v2beta1/grpc_route_types.go b/control-plane/api/mesh/v2beta1/grpc_route_types.go new file mode 100644 index 0000000000..44a8949e1d --- /dev/null +++ b/control-plane/api/mesh/v2beta1/grpc_route_types.go @@ -0,0 +1,327 @@ +// // Copyright (c) HashiCorp, Inc. +// // SPDX-License-Identifier: MPL-2.0 +package v2beta1 + +import ( + "fmt" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v2beta1" + "github.com/hashicorp/consul/proto-public/pbresource" + "google.golang.org/protobuf/testing/protocmp" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + + "github.com/hashicorp/consul-k8s/control-plane/api/common" + inject "github.com/hashicorp/consul-k8s/control-plane/connect-inject/common" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" +) + +const ( + grpcRouteKubeKind = "grpcroute" +) + +func init() { + MeshSchemeBuilder.Register(&GRPCRoute{}, &GRPCRouteList{}) +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status + +// GRPCRoute is the Schema for the GRPC Route API +// +kubebuilder:printcolumn:name="Synced",type="string",JSONPath=".status.conditions[?(@.type==\"Synced\")].status",description="The sync status of the resource with Consul" +// +kubebuilder:printcolumn:name="Last Synced",type="date",JSONPath=".status.lastSyncedTime",description="The last successful synced time of the resource with Consul" +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="The age of the resource" +// +kubebuilder:resource:shortName="grpc-route" +type GRPCRoute struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec pbmesh.GRPCRoute `json:"spec,omitempty"` + Status `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// GRPCRouteList contains a list of GRPCRoute. +type GRPCRouteList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []*GRPCRoute `json:"items"` +} + +func (in *GRPCRoute) ResourceID(namespace, partition string) *pbresource.ID { + return &pbresource.ID{ + Name: in.Name, + Type: pbmesh.GRPCRouteType, + Tenancy: &pbresource.Tenancy{ + Partition: partition, + Namespace: namespace, + + // Because we are explicitly defining NS/partition, this will not default and must be explicit. + // At a future point, this will move out of the Tenancy block. + PeerName: constants.DefaultConsulPeer, + }, + } +} + +func (in *GRPCRoute) Resource(namespace, partition string) *pbresource.Resource { + return &pbresource.Resource{ + Id: in.ResourceID(namespace, partition), + Data: inject.ToProtoAny(&in.Spec), + Metadata: meshConfigMeta(), + } +} + +func (in *GRPCRoute) AddFinalizer(f string) { + in.ObjectMeta.Finalizers = append(in.Finalizers(), f) +} + +func (in *GRPCRoute) RemoveFinalizer(f string) { + var newFinalizers []string + for _, oldF := range in.Finalizers() { + if oldF != f { + newFinalizers = append(newFinalizers, oldF) + } + } + in.ObjectMeta.Finalizers = newFinalizers +} + +func (in *GRPCRoute) Finalizers() []string { + return in.ObjectMeta.Finalizers +} + +func (in *GRPCRoute) MatchesConsul(candidate *pbresource.Resource, namespace, partition string) bool { + return cmp.Equal( + in.Resource(namespace, partition), + candidate, + protocmp.IgnoreFields(&pbresource.Resource{}, "status", "generation", "version"), + protocmp.IgnoreFields(&pbresource.ID{}, "uid"), + protocmp.Transform(), + cmpopts.SortSlices(func(a, b any) bool { return fmt.Sprintf("%v", a) < fmt.Sprintf("%v", b) }), + ) +} + +func (in *GRPCRoute) KubeKind() string { + return grpcRouteKubeKind +} + +func (in *GRPCRoute) KubernetesName() string { + return in.ObjectMeta.Name +} + +func (in *GRPCRoute) SetSyncedCondition(status corev1.ConditionStatus, reason, message string) { + in.Status.Conditions = Conditions{ + { + Type: ConditionSynced, + Status: status, + LastTransitionTime: metav1.Now(), + Reason: reason, + Message: message, + }, + } +} + +func (in *GRPCRoute) SetLastSyncedTime(time *metav1.Time) { + in.Status.LastSyncedTime = time +} + +func (in *GRPCRoute) SyncedCondition() (status corev1.ConditionStatus, reason, message string) { + cond := in.Status.GetCondition(ConditionSynced) + if cond == nil { + return corev1.ConditionUnknown, "", "" + } + return cond.Status, cond.Reason, cond.Message +} + +func (in *GRPCRoute) SyncedConditionStatus() corev1.ConditionStatus { + condition := in.Status.GetCondition(ConditionSynced) + if condition == nil { + return corev1.ConditionUnknown + } + return condition.Status +} + +func (in *GRPCRoute) Validate(tenancy common.ConsulTenancyConfig) error { + var errs field.ErrorList + var route pbmesh.GRPCRoute + path := field.NewPath("spec") + + res := in.Resource(tenancy.ConsulDestinationNamespace, tenancy.ConsulPartition) + + if err := res.Data.UnmarshalTo(&route); err != nil { + return fmt.Errorf("error parsing resource data as type %q: %s", &route, err) + } + + if len(route.ParentRefs) == 0 { + errs = append(errs, field.Required(path.Child("parentRefs"), "cannot be empty")) + } + + if len(route.Hostnames) > 0 { + errs = append(errs, field.Invalid(path.Child("hostnames"), route.Hostnames, "should not populate hostnames")) + } + + for i, rule := range route.Rules { + rulePath := path.Child("rules").Index(i) + for j, match := range rule.Matches { + ruleMatchPath := rulePath.Child("matches").Index(j) + if match.Method != nil { + switch match.Method.Type { + case pbmesh.GRPCMethodMatchType_GRPC_METHOD_MATCH_TYPE_UNSPECIFIED: + errs = append(errs, field.Invalid(ruleMatchPath.Child("method").Child("type"), match.Method.Type, "missing required field")) + case pbmesh.GRPCMethodMatchType_GRPC_METHOD_MATCH_TYPE_EXACT: + case pbmesh.GRPCMethodMatchType_GRPC_METHOD_MATCH_TYPE_REGEX: + default: + errs = append(errs, field.Invalid(ruleMatchPath.Child("method").Child("type"), match.Method.Type, fmt.Sprintf("not a supported enum value: %v", match.Method.Type))) + } + if match.Method.Service == "" && match.Method.Method == "" { + errs = append(errs, field.Invalid(ruleMatchPath.Child("method").Child("service"), match.Method.Service, "at least one of \"service\" or \"method\" must be set")) + } + } + + for k, header := range match.Headers { + ruleHeaderPath := ruleMatchPath.Child("headers").Index(k) + if err := validateHeaderMatchType(header.Type); err != nil { + errs = append(errs, field.Invalid(ruleHeaderPath.Child("type"), header.Type, err.Error())) + } + + if header.Name == "" { + errs = append(errs, field.Required(ruleHeaderPath.Child("name"), "missing required field")) + } + } + } + + for j, filter := range rule.Filters { + set := 0 + if filter.RequestHeaderModifier != nil { + set++ + } + if filter.ResponseHeaderModifier != nil { + set++ + } + if filter.UrlRewrite != nil { + set++ + if filter.UrlRewrite.PathPrefix == "" { + errs = append(errs, field.Required(rulePath.Child("filters").Index(j).Child("urlRewrite").Child("pathPrefix"), "field should not be empty if enclosing section is set")) + } + } + if set != 1 { + errs = append(errs, field.Invalid(rulePath.Child("filters").Index(j), filter, "exactly one of request_header_modifier, response_header_modifier, or url_rewrite is required")) + } + } + + if len(rule.BackendRefs) == 0 { + errs = append(errs, field.Required(rulePath.Child("backendRefs"), "missing required field")) + } + for j, hbref := range rule.BackendRefs { + ruleBackendRefsPath := rulePath.Child("backendRefs").Index(j) + if hbref.BackendRef == nil { + errs = append(errs, field.Required(ruleBackendRefsPath.Child("backendRef"), "missing required field")) + continue + } + + if hbref.BackendRef.Datacenter != "" { + errs = append(errs, field.Invalid(ruleBackendRefsPath.Child("backendRef").Child("datacenter"), hbref.BackendRef.Datacenter, "datacenter is not yet supported on backend refs")) + } + + if len(hbref.Filters) > 0 { + errs = append(errs, field.Invalid(ruleBackendRefsPath.Child("filters"), hbref.Filters, "filters are not supported at this level yet")) + } + } + + if rule.Timeouts != nil { + errs = append(errs, validateHTTPTimeouts(rule.Timeouts, rulePath.Child("timeouts"))...) + } + if rule.Retries != nil { + errs = append(errs, validateHTTPRetries(rule.Retries, rulePath.Child("retries"))...) + } + } + + if len(errs) > 0 { + return apierrors.NewInvalid( + schema.GroupKind{Group: MeshGroup, Kind: common.GRPCRoute}, + in.KubernetesName(), errs) + } + return nil +} + +func validateHeaderMatchType(typ pbmesh.HeaderMatchType) error { + switch typ { + case pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_UNSPECIFIED: + return fmt.Errorf("missing required field") + case pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_EXACT: + case pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_REGEX: + case pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_PRESENT: + case pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_PREFIX: + case pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_SUFFIX: + default: + return fmt.Errorf("not a supported enum value: %v", typ) + } + return nil +} + +func validateHTTPTimeouts(timeouts *pbmesh.HTTPRouteTimeouts, path *field.Path) field.ErrorList { + if timeouts == nil { + return nil + } + + var errs field.ErrorList + + if timeouts.Request != nil { + val := timeouts.Request.AsDuration() + if val < 0 { + errs = append(errs, field.Invalid(path.Child("request"), val, "timeout cannot be negative")) + } + } + if timeouts.Idle != nil { + val := timeouts.Idle.AsDuration() + if val < 0 { + errs = append(errs, field.Invalid(path.Child("idle"), val, "timeout cannot be negative")) + } + } + + return errs +} + +func validateHTTPRetries(retries *pbmesh.HTTPRouteRetries, path *field.Path) field.ErrorList { + if retries == nil { + return nil + } + + var errs field.ErrorList + + for i, condition := range retries.OnConditions { + if !isValidRetryCondition(condition) { + errs = append(errs, field.Invalid(path.Child("onConditions").Index(i), condition, "not a valid retry condition")) + } + } + + return errs +} + +func isValidRetryCondition(retryOn string) bool { + switch retryOn { + case "5xx", + "gateway-error", + "reset", + "connect-failure", + "envoy-ratelimited", + "retriable-4xx", + "refused-stream", + "cancelled", + "deadline-exceeded", + "internal", + "resource-exhausted", + "unavailable": + return true + default: + return false + } +} + +// DefaultNamespaceFields is required as part of the common.MeshConfig interface. +func (in *GRPCRoute) DefaultNamespaceFields(tenancy common.ConsulTenancyConfig) {} diff --git a/control-plane/api/mesh/v2beta1/grpc_route_types_test.go b/control-plane/api/mesh/v2beta1/grpc_route_types_test.go new file mode 100644 index 0000000000..69695284d0 --- /dev/null +++ b/control-plane/api/mesh/v2beta1/grpc_route_types_test.go @@ -0,0 +1,1201 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package v2beta1 + +import ( + "testing" + "time" + + "github.com/google/go-cmp/cmp" + pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v2beta1" + "github.com/hashicorp/consul/proto-public/pbresource" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/timestamppb" + "google.golang.org/protobuf/types/known/wrapperspb" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/hashicorp/consul-k8s/control-plane/api/common" + inject "github.com/hashicorp/consul-k8s/control-plane/connect-inject/common" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" + "github.com/hashicorp/consul-k8s/control-plane/helper/test" +) + +func TestGRPCRoute_MatchesConsul(t *testing.T) { + cases := map[string]struct { + OurConsulNamespace string + OurConsulPartition string + OurData *GRPCRoute + + TheirName string + TheirConsulNamespace string + TheirConsulPartition string + TheirData *pbmesh.GRPCRoute + ResourceOverride *pbresource.Resource // Used to test that an empty resource of another type will not match + + Matches bool + }{ + "empty fields matches": { + OurConsulNamespace: constants.DefaultConsulNS, + OurConsulPartition: constants.DefaultConsulPartition, + OurData: &GRPCRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + }, + Spec: pbmesh.GRPCRoute{}, + }, + TheirName: "name", + TheirConsulNamespace: constants.DefaultConsulNS, + TheirConsulPartition: constants.DefaultConsulPartition, + TheirData: &pbmesh.GRPCRoute{}, + Matches: true, + }, + "hostnames are compared": { + OurConsulNamespace: "consul-ns", + OurConsulPartition: "consul-partition", + OurData: &GRPCRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.GRPCRoute{ + Hostnames: []string{ + "a-hostname", "another-hostname", + }, + }, + }, + TheirName: "foo", + TheirConsulNamespace: "consul-ns", + TheirConsulPartition: "consul-partition", + TheirData: &pbmesh.GRPCRoute{ + Hostnames: []string{ + "not-a-hostname", "another-hostname", + }, + }, + Matches: false, + }, + "all fields set matches": { + OurConsulNamespace: "consul-ns", + OurConsulPartition: "consul-partition", + OurData: &GRPCRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.GRPCRoute{ + Rules: []*pbmesh.GRPCRouteRule{ + { + Matches: []*pbmesh.GRPCRouteMatch{ + { + Method: &pbmesh.GRPCMethodMatch{ + Type: pbmesh.GRPCMethodMatchType_GRPC_METHOD_MATCH_TYPE_EXACT, + Service: "test-service", + Method: "GET", + }, + Headers: []*pbmesh.GRPCHeaderMatch{ + { + Type: pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_PREFIX, + Name: "test-header", + Value: "header-value", + }, + }, + }, + }, + Filters: []*pbmesh.GRPCRouteFilter{ + { + RequestHeaderModifier: &pbmesh.HTTPHeaderFilter{ + Set: []*pbmesh.HTTPHeader{ + { + Name: "set-header", + Value: "a-header-value", + }, + }, + Add: []*pbmesh.HTTPHeader{ + { + Name: "added-header", + Value: "another-header-value", + }, + }, + Remove: []string{ + "remove-header", + }, + }, + ResponseHeaderModifier: &pbmesh.HTTPHeaderFilter{ + Set: []*pbmesh.HTTPHeader{ + { + Name: "set-header", + Value: "a-header-value", + }, + }, + Add: []*pbmesh.HTTPHeader{ + { + Name: "added-header", + Value: "another-header-value", + }, + }, + Remove: []string{ + "remove-header", + }, + }, + UrlRewrite: &pbmesh.HTTPURLRewriteFilter{ + PathPrefix: "a-path-prefix", + }, + }, + }, + Timeouts: &pbmesh.HTTPRouteTimeouts{ + Request: &durationpb.Duration{ + Seconds: 10, + Nanos: 5, + }, + Idle: &durationpb.Duration{ + Seconds: 5, + Nanos: 10, + }, + }, + Retries: &pbmesh.HTTPRouteRetries{ + Number: &wrapperspb.UInt32Value{ + Value: 1, + }, + OnConnectFailure: false, + OnConditions: []string{ + "condition-one", "condition-two", + }, + OnStatusCodes: []uint32{ + 200, 201, 202, + }, + }, + }, + }, + }, + }, + TheirName: "foo", + TheirConsulNamespace: "consul-ns", + TheirConsulPartition: "consul-partition", + TheirData: &pbmesh.GRPCRoute{ + Rules: []*pbmesh.GRPCRouteRule{ + { + Matches: []*pbmesh.GRPCRouteMatch{ + { + Method: &pbmesh.GRPCMethodMatch{ + Type: pbmesh.GRPCMethodMatchType_GRPC_METHOD_MATCH_TYPE_EXACT, + Service: "test-service", + Method: "GET", + }, + Headers: []*pbmesh.GRPCHeaderMatch{ + { + Type: pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_PREFIX, + Name: "test-header", + Value: "header-value", + }, + }, + }, + }, + Filters: []*pbmesh.GRPCRouteFilter{ + { + RequestHeaderModifier: &pbmesh.HTTPHeaderFilter{ + Set: []*pbmesh.HTTPHeader{ + { + Name: "set-header", + Value: "a-header-value", + }, + }, + Add: []*pbmesh.HTTPHeader{ + { + Name: "added-header", + Value: "another-header-value", + }, + }, + Remove: []string{ + "remove-header", + }, + }, + ResponseHeaderModifier: &pbmesh.HTTPHeaderFilter{ + Set: []*pbmesh.HTTPHeader{ + { + Name: "set-header", + Value: "a-header-value", + }, + }, + Add: []*pbmesh.HTTPHeader{ + { + Name: "added-header", + Value: "another-header-value", + }, + }, + Remove: []string{ + "remove-header", + }, + }, + UrlRewrite: &pbmesh.HTTPURLRewriteFilter{ + PathPrefix: "a-path-prefix", + }, + }, + }, + Timeouts: &pbmesh.HTTPRouteTimeouts{ + Request: &durationpb.Duration{ + Seconds: 10, + Nanos: 5, + }, + Idle: &durationpb.Duration{ + Seconds: 5, + Nanos: 10, + }, + }, + Retries: &pbmesh.HTTPRouteRetries{ + Number: &wrapperspb.UInt32Value{ + Value: 1, + }, + OnConnectFailure: false, + OnConditions: []string{ + "condition-one", "condition-two", + }, + OnStatusCodes: []uint32{ + 200, 201, 202, + }, + }, + }, + }, + }, + Matches: true, + }, + "different types does not match": { + OurConsulNamespace: constants.DefaultConsulNS, + OurConsulPartition: constants.DefaultConsulPartition, + OurData: &GRPCRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + }, + Spec: pbmesh.GRPCRoute{}, + }, + ResourceOverride: &pbresource.Resource{ + Id: &pbresource.ID{ + Name: "name", + Type: pbmesh.ProxyConfigurationType, + Tenancy: &pbresource.Tenancy{ + Partition: constants.DefaultConsulNS, + Namespace: constants.DefaultConsulPartition, + + // Because we are explicitly defining NS/partition, this will not default and must be explicit. + // At a future point, this will move out of the Tenancy block. + PeerName: constants.DefaultConsulPeer, + }, + }, + Data: inject.ToProtoAny(&pbmesh.ProxyConfiguration{}), + Metadata: meshConfigMeta(), + }, + Matches: false, + }, + } + for name, c := range cases { + t.Run(name, func(t *testing.T) { + consulResource := c.ResourceOverride + if c.TheirName != "" { + consulResource = constructGRPCRouteResource(c.TheirData, c.TheirName, c.TheirConsulNamespace, c.TheirConsulPartition) + } + require.Equal(t, c.Matches, c.OurData.MatchesConsul(consulResource, c.OurConsulNamespace, c.OurConsulPartition)) + }) + } +} + +// TestGRPCRoute_Resource also includes test to verify ResourceID(). +func TestGRPCRoute_Resource(t *testing.T) { + cases := map[string]struct { + Ours *GRPCRoute + ConsulNamespace string + ConsulPartition string + ExpectedName string + ExpectedData *pbmesh.GRPCRoute + }{ + "empty fields": { + Ours: &GRPCRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: pbmesh.GRPCRoute{}, + }, + ConsulNamespace: constants.DefaultConsulNS, + ConsulPartition: constants.DefaultConsulPartition, + ExpectedName: "foo", + ExpectedData: &pbmesh.GRPCRoute{}, + }, + "every field set": { + Ours: &GRPCRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.GRPCRoute{ + Rules: []*pbmesh.GRPCRouteRule{ + { + Matches: []*pbmesh.GRPCRouteMatch{ + { + Method: &pbmesh.GRPCMethodMatch{ + Type: pbmesh.GRPCMethodMatchType_GRPC_METHOD_MATCH_TYPE_EXACT, + Service: "test-service", + Method: "GET", + }, + Headers: []*pbmesh.GRPCHeaderMatch{ + { + Type: pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_PREFIX, + Name: "test-header", + Value: "header-value", + }, + }, + }, + }, + Filters: []*pbmesh.GRPCRouteFilter{ + { + RequestHeaderModifier: &pbmesh.HTTPHeaderFilter{ + Set: []*pbmesh.HTTPHeader{ + { + Name: "set-header", + Value: "a-header-value", + }, + }, + Add: []*pbmesh.HTTPHeader{ + { + Name: "added-header", + Value: "another-header-value", + }, + }, + Remove: []string{ + "remove-header", + }, + }, + ResponseHeaderModifier: &pbmesh.HTTPHeaderFilter{ + Set: []*pbmesh.HTTPHeader{ + { + Name: "set-header", + Value: "a-header-value", + }, + }, + Add: []*pbmesh.HTTPHeader{ + { + Name: "added-header", + Value: "another-header-value", + }, + }, + Remove: []string{ + "remove-header", + }, + }, + UrlRewrite: &pbmesh.HTTPURLRewriteFilter{ + PathPrefix: "a-path-prefix", + }, + }, + }, + Timeouts: &pbmesh.HTTPRouteTimeouts{ + Request: &durationpb.Duration{ + Seconds: 10, + Nanos: 5, + }, + Idle: &durationpb.Duration{ + Seconds: 5, + Nanos: 10, + }, + }, + Retries: &pbmesh.HTTPRouteRetries{ + Number: &wrapperspb.UInt32Value{ + Value: 1, + }, + OnConnectFailure: false, + OnConditions: []string{ + "condition-one", "condition-two", + }, + OnStatusCodes: []uint32{ + 200, 201, 202, + }, + }, + }, + }, + }, + }, + ConsulNamespace: "not-default-namespace", + ConsulPartition: "not-default-partition", + ExpectedName: "foo", + ExpectedData: &pbmesh.GRPCRoute{ + Rules: []*pbmesh.GRPCRouteRule{ + { + Matches: []*pbmesh.GRPCRouteMatch{ + { + Method: &pbmesh.GRPCMethodMatch{ + Type: pbmesh.GRPCMethodMatchType_GRPC_METHOD_MATCH_TYPE_EXACT, + Service: "test-service", + Method: "GET", + }, + Headers: []*pbmesh.GRPCHeaderMatch{ + { + Type: pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_PREFIX, + Name: "test-header", + Value: "header-value", + }, + }, + }, + }, + Filters: []*pbmesh.GRPCRouteFilter{ + { + RequestHeaderModifier: &pbmesh.HTTPHeaderFilter{ + Set: []*pbmesh.HTTPHeader{ + { + Name: "set-header", + Value: "a-header-value", + }, + }, + Add: []*pbmesh.HTTPHeader{ + { + Name: "added-header", + Value: "another-header-value", + }, + }, + Remove: []string{ + "remove-header", + }, + }, + ResponseHeaderModifier: &pbmesh.HTTPHeaderFilter{ + Set: []*pbmesh.HTTPHeader{ + { + Name: "set-header", + Value: "a-header-value", + }, + }, + Add: []*pbmesh.HTTPHeader{ + { + Name: "added-header", + Value: "another-header-value", + }, + }, + Remove: []string{ + "remove-header", + }, + }, + UrlRewrite: &pbmesh.HTTPURLRewriteFilter{ + PathPrefix: "a-path-prefix", + }, + }, + }, + Timeouts: &pbmesh.HTTPRouteTimeouts{ + Request: &durationpb.Duration{ + Seconds: 10, + Nanos: 5, + }, + Idle: &durationpb.Duration{ + Seconds: 5, + Nanos: 10, + }, + }, + Retries: &pbmesh.HTTPRouteRetries{ + Number: &wrapperspb.UInt32Value{ + Value: 1, + }, + OnConnectFailure: false, + OnConditions: []string{ + "condition-one", "condition-two", + }, + OnStatusCodes: []uint32{ + 200, 201, 202, + }, + }, + }, + }, + }, + }, + } + for name, c := range cases { + t.Run(name, func(t *testing.T) { + actual := c.Ours.Resource(c.ConsulNamespace, c.ConsulPartition) + expected := constructGRPCRouteResource(c.ExpectedData, c.ExpectedName, c.ConsulNamespace, c.ConsulPartition) + + opts := append([]cmp.Option{ + protocmp.IgnoreFields(&pbresource.Resource{}, "status", "generation", "version"), + protocmp.IgnoreFields(&pbresource.ID{}, "uid"), + }, test.CmpProtoIgnoreOrder()...) + diff := cmp.Diff(expected, actual, opts...) + require.Equal(t, "", diff, "GRPCRoute do not match") + }) + } +} + +func TestGRPCRoute_SetSyncedCondition(t *testing.T) { + trafficPermissions := &GRPCRoute{} + trafficPermissions.SetSyncedCondition(corev1.ConditionTrue, "reason", "message") + + require.Equal(t, corev1.ConditionTrue, trafficPermissions.Status.Conditions[0].Status) + require.Equal(t, "reason", trafficPermissions.Status.Conditions[0].Reason) + require.Equal(t, "message", trafficPermissions.Status.Conditions[0].Message) + now := metav1.Now() + require.True(t, trafficPermissions.Status.Conditions[0].LastTransitionTime.Before(&now)) +} + +func TestGRPCRoute_SetLastSyncedTime(t *testing.T) { + trafficPermissions := &GRPCRoute{} + syncedTime := metav1.NewTime(time.Now()) + trafficPermissions.SetLastSyncedTime(&syncedTime) + + require.Equal(t, &syncedTime, trafficPermissions.Status.LastSyncedTime) +} + +func TestGRPCRoute_GetSyncedConditionStatus(t *testing.T) { + cases := []corev1.ConditionStatus{ + corev1.ConditionUnknown, + corev1.ConditionFalse, + corev1.ConditionTrue, + } + for _, status := range cases { + t.Run(string(status), func(t *testing.T) { + trafficPermissions := &GRPCRoute{ + Status: Status{ + Conditions: []Condition{{ + Type: ConditionSynced, + Status: status, + }}, + }, + } + + require.Equal(t, status, trafficPermissions.SyncedConditionStatus()) + }) + } +} + +func TestGRPCRoute_GetConditionWhenStatusNil(t *testing.T) { + require.Nil(t, (&GRPCRoute{}).GetCondition(ConditionSynced)) +} + +func TestGRPCRoute_SyncedConditionStatusWhenStatusNil(t *testing.T) { + require.Equal(t, corev1.ConditionUnknown, (&GRPCRoute{}).SyncedConditionStatus()) +} + +func TestGRPCRoute_SyncedConditionWhenStatusNil(t *testing.T) { + status, reason, message := (&GRPCRoute{}).SyncedCondition() + require.Equal(t, corev1.ConditionUnknown, status) + require.Equal(t, "", reason) + require.Equal(t, "", message) +} + +func TestGRPCRoute_KubeKind(t *testing.T) { + require.Equal(t, "grpcroute", (&GRPCRoute{}).KubeKind()) +} + +func TestGRPCRoute_KubernetesName(t *testing.T) { + require.Equal(t, "test", (&GRPCRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "bar", + }, + Spec: pbmesh.GRPCRoute{}, + }).KubernetesName()) +} + +func TestGRPCRoute_ObjectMeta(t *testing.T) { + meta := metav1.ObjectMeta{ + Name: "name", + Namespace: "namespace", + } + trafficPermissions := &GRPCRoute{ + ObjectMeta: meta, + } + require.Equal(t, &meta, trafficPermissions.GetObjectMeta()) +} + +// Test defaulting behavior when namespaces are enabled as well as disabled. +// TODO: add when implemented +//func TestGRPCRoute_DefaultNamespaceFields(t *testing.T) + +func TestGRPCRoute_Validate(t *testing.T) { + cases := []struct { + name string + input *GRPCRoute + expectedErrMsgs []string + }{ + { + name: "kitchen sink OK", + input: &GRPCRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.GRPCRoute{ + ParentRefs: []*pbmesh.ParentReference{ + { + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "some-partition", + Namespace: "some-namespace", + }, + Name: "reference", + Section: "some-section", + }, + Port: "20020", + }, + }, + Hostnames: []string{}, + Rules: []*pbmesh.GRPCRouteRule{ + { + Matches: []*pbmesh.GRPCRouteMatch{ + { + Method: &pbmesh.GRPCMethodMatch{ + Type: pbmesh.GRPCMethodMatchType_GRPC_METHOD_MATCH_TYPE_EXACT, + Service: "test-service", + Method: "GET", + }, + Headers: []*pbmesh.GRPCHeaderMatch{ + { + Type: pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_PREFIX, + Name: "test-header", + Value: "header-value", + }, + }, + }, + }, + Filters: []*pbmesh.GRPCRouteFilter{ + { + UrlRewrite: &pbmesh.HTTPURLRewriteFilter{ + PathPrefix: "a-path-prefix", + }, + }, + }, + Timeouts: &pbmesh.HTTPRouteTimeouts{ + Request: &durationpb.Duration{ + Seconds: 10, + Nanos: 5, + }, + Idle: &durationpb.Duration{ + Seconds: 5, + Nanos: 10, + }, + }, + Retries: &pbmesh.HTTPRouteRetries{ + Number: &wrapperspb.UInt32Value{ + Value: 1, + }, + OnConnectFailure: false, + OnConditions: []string{ + "5xx", "resource-exhausted", + }, + OnStatusCodes: []uint32{ + 200, 201, 202, + }, + }, + BackendRefs: []*pbmesh.GRPCBackendRef{ + { + BackendRef: &pbmesh.BackendReference{ + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "some-partition", + Namespace: "some-namespace", + }, + Name: "reference", + Section: "some-section", + }, + Port: "21000", + }, + Weight: 50, + }, + }, + }, + }, + }, + }, + expectedErrMsgs: nil, + }, + { + name: "empty parentRefs", + input: &GRPCRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.GRPCRoute{ + ParentRefs: []*pbmesh.ParentReference{}, + }, + }, + expectedErrMsgs: []string{ + `spec.parentRefs: Required value: cannot be empty`, + }, + }, + { + name: "populated hostnames", + input: &GRPCRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.GRPCRoute{ + ParentRefs: []*pbmesh.ParentReference{ + { + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "some-partition", + Namespace: "some-namespace", + }, + Name: "reference", + Section: "some-section", + }, + Port: "20020", + }, + }, + Hostnames: []string{"a-hostname"}, + }, + }, + expectedErrMsgs: []string{ + `spec.hostnames: Invalid value: []string{"a-hostname"}: should not populate hostnames`, + }, + }, + { + name: "rules.matches.method", + input: &GRPCRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.GRPCRoute{ + ParentRefs: []*pbmesh.ParentReference{ + { + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "some-partition", + Namespace: "some-namespace", + }, + Name: "reference", + Section: "some-section", + }, + Port: "20020", + }, + }, + Hostnames: []string{}, + Rules: []*pbmesh.GRPCRouteRule{ + { + Matches: []*pbmesh.GRPCRouteMatch{ + { + Method: &pbmesh.GRPCMethodMatch{ + Type: pbmesh.GRPCMethodMatchType_GRPC_METHOD_MATCH_TYPE_UNSPECIFIED, + Service: "test-service", + Method: "GET", + }, + }, { + Method: &pbmesh.GRPCMethodMatch{ + Service: "test-service", + Method: "GET", + }, + }, { + Method: &pbmesh.GRPCMethodMatch{ + Type: pbmesh.GRPCMethodMatchType_GRPC_METHOD_MATCH_TYPE_EXACT, + }, + }, + }, + BackendRefs: []*pbmesh.GRPCBackendRef{ + { + BackendRef: &pbmesh.BackendReference{ + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "some-partition", + Namespace: "some-namespace", + }, + Name: "reference", + Section: "some-section", + }, + Port: "21000", + }, + Weight: 50, + }, + }, + }, + }, + }, + }, + expectedErrMsgs: []string{ + `spec.rules[0].matches[0].method.type: Invalid value: GRPC_METHOD_MATCH_TYPE_UNSPECIFIED: missing required field`, + `spec.rules[0].matches[1].method.type: Invalid value: GRPC_METHOD_MATCH_TYPE_UNSPECIFIED: missing required field`, + `spec.rules[0].matches[2].method.service: Invalid value: "": at least one of "service" or "method" must be set`, + }, + }, + { + name: "rules.matches.headers", + input: &GRPCRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.GRPCRoute{ + ParentRefs: []*pbmesh.ParentReference{ + { + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "some-partition", + Namespace: "some-namespace", + }, + Name: "reference", + Section: "some-section", + }, + Port: "20020", + }, + }, + Hostnames: []string{}, + Rules: []*pbmesh.GRPCRouteRule{ + { + Matches: []*pbmesh.GRPCRouteMatch{ + { + Headers: []*pbmesh.GRPCHeaderMatch{ + { + Type: pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_UNSPECIFIED, + Name: "test-header", + Value: "header-value", + }, + { + Name: "test-header", + Value: "header-value", + }, + { + Type: pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_PREFIX, + Value: "header-value", + }, + }, + }, + }, + BackendRefs: []*pbmesh.GRPCBackendRef{ + { + BackendRef: &pbmesh.BackendReference{ + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "some-partition", + Namespace: "some-namespace", + }, + Name: "reference", + Section: "some-section", + }, + Port: "21000", + }, + Weight: 50, + }, + }, + }, + }, + }, + }, + expectedErrMsgs: []string{ + `spec.rules[0].matches[0].headers[0].type: Invalid value: HEADER_MATCH_TYPE_UNSPECIFIED: missing required field`, + `spec.rules[0].matches[0].headers[1].type: Invalid value: HEADER_MATCH_TYPE_UNSPECIFIED: missing required field`, + `spec.rules[0].matches[0].headers[2].name: Required value: missing required field`, + }, + }, + { + name: "rules.filters", + input: &GRPCRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.GRPCRoute{ + ParentRefs: []*pbmesh.ParentReference{ + { + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "some-partition", + Namespace: "some-namespace", + }, + Name: "reference", + Section: "some-section", + }, + Port: "20020", + }, + }, + Hostnames: []string{}, + Rules: []*pbmesh.GRPCRouteRule{ + { + Filters: []*pbmesh.GRPCRouteFilter{ + { + RequestHeaderModifier: &pbmesh.HTTPHeaderFilter{}, + ResponseHeaderModifier: &pbmesh.HTTPHeaderFilter{}, + UrlRewrite: &pbmesh.HTTPURLRewriteFilter{ + PathPrefix: "", + }, + }, + }, + BackendRefs: []*pbmesh.GRPCBackendRef{ + { + BackendRef: &pbmesh.BackendReference{ + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "some-partition", + Namespace: "some-namespace", + }, + Name: "reference", + Section: "some-section", + }, + Port: "21000", + }, + Weight: 50, + }, + }, + }, + }, + }, + }, + expectedErrMsgs: []string{ + `spec.rules[0].filters[0].urlRewrite.pathPrefix: Required value: field should not be empty if enclosing section is set`, + `exactly one of request_header_modifier, response_header_modifier, or url_rewrite is required`, + }, + }, + { + name: "missing backendRefs", + input: &GRPCRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.GRPCRoute{ + ParentRefs: []*pbmesh.ParentReference{ + { + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "some-partition", + Namespace: "some-namespace", + }, + Name: "reference", + Section: "some-section", + }, + Port: "20020", + }, + }, + Hostnames: []string{}, + Rules: []*pbmesh.GRPCRouteRule{ + { + BackendRefs: []*pbmesh.GRPCBackendRef{}, + }, + }, + }, + }, + expectedErrMsgs: []string{ + `spec.rules[0].backendRefs: Required value: missing required field`, + }, + }, + { + name: "rules.backendRefs", + input: &GRPCRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.GRPCRoute{ + ParentRefs: []*pbmesh.ParentReference{ + { + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "some-partition", + Namespace: "some-namespace", + }, + Name: "reference", + Section: "some-section", + }, + Port: "20020", + }, + }, + Hostnames: []string{}, + Rules: []*pbmesh.GRPCRouteRule{ + { + BackendRefs: []*pbmesh.GRPCBackendRef{ + { + Weight: 50, + }, + { + BackendRef: &pbmesh.BackendReference{ + Datacenter: "wrong-datacenter", + Port: "21000", + }, + Weight: 50, + }, + { + BackendRef: &pbmesh.BackendReference{ + Port: "21000", + }, + Filters: []*pbmesh.GRPCRouteFilter{{}}, + }, + }, + }, + }, + }, + }, + expectedErrMsgs: []string{ + `spec.rules[0].backendRefs[0].backendRef: Required value: missing required field`, + `spec.rules[0].backendRefs[1].backendRef.datacenter: Invalid value: "wrong-datacenter": datacenter is not yet supported on backend refs`, + `filters are not supported at this level yet`, + }, + }, + { + name: "rules.timeout", + input: &GRPCRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.GRPCRoute{ + ParentRefs: []*pbmesh.ParentReference{ + { + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "some-partition", + Namespace: "some-namespace", + }, + Name: "reference", + Section: "some-section", + }, + Port: "20020", + }, + }, + Hostnames: []string{}, + Rules: []*pbmesh.GRPCRouteRule{ + { + Timeouts: &pbmesh.HTTPRouteTimeouts{ + Request: &durationpb.Duration{ + Seconds: -9, + Nanos: -10, + }, + Idle: &durationpb.Duration{ + Seconds: -2, + Nanos: -3, + }, + }, + BackendRefs: []*pbmesh.GRPCBackendRef{ + { + BackendRef: &pbmesh.BackendReference{ + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "some-partition", + Namespace: "some-namespace", + }, + Name: "reference", + Section: "some-section", + }, + Port: "21000", + }, + Weight: 50, + }, + }, + }, + }, + }, + }, + expectedErrMsgs: []string{ + `spec.rules[0].timeouts.request: Invalid value: -9.00000001s: timeout cannot be negative`, + `spec.rules[0].timeouts.idle: Invalid value: -2.000000003s: timeout cannot be negative`, + }, + }, + { + name: "rules.retries", + input: &GRPCRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.GRPCRoute{ + ParentRefs: []*pbmesh.ParentReference{ + { + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "some-partition", + Namespace: "some-namespace", + }, + Name: "reference", + Section: "some-section", + }, + Port: "20020", + }, + }, + Hostnames: []string{}, + Rules: []*pbmesh.GRPCRouteRule{ + { + Retries: &pbmesh.HTTPRouteRetries{ + OnConditions: []string{"invalid-condition", "another-invalid-condition", "internal"}, + }, + BackendRefs: []*pbmesh.GRPCBackendRef{ + { + BackendRef: &pbmesh.BackendReference{ + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "some-partition", + Namespace: "some-namespace", + }, + Name: "reference", + Section: "some-section", + }, + Port: "21000", + }, + Weight: 50, + }, + }, + }, + }, + }, + }, + expectedErrMsgs: []string{ + `spec.rules[0].retries.onConditions[0]: Invalid value: "invalid-condition": not a valid retry condition`, + `spec.rules[0].retries.onConditions[1]: Invalid value: "another-invalid-condition": not a valid retry condition`, + }, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + err := tc.input.Validate(common.ConsulTenancyConfig{}) + if len(tc.expectedErrMsgs) != 0 { + require.Error(t, err) + for _, s := range tc.expectedErrMsgs { + require.Contains(t, err.Error(), s) + } + } else { + require.NoError(t, err) + } + }) + } +} + +func constructGRPCRouteResource(tp *pbmesh.GRPCRoute, name, namespace, partition string) *pbresource.Resource { + data := inject.ToProtoAny(tp) + + id := &pbresource.ID{ + Name: name, + Type: pbmesh.GRPCRouteType, + Tenancy: &pbresource.Tenancy{ + Partition: partition, + Namespace: namespace, + + // Because we are explicitly defining NS/partition, this will not default and must be explicit. + // At a future point, this will move out of the Tenancy block. + PeerName: constants.DefaultConsulPeer, + }, + Uid: "ABCD", // We add this to show it does not factor into the comparison + } + + return &pbresource.Resource{ + Id: id, + Data: data, + Metadata: meshConfigMeta(), + + // We add the fields below to prove that they are not used in the Match when comparing the CRD to Consul. + Version: "123456", + Generation: "01ARZ3NDEKTSV4RRFFQ69G5FAV", + Status: map[string]*pbresource.Status{ + "knock": { + ObservedGeneration: "01ARZ3NDEKTSV4RRFFQ69G5FAV", + Conditions: make([]*pbresource.Condition, 0), + UpdatedAt: timestamppb.Now(), + }, + }, + } +} diff --git a/control-plane/api/mesh/v2beta1/grpc_route_webhook.go b/control-plane/api/mesh/v2beta1/grpc_route_webhook.go new file mode 100644 index 0000000000..15b4cbfc46 --- /dev/null +++ b/control-plane/api/mesh/v2beta1/grpc_route_webhook.go @@ -0,0 +1,65 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package v2beta1 + +import ( + "context" + "net/http" + + "github.com/go-logr/logr" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/hashicorp/consul-k8s/control-plane/api/common" +) + +// +kubebuilder:object:generate=false + +type GRPCRouteWebhook struct { + Logger logr.Logger + + // ConsulTenancyConfig contains the injector's namespace and partition configuration. + ConsulTenancyConfig common.ConsulTenancyConfig + + decoder *admission.Decoder + client.Client +} + +var _ common.MeshConfigLister = &GRPCRouteWebhook{} + +// NOTE: The path value in the below line is the path to the webhook. +// If it is updated, run code-gen, update subcommand/inject-connect/command.go +// and the consul-helm value for the path to the webhook. +// +// NOTE: The below line cannot be combined with any other comment. If it is it will break the code generation. +// +// +kubebuilder:webhook:verbs=create;update,path=/mutate-v2beta1-grpcroute,mutating=true,failurePolicy=fail,groups=auth.consul.hashicorp.com,resources=grpcroute,versions=v2beta1,name=mutate-grpcroute.auth.consul.hashicorp.com,sideEffects=None,admissionReviewVersions=v1beta1;v1 + +func (v *GRPCRouteWebhook) Handle(ctx context.Context, req admission.Request) admission.Response { + var resource GRPCRoute + err := v.decoder.Decode(req, &resource) + if err != nil { + return admission.Errored(http.StatusBadRequest, err) + } + + return common.ValidateMeshConfig(ctx, req, v.Logger, v, &resource, v.ConsulTenancyConfig) +} + +func (v *GRPCRouteWebhook) List(ctx context.Context) ([]common.MeshConfig, error) { + var resourceList GRPCRouteList + if err := v.Client.List(ctx, &resourceList); err != nil { + return nil, err + } + var entries []common.MeshConfig + for _, item := range resourceList.Items { + entries = append(entries, common.MeshConfig(item)) + } + return entries, nil +} + +func (v *GRPCRouteWebhook) InjectDecoder(d *admission.Decoder) error { + v.decoder = d + return nil +} diff --git a/control-plane/api/mesh/v2beta1/http_route_types.go b/control-plane/api/mesh/v2beta1/http_route_types.go new file mode 100644 index 0000000000..7ea1b97936 --- /dev/null +++ b/control-plane/api/mesh/v2beta1/http_route_types.go @@ -0,0 +1,309 @@ +// // Copyright (c) HashiCorp, Inc. +// // SPDX-License-Identifier: MPL-2.0 +package v2beta1 + +import ( + "fmt" + "net/http" + "strings" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v2beta1" + "github.com/hashicorp/consul/proto-public/pbresource" + "google.golang.org/protobuf/testing/protocmp" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + + "github.com/hashicorp/consul-k8s/control-plane/api/common" + inject "github.com/hashicorp/consul-k8s/control-plane/connect-inject/common" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" +) + +const ( + httpRouteKubeKind = "httproute" +) + +func init() { + MeshSchemeBuilder.Register(&HTTPRoute{}, &HTTPRouteList{}) +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status + +// HTTPRoute is the Schema for the HTTP Route API +// +kubebuilder:printcolumn:name="Synced",type="string",JSONPath=".status.conditions[?(@.type==\"Synced\")].status",description="The sync status of the resource with Consul" +// +kubebuilder:printcolumn:name="Last Synced",type="date",JSONPath=".status.lastSyncedTime",description="The last successful synced time of the resource with Consul" +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="The age of the resource" +// +kubebuilder:resource:shortName="http-route" +type HTTPRoute struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec pbmesh.HTTPRoute `json:"spec,omitempty"` + Status `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// HTTPRouteList contains a list of HTTPRoute. +type HTTPRouteList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []*HTTPRoute `json:"items"` +} + +func (in *HTTPRoute) ResourceID(namespace, partition string) *pbresource.ID { + return &pbresource.ID{ + Name: in.Name, + Type: pbmesh.HTTPRouteType, + Tenancy: &pbresource.Tenancy{ + Partition: partition, + Namespace: namespace, + + // Because we are explicitly defining NS/partition, this will not default and must be explicit. + // At a future point, this will move out of the Tenancy block. + PeerName: constants.DefaultConsulPeer, + }, + } +} + +func (in *HTTPRoute) Resource(namespace, partition string) *pbresource.Resource { + return &pbresource.Resource{ + Id: in.ResourceID(namespace, partition), + Data: inject.ToProtoAny(&in.Spec), + Metadata: meshConfigMeta(), + } +} + +func (in *HTTPRoute) AddFinalizer(f string) { + in.ObjectMeta.Finalizers = append(in.Finalizers(), f) +} + +func (in *HTTPRoute) RemoveFinalizer(f string) { + var newFinalizers []string + for _, oldF := range in.Finalizers() { + if oldF != f { + newFinalizers = append(newFinalizers, oldF) + } + } + in.ObjectMeta.Finalizers = newFinalizers +} + +func (in *HTTPRoute) Finalizers() []string { + return in.ObjectMeta.Finalizers +} + +func (in *HTTPRoute) MatchesConsul(candidate *pbresource.Resource, namespace, partition string) bool { + return cmp.Equal( + in.Resource(namespace, partition), + candidate, + protocmp.IgnoreFields(&pbresource.Resource{}, "status", "generation", "version"), + protocmp.IgnoreFields(&pbresource.ID{}, "uid"), + protocmp.Transform(), + cmpopts.SortSlices(func(a, b any) bool { return fmt.Sprintf("%v", a) < fmt.Sprintf("%v", b) }), + ) +} + +func (in *HTTPRoute) KubeKind() string { + return httpRouteKubeKind +} + +func (in *HTTPRoute) KubernetesName() string { + return in.ObjectMeta.Name +} + +func (in *HTTPRoute) SetSyncedCondition(status corev1.ConditionStatus, reason, message string) { + in.Status.Conditions = Conditions{ + { + Type: ConditionSynced, + Status: status, + LastTransitionTime: metav1.Now(), + Reason: reason, + Message: message, + }, + } +} + +func (in *HTTPRoute) SetLastSyncedTime(time *metav1.Time) { + in.Status.LastSyncedTime = time +} + +func (in *HTTPRoute) SyncedCondition() (status corev1.ConditionStatus, reason, message string) { + cond := in.Status.GetCondition(ConditionSynced) + if cond == nil { + return corev1.ConditionUnknown, "", "" + } + return cond.Status, cond.Reason, cond.Message +} + +func (in *HTTPRoute) SyncedConditionStatus() corev1.ConditionStatus { + condition := in.Status.GetCondition(ConditionSynced) + if condition == nil { + return corev1.ConditionUnknown + } + return condition.Status +} + +func (in *HTTPRoute) Validate(tenancy common.ConsulTenancyConfig) error { + var errs field.ErrorList + var route pbmesh.HTTPRoute + path := field.NewPath("spec") + + res := in.Resource(tenancy.ConsulDestinationNamespace, tenancy.ConsulPartition) + + if err := res.Data.UnmarshalTo(&route); err != nil { + return fmt.Errorf("error parsing resource data as type %q: %s", &route, err) + } + + if len(route.ParentRefs) == 0 { + errs = append(errs, field.Required(path.Child("parentRefs"), "cannot be empty")) + } + + if len(route.Hostnames) > 0 { + errs = append(errs, field.Invalid(path.Child("hostnames"), route.Hostnames, "should not populate hostnames")) + } + + for i, rule := range route.Rules { + rulePath := path.Child("rules").Index(i) + for j, match := range rule.Matches { + ruleMatchPath := rulePath.Child("matches").Index(j) + if match.Path != nil { + switch match.Path.Type { + case pbmesh.PathMatchType_PATH_MATCH_TYPE_UNSPECIFIED: + errs = append(errs, field.Invalid(ruleMatchPath.Child("path").Child("type"), pbmesh.PathMatchType_PATH_MATCH_TYPE_UNSPECIFIED, "missing required field")) + case pbmesh.PathMatchType_PATH_MATCH_TYPE_EXACT: + if !strings.HasPrefix(match.Path.Value, "/") { + errs = append(errs, field.Invalid(ruleMatchPath.Child("path").Child("value"), match.Path.Value, "exact patch value does not start with '/'")) + } + case pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX: + if !strings.HasPrefix(match.Path.Value, "/") { + errs = append(errs, field.Invalid(ruleMatchPath.Child("path").Child("value"), match.Path.Value, "prefix patch value does not start with '/'")) + } + case pbmesh.PathMatchType_PATH_MATCH_TYPE_REGEX: + if match.Path.Value == "" { + errs = append(errs, field.Required(ruleMatchPath.Child("path").Child("value"), "missing required field")) + } + default: + errs = append(errs, field.Invalid(ruleMatchPath.Child("path").Child("type"), match.Path, "not a supported enum value")) + } + } + + for k, hdr := range match.Headers { + if err := validateHeaderMatchType(hdr.Type); err != nil { + errs = append(errs, field.Invalid(ruleMatchPath.Child("headers").Index(k).Child("type"), hdr.Type, err.Error())) + } + + if hdr.Name == "" { + errs = append(errs, field.Required(ruleMatchPath.Child("headers").Index(k).Child("name"), "missing required field")) + } + } + + for k, qm := range match.QueryParams { + switch qm.Type { + case pbmesh.QueryParamMatchType_QUERY_PARAM_MATCH_TYPE_UNSPECIFIED: + errs = append(errs, field.Invalid(ruleMatchPath.Child("queryParams").Index(k).Child("type"), pbmesh.QueryParamMatchType_QUERY_PARAM_MATCH_TYPE_UNSPECIFIED, "missing required field")) + case pbmesh.QueryParamMatchType_QUERY_PARAM_MATCH_TYPE_EXACT: + case pbmesh.QueryParamMatchType_QUERY_PARAM_MATCH_TYPE_REGEX: + case pbmesh.QueryParamMatchType_QUERY_PARAM_MATCH_TYPE_PRESENT: + default: + errs = append(errs, field.Invalid(ruleMatchPath.Child("queryParams").Index(k).Child("type"), qm.Type, "not a supported enum value")) + } + + if qm.Name == "" { + errs = append(errs, field.Required(ruleMatchPath.Child("queryParams").Index(k).Child("name"), "missing required field")) + } + } + + if match.Method != "" && !isValidHTTPMethod(match.Method) { + errs = append(errs, field.Invalid(ruleMatchPath.Child("method"), match.Method, "not a valid http method")) + } + } + + var ( + hasReqMod bool + hasUrlRewrite bool + ) + for j, filter := range rule.Filters { + ruleFilterPath := path.Child("filters").Index(j) + set := 0 + if filter.RequestHeaderModifier != nil { + set++ + hasReqMod = true + } + if filter.ResponseHeaderModifier != nil { + set++ + } + if filter.UrlRewrite != nil { + set++ + hasUrlRewrite = true + if filter.UrlRewrite.PathPrefix == "" { + errs = append(errs, field.Invalid(ruleFilterPath.Child("urlRewrite").Child("pathPrefix"), filter.UrlRewrite.PathPrefix, "field should not be empty if enclosing section is set")) + } + } + if set != 1 { + errs = append(errs, field.Invalid(ruleFilterPath, filter, "exactly one of request_header_modifier, response_header_modifier, or url_rewrite is required")) + } + } + + if hasReqMod && hasUrlRewrite { + errs = append(errs, field.Invalid(rulePath.Child("filters"), rule.Filters, "exactly one of request_header_modifier or url_rewrite can be set at a time")) + } + + if len(rule.BackendRefs) == 0 { + errs = append(errs, field.Required(rulePath.Child("backendRefs"), "missing required field")) + } + for j, hbref := range rule.BackendRefs { + ruleBackendRefsPath := rulePath.Child("backendRefs").Index(j) + if hbref.BackendRef == nil { + errs = append(errs, field.Required(ruleBackendRefsPath.Child("backendRef"), "missing required field")) + continue + } + + if hbref.BackendRef.Datacenter != "" { + errs = append(errs, field.Invalid(ruleBackendRefsPath.Child("backendRef").Child("datacenter"), hbref.BackendRef.Datacenter, "datacenter is not yet supported on backend refs")) + } + + if len(hbref.Filters) > 0 { + errs = append(errs, field.Invalid(ruleBackendRefsPath.Child("filters"), hbref.Filters, "filters are not supported at this level yet")) + } + } + + if rule.Timeouts != nil { + errs = append(errs, validateHTTPTimeouts(rule.Timeouts, rulePath.Child("timeouts"))...) + } + if rule.Retries != nil { + errs = append(errs, validateHTTPRetries(rule.Retries, rulePath.Child("retries"))...) + } + } + + if len(errs) > 0 { + return apierrors.NewInvalid( + schema.GroupKind{Group: MeshGroup, Kind: common.HTTPRoute}, + in.KubernetesName(), errs) + } + return nil +} + +func isValidHTTPMethod(method string) bool { + switch method { + case http.MethodGet, + http.MethodHead, + http.MethodPost, + http.MethodPut, + http.MethodPatch, + http.MethodDelete, + http.MethodConnect, + http.MethodOptions, + http.MethodTrace: + return true + default: + return false + } +} + +// DefaultNamespaceFields is required as part of the common.MeshConfig interface. +func (in *HTTPRoute) DefaultNamespaceFields(tenancy common.ConsulTenancyConfig) {} diff --git a/control-plane/api/mesh/v2beta1/http_route_types_test.go b/control-plane/api/mesh/v2beta1/http_route_types_test.go new file mode 100644 index 0000000000..824d7e35b7 --- /dev/null +++ b/control-plane/api/mesh/v2beta1/http_route_types_test.go @@ -0,0 +1,1338 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package v2beta1 + +import ( + "testing" + "time" + + "github.com/google/go-cmp/cmp" + pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v2beta1" + "github.com/hashicorp/consul/proto-public/pbresource" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/timestamppb" + "google.golang.org/protobuf/types/known/wrapperspb" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/hashicorp/consul-k8s/control-plane/api/common" + inject "github.com/hashicorp/consul-k8s/control-plane/connect-inject/common" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" + "github.com/hashicorp/consul-k8s/control-plane/helper/test" +) + +func TestHTTPRoute_MatchesConsul(t *testing.T) { + cases := map[string]struct { + OurConsulNamespace string + OurConsulPartition string + OurData *HTTPRoute + + TheirName string + TheirConsulNamespace string + TheirConsulPartition string + TheirData *pbmesh.HTTPRoute + ResourceOverride *pbresource.Resource // Used to test that an empty resource of another type will not match + + Matches bool + }{ + "empty fields matches": { + OurConsulNamespace: constants.DefaultConsulNS, + OurConsulPartition: constants.DefaultConsulPartition, + OurData: &HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + }, + Spec: pbmesh.HTTPRoute{}, + }, + TheirName: "name", + TheirConsulNamespace: constants.DefaultConsulNS, + TheirConsulPartition: constants.DefaultConsulPartition, + TheirData: &pbmesh.HTTPRoute{}, + Matches: true, + }, + "hostnames are compared": { + OurConsulNamespace: "consul-ns", + OurConsulPartition: "consul-partition", + OurData: &HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.HTTPRoute{ + Hostnames: []string{ + "a-hostname", "another-hostname", + }, + }, + }, + TheirName: "foo", + TheirConsulNamespace: "consul-ns", + TheirConsulPartition: "consul-partition", + TheirData: &pbmesh.HTTPRoute{ + Hostnames: []string{ + "not-a-hostname", "another-hostname", + }, + }, + Matches: false, + }, + "all fields set matches": { + OurConsulNamespace: "consul-ns", + OurConsulPartition: "consul-partition", + OurData: &HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + { + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "a-partition", + Namespace: "a-namespace", + }, + Name: "reference-name", + Section: "section-name", + }, + Port: "20201", + }, + }, + Hostnames: []string{ + "a-hostname", "another-hostname", + }, + Rules: []*pbmesh.HTTPRouteRule{ + { + Matches: []*pbmesh.HTTPRouteMatch{ + { + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_EXACT, + Value: "exact-value", + }, + Headers: []*pbmesh.HTTPHeaderMatch{ + { + Type: pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_PREFIX, + Name: "test-header", + Value: "header-value", + }, + }, + QueryParams: []*pbmesh.HTTPQueryParamMatch{ + { + Type: pbmesh.QueryParamMatchType_QUERY_PARAM_MATCH_TYPE_PRESENT, + Name: "query-param-name", + Value: "query-value", + }, + }, + Method: "GET", + }, + }, + Filters: []*pbmesh.HTTPRouteFilter{ + { + RequestHeaderModifier: &pbmesh.HTTPHeaderFilter{ + Set: []*pbmesh.HTTPHeader{ + { + Name: "set-header", + Value: "a-header-value", + }, + }, + Add: []*pbmesh.HTTPHeader{ + { + Name: "added-header", + Value: "another-header-value", + }, + }, + Remove: []string{ + "remove-header", + }, + }, + ResponseHeaderModifier: &pbmesh.HTTPHeaderFilter{ + Set: []*pbmesh.HTTPHeader{ + { + Name: "set-header", + Value: "a-header-value", + }, + }, + Add: []*pbmesh.HTTPHeader{ + { + Name: "added-header", + Value: "another-header-value", + }, + }, + Remove: []string{ + "remove-header", + }, + }, + UrlRewrite: &pbmesh.HTTPURLRewriteFilter{ + PathPrefix: "a-path-prefix", + }, + }, + }, + Timeouts: &pbmesh.HTTPRouteTimeouts{ + Request: &durationpb.Duration{ + Seconds: 10, + Nanos: 5, + }, + Idle: &durationpb.Duration{ + Seconds: 5, + Nanos: 10, + }, + }, + Retries: &pbmesh.HTTPRouteRetries{ + Number: &wrapperspb.UInt32Value{ + Value: 1, + }, + OnConnectFailure: false, + OnConditions: []string{ + "condition-one", "condition-two", + }, + OnStatusCodes: []uint32{ + 200, 201, 202, + }, + }, + BackendRefs: []*pbmesh.HTTPBackendRef{ + { + BackendRef: &pbmesh.BackendReference{ + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "some-partition", + Namespace: "some-namespace", + }, + Name: "backend-name", + Section: "backend-section", + }, + Port: "20211", + Datacenter: "another-datacenter", + }, + Weight: 12, + Filters: []*pbmesh.HTTPRouteFilter{ + { + RequestHeaderModifier: &pbmesh.HTTPHeaderFilter{ + Set: []*pbmesh.HTTPHeader{ + { + Name: "set-header", + Value: "setting", + }, + }, + Add: []*pbmesh.HTTPHeader{ + { + Name: "added-header", + Value: "adding", + }, + }, + Remove: []string{"removing"}, + }, + ResponseHeaderModifier: &pbmesh.HTTPHeaderFilter{ + Set: []*pbmesh.HTTPHeader{ + { + Name: "another-set-header", + Value: "setting", + }, + }, + Add: []*pbmesh.HTTPHeader{ + { + Name: "another-added-header", + Value: "adding", + }, + }, + Remove: []string{"also-removing"}, + }, + UrlRewrite: &pbmesh.HTTPURLRewriteFilter{ + PathPrefix: "/prefixing-it", + }, + }, + }, + }, + }, + }, + }, + }, + }, + TheirName: "foo", + TheirConsulNamespace: "consul-ns", + TheirConsulPartition: "consul-partition", + TheirData: &pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + { + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "a-partition", + Namespace: "a-namespace", + }, + Name: "reference-name", + Section: "section-name", + }, + Port: "20201", + }, + }, + Hostnames: []string{ + "a-hostname", "another-hostname", + }, + Rules: []*pbmesh.HTTPRouteRule{ + { + Matches: []*pbmesh.HTTPRouteMatch{ + { + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_EXACT, + Value: "exact-value", + }, + Headers: []*pbmesh.HTTPHeaderMatch{ + { + Type: pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_PREFIX, + Name: "test-header", + Value: "header-value", + }, + }, + QueryParams: []*pbmesh.HTTPQueryParamMatch{ + { + Type: pbmesh.QueryParamMatchType_QUERY_PARAM_MATCH_TYPE_PRESENT, + Name: "query-param-name", + Value: "query-value", + }, + }, + Method: "GET", + }, + }, + Filters: []*pbmesh.HTTPRouteFilter{ + { + RequestHeaderModifier: &pbmesh.HTTPHeaderFilter{ + Set: []*pbmesh.HTTPHeader{ + { + Name: "set-header", + Value: "a-header-value", + }, + }, + Add: []*pbmesh.HTTPHeader{ + { + Name: "added-header", + Value: "another-header-value", + }, + }, + Remove: []string{ + "remove-header", + }, + }, + ResponseHeaderModifier: &pbmesh.HTTPHeaderFilter{ + Set: []*pbmesh.HTTPHeader{ + { + Name: "set-header", + Value: "a-header-value", + }, + }, + Add: []*pbmesh.HTTPHeader{ + { + Name: "added-header", + Value: "another-header-value", + }, + }, + Remove: []string{ + "remove-header", + }, + }, + UrlRewrite: &pbmesh.HTTPURLRewriteFilter{ + PathPrefix: "a-path-prefix", + }, + }, + }, + Timeouts: &pbmesh.HTTPRouteTimeouts{ + Request: &durationpb.Duration{ + Seconds: 10, + Nanos: 5, + }, + Idle: &durationpb.Duration{ + Seconds: 5, + Nanos: 10, + }, + }, + Retries: &pbmesh.HTTPRouteRetries{ + Number: &wrapperspb.UInt32Value{ + Value: 1, + }, + OnConnectFailure: false, + OnConditions: []string{ + "condition-one", "condition-two", + }, + OnStatusCodes: []uint32{ + 200, 201, 202, + }, + }, + BackendRefs: []*pbmesh.HTTPBackendRef{ + { + BackendRef: &pbmesh.BackendReference{ + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "some-partition", + Namespace: "some-namespace", + }, + Name: "backend-name", + Section: "backend-section", + }, + Port: "20211", + Datacenter: "another-datacenter", + }, + Weight: 12, + Filters: []*pbmesh.HTTPRouteFilter{ + { + RequestHeaderModifier: &pbmesh.HTTPHeaderFilter{ + Set: []*pbmesh.HTTPHeader{ + { + Name: "set-header", + Value: "setting", + }, + }, + Add: []*pbmesh.HTTPHeader{ + { + Name: "added-header", + Value: "adding", + }, + }, + Remove: []string{"removing"}, + }, + ResponseHeaderModifier: &pbmesh.HTTPHeaderFilter{ + Set: []*pbmesh.HTTPHeader{ + { + Name: "another-set-header", + Value: "setting", + }, + }, + Add: []*pbmesh.HTTPHeader{ + { + Name: "another-added-header", + Value: "adding", + }, + }, + Remove: []string{"also-removing"}, + }, + UrlRewrite: &pbmesh.HTTPURLRewriteFilter{ + PathPrefix: "/prefixing-it", + }, + }, + }, + }, + }, + }, + }, + }, + Matches: true, + }, + "different types does not match": { + OurConsulNamespace: constants.DefaultConsulNS, + OurConsulPartition: constants.DefaultConsulPartition, + OurData: &HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + }, + Spec: pbmesh.HTTPRoute{}, + }, + ResourceOverride: &pbresource.Resource{ + Id: &pbresource.ID{ + Name: "name", + Type: pbmesh.ProxyConfigurationType, + Tenancy: &pbresource.Tenancy{ + Partition: constants.DefaultConsulNS, + Namespace: constants.DefaultConsulPartition, + + // Because we are explicitly defining NS/partition, this will not default and must be explicit. + // At a future point, this will move out of the Tenancy block. + PeerName: constants.DefaultConsulPeer, + }, + }, + Data: inject.ToProtoAny(&pbmesh.ProxyConfiguration{}), + Metadata: meshConfigMeta(), + }, + Matches: false, + }, + } + for name, c := range cases { + t.Run(name, func(t *testing.T) { + consulResource := c.ResourceOverride + if c.TheirName != "" { + consulResource = constructHTTPRouteResource(c.TheirData, c.TheirName, c.TheirConsulNamespace, c.TheirConsulPartition) + } + require.Equal(t, c.Matches, c.OurData.MatchesConsul(consulResource, c.OurConsulNamespace, c.OurConsulPartition)) + }) + } +} + +// TestHTTPRoute_Resource also includes test to verify ResourceID(). +func TestHTTPRoute_Resource(t *testing.T) { + cases := map[string]struct { + Ours *HTTPRoute + ConsulNamespace string + ConsulPartition string + ExpectedName string + ExpectedData *pbmesh.HTTPRoute + }{ + "empty fields": { + Ours: &HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: pbmesh.HTTPRoute{}, + }, + ConsulNamespace: constants.DefaultConsulNS, + ConsulPartition: constants.DefaultConsulPartition, + ExpectedName: "foo", + ExpectedData: &pbmesh.HTTPRoute{}, + }, + "every field set": { + Ours: &HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + { + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "a-partition", + Namespace: "a-namespace", + }, + Name: "reference-name", + Section: "section-name", + }, + Port: "20201", + }, + }, + Hostnames: []string{ + "a-hostname", "another-hostname", + }, + Rules: []*pbmesh.HTTPRouteRule{ + { + Matches: []*pbmesh.HTTPRouteMatch{ + { + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_EXACT, + Value: "exact-value", + }, + Headers: []*pbmesh.HTTPHeaderMatch{ + { + Type: pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_PREFIX, + Name: "test-header", + Value: "header-value", + }, + }, + QueryParams: []*pbmesh.HTTPQueryParamMatch{ + { + Type: pbmesh.QueryParamMatchType_QUERY_PARAM_MATCH_TYPE_PRESENT, + Name: "query-param-name", + Value: "query-value", + }, + }, + Method: "GET", + }, + }, + Filters: []*pbmesh.HTTPRouteFilter{ + { + RequestHeaderModifier: &pbmesh.HTTPHeaderFilter{ + Set: []*pbmesh.HTTPHeader{ + { + Name: "set-header", + Value: "a-header-value", + }, + }, + Add: []*pbmesh.HTTPHeader{ + { + Name: "added-header", + Value: "another-header-value", + }, + }, + Remove: []string{ + "remove-header", + }, + }, + ResponseHeaderModifier: &pbmesh.HTTPHeaderFilter{ + Set: []*pbmesh.HTTPHeader{ + { + Name: "set-header", + Value: "a-header-value", + }, + }, + Add: []*pbmesh.HTTPHeader{ + { + Name: "added-header", + Value: "another-header-value", + }, + }, + Remove: []string{ + "remove-header", + }, + }, + UrlRewrite: &pbmesh.HTTPURLRewriteFilter{ + PathPrefix: "a-path-prefix", + }, + }, + }, + Timeouts: &pbmesh.HTTPRouteTimeouts{ + Request: &durationpb.Duration{ + Seconds: 10, + Nanos: 5, + }, + Idle: &durationpb.Duration{ + Seconds: 5, + Nanos: 10, + }, + }, + Retries: &pbmesh.HTTPRouteRetries{ + Number: &wrapperspb.UInt32Value{ + Value: 1, + }, + OnConnectFailure: false, + OnConditions: []string{ + "condition-one", "condition-two", + }, + OnStatusCodes: []uint32{ + 200, 201, 202, + }, + }, + }, + }, + }, + }, + ConsulNamespace: "not-default-namespace", + ConsulPartition: "not-default-partition", + ExpectedName: "foo", + ExpectedData: &pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + { + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "a-partition", + Namespace: "a-namespace", + }, + Name: "reference-name", + Section: "section-name", + }, + Port: "20201", + }, + }, + Hostnames: []string{ + "a-hostname", "another-hostname", + }, + Rules: []*pbmesh.HTTPRouteRule{ + { + Matches: []*pbmesh.HTTPRouteMatch{ + { + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_EXACT, + Value: "exact-value", + }, + Headers: []*pbmesh.HTTPHeaderMatch{ + { + Type: pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_PREFIX, + Name: "test-header", + Value: "header-value", + }, + }, + QueryParams: []*pbmesh.HTTPQueryParamMatch{ + { + Type: pbmesh.QueryParamMatchType_QUERY_PARAM_MATCH_TYPE_PRESENT, + Name: "query-param-name", + Value: "query-value", + }, + }, + Method: "GET", + }, + }, + Filters: []*pbmesh.HTTPRouteFilter{ + { + RequestHeaderModifier: &pbmesh.HTTPHeaderFilter{ + Set: []*pbmesh.HTTPHeader{ + { + Name: "set-header", + Value: "a-header-value", + }, + }, + Add: []*pbmesh.HTTPHeader{ + { + Name: "added-header", + Value: "another-header-value", + }, + }, + Remove: []string{ + "remove-header", + }, + }, + ResponseHeaderModifier: &pbmesh.HTTPHeaderFilter{ + Set: []*pbmesh.HTTPHeader{ + { + Name: "set-header", + Value: "a-header-value", + }, + }, + Add: []*pbmesh.HTTPHeader{ + { + Name: "added-header", + Value: "another-header-value", + }, + }, + Remove: []string{ + "remove-header", + }, + }, + UrlRewrite: &pbmesh.HTTPURLRewriteFilter{ + PathPrefix: "a-path-prefix", + }, + }, + }, + Timeouts: &pbmesh.HTTPRouteTimeouts{ + Request: &durationpb.Duration{ + Seconds: 10, + Nanos: 5, + }, + Idle: &durationpb.Duration{ + Seconds: 5, + Nanos: 10, + }, + }, + Retries: &pbmesh.HTTPRouteRetries{ + Number: &wrapperspb.UInt32Value{ + Value: 1, + }, + OnConnectFailure: false, + OnConditions: []string{ + "condition-one", "condition-two", + }, + OnStatusCodes: []uint32{ + 200, 201, 202, + }, + }, + }, + }, + }, + }, + } + for name, c := range cases { + t.Run(name, func(t *testing.T) { + actual := c.Ours.Resource(c.ConsulNamespace, c.ConsulPartition) + expected := constructHTTPRouteResource(c.ExpectedData, c.ExpectedName, c.ConsulNamespace, c.ConsulPartition) + + opts := append([]cmp.Option{ + protocmp.IgnoreFields(&pbresource.Resource{}, "status", "generation", "version"), + protocmp.IgnoreFields(&pbresource.ID{}, "uid"), + }, test.CmpProtoIgnoreOrder()...) + diff := cmp.Diff(expected, actual, opts...) + require.Equal(t, "", diff, "HTTPRoute do not match") + }) + } +} + +func TestHTTPRoute_SetSyncedCondition(t *testing.T) { + trafficPermissions := &HTTPRoute{} + trafficPermissions.SetSyncedCondition(corev1.ConditionTrue, "reason", "message") + + require.Equal(t, corev1.ConditionTrue, trafficPermissions.Status.Conditions[0].Status) + require.Equal(t, "reason", trafficPermissions.Status.Conditions[0].Reason) + require.Equal(t, "message", trafficPermissions.Status.Conditions[0].Message) + now := metav1.Now() + require.True(t, trafficPermissions.Status.Conditions[0].LastTransitionTime.Before(&now)) +} + +func TestHTTPRoute_SetLastSyncedTime(t *testing.T) { + trafficPermissions := &HTTPRoute{} + syncedTime := metav1.NewTime(time.Now()) + trafficPermissions.SetLastSyncedTime(&syncedTime) + + require.Equal(t, &syncedTime, trafficPermissions.Status.LastSyncedTime) +} + +func TestHTTPRoute_GetSyncedConditionStatus(t *testing.T) { + cases := []corev1.ConditionStatus{ + corev1.ConditionUnknown, + corev1.ConditionFalse, + corev1.ConditionTrue, + } + for _, status := range cases { + t.Run(string(status), func(t *testing.T) { + trafficPermissions := &HTTPRoute{ + Status: Status{ + Conditions: []Condition{{ + Type: ConditionSynced, + Status: status, + }}, + }, + } + + require.Equal(t, status, trafficPermissions.SyncedConditionStatus()) + }) + } +} + +func TestHTTPRoute_GetConditionWhenStatusNil(t *testing.T) { + require.Nil(t, (&HTTPRoute{}).GetCondition(ConditionSynced)) +} + +func TestHTTPRoute_SyncedConditionStatusWhenStatusNil(t *testing.T) { + require.Equal(t, corev1.ConditionUnknown, (&HTTPRoute{}).SyncedConditionStatus()) +} + +func TestHTTPRoute_SyncedConditionWhenStatusNil(t *testing.T) { + status, reason, message := (&HTTPRoute{}).SyncedCondition() + require.Equal(t, corev1.ConditionUnknown, status) + require.Equal(t, "", reason) + require.Equal(t, "", message) +} + +func TestHTTPRoute_KubeKind(t *testing.T) { + require.Equal(t, "httproute", (&HTTPRoute{}).KubeKind()) +} + +func TestHTTPRoute_KubernetesName(t *testing.T) { + require.Equal(t, "test", (&HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "bar", + }, + Spec: pbmesh.HTTPRoute{}, + }).KubernetesName()) +} + +func TestHTTPRoute_ObjectMeta(t *testing.T) { + meta := metav1.ObjectMeta{ + Name: "name", + Namespace: "namespace", + } + trafficPermissions := &HTTPRoute{ + ObjectMeta: meta, + } + require.Equal(t, &meta, trafficPermissions.GetObjectMeta()) +} + +// Test defaulting behavior when namespaces are enabled as well as disabled. +// TODO: add when implemented +//func TestHTTPRoute_DefaultNamespaceFields(t *testing.T) + +func TestHTTPRoute_Validate(t *testing.T) { + cases := []struct { + name string + input *HTTPRoute + expectedErrMsgs []string + }{ + { + name: "kitchen sink OK", + input: &HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + { + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "a-partition", + Namespace: "a-namespace", + }, + Name: "reference-name", + Section: "section-name", + }, + Port: "20201", + }, + }, + Hostnames: []string{}, + Rules: []*pbmesh.HTTPRouteRule{ + { + Matches: []*pbmesh.HTTPRouteMatch{ + { + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_EXACT, + Value: "/exactValue", + }, + Headers: []*pbmesh.HTTPHeaderMatch{ + { + Type: pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_PREFIX, + Name: "test-header", + Value: "header-value", + }, + }, + QueryParams: []*pbmesh.HTTPQueryParamMatch{ + { + Type: pbmesh.QueryParamMatchType_QUERY_PARAM_MATCH_TYPE_PRESENT, + Name: "query-param-name", + Value: "query-value", + }, + }, + Method: "GET", + }, + }, + Filters: []*pbmesh.HTTPRouteFilter{ + { + UrlRewrite: &pbmesh.HTTPURLRewriteFilter{ + PathPrefix: "a-path-prefix", + }, + }, + }, + Timeouts: &pbmesh.HTTPRouteTimeouts{ + Request: &durationpb.Duration{ + Seconds: 10, + Nanos: 5, + }, + Idle: &durationpb.Duration{ + Seconds: 5, + Nanos: 10, + }, + }, + Retries: &pbmesh.HTTPRouteRetries{ + Number: &wrapperspb.UInt32Value{ + Value: 1, + }, + OnConnectFailure: false, + OnConditions: []string{ + "reset", "cancelled", + }, + OnStatusCodes: []uint32{ + 200, 201, 202, + }, + }, + BackendRefs: []*pbmesh.HTTPBackendRef{ + { + BackendRef: &pbmesh.BackendReference{ + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "some-partition", + Namespace: "some-namespace", + }, + Name: "backend", + Section: "backend-section", + }, + Port: "20101", + }, + Weight: 15, + }, + }, + }, + }, + }, + }, + expectedErrMsgs: nil, + }, + { + name: "missing parentRefs", + input: &HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{}, + }, + }, + expectedErrMsgs: []string{ + `spec.parentRefs: Required value: cannot be empty`, + }, + }, + { + name: "hostnames created", + input: &HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + { + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "a-partition", + Namespace: "a-namespace", + }, + Name: "reference-name", + Section: "section-name", + }, + Port: "20201", + }, + }, + Hostnames: []string{"a-hostname", "another-hostname"}, + }, + }, + expectedErrMsgs: []string{ + `spec.hostnames: Invalid value: []string{"a-hostname", "another-hostname"}: should not populate hostnames`, + }, + }, + { + name: "rules.matches.path", + input: &HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + { + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "a-partition", + Namespace: "a-namespace", + }, + Name: "reference-name", + Section: "section-name", + }, + Port: "20201", + }, + }, + Hostnames: []string{}, + Rules: []*pbmesh.HTTPRouteRule{ + { + Matches: []*pbmesh.HTTPRouteMatch{ + { + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_UNSPECIFIED, + }, + }, + { + Path: &pbmesh.HTTPPathMatch{}, + }, + { + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_EXACT, + Value: "does-not-have-/-prefix", + }, + }, + { + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX, + Value: "does-not-have-/-prefix-either", + }, + }, + { + Path: &pbmesh.HTTPPathMatch{ + Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_REGEX, + Value: "", + }, + }, + }, + BackendRefs: []*pbmesh.HTTPBackendRef{{BackendRef: &pbmesh.BackendReference{}}}, + }, + }, + }, + }, + expectedErrMsgs: []string{ + `spec.rules[0].matches[0].path.type: Invalid value: PATH_MATCH_TYPE_UNSPECIFIED: missing required field`, + `spec.rules[0].matches[1].path.type: Invalid value: PATH_MATCH_TYPE_UNSPECIFIED: missing required field`, + `spec.rules[0].matches[2].path.value: Invalid value: "does-not-have-/-prefix": exact patch value does not start with '/'`, + `spec.rules[0].matches[3].path.value: Invalid value: "does-not-have-/-prefix-either": prefix patch value does not start with '/'`, + `spec.rules[0].matches[4].path.value: Required value: missing required field`, + }, + }, + { + name: "rules.matches.headers", + input: &HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + { + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "a-partition", + Namespace: "a-namespace", + }, + Name: "reference-name", + Section: "section-name", + }, + Port: "20201", + }, + }, + Hostnames: []string{}, + Rules: []*pbmesh.HTTPRouteRule{ + { + Matches: []*pbmesh.HTTPRouteMatch{ + { + Headers: []*pbmesh.HTTPHeaderMatch{ + { + Type: pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_UNSPECIFIED, + Name: "test-header", + Value: "header-value", + }, + { + // Type: "", + Name: "test-header", + Value: "header-value", + }, + { + Type: pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_EXACT, + Name: "", + }, + }, + Method: "GET", + }, + }, + BackendRefs: []*pbmesh.HTTPBackendRef{{BackendRef: &pbmesh.BackendReference{}}}, + }, + }, + }, + }, + expectedErrMsgs: []string{ + `spec.rules[0].matches[0].headers[0].type: Invalid value: HEADER_MATCH_TYPE_UNSPECIFIED: missing required field`, + `spec.rules[0].matches[0].headers[1].type: Invalid value: HEADER_MATCH_TYPE_UNSPECIFIED: missing required field`, + `spec.rules[0].matches[0].headers[2].name: Required value: missing required field`, + }, + }, + { + name: "rules.filters", + input: &HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + { + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "a-partition", + Namespace: "a-namespace", + }, + Name: "reference-name", + Section: "section-name", + }, + Port: "20201", + }, + }, + Hostnames: []string{}, + Rules: []*pbmesh.HTTPRouteRule{ + { + Filters: []*pbmesh.HTTPRouteFilter{ + { + RequestHeaderModifier: &pbmesh.HTTPHeaderFilter{}, + ResponseHeaderModifier: &pbmesh.HTTPHeaderFilter{}, + }, + { + RequestHeaderModifier: &pbmesh.HTTPHeaderFilter{}, + UrlRewrite: &pbmesh.HTTPURLRewriteFilter{ + PathPrefix: "prefix-1", + }, + }, + { + ResponseHeaderModifier: &pbmesh.HTTPHeaderFilter{}, + UrlRewrite: &pbmesh.HTTPURLRewriteFilter{ + PathPrefix: "prefix-2", + }, + }, + { + UrlRewrite: &pbmesh.HTTPURLRewriteFilter{ + PathPrefix: "", + }, + }, + }, + BackendRefs: []*pbmesh.HTTPBackendRef{{BackendRef: &pbmesh.BackendReference{}}}, + }, + }, + }, + }, + expectedErrMsgs: []string{ + `spec.filters[0]: Invalid value`, + `spec.filters[1]: Invalid value`, + `spec.filters[2]: Invalid value`, + `spec.filters[3].urlRewrite.pathPrefix: Invalid value: "": field should not be empty if enclosing section is set`, + `exactly one of request_header_modifier, response_header_modifier, or url_rewrite is required`, + }, + }, + { + name: "rule.backendRefs", + input: &HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + { + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "a-partition", + Namespace: "a-namespace", + }, + Name: "reference-name", + Section: "section-name", + }, + Port: "20201", + }, + }, + Hostnames: []string{}, + Rules: []*pbmesh.HTTPRouteRule{ + { + BackendRefs: []*pbmesh.HTTPBackendRef{}, + }, + { + BackendRefs: []*pbmesh.HTTPBackendRef{ + {}, + { + BackendRef: &pbmesh.BackendReference{ + Datacenter: "some-datacenter", + }, + }, + { + BackendRef: &pbmesh.BackendReference{}, + Filters: []*pbmesh.HTTPRouteFilter{ + { + UrlRewrite: &pbmesh.HTTPURLRewriteFilter{ + PathPrefix: "/prefixed", + }, + }, + }, + }, + }, + }, + }, + }, + }, + expectedErrMsgs: []string{ + `spec.rules[0].backendRefs: Required value: missing required field`, + `spec.rules[1].backendRefs[0].backendRef: Required value: missing required field`, + `spec.rules[1].backendRefs[1].backendRef.datacenter: Invalid value: "some-datacenter": datacenter is not yet supported on backend refs`, + `spec.rules[1].backendRefs[2].filters: Invalid value`, + `filters are not supported at this level yet`, + }, + }, + { + name: "rules.timeouts", + input: &HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + { + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "a-partition", + Namespace: "a-namespace", + }, + Name: "reference-name", + Section: "section-name", + }, + Port: "20201", + }, + }, + Hostnames: []string{}, + Rules: []*pbmesh.HTTPRouteRule{ + { + Timeouts: &pbmesh.HTTPRouteTimeouts{ + Request: &durationpb.Duration{ + Seconds: -10, + Nanos: -5, + }, + Idle: &durationpb.Duration{ + Seconds: -5, + Nanos: -10, + }, + }, + BackendRefs: []*pbmesh.HTTPBackendRef{{BackendRef: &pbmesh.BackendReference{}}}, + }, + }, + }, + }, + expectedErrMsgs: []string{ + `spec.rules[0].timeouts.request: Invalid value: -10.000000005s: timeout cannot be negative`, + `spec.rules[0].timeouts.idle: Invalid value: -5.00000001s: timeout cannot be negative`, + }, + }, + { + name: "rules.timeouts", + input: &HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + { + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "a-partition", + Namespace: "a-namespace", + }, + Name: "reference-name", + Section: "section-name", + }, + Port: "20201", + }, + }, + Hostnames: []string{}, + Rules: []*pbmesh.HTTPRouteRule{ + { + Retries: &pbmesh.HTTPRouteRetries{ + OnConditions: []string{ + "invalid-condition", "another-invalid-condition", + }, + }, + BackendRefs: []*pbmesh.HTTPBackendRef{{BackendRef: &pbmesh.BackendReference{}}}, + }, + }, + }, + }, + expectedErrMsgs: []string{ + `spec.rules[0].retries.onConditions[0]: Invalid value: "invalid-condition": not a valid retry condition`, + `spec.rules[0].retries.onConditions[1]: Invalid value: "another-invalid-condition": not a valid retry condition`, + }, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + err := tc.input.Validate(common.ConsulTenancyConfig{}) + if len(tc.expectedErrMsgs) != 0 { + require.Error(t, err) + for _, s := range tc.expectedErrMsgs { + require.Contains(t, err.Error(), s) + } + } else { + require.NoError(t, err) + } + }) + } +} + +func constructHTTPRouteResource(tp *pbmesh.HTTPRoute, name, namespace, partition string) *pbresource.Resource { + data := inject.ToProtoAny(tp) + + id := &pbresource.ID{ + Name: name, + Type: pbmesh.HTTPRouteType, + Tenancy: &pbresource.Tenancy{ + Partition: partition, + Namespace: namespace, + + // Because we are explicitly defining NS/partition, this will not default and must be explicit. + // At a future point, this will move out of the Tenancy block. + PeerName: constants.DefaultConsulPeer, + }, + Uid: "ABCD", // We add this to show it does not factor into the comparison + } + + return &pbresource.Resource{ + Id: id, + Data: data, + Metadata: meshConfigMeta(), + + // We add the fields below to prove that they are not used in the Match when comparing the CRD to Consul. + Version: "123456", + Generation: "01ARZ3NDEKTSV4RRFFQ69G5FAV", + Status: map[string]*pbresource.Status{ + "knock": { + ObservedGeneration: "01ARZ3NDEKTSV4RRFFQ69G5FAV", + Conditions: make([]*pbresource.Condition, 0), + UpdatedAt: timestamppb.Now(), + }, + }, + } +} diff --git a/control-plane/api/mesh/v2beta1/http_route_webhook.go b/control-plane/api/mesh/v2beta1/http_route_webhook.go new file mode 100644 index 0000000000..e16db17458 --- /dev/null +++ b/control-plane/api/mesh/v2beta1/http_route_webhook.go @@ -0,0 +1,65 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package v2beta1 + +import ( + "context" + "net/http" + + "github.com/go-logr/logr" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/hashicorp/consul-k8s/control-plane/api/common" +) + +// +kubebuilder:object:generate=false + +type HTTPRouteWebhook struct { + Logger logr.Logger + + // ConsulTenancyConfig contains the injector's namespace and partition configuration. + ConsulTenancyConfig common.ConsulTenancyConfig + + decoder *admission.Decoder + client.Client +} + +var _ common.MeshConfigLister = &HTTPRouteWebhook{} + +// NOTE: The path value in the below line is the path to the webhook. +// If it is updated, run code-gen, update subcommand/inject-connect/command.go +// and the consul-helm value for the path to the webhook. +// +// NOTE: The below line cannot be combined with any other comment. If it is it will break the code generation. +// +// +kubebuilder:webhook:verbs=create;update,path=/mutate-v2beta1-httproute,mutating=true,failurePolicy=fail,groups=auth.consul.hashicorp.com,resources=httproute,versions=v2beta1,name=mutate-httproute.auth.consul.hashicorp.com,sideEffects=None,admissionReviewVersions=v1beta1;v1 + +func (v *HTTPRouteWebhook) Handle(ctx context.Context, req admission.Request) admission.Response { + var resource HTTPRoute + err := v.decoder.Decode(req, &resource) + if err != nil { + return admission.Errored(http.StatusBadRequest, err) + } + + return common.ValidateMeshConfig(ctx, req, v.Logger, v, &resource, v.ConsulTenancyConfig) +} + +func (v *HTTPRouteWebhook) List(ctx context.Context) ([]common.MeshConfig, error) { + var resourceList HTTPRouteList + if err := v.Client.List(ctx, &resourceList); err != nil { + return nil, err + } + var entries []common.MeshConfig + for _, item := range resourceList.Items { + entries = append(entries, common.MeshConfig(item)) + } + return entries, nil +} + +func (v *HTTPRouteWebhook) InjectDecoder(d *admission.Decoder) error { + v.decoder = d + return nil +} diff --git a/control-plane/api/mesh/v2beta1/mesh_groupversion_info.go b/control-plane/api/mesh/v2beta1/mesh_groupversion_info.go new file mode 100644 index 0000000000..a9fe6a6a83 --- /dev/null +++ b/control-plane/api/mesh/v2beta1/mesh_groupversion_info.go @@ -0,0 +1,27 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// Package v2beta1 contains API Schema definitions for the consul.hashicorp.com v2beta1 API group +// +kubebuilder:object:generate=true +// +groupName=mesh.consul.hashicorp.com +package v2beta1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + + // MeshGroup is a collection of mesh resources. + MeshGroup = "mesh.consul.hashicorp.com" + + // MeshGroupVersion is group version used to register these objects. + MeshGroupVersion = schema.GroupVersion{Group: MeshGroup, Version: "v2beta1"} + + // MeshSchemeBuilder is used to add go types to the GroupVersionKind scheme. + MeshSchemeBuilder = &scheme.Builder{GroupVersion: MeshGroupVersion} + + // AddMeshToScheme adds the types in this group-version to the given scheme. + AddMeshToScheme = MeshSchemeBuilder.AddToScheme +) diff --git a/control-plane/api/mesh/v2beta1/proxy_configuration_route_webhook.go b/control-plane/api/mesh/v2beta1/proxy_configuration_route_webhook.go new file mode 100644 index 0000000000..711d8c81bc --- /dev/null +++ b/control-plane/api/mesh/v2beta1/proxy_configuration_route_webhook.go @@ -0,0 +1,65 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package v2beta1 + +import ( + "context" + "net/http" + + "github.com/go-logr/logr" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/hashicorp/consul-k8s/control-plane/api/common" +) + +// +kubebuilder:object:generate=false + +type ProxyConfigurationWebhook struct { + Logger logr.Logger + + // ConsulTenancyConfig contains the injector's namespace and partition configuration. + ConsulTenancyConfig common.ConsulTenancyConfig + + decoder *admission.Decoder + client.Client +} + +var _ common.MeshConfigLister = &ProxyConfigurationWebhook{} + +// NOTE: The path value in the below line is the path to the webhook. +// If it is updated, run code-gen, update subcommand/inject-connect/command.go +// and the consul-helm value for the path to the webhook. +// +// NOTE: The below line cannot be combined with any other comment. If it is it will break the code generation. +// +// +kubebuilder:webhook:verbs=create;update,path=/mutate-v2beta1-proxyconfiguration,mutating=true,failurePolicy=fail,groups=auth.consul.hashicorp.com,resources=proxyconfiguration,versions=v2beta1,name=mutate-proxyconfiguration.auth.consul.hashicorp.com,sideEffects=None,admissionReviewVersions=v1beta1;v1 + +func (v *ProxyConfigurationWebhook) Handle(ctx context.Context, req admission.Request) admission.Response { + var resource ProxyConfiguration + err := v.decoder.Decode(req, &resource) + if err != nil { + return admission.Errored(http.StatusBadRequest, err) + } + + return common.ValidateMeshConfig(ctx, req, v.Logger, v, &resource, v.ConsulTenancyConfig) +} + +func (v *ProxyConfigurationWebhook) List(ctx context.Context) ([]common.MeshConfig, error) { + var resourceList ProxyConfigurationList + if err := v.Client.List(ctx, &resourceList); err != nil { + return nil, err + } + var entries []common.MeshConfig + for _, item := range resourceList.Items { + entries = append(entries, common.MeshConfig(item)) + } + return entries, nil +} + +func (v *ProxyConfigurationWebhook) InjectDecoder(d *admission.Decoder) error { + v.decoder = d + return nil +} diff --git a/control-plane/api/mesh/v2beta1/proxy_configuration_types.go b/control-plane/api/mesh/v2beta1/proxy_configuration_types.go new file mode 100644 index 0000000000..4df6ff95e1 --- /dev/null +++ b/control-plane/api/mesh/v2beta1/proxy_configuration_types.go @@ -0,0 +1,160 @@ +// // Copyright (c) HashiCorp, Inc. +// // SPDX-License-Identifier: MPL-2.0 +package v2beta1 + +import ( + "fmt" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v2beta1" + "github.com/hashicorp/consul/proto-public/pbresource" + "google.golang.org/protobuf/testing/protocmp" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + + "github.com/hashicorp/consul-k8s/control-plane/api/common" + inject "github.com/hashicorp/consul-k8s/control-plane/connect-inject/common" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" +) + +const ( + proxyConfigurationKubeKind = "proxyconfiguration" +) + +func init() { + MeshSchemeBuilder.Register(&ProxyConfiguration{}, &ProxyConfigurationList{}) +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status + +// ProxyConfiguration is the Schema for the TCP Routes API +// +kubebuilder:printcolumn:name="Synced",type="string",JSONPath=".status.conditions[?(@.type==\"Synced\")].status",description="The sync status of the resource with Consul" +// +kubebuilder:printcolumn:name="Last Synced",type="date",JSONPath=".status.lastSyncedTime",description="The last successful synced time of the resource with Consul" +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="The age of the resource" +// +kubebuilder:resource:shortName="proxy-configuration" +type ProxyConfiguration struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec pbmesh.ProxyConfiguration `json:"spec,omitempty"` + Status `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// ProxyConfigurationList contains a list of ProxyConfiguration. +type ProxyConfigurationList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []*ProxyConfiguration `json:"items"` +} + +func (in *ProxyConfiguration) ResourceID(namespace, partition string) *pbresource.ID { + return &pbresource.ID{ + Name: in.Name, + Type: pbmesh.ProxyConfigurationType, + Tenancy: &pbresource.Tenancy{ + Partition: partition, + Namespace: namespace, + + // Because we are explicitly defining NS/partition, this will not default and must be explicit. + // At a future point, this will move out of the Tenancy block. + PeerName: constants.DefaultConsulPeer, + }, + } +} + +func (in *ProxyConfiguration) Resource(namespace, partition string) *pbresource.Resource { + return &pbresource.Resource{ + Id: in.ResourceID(namespace, partition), + Data: inject.ToProtoAny(&in.Spec), + Metadata: meshConfigMeta(), + } +} + +func (in *ProxyConfiguration) AddFinalizer(f string) { + in.ObjectMeta.Finalizers = append(in.Finalizers(), f) +} + +func (in *ProxyConfiguration) RemoveFinalizer(f string) { + var newFinalizers []string + for _, oldF := range in.Finalizers() { + if oldF != f { + newFinalizers = append(newFinalizers, oldF) + } + } + in.ObjectMeta.Finalizers = newFinalizers +} + +func (in *ProxyConfiguration) Finalizers() []string { + return in.ObjectMeta.Finalizers +} + +func (in *ProxyConfiguration) MatchesConsul(candidate *pbresource.Resource, namespace, partition string) bool { + return cmp.Equal( + in.Resource(namespace, partition), + candidate, + protocmp.IgnoreFields(&pbresource.Resource{}, "status", "generation", "version"), + protocmp.IgnoreFields(&pbresource.ID{}, "uid"), + protocmp.Transform(), + cmpopts.SortSlices(func(a, b any) bool { return fmt.Sprintf("%v", a) < fmt.Sprintf("%v", b) }), + ) +} + +func (in *ProxyConfiguration) KubeKind() string { + return proxyConfigurationKubeKind +} + +func (in *ProxyConfiguration) KubernetesName() string { + return in.ObjectMeta.Name +} + +func (in *ProxyConfiguration) SetSyncedCondition(status corev1.ConditionStatus, reason, message string) { + in.Status.Conditions = Conditions{ + { + Type: ConditionSynced, + Status: status, + LastTransitionTime: metav1.Now(), + Reason: reason, + Message: message, + }, + } +} + +func (in *ProxyConfiguration) SetLastSyncedTime(time *metav1.Time) { + in.Status.LastSyncedTime = time +} + +func (in *ProxyConfiguration) SyncedCondition() (status corev1.ConditionStatus, reason, message string) { + cond := in.Status.GetCondition(ConditionSynced) + if cond == nil { + return corev1.ConditionUnknown, "", "" + } + return cond.Status, cond.Reason, cond.Message +} + +func (in *ProxyConfiguration) SyncedConditionStatus() corev1.ConditionStatus { + condition := in.Status.GetCondition(ConditionSynced) + if condition == nil { + return corev1.ConditionUnknown + } + return condition.Status +} + +func (in *ProxyConfiguration) Validate(_ common.ConsulTenancyConfig) error { + var errs field.ErrorList + if len(errs) > 0 { + return apierrors.NewInvalid( + schema.GroupKind{Group: MeshGroup, Kind: common.ProxyConfiguration}, + in.KubernetesName(), errs) + } + return nil +} + +// DefaultNamespaceFields is required as part of the common.MeshConfig interface. +func (in *ProxyConfiguration) DefaultNamespaceFields(tenancy common.ConsulTenancyConfig) {} diff --git a/control-plane/api/mesh/v2beta1/proxy_configuration_types_test.go b/control-plane/api/mesh/v2beta1/proxy_configuration_types_test.go new file mode 100644 index 0000000000..5e432175f4 --- /dev/null +++ b/control-plane/api/mesh/v2beta1/proxy_configuration_types_test.go @@ -0,0 +1,608 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package v2beta1 + +import ( + "testing" + "time" + + "github.com/google/go-cmp/cmp" + pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1" + pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v2beta1" + "github.com/hashicorp/consul/proto-public/pbresource" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/structpb" + "google.golang.org/protobuf/types/known/timestamppb" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + inject "github.com/hashicorp/consul-k8s/control-plane/connect-inject/common" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" + "github.com/hashicorp/consul-k8s/control-plane/helper/test" +) + +func TestProxyConfiguration_MatchesConsul(t *testing.T) { + cases := map[string]struct { + OurConsulNamespace string + OurConsulPartition string + OurData *ProxyConfiguration + + TheirName string + TheirConsulNamespace string + TheirConsulPartition string + TheirData *pbmesh.ProxyConfiguration + ResourceOverride *pbresource.Resource // Used to test that an empty resource of another type will not match + + Matches bool + }{ + "empty fields matches": { + OurConsulNamespace: constants.DefaultConsulNS, + OurConsulPartition: constants.DefaultConsulPartition, + OurData: &ProxyConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + }, + Spec: pbmesh.ProxyConfiguration{}, + }, + TheirName: "name", + TheirConsulNamespace: constants.DefaultConsulNS, + TheirConsulPartition: constants.DefaultConsulPartition, + TheirData: &pbmesh.ProxyConfiguration{}, + Matches: true, + }, + "all fields set matches": { + OurConsulNamespace: "consul-ns", + OurConsulPartition: "consul-partition", + OurData: &ProxyConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.ProxyConfiguration{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"prefix-1", "prefix-2"}, + Names: []string{"workload-name"}, + Filter: "first-filter", + }, + DynamicConfig: &pbmesh.DynamicConfig{ + Mode: 2, + TransparentProxy: &pbmesh.TransparentProxy{ + OutboundListenerPort: 1234, + DialedDirectly: true, + }, + MutualTlsMode: 1, + LocalConnection: map[string]*pbmesh.ConnectionConfig{ + "local": { + ConnectTimeout: &durationpb.Duration{ + Seconds: 5, + Nanos: 10, + }, + RequestTimeout: &durationpb.Duration{ + Seconds: 2, + Nanos: 10, + }, + }, + }, + InboundConnections: &pbmesh.InboundConnectionsConfig{ + MaxInboundConnections: 5, + BalanceInboundConnections: 10, + }, + MeshGatewayMode: pbmesh.MeshGatewayMode_MESH_GATEWAY_MODE_LOCAL, + ExposeConfig: &pbmesh.ExposeConfig{ + ExposePaths: []*pbmesh.ExposePath{ + { + ListenerPort: 19000, + Path: "/expose-path", + LocalPathPort: 1901, + Protocol: 2, + }, + }, + }, + AccessLogs: &pbmesh.AccessLogsConfig{ + Enabled: true, + DisableListenerLogs: true, + Type: 3, + Path: "/path", + JsonFormat: "jsonFormat", + TextFormat: "text format.", + }, + EnvoyExtensions: []*pbmesh.EnvoyExtension{ + { + Name: "extension-1", + Required: true, + Arguments: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "field-1": {}, + "field-2": {}, + }, + }, + ConsulVersion: "1.22.3-beta1", + EnvoyVersion: "1.33.4", + }, + }, + PublicListenerJson: "publicListenerJson{}", + ListenerTracingJson: "listenerTracingJson{}", + LocalClusterJson: "localClusterJson{}", + }, + BootstrapConfig: &pbmesh.BootstrapConfig{ + StatsdUrl: "statsdURL", + DogstatsdUrl: "dogstatsdURL", + StatsTags: []string{"statsTags"}, + PrometheusBindAddr: "firstBindAddr", + StatsBindAddr: "secondBindAddr", + ReadyBindAddr: "thirdBindAddr", + OverrideJsonTpl: "overrideJSON", + StaticClustersJson: "staticClusterJSON", + StaticListenersJson: "staticListenersJSON", + StatsSinksJson: "statsSinksJSON", + StatsConfigJson: "statsConfigJSON", + StatsFlushInterval: "45s", + TracingConfigJson: "tracingConfigJSON", + TelemetryCollectorBindSocketDir: "/bindSocketDir", + }, + }, + }, + TheirName: "foo", + TheirConsulNamespace: "consul-ns", + TheirConsulPartition: "consul-partition", + TheirData: &pbmesh.ProxyConfiguration{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"prefix-1", "prefix-2"}, + Names: []string{"workload-name"}, + Filter: "first-filter", + }, + DynamicConfig: &pbmesh.DynamicConfig{ + Mode: 2, + TransparentProxy: &pbmesh.TransparentProxy{ + OutboundListenerPort: 1234, + DialedDirectly: true, + }, + MutualTlsMode: 1, + LocalConnection: map[string]*pbmesh.ConnectionConfig{ + "local": { + ConnectTimeout: &durationpb.Duration{ + Seconds: 5, + Nanos: 10, + }, + RequestTimeout: &durationpb.Duration{ + Seconds: 2, + Nanos: 10, + }, + }, + }, + InboundConnections: &pbmesh.InboundConnectionsConfig{ + MaxInboundConnections: 5, + BalanceInboundConnections: 10, + }, + MeshGatewayMode: pbmesh.MeshGatewayMode_MESH_GATEWAY_MODE_LOCAL, + ExposeConfig: &pbmesh.ExposeConfig{ + ExposePaths: []*pbmesh.ExposePath{ + { + ListenerPort: 19000, + Path: "/expose-path", + LocalPathPort: 1901, + Protocol: 2, + }, + }, + }, + AccessLogs: &pbmesh.AccessLogsConfig{ + Enabled: true, + DisableListenerLogs: true, + Type: 3, + Path: "/path", + JsonFormat: "jsonFormat", + TextFormat: "text format.", + }, + EnvoyExtensions: []*pbmesh.EnvoyExtension{ + { + Name: "extension-1", + Required: true, + Arguments: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "field-1": {}, + "field-2": {}, + }, + }, + ConsulVersion: "1.22.3-beta1", + EnvoyVersion: "1.33.4", + }, + }, + PublicListenerJson: "publicListenerJson{}", + ListenerTracingJson: "listenerTracingJson{}", + LocalClusterJson: "localClusterJson{}", + }, + BootstrapConfig: &pbmesh.BootstrapConfig{ + StatsdUrl: "statsdURL", + DogstatsdUrl: "dogstatsdURL", + StatsTags: []string{"statsTags"}, + PrometheusBindAddr: "firstBindAddr", + StatsBindAddr: "secondBindAddr", + ReadyBindAddr: "thirdBindAddr", + OverrideJsonTpl: "overrideJSON", + StaticClustersJson: "staticClusterJSON", + StaticListenersJson: "staticListenersJSON", + StatsSinksJson: "statsSinksJSON", + StatsConfigJson: "statsConfigJSON", + StatsFlushInterval: "45s", + TracingConfigJson: "tracingConfigJSON", + TelemetryCollectorBindSocketDir: "/bindSocketDir", + }, + }, + Matches: true, + }, + "different types does not match": { + OurConsulNamespace: constants.DefaultConsulNS, + OurConsulPartition: constants.DefaultConsulPartition, + OurData: &ProxyConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + }, + Spec: pbmesh.ProxyConfiguration{}, + }, + ResourceOverride: &pbresource.Resource{ + Id: &pbresource.ID{ + Name: "name", + Type: pbmesh.TCPRouteType, + Tenancy: &pbresource.Tenancy{ + Partition: constants.DefaultConsulNS, + Namespace: constants.DefaultConsulPartition, + + // Because we are explicitly defining NS/partition, this will not default and must be explicit. + // At a future point, this will move out of the Tenancy block. + PeerName: constants.DefaultConsulPeer, + }, + }, + Data: inject.ToProtoAny(&pbmesh.ProxyConfiguration{}), + Metadata: meshConfigMeta(), + }, + Matches: false, + }, + } + for name, c := range cases { + t.Run(name, func(t *testing.T) { + consulResource := c.ResourceOverride + if c.TheirName != "" { + consulResource = constructProxyConfigurationResource(c.TheirData, c.TheirName, c.TheirConsulNamespace, c.TheirConsulPartition) + } + require.Equal(t, c.Matches, c.OurData.MatchesConsul(consulResource, c.OurConsulNamespace, c.OurConsulPartition)) + }) + } +} + +// TestProxyConfiguration_Resource also includes test to verify ResourceID(). +func TestProxyConfiguration_Resource(t *testing.T) { + cases := map[string]struct { + Ours *ProxyConfiguration + ConsulNamespace string + ConsulPartition string + ExpectedName string + ExpectedData *pbmesh.ProxyConfiguration + }{ + "empty fields": { + Ours: &ProxyConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: pbmesh.ProxyConfiguration{}, + }, + ConsulNamespace: constants.DefaultConsulNS, + ConsulPartition: constants.DefaultConsulPartition, + ExpectedName: "foo", + ExpectedData: &pbmesh.ProxyConfiguration{}, + }, + "every field set": { + Ours: &ProxyConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.ProxyConfiguration{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"prefix-1", "prefix-2"}, + Names: []string{"workload-name"}, + Filter: "first-filter", + }, + DynamicConfig: &pbmesh.DynamicConfig{ + Mode: 2, + TransparentProxy: &pbmesh.TransparentProxy{ + OutboundListenerPort: 1234, + DialedDirectly: true, + }, + MutualTlsMode: 1, + LocalConnection: map[string]*pbmesh.ConnectionConfig{ + "local": { + ConnectTimeout: &durationpb.Duration{ + Seconds: 5, + Nanos: 10, + }, + RequestTimeout: &durationpb.Duration{ + Seconds: 2, + Nanos: 10, + }, + }, + }, + InboundConnections: &pbmesh.InboundConnectionsConfig{ + MaxInboundConnections: 5, + BalanceInboundConnections: 10, + }, + MeshGatewayMode: pbmesh.MeshGatewayMode_MESH_GATEWAY_MODE_LOCAL, + ExposeConfig: &pbmesh.ExposeConfig{ + ExposePaths: []*pbmesh.ExposePath{ + { + ListenerPort: 19000, + Path: "/expose-path", + LocalPathPort: 1901, + Protocol: 2, + }, + }, + }, + AccessLogs: &pbmesh.AccessLogsConfig{ + Enabled: true, + DisableListenerLogs: true, + Type: 3, + Path: "/path", + JsonFormat: "jsonFormat", + TextFormat: "text format.", + }, + EnvoyExtensions: []*pbmesh.EnvoyExtension{ + { + Name: "extension-1", + Required: true, + Arguments: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "field-1": {}, + "field-2": {}, + }, + }, + ConsulVersion: "1.22.3-beta1", + EnvoyVersion: "1.33.4", + }, + }, + PublicListenerJson: "publicListenerJson{}", + ListenerTracingJson: "listenerTracingJson{}", + LocalClusterJson: "localClusterJson{}", + }, + BootstrapConfig: &pbmesh.BootstrapConfig{ + StatsdUrl: "statsdURL", + DogstatsdUrl: "dogstatsdURL", + StatsTags: []string{"statsTags"}, + PrometheusBindAddr: "firstBindAddr", + StatsBindAddr: "secondBindAddr", + ReadyBindAddr: "thirdBindAddr", + OverrideJsonTpl: "overrideJSON", + StaticClustersJson: "staticClusterJSON", + StaticListenersJson: "staticListenersJSON", + StatsSinksJson: "statsSinksJSON", + StatsConfigJson: "statsConfigJSON", + StatsFlushInterval: "45s", + TracingConfigJson: "tracingConfigJSON", + TelemetryCollectorBindSocketDir: "/bindSocketDir", + }, + }, + }, + ConsulNamespace: "not-default-namespace", + ConsulPartition: "not-default-partition", + ExpectedName: "foo", + ExpectedData: &pbmesh.ProxyConfiguration{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"prefix-1", "prefix-2"}, + Names: []string{"workload-name"}, + Filter: "first-filter", + }, + DynamicConfig: &pbmesh.DynamicConfig{ + Mode: 2, + TransparentProxy: &pbmesh.TransparentProxy{ + OutboundListenerPort: 1234, + DialedDirectly: true, + }, + MutualTlsMode: 1, + LocalConnection: map[string]*pbmesh.ConnectionConfig{ + "local": { + ConnectTimeout: &durationpb.Duration{ + Seconds: 5, + Nanos: 10, + }, + RequestTimeout: &durationpb.Duration{ + Seconds: 2, + Nanos: 10, + }, + }, + }, + InboundConnections: &pbmesh.InboundConnectionsConfig{ + MaxInboundConnections: 5, + BalanceInboundConnections: 10, + }, + MeshGatewayMode: pbmesh.MeshGatewayMode_MESH_GATEWAY_MODE_LOCAL, + ExposeConfig: &pbmesh.ExposeConfig{ + ExposePaths: []*pbmesh.ExposePath{ + { + ListenerPort: 19000, + Path: "/expose-path", + LocalPathPort: 1901, + Protocol: 2, + }, + }, + }, + AccessLogs: &pbmesh.AccessLogsConfig{ + Enabled: true, + DisableListenerLogs: true, + Type: 3, + Path: "/path", + JsonFormat: "jsonFormat", + TextFormat: "text format.", + }, + EnvoyExtensions: []*pbmesh.EnvoyExtension{ + { + Name: "extension-1", + Required: true, + Arguments: &structpb.Struct{ + Fields: map[string]*structpb.Value{ + "field-1": {}, + "field-2": {}, + }, + }, + ConsulVersion: "1.22.3-beta1", + EnvoyVersion: "1.33.4", + }, + }, + PublicListenerJson: "publicListenerJson{}", + ListenerTracingJson: "listenerTracingJson{}", + LocalClusterJson: "localClusterJson{}", + }, + BootstrapConfig: &pbmesh.BootstrapConfig{ + StatsdUrl: "statsdURL", + DogstatsdUrl: "dogstatsdURL", + StatsTags: []string{"statsTags"}, + PrometheusBindAddr: "firstBindAddr", + StatsBindAddr: "secondBindAddr", + ReadyBindAddr: "thirdBindAddr", + OverrideJsonTpl: "overrideJSON", + StaticClustersJson: "staticClusterJSON", + StaticListenersJson: "staticListenersJSON", + StatsSinksJson: "statsSinksJSON", + StatsConfigJson: "statsConfigJSON", + StatsFlushInterval: "45s", + TracingConfigJson: "tracingConfigJSON", + TelemetryCollectorBindSocketDir: "/bindSocketDir", + }, + }, + }, + } + for name, c := range cases { + t.Run(name, func(t *testing.T) { + actual := c.Ours.Resource(c.ConsulNamespace, c.ConsulPartition) + expected := constructProxyConfigurationResource(c.ExpectedData, c.ExpectedName, c.ConsulNamespace, c.ConsulPartition) + + opts := append([]cmp.Option{ + protocmp.IgnoreFields(&pbresource.Resource{}, "status", "generation", "version"), + protocmp.IgnoreFields(&pbresource.ID{}, "uid"), + }, test.CmpProtoIgnoreOrder()...) + diff := cmp.Diff(expected, actual, opts...) + require.Equal(t, "", diff, "ProxyConfiguration do not match") + }) + } +} + +func TestProxyConfiguration_SetSyncedCondition(t *testing.T) { + trafficPermissions := &ProxyConfiguration{} + trafficPermissions.SetSyncedCondition(corev1.ConditionTrue, "reason", "message") + + require.Equal(t, corev1.ConditionTrue, trafficPermissions.Status.Conditions[0].Status) + require.Equal(t, "reason", trafficPermissions.Status.Conditions[0].Reason) + require.Equal(t, "message", trafficPermissions.Status.Conditions[0].Message) + now := metav1.Now() + require.True(t, trafficPermissions.Status.Conditions[0].LastTransitionTime.Before(&now)) + require.True(t, trafficPermissions.Status.Conditions[0].LastTransitionTime.Before(&now)) +} + +func TestProxyConfiguration_SetLastSyncedTime(t *testing.T) { + trafficPermissions := &ProxyConfiguration{} + syncedTime := metav1.NewTime(time.Now()) + trafficPermissions.SetLastSyncedTime(&syncedTime) + + require.Equal(t, &syncedTime, trafficPermissions.Status.LastSyncedTime) +} + +func TestProxyConfiguration_GetSyncedConditionStatus(t *testing.T) { + cases := []corev1.ConditionStatus{ + corev1.ConditionUnknown, + corev1.ConditionFalse, + corev1.ConditionTrue, + } + for _, status := range cases { + t.Run(string(status), func(t *testing.T) { + trafficPermissions := &ProxyConfiguration{ + Status: Status{ + Conditions: []Condition{{ + Type: ConditionSynced, + Status: status, + }}, + }, + } + + require.Equal(t, status, trafficPermissions.SyncedConditionStatus()) + }) + } +} + +func TestProxyConfiguration_GetConditionWhenStatusNil(t *testing.T) { + require.Nil(t, (&ProxyConfiguration{}).GetCondition(ConditionSynced)) +} + +func TestProxyConfiguration_SyncedConditionStatusWhenStatusNil(t *testing.T) { + require.Equal(t, corev1.ConditionUnknown, (&ProxyConfiguration{}).SyncedConditionStatus()) +} + +func TestProxyConfiguration_SyncedConditionWhenStatusNil(t *testing.T) { + status, reason, message := (&ProxyConfiguration{}).SyncedCondition() + require.Equal(t, corev1.ConditionUnknown, status) + require.Equal(t, "", reason) + require.Equal(t, "", message) +} + +func TestProxyConfiguration_KubeKind(t *testing.T) { + require.Equal(t, "proxyconfiguration", (&ProxyConfiguration{}).KubeKind()) +} + +func TestProxyConfiguration_KubernetesName(t *testing.T) { + require.Equal(t, "test", (&ProxyConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "bar", + }, + Spec: pbmesh.ProxyConfiguration{}, + }).KubernetesName()) +} + +func TestProxyConfiguration_ObjectMeta(t *testing.T) { + meta := metav1.ObjectMeta{ + Name: "name", + Namespace: "namespace", + } + trafficPermissions := &ProxyConfiguration{ + ObjectMeta: meta, + } + require.Equal(t, &meta, trafficPermissions.GetObjectMeta()) +} + +// Test defaulting behavior when namespaces are enabled as well as disabled. +// TODO: add when implemented +//func TestProxyConfiguration_DefaultNamespaceFields(t *testing.T) + +func constructProxyConfigurationResource(tp *pbmesh.ProxyConfiguration, name, namespace, partition string) *pbresource.Resource { + data := inject.ToProtoAny(tp) + + id := &pbresource.ID{ + Name: name, + Type: pbmesh.ProxyConfigurationType, + Tenancy: &pbresource.Tenancy{ + Partition: partition, + Namespace: namespace, + + // Because we are explicitly defining NS/partition, this will not default and must be explicit. + // At a future point, this will move out of the Tenancy block. + PeerName: constants.DefaultConsulPeer, + }, + Uid: "ABCD", // We add this to show it does not factor into the comparison + } + + return &pbresource.Resource{ + Id: id, + Data: data, + Metadata: meshConfigMeta(), + + // We add the fields below to prove that they are not used in the Match when comparing the CRD to Consul. + Version: "123456", + Generation: "01ARZ3NDEKTSV4RRFFQ69G5FAV", + Status: map[string]*pbresource.Status{ + "knock": { + ObservedGeneration: "01ARZ3NDEKTSV4RRFFQ69G5FAV", + Conditions: make([]*pbresource.Condition, 0), + UpdatedAt: timestamppb.Now(), + }, + }, + } +} diff --git a/control-plane/api/mesh/v2beta1/shared_types.go b/control-plane/api/mesh/v2beta1/shared_types.go new file mode 100644 index 0000000000..a5225afb71 --- /dev/null +++ b/control-plane/api/mesh/v2beta1/shared_types.go @@ -0,0 +1,14 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package v2beta1 + +import ( + "github.com/hashicorp/consul-k8s/control-plane/api/common" +) + +func meshConfigMeta() map[string]string { + return map[string]string{ + common.SourceKey: common.SourceValue, + } +} diff --git a/control-plane/api/mesh/v2beta1/status.go b/control-plane/api/mesh/v2beta1/status.go new file mode 100644 index 0000000000..cc75a1cd82 --- /dev/null +++ b/control-plane/api/mesh/v2beta1/status.go @@ -0,0 +1,93 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package v2beta1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// Conditions is the schema for the conditions portion of the payload. +type Conditions []Condition + +// ConditionType is a camel-cased condition type. +type ConditionType string + +const ( + // ConditionSynced specifies that the resource has been synced with Consul. + ConditionSynced ConditionType = "Synced" +) + +// Conditions define a readiness condition for a Consul resource. +// See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties +// +k8s:deepcopy-gen=true +// +k8s:openapi-gen=true +type Condition struct { + // Type of condition. + // +required + Type ConditionType `json:"type" description:"type of status condition"` + + // Status of the condition, one of True, False, Unknown. + // +required + Status corev1.ConditionStatus `json:"status" description:"status of the condition, one of True, False, Unknown"` + + // LastTransitionTime is the last time the condition transitioned from one status to another. + // +optional + LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty" description:"last time the condition transitioned from one status to another"` + + // The reason for the condition's last transition. + // +optional + Reason string `json:"reason,omitempty" description:"one-word CamelCase reason for the condition's last transition"` + + // A human readable message indicating details about the transition. + // +optional + Message string `json:"message,omitempty" description:"human-readable message indicating details about last transition"` +} + +// IsTrue is true if the condition is True. +func (c *Condition) IsTrue() bool { + if c == nil { + return false + } + return c.Status == corev1.ConditionTrue +} + +// IsFalse is true if the condition is False. +func (c *Condition) IsFalse() bool { + if c == nil { + return false + } + return c.Status == corev1.ConditionFalse +} + +// IsUnknown is true if the condition is Unknown. +func (c *Condition) IsUnknown() bool { + if c == nil { + return true + } + return c.Status == corev1.ConditionUnknown +} + +// +k8s:deepcopy-gen=true +// +k8s:openapi-gen=true +type Status struct { + // Conditions indicate the latest available observations of a resource's current state. + // +optional + // +patchMergeKey=type + // +patchStrategy=merge + Conditions Conditions `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` + + // LastSyncedTime is the last time the resource successfully synced with Consul. + // +optional + LastSyncedTime *metav1.Time `json:"lastSyncedTime,omitempty" description:"last time the condition transitioned from one status to another"` +} + +func (s *Status) GetCondition(t ConditionType) *Condition { + for _, cond := range s.Conditions { + if cond.Type == t { + return &cond + } + } + return nil +} diff --git a/control-plane/api/mesh/v2beta1/tcp_route_types.go b/control-plane/api/mesh/v2beta1/tcp_route_types.go new file mode 100644 index 0000000000..207ef7afb3 --- /dev/null +++ b/control-plane/api/mesh/v2beta1/tcp_route_types.go @@ -0,0 +1,195 @@ +// // Copyright (c) HashiCorp, Inc. +// // SPDX-License-Identifier: MPL-2.0 +package v2beta1 + +import ( + "fmt" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v2beta1" + "github.com/hashicorp/consul/proto-public/pbresource" + "google.golang.org/protobuf/testing/protocmp" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + + "github.com/hashicorp/consul-k8s/control-plane/api/common" + inject "github.com/hashicorp/consul-k8s/control-plane/connect-inject/common" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" +) + +const ( + tcpRouteKubeKind = "tcproute" +) + +func init() { + MeshSchemeBuilder.Register(&TCPRoute{}, &TCPRouteList{}) +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status + +// TCPRoute is the Schema for the TCP Route API +// +kubebuilder:printcolumn:name="Synced",type="string",JSONPath=".status.conditions[?(@.type==\"Synced\")].status",description="The sync status of the resource with Consul" +// +kubebuilder:printcolumn:name="Last Synced",type="date",JSONPath=".status.lastSyncedTime",description="The last successful synced time of the resource with Consul" +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="The age of the resource" +// +kubebuilder:resource:shortName="tcp-route" +type TCPRoute struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec pbmesh.TCPRoute `json:"spec,omitempty"` + Status `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// TCPRouteList contains a list of TCPRoute. +type TCPRouteList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []*TCPRoute `json:"items"` +} + +func (in *TCPRoute) ResourceID(namespace, partition string) *pbresource.ID { + return &pbresource.ID{ + Name: in.Name, + Type: pbmesh.TCPRouteType, + Tenancy: &pbresource.Tenancy{ + Partition: partition, + Namespace: namespace, + + // Because we are explicitly defining NS/partition, this will not default and must be explicit. + // At a future point, this will move out of the Tenancy block. + PeerName: constants.DefaultConsulPeer, + }, + } +} + +func (in *TCPRoute) Resource(namespace, partition string) *pbresource.Resource { + return &pbresource.Resource{ + Id: in.ResourceID(namespace, partition), + Data: inject.ToProtoAny(&in.Spec), + Metadata: meshConfigMeta(), + } +} + +func (in *TCPRoute) AddFinalizer(f string) { + in.ObjectMeta.Finalizers = append(in.Finalizers(), f) +} + +func (in *TCPRoute) RemoveFinalizer(f string) { + var newFinalizers []string + for _, oldF := range in.Finalizers() { + if oldF != f { + newFinalizers = append(newFinalizers, oldF) + } + } + in.ObjectMeta.Finalizers = newFinalizers +} + +func (in *TCPRoute) Finalizers() []string { + return in.ObjectMeta.Finalizers +} + +func (in *TCPRoute) MatchesConsul(candidate *pbresource.Resource, namespace, partition string) bool { + return cmp.Equal( + in.Resource(namespace, partition), + candidate, + protocmp.IgnoreFields(&pbresource.Resource{}, "status", "generation", "version"), + protocmp.IgnoreFields(&pbresource.ID{}, "uid"), + protocmp.Transform(), + cmpopts.SortSlices(func(a, b any) bool { return fmt.Sprintf("%v", a) < fmt.Sprintf("%v", b) }), + ) +} + +func (in *TCPRoute) KubeKind() string { + return tcpRouteKubeKind +} + +func (in *TCPRoute) KubernetesName() string { + return in.ObjectMeta.Name +} + +func (in *TCPRoute) SetSyncedCondition(status corev1.ConditionStatus, reason, message string) { + in.Status.Conditions = Conditions{ + { + Type: ConditionSynced, + Status: status, + LastTransitionTime: metav1.Now(), + Reason: reason, + Message: message, + }, + } +} + +func (in *TCPRoute) SetLastSyncedTime(time *metav1.Time) { + in.Status.LastSyncedTime = time +} + +func (in *TCPRoute) SyncedCondition() (status corev1.ConditionStatus, reason, message string) { + cond := in.Status.GetCondition(ConditionSynced) + if cond == nil { + return corev1.ConditionUnknown, "", "" + } + return cond.Status, cond.Reason, cond.Message +} + +func (in *TCPRoute) SyncedConditionStatus() corev1.ConditionStatus { + condition := in.Status.GetCondition(ConditionSynced) + if condition == nil { + return corev1.ConditionUnknown + } + return condition.Status +} + +func (in *TCPRoute) Validate(tenancy common.ConsulTenancyConfig) error { + var errs field.ErrorList + var route pbmesh.TCPRoute + path := field.NewPath("spec") + res := in.Resource(tenancy.ConsulDestinationNamespace, tenancy.ConsulPartition) + + if err := res.Data.UnmarshalTo(&route); err != nil { + return fmt.Errorf("error parsing resource data as type %q: %s", &route, err) + } + + if len(route.ParentRefs) == 0 { + errs = append(errs, field.Required(path.Child("parentRefs"), "cannot be empty")) + } + + if len(route.Rules) > 1 { + errs = append(errs, field.Invalid(path.Child("rules"), route.Rules, "must only specify a single rule for now")) + } + + for i, rule := range route.Rules { + rulePath := path.Child("rules").Index(i) + + if len(rule.BackendRefs) == 0 { + errs = append(errs, field.Required(rulePath.Child("backendRefs"), "cannot be empty")) + } + for j, hbref := range rule.BackendRefs { + ruleBackendRefsPath := rulePath.Child("backendRefs").Index(j) + if hbref.BackendRef == nil { + errs = append(errs, field.Required(ruleBackendRefsPath.Child("backendRef"), "missing required field")) + continue + } + + if hbref.BackendRef.Datacenter != "" { + errs = append(errs, field.Invalid(ruleBackendRefsPath.Child("backendRef").Child("datacenter"), hbref.BackendRef.Datacenter, "datacenter is not yet supported on backend refs")) + } + } + } + + if len(errs) > 0 { + return apierrors.NewInvalid( + schema.GroupKind{Group: MeshGroup, Kind: common.TCPRoute}, + in.KubernetesName(), errs) + } + return nil +} + +// DefaultNamespaceFields is required as part of the common.MeshConfig interface. +func (in *TCPRoute) DefaultNamespaceFields(tenancy common.ConsulTenancyConfig) {} diff --git a/control-plane/api/mesh/v2beta1/tcp_route_types_test.go b/control-plane/api/mesh/v2beta1/tcp_route_types_test.go new file mode 100644 index 0000000000..9bedc58293 --- /dev/null +++ b/control-plane/api/mesh/v2beta1/tcp_route_types_test.go @@ -0,0 +1,577 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package v2beta1 + +import ( + "testing" + "time" + + "github.com/google/go-cmp/cmp" + pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v2beta1" + "github.com/hashicorp/consul/proto-public/pbresource" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/testing/protocmp" + "google.golang.org/protobuf/types/known/timestamppb" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/hashicorp/consul-k8s/control-plane/api/common" + inject "github.com/hashicorp/consul-k8s/control-plane/connect-inject/common" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" + "github.com/hashicorp/consul-k8s/control-plane/helper/test" +) + +func TestTCPRoute_MatchesConsul(t *testing.T) { + cases := map[string]struct { + OurConsulNamespace string + OurConsulPartition string + OurData *TCPRoute + + TheirName string + TheirConsulNamespace string + TheirConsulPartition string + TheirData *pbmesh.TCPRoute + ResourceOverride *pbresource.Resource // Used to test that an empty resource of another type will not match + + Matches bool + }{ + "empty fields matches": { + OurConsulNamespace: constants.DefaultConsulNS, + OurConsulPartition: constants.DefaultConsulPartition, + OurData: &TCPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + }, + Spec: pbmesh.TCPRoute{}, + }, + TheirName: "name", + TheirConsulNamespace: constants.DefaultConsulNS, + TheirConsulPartition: constants.DefaultConsulPartition, + TheirData: &pbmesh.TCPRoute{}, + Matches: true, + }, + "all fields set matches": { + OurConsulNamespace: "consul-ns", + OurConsulPartition: "consul-partition", + OurData: &TCPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.TCPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + { + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "some-partition", + Namespace: "some-namespace", + }, + Name: "parent-name", + Section: "parent-section", + }, + Port: "20122", + }, + }, + Rules: []*pbmesh.TCPRouteRule{ + { + BackendRefs: []*pbmesh.TCPBackendRef{ + { + BackendRef: &pbmesh.BackendReference{ + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Namespace: "another-namespace", + PeerName: "another-peer", + }, + Name: "backend-name", + Section: "backend-section", + }, + Port: "20111", + Datacenter: "different-datacenter", + }, + Weight: 50, + }, + }, + }, + }, + }, + }, + TheirName: "foo", + TheirConsulNamespace: "consul-ns", + TheirConsulPartition: "consul-partition", + TheirData: &pbmesh.TCPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + { + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "some-partition", + Namespace: "some-namespace", + }, + Name: "parent-name", + Section: "parent-section", + }, + Port: "20122", + }, + }, + Rules: []*pbmesh.TCPRouteRule{ + { + BackendRefs: []*pbmesh.TCPBackendRef{ + { + BackendRef: &pbmesh.BackendReference{ + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Namespace: "another-namespace", + PeerName: "another-peer", + }, + Name: "backend-name", + Section: "backend-section", + }, + Port: "20111", + Datacenter: "different-datacenter", + }, + Weight: 50, + }, + }, + }, + }, + }, + Matches: true, + }, + "different types does not match": { + OurConsulNamespace: constants.DefaultConsulNS, + OurConsulPartition: constants.DefaultConsulPartition, + OurData: &TCPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + }, + Spec: pbmesh.TCPRoute{}, + }, + ResourceOverride: &pbresource.Resource{ + Id: &pbresource.ID{ + Name: "name", + Type: pbmesh.ProxyConfigurationType, + Tenancy: &pbresource.Tenancy{ + Partition: constants.DefaultConsulNS, + Namespace: constants.DefaultConsulPartition, + + // Because we are explicitly defining NS/partition, this will not default and must be explicit. + // At a future point, this will move out of the Tenancy block. + PeerName: constants.DefaultConsulPeer, + }, + }, + Data: inject.ToProtoAny(&pbmesh.ProxyConfiguration{}), + Metadata: meshConfigMeta(), + }, + Matches: false, + }, + } + for name, c := range cases { + t.Run(name, func(t *testing.T) { + consulResource := c.ResourceOverride + if c.TheirName != "" { + consulResource = constructTCPRouteResource(c.TheirData, c.TheirName, c.TheirConsulNamespace, c.TheirConsulPartition) + } + require.Equal(t, c.Matches, c.OurData.MatchesConsul(consulResource, c.OurConsulNamespace, c.OurConsulPartition)) + }) + } +} + +// TestTCPRoute_Resource also includes test to verify ResourceID(). +func TestTCPRoute_Resource(t *testing.T) { + cases := map[string]struct { + Ours *TCPRoute + ConsulNamespace string + ConsulPartition string + ExpectedName string + ExpectedData *pbmesh.TCPRoute + }{ + "empty fields": { + Ours: &TCPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: pbmesh.TCPRoute{}, + }, + ConsulNamespace: constants.DefaultConsulNS, + ConsulPartition: constants.DefaultConsulPartition, + ExpectedName: "foo", + ExpectedData: &pbmesh.TCPRoute{}, + }, + "every field set": { + Ours: &TCPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.TCPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + { + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "some-partition", + Namespace: "some-namespace", + }, + Name: "parent-name", + Section: "parent-section", + }, + Port: "20122", + }, + }, + Rules: []*pbmesh.TCPRouteRule{ + { + BackendRefs: []*pbmesh.TCPBackendRef{ + { + BackendRef: &pbmesh.BackendReference{ + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Namespace: "another-namespace", + PeerName: "another-peer", + }, + Name: "backend-name", + Section: "backend-section", + }, + Port: "20111", + Datacenter: "different-datacenter", + }, + Weight: 50, + }, + }, + }, + }, + }, + }, + ConsulNamespace: "not-default-namespace", + ConsulPartition: "not-default-partition", + ExpectedName: "foo", + ExpectedData: &pbmesh.TCPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + { + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "some-partition", + Namespace: "some-namespace", + }, + Name: "parent-name", + Section: "parent-section", + }, + Port: "20122", + }, + }, + Rules: []*pbmesh.TCPRouteRule{ + { + BackendRefs: []*pbmesh.TCPBackendRef{ + { + BackendRef: &pbmesh.BackendReference{ + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Namespace: "another-namespace", + PeerName: "another-peer", + }, + Name: "backend-name", + Section: "backend-section", + }, + Port: "20111", + Datacenter: "different-datacenter", + }, + Weight: 50, + }, + }, + }, + }, + }, + }, + } + for name, c := range cases { + t.Run(name, func(t *testing.T) { + actual := c.Ours.Resource(c.ConsulNamespace, c.ConsulPartition) + expected := constructTCPRouteResource(c.ExpectedData, c.ExpectedName, c.ConsulNamespace, c.ConsulPartition) + + opts := append([]cmp.Option{ + protocmp.IgnoreFields(&pbresource.Resource{}, "status", "generation", "version"), + protocmp.IgnoreFields(&pbresource.ID{}, "uid"), + }, test.CmpProtoIgnoreOrder()...) + diff := cmp.Diff(expected, actual, opts...) + require.Equal(t, "", diff, "TCPRoute do not match") + }) + } +} + +func TestTCPRoute_SetSyncedCondition(t *testing.T) { + trafficPermissions := &TCPRoute{} + trafficPermissions.SetSyncedCondition(corev1.ConditionTrue, "reason", "message") + + require.Equal(t, corev1.ConditionTrue, trafficPermissions.Status.Conditions[0].Status) + require.Equal(t, "reason", trafficPermissions.Status.Conditions[0].Reason) + require.Equal(t, "message", trafficPermissions.Status.Conditions[0].Message) + now := metav1.Now() + require.True(t, trafficPermissions.Status.Conditions[0].LastTransitionTime.Before(&now)) +} + +func TestTCPRoute_SetLastSyncedTime(t *testing.T) { + trafficPermissions := &TCPRoute{} + syncedTime := metav1.NewTime(time.Now()) + trafficPermissions.SetLastSyncedTime(&syncedTime) + + require.Equal(t, &syncedTime, trafficPermissions.Status.LastSyncedTime) +} + +func TestTCPRoute_GetSyncedConditionStatus(t *testing.T) { + cases := []corev1.ConditionStatus{ + corev1.ConditionUnknown, + corev1.ConditionFalse, + corev1.ConditionTrue, + } + for _, status := range cases { + t.Run(string(status), func(t *testing.T) { + trafficPermissions := &TCPRoute{ + Status: Status{ + Conditions: []Condition{{ + Type: ConditionSynced, + Status: status, + }}, + }, + } + + require.Equal(t, status, trafficPermissions.SyncedConditionStatus()) + }) + } +} + +func TestTCPRoute_GetConditionWhenStatusNil(t *testing.T) { + require.Nil(t, (&TCPRoute{}).GetCondition(ConditionSynced)) +} + +func TestTCPRoute_SyncedConditionStatusWhenStatusNil(t *testing.T) { + require.Equal(t, corev1.ConditionUnknown, (&TCPRoute{}).SyncedConditionStatus()) +} + +func TestTCPRoute_SyncedConditionWhenStatusNil(t *testing.T) { + status, reason, message := (&TCPRoute{}).SyncedCondition() + require.Equal(t, corev1.ConditionUnknown, status) + require.Equal(t, "", reason) + require.Equal(t, "", message) +} + +func TestTCPRoute_KubeKind(t *testing.T) { + require.Equal(t, "tcproute", (&TCPRoute{}).KubeKind()) +} + +func TestTCPRoute_KubernetesName(t *testing.T) { + require.Equal(t, "test", (&TCPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "bar", + }, + Spec: pbmesh.TCPRoute{}, + }).KubernetesName()) +} + +func TestTCPRoute_ObjectMeta(t *testing.T) { + meta := metav1.ObjectMeta{ + Name: "name", + Namespace: "namespace", + } + trafficPermissions := &TCPRoute{ + ObjectMeta: meta, + } + require.Equal(t, &meta, trafficPermissions.GetObjectMeta()) +} + +// Test defaulting behavior when namespaces are enabled as well as disabled. +// TODO: add when implemented +//func TestTCPRoute_DefaultNamespaceFields(t *testing.T) + +func TestTCPRoute_Validate(t *testing.T) { + cases := []struct { + name string + input *TCPRoute + expectedErrMsgs []string + }{ + { + name: "kitchen sink OK", + input: &TCPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.TCPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + { + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Partition: "some-partition", + Namespace: "some-namespace", + }, + Name: "parent-name", + Section: "parent-section", + }, + Port: "20122", + }, + }, + Rules: []*pbmesh.TCPRouteRule{ + { + BackendRefs: []*pbmesh.TCPBackendRef{ + { + BackendRef: &pbmesh.BackendReference{ + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + Tenancy: &pbresource.Tenancy{ + Namespace: "another-namespace", + PeerName: "another-peer", + }, + Name: "backend-name", + Section: "backend-section", + }, + Port: "20111", + }, + Weight: 50, + }, + }, + }, + }, + }, + }, + expectedErrMsgs: nil, + }, + { + name: "no parentRefs", + input: &TCPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.TCPRoute{ + ParentRefs: []*pbmesh.ParentReference{}, + }, + }, + expectedErrMsgs: []string{ + `spec.parentRefs: Required value: cannot be empty`, + }, + }, + { + name: "multiple rules", + input: &TCPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.TCPRoute{ + ParentRefs: []*pbmesh.ParentReference{{}}, + Rules: []*pbmesh.TCPRouteRule{ + {BackendRefs: []*pbmesh.TCPBackendRef{{BackendRef: &pbmesh.BackendReference{}}}}, + {BackendRefs: []*pbmesh.TCPBackendRef{{BackendRef: &pbmesh.BackendReference{}}}}, + }, + }, + }, + expectedErrMsgs: []string{ + `must only specify a single rule for now`, + }, + }, + { + name: "rules.backendRefs", + input: &TCPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.TCPRoute{ + ParentRefs: []*pbmesh.ParentReference{{}}, + Rules: []*pbmesh.TCPRouteRule{ + {BackendRefs: []*pbmesh.TCPBackendRef{}}, + }, + }, + }, + expectedErrMsgs: []string{ + `spec.rules[0].backendRefs: Required value: cannot be empty`, + }, + }, + { + name: "rules.backendRefs.backendRef", + input: &TCPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-ns", + }, + Spec: pbmesh.TCPRoute{ + ParentRefs: []*pbmesh.ParentReference{{}}, + Rules: []*pbmesh.TCPRouteRule{ + { + BackendRefs: []*pbmesh.TCPBackendRef{ + {}, + { + BackendRef: &pbmesh.BackendReference{ + Ref: &pbresource.Reference{ + Type: pbmesh.ComputedRoutesType, + }, + Datacenter: "backend-datacenter", + }, + }, + }, + }, + }, + }, + }, + expectedErrMsgs: []string{ + `spec.rules[0].backendRefs[0].backendRef: Required value: missing required field`, + `spec.rules[0].backendRefs[1].backendRef.datacenter: Invalid value: "backend-datacenter": datacenter is not yet supported on backend refs`, + }, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + err := tc.input.Validate(common.ConsulTenancyConfig{}) + if len(tc.expectedErrMsgs) != 0 { + require.Error(t, err) + for _, s := range tc.expectedErrMsgs { + require.Contains(t, err.Error(), s) + } + } else { + require.NoError(t, err) + } + }) + } +} + +func constructTCPRouteResource(tp *pbmesh.TCPRoute, name, namespace, partition string) *pbresource.Resource { + data := inject.ToProtoAny(tp) + + id := &pbresource.ID{ + Name: name, + Type: pbmesh.TCPRouteType, + Tenancy: &pbresource.Tenancy{ + Partition: partition, + Namespace: namespace, + + // Because we are explicitly defining NS/partition, this will not default and must be explicit. + // At a future point, this will move out of the Tenancy block. + PeerName: constants.DefaultConsulPeer, + }, + Uid: "ABCD", // We add this to show it does not factor into the comparison + } + + return &pbresource.Resource{ + Id: id, + Data: data, + Metadata: meshConfigMeta(), + + // We add the fields below to prove that they are not used in the Match when comparing the CRD to Consul. + Version: "123456", + Generation: "01ARZ3NDEKTSV4RRFFQ69G5FAV", + Status: map[string]*pbresource.Status{ + "knock": { + ObservedGeneration: "01ARZ3NDEKTSV4RRFFQ69G5FAV", + Conditions: make([]*pbresource.Condition, 0), + UpdatedAt: timestamppb.Now(), + }, + }, + } +} diff --git a/control-plane/api/mesh/v2beta1/tcp_route_webhook.go b/control-plane/api/mesh/v2beta1/tcp_route_webhook.go new file mode 100644 index 0000000000..68e72c6e35 --- /dev/null +++ b/control-plane/api/mesh/v2beta1/tcp_route_webhook.go @@ -0,0 +1,65 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package v2beta1 + +import ( + "context" + "net/http" + + "github.com/go-logr/logr" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/hashicorp/consul-k8s/control-plane/api/common" +) + +// +kubebuilder:object:generate=false + +type TCPRouteWebhook struct { + Logger logr.Logger + + // ConsulTenancyConfig contains the injector's namespace and partition configuration. + ConsulTenancyConfig common.ConsulTenancyConfig + + decoder *admission.Decoder + client.Client +} + +var _ common.MeshConfigLister = &TCPRouteWebhook{} + +// NOTE: The path value in the below line is the path to the webhook. +// If it is updated, run code-gen, update subcommand/inject-connect/command.go +// and the consul-helm value for the path to the webhook. +// +// NOTE: The below line cannot be combined with any other comment. If it is it will break the code generation. +// +// +kubebuilder:webhook:verbs=create;update,path=/mutate-v2beta1-tcproute,mutating=true,failurePolicy=fail,groups=auth.consul.hashicorp.com,resources=tcproute,versions=v2beta1,name=mutate-tcproute.auth.consul.hashicorp.com,sideEffects=None,admissionReviewVersions=v1beta1;v1 + +func (v *TCPRouteWebhook) Handle(ctx context.Context, req admission.Request) admission.Response { + var resource TCPRoute + err := v.decoder.Decode(req, &resource) + if err != nil { + return admission.Errored(http.StatusBadRequest, err) + } + + return common.ValidateMeshConfig(ctx, req, v.Logger, v, &resource, v.ConsulTenancyConfig) +} + +func (v *TCPRouteWebhook) List(ctx context.Context) ([]common.MeshConfig, error) { + var resourceList TCPRouteList + if err := v.Client.List(ctx, &resourceList); err != nil { + return nil, err + } + var entries []common.MeshConfig + for _, item := range resourceList.Items { + entries = append(entries, common.MeshConfig(item)) + } + return entries, nil +} + +func (v *TCPRouteWebhook) InjectDecoder(d *admission.Decoder) error { + v.decoder = d + return nil +} diff --git a/control-plane/api/mesh/v2beta1/zz_generated.deepcopy.go b/control-plane/api/mesh/v2beta1/zz_generated.deepcopy.go new file mode 100644 index 0000000000..2d7aadbee7 --- /dev/null +++ b/control-plane/api/mesh/v2beta1/zz_generated.deepcopy.go @@ -0,0 +1,325 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +// Code generated by controller-gen. DO NOT EDIT. + +package v2beta1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Condition) DeepCopyInto(out *Condition) { + *out = *in + in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Condition. +func (in *Condition) DeepCopy() *Condition { + if in == nil { + return nil + } + out := new(Condition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in Conditions) DeepCopyInto(out *Conditions) { + { + in := &in + *out = make(Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Conditions. +func (in Conditions) DeepCopy() Conditions { + if in == nil { + return nil + } + out := new(Conditions) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GRPCRoute) DeepCopyInto(out *GRPCRoute) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GRPCRoute. +func (in *GRPCRoute) DeepCopy() *GRPCRoute { + if in == nil { + return nil + } + out := new(GRPCRoute) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *GRPCRoute) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GRPCRouteList) DeepCopyInto(out *GRPCRouteList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]*GRPCRoute, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(GRPCRoute) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GRPCRouteList. +func (in *GRPCRouteList) DeepCopy() *GRPCRouteList { + if in == nil { + return nil + } + out := new(GRPCRouteList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *GRPCRouteList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPRoute) DeepCopyInto(out *HTTPRoute) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRoute. +func (in *HTTPRoute) DeepCopy() *HTTPRoute { + if in == nil { + return nil + } + out := new(HTTPRoute) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *HTTPRoute) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPRouteList) DeepCopyInto(out *HTTPRouteList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]*HTTPRoute, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(HTTPRoute) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRouteList. +func (in *HTTPRouteList) DeepCopy() *HTTPRouteList { + if in == nil { + return nil + } + out := new(HTTPRouteList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *HTTPRouteList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyConfiguration) DeepCopyInto(out *ProxyConfiguration) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyConfiguration. +func (in *ProxyConfiguration) DeepCopy() *ProxyConfiguration { + if in == nil { + return nil + } + out := new(ProxyConfiguration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ProxyConfiguration) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyConfigurationList) DeepCopyInto(out *ProxyConfigurationList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]*ProxyConfiguration, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(ProxyConfiguration) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyConfigurationList. +func (in *ProxyConfigurationList) DeepCopy() *ProxyConfigurationList { + if in == nil { + return nil + } + out := new(ProxyConfigurationList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ProxyConfigurationList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Status) DeepCopyInto(out *Status) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.LastSyncedTime != nil { + in, out := &in.LastSyncedTime, &out.LastSyncedTime + *out = (*in).DeepCopy() + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Status. +func (in *Status) DeepCopy() *Status { + if in == nil { + return nil + } + out := new(Status) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TCPRoute) DeepCopyInto(out *TCPRoute) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPRoute. +func (in *TCPRoute) DeepCopy() *TCPRoute { + if in == nil { + return nil + } + out := new(TCPRoute) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TCPRoute) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TCPRouteList) DeepCopyInto(out *TCPRouteList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]*TCPRoute, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(TCPRoute) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPRouteList. +func (in *TCPRouteList) DeepCopy() *TCPRouteList { + if in == nil { + return nil + } + out := new(TCPRouteList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TCPRouteList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} diff --git a/control-plane/api/v1alpha1/jwtprovider_types.go b/control-plane/api/v1alpha1/jwtprovider_types.go index 8e80ece1dc..a38a1df0a7 100644 --- a/control-plane/api/v1alpha1/jwtprovider_types.go +++ b/control-plane/api/v1alpha1/jwtprovider_types.go @@ -7,11 +7,9 @@ import ( "encoding/base64" "encoding/json" "net/url" - "time" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - "github.com/hashicorp/consul-k8s/control-plane/api/common" "github.com/hashicorp/consul/api" capi "github.com/hashicorp/consul/api" corev1 "k8s.io/api/core/v1" @@ -19,6 +17,8 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" + + "github.com/hashicorp/consul-k8s/control-plane/api/common" ) const ( @@ -394,7 +394,7 @@ type RemoteJWKS struct { // should be expired. // // Default value is 5 minutes. - CacheDuration time.Duration `json:"cacheDuration,omitempty"` + CacheDuration metav1.Duration `json:"cacheDuration,omitempty"` // FetchAsynchronously indicates that the JWKS should be fetched // when a client request arrives. Client requests will be paused @@ -421,7 +421,7 @@ func (r *RemoteJWKS) toConsul() *capi.RemoteJWKS { return &capi.RemoteJWKS{ URI: r.URI, RequestTimeoutMs: r.RequestTimeoutMs, - CacheDuration: r.CacheDuration, + CacheDuration: r.CacheDuration.Duration, FetchAsynchronously: r.FetchAsynchronously, RetryPolicy: r.RetryPolicy.toConsul(), JWKSCluster: r.JWKSCluster.toConsul(), @@ -462,7 +462,7 @@ type JWKSCluster struct { // The timeout for new network connections to hosts in the cluster. // If not set, a default value of 5s will be used. - ConnectTimeout time.Duration `json:"connectTimeout,omitempty"` + ConnectTimeout metav1.Duration `json:"connectTimeout,omitempty"` } func (c *JWKSCluster) toConsul() *capi.JWKSCluster { @@ -472,7 +472,7 @@ func (c *JWKSCluster) toConsul() *capi.JWKSCluster { return &capi.JWKSCluster{ DiscoveryType: c.DiscoveryType.toConsul(), TLSCertificates: c.TLSCertificates.toConsul(), - ConnectTimeout: c.ConnectTimeout, + ConnectTimeout: c.ConnectTimeout.Duration, } } @@ -663,13 +663,13 @@ type RetryPolicyBackOff struct { // BaseInterval to be used for the next back off computation. // // The default value from envoy is 1s. - BaseInterval time.Duration `json:"baseInterval,omitempty"` + BaseInterval metav1.Duration `json:"baseInterval,omitempty"` // MaxInternal to be used to specify the maximum interval between retries. // Optional but should be greater or equal to BaseInterval. // // Defaults to 10 times BaseInterval. - MaxInterval time.Duration `json:"maxInterval,omitempty"` + MaxInterval metav1.Duration `json:"maxInterval,omitempty"` } func (r *RetryPolicyBackOff) toConsul() *capi.RetryPolicyBackOff { @@ -677,8 +677,8 @@ func (r *RetryPolicyBackOff) toConsul() *capi.RetryPolicyBackOff { return nil } return &capi.RetryPolicyBackOff{ - BaseInterval: r.BaseInterval, - MaxInterval: r.MaxInterval, + BaseInterval: r.BaseInterval.Duration, + MaxInterval: r.MaxInterval.Duration, } } @@ -688,7 +688,7 @@ func (r *RetryPolicyBackOff) validate(path *field.Path) field.ErrorList { return errs } - if (r.MaxInterval != 0) && (r.BaseInterval > r.MaxInterval) { + if (r.MaxInterval.Duration != 0) && (r.BaseInterval.Duration > r.MaxInterval.Duration) { asJSON, _ := json.Marshal(r) errs = append(errs, field.Invalid(path, string(asJSON), "maxInterval should be greater or equal to baseInterval")) } diff --git a/control-plane/api/v1alpha1/jwtprovider_types_test.go b/control-plane/api/v1alpha1/jwtprovider_types_test.go index 8f6219e8d6..098e34494e 100644 --- a/control-plane/api/v1alpha1/jwtprovider_types_test.go +++ b/control-plane/api/v1alpha1/jwtprovider_types_test.go @@ -7,11 +7,12 @@ import ( "testing" "time" - "github.com/hashicorp/consul-k8s/control-plane/api/common" capi "github.com/hashicorp/consul/api" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/hashicorp/consul-k8s/control-plane/api/common" ) // Test MatchesConsul for cases that should return true. @@ -55,13 +56,13 @@ func TestJWTProvider_MatchesConsul(t *testing.T) { Remote: &RemoteJWKS{ URI: "https://jwks.example.com", RequestTimeoutMs: 567, - CacheDuration: 890, + CacheDuration: metav1.Duration{Duration: 890}, FetchAsynchronously: true, RetryPolicy: &JWKSRetryPolicy{ NumRetries: 1, RetryPolicyBackOff: &RetryPolicyBackOff{ - BaseInterval: 23, - MaxInterval: 456, + BaseInterval: metav1.Duration{Duration: 23}, + MaxInterval: metav1.Duration{Duration: 456}, }, }, JWKSCluster: &JWKSCluster{ @@ -78,7 +79,7 @@ func TestJWTProvider_MatchesConsul(t *testing.T) { InlineBytes: []byte("inline-bytes"), }, }, - ConnectTimeout: 890, + ConnectTimeout: metav1.Duration{Duration: 890}, }, }, }, @@ -238,13 +239,13 @@ func TestJWTProvider_ToConsul(t *testing.T) { Remote: &RemoteJWKS{ URI: "https://jwks.example.com", RequestTimeoutMs: 567, - CacheDuration: 890, + CacheDuration: metav1.Duration{Duration: 890}, FetchAsynchronously: true, RetryPolicy: &JWKSRetryPolicy{ NumRetries: 1, RetryPolicyBackOff: &RetryPolicyBackOff{ - BaseInterval: 23, - MaxInterval: 456, + BaseInterval: metav1.Duration{Duration: 23}, + MaxInterval: metav1.Duration{Duration: 456}, }, }, JWKSCluster: &JWKSCluster{ @@ -261,7 +262,7 @@ func TestJWTProvider_ToConsul(t *testing.T) { InlineBytes: []byte("inline-bytes"), }, }, - ConnectTimeout: 890, + ConnectTimeout: metav1.Duration{Duration: 890}, }, }, }, @@ -440,13 +441,13 @@ func TestJWTProvider_Validate(t *testing.T) { Remote: &RemoteJWKS{ URI: "https://jwks.example.com", RequestTimeoutMs: 5000, - CacheDuration: 10 * time.Second, + CacheDuration: metav1.Duration{Duration: 10 * time.Second}, FetchAsynchronously: true, RetryPolicy: &JWKSRetryPolicy{ NumRetries: 3, RetryPolicyBackOff: &RetryPolicyBackOff{ - BaseInterval: 5 * time.Second, - MaxInterval: 20 * time.Second, + BaseInterval: metav1.Duration{Duration: 5 * time.Second}, + MaxInterval: metav1.Duration{Duration: 20 * time.Second}, }, }, JWKSCluster: &JWKSCluster{ @@ -456,7 +457,7 @@ func TestJWTProvider_Validate(t *testing.T) { Filename: "cert.crt", }, }, - ConnectTimeout: 890, + ConnectTimeout: metav1.Duration{Duration: 890}, }, }, }, @@ -504,13 +505,13 @@ func TestJWTProvider_Validate(t *testing.T) { Remote: &RemoteJWKS{ URI: "https://jwks.example.com", RequestTimeoutMs: 5000, - CacheDuration: 10 * time.Second, + CacheDuration: metav1.Duration{Duration: 10 * time.Second}, FetchAsynchronously: true, RetryPolicy: &JWKSRetryPolicy{ NumRetries: 3, RetryPolicyBackOff: &RetryPolicyBackOff{ - BaseInterval: 5 * time.Second, - MaxInterval: 20 * time.Second, + BaseInterval: metav1.Duration{Duration: 5 * time.Second}, + MaxInterval: metav1.Duration{Duration: 20 * time.Second}, }, }, JWKSCluster: &JWKSCluster{ @@ -521,7 +522,7 @@ func TestJWTProvider_Validate(t *testing.T) { CertificateName: "ROOTCA", }, }, - ConnectTimeout: 890, + ConnectTimeout: metav1.Duration{Duration: 890}, }, }, }, @@ -620,7 +621,7 @@ func TestJWTProvider_Validate(t *testing.T) { }, }, expectedErrMsgs: []string{ - `jwtprovider.consul.hashicorp.com "test-jwks-local-and-remote" is invalid: spec.jsonWebKeySet: Invalid value: "{\"local\":{\"filename\":\"jwks.txt\"},\"remote\":{\"uri\":\"https://jwks.example.com\"}}": exactly one of 'local' or 'remote' is required`, + `jwtprovider.consul.hashicorp.com "test-jwks-local-and-remote" is invalid: spec.jsonWebKeySet: Invalid value: "{\"local\":{\"filename\":\"jwks.txt\"},\"remote\":{\"uri\":\"https://jwks.example.com\",\"cacheDuration\":\"0s\"}}": exactly one of 'local' or 'remote' is required`, }, }, @@ -679,7 +680,7 @@ func TestJWTProvider_Validate(t *testing.T) { Filename: "cert.crt", }, }, - ConnectTimeout: 890, + ConnectTimeout: metav1.Duration{Duration: 890}, }, }, }, @@ -702,7 +703,7 @@ func TestJWTProvider_Validate(t *testing.T) { URI: "https://jwks.example.com", JWKSCluster: &JWKSCluster{ DiscoveryType: "FAKE_DNS", - ConnectTimeout: 890, + ConnectTimeout: metav1.Duration{Duration: 890}, }, }, }, @@ -732,7 +733,7 @@ func TestJWTProvider_Validate(t *testing.T) { InlineBytes: []byte("inline-bytes"), }, }, - ConnectTimeout: 890, + ConnectTimeout: metav1.Duration{Duration: 890}, }, }, }, @@ -761,7 +762,7 @@ func TestJWTProvider_Validate(t *testing.T) { EnvironmentVariable: "env-variable", }, }, - ConnectTimeout: 890, + ConnectTimeout: metav1.Duration{Duration: 890}, }, }, }, @@ -844,8 +845,8 @@ func TestJWTProvider_Validate(t *testing.T) { RetryPolicy: &JWKSRetryPolicy{ NumRetries: 0, RetryPolicyBackOff: &RetryPolicyBackOff{ - BaseInterval: 100 * time.Second, - MaxInterval: 10 * time.Second, + BaseInterval: metav1.Duration{Duration: 100 * time.Second}, + MaxInterval: metav1.Duration{Duration: 10 * time.Second}, }, }, }, @@ -853,7 +854,7 @@ func TestJWTProvider_Validate(t *testing.T) { }, }, expectedErrMsgs: []string{ - `jwtprovider.consul.hashicorp.com "test-jwks-retry-intervals" is invalid: spec.jsonWebKeySet.remote.retryPolicy.retryPolicyBackOff: Invalid value: "{\"baseInterval\":100000000000,\"maxInterval\":10000000000}": maxInterval should be greater or equal to baseInterval`, + `jwtprovider.consul.hashicorp.com "test-jwks-retry-intervals" is invalid: spec.jsonWebKeySet.remote.retryPolicy.retryPolicyBackOff: Invalid value: "{\"baseInterval\":\"1m40s\",\"maxInterval\":\"10s\"}": maxInterval should be greater or equal to baseInterval`, }, }, } diff --git a/control-plane/api/v1alpha1/routetimeoutfilter_types.go b/control-plane/api/v1alpha1/routetimeoutfilter_types.go index 59b25f62ea..5aed6461bf 100644 --- a/control-plane/api/v1alpha1/routetimeoutfilter_types.go +++ b/control-plane/api/v1alpha1/routetimeoutfilter_types.go @@ -5,7 +5,6 @@ package v1alpha1 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "time" ) func init() { @@ -41,10 +40,10 @@ type RouteTimeoutFilterList struct { // RouteTimeoutFilterSpec defines the desired state of RouteTimeoutFilter. type RouteTimeoutFilterSpec struct { // +kubebuilder:validation:Optional - RequestTimeout time.Duration `json:"requestTimeout"` + RequestTimeout metav1.Duration `json:"requestTimeout"` // +kubebuilder:validation:Optional - IdleTimeout time.Duration `json:"idleTimeout"` + IdleTimeout metav1.Duration `json:"idleTimeout"` } func (h *RouteTimeoutFilter) GetNamespace() string { diff --git a/control-plane/api/v1alpha1/zz_generated.deepcopy.go b/control-plane/api/v1alpha1/zz_generated.deepcopy.go index cc8447c6c2..045bd5144e 100644 --- a/control-plane/api/v1alpha1/zz_generated.deepcopy.go +++ b/control-plane/api/v1alpha1/zz_generated.deepcopy.go @@ -1329,6 +1329,7 @@ func (in *JWKSCluster) DeepCopyInto(out *JWKSCluster) { *out = new(JWKSTLSCertificate) (*in).DeepCopyInto(*out) } + out.ConnectTimeout = in.ConnectTimeout } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JWKSCluster. @@ -2454,6 +2455,7 @@ func (in *ReadWriteRatesConfig) DeepCopy() *ReadWriteRatesConfig { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RemoteJWKS) DeepCopyInto(out *RemoteJWKS) { *out = *in + out.CacheDuration = in.CacheDuration if in.RetryPolicy != nil { in, out := &in.RetryPolicy, &out.RetryPolicy *out = new(JWKSRetryPolicy) @@ -2479,6 +2481,8 @@ func (in *RemoteJWKS) DeepCopy() *RemoteJWKS { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RetryPolicyBackOff) DeepCopyInto(out *RetryPolicyBackOff) { *out = *in + out.BaseInterval = in.BaseInterval + out.MaxInterval = in.MaxInterval } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RetryPolicyBackOff. @@ -2763,6 +2767,8 @@ func (in *RouteTimeoutFilterList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RouteTimeoutFilterSpec) DeepCopyInto(out *RouteTimeoutFilterSpec) { *out = *in + out.RequestTimeout = in.RequestTimeout + out.IdleTimeout = in.IdleTimeout } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteTimeoutFilterSpec. diff --git a/control-plane/api/v2beta1/traffic_permissions_types.go b/control-plane/api/v2beta1/traffic_permissions_types.go deleted file mode 100644 index 533b5d0e33..0000000000 --- a/control-plane/api/v2beta1/traffic_permissions_types.go +++ /dev/null @@ -1,427 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package v2beta1 - -import ( - "fmt" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - pbauth "github.com/hashicorp/consul/proto-public/pbauth/v2beta1" - "github.com/hashicorp/consul/proto-public/pbresource" - "google.golang.org/protobuf/testing/protocmp" - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/util/validation/field" - - "github.com/hashicorp/consul-k8s/control-plane/api/common" - inject "github.com/hashicorp/consul-k8s/control-plane/connect-inject/common" - "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" -) - -const ( - trafficpermissionsKubeKind = "trafficpermissions" -) - -func init() { - AuthSchemeBuilder.Register(&TrafficPermissions{}, &TrafficPermissionsList{}) -} - -var _ common.MeshConfig = &TrafficPermissions{} - -// +kubebuilder:object:root=true -// +kubebuilder:subresource:status - -// TrafficPermissions is the Schema for the traffic-permissions API -// +kubebuilder:printcolumn:name="Synced",type="string",JSONPath=".status.conditions[?(@.type==\"Synced\")].status",description="The sync status of the resource with Consul" -// +kubebuilder:printcolumn:name="Last Synced",type="date",JSONPath=".status.lastSyncedTime",description="The last successful synced time of the resource with Consul" -// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="The age of the resource" -// +kubebuilder:resource:shortName="traffic-permissions" -type TrafficPermissions struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec TrafficPermissionsSpec `json:"spec,omitempty"` - Status `json:"status,omitempty"` -} - -// +kubebuilder:object:root=true - -// TrafficPermissionsList contains a list of TrafficPermissions. -type TrafficPermissionsList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []TrafficPermissions `json:"items"` -} - -// TrafficPermissionsSpec defines the desired state of TrafficPermissions. -type TrafficPermissionsSpec struct { - // Destination is a configuration of the destination proxies - // where these traffic permissions should apply. - Destination *Destination `json:"destination,omitempty"` - // Action can be either allow or deny for the entire object. It will default to allow. - // - // If action is allow, - // we will allow the connection if one of the rules in Rules matches, in other words, we will deny - // all requests except for the ones that match Rules. If Consul is in default allow mode, then allow - // actions have no effect without a deny permission as everything is allowed by default. - // - // If action is deny, - // we will deny the connection if one of the rules in Rules match, in other words, - // we will allow all requests except for the ones that match Rules. If Consul is default deny mode, - // then deny permissions have no effect without an allow permission as everything is denied by default. - // - // Action unspecified is reserved for compatibility with the addition of future actions. - Action IntentionAction `json:"action,omitempty"` - // Permissions is a list of permissions to match on. - // They are applied using OR semantics. - Permissions Permissions `json:"permissions,omitempty"` -} - -type Destination struct { - // Name is the destination of all intentions defined in this config entry. - // This may be set to the wildcard character (*) to match - // all services that don't otherwise have intentions defined. - IdentityName string `json:"identityName,omitempty"` -} - -func (in *Destination) validate(path *field.Path) *field.Error { - if in == nil { - return field.Required(path, `destination and destination.identityName are required`) - } - if in.IdentityName == "" { - return field.Required(path.Child("identityName"), `identityName is required`) - } - return nil -} - -// IntentionAction is the action that the intention represents. This -// can be "allow" or "deny" to allowlist or denylist intentions. -type IntentionAction string - -const ( - ActionDeny IntentionAction = "deny" - ActionAllow IntentionAction = "allow" - ActionUnspecified IntentionAction = "" -) - -func (in IntentionAction) validate(path *field.Path) *field.Error { - switch in { - case ActionDeny, ActionAllow: - return nil - default: - return field.Invalid(path.Child("action"), in, "must be one of \"allow\" or \"deny\"") - } -} - -type Permissions []*Permission - -type Permission struct { - // sources is a list of sources in this traffic permission. - Sources Sources `json:"sources,omitempty"` - // destinationRules is a list of rules to apply for matching sources in this Permission. - // These rules are specific to the request or connection that is going to the destination(s) - // selected by the TrafficPermissions resource. - DestinationRules DestinationRules `json:"destinationRules,omitempty"` -} - -type Sources []*Source - -type DestinationRules []*DestinationRule - -// Source represents the source identity. -// To specify any of the wildcard sources, the specific fields need to be omitted. -// For example, for a wildcard namespace, identityName should be omitted. -type Source struct { - IdentityName string `json:"identityName,omitempty"` - Namespace string `json:"namespace,omitempty"` - Partition string `json:"partition,omitempty"` - Peer string `json:"peer,omitempty"` - SamenessGroup string `json:"samenessGroup,omitempty"` - // exclude is a list of sources to exclude from this source. - Exclude Exclude `json:"exclude,omitempty"` -} - -// DestinationRule contains rules to apply to the incoming connection. -type DestinationRule struct { - PathExact string `json:"pathExact,omitempty"` - PathPrefix string `json:"pathPrefix,omitempty"` - PathRegex string `json:"pathRegex,omitempty"` - // methods is the list of HTTP methods. If no methods are specified, - // this rule will apply to all methods. - Methods []string `json:"methods,omitempty"` - Header *DestinationRuleHeader `json:"header,omitempty"` - PortNames []string `json:"portNames,omitempty"` - // exclude contains a list of rules to exclude when evaluating rules for the incoming connection. - Exclude ExcludePermissions `json:"exclude,omitempty"` -} - -type Exclude []*ExcludeSource - -// ExcludeSource is almost the same as source but it prevents the addition of -// matchiing sources. -type ExcludeSource struct { - IdentityName string `json:"identityName,omitempty"` - Namespace string `json:"namespace,omitempty"` - Partition string `json:"partition,omitempty"` - Peer string `json:"peer,omitempty"` - SamenessGroup string `json:"samenessGroup,omitempty"` -} - -type DestinationRuleHeader struct { - Name string `json:"name,omitempty"` - Present bool `json:"present,omitempty"` - Exact string `json:"exact,omitempty"` - Prefix string `json:"prefix,omitempty"` - Suffix string `json:"suffix,omitempty"` - Regex string `json:"regex,omitempty"` - Invert bool `json:"invert,omitempty"` -} - -type ExcludePermissions []*ExcludePermissionRule - -type ExcludePermissionRule struct { - PathExact string `json:"pathExact,omitempty"` - PathPrefix string `json:"pathPrefix,omitempty"` - PathRegex string `json:"pathRegex,omitempty"` - // methods is the list of HTTP methods. - Methods []string `json:"methods,omitempty"` - Header *DestinationRuleHeader `json:"header,omitempty"` - // portNames is a list of workload ports to apply this rule to. The ports specified here - // must be the ports used in the connection. - PortNames []string `json:"portNames,omitempty"` -} - -func (in *TrafficPermissions) ResourceID(namespace, partition string) *pbresource.ID { - return &pbresource.ID{ - Name: in.Name, - Type: pbauth.TrafficPermissionsType, - Tenancy: &pbresource.Tenancy{ - Partition: partition, - Namespace: namespace, - - // Because we are explicitly defining NS/partition, this will not default and must be explicit. - // At a future point, this will move out of the Tenancy block. - PeerName: constants.DefaultConsulPeer, - }, - } -} - -func (in *TrafficPermissions) Resource(namespace, partition string) *pbresource.Resource { - return &pbresource.Resource{ - Id: in.ResourceID(namespace, partition), - Data: inject.ToProtoAny(&pbauth.TrafficPermissions{ - Destination: in.Spec.Destination.toProto(), - Action: in.Spec.Action.toProto(), - Permissions: in.Spec.Permissions.toProto(), - }), - Metadata: meshConfigMeta(), - } -} - -func (in *TrafficPermissions) MatchesConsul(candidate *pbresource.Resource, namespace, partition string) bool { - return cmp.Equal( - in.Resource(namespace, partition), - candidate, - protocmp.IgnoreFields(&pbresource.Resource{}, "status", "generation", "version"), - protocmp.IgnoreFields(&pbresource.ID{}, "uid"), - protocmp.Transform(), - cmpopts.SortSlices(func(a, b any) bool { return fmt.Sprintf("%v", a) < fmt.Sprintf("%v", b) }), - ) -} - -func (in *TrafficPermissions) AddFinalizer(f string) { - in.ObjectMeta.Finalizers = append(in.Finalizers(), f) -} - -func (in *TrafficPermissions) RemoveFinalizer(f string) { - var newFinalizers []string - for _, oldF := range in.Finalizers() { - if oldF != f { - newFinalizers = append(newFinalizers, oldF) - } - } - in.ObjectMeta.Finalizers = newFinalizers -} - -func (in *TrafficPermissions) Finalizers() []string { - return in.ObjectMeta.Finalizers -} - -func (in *TrafficPermissions) KubeKind() string { - return trafficpermissionsKubeKind -} - -func (in *TrafficPermissions) KubernetesName() string { - return in.ObjectMeta.Name -} - -func (in *TrafficPermissions) SetSyncedCondition(status corev1.ConditionStatus, reason, message string) { - in.Status.Conditions = Conditions{ - { - Type: ConditionSynced, - Status: status, - LastTransitionTime: metav1.Now(), - Reason: reason, - Message: message, - }, - } -} - -func (in *TrafficPermissions) SetLastSyncedTime(time *metav1.Time) { - in.Status.LastSyncedTime = time -} - -func (in *TrafficPermissions) SyncedCondition() (status corev1.ConditionStatus, reason, message string) { - cond := in.Status.GetCondition(ConditionSynced) - if cond == nil { - return corev1.ConditionUnknown, "", "" - } - return cond.Status, cond.Reason, cond.Message -} - -func (in *TrafficPermissions) SyncedConditionStatus() corev1.ConditionStatus { - condition := in.Status.GetCondition(ConditionSynced) - if condition == nil { - return corev1.ConditionUnknown - } - return condition.Status -} - -func (in *TrafficPermissions) Validate(_ common.ConsulTenancyConfig) error { - var errs field.ErrorList - path := field.NewPath("spec") - - if in.Spec.Action == ActionUnspecified { - errs = append(errs, field.Required(path.Child("action"), `action is required`)) - } - if err := in.Spec.Action.validate(path); err != nil { - errs = append(errs, err) - } - - // Validate Destinations - if err := in.Spec.Destination.validate(path.Child("destination")); err != nil { - errs = append(errs, err) - } - - // TODO: add validation for permissions - // Validate permissions in Consul: - // https://github.com/hashicorp/consul/blob/203a36821ef6182b2d2b30c1012ca5a42c7dd8f3/internal/auth/internal/types/traffic_permissions.go#L59-L141 - - if len(errs) > 0 { - return apierrors.NewInvalid( - schema.GroupKind{Group: AuthGroup, Kind: common.TrafficPermissions}, - in.KubernetesName(), errs) - } - return nil -} - -// DefaultNamespaceFields is required as part of the common.MeshConfig interface. -func (in *TrafficPermissions) DefaultNamespaceFields(tenancy common.ConsulTenancyConfig) {} - -func (p Permissions) toProto() []*pbauth.Permission { - var perms []*pbauth.Permission - for _, permission := range p { - perms = append(perms, &pbauth.Permission{ - Sources: permission.Sources.toProto(), - DestinationRules: permission.DestinationRules.toProto(), - }) - } - return perms -} - -func (s Sources) toProto() []*pbauth.Source { - var srcs []*pbauth.Source - for _, source := range s { - srcs = append(srcs, &pbauth.Source{ - IdentityName: source.IdentityName, - Namespace: source.Namespace, - Partition: source.Partition, - Peer: source.Peer, - SamenessGroup: source.SamenessGroup, - Exclude: source.Exclude.toProto(), - }) - } - return srcs -} - -func (r DestinationRules) toProto() []*pbauth.DestinationRule { - var dstnRules []*pbauth.DestinationRule - for _, rule := range r { - dstnRules = append(dstnRules, &pbauth.DestinationRule{ - PathExact: rule.PathExact, - PathPrefix: rule.PathPrefix, - PathRegex: rule.PathRegex, - Methods: rule.Methods, - Header: rule.Header.toProto(), - PortNames: rule.PortNames, - Exclude: rule.Exclude.toProto(), - }) - } - return dstnRules -} - -func (e Exclude) toProto() []*pbauth.ExcludeSource { - var exSrcs []*pbauth.ExcludeSource - for _, source := range e { - exSrcs = append(exSrcs, &pbauth.ExcludeSource{ - IdentityName: source.IdentityName, - Namespace: source.Namespace, - Partition: source.Partition, - Peer: source.Peer, - SamenessGroup: source.SamenessGroup, - }) - } - return exSrcs -} - -func (p ExcludePermissions) toProto() []*pbauth.ExcludePermissionRule { - var exclPerms []*pbauth.ExcludePermissionRule - for _, rule := range p { - exclPerms = append(exclPerms, &pbauth.ExcludePermissionRule{ - PathExact: rule.PathExact, - PathPrefix: rule.PathPrefix, - PathRegex: rule.PathRegex, - Methods: rule.Methods, - Header: rule.Header.toProto(), - PortNames: rule.PortNames, - }) - } - return exclPerms -} - -func (h *DestinationRuleHeader) toProto() *pbauth.DestinationRuleHeader { - if h == nil { - return nil - } - return &pbauth.DestinationRuleHeader{ - Name: h.Name, - Present: h.Present, - Exact: h.Exact, - Prefix: h.Prefix, - Suffix: h.Suffix, - Regex: h.Regex, - Invert: h.Invert, - } -} - -func (in *Destination) toProto() *pbauth.Destination { - if in == nil { - return nil - } - return &pbauth.Destination{ - IdentityName: in.IdentityName, - } -} - -func (in IntentionAction) toProto() pbauth.Action { - if in == ActionAllow { - return pbauth.Action_ACTION_ALLOW - } else if in == ActionDeny { - return pbauth.Action_ACTION_DENY - } - return pbauth.Action_ACTION_UNSPECIFIED -} diff --git a/control-plane/api/v2beta1/zz_generated.deepcopy.go b/control-plane/api/v2beta1/zz_generated.deepcopy.go deleted file mode 100644 index 0be67d0aa2..0000000000 --- a/control-plane/api/v2beta1/zz_generated.deepcopy.go +++ /dev/null @@ -1,467 +0,0 @@ -//go:build !ignore_autogenerated -// +build !ignore_autogenerated - -// Code generated by controller-gen. DO NOT EDIT. - -package v2beta1 - -import ( - runtime "k8s.io/apimachinery/pkg/runtime" -) - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Condition) DeepCopyInto(out *Condition) { - *out = *in - in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Condition. -func (in *Condition) DeepCopy() *Condition { - if in == nil { - return nil - } - out := new(Condition) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in Conditions) DeepCopyInto(out *Conditions) { - { - in := &in - *out = make(Conditions, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Conditions. -func (in Conditions) DeepCopy() Conditions { - if in == nil { - return nil - } - out := new(Conditions) - in.DeepCopyInto(out) - return *out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Destination) DeepCopyInto(out *Destination) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Destination. -func (in *Destination) DeepCopy() *Destination { - if in == nil { - return nil - } - out := new(Destination) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DestinationRule) DeepCopyInto(out *DestinationRule) { - *out = *in - if in.Methods != nil { - in, out := &in.Methods, &out.Methods - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Header != nil { - in, out := &in.Header, &out.Header - *out = new(DestinationRuleHeader) - **out = **in - } - if in.PortNames != nil { - in, out := &in.PortNames, &out.PortNames - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Exclude != nil { - in, out := &in.Exclude, &out.Exclude - *out = make(ExcludePermissions, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = new(ExcludePermissionRule) - (*in).DeepCopyInto(*out) - } - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DestinationRule. -func (in *DestinationRule) DeepCopy() *DestinationRule { - if in == nil { - return nil - } - out := new(DestinationRule) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DestinationRuleHeader) DeepCopyInto(out *DestinationRuleHeader) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DestinationRuleHeader. -func (in *DestinationRuleHeader) DeepCopy() *DestinationRuleHeader { - if in == nil { - return nil - } - out := new(DestinationRuleHeader) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in DestinationRules) DeepCopyInto(out *DestinationRules) { - { - in := &in - *out = make(DestinationRules, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = new(DestinationRule) - (*in).DeepCopyInto(*out) - } - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DestinationRules. -func (in DestinationRules) DeepCopy() DestinationRules { - if in == nil { - return nil - } - out := new(DestinationRules) - in.DeepCopyInto(out) - return *out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in Exclude) DeepCopyInto(out *Exclude) { - { - in := &in - *out = make(Exclude, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = new(ExcludeSource) - **out = **in - } - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Exclude. -func (in Exclude) DeepCopy() Exclude { - if in == nil { - return nil - } - out := new(Exclude) - in.DeepCopyInto(out) - return *out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ExcludePermissionRule) DeepCopyInto(out *ExcludePermissionRule) { - *out = *in - if in.Methods != nil { - in, out := &in.Methods, &out.Methods - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Header != nil { - in, out := &in.Header, &out.Header - *out = new(DestinationRuleHeader) - **out = **in - } - if in.PortNames != nil { - in, out := &in.PortNames, &out.PortNames - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludePermissionRule. -func (in *ExcludePermissionRule) DeepCopy() *ExcludePermissionRule { - if in == nil { - return nil - } - out := new(ExcludePermissionRule) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in ExcludePermissions) DeepCopyInto(out *ExcludePermissions) { - { - in := &in - *out = make(ExcludePermissions, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = new(ExcludePermissionRule) - (*in).DeepCopyInto(*out) - } - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludePermissions. -func (in ExcludePermissions) DeepCopy() ExcludePermissions { - if in == nil { - return nil - } - out := new(ExcludePermissions) - in.DeepCopyInto(out) - return *out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ExcludeSource) DeepCopyInto(out *ExcludeSource) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExcludeSource. -func (in *ExcludeSource) DeepCopy() *ExcludeSource { - if in == nil { - return nil - } - out := new(ExcludeSource) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Permission) DeepCopyInto(out *Permission) { - *out = *in - if in.Sources != nil { - in, out := &in.Sources, &out.Sources - *out = make(Sources, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = new(Source) - (*in).DeepCopyInto(*out) - } - } - } - if in.DestinationRules != nil { - in, out := &in.DestinationRules, &out.DestinationRules - *out = make(DestinationRules, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = new(DestinationRule) - (*in).DeepCopyInto(*out) - } - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Permission. -func (in *Permission) DeepCopy() *Permission { - if in == nil { - return nil - } - out := new(Permission) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in Permissions) DeepCopyInto(out *Permissions) { - { - in := &in - *out = make(Permissions, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = new(Permission) - (*in).DeepCopyInto(*out) - } - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Permissions. -func (in Permissions) DeepCopy() Permissions { - if in == nil { - return nil - } - out := new(Permissions) - in.DeepCopyInto(out) - return *out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Source) DeepCopyInto(out *Source) { - *out = *in - if in.Exclude != nil { - in, out := &in.Exclude, &out.Exclude - *out = make(Exclude, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = new(ExcludeSource) - **out = **in - } - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Source. -func (in *Source) DeepCopy() *Source { - if in == nil { - return nil - } - out := new(Source) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in Sources) DeepCopyInto(out *Sources) { - { - in := &in - *out = make(Sources, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = new(Source) - (*in).DeepCopyInto(*out) - } - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Sources. -func (in Sources) DeepCopy() Sources { - if in == nil { - return nil - } - out := new(Sources) - in.DeepCopyInto(out) - return *out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Status) DeepCopyInto(out *Status) { - *out = *in - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make(Conditions, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.LastSyncedTime != nil { - in, out := &in.LastSyncedTime, &out.LastSyncedTime - *out = (*in).DeepCopy() - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Status. -func (in *Status) DeepCopy() *Status { - if in == nil { - return nil - } - out := new(Status) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TrafficPermissions) DeepCopyInto(out *TrafficPermissions) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficPermissions. -func (in *TrafficPermissions) DeepCopy() *TrafficPermissions { - if in == nil { - return nil - } - out := new(TrafficPermissions) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *TrafficPermissions) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TrafficPermissionsList) DeepCopyInto(out *TrafficPermissionsList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]TrafficPermissions, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficPermissionsList. -func (in *TrafficPermissionsList) DeepCopy() *TrafficPermissionsList { - if in == nil { - return nil - } - out := new(TrafficPermissionsList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *TrafficPermissionsList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TrafficPermissionsSpec) DeepCopyInto(out *TrafficPermissionsSpec) { - *out = *in - if in.Destination != nil { - in, out := &in.Destination, &out.Destination - *out = new(Destination) - **out = **in - } - if in.Permissions != nil { - in, out := &in.Permissions, &out.Permissions - *out = make(Permissions, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = new(Permission) - (*in).DeepCopyInto(*out) - } - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficPermissionsSpec. -func (in *TrafficPermissionsSpec) DeepCopy() *TrafficPermissionsSpec { - if in == nil { - return nil - } - out := new(TrafficPermissionsSpec) - in.DeepCopyInto(out) - return out -} diff --git a/control-plane/config-entries/controllersv2/grpc_route_controller.go b/control-plane/config-entries/controllersv2/grpc_route_controller.go new file mode 100644 index 0000000000..06accae268 --- /dev/null +++ b/control-plane/config-entries/controllersv2/grpc_route_controller.go @@ -0,0 +1,43 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package controllersv2 + +import ( + "context" + + "github.com/go-logr/logr" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + meshv2beta1 "github.com/hashicorp/consul-k8s/control-plane/api/mesh/v2beta1" +) + +// GRPCRouteController reconciles a GRPCRoute object. +type GRPCRouteController struct { + client.Client + Log logr.Logger + Scheme *runtime.Scheme + MeshConfigController *MeshConfigController +} + +// +kubebuilder:rbac:groups=mesh.consul.hashicorp.com,resources=grpcroute,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=mesh.consul.hashicorp.com,resources=grpcroute/status,verbs=get;update;patch + +func (r *GRPCRouteController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + return r.MeshConfigController.ReconcileEntry(ctx, r, req, &meshv2beta1.GRPCRoute{}) +} + +func (r *GRPCRouteController) Logger(name types.NamespacedName) logr.Logger { + return r.Log.WithValues("request", name) +} + +func (r *GRPCRouteController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { + return r.Status().Update(ctx, obj, opts...) +} + +func (r *GRPCRouteController) SetupWithManager(mgr ctrl.Manager) error { + return setupWithManager(mgr, &meshv2beta1.GRPCRoute{}, r) +} diff --git a/control-plane/config-entries/controllersv2/http_route_controller.go b/control-plane/config-entries/controllersv2/http_route_controller.go new file mode 100644 index 0000000000..545250af74 --- /dev/null +++ b/control-plane/config-entries/controllersv2/http_route_controller.go @@ -0,0 +1,43 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package controllersv2 + +import ( + "context" + + "github.com/go-logr/logr" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + meshv2beta1 "github.com/hashicorp/consul-k8s/control-plane/api/mesh/v2beta1" +) + +// HTTPRouteController reconciles a HTTPRoute object. +type HTTPRouteController struct { + client.Client + Log logr.Logger + Scheme *runtime.Scheme + MeshConfigController *MeshConfigController +} + +// +kubebuilder:rbac:groups=mesh.consul.hashicorp.com,resources=httproute,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=mesh.consul.hashicorp.com,resources=httproute/status,verbs=get;update;patch + +func (r *HTTPRouteController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + return r.MeshConfigController.ReconcileEntry(ctx, r, req, &meshv2beta1.HTTPRoute{}) +} + +func (r *HTTPRouteController) Logger(name types.NamespacedName) logr.Logger { + return r.Log.WithValues("request", name) +} + +func (r *HTTPRouteController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { + return r.Status().Update(ctx, obj, opts...) +} + +func (r *HTTPRouteController) SetupWithManager(mgr ctrl.Manager) error { + return setupWithManager(mgr, &meshv2beta1.HTTPRoute{}, r) +} diff --git a/control-plane/config-entries/controllersv2/meshconfig_controller_test.go b/control-plane/config-entries/controllersv2/meshconfig_controller_test.go index ff2a94ae27..e95e722ea6 100644 --- a/control-plane/config-entries/controllersv2/meshconfig_controller_test.go +++ b/control-plane/config-entries/controllersv2/meshconfig_controller_test.go @@ -25,9 +25,9 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" + "github.com/hashicorp/consul-k8s/control-plane/api/auth/v2beta1" "github.com/hashicorp/consul-k8s/control-plane/api/common" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul-k8s/control-plane/api/v2beta1" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" "github.com/hashicorp/consul-k8s/control-plane/consul" "github.com/hashicorp/consul-k8s/control-plane/helper/test" @@ -55,14 +55,14 @@ func TestMeshConfigController_createsMeshConfig(t *testing.T) { Name: "my-traffic-permission", Namespace: metav1.NamespaceDefault, }, - Spec: v2beta1.TrafficPermissionsSpec{ - Destination: &v2beta1.Destination{ + Spec: pbauth.TrafficPermissions{ + Destination: &pbauth.Destination{ IdentityName: "destination-identity", }, - Action: v2beta1.ActionAllow, - Permissions: v2beta1.Permissions{ + Action: pbauth.Action_ACTION_ALLOW, + Permissions: []*pbauth.Permission{ { - Sources: v2beta1.Sources{ + Sources: []*pbauth.Source{ { Namespace: "the space namespace space", }, @@ -70,7 +70,7 @@ func TestMeshConfigController_createsMeshConfig(t *testing.T) { IdentityName: "source-identity", }, }, - DestinationRules: v2beta1.DestinationRules{ + DestinationRules: []*pbauth.DestinationRule{ { PathExact: "/hello", Methods: []string{"GET", "POST"}, @@ -201,14 +201,14 @@ func TestMeshConfigController_updatesMeshConfig(t *testing.T) { Name: "my-traffic-permission", Namespace: metav1.NamespaceDefault, }, - Spec: v2beta1.TrafficPermissionsSpec{ - Destination: &v2beta1.Destination{ + Spec: pbauth.TrafficPermissions{ + Destination: &pbauth.Destination{ IdentityName: "destination-identity", }, - Action: v2beta1.ActionAllow, - Permissions: v2beta1.Permissions{ + Action: pbauth.Action_ACTION_ALLOW, + Permissions: []*pbauth.Permission{ { - Sources: v2beta1.Sources{ + Sources: []*pbauth.Source{ { Namespace: "the space namespace space", }, @@ -216,7 +216,7 @@ func TestMeshConfigController_updatesMeshConfig(t *testing.T) { IdentityName: "source-identity", }, }, - DestinationRules: v2beta1.DestinationRules{ + DestinationRules: []*pbauth.DestinationRule{ { PathExact: "/hello", Methods: []string{"GET", "POST"}, @@ -263,7 +263,7 @@ func TestMeshConfigController_updatesMeshConfig(t *testing.T) { }, updateF: func(resource common.MeshConfig) { trafficPermissions := resource.(*v2beta1.TrafficPermissions) - trafficPermissions.Spec.Action = "deny" + trafficPermissions.Spec.Action = pbauth.Action_ACTION_DENY trafficPermissions.Spec.Permissions[0].Sources = trafficPermissions.Spec.Permissions[0].Sources[:1] }, unmarshal: func(t *testing.T, resource *pbresource.Resource) proto.Message { @@ -358,14 +358,14 @@ func TestMeshConfigController_deletesMeshConfig(t *testing.T) { DeletionTimestamp: &metav1.Time{Time: time.Now()}, Finalizers: []string{FinalizerName}, }, - Spec: v2beta1.TrafficPermissionsSpec{ - Destination: &v2beta1.Destination{ + Spec: pbauth.TrafficPermissions{ + Destination: &pbauth.Destination{ IdentityName: "destination-identity", }, - Action: v2beta1.ActionAllow, - Permissions: v2beta1.Permissions{ + Action: pbauth.Action_ACTION_ALLOW, + Permissions: []*pbauth.Permission{ { - Sources: v2beta1.Sources{ + Sources: []*pbauth.Source{ { Namespace: "the space namespace space", }, @@ -373,7 +373,7 @@ func TestMeshConfigController_deletesMeshConfig(t *testing.T) { IdentityName: "source-identity", }, }, - DestinationRules: v2beta1.DestinationRules{ + DestinationRules: []*pbauth.DestinationRule{ { PathExact: "/hello", Methods: []string{"GET", "POST"}, @@ -457,14 +457,14 @@ func TestMeshConfigController_errorUpdatesSyncStatus(t *testing.T) { Name: "foo", Namespace: metav1.NamespaceDefault, }, - Spec: v2beta1.TrafficPermissionsSpec{ - Destination: &v2beta1.Destination{ + Spec: pbauth.TrafficPermissions{ + Destination: &pbauth.Destination{ IdentityName: "destination-identity", }, - Action: v2beta1.ActionAllow, - Permissions: v2beta1.Permissions{ + Action: pbauth.Action_ACTION_ALLOW, + Permissions: []*pbauth.Permission{ { - Sources: v2beta1.Sources{ + Sources: []*pbauth.Source{ { IdentityName: "source-identity", }, @@ -533,14 +533,14 @@ func TestMeshConfigController_setsSyncedToTrue(t *testing.T) { Name: "foo", Namespace: metav1.NamespaceDefault, }, - Spec: v2beta1.TrafficPermissionsSpec{ - Destination: &v2beta1.Destination{ + Spec: pbauth.TrafficPermissions{ + Destination: &pbauth.Destination{ IdentityName: "destination-identity", }, - Action: v2beta1.ActionAllow, - Permissions: v2beta1.Permissions{ + Action: pbauth.Action_ACTION_ALLOW, + Permissions: []*pbauth.Permission{ { - Sources: v2beta1.Sources{ + Sources: []*pbauth.Source{ { IdentityName: "source-identity", }, @@ -620,14 +620,14 @@ func TestMeshConfigController_doesNotCreateUnownedMeshConfig(t *testing.T) { Name: "foo", Namespace: metav1.NamespaceDefault, }, - Spec: v2beta1.TrafficPermissionsSpec{ - Destination: &v2beta1.Destination{ + Spec: pbauth.TrafficPermissions{ + Destination: &pbauth.Destination{ IdentityName: "destination-identity", }, - Action: v2beta1.ActionAllow, - Permissions: v2beta1.Permissions{ + Action: pbauth.Action_ACTION_ALLOW, + Permissions: []*pbauth.Permission{ { - Sources: v2beta1.Sources{ + Sources: []*pbauth.Source{ { IdentityName: "source-identity", Namespace: common.DefaultConsulNamespace, @@ -728,14 +728,14 @@ func TestMeshConfigController_doesNotDeleteUnownedConfig(t *testing.T) { DeletionTimestamp: &metav1.Time{Time: time.Now()}, Finalizers: []string{FinalizerName}, }, - Spec: v2beta1.TrafficPermissionsSpec{ - Destination: &v2beta1.Destination{ + Spec: pbauth.TrafficPermissions{ + Destination: &pbauth.Destination{ IdentityName: "destination-identity", }, - Action: v2beta1.ActionAllow, - Permissions: v2beta1.Permissions{ + Action: pbauth.Action_ACTION_ALLOW, + Permissions: []*pbauth.Permission{ { - Sources: v2beta1.Sources{ + Sources: []*pbauth.Source{ { IdentityName: "source-identity", Namespace: common.DefaultConsulNamespace, diff --git a/control-plane/config-entries/controllersv2/proxy_configuration_controller.go b/control-plane/config-entries/controllersv2/proxy_configuration_controller.go new file mode 100644 index 0000000000..f45dda1962 --- /dev/null +++ b/control-plane/config-entries/controllersv2/proxy_configuration_controller.go @@ -0,0 +1,43 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package controllersv2 + +import ( + "context" + + "github.com/go-logr/logr" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + meshv2beta1 "github.com/hashicorp/consul-k8s/control-plane/api/mesh/v2beta1" +) + +// ProxyConfigurationController reconciles a ProxyConfiguration object. +type ProxyConfigurationController struct { + client.Client + Log logr.Logger + Scheme *runtime.Scheme + MeshConfigController *MeshConfigController +} + +// +kubebuilder:rbac:groups=mesh.consul.hashicorp.com,resources=proxyconfiguration,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=mesh.consul.hashicorp.com,resources=proxyconfiguration/status,verbs=get;update;patch + +func (r *ProxyConfigurationController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + return r.MeshConfigController.ReconcileEntry(ctx, r, req, &meshv2beta1.ProxyConfiguration{}) +} + +func (r *ProxyConfigurationController) Logger(name types.NamespacedName) logr.Logger { + return r.Log.WithValues("request", name) +} + +func (r *ProxyConfigurationController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { + return r.Status().Update(ctx, obj, opts...) +} + +func (r *ProxyConfigurationController) SetupWithManager(mgr ctrl.Manager) error { + return setupWithManager(mgr, &meshv2beta1.ProxyConfiguration{}, r) +} diff --git a/control-plane/config-entries/controllersv2/tcp_route_controller.go b/control-plane/config-entries/controllersv2/tcp_route_controller.go new file mode 100644 index 0000000000..170dbb9fd4 --- /dev/null +++ b/control-plane/config-entries/controllersv2/tcp_route_controller.go @@ -0,0 +1,43 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package controllersv2 + +import ( + "context" + + "github.com/go-logr/logr" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + meshv2beta1 "github.com/hashicorp/consul-k8s/control-plane/api/mesh/v2beta1" +) + +// TCPRouteController reconciles a TCPRoute object. +type TCPRouteController struct { + client.Client + Log logr.Logger + Scheme *runtime.Scheme + MeshConfigController *MeshConfigController +} + +// +kubebuilder:rbac:groups=mesh.consul.hashicorp.com,resources=tcproute,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=mesh.consul.hashicorp.com,resources=tcproute/status,verbs=get;update;patch + +func (r *TCPRouteController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + return r.MeshConfigController.ReconcileEntry(ctx, r, req, &meshv2beta1.TCPRoute{}) +} + +func (r *TCPRouteController) Logger(name types.NamespacedName) logr.Logger { + return r.Log.WithValues("request", name) +} + +func (r *TCPRouteController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { + return r.Status().Update(ctx, obj, opts...) +} + +func (r *TCPRouteController) SetupWithManager(mgr ctrl.Manager) error { + return setupWithManager(mgr, &meshv2beta1.TCPRoute{}, r) +} diff --git a/control-plane/config-entries/controllersv2/traffic_permissions_controller.go b/control-plane/config-entries/controllersv2/traffic_permissions_controller.go index a8c9f63e56..abcaf98906 100644 --- a/control-plane/config-entries/controllersv2/traffic_permissions_controller.go +++ b/control-plane/config-entries/controllersv2/traffic_permissions_controller.go @@ -12,7 +12,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - consulv2beta1 "github.com/hashicorp/consul-k8s/control-plane/api/v2beta1" + consulv2beta1 "github.com/hashicorp/consul-k8s/control-plane/api/auth/v2beta1" ) // TrafficPermissionsController reconciles a TrafficPermissions object. diff --git a/control-plane/config/crd/bases/auth.consul.hashicorp.com_trafficpermissions.yaml b/control-plane/config/crd/bases/auth.consul.hashicorp.com_trafficpermissions.yaml index 270f86ee0f..3a7699dce4 100644 --- a/control-plane/config/crd/bases/auth.consul.hashicorp.com_trafficpermissions.yaml +++ b/control-plane/config/crd/bases/auth.consul.hashicorp.com_trafficpermissions.yaml @@ -1,13 +1,11 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.1 name: trafficpermissions.auth.consul.hashicorp.com spec: group: auth.consul.hashicorp.com @@ -52,7 +50,6 @@ spec: metadata: type: object spec: - description: TrafficPermissionsSpec defines the desired state of TrafficPermissions. properties: action: description: "Action can be either allow or deny for the entire object. @@ -67,34 +64,36 @@ spec: deny permissions have no effect without an allow permission as everything is denied by default. \n Action unspecified is reserved for compatibility with the addition of future actions." + enum: + - ACTION_ALLOW + - ACTION_DENY + - ACTION_UNKNOWN + format: int32 type: string destination: description: Destination is a configuration of the destination proxies where these traffic permissions should apply. properties: identityName: - description: Name is the destination of all intentions defined - in this config entry. This may be set to the wildcard character - (*) to match all services that don't otherwise have intentions - defined. type: string type: object permissions: description: Permissions is a list of permissions to match on. They are applied using OR semantics. items: + description: Permissions is a list of permissions to match on. properties: destinationRules: - description: destinationRules is a list of rules to apply for + description: DestinationRules is a list of rules to apply for matching sources in this Permission. These rules are specific to the request or connection that is going to the destination(s) selected by the TrafficPermissions resource. items: - description: DestinationRule contains rules to apply to the - incoming connection. + description: DestinationRule contains rules rules to apply + to the incoming connection. properties: exclude: - description: exclude contains a list of rules to exclude + description: Exclude contains a list of rules to exclude when evaluating rules for the incoming connection. items: properties: @@ -116,7 +115,7 @@ spec: type: string type: object methods: - description: methods is the list of HTTP methods. + description: Methods is the list of HTTP methods. items: type: string type: array @@ -127,7 +126,7 @@ spec: pathRegex: type: string portNames: - description: portNames is a list of workload ports + description: PortNames is a list of workload ports to apply this rule to. The ports specified here must be the ports used in the connection. items: @@ -153,7 +152,7 @@ spec: type: string type: object methods: - description: methods is the list of HTTP methods. If no + description: Methods is the list of HTTP methods. If no methods are specified, this rule will apply to all methods. items: type: string @@ -171,19 +170,19 @@ spec: type: object type: array sources: - description: sources is a list of sources in this traffic permission. + description: Sources is a list of sources in this traffic permission. items: description: Source represents the source identity. To specify any of the wildcard sources, the specific fields need to - be omitted. For example, for a wildcard namespace, identityName + be omitted. For example, for a wildcard namespace, identity_name should be omitted. properties: exclude: - description: exclude is a list of sources to exclude from + description: Exclude is a list of sources to exclude from this source. items: description: ExcludeSource is almost the same as source - but it prevents the addition of matchiing sources. + but it prevents the addition of matching sources. properties: identityName: type: string @@ -255,9 +254,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_controlplanerequestlimits.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_controlplanerequestlimits.yaml index f74743655e..49fc1ae135 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_controlplanerequestlimits.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_controlplanerequestlimits.yaml @@ -1,13 +1,11 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.1 name: controlplanerequestlimits.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -190,9 +188,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml index 0b6b969856..22f816cb18 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_exportedservices.yaml @@ -1,13 +1,11 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.1 name: exportedservices.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -134,9 +132,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml index c4a510ffad..ff3158f2a7 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_gatewayclassconfigs.yaml @@ -1,13 +1,11 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.1 name: gatewayclassconfigs.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -196,9 +194,3 @@ spec: type: object served: true storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_gatewaypolicies.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_gatewaypolicies.yaml index 9b6b2e05c5..e12db4cf20 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_gatewaypolicies.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_gatewaypolicies.yaml @@ -1,13 +1,11 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.1 name: gatewaypolicies.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -277,9 +275,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml index e9994d8457..79450327cb 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_ingressgateways.yaml @@ -1,13 +1,11 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.1 name: ingressgateways.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -442,9 +440,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_jwtproviders.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_jwtproviders.yaml index e9bfd8330c..df234ae1eb 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_jwtproviders.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_jwtproviders.yaml @@ -1,13 +1,11 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.1 name: jwtproviders.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -107,8 +105,7 @@ spec: cacheDuration: description: "CacheDuration is the duration after which cached keys should be expired. \n Default value is 5 minutes." - format: int64 - type: integer + type: string fetchAsynchronously: description: "FetchAsynchronously indicates that the JWKS should be fetched when a client request arrives. Client @@ -124,8 +121,7 @@ spec: description: The timeout for new network connections to hosts in the cluster. If not set, a default value of 5s will be used. - format: int64 - type: integer + type: string discoveryType: description: "DiscoveryType refers to the service discovery type to use for resolving the cluster. \n This defaults @@ -198,15 +194,13 @@ spec: description: "BaseInterval to be used for the next back off computation. \n The default value from envoy is 1s." - format: int64 - type: integer + type: string maxInterval: description: "MaxInternal to be used to specify the maximum interval between retries. Optional but should be greater or equal to BaseInterval. \n Defaults to 10 times BaseInterval." - format: int64 - type: integer + type: string type: object type: object uri: @@ -312,9 +306,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml index adbb12bba6..3c22a4842e 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_meshes.yaml @@ -1,13 +1,11 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.1 name: meshes.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -202,9 +200,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_meshservices.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_meshservices.yaml index 04f8f493e7..9eccd85cad 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_meshservices.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_meshservices.yaml @@ -1,13 +1,11 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.1 name: meshservices.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -51,9 +49,3 @@ spec: type: object served: true storage: true -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml index 50df179f04..b568a94962 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_peeringacceptors.yaml @@ -1,13 +1,11 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.1 name: peeringacceptors.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -141,9 +139,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml index 01e4363f14..ebf64adf67 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_peeringdialers.yaml @@ -1,13 +1,11 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.1 name: peeringdialers.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -141,9 +139,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml index e7ef98d96a..20f2faeb63 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_proxydefaults.yaml @@ -1,13 +1,11 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.1 name: proxydefaults.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -260,9 +258,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_routeauthfilters.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_routeauthfilters.yaml index a83a4bb7d0..5072fdf391 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_routeauthfilters.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_routeauthfilters.yaml @@ -1,13 +1,11 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.1 name: routeauthfilters.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -194,9 +192,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_routeretryfilters.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_routeretryfilters.yaml index c27f7d663f..8fa61cb683 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_routeretryfilters.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_routeretryfilters.yaml @@ -1,13 +1,11 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.1 name: routeretryfilters.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -110,9 +108,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_routetimeoutfilters.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_routetimeoutfilters.yaml index 6eb46a6171..0822050cb2 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_routetimeoutfilters.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_routetimeoutfilters.yaml @@ -1,13 +1,11 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.1 name: routetimeoutfilters.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -53,17 +51,9 @@ spec: description: RouteTimeoutFilterSpec defines the desired state of RouteTimeoutFilter. properties: idleTimeout: - description: A Duration represents the elapsed time between two instants - as an int64 nanosecond count. The representation limits the largest - representable duration to approximately 290 years. - format: int64 - type: integer + type: string requestTimeout: - description: A Duration represents the elapsed time between two instants - as an int64 nanosecond count. The representation limits the largest - representable duration to approximately 290 years. - format: int64 - type: integer + type: string type: object status: properties: @@ -108,9 +98,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml index c71a211f63..4274efffc8 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_samenessgroups.yaml @@ -1,13 +1,11 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.1 name: samenessgroups.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -124,9 +122,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml index 2b5ab54acd..7e7bcfaacc 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml @@ -1,13 +1,11 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.1 name: servicedefaults.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -560,9 +558,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml index a4efd6e958..4718ee24e5 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_serviceintentions.yaml @@ -1,13 +1,11 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.1 name: serviceintentions.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -306,9 +304,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml index 0146eca982..a1e3844b9c 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml @@ -1,13 +1,11 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.1 name: serviceresolvers.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -343,9 +341,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml index 31f5ee2924..764b4614f3 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml @@ -1,13 +1,11 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.1 name: servicerouters.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -307,9 +305,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml index aa2b592c94..36f9c9f6c9 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicesplitters.yaml @@ -1,13 +1,11 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.1 name: servicesplitters.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -181,9 +179,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml index b465cd9494..7f22c65d09 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_terminatinggateways.yaml @@ -1,13 +1,11 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.1 name: terminatinggateways.consul.hashicorp.com spec: group: consul.hashicorp.com @@ -132,9 +130,3 @@ spec: storage: true subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/control-plane/config/crd/bases/mesh.consul.hashicorp.com_grpcroutes.yaml b/control-plane/config/crd/bases/mesh.consul.hashicorp.com_grpcroutes.yaml new file mode 100644 index 0000000000..fda3e4255e --- /dev/null +++ b/control-plane/config/crd/bases/mesh.consul.hashicorp.com_grpcroutes.yaml @@ -0,0 +1,612 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.12.1 + name: grpcroutes.mesh.consul.hashicorp.com +spec: + group: mesh.consul.hashicorp.com + names: + kind: GRPCRoute + listKind: GRPCRouteList + plural: grpcroutes + shortNames: + - grpc-route + singular: grpcroute + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v2beta1 + schema: + openAPIV3Schema: + description: GRPCRoute is the Schema for the GRPC Route API + 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: "NOTE: this should align to the GAMMA/gateway-api version, + or at least be easily translatable. \n https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRoute + \n This is a Resource type." + properties: + hostnames: + description: "Hostnames are the hostnames for which this GRPCRoute + should respond to requests. \n This is only valid for north/south." + items: + type: string + type: array + parentRefs: + description: "ParentRefs references the resources (usually Services) + that a Route wants to be attached to. \n It is invalid to reference + an identical parent more than once. It is valid to reference multiple + distinct sections within the same parent resource." + items: + description: 'NOTE: roughly equivalent to structs.ResourceReference' + properties: + port: + description: For east/west this is the name of the Consul Service + port to direct traffic to or empty to imply all. For north/south + this is TBD. + type: string + ref: + description: For east/west configuration, this should point + to a Service. For north/south it should point to a Gateway. + properties: + name: + description: Name is the user-given name of the resource + (e.g. the "billing" service). + type: string + section: + description: Section identifies which part of the resource + the condition relates to. + type: string + tenancy: + description: Tenancy identifies the tenancy units (i.e. + partition, namespace) in which the resource resides. + properties: + namespace: + description: "Namespace further isolates resources within + a partition. https://developer.hashicorp.com/consul/docs/enterprise/namespaces + \n When using the List and WatchList endpoints, provide + the wildcard value \"*\" to list resources across + all namespaces." + type: string + partition: + description: "Partition is the topmost administrative + boundary within a cluster. https://developer.hashicorp.com/consul/docs/enterprise/admin-partitions + \n When using the List and WatchList endpoints, provide + the wildcard value \"*\" to list resources across + all partitions." + type: string + peerName: + description: "PeerName identifies which peer the resource + is imported from. https://developer.hashicorp.com/consul/docs/connect/cluster-peering + \n When using the List and WatchList endpoints, provide + the wildcard value \"*\" to list resources across + all peers." + type: string + type: object + type: + description: Type identifies the resource's type. + properties: + group: + description: Group describes the area of functionality + to which this resource type relates (e.g. "catalog", + "authorization"). + type: string + groupVersion: + description: GroupVersion is incremented when sweeping + or backward-incompatible changes are made to the group's + resource types. + type: string + kind: + description: Kind identifies the specific resource type + within the group. + type: string + type: object + type: object + type: object + type: array + rules: + description: Rules are a list of GRPC matchers, filters and actions. + items: + properties: + backendRefs: + description: "BackendRefs defines the backend(s) where matching + requests should be sent. Failure behavior here depends on + how many BackendRefs are specified and how many are invalid. + \n If all entries in BackendRefs are invalid, and there are + also no filters specified in this route rule, all traffic + which matches this rule MUST receive a 500 status code. \n + See the GRPCBackendRef definition for the rules about what + makes a single GRPCBackendRef invalid. \n When a GRPCBackendRef + is invalid, 500 status codes MUST be returned for requests + that would have otherwise been routed to an invalid backend. + If multiple backends are specified, and some are invalid, + the proportion of requests that would otherwise have been + routed to an invalid backend MUST receive a 500 status code. + \n For example, if two backends are specified with equal weights, + and one is invalid, 50 percent of traffic must receive a 500. + Implementations may choose how that 50 percent is determined." + items: + properties: + backendRef: + properties: + datacenter: + type: string + port: + description: "For east/west this is the name of the + Consul Service port to direct traffic to or empty + to imply using the same value as the parent ref. + \n For north/south this is TBD." + type: string + ref: + description: For east/west configuration, this should + point to a Service. + properties: + name: + description: Name is the user-given name of the + resource (e.g. the "billing" service). + type: string + section: + description: Section identifies which part of + the resource the condition relates to. + type: string + tenancy: + description: Tenancy identifies the tenancy units + (i.e. partition, namespace) in which the resource + resides. + properties: + namespace: + description: "Namespace further isolates resources + within a partition. https://developer.hashicorp.com/consul/docs/enterprise/namespaces + \n When using the List and WatchList endpoints, + provide the wildcard value \"*\" to list + resources across all namespaces." + type: string + partition: + description: "Partition is the topmost administrative + boundary within a cluster. https://developer.hashicorp.com/consul/docs/enterprise/admin-partitions + \n When using the List and WatchList endpoints, + provide the wildcard value \"*\" to list + resources across all partitions." + type: string + peerName: + description: "PeerName identifies which peer + the resource is imported from. https://developer.hashicorp.com/consul/docs/connect/cluster-peering + \n When using the List and WatchList endpoints, + provide the wildcard value \"*\" to list + resources across all peers." + type: string + type: object + type: + description: Type identifies the resource's type. + properties: + group: + description: Group describes the area of functionality + to which this resource type relates (e.g. + "catalog", "authorization"). + type: string + groupVersion: + description: GroupVersion is incremented when + sweeping or backward-incompatible changes + are made to the group's resource types. + type: string + kind: + description: Kind identifies the specific + resource type within the group. + type: string + type: object + type: object + 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. + items: + properties: + requestHeaderModifier: + description: RequestHeaderModifier defines a schema + for a filter that modifies request headers. + properties: + add: + description: Add adds the given header(s) (name, + value) to the request before the action. It + appends to any existing values associated + with the header name. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + remove: + description: Remove the given header(s) from + the HTTP request before the action. The value + of Remove is a list of HTTP header names. + Note that the header names are case-insensitive + (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + items: + type: string + type: array + set: + description: Set overwrites the request with + the given header (name, value) before the + action. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + type: object + responseHeaderModifier: + description: ResponseHeaderModifier defines a schema + for a filter that modifies response headers. + properties: + add: + description: Add adds the given header(s) (name, + value) to the request before the action. It + appends to any existing values associated + with the header name. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + remove: + description: Remove the given header(s) from + the HTTP request before the action. The value + of Remove is a list of HTTP header names. + Note that the header names are case-insensitive + (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + items: + type: string + type: array + set: + description: Set overwrites the request with + the given header (name, value) before the + action. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + type: object + urlRewrite: + description: URLRewrite defines a schema for a filter + that modifies a request during forwarding. + properties: + pathPrefix: + type: string + type: object + type: object + type: array + weight: + description: "Weight specifies the proportion of requests + forwarded to the referenced backend. This is computed + as weight/(sum of all weights in this BackendRefs 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." + format: int32 + type: integer + type: object + type: array + filters: + items: + properties: + requestHeaderModifier: + description: RequestHeaderModifier defines a schema for + a filter that modifies request headers. + properties: + add: + description: Add adds the given header(s) (name, value) + to the request before the action. It appends to + any existing values associated with the header name. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + remove: + description: Remove the given header(s) from the HTTP + request before the action. The value of Remove is + a list of HTTP header names. Note that the header + names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + items: + type: string + type: array + set: + description: Set overwrites the request with the given + header (name, value) before the action. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + type: object + responseHeaderModifier: + description: ResponseHeaderModifier defines a schema for + a filter that modifies response headers. + properties: + add: + description: Add adds the given header(s) (name, value) + to the request before the action. It appends to + any existing values associated with the header name. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + remove: + description: Remove the given header(s) from the HTTP + request before the action. The value of Remove is + a list of HTTP header names. Note that the header + names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + items: + type: string + type: array + set: + description: Set overwrites the request with the given + header (name, value) before the action. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + type: object + urlRewrite: + description: URLRewrite defines a schema for a filter + that modifies a request during forwarding. + properties: + pathPrefix: + type: string + type: object + type: object + type: array + matches: + items: + properties: + headers: + description: Headers specifies gRPC request header matchers. + Multiple match values are ANDed together, meaning, a + request MUST match all the specified headers to select + the route. + items: + properties: + name: + type: string + type: + description: "HeaderMatchType specifies the semantics + of how HTTP header values should be compared. + Valid HeaderMatchType values, along with their + conformance levels, are: \n Note that values may + be added to this enum, implementations must ensure + that unknown values will not cause a crash. \n + Unknown values here must result in the implementation + setting the Accepted Condition for the Route to + status: False, with a Reason of UnsupportedValue." + enum: + - HEADER_MATCH_TYPE_UNSPECIFIED + - HEADER_MATCH_TYPE_EXACT + - HEADER_MATCH_TYPE_REGEX + - HEADER_MATCH_TYPE_PRESENT + - HEADER_MATCH_TYPE_PREFIX + - HEADER_MATCH_TYPE_SUFFIX + format: int32 + type: string + value: + type: string + type: object + type: array + method: + description: Method specifies a gRPC request service/method + matcher. If this field is not specified, all services + and methods will match. + properties: + method: + description: "Value of the method to match against. + If left empty or omitted, will match all services. + \n At least one of Service and Method MUST be a + non-empty string.}" + type: string + service: + description: "Value of the service to match against. + If left empty or omitted, will match any service. + \n At least one of Service and Method MUST be a + non-empty string." + type: string + type: + description: 'Type specifies how to match against + the service and/or method. Support: Core (Exact + with service and method specified)' + enum: + - GRPC_METHOD_MATCH_TYPE_UNSPECIFIED + - GRPC_METHOD_MATCH_TYPE_EXACT + - GRPC_METHOD_MATCH_TYPE_REGEX + format: int32 + type: string + type: object + type: object + type: array + retries: + properties: + number: + description: Number is the number of times to retry the + request when a retryable result occurs. + properties: + value: + description: The uint32 value. + format: int32 + type: integer + type: object + onConditions: + description: RetryOn allows setting envoy specific conditions + when a request should be automatically retried. + items: + type: string + type: array + onConnectFailure: + description: RetryOnConnectFailure allows for connection + failure errors to trigger a retry. + type: boolean + onStatusCodes: + description: RetryOnStatusCodes is a flat list of http response + status codes that are eligible for retry. This again should + be feasible in any reasonable proxy. + items: + format: int32 + type: integer + type: array + type: object + timeouts: + description: HTTPRouteTimeouts defines timeouts that can be + configured for an HTTPRoute or GRPCRoute. + properties: + idle: + description: Idle specifies the total amount of time permitted + for the request stream to be idle. + format: duration + properties: + nanos: + description: Signed fractions of a second at nanosecond + resolution of the span of time. Durations less than + one second are represented with a 0 `seconds` field + and a positive or negative `nanos` field. For durations + of one second or more, a non-zero value for the `nanos` + field must be of the same sign as the `seconds` field. + Must be from -999,999,999 to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the span of time. Must + be from -315,576,000,000 to +315,576,000,000 inclusive. + Note: these bounds are computed from: 60 sec/min * + 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years' + format: int64 + type: integer + type: object + request: + description: RequestTimeout is the total amount of time + permitted for the entire downstream request (and retries) + to be processed. + format: duration + properties: + nanos: + description: Signed fractions of a second at nanosecond + resolution of the span of time. Durations less than + one second are represented with a 0 `seconds` field + and a positive or negative `nanos` field. For durations + of one second or more, a non-zero value for the `nanos` + field must be of the same sign as the `seconds` field. + Must be from -999,999,999 to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the span of time. Must + be from -315,576,000,000 to +315,576,000,000 inclusive. + Note: these bounds are computed from: 60 sec/min * + 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years' + format: int64 + type: integer + type: object + type: object + type: object + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/control-plane/config/crd/bases/mesh.consul.hashicorp.com_httproutes.yaml b/control-plane/config/crd/bases/mesh.consul.hashicorp.com_httproutes.yaml new file mode 100644 index 0000000000..46bf7162a6 --- /dev/null +++ b/control-plane/config/crd/bases/mesh.consul.hashicorp.com_httproutes.yaml @@ -0,0 +1,668 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.12.1 + name: httproutes.mesh.consul.hashicorp.com +spec: + group: mesh.consul.hashicorp.com + names: + kind: HTTPRoute + listKind: HTTPRouteList + plural: httproutes + shortNames: + - http-route + singular: httproute + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v2beta1 + schema: + openAPIV3Schema: + description: HTTPRoute is the Schema for the HTTP Route API + 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: "NOTE: this should align to the GAMMA/gateway-api version, + or at least be easily translatable. \n https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.HTTPRoute + \n This is a Resource type." + properties: + hostnames: + description: "Hostnames are the hostnames for which this HTTPRoute + should respond to requests. \n This is only valid for north/south." + items: + type: string + type: array + parentRefs: + description: "ParentRefs references the resources (usually Services) + that a Route wants to be attached to. \n It is invalid to reference + an identical parent more than once. It is valid to reference multiple + distinct sections within the same parent resource." + items: + description: 'NOTE: roughly equivalent to structs.ResourceReference' + properties: + port: + description: For east/west this is the name of the Consul Service + port to direct traffic to or empty to imply all. For north/south + this is TBD. + type: string + ref: + description: For east/west configuration, this should point + to a Service. For north/south it should point to a Gateway. + properties: + name: + description: Name is the user-given name of the resource + (e.g. the "billing" service). + type: string + section: + description: Section identifies which part of the resource + the condition relates to. + type: string + tenancy: + description: Tenancy identifies the tenancy units (i.e. + partition, namespace) in which the resource resides. + properties: + namespace: + description: "Namespace further isolates resources within + a partition. https://developer.hashicorp.com/consul/docs/enterprise/namespaces + \n When using the List and WatchList endpoints, provide + the wildcard value \"*\" to list resources across + all namespaces." + type: string + partition: + description: "Partition is the topmost administrative + boundary within a cluster. https://developer.hashicorp.com/consul/docs/enterprise/admin-partitions + \n When using the List and WatchList endpoints, provide + the wildcard value \"*\" to list resources across + all partitions." + type: string + peerName: + description: "PeerName identifies which peer the resource + is imported from. https://developer.hashicorp.com/consul/docs/connect/cluster-peering + \n When using the List and WatchList endpoints, provide + the wildcard value \"*\" to list resources across + all peers." + type: string + type: object + type: + description: Type identifies the resource's type. + properties: + group: + description: Group describes the area of functionality + to which this resource type relates (e.g. "catalog", + "authorization"). + type: string + groupVersion: + description: GroupVersion is incremented when sweeping + or backward-incompatible changes are made to the group's + resource types. + type: string + kind: + description: Kind identifies the specific resource type + within the group. + type: string + type: object + type: object + type: object + type: array + rules: + description: Rules are a list of HTTP-based routing rules that this + route should use for constructing a routing table. + items: + description: HTTPRouteRule specifies the routing rules used to determine + what upstream service an HTTP request is routed to. + properties: + backendRefs: + description: "BackendRefs defines the backend(s) where matching + requests should be sent. \n Failure behavior here depends + on how many BackendRefs are specified and how many are invalid. + \n If all entries in BackendRefs are invalid, and there are + also no filters specified in this route rule, all traffic + which matches this rule MUST receive a 500 status code. \n + See the HTTPBackendRef definition for the rules about what + makes a single HTTPBackendRef invalid. \n When a HTTPBackendRef + is invalid, 500 status codes MUST be returned for requests + that would have otherwise been routed to an invalid backend. + If multiple backends are specified, and some are invalid, + the proportion of requests that would otherwise have been + routed to an invalid backend MUST receive a 500 status code. + \n For example, if two backends are specified with equal weights, + and one is invalid, 50 percent of traffic must receive a 500. + Implementations may choose how that 50 percent is determined." + items: + properties: + backendRef: + properties: + datacenter: + type: string + port: + description: "For east/west this is the name of the + Consul Service port to direct traffic to or empty + to imply using the same value as the parent ref. + \n For north/south this is TBD." + type: string + ref: + description: For east/west configuration, this should + point to a Service. + properties: + name: + description: Name is the user-given name of the + resource (e.g. the "billing" service). + type: string + section: + description: Section identifies which part of + the resource the condition relates to. + type: string + tenancy: + description: Tenancy identifies the tenancy units + (i.e. partition, namespace) in which the resource + resides. + properties: + namespace: + description: "Namespace further isolates resources + within a partition. https://developer.hashicorp.com/consul/docs/enterprise/namespaces + \n When using the List and WatchList endpoints, + provide the wildcard value \"*\" to list + resources across all namespaces." + type: string + partition: + description: "Partition is the topmost administrative + boundary within a cluster. https://developer.hashicorp.com/consul/docs/enterprise/admin-partitions + \n When using the List and WatchList endpoints, + provide the wildcard value \"*\" to list + resources across all partitions." + type: string + peerName: + description: "PeerName identifies which peer + the resource is imported from. https://developer.hashicorp.com/consul/docs/connect/cluster-peering + \n When using the List and WatchList endpoints, + provide the wildcard value \"*\" to list + resources across all peers." + type: string + type: object + type: + description: Type identifies the resource's type. + properties: + group: + description: Group describes the area of functionality + to which this resource type relates (e.g. + "catalog", "authorization"). + type: string + groupVersion: + description: GroupVersion is incremented when + sweeping or backward-incompatible changes + are made to the group's resource types. + type: string + kind: + description: Kind identifies the specific + resource type within the group. + type: string + type: object + type: object + 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. + items: + properties: + requestHeaderModifier: + description: RequestHeaderModifier defines a schema + for a filter that modifies request headers. + properties: + add: + description: Add adds the given header(s) (name, + value) to the request before the action. It + appends to any existing values associated + with the header name. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + remove: + description: Remove the given header(s) from + the HTTP request before the action. The value + of Remove is a list of HTTP header names. + Note that the header names are case-insensitive + (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + items: + type: string + type: array + set: + description: Set overwrites the request with + the given header (name, value) before the + action. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + type: object + responseHeaderModifier: + description: ResponseHeaderModifier defines a schema + for a filter that modifies response headers. + properties: + add: + description: Add adds the given header(s) (name, + value) to the request before the action. It + appends to any existing values associated + with the header name. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + remove: + description: Remove the given header(s) from + the HTTP request before the action. The value + of Remove is a list of HTTP header names. + Note that the header names are case-insensitive + (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + items: + type: string + type: array + set: + description: Set overwrites the request with + the given header (name, value) before the + action. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + type: object + urlRewrite: + description: URLRewrite defines a schema for a filter + that modifies a request during forwarding. + properties: + pathPrefix: + type: string + type: object + type: object + type: array + weight: + description: "Weight specifies the proportion of requests + forwarded to the referenced backend. This is computed + as weight/(sum of all weights in this BackendRefs 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." + format: int32 + type: integer + type: object + type: array + filters: + items: + properties: + requestHeaderModifier: + description: RequestHeaderModifier defines a schema for + a filter that modifies request headers. + properties: + add: + description: Add adds the given header(s) (name, value) + to the request before the action. It appends to + any existing values associated with the header name. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + remove: + description: Remove the given header(s) from the HTTP + request before the action. The value of Remove is + a list of HTTP header names. Note that the header + names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + items: + type: string + type: array + set: + description: Set overwrites the request with the given + header (name, value) before the action. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + type: object + responseHeaderModifier: + description: ResponseHeaderModifier defines a schema for + a filter that modifies response headers. + properties: + add: + description: Add adds the given header(s) (name, value) + to the request before the action. It appends to + any existing values associated with the header name. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + remove: + description: Remove the given header(s) from the HTTP + request before the action. The value of Remove is + a list of HTTP header names. Note that the header + names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). + items: + type: string + type: array + set: + description: Set overwrites the request with the given + header (name, value) before the action. + items: + properties: + name: + type: string + value: + type: string + type: object + type: array + type: object + urlRewrite: + description: URLRewrite defines a schema for a filter + that modifies a request during forwarding. + properties: + pathPrefix: + type: string + type: object + type: object + type: array + matches: + items: + properties: + headers: + description: Headers specifies HTTP request header matchers. + Multiple match values are ANDed together, meaning, a + request must match all the specified headers to select + the route. + items: + properties: + invert: + description: 'NOTE: not in gamma; service-router + compat' + type: boolean + name: + description: "Name is the name of the HTTP Header + to be matched. Name matching MUST be case insensitive. + (See https://tools.ietf.org/html/rfc7230#section-3.2). + \n If multiple entries specify equivalent header + names, only the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent header name MUST be + ignored. Due to the case-insensitivity of header + names, “foo” and “Foo” are considered equivalent. + \n When a header is repeated in an HTTP request, + it is implementation-specific behavior as to how + this is represented. Generally, proxies should + follow the guidance from the RFC: https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 + regarding processing a repeated header, with special + handling for “Set-Cookie”." + type: string + type: + description: Type specifies how to match against + the value of the header. + enum: + - HEADER_MATCH_TYPE_UNSPECIFIED + - HEADER_MATCH_TYPE_EXACT + - HEADER_MATCH_TYPE_REGEX + - HEADER_MATCH_TYPE_PRESENT + - HEADER_MATCH_TYPE_PREFIX + - HEADER_MATCH_TYPE_SUFFIX + format: int32 + type: string + value: + description: Value is the value of HTTP Header to + be matched. + type: string + type: object + type: array + method: + description: Method specifies HTTP method matcher. When + specified, this route will be matched only if the request + has the specified method. + type: string + path: + 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: + description: Type specifies how to match against the + path Value. + enum: + - PATH_MATCH_TYPE_UNSPECIFIED + - PATH_MATCH_TYPE_EXACT + - PATH_MATCH_TYPE_PREFIX + - PATH_MATCH_TYPE_REGEX + format: int32 + type: string + value: + description: Value of the HTTP path to match against. + type: string + type: object + queryParams: + description: QueryParams specifies HTTP query parameter + matchers. Multiple match values are ANDed together, + meaning, a request must match all the specified query + parameters to select the route. + items: + properties: + name: + description: "Name is the name of the HTTP query + param to be matched. This must be an exact string + match. (See https://tools.ietf.org/html/rfc7230#section-2.7.3). + \n If multiple entries specify equivalent query + param names, only the first entry with an equivalent + name MUST be considered for a match. Subsequent + entries with an equivalent query param name MUST + be ignored. \n If a query param is repeated in + an HTTP request, the behavior is purposely left + undefined, since different data planes have different + capabilities. However, it is recommended that + implementations should match against the first + value of the param if the data plane supports + it, as this behavior is expected in other load + balancing contexts outside of the Gateway API. + \n Users SHOULD NOT route traffic based on repeated + query params to guard themselves against potential + differences in the implementations." + type: string + type: + description: Type specifies how to match against + the value of the query parameter. + enum: + - QUERY_PARAM_MATCH_TYPE_UNSPECIFIED + - QUERY_PARAM_MATCH_TYPE_EXACT + - QUERY_PARAM_MATCH_TYPE_REGEX + - QUERY_PARAM_MATCH_TYPE_PRESENT + format: int32 + type: string + value: + description: Value is the value of HTTP query param + to be matched. + type: string + type: object + type: array + type: object + type: array + retries: + properties: + number: + description: Number is the number of times to retry the + request when a retryable result occurs. + properties: + value: + description: The uint32 value. + format: int32 + type: integer + type: object + onConditions: + description: RetryOn allows setting envoy specific conditions + when a request should be automatically retried. + items: + type: string + type: array + onConnectFailure: + description: RetryOnConnectFailure allows for connection + failure errors to trigger a retry. + type: boolean + onStatusCodes: + description: RetryOnStatusCodes is a flat list of http response + status codes that are eligible for retry. This again should + be feasible in any reasonable proxy. + items: + format: int32 + type: integer + type: array + type: object + timeouts: + description: HTTPRouteTimeouts defines timeouts that can be + configured for an HTTPRoute or GRPCRoute. + properties: + idle: + description: Idle specifies the total amount of time permitted + for the request stream to be idle. + format: duration + properties: + nanos: + description: Signed fractions of a second at nanosecond + resolution of the span of time. Durations less than + one second are represented with a 0 `seconds` field + and a positive or negative `nanos` field. For durations + of one second or more, a non-zero value for the `nanos` + field must be of the same sign as the `seconds` field. + Must be from -999,999,999 to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the span of time. Must + be from -315,576,000,000 to +315,576,000,000 inclusive. + Note: these bounds are computed from: 60 sec/min * + 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years' + format: int64 + type: integer + type: object + request: + description: RequestTimeout is the total amount of time + permitted for the entire downstream request (and retries) + to be processed. + format: duration + properties: + nanos: + description: Signed fractions of a second at nanosecond + resolution of the span of time. Durations less than + one second are represented with a 0 `seconds` field + and a positive or negative `nanos` field. For durations + of one second or more, a non-zero value for the `nanos` + field must be of the same sign as the `seconds` field. + Must be from -999,999,999 to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the span of time. Must + be from -315,576,000,000 to +315,576,000,000 inclusive. + Note: these bounds are computed from: 60 sec/min * + 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years' + format: int64 + type: integer + type: object + type: object + type: object + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/control-plane/config/crd/bases/mesh.consul.hashicorp.com_proxyconfigurations.yaml b/control-plane/config/crd/bases/mesh.consul.hashicorp.com_proxyconfigurations.yaml new file mode 100644 index 0000000000..1d15b34111 --- /dev/null +++ b/control-plane/config/crd/bases/mesh.consul.hashicorp.com_proxyconfigurations.yaml @@ -0,0 +1,418 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.12.1 + name: proxyconfigurations.mesh.consul.hashicorp.com +spec: + group: mesh.consul.hashicorp.com + names: + kind: ProxyConfiguration + listKind: ProxyConfigurationList + plural: proxyconfigurations + shortNames: + - proxy-configuration + singular: proxyconfiguration + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v2beta1 + schema: + openAPIV3Schema: + description: ProxyConfiguration is the Schema for the TCP Routes API + 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: This is a Resource type. + properties: + bootstrapConfig: + description: bootstrap_config is the configuration that requires proxies + to be restarted to be applied. + properties: + dogstatsdUrl: + type: string + overrideJsonTpl: + type: string + prometheusBindAddr: + type: string + readyBindAddr: + type: string + staticClustersJson: + type: string + staticListenersJson: + type: string + statsBindAddr: + type: string + statsConfigJson: + type: string + statsFlushInterval: + type: string + statsSinksJson: + type: string + statsTags: + items: + type: string + type: array + statsdUrl: + type: string + telemetryCollectorBindSocketDir: + type: string + tracingConfigJson: + type: string + type: object + dynamicConfig: + description: dynamic_config is the configuration that could be changed + dynamically (i.e. without needing restart). + properties: + accessLogs: + description: AccessLogs configures the output and format of Envoy + access logs + properties: + disableListenerLogs: + description: DisableListenerLogs turns off just listener logs + for connections rejected by Envoy because they don't have + a matching listener filter. + type: boolean + enabled: + description: Enabled turns off all access logging + type: boolean + jsonFormat: + description: The presence of one format string or the other + implies the access log string encoding. Defining both is + invalid. + type: string + path: + description: Path is the output file to write logs + type: string + textFormat: + type: string + type: + description: 'Type selects the output for logs: "file", "stderr". + "stdout"' + enum: + - LOG_SINK_TYPE_DEFAULT + - LOG_SINK_TYPE_FILE + - LOG_SINK_TYPE_STDERR + - LOG_SINK_TYPE_STDOUT + format: int32 + type: string + type: object + envoyExtensions: + items: + description: EnvoyExtension has configuration for an extension + that patches Envoy resources. + properties: + arguments: + type: object + x-kubernetes-preserve-unknown-fields: true + consulVersion: + type: string + envoyVersion: + type: string + name: + type: string + required: + type: boolean + type: object + type: array + exposeConfig: + properties: + exposePaths: + items: + properties: + listenerPort: + format: int32 + type: integer + localPathPort: + format: int32 + type: integer + path: + type: string + protocol: + enum: + - EXPOSE_PATH_PROTOCOL_HTTP + - EXPOSE_PATH_PROTOCOL_HTTP2 + format: int32 + type: string + type: object + type: array + type: object + inboundConnections: + description: inbound_connections configures inbound connections + to the proxy. + properties: + balanceInboundConnections: + enum: + - BALANCE_CONNECTIONS_DEFAULT + - BALANCE_CONNECTIONS_EXACT + format: int32 + type: string + maxInboundConnections: + format: int64 + type: integer + type: object + listenerTracingJson: + type: string + localClusterJson: + type: string + localConnection: + additionalProperties: + description: Referenced by ProxyConfiguration + properties: + connectTimeout: + description: "A Duration represents a signed, fixed-length + span of time represented as a count of seconds and fractions + of seconds at nanosecond resolution. It is independent + of any calendar and concepts like \"day\" or \"month\". + It is related to Timestamp in that the difference between + two Timestamp values is a Duration and it can be added + or subtracted from a Timestamp. Range is approximately + +-10,000 years. \n # Examples \n Example 1: Compute Duration + from two Timestamps in pseudo code. \n Timestamp start + = ...; Timestamp end = ...; Duration duration = ...; \n + duration.seconds = end.seconds - start.seconds; duration.nanos + = end.nanos - start.nanos; \n if (duration.seconds < 0 + && duration.nanos > 0) { duration.seconds += 1; duration.nanos + -= 1000000000; } else if (duration.seconds > 0 && duration.nanos + < 0) { duration.seconds -= 1; duration.nanos += 1000000000; + } \n Example 2: Compute Timestamp from Timestamp + Duration + in pseudo code. \n Timestamp start = ...; Duration duration + = ...; Timestamp end = ...; \n end.seconds = start.seconds + + duration.seconds; end.nanos = start.nanos + duration.nanos; + \n if (end.nanos < 0) { end.seconds -= 1; end.nanos += + 1000000000; } else if (end.nanos >= 1000000000) { end.seconds + += 1; end.nanos -= 1000000000; } \n Example 3: Compute + Duration from datetime.timedelta in Python. \n td = datetime.timedelta(days=3, + minutes=10) duration = Duration() duration.FromTimedelta(td) + \n # JSON Mapping \n In JSON format, the Duration type + is encoded as a string rather than an object, where the + string ends in the suffix \"s\" (indicating seconds) and + is preceded by the number of seconds, with nanoseconds + expressed as fractional seconds. For example, 3 seconds + with 0 nanoseconds should be encoded in JSON format as + \"3s\", while 3 seconds and 1 nanosecond should be expressed + in JSON format as \"3.000000001s\", and 3 seconds and + 1 microsecond should be expressed in JSON format as \"3.000001s\"." + format: duration + properties: + nanos: + description: Signed fractions of a second at nanosecond + resolution of the span of time. Durations less than + one second are represented with a 0 `seconds` field + and a positive or negative `nanos` field. For durations + of one second or more, a non-zero value for the `nanos` + field must be of the same sign as the `seconds` field. + Must be from -999,999,999 to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the span of time. Must + be from -315,576,000,000 to +315,576,000,000 inclusive. + Note: these bounds are computed from: 60 sec/min * + 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years' + format: int64 + type: integer + type: object + requestTimeout: + description: "A Duration represents a signed, fixed-length + span of time represented as a count of seconds and fractions + of seconds at nanosecond resolution. It is independent + of any calendar and concepts like \"day\" or \"month\". + It is related to Timestamp in that the difference between + two Timestamp values is a Duration and it can be added + or subtracted from a Timestamp. Range is approximately + +-10,000 years. \n # Examples \n Example 1: Compute Duration + from two Timestamps in pseudo code. \n Timestamp start + = ...; Timestamp end = ...; Duration duration = ...; \n + duration.seconds = end.seconds - start.seconds; duration.nanos + = end.nanos - start.nanos; \n if (duration.seconds < 0 + && duration.nanos > 0) { duration.seconds += 1; duration.nanos + -= 1000000000; } else if (duration.seconds > 0 && duration.nanos + < 0) { duration.seconds -= 1; duration.nanos += 1000000000; + } \n Example 2: Compute Timestamp from Timestamp + Duration + in pseudo code. \n Timestamp start = ...; Duration duration + = ...; Timestamp end = ...; \n end.seconds = start.seconds + + duration.seconds; end.nanos = start.nanos + duration.nanos; + \n if (end.nanos < 0) { end.seconds -= 1; end.nanos += + 1000000000; } else if (end.nanos >= 1000000000) { end.seconds + += 1; end.nanos -= 1000000000; } \n Example 3: Compute + Duration from datetime.timedelta in Python. \n td = datetime.timedelta(days=3, + minutes=10) duration = Duration() duration.FromTimedelta(td) + \n # JSON Mapping \n In JSON format, the Duration type + is encoded as a string rather than an object, where the + string ends in the suffix \"s\" (indicating seconds) and + is preceded by the number of seconds, with nanoseconds + expressed as fractional seconds. For example, 3 seconds + with 0 nanoseconds should be encoded in JSON format as + \"3s\", while 3 seconds and 1 nanosecond should be expressed + in JSON format as \"3.000000001s\", and 3 seconds and + 1 microsecond should be expressed in JSON format as \"3.000001s\"." + format: duration + properties: + nanos: + description: Signed fractions of a second at nanosecond + resolution of the span of time. Durations less than + one second are represented with a 0 `seconds` field + and a positive or negative `nanos` field. For durations + of one second or more, a non-zero value for the `nanos` + field must be of the same sign as the `seconds` field. + Must be from -999,999,999 to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the span of time. Must + be from -315,576,000,000 to +315,576,000,000 inclusive. + Note: these bounds are computed from: 60 sec/min * + 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years' + format: int64 + type: integer + type: object + type: object + description: local_connection is the configuration that should + be used to connect to the local application provided per-port. + The map keys should correspond to port names on the workload. + type: object + localWorkloadAddress: + description: "deprecated: local_workload_address, local_workload_port, + and local_workload_socket_path are deprecated and are only needed + for migration of existing resources. \n Deprecated: Marked as + deprecated in pbmesh/v2beta1/proxy_configuration.proto." + type: string + localWorkloadPort: + description: 'Deprecated: Marked as deprecated in pbmesh/v2beta1/proxy_configuration.proto.' + format: int32 + type: integer + localWorkloadSocketPath: + description: 'Deprecated: Marked as deprecated in pbmesh/v2beta1/proxy_configuration.proto.' + type: string + meshGatewayMode: + enum: + - MESH_GATEWAY_MODE_UNSPECIFIED + - MESH_GATEWAY_MODE_NONE + - MESH_GATEWAY_MODE_LOCAL + - MESH_GATEWAY_MODE_REMOTE + format: int32 + type: string + mode: + description: mode indicates the proxy's mode. This will default + to 'transparent'. + enum: + - PROXY_MODE_DEFAULT + - PROXY_MODE_TRANSPARENT + - PROXY_MODE_DIRECT + format: int32 + type: string + mutualTlsMode: + enum: + - MUTUAL_TLS_MODE_DEFAULT + - MUTUAL_TLS_MODE_STRICT + - MUTUAL_TLS_MODE_PERMISSIVE + format: int32 + type: string + publicListenerJson: + type: string + transparentProxy: + properties: + dialedDirectly: + description: dialed_directly indicates whether this proxy + should be dialed using original destination IP in the connection + rather than load balance between all endpoints. + type: boolean + outboundListenerPort: + description: outbound_listener_port is the port for the proxy's + outbound listener. This defaults to 15001. + format: int32 + type: integer + type: object + type: object + opaqueConfig: + description: "deprecated: prevent usage when using v2 APIs directly. + needed for backwards compatibility \n Deprecated: Marked as deprecated + in pbmesh/v2beta1/proxy_configuration.proto." + type: object + x-kubernetes-preserve-unknown-fields: true + workloads: + description: Selection of workloads this proxy configuration should + apply to. These can be prefixes or specific workload names. + properties: + filter: + type: string + names: + items: + type: string + type: array + prefixes: + items: + type: string + type: array + type: object + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/control-plane/config/crd/bases/mesh.consul.hashicorp.com_tcproutes.yaml b/control-plane/config/crd/bases/mesh.consul.hashicorp.com_tcproutes.yaml new file mode 100644 index 0000000000..21a3a9c5ec --- /dev/null +++ b/control-plane/config/crd/bases/mesh.consul.hashicorp.com_tcproutes.yaml @@ -0,0 +1,273 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.12.1 + name: tcproutes.mesh.consul.hashicorp.com +spec: + group: mesh.consul.hashicorp.com + names: + kind: TCPRoute + listKind: TCPRouteList + plural: tcproutes + shortNames: + - tcp-route + singular: tcproute + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The sync status of the resource with Consul + jsonPath: .status.conditions[?(@.type=="Synced")].status + name: Synced + type: string + - description: The last successful synced time of the resource with Consul + jsonPath: .status.lastSyncedTime + name: Last Synced + type: date + - description: The age of the resource + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v2beta1 + schema: + openAPIV3Schema: + description: TCPRoute is the Schema for the TCP Route API + 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: "NOTE: this should align to the GAMMA/gateway-api version, + or at least be easily translatable. \n https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.TCPRoute + \n This is a Resource type." + properties: + parentRefs: + description: "ParentRefs references the resources (usually Services) + that a Route wants to be attached to. \n It is invalid to reference + an identical parent more than once. It is valid to reference multiple + distinct sections within the same parent resource." + items: + description: 'NOTE: roughly equivalent to structs.ResourceReference' + properties: + port: + description: For east/west this is the name of the Consul Service + port to direct traffic to or empty to imply all. For north/south + this is TBD. + type: string + ref: + description: For east/west configuration, this should point + to a Service. For north/south it should point to a Gateway. + properties: + name: + description: Name is the user-given name of the resource + (e.g. the "billing" service). + type: string + section: + description: Section identifies which part of the resource + the condition relates to. + type: string + tenancy: + description: Tenancy identifies the tenancy units (i.e. + partition, namespace) in which the resource resides. + properties: + namespace: + description: "Namespace further isolates resources within + a partition. https://developer.hashicorp.com/consul/docs/enterprise/namespaces + \n When using the List and WatchList endpoints, provide + the wildcard value \"*\" to list resources across + all namespaces." + type: string + partition: + description: "Partition is the topmost administrative + boundary within a cluster. https://developer.hashicorp.com/consul/docs/enterprise/admin-partitions + \n When using the List and WatchList endpoints, provide + the wildcard value \"*\" to list resources across + all partitions." + type: string + peerName: + description: "PeerName identifies which peer the resource + is imported from. https://developer.hashicorp.com/consul/docs/connect/cluster-peering + \n When using the List and WatchList endpoints, provide + the wildcard value \"*\" to list resources across + all peers." + type: string + type: object + type: + description: Type identifies the resource's type. + properties: + group: + description: Group describes the area of functionality + to which this resource type relates (e.g. "catalog", + "authorization"). + type: string + groupVersion: + description: GroupVersion is incremented when sweeping + or backward-incompatible changes are made to the group's + resource types. + type: string + kind: + description: Kind identifies the specific resource type + within the group. + type: string + type: object + type: object + type: object + type: array + rules: + description: Rules are a list of TCP matchers and actions. + items: + properties: + backendRefs: + description: BackendRefs defines the backend(s) where matching + requests should be sent. If unspecified or invalid (refers + to a non-existent resource or a Service with no endpoints), + the underlying implementation MUST actively reject connection + attempts to this backend. Connection rejections must respect + weight; if an invalid backend is requested to have 80% of + connections, then 80% of connections must be rejected instead. + items: + properties: + backendRef: + properties: + datacenter: + type: string + port: + description: "For east/west this is the name of the + Consul Service port to direct traffic to or empty + to imply using the same value as the parent ref. + \n For north/south this is TBD." + type: string + ref: + description: For east/west configuration, this should + point to a Service. + properties: + name: + description: Name is the user-given name of the + resource (e.g. the "billing" service). + type: string + section: + description: Section identifies which part of + the resource the condition relates to. + type: string + tenancy: + description: Tenancy identifies the tenancy units + (i.e. partition, namespace) in which the resource + resides. + properties: + namespace: + description: "Namespace further isolates resources + within a partition. https://developer.hashicorp.com/consul/docs/enterprise/namespaces + \n When using the List and WatchList endpoints, + provide the wildcard value \"*\" to list + resources across all namespaces." + type: string + partition: + description: "Partition is the topmost administrative + boundary within a cluster. https://developer.hashicorp.com/consul/docs/enterprise/admin-partitions + \n When using the List and WatchList endpoints, + provide the wildcard value \"*\" to list + resources across all partitions." + type: string + peerName: + description: "PeerName identifies which peer + the resource is imported from. https://developer.hashicorp.com/consul/docs/connect/cluster-peering + \n When using the List and WatchList endpoints, + provide the wildcard value \"*\" to list + resources across all peers." + type: string + type: object + type: + description: Type identifies the resource's type. + properties: + group: + description: Group describes the area of functionality + to which this resource type relates (e.g. + "catalog", "authorization"). + type: string + groupVersion: + description: GroupVersion is incremented when + sweeping or backward-incompatible changes + are made to the group's resource types. + type: string + kind: + description: Kind identifies the specific + resource type within the group. + type: string + type: object + type: object + type: object + weight: + description: "Weight specifies the proportion of requests + forwarded to the referenced backend. This is computed + as weight/(sum of all weights in this BackendRefs 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." + format: int32 + type: integer + type: object + type: array + type: object + type: array + type: object + status: + properties: + conditions: + description: Conditions indicate the latest available observations + of a resource's current state. + items: + description: 'Conditions define a readiness condition for a Consul + resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties' + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + required: + - status + - type + type: object + type: array + lastSyncedTime: + description: LastSyncedTime is the last time the resource successfully + synced with Consul. + format: date-time + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/control-plane/config/crd/external/gatewayclasses.gateway.networking.k8s.io.yaml b/control-plane/config/crd/external/gatewayclasses.gateway.networking.k8s.io.yaml index ff0b2fc2f6..044c7af939 100644 --- a/control-plane/config/crd/external/gatewayclasses.gateway.networking.k8s.io.yaml +++ b/control-plane/config/crd/external/gatewayclasses.gateway.networking.k8s.io.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: diff --git a/control-plane/config/crd/external/gateways.gateway.networking.k8s.io.yaml b/control-plane/config/crd/external/gateways.gateway.networking.k8s.io.yaml index fa64481667..b7a7c8a7d1 100644 --- a/control-plane/config/crd/external/gateways.gateway.networking.k8s.io.yaml +++ b/control-plane/config/crd/external/gateways.gateway.networking.k8s.io.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: diff --git a/control-plane/config/crd/external/grpcroutes.gateway.networking.k8s.io.yaml b/control-plane/config/crd/external/grpcroutes.gateway.networking.k8s.io.yaml index 8d190ea7b6..8f3ab6d385 100644 --- a/control-plane/config/crd/external/grpcroutes.gateway.networking.k8s.io.yaml +++ b/control-plane/config/crd/external/grpcroutes.gateway.networking.k8s.io.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: diff --git a/control-plane/config/crd/external/httproutes.gateway.networking.k8s.io.yaml b/control-plane/config/crd/external/httproutes.gateway.networking.k8s.io.yaml index 90c151a787..b455d788de 100644 --- a/control-plane/config/crd/external/httproutes.gateway.networking.k8s.io.yaml +++ b/control-plane/config/crd/external/httproutes.gateway.networking.k8s.io.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: diff --git a/control-plane/config/crd/external/referencegrants.gateway.networking.k8s.io.yaml b/control-plane/config/crd/external/referencegrants.gateway.networking.k8s.io.yaml index 5eee4889a4..cd39b9c12a 100644 --- a/control-plane/config/crd/external/referencegrants.gateway.networking.k8s.io.yaml +++ b/control-plane/config/crd/external/referencegrants.gateway.networking.k8s.io.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: diff --git a/control-plane/config/crd/external/tcproutes.gateway.networking.k8s.io.yaml b/control-plane/config/crd/external/tcproutes.gateway.networking.k8s.io.yaml index a136b28f41..906b442d31 100644 --- a/control-plane/config/crd/external/tcproutes.gateway.networking.k8s.io.yaml +++ b/control-plane/config/crd/external/tcproutes.gateway.networking.k8s.io.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: diff --git a/control-plane/config/crd/external/tlsroutes.gateway.networking.k8s.io.yaml b/control-plane/config/crd/external/tlsroutes.gateway.networking.k8s.io.yaml index cc3cf65d6c..2e22b04ef0 100644 --- a/control-plane/config/crd/external/tlsroutes.gateway.networking.k8s.io.yaml +++ b/control-plane/config/crd/external/tlsroutes.gateway.networking.k8s.io.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: diff --git a/control-plane/config/crd/external/udproutes.gateway.networking.k8s.io.yaml b/control-plane/config/crd/external/udproutes.gateway.networking.k8s.io.yaml index 204f8e4824..19b03dcd0b 100644 --- a/control-plane/config/crd/external/udproutes.gateway.networking.k8s.io.yaml +++ b/control-plane/config/crd/external/udproutes.gateway.networking.k8s.io.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: diff --git a/control-plane/config/rbac/role.yaml b/control-plane/config/rbac/role.yaml index 88c7c44980..cce5208eda 100644 --- a/control-plane/config/rbac/role.yaml +++ b/control-plane/config/rbac/role.yaml @@ -5,7 +5,6 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - creationTimestamp: null name: manager-role rules: - apiGroups: @@ -346,3 +345,83 @@ rules: - get - patch - update +- apiGroups: + - mesh.consul.hashicorp.com + resources: + - grpcroute + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - mesh.consul.hashicorp.com + resources: + - grpcroute/status + verbs: + - get + - patch + - update +- apiGroups: + - mesh.consul.hashicorp.com + resources: + - httproute + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - mesh.consul.hashicorp.com + resources: + - httproute/status + verbs: + - get + - patch + - update +- apiGroups: + - mesh.consul.hashicorp.com + resources: + - proxyconfiguration + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - mesh.consul.hashicorp.com + resources: + - proxyconfiguration/status + verbs: + - get + - patch + - update +- apiGroups: + - mesh.consul.hashicorp.com + resources: + - tcproute + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - mesh.consul.hashicorp.com + resources: + - tcproute/status + verbs: + - get + - patch + - update diff --git a/control-plane/config/webhook/manifests.yaml b/control-plane/config/webhook/manifests.yaml index 6b675d459a..b5e3bb52fd 100644 --- a/control-plane/config/webhook/manifests.yaml +++ b/control-plane/config/webhook/manifests.yaml @@ -5,7 +5,6 @@ apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration metadata: - creationTimestamp: null name: mutating-webhook-configuration webhooks: - admissionReviewVersions: @@ -344,11 +343,94 @@ webhooks: resources: - trafficpermissions sideEffects: None +- admissionReviewVersions: + - v1beta1 + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-v2beta1-grpcroute + failurePolicy: Fail + name: mutate-grpcroute.auth.consul.hashicorp.com + rules: + - apiGroups: + - auth.consul.hashicorp.com + apiVersions: + - v2beta1 + operations: + - CREATE + - UPDATE + resources: + - grpcroute + sideEffects: None +- admissionReviewVersions: + - v1beta1 + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-v2beta1-httproute + failurePolicy: Fail + name: mutate-httproute.auth.consul.hashicorp.com + rules: + - apiGroups: + - auth.consul.hashicorp.com + apiVersions: + - v2beta1 + operations: + - CREATE + - UPDATE + resources: + - httproute + sideEffects: None +- admissionReviewVersions: + - v1beta1 + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-v2beta1-proxyconfiguration + failurePolicy: Fail + name: mutate-proxyconfiguration.auth.consul.hashicorp.com + rules: + - apiGroups: + - auth.consul.hashicorp.com + apiVersions: + - v2beta1 + operations: + - CREATE + - UPDATE + resources: + - proxyconfiguration + sideEffects: None +- admissionReviewVersions: + - v1beta1 + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-v2beta1-tcproute + failurePolicy: Fail + name: mutate-tcproute.auth.consul.hashicorp.com + rules: + - apiGroups: + - auth.consul.hashicorp.com + apiVersions: + - v2beta1 + operations: + - CREATE + - UPDATE + resources: + - tcproute + sideEffects: None --- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: - creationTimestamp: null name: validating-webhook-configuration webhooks: - admissionReviewVersions: diff --git a/control-plane/connect-inject/controllers/endpointsv2/write_cache_test.go b/control-plane/connect-inject/controllers/endpointsv2/write_cache_test.go index 059af35257..2b22c5707a 100644 --- a/control-plane/connect-inject/controllers/endpointsv2/write_cache_test.go +++ b/control-plane/connect-inject/controllers/endpointsv2/write_cache_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package endpointsv2 import ( diff --git a/control-plane/connect-inject/controllers/pod/pod_controller_ent_test.go b/control-plane/connect-inject/controllers/pod/pod_controller_ent_test.go index 22af958fd9..8fdb2e0167 100644 --- a/control-plane/connect-inject/controllers/pod/pod_controller_ent_test.go +++ b/control-plane/connect-inject/controllers/pod/pod_controller_ent_test.go @@ -181,7 +181,7 @@ func TestReconcileCreatePodWithMirrorNamespaces(t *testing.T) { k8sObjects: func() []runtime.Object { pod := createPod(testPodName, metav1.NamespaceDefault, true, true) addProbesAndOriginalPodAnnotation(pod) - pod.Annotations[constants.AnnotationMeshDestinations] = "myPort.port.mySVC.svc:24601" + pod.Annotations[constants.AnnotationMeshDestinations] = "destination.port.mySVC.svc:24601" return []runtime.Object{pod} }, tproxy: false, @@ -425,7 +425,7 @@ func TestReconcileCreatePodWithDestinationNamespace(t *testing.T) { k8sObjects: func() []runtime.Object { pod := createPod(testPodName, metav1.NamespaceDefault, true, true) addProbesAndOriginalPodAnnotation(pod) - pod.Annotations[constants.AnnotationMeshDestinations] = "myPort.port.mySVC.svc:24601" + pod.Annotations[constants.AnnotationMeshDestinations] = "destination.port.mySVC.svc:24601" return []runtime.Object{pod} }, telemetry: true, diff --git a/control-plane/connect-inject/controllers/pod/pod_controller_test.go b/control-plane/connect-inject/controllers/pod/pod_controller_test.go index ed48f7bc78..3ae9f3747c 100644 --- a/control-plane/connect-inject/controllers/pod/pod_controller_test.go +++ b/control-plane/connect-inject/controllers/pod/pod_controller_test.go @@ -814,7 +814,7 @@ func TestDestinationsWrite(t *testing.T) { name: "labeled annotated destination with svc only", pod: func() *corev1.Pod { pod1 := createPod(podName, "", true, true) - pod1.Annotations[constants.AnnotationMeshDestinations] = "myPort.port.upstream1.svc:1234" + pod1.Annotations[constants.AnnotationMeshDestinations] = "destination.port.upstream1.svc:1234" return pod1 }, expected: &pbmesh.Destinations{ @@ -832,7 +832,7 @@ func TestDestinationsWrite(t *testing.T) { }, Name: "upstream1", }, - DestinationPort: "myPort", + DestinationPort: "destination", Datacenter: "", ListenAddr: &pbmesh.Destination_IpPort{ IpPort: &pbmesh.IPPortAddress{ @@ -850,10 +850,10 @@ func TestDestinationsWrite(t *testing.T) { name: "labeled annotated destination with svc, ns, and peer", pod: func() *corev1.Pod { pod1 := createPod(podName, "", true, true) - pod1.Annotations[constants.AnnotationMeshDestinations] = "myPort.port.upstream1.svc.ns1.ns.peer1.peer:1234" + pod1.Annotations[constants.AnnotationMeshDestinations] = "destination.port.upstream1.svc.ns1.ns.peer1.peer:1234" return pod1 }, - expErr: "error processing destination annotations: destination currently does not support peers: myPort.port.upstream1.svc.ns1.ns.peer1.peer:1234", + expErr: "error processing destination annotations: destination currently does not support peers: destination.port.upstream1.svc.ns1.ns.peer1.peer:1234", // TODO: uncomment this and remove expErr when peers is supported //expected: &pbmesh.Destinations{ // Workloads: &pbcatalog.WorkloadSelector{ @@ -870,7 +870,7 @@ func TestDestinationsWrite(t *testing.T) { // }, // Name: "upstream1", // }, - // DestinationPort: "myPort", + // DestinationPort: "destination", // Datacenter: "", // ListenAddr: &pbmesh.Destination_IpPort{ // IpPort: &pbmesh.IPPortAddress{ @@ -888,7 +888,7 @@ func TestDestinationsWrite(t *testing.T) { name: "labeled annotated destination with svc, ns, and partition", pod: func() *corev1.Pod { pod1 := createPod(podName, "", true, true) - pod1.Annotations[constants.AnnotationMeshDestinations] = "myPort.port.upstream1.svc.ns1.ns.part1.ap:1234" + pod1.Annotations[constants.AnnotationMeshDestinations] = "destination.port.upstream1.svc.ns1.ns.part1.ap:1234" return pod1 }, expected: &pbmesh.Destinations{ @@ -906,7 +906,7 @@ func TestDestinationsWrite(t *testing.T) { }, Name: "upstream1", }, - DestinationPort: "myPort", + DestinationPort: "destination", Datacenter: "", ListenAddr: &pbmesh.Destination_IpPort{ IpPort: &pbmesh.IPPortAddress{ @@ -924,10 +924,10 @@ func TestDestinationsWrite(t *testing.T) { name: "error labeled annotated destination error: invalid partition/dc/peer", pod: func() *corev1.Pod { pod1 := createPod(podName, "", true, true) - pod1.Annotations[constants.AnnotationMeshDestinations] = "myPort.port.upstream1.svc.ns1.ns.part1.err:1234" + pod1.Annotations[constants.AnnotationMeshDestinations] = "destination.port.upstream1.svc.ns1.ns.part1.err:1234" return pod1 }, - expErr: "error processing destination annotations: destination structured incorrectly: myPort.port.upstream1.svc.ns1.ns.part1.err:1234", + expErr: "error processing destination annotations: destination structured incorrectly: destination.port.upstream1.svc.ns1.ns.part1.err:1234", consulNamespacesEnabled: true, consulPartitionsEnabled: false, }, @@ -935,7 +935,7 @@ func TestDestinationsWrite(t *testing.T) { name: "unlabeled single destination", pod: func() *corev1.Pod { pod1 := createPod(podName, "", true, true) - pod1.Annotations[constants.AnnotationMeshDestinations] = "myPort.upstream:1234" + pod1.Annotations[constants.AnnotationMeshDestinations] = "destination.upstream:1234" return pod1 }, expected: &pbmesh.Destinations{ @@ -953,7 +953,7 @@ func TestDestinationsWrite(t *testing.T) { }, Name: "upstream", }, - DestinationPort: "myPort", + DestinationPort: "destination", Datacenter: "", ListenAddr: &pbmesh.Destination_IpPort{ IpPort: &pbmesh.IPPortAddress{ @@ -971,7 +971,7 @@ func TestDestinationsWrite(t *testing.T) { name: "unlabeled single destination with namespace and partition", pod: func() *corev1.Pod { pod1 := createPod(podName, "", true, true) - pod1.Annotations[constants.AnnotationMeshDestinations] = "myPort.upstream.foo.bar:1234" + pod1.Annotations[constants.AnnotationMeshDestinations] = "destination.upstream.foo.bar:1234" return pod1 }, expected: &pbmesh.Destinations{ @@ -989,7 +989,7 @@ func TestDestinationsWrite(t *testing.T) { }, Name: "upstream", }, - DestinationPort: "myPort", + DestinationPort: "destination", Datacenter: "", ListenAddr: &pbmesh.Destination_IpPort{ IpPort: &pbmesh.IPPortAddress{ @@ -1061,7 +1061,7 @@ func TestDestinationsDelete(t *testing.T) { name: "labeled annotated destination with svc only", pod: func() *corev1.Pod { pod1 := createPod(podName, "", true, true) - pod1.Annotations[constants.AnnotationMeshDestinations] = "myPort.port.upstream1.svc:1234" + pod1.Annotations[constants.AnnotationMeshDestinations] = "destination.port.upstream1.svc:1234" return pod1 }, existingDestinations: &pbmesh.Destinations{ @@ -1079,7 +1079,7 @@ func TestDestinationsDelete(t *testing.T) { }, Name: "upstream1", }, - DestinationPort: "myPort", + DestinationPort: "destination", Datacenter: "", ListenAddr: &pbmesh.Destination_IpPort{ IpPort: &pbmesh.IPPortAddress{ @@ -1315,7 +1315,7 @@ func TestReconcileCreatePod(t *testing.T) { k8sObjects: func() []runtime.Object { pod := createPod("foo", "", true, true) addProbesAndOriginalPodAnnotation(pod) - pod.Annotations[constants.AnnotationMeshDestinations] = "myPort.port.mySVC.svc:24601" + pod.Annotations[constants.AnnotationMeshDestinations] = "destination.port.mySVC.svc:24601" return []runtime.Object{pod} }, tproxy: false, @@ -1567,7 +1567,7 @@ func TestReconcileUpdatePod(t *testing.T) { podName: "foo", k8sObjects: func() []runtime.Object { pod := createPod("foo", "", true, true) - pod.Annotations[constants.AnnotationMeshDestinations] = "myPort.port.mySVC.svc:24601" + pod.Annotations[constants.AnnotationMeshDestinations] = "destination.port.mySVC.svc:24601" return []runtime.Object{pod} }, existingWorkload: createWorkload(), @@ -1587,7 +1587,7 @@ func TestReconcileUpdatePod(t *testing.T) { }, Name: "mySVC3", }, - DestinationPort: "myPort2", + DestinationPort: "destination2", Datacenter: "", ListenAddr: &pbmesh.Destination_IpPort{ IpPort: &pbmesh.IPPortAddress{ @@ -1955,7 +1955,7 @@ func createDestinations() *pbmesh.Destinations { }, Name: "mySVC", }, - DestinationPort: "myPort", + DestinationPort: "destination", Datacenter: "", ListenAddr: &pbmesh.Destination_IpPort{ IpPort: &pbmesh.IPPortAddress{ diff --git a/control-plane/go.mod b/control-plane/go.mod index 2b2c470e57..1f9c3e06fb 100644 --- a/control-plane/go.mod +++ b/control-plane/go.mod @@ -3,7 +3,7 @@ module github.com/hashicorp/consul-k8s/control-plane // TODO: remove these when the SDK is released for Consul 1.17 and coinciding patch releases replace ( // This replace directive is needed because `api` requires 0.4.1 of proto-public but we need an unreleased version - github.com/hashicorp/consul/proto-public v0.4.1 => github.com/hashicorp/consul/proto-public v0.1.2-0.20230929231147-632fd65c091c + github.com/hashicorp/consul/proto-public v0.4.1 => github.com/hashicorp/consul/proto-public v0.1.2-0.20231013204122-3d1a606c3b58 // This replace directive is needed because `api` requires 0.14.1 of `sdk` but we need an unreleased version github.com/hashicorp/consul/sdk v0.14.1 => github.com/hashicorp/consul/sdk v0.4.1-0.20231011203909-c26d5cf62cb9 ) diff --git a/control-plane/go.sum b/control-plane/go.sum index f04eab1013..d1b115f096 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -265,8 +265,8 @@ github.com/hashicorp/consul-server-connection-manager v0.1.4 h1:wrcSRV6WGXFBNpNb github.com/hashicorp/consul-server-connection-manager v0.1.4/go.mod h1:LMqHkALoLP0HUQKOG21xXYr0YPUayIQIHNTlmxG100E= github.com/hashicorp/consul/api v1.10.1-0.20230914174054-e5808d85f751 h1:LnzgDq4e7ZfM1+XS6S21B9taQrbfdydXenL1xHyG1PQ= github.com/hashicorp/consul/api v1.10.1-0.20230914174054-e5808d85f751/go.mod h1:/Fz5sgOC0a5XY0BmPGj7aDSZRNgySLm02lV4xkU4DS4= -github.com/hashicorp/consul/proto-public v0.1.2-0.20230929231147-632fd65c091c h1:d1ULTfDs6Hha01yfITC55MPGIsQpv0VqQfS45WZHJiY= -github.com/hashicorp/consul/proto-public v0.1.2-0.20230929231147-632fd65c091c/go.mod h1:KAOxsaELPpA7JX10kMeygAskAqsQnu3SPgeruMhYZMU= +github.com/hashicorp/consul/proto-public v0.1.2-0.20231013204122-3d1a606c3b58 h1:3VHvqLs2zTa9YWMsE4A9IricAZB6rAcXVe8e+pb5slw= +github.com/hashicorp/consul/proto-public v0.1.2-0.20231013204122-3d1a606c3b58/go.mod h1:KAOxsaELPpA7JX10kMeygAskAqsQnu3SPgeruMhYZMU= github.com/hashicorp/consul/sdk v0.4.1-0.20231011203909-c26d5cf62cb9 h1:j0Rvt1KiKIKlMc2UnA0ilHfIGo66SzrBztGZaCou3+w= github.com/hashicorp/consul/sdk v0.4.1-0.20231011203909-c26d5cf62cb9/go.mod h1:vFt03juSzocLRFo59NkeQHHmQa6+g7oU0pfzdI1mUhg= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= diff --git a/control-plane/subcommand/inject-connect/command.go b/control-plane/subcommand/inject-connect/command.go index 0d9fba6cb0..a81a290dc4 100644 --- a/control-plane/subcommand/inject-connect/command.go +++ b/control-plane/subcommand/inject-connect/command.go @@ -30,8 +30,9 @@ import ( gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + authv2beta1 "github.com/hashicorp/consul-k8s/control-plane/api/auth/v2beta1" + meshv2beta1 "github.com/hashicorp/consul-k8s/control-plane/api/mesh/v2beta1" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" - "github.com/hashicorp/consul-k8s/control-plane/api/v2beta1" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" "github.com/hashicorp/consul-k8s/control-plane/subcommand/common" "github.com/hashicorp/consul-k8s/control-plane/subcommand/flags" @@ -163,7 +164,8 @@ func init() { utilruntime.Must(gwv1alpha2.AddToScheme(scheme)) // V2 resources - utilruntime.Must(v2beta1.AddAuthToScheme(scheme)) + utilruntime.Must(authv2beta1.AddAuthToScheme(scheme)) + utilruntime.Must(meshv2beta1.AddMeshToScheme(scheme)) //+kubebuilder:scaffold:scheme } diff --git a/control-plane/subcommand/inject-connect/v2controllers.go b/control-plane/subcommand/inject-connect/v2controllers.go index da522c6840..abe4b38c53 100644 --- a/control-plane/subcommand/inject-connect/v2controllers.go +++ b/control-plane/subcommand/inject-connect/v2controllers.go @@ -11,8 +11,9 @@ import ( "sigs.k8s.io/controller-runtime/pkg/manager" ctrlRuntimeWebhook "sigs.k8s.io/controller-runtime/pkg/webhook" + authv2beta1 "github.com/hashicorp/consul-k8s/control-plane/api/auth/v2beta1" "github.com/hashicorp/consul-k8s/control-plane/api/common" - "github.com/hashicorp/consul-k8s/control-plane/api/v2beta1" + meshv2beta1 "github.com/hashicorp/consul-k8s/control-plane/api/mesh/v2beta1" "github.com/hashicorp/consul-k8s/control-plane/config-entries/controllersv2" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/controllers/endpointsv2" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/controllers/pod" @@ -140,6 +141,42 @@ func (c *Command) configureV2Controllers(ctx context.Context, mgr manager.Manage setupLog.Error(err, "unable to create controller", "controller", common.TrafficPermissions) return err } + if err := (&controllersv2.GRPCRouteController{ + MeshConfigController: meshConfigReconciler, + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controller").WithName(common.GRPCRoute), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", common.GRPCRoute) + return err + } + if err := (&controllersv2.HTTPRouteController{ + MeshConfigController: meshConfigReconciler, + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controller").WithName(common.HTTPRoute), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", common.HTTPRoute) + return err + } + if err := (&controllersv2.TCPRouteController{ + MeshConfigController: meshConfigReconciler, + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controller").WithName(common.TCPRoute), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", common.TCPRoute) + return err + } + if err := (&controllersv2.ProxyConfigurationController{ + MeshConfigController: meshConfigReconciler, + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controller").WithName(common.ProxyConfiguration), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", common.ProxyConfiguration) + return err + } mgr.GetWebhookServer().CertDir = c.flagCertDir @@ -187,11 +224,35 @@ func (c *Command) configureV2Controllers(ctx context.Context, mgr manager.Manage }}) mgr.GetWebhookServer().Register("/mutate-v2beta1-trafficpermissions", - &ctrlRuntimeWebhook.Admission{Handler: &v2beta1.TrafficPermissionsWebhook{ + &ctrlRuntimeWebhook.Admission{Handler: &authv2beta1.TrafficPermissionsWebhook{ Client: mgr.GetClient(), Logger: ctrl.Log.WithName("webhooks").WithName(common.TrafficPermissions), ConsulTenancyConfig: consulTenancyConfig, }}) + mgr.GetWebhookServer().Register("/mutate-v2beta1-proxyconfigurations", + &ctrlRuntimeWebhook.Admission{Handler: &meshv2beta1.ProxyConfigurationWebhook{ + Client: mgr.GetClient(), + Logger: ctrl.Log.WithName("webhooks").WithName(common.ProxyConfiguration), + ConsulTenancyConfig: consulTenancyConfig, + }}) + mgr.GetWebhookServer().Register("/mutate-v2beta1-httproute", + &ctrlRuntimeWebhook.Admission{Handler: &meshv2beta1.HTTPRouteWebhook{ + Client: mgr.GetClient(), + Logger: ctrl.Log.WithName("webhooks").WithName(common.HTTPRoute), + ConsulTenancyConfig: consulTenancyConfig, + }}) + mgr.GetWebhookServer().Register("/mutate-v2beta1-grpcroute", + &ctrlRuntimeWebhook.Admission{Handler: &meshv2beta1.GRPCRouteWebhook{ + Client: mgr.GetClient(), + Logger: ctrl.Log.WithName("webhooks").WithName(common.GRPCRoute), + ConsulTenancyConfig: consulTenancyConfig, + }}) + mgr.GetWebhookServer().Register("/mutate-v2beta1-tcproute", + &ctrlRuntimeWebhook.Admission{Handler: &meshv2beta1.TCPRouteWebhook{ + Client: mgr.GetClient(), + Logger: ctrl.Log.WithName("webhooks").WithName(common.TCPRoute), + ConsulTenancyConfig: consulTenancyConfig, + }}) if err := mgr.AddReadyzCheck("ready", webhook.ReadinessCheck{CertDir: c.flagCertDir}.Ready); err != nil { setupLog.Error(err, "unable to create readiness check") diff --git a/hack/camel-crds/go.mod b/hack/camel-crds/go.mod new file mode 100644 index 0000000000..0262e70483 --- /dev/null +++ b/hack/camel-crds/go.mod @@ -0,0 +1,15 @@ +module github.com/hashicorp/consul-k8s/hack/copy-crds-to-chart + +go 1.20 + +require ( + github.com/iancoleman/strcase v0.3.0 + sigs.k8s.io/yaml v1.3.0 +) + +require ( + github.com/kr/pretty v0.3.0 // indirect + github.com/rogpeppe/go-internal v1.10.0 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) diff --git a/hack/camel-crds/go.sum b/hack/camel-crds/go.sum new file mode 100644 index 0000000000..21efe55d32 --- /dev/null +++ b/hack/camel-crds/go.sum @@ -0,0 +1,25 @@ +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +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/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= +github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +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/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/hack/camel-crds/main.go b/hack/camel-crds/main.go new file mode 100644 index 0000000000..dde2ac3de2 --- /dev/null +++ b/hack/camel-crds/main.go @@ -0,0 +1,117 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// Script to parse a YAML CRD file and change all the +// snake_case keys to camelCase and rewrite the file in-situ +package main + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/iancoleman/strcase" + "sigs.k8s.io/yaml" +) + +func main() { + if len(os.Args) != 1 { + fmt.Println("Usage: go run ./...") + os.Exit(1) + } + + if err := realMain(); err != nil { + fmt.Printf("Error: %s\n", err) + os.Exit(1) + } + os.Exit(0) +} + +func realMain() error { + root := "../../control-plane/config/crd/" + // explicitly ignore the `external` folder since we only want this to apply to CRDs that we have built-in this project. + dirs := []string{"bases"} + + for _, dir := range dirs { + err := filepath.Walk(root+dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() || filepath.Ext(path) != ".yaml" || filepath.Base(path) == "kustomization.yaml" { + return nil + } + printf("processing %s", filepath.Base(path)) + + contentBytes, err := os.ReadFile(path) + if err != nil { + return err + } + + jsonBytes, err := yaml.YAMLToJSON(contentBytes) + if err != nil { + return err + } + fixedJsonBytes := convertKeys(jsonBytes) + contentsCamel, err := yaml.JSONToYAML(fixedJsonBytes) + return os.WriteFile(path, contentsCamel, os.ModePerm) + }) + if err != nil { + return err + } + } + return nil +} + +func convertKeys(j json.RawMessage) json.RawMessage { + m := make(map[string]json.RawMessage) + n := make([]json.RawMessage, 0) + array := false + if err := json.Unmarshal(j, &m); err != nil { + // Not a JSON object + errArray := json.Unmarshal(j, &n) + if errArray != nil { + return j + } else { + array = true + } + } + if !array { + for k, v := range m { + if k == "annotations" { + continue + } + var fixed string + if !strings.Contains(k, "_") { + fixed = k + } else { + fixed = strcase.ToLowerCamel(k) + } + delete(m, k) + m[fixed] = convertKeys(v) + } + + b, err := json.Marshal(m) + if err != nil { + fmt.Printf("something went wrong", err) + return j + } + return b + } else { + for i, message := range n { + fixed := convertKeys(message) + n[i] = fixed + } + b, err := json.Marshal(n) + if err != nil { + fmt.Printf("something went wrong", err) + return j + } + return b + } +} + +func printf(format string, args ...interface{}) { + fmt.Println(fmt.Sprintf(format, args...)) +} diff --git a/hack/copy-crds-to-chart/main.go b/hack/copy-crds-to-chart/main.go index 149126b392..435918c53d 100644 --- a/hack/copy-crds-to-chart/main.go +++ b/hack/copy-crds-to-chart/main.go @@ -79,7 +79,13 @@ func realMain(helmPath string) error { ` release: {{ .Release.Name }}`, ` component: crd`, } - withLabels := append(splitOnNewlines[0:9], append(labelLines, splitOnNewlines[9:]...)...) + var split int + if dir == "bases" { + split = 6 + } else { + split = 9 + } + withLabels := append(splitOnNewlines[0:split], append(labelLines, splitOnNewlines[split:]...)...) contents = strings.Join(withLabels, "\n") var crdName string @@ -89,7 +95,7 @@ func realMain(helmPath string) error { crdName = filenameSplit[1] } else if dir == "external" { filenameSplit := strings.Split(info.Name(), ".") - crdName = filenameSplit[0] + ".yaml" + crdName = filenameSplit[0] + "-external.yaml" } destinationPath := filepath.Join(helmPath, "templates", fmt.Sprintf("crd-%s", crdName))