diff --git a/api/v1alpha1/envoypatchpolicy_types.go b/api/v1alpha1/envoypatchpolicy_types.go new file mode 100644 index 0000000000..f5e65a612b --- /dev/null +++ b/api/v1alpha1/envoypatchpolicy_types.go @@ -0,0 +1,131 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" +) + +const ( + // KindEnvoyPatchPolicy is the name of the EnvoyPatchPolicy kind. + KindEnvoyPatchPolicy = "EnvoyPatchPolicy" +) + +// +kubebuilder:object:root=true + +// EnvoyPatchPolicy allows the user to modify the generated Envoy xDS +// resources by Envoy Gateway using this patch API +type EnvoyPatchPolicy struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec defines the desired state of EnvoyPatchPolicy. + Spec EnvoyPatchPolicySpec `json:"spec"` + + // Status defines the current status of EnvoyPatchPolicy. + Status EnvoyPatchPolicyStatus `json:"status"` +} + +// EnvoyPatchPolicySpec defines the desired state of EnvoyPatchPolicy. +// +union +type EnvoyPatchPolicySpec struct { + // Type decides the type of patch. + // Valid EnvoyPatchType values are "JSONPatch". + // + // +unionDiscriminator + Type EnvoyPatchType `json:"type"` + // JSONPatch defines the JSONPatch configuration. + // + // +optional + JSONPatches []EnvoyJSONPatchConfig `json:"jsonPatches,omitempty"` + // TargetRef is the name of the Gateway API resource this policy + // is being attached to. + // Currently only attaching to Gateway is supported + // This Policy and the TargetRef MUST be in the same namespace + // for this Policy to have effect and be applied to the Gateway + // TargetRef + TargetRef gwapiv1a2.PolicyTargetReference `json:"targetRef"` + // Priority of the EnvoyPatchPolicy. + // If multiple EnvoyPatchPolicies are applied to the same + // TargetRef, they will be applied in the ascending order of + // the priority i.e. int32.min has the highest priority and + // int32.max has the lowest priority. + // Defaults to 0. + Priority int32 `json:"priority"` +} + +// EnvoyPatchType specifies the types of Envoy patching mechanisms. +// +kubebuilder:validation:Enum=JSONPatch +type EnvoyPatchType string + +const ( + // JSONPatchEnvoyPatchType allows the user to patch the generated xDS resources using JSONPatch semantics. + // For more details on the semantics, please refer to https://datatracker.ietf.org/doc/html/rfc6902 + JSONPatchEnvoyPatchType EnvoyPatchType = "JSONPatch" +) + +// EnvoyJSONPatchConfig defines the configuration for patching a Envoy xDS Resource +// using JSONPatch semantic +type EnvoyJSONPatchConfig struct { + // Type is the typed URL of the Envoy xDS Resource + Type EnvoyResourceType `json:"type"` + // Name is the name of the resource + Name string `json:"name"` + // Patch defines the JSON Patch Operation + Operation JSONPatchOperation `json:"operation"` +} + +// EnvoyResourceType specifies the type URL of the Envoy resource. +// +kubebuilder:validation:Enum=type.googleapis.com/envoy.config.listener.v3.Listener;type.googleapis.com/envoy.config.route.v3.RouteConfiguration;type.googleapis.com/envoy.config.cluster.v3.Cluster;type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment +type EnvoyResourceType string + +const ( + // ListenerEnvoyResourceType defines the Type URL of the Listener resource + ListenerEnvoyResourceType EnvoyResourceType = "type.googleapis.com/envoy.config.listener.v3.Listener" + // RouteConfigurationEnvoyResourceType defines the Type URL of the RouteConfiguration resource + RouteConfigurationEnvoyResourceType EnvoyResourceType = "type.googleapis.com/envoy.config.route.v3.RouteConfiguration" + // ClusterEnvoyResourceType defines the Type URL of the Cluster resource + ClusterEnvoyResourceType EnvoyResourceType = "type.googleapis.com/envoy.config.cluster.v3.Cluster" + // ClusterLoadAssignmentEnvoyResourceType defines the Type URL of the ClusterLoadAssignment resource + ClusterLoadAssignmentEnvoyResourceType EnvoyResourceType = "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment" +) + +// JSONPatchOperation defines the JSON Patch Operation as defined in +// https://datatracker.ietf.org/doc/html/rfc6902 +type JSONPatchOperation struct { + // Op is the type of operation to perform + Op string `json:"op"` + // Path is the location of the target document/field where the operation will be performed + // Refer to https://datatracker.ietf.org/doc/html/rfc6901 for more details. + Path string `json:"path"` + // Value is the new value of the path location. + Value string `json:"value"` +} + +// EnvoyPatchPolicyStatus defines the state of EnvoyPatchPolicy +type EnvoyPatchPolicyStatus struct { + // Conditions describe the current conditions of the EnvoyPatchPolicy. + // + // +optional + // +listType=map + // +listMapKey=type + // +kubebuilder:validation:MaxItems=8 + Conditions []metav1.Condition `json:"conditions,omitempty"` +} + +//+kubebuilder:object:root=true + +// EnvoyPatchPolicyList contains a list of EnvoyPatchPolicy resources. +type EnvoyPatchPolicyList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []EnvoyPatchPolicy `json:"items"` +} + +func init() { + SchemeBuilder.Register(&EnvoyPatchPolicy{}, &EnvoyPatchPolicyList{}) +} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index cf139703a9..f04385414e 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -11,6 +11,7 @@ package v1alpha1 import ( + "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -94,6 +95,124 @@ func (in *AuthenticationFilterSpec) DeepCopy() *AuthenticationFilterSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyJSONPatchConfig) DeepCopyInto(out *EnvoyJSONPatchConfig) { + *out = *in + out.Operation = in.Operation +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyJSONPatchConfig. +func (in *EnvoyJSONPatchConfig) DeepCopy() *EnvoyJSONPatchConfig { + if in == nil { + return nil + } + out := new(EnvoyJSONPatchConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyPatchPolicy) DeepCopyInto(out *EnvoyPatchPolicy) { + *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 EnvoyPatchPolicy. +func (in *EnvoyPatchPolicy) DeepCopy() *EnvoyPatchPolicy { + if in == nil { + return nil + } + out := new(EnvoyPatchPolicy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *EnvoyPatchPolicy) 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 *EnvoyPatchPolicyList) DeepCopyInto(out *EnvoyPatchPolicyList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]EnvoyPatchPolicy, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyPatchPolicyList. +func (in *EnvoyPatchPolicyList) DeepCopy() *EnvoyPatchPolicyList { + if in == nil { + return nil + } + out := new(EnvoyPatchPolicyList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *EnvoyPatchPolicyList) 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 *EnvoyPatchPolicySpec) DeepCopyInto(out *EnvoyPatchPolicySpec) { + *out = *in + if in.JSONPatches != nil { + in, out := &in.JSONPatches, &out.JSONPatches + *out = make([]EnvoyJSONPatchConfig, len(*in)) + copy(*out, *in) + } + in.TargetRef.DeepCopyInto(&out.TargetRef) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyPatchPolicySpec. +func (in *EnvoyPatchPolicySpec) DeepCopy() *EnvoyPatchPolicySpec { + if in == nil { + return nil + } + out := new(EnvoyPatchPolicySpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyPatchPolicyStatus) DeepCopyInto(out *EnvoyPatchPolicyStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyPatchPolicyStatus. +func (in *EnvoyPatchPolicyStatus) DeepCopy() *EnvoyPatchPolicyStatus { + if in == nil { + return nil + } + out := new(EnvoyPatchPolicyStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GlobalRateLimit) DeepCopyInto(out *GlobalRateLimit) { *out = *in @@ -141,6 +260,21 @@ func (in *HeaderMatch) DeepCopy() *HeaderMatch { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JSONPatchOperation) DeepCopyInto(out *JSONPatchOperation) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JSONPatchOperation. +func (in *JSONPatchOperation) DeepCopy() *JSONPatchOperation { + if in == nil { + return nil + } + out := new(JSONPatchOperation) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *JwtAuthenticationFilterProvider) DeepCopyInto(out *JwtAuthenticationFilterProvider) { *out = *in diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoypatchpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoypatchpolicies.yaml new file mode 100644 index 0000000000..0aa230ebb8 --- /dev/null +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoypatchpolicies.yaml @@ -0,0 +1,217 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: envoypatchpolicies.gateway.envoyproxy.io +spec: + group: gateway.envoyproxy.io + names: + kind: EnvoyPatchPolicy + listKind: EnvoyPatchPolicyList + plural: envoypatchpolicies + singular: envoypatchpolicy + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: EnvoyPatchPolicy allows the user to modify the generated Envoy + xDS resources by Envoy Gateway using this patch 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: Spec defines the desired state of EnvoyPatchPolicy. + properties: + jsonPatches: + description: JSONPatch defines the JSONPatch configuration. + items: + description: EnvoyJSONPatchConfig defines the configuration for + patching a Envoy xDS Resource using JSONPatch semantic + properties: + name: + description: Name is the name of the resource + type: string + operation: + description: Patch defines the JSON Patch Operation + properties: + op: + description: Op is the type of operation to perform + type: string + path: + description: Path is the location of the target document/field + where the operation will be performed Refer to https://datatracker.ietf.org/doc/html/rfc6901 + for more details. + type: string + value: + description: Value is the new value of the path location. + type: string + required: + - op + - path + - value + type: object + type: + description: Type is the typed URL of the Envoy xDS Resource + enum: + - type.googleapis.com/envoy.config.listener.v3.Listener + - type.googleapis.com/envoy.config.route.v3.RouteConfiguration + - type.googleapis.com/envoy.config.cluster.v3.Cluster + - type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment + type: string + required: + - name + - operation + - type + type: object + type: array + priority: + description: Priority of the EnvoyPatchPolicy. If multiple EnvoyPatchPolicies + are applied to the same TargetRef, they will be applied in the ascending + order of the priority i.e. int32.min has the highest priority and + int32.max has the lowest priority. Defaults to 0. + format: int32 + type: integer + targetRef: + description: TargetRef is the name of the Gateway API resource this + policy is being attached to. Currently only attaching to Gateway + is supported This Policy and the TargetRef MUST be in the same namespace + for this Policy to have effect and be applied to the Gateway TargetRef + properties: + group: + description: Group is the group of the target resource. + 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 target resource. + 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 target resource. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: Namespace is the namespace of the referent. When + unspecified, the local namespace is inferred. Even when policy + targets a resource in a different namespace, it MUST only apply + to traffic originating from the same namespace as the policy. + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - group + - kind + - name + type: object + type: + description: Type decides the type of patch. Valid EnvoyPatchType + values are "JSONPatch". + enum: + - JSONPatch + type: string + required: + - priority + - targetRef + - type + type: object + status: + description: Status defines the current status of EnvoyPatchPolicy. + properties: + conditions: + description: Conditions describe the current conditions of the EnvoyPatchPolicy. + 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 type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + - status + type: object + served: true + storage: true diff --git a/docs/latest/api/extension_types.md b/docs/latest/api/extension_types.md index bcfe1ad5b8..b69ecdd867 100644 --- a/docs/latest/api/extension_types.md +++ b/docs/latest/api/extension_types.md @@ -11,6 +11,8 @@ Package v1alpha1 contains API schema definitions for the gateway.envoyproxy.io A ### Resource Types - [AuthenticationFilter](#authenticationfilter) +- [EnvoyPatchPolicy](#envoypatchpolicy) +- [EnvoyPatchPolicyList](#envoypatchpolicylist) - [RateLimitFilter](#ratelimitfilter) @@ -57,6 +59,96 @@ _Appears in:_ +## EnvoyJSONPatchConfig + + + +EnvoyJSONPatchConfig defines the configuration for patching a Envoy xDS Resource using JSONPatch semantic + +_Appears in:_ +- [EnvoyPatchPolicySpec](#envoypatchpolicyspec) + +| Field | Description | +| --- | --- | +| `type` _[EnvoyResourceType](#envoyresourcetype)_ | Type is the typed URL of the Envoy xDS Resource | +| `name` _string_ | Name is the name of the resource | +| `operation` _[JSONPatchOperation](#jsonpatchoperation)_ | Patch defines the JSON Patch Operation | + + +## EnvoyPatchPolicy + + + +EnvoyPatchPolicy allows the user to modify the generated Envoy xDS resources by Envoy Gateway using this patch API + +_Appears in:_ +- [EnvoyPatchPolicyList](#envoypatchpolicylist) + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `EnvoyPatchPolicy` +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `spec` _[EnvoyPatchPolicySpec](#envoypatchpolicyspec)_ | Spec defines the desired state of EnvoyPatchPolicy. | + + +## EnvoyPatchPolicyList + + + +EnvoyPatchPolicyList contains a list of EnvoyPatchPolicy resources. + + + +| Field | Description | +| --- | --- | +| `apiVersion` _string_ | `gateway.envoyproxy.io/v1alpha1` +| `kind` _string_ | `EnvoyPatchPolicyList` +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `items` _[EnvoyPatchPolicy](#envoypatchpolicy) array_ | | + + +## EnvoyPatchPolicySpec + + + +EnvoyPatchPolicySpec defines the desired state of EnvoyPatchPolicy. + +_Appears in:_ +- [EnvoyPatchPolicy](#envoypatchpolicy) + +| Field | Description | +| --- | --- | +| `type` _[EnvoyPatchType](#envoypatchtype)_ | Type decides the type of patch. Valid EnvoyPatchType values are "JSONPatch". | +| `jsonPatches` _[EnvoyJSONPatchConfig](#envoyjsonpatchconfig) array_ | JSONPatch defines the JSONPatch configuration. | +| `targetRef` _[PolicyTargetReference](#policytargetreference)_ | TargetRef is the name of the Gateway API resource this policy is being attached to. Currently only attaching to Gateway is supported This Policy and the TargetRef MUST be in the same namespace for this Policy to have effect and be applied to the Gateway TargetRef | +| `priority` _integer_ | Priority of the EnvoyPatchPolicy. If multiple EnvoyPatchPolicies are applied to the same TargetRef, they will be applied in the ascending order of the priority i.e. int32.min has the highest priority and int32.max has the lowest priority. Defaults to 0. | + + + + +## EnvoyPatchType + +_Underlying type:_ `string` + +EnvoyPatchType specifies the types of Envoy patching mechanisms. + +_Appears in:_ +- [EnvoyPatchPolicySpec](#envoypatchpolicyspec) + + + +## EnvoyResourceType + +_Underlying type:_ `string` + +EnvoyResourceType specifies the type URL of the Envoy resource. + +_Appears in:_ +- [EnvoyJSONPatchConfig](#envoyjsonpatchconfig) + + + ## GlobalRateLimit @@ -98,6 +190,22 @@ _Appears in:_ +## JSONPatchOperation + + + +JSONPatchOperation defines the JSON Patch Operation as defined in https://datatracker.ietf.org/doc/html/rfc6902 + +_Appears in:_ +- [EnvoyJSONPatchConfig](#envoyjsonpatchconfig) + +| Field | Description | +| --- | --- | +| `op` _string_ | Op is the type of operation to perform | +| `path` _string_ | Path is the location of the target document/field where the operation will be performed Refer to https://datatracker.ietf.org/doc/html/rfc6901 for more details. | +| `value` _string_ | Value is the new value of the path location. | + + ## JwtAuthenticationFilterProvider diff --git a/docs/latest/design/envoy-patch-policy.md b/docs/latest/design/envoy-patch-policy.md new file mode 100644 index 0000000000..1d7e82e15c --- /dev/null +++ b/docs/latest/design/envoy-patch-policy.md @@ -0,0 +1,151 @@ +# EnvoyPatchPolicy + +## Overview + +This design introduces the `EnvoyPatchPolicy` API allowing users to modify the generated Envoy xDS Configuration +that Envoy Gateway generates before sending it to Envoy Proxy. + +Envoy Gateway allows users to configure networking and security intent using the +upstream [Gateway API][] as well as implementation specific [Extension APIs][] defined in this project +to provide a more batteries included experience for application developers. +* These APIs are an abstracted version of the underlying Envoy xDS API to provide a better user experience for the application developer, exposing and setting only a subset of the fields for a specific feature, sometimes in a opinionated way (e.g [RateLimit][]) +* These APIs do not expose all the features capabilities that Envoy has either because these features are desired but the API +is not defined yet or the project cannot support such an extensive list of features. +To alleviate this problem, and provide an interim solution for a small section of advanced users who are well versed in +Envoy xDS API and its capabilities, this API is being introduced. + +## Goals +* Add an API allowing users to modify the generated xDS Configuration + +## Non Goals +* Support multiple patch mechanisims + +## Implementation +`EnvoyPatchPolicy` is a [Direct Policy Attachment][] type API that can be used to extend [Gateway API][] +Modifications to the generated xDS configuration can be provided as a JSON Patch which is defined in +[RFC 6902][]. This patching mechanism has been adopted in [Kubernetes][] as well as [Kustomize][] to update +resource objects. + +### Example +Here is an example highlighting how a user can configure global ratelimiting using an external rate limit service using this API. + +``` +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: eg-gw + namespace: default +spec: + gatewayClassName: eg-gc + listeners: + - name: example + protocol: HTTPS + port: 443 + hostname: example.com + tls: + certificateRefs: + - kind: Secret + group: "" + name: example-cert +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyPatchPolicy +metadata: + name: ratelimit-patch-policy + namespace: default +spec: + targetRef: + group: gateway.networking.k8s.io/v1beta1 + kind: Gateway + name: eg-gw + namespace: default + type: JSONPatch + jsonPatches: + - type: "type.googleapis.com/envoy.config.listener.v3.Listener" + # The listener name is of the form -- + name: default-eg-gw-example + operation: + op: add + path: "/filter_chains/0/filters/0/http_filters/0" + value: | + name: "envoy.filters.http.ratelimit" + typed_config: + "@type": "type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit" + domain: "eag-ratelimit" + failure_mode_deny: true + timeout: 1s + rate_limit_service: + grpc_service: + envoy_grpc: + cluster_name: rate-limit-cluster + transport_api_version: V3 + - type: "type.googleapis.com/envoy.config.route.v3.RouteConfiguration" + # The route name is of the form -- + name: default-eg-gw-example + operation: + op: add + path: "/virtual_hosts/0/rate_limits" + value: | + - actions: + - remote_address: {} + - type: "type.googleapis.com/envoy.config.cluster.v3.Cluster" + name: rate-limit-cluster + operation: + op: add + path: "/" + value: | + name: rate-limit-cluster + type: STRICT_DNS + connect_timeout: 10s + lb_policy: ROUND_ROBIN + http2_protocol_options: {} + load_assignment: + cluster_name: rate-limit-cluster + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: ratelimit.svc.cluster.local + port_value: 8081 +``` + + +## Verification +* Offline - Leverage [egctl x translate][] to ensure that the `EnvoyPatchPolicy` can be successfully applied and the desired +output xDS is created. +* Runtime - Use the `Status` field within `EnvoyPatchPolicy` to highlight whether the patch was applied successfully or not. + +## State of the World +* Istio - Supports the [EnvoyFilter][] API which allows users to customize the output xDS using patches and proto based merge +semantics. + +## Design Decisions +* This API will only support a single `targetRef` and can bind to only a `Gateway` resource. This simplifies reasoning of how +patches will work. +* This API will always be an experimental API and cannot be graduated into a stable API because Envoy Gateway cannot garuntee + * that the naming scheme for the generated resources names will not change across releases + * that the underlying Envoy Proxy API will not change across releases +* This API needs to be explicitly enabled using the [EnvoyGateway][] API + +## Open Questions +* Should the value only support JSON or YAML as well (which is a JSON superset) ? + +## Alternatives +* Users can customize the Envoy [Bootstrap configuration using EnvoyProxy API][] and provide static xDS configuration. +* Users can extend functionality by [Extending the Control Plane][] and adding gRPC hooks to modify the generated xDS configuration. + + + +[Direct Policy Attachment]: https://gateway-api.sigs.k8s.io/references/policy-attachment/#direct-policy-attachment +[RFC 6902]: https://datatracker.ietf.org/doc/html/rfc6902 +[Gateway API]: https://gateway-api.sigs.k8s.io/ +[Kubernetes]: https://kubernetes.io/docs/tasks/manage-kubernetes-objects/update-api-object-kubectl-patch/ +[Kustomize]: https://github.com/kubernetes-sigs/kustomize/blob/master/examples/jsonpatch.md +[Extension APIs]: https://gateway.envoyproxy.io/latest/api/extension_types.html +[RateLimit]: https://gateway.envoyproxy.io/latest/user/rate-limit.html +[EnvoyGateway]: https://gateway.envoyproxy.io/latest/api/config_types.html#envoygateway +[Extending the Control Plane]: https://gateway.envoyproxy.io/latest/design/extending-envoy-gateway.html +[EnvoyFilter]: https://istio.io/latest/docs/reference/config/networking/envoy-filter +[egctl x translate]: https://gateway.envoyproxy.io/latest/user/egctl.html#egctl-experimental-translate +[Bootstrap configuration using EnvoyProxy API]: https://gateway.envoyproxy.io/latest/user/customize-envoyproxy.html#customize-envoyproxy-bootstrap-config