From 04b7280ac9fe1ac009904758101880027b32e2df Mon Sep 17 00:00:00 2001 From: Guy Daich Date: Thu, 14 Mar 2024 16:02:53 -0500 Subject: [PATCH 1/7] api: EnvoyExtensionPolicy Signed-off-by: Guy Daich --- api/v1alpha1/envoyextensionypolicy_types.go | 87 ++++ api/v1alpha1/envoygateway_types.go | 4 + api/v1alpha1/envoyproxy_types.go | 40 ++ api/v1alpha1/ext_proc_types.go | 130 ++++++ api/v1alpha1/zz_generated.deepcopy.go | 313 ++++++++++++++ ....envoyproxy.io_envoyextensionpolicies.yaml | 405 ++++++++++++++++++ .../gateway.envoyproxy.io_envoyproxies.yaml | 21 + site/content/en/latest/api/extension_types.md | 197 +++++++++ .../design/envoy-extension-policy.md | 149 +++++++ 9 files changed, 1346 insertions(+) create mode 100644 api/v1alpha1/envoyextensionypolicy_types.go create mode 100644 api/v1alpha1/ext_proc_types.go create mode 100644 charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml create mode 100644 site/content/en/latest/contributions/design/envoy-extension-policy.md diff --git a/api/v1alpha1/envoyextensionypolicy_types.go b/api/v1alpha1/envoyextensionypolicy_types.go new file mode 100644 index 00000000000..30931b93173 --- /dev/null +++ b/api/v1alpha1/envoyextensionypolicy_types.go @@ -0,0 +1,87 @@ +// 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 ( + // KindEnvoyExtensionPolicy is the name of the EnvoyExtensionPolicy kind. + KindEnvoyExtensionPolicy = "EnvoyExtensionPolicy" +) + +// +kubebuilder:object:root=true +// +kubebuilder:resource:shortName=eep +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Accepted")].reason` +// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` + +// EnvoyExtensionPolicy allows the user to configure various envoy extensibility options for the Gateway. +type EnvoyExtensionPolicy struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec defines the desired state of EnvoyExtensionPolicy. + Spec EnvoyExtensionPolicySpec `json:"spec"` + + // Status defines the current status of EnvoyExtensionPolicy. + Status EnvoyExtensionPolicyStatus `json:"status,omitempty"` +} + +// EnvoyExtensionPolicySpec defines the desired state of EnvoyExtensionPolicy. +type EnvoyExtensionPolicySpec struct { + // +kubebuilder:validation:XValidation:rule="self.group == 'gateway.networking.k8s.io'", message="this policy can only have a targetRef.group of gateway.networking.k8s.io" + // +kubebuilder:validation:XValidation:rule="self.kind in ['Gateway']", message="this policy can only have a targetRef.kind of Gateway" + // +kubebuilder:validation:XValidation:rule="!has(self.sectionName)",message="this policy does not yet support the sectionName field" + // + // TargetRef is the name of the Gateway resource this policy + // is being attached to. + // 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.PolicyTargetReferenceWithSectionName `json:"targetRef"` + + // Priority of the EnvoyExtensionPolicy. + // If multiple EnvoyExtensionPolices are applied to the same + // TargetRef, extensions will execute 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. + // + // +optional + Priority int32 `json:"priority,omitempty"` + + // ExtProc defines the configuration for the external processor extension. + // + // +optional + ExtProc *ExtProc `json:"extProc,omitempty"` +} + +// EnvoyExtensionPolicyStatus defines the state of EnvoyExtensionPolicy +type EnvoyExtensionPolicyStatus struct { + // Conditions describe the current conditions of the EnvoyExtensionPolicy. + // + // +optional + // +listType=map + // +listMapKey=type + // +kubebuilder:validation:MaxItems=8 + Conditions []metav1.Condition `json:"conditions,omitempty"` +} + +//+kubebuilder:object:root=true + +// EnvoyExtensionPolicyList contains a list of EnvoyExtensionPolicy resources. +type EnvoyExtensionPolicyList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []EnvoyExtensionPolicy `json:"items"` +} + +func init() { + SchemeBuilder.Register(&EnvoyExtensionPolicy{}, &EnvoyExtensionPolicyList{}) +} diff --git a/api/v1alpha1/envoygateway_types.go b/api/v1alpha1/envoygateway_types.go index 145fc9b5fca..74f1edafe85 100644 --- a/api/v1alpha1/envoygateway_types.go +++ b/api/v1alpha1/envoygateway_types.go @@ -151,6 +151,10 @@ type ExtensionAPISettings struct { // EnableEnvoyPatchPolicy enables Envoy Gateway to // reconcile and implement the EnvoyPatchPolicy resources. EnableEnvoyPatchPolicy bool `json:"enableEnvoyPatchPolicy"` + + // EnableEnvoyExtensionPolicy enables Envoy Gateway to + // reconcile and implement the EnvoyExtensionPolicy resources. + EnableEnvoyExtensionPolicy bool `json:"enableEnvoyExtensionPolicy"` } // EnvoyGatewayProvider defines the desired configuration of a provider. diff --git a/api/v1alpha1/envoyproxy_types.go b/api/v1alpha1/envoyproxy_types.go index a8bb1505717..266e0d99b50 100644 --- a/api/v1alpha1/envoyproxy_types.go +++ b/api/v1alpha1/envoyproxy_types.go @@ -14,6 +14,24 @@ const ( KindEnvoyProxy = "EnvoyProxy" ) +// +kubebuilder:validation:Enum=CORS;External-Authorization;Basic-Authorization;OAuth2;JWT-Authentication;Fault-Injection;Fault-Injection;Local-Rate-Limit;Global-Rate-Limit +// +// EnvoyFilterName is the name of an Envoy HTTP filter +type EnvoyFilterName string + +const ( + CORSFilterName EnvoyFilterName = "CORS" + ExtAuthFilterName EnvoyFilterName = "External-Authorization" + BasicAuthFilterName EnvoyFilterName = "Basic-Authorization" + OAuth2FilterName EnvoyFilterName = "OAuth2" + JWTAuthnOAuth2FilterName EnvoyFilterName = "JWT-Authentication" + FaultInjectionFilterName EnvoyFilterName = "Fault-Injection" + LocalRateLimitFilterName EnvoyFilterName = "Local-Rate-Limit" + GlobalRateLimitFilterName EnvoyFilterName = "Global-Rate-Limit" + ExtProcFilterName EnvoyFilterName = "External-Processing" + WASMFilterName EnvoyFilterName = "WASM" +) + // +kubebuilder:object:root=true // +kubebuilder:resource:categories=envoy-gateway,shortName=eproxy // +kubebuilder:subresource:status @@ -86,6 +104,14 @@ type EnvoyProxySpec struct { // // +optional Shutdown *ShutdownConfig `json:"shutdown,omitempty"` + + // FilterOrdering defines the order of Envoy HTTP filter execution. If a partial filter list is provided, the default + // order will apply, and only the listed filters will be re-ordered amongst themselves. + // Default: CORS, External-Processing, WASM, External-Authorization, Basic-Authorization, OAuth2, JWT-Authentication, + // Fault-Injection, Local-Rate-Limit, Global-Rate-Limit + // + // +optional + FilterOrdering []EnvoyFilterName `json:"filterOrdering,omitempty"` } type ProxyTelemetry struct { @@ -134,6 +160,20 @@ type ShutdownConfig struct { MinDrainDuration *metav1.Duration `json:"minDrainDuration,omitempty"` } +// FilterOrdering defines the ordering of well-known. +type FilterOrdering struct { + // DrainTimeout defines the graceful drain timeout. This should be less than the pod's terminationGracePeriodSeconds. + // If unspecified, defaults to 600 seconds. + // + // +optional + DrainTimeout *metav1.Duration `json:"drainTimeout,omitempty"` + // MinDrainDuration defines the minimum drain duration allowing time for endpoint deprogramming to complete. + // If unspecified, defaults to 5 seconds. + // + // +optional + MinDrainDuration *metav1.Duration `json:"minDrainDuration,omitempty"` +} + // EnvoyProxyKubernetesProvider defines configuration for the Kubernetes resource // provider. type EnvoyProxyKubernetesProvider struct { diff --git a/api/v1alpha1/ext_proc_types.go b/api/v1alpha1/ext_proc_types.go new file mode 100644 index 00000000000..b9cbdc2867b --- /dev/null +++ b/api/v1alpha1/ext_proc_types.go @@ -0,0 +1,130 @@ +// 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 ( + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" +) + +// +kubebuilder:validation:Enum=Default;Send;Skip +type ExtProcHeaderProcessingMode string + +const ( + DefaultExtProcHeaderProcessingMode ExtProcHeaderProcessingMode = "Default" + SendExtProcHeaderProcessingMode ExtProcHeaderProcessingMode = "Send" + SkipExtProcHeaderProcessingMode ExtProcHeaderProcessingMode = "Skip" +) + +// +kubebuilder:validation:Enum=None;Streamed;Buffered;BufferedPartial +type ExtProcBodyProcessingMode string + +const ( + NoneExtProcHeaderProcessingMode ExtProcBodyProcessingMode = "None" + StreamedExtProcHeaderProcessingMode ExtProcBodyProcessingMode = "Streamed" + BufferedExtProcHeaderProcessingMode ExtProcBodyProcessingMode = "Buffered" + BufferedPartialExtProcHeaderProcessingMode ExtProcBodyProcessingMode = "BufferedPartial" +) + +type ProcessingModeOptions struct { + // Defines header processing mode + // + // +kubebuilder:default:=Send + // +optional + Headers *ExtProcHeaderProcessingMode `json:"header,omitempty"` + // Defines body processing mode + // + // +kubebuilder:default:=None + // +optional + Body *ExtProcBodyProcessingMode `json:"body,omitempty"` +} + +// ExtProcProcessingMode defines if and how headers and bodies are sent to the service. +// https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ext_proc/v3/processing_mode.proto#envoy-v3-api-msg-extensions-filters-http-ext-proc-v3-processingmode +type ExtProcProcessingMode struct { + // Defines header and body treatment for requests + // + // +optional + Request *ProcessingModeOptions `json:"request,omitempty"` + // Defines header and body treatment for responses + // + // +optional + Response *ProcessingModeOptions `json:"response,omitempty"` +} + +// ExtProcAttributes defines which attributes are +type ExtProcAttributes struct { + // defines attributes to send for Request processing + // + // +optional + Request []string `json:"request,omitempty"` + // defines attributes to send for Response processing + // + // +optional + Response []string `json:"response,omitempty"` +} + +// MetadataNamespaces defines metadata namespaces that can be used to forward or receive dynamic metadata +type MetadataNamespaces struct { + // Specifies a list of metadata namespaces whose values, if present, will be passed to the ext_proc service as an opaque protobuf::Struct. + // + // +optional + Untyped []string `json:"untyped,omitempty"` + // Specifies a list of metadata namespaces whose values, if present, will be passed to the ext_proc service as a protobuf::Any. + // + // +optional + Typed []string `json:"typed,omitempty"` +} + +// ExtProcMetadataOptions defines options related to the sending and receiving of dynamic metadata +type ExtProcMetadataOptions struct { + // metadata namespaces forwarded to external processor + // + // +optional + ForwardingNamespaces []MetadataNamespaces `json:"forwardingNamespaces,omitempty"` + // metadata namespaces updatable by external processor + // + // +optional + ReceivingNamespaces []MetadataNamespaces `json:"receivingNamespaces,omitempty"` +} + +// +kubebuilder:validation:XValidation:rule="has(self.service) ? (!has(self.service.backendRef.group) || self.service.backendRef.group == \"\") : true", message="group is invalid, only the core API group (specified by omitting the group field or setting it to an empty string) is supported" +// +kubebuilder:validation:XValidation:rule="has(self.service) ? (!has(self.service.backendRef.kind) || self.service.backendRef.kind == 'Service') : true", message="kind is invalid, only Service (specified by omitting the kind field or setting it to 'Service') is supported" +// +// ExtProc defines the configuration for External Processing. +type ExtProc struct { + // Service defines the configuration of the external processing service + Service ExtProcService `json:"service"` + // ProcessingMode defines how request and response headers and body are processed + // Default: request and response headers are sent, bodies are not sent + // + // +optional + ProcessingMode *ExtProcProcessingMode `json:"processingMode,omitempty"` + // Attributes defines which envoy request and response attributes are provided as context to external processor + // Default: no attributes are sent + // + // +optional + Attributes *ExtProcAttributes `json:"attributes,omitempty"` + // MetadataOptions defines options related to the sending and receiving of dynamic metadata + // Default: no metadata context is sent or received + // + // +optional + MetadataOptions *ExtProcMetadataOptions `json:"metadataOptions,omitempty"` + // The timeout for a response to be returned from the external processor + // Default: 200ms + // + // +optional + MessageTimeout *gwapiv1.Duration `json:"messageTimeout,omitempty"` +} + +// ExtProcService defines the gRPC External Processing service using the envoy grpc client +// The processing request and response messages are defined in +// https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/ext_proc/v3/external_processor.proto +type ExtProcService struct { + // BackendObjectReference references a Kubernetes object that represents the + // backend server to which the processing requests will be sent. + // Only service Kind is supported for now. + BackendRef gwapiv1.BackendObjectReference `json:"backendRef"` +} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 1bd56138b65..73d07d15681 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -653,6 +653,108 @@ func (in *EnvironmentCustomTag) DeepCopy() *EnvironmentCustomTag { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyExtensionPolicy) DeepCopyInto(out *EnvoyExtensionPolicy) { + *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 EnvoyExtensionPolicy. +func (in *EnvoyExtensionPolicy) DeepCopy() *EnvoyExtensionPolicy { + if in == nil { + return nil + } + out := new(EnvoyExtensionPolicy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *EnvoyExtensionPolicy) 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 *EnvoyExtensionPolicyList) DeepCopyInto(out *EnvoyExtensionPolicyList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]EnvoyExtensionPolicy, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyExtensionPolicyList. +func (in *EnvoyExtensionPolicyList) DeepCopy() *EnvoyExtensionPolicyList { + if in == nil { + return nil + } + out := new(EnvoyExtensionPolicyList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *EnvoyExtensionPolicyList) 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 *EnvoyExtensionPolicySpec) DeepCopyInto(out *EnvoyExtensionPolicySpec) { + *out = *in + in.TargetRef.DeepCopyInto(&out.TargetRef) + if in.ExtProc != nil { + in, out := &in.ExtProc, &out.ExtProc + *out = new(ExtProc) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyExtensionPolicySpec. +func (in *EnvoyExtensionPolicySpec) DeepCopy() *EnvoyExtensionPolicySpec { + if in == nil { + return nil + } + out := new(EnvoyExtensionPolicySpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyExtensionPolicyStatus) DeepCopyInto(out *EnvoyExtensionPolicyStatus) { + *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 EnvoyExtensionPolicyStatus. +func (in *EnvoyExtensionPolicyStatus) DeepCopy() *EnvoyExtensionPolicyStatus { + if in == nil { + return nil + } + out := new(EnvoyExtensionPolicyStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *EnvoyGateway) DeepCopyInto(out *EnvoyGateway) { *out = *in @@ -1285,6 +1387,11 @@ func (in *EnvoyProxySpec) DeepCopyInto(out *EnvoyProxySpec) { *out = new(ShutdownConfig) (*in).DeepCopyInto(*out) } + if in.FilterOrdering != nil { + in, out := &in.FilterOrdering, &out.FilterOrdering + *out = make([]EnvoyFilterName, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyProxySpec. @@ -1342,6 +1449,137 @@ func (in *ExtAuth) DeepCopy() *ExtAuth { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExtProc) DeepCopyInto(out *ExtProc) { + *out = *in + in.Service.DeepCopyInto(&out.Service) + if in.ProcessingMode != nil { + in, out := &in.ProcessingMode, &out.ProcessingMode + *out = new(ExtProcProcessingMode) + (*in).DeepCopyInto(*out) + } + if in.Attributes != nil { + in, out := &in.Attributes, &out.Attributes + *out = new(ExtProcAttributes) + (*in).DeepCopyInto(*out) + } + if in.MetadataOptions != nil { + in, out := &in.MetadataOptions, &out.MetadataOptions + *out = new(ExtProcMetadataOptions) + (*in).DeepCopyInto(*out) + } + if in.MessageTimeout != nil { + in, out := &in.MessageTimeout, &out.MessageTimeout + *out = new(apisv1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtProc. +func (in *ExtProc) DeepCopy() *ExtProc { + if in == nil { + return nil + } + out := new(ExtProc) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExtProcAttributes) DeepCopyInto(out *ExtProcAttributes) { + *out = *in + if in.Request != nil { + in, out := &in.Request, &out.Request + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Response != nil { + in, out := &in.Response, &out.Response + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtProcAttributes. +func (in *ExtProcAttributes) DeepCopy() *ExtProcAttributes { + if in == nil { + return nil + } + out := new(ExtProcAttributes) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExtProcMetadataOptions) DeepCopyInto(out *ExtProcMetadataOptions) { + *out = *in + if in.ForwardingNamespaces != nil { + in, out := &in.ForwardingNamespaces, &out.ForwardingNamespaces + *out = make([]MetadataNamespaces, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ReceivingNamespaces != nil { + in, out := &in.ReceivingNamespaces, &out.ReceivingNamespaces + *out = make([]MetadataNamespaces, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtProcMetadataOptions. +func (in *ExtProcMetadataOptions) DeepCopy() *ExtProcMetadataOptions { + if in == nil { + return nil + } + out := new(ExtProcMetadataOptions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExtProcProcessingMode) DeepCopyInto(out *ExtProcProcessingMode) { + *out = *in + if in.Request != nil { + in, out := &in.Request, &out.Request + *out = new(ProcessingModeOptions) + (*in).DeepCopyInto(*out) + } + if in.Response != nil { + in, out := &in.Response, &out.Response + *out = new(ProcessingModeOptions) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtProcProcessingMode. +func (in *ExtProcProcessingMode) DeepCopy() *ExtProcProcessingMode { + if in == nil { + return nil + } + out := new(ExtProcProcessingMode) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExtProcService) DeepCopyInto(out *ExtProcService) { + *out = *in + in.BackendRef.DeepCopyInto(&out.BackendRef) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtProcService. +func (in *ExtProcService) DeepCopy() *ExtProcService { + if in == nil { + return nil + } + out := new(ExtProcService) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ExtensionAPISettings) DeepCopyInto(out *ExtensionAPISettings) { *out = *in @@ -1538,6 +1776,31 @@ func (in *FileEnvoyProxyAccessLog) DeepCopy() *FileEnvoyProxyAccessLog { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FilterOrdering) DeepCopyInto(out *FilterOrdering) { + *out = *in + if in.DrainTimeout != nil { + in, out := &in.DrainTimeout, &out.DrainTimeout + *out = new(v1.Duration) + **out = **in + } + if in.MinDrainDuration != nil { + in, out := &in.MinDrainDuration, &out.MinDrainDuration + *out = new(v1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FilterOrdering. +func (in *FilterOrdering) DeepCopy() *FilterOrdering { + if in == nil { + return nil + } + out := new(FilterOrdering) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GRPCExtAuthService) DeepCopyInto(out *GRPCExtAuthService) { *out = *in @@ -2367,6 +2630,31 @@ func (in *LocalRateLimit) DeepCopy() *LocalRateLimit { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MetadataNamespaces) DeepCopyInto(out *MetadataNamespaces) { + *out = *in + if in.Untyped != nil { + in, out := &in.Untyped, &out.Untyped + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Typed != nil { + in, out := &in.Typed, &out.Typed + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetadataNamespaces. +func (in *MetadataNamespaces) DeepCopy() *MetadataNamespaces { + if in == nil { + return nil + } + out := new(MetadataNamespaces) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OIDC) DeepCopyInto(out *OIDC) { *out = *in @@ -2546,6 +2834,31 @@ func (in *PerRetryPolicy) DeepCopy() *PerRetryPolicy { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProcessingModeOptions) DeepCopyInto(out *ProcessingModeOptions) { + *out = *in + if in.Headers != nil { + in, out := &in.Headers, &out.Headers + *out = new(ExtProcHeaderProcessingMode) + **out = **in + } + if in.Body != nil { + in, out := &in.Body, &out.Body + *out = new(ExtProcBodyProcessingMode) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProcessingModeOptions. +func (in *ProcessingModeOptions) DeepCopy() *ProcessingModeOptions { + if in == nil { + return nil + } + out := new(ProcessingModeOptions) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ProxyAccessLog) DeepCopyInto(out *ProxyAccessLog) { *out = *in diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml new file mode 100644 index 00000000000..53de9042e55 --- /dev/null +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml @@ -0,0 +1,405 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.13.0 + name: envoyextensionpolicies.gateway.envoyproxy.io +spec: + group: gateway.envoyproxy.io + names: + kind: EnvoyExtensionPolicy + listKind: EnvoyExtensionPolicyList + plural: envoyextensionpolicies + shortNames: + - eep + singular: envoyextensionpolicy + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=="Accepted")].reason + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: EnvoyExtensionPolicy allows the user to configure various envoy + extensibility options for the Gateway. + 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 EnvoyExtensionPolicy. + properties: + extProc: + description: ExtProc defines the configuration for the external processor + extension. + properties: + attributes: + description: 'Attributes defines which envoy request and response + attributes are provided as context to external processor Default: + no attributes are sent' + properties: + request: + description: defines attributes to send for Request processing + items: + type: string + type: array + response: + description: defines attributes to send for Response processing + items: + type: string + type: array + type: object + messageTimeout: + description: 'The timeout for a response to be returned from the + external processor Default: 200ms' + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string + metadataOptions: + description: 'MetadataOptions defines options related to the sending + and receiving of dynamic metadata Default: no metadata context + is sent or received' + properties: + forwardingNamespaces: + description: metadata namespaces forwarded to external processor + items: + description: MetadataNamespaces defines metadata namespaces + that can be used to forward or receive dynamic metadata + properties: + typed: + description: Specifies a list of metadata namespaces + whose values, if present, will be passed to the ext_proc + service as a protobuf::Any. + items: + type: string + type: array + untyped: + description: Specifies a list of metadata namespaces + whose values, if present, will be passed to the ext_proc + service as an opaque protobuf::Struct. + items: + type: string + type: array + type: object + type: array + receivingNamespaces: + description: metadata namespaces updatable by external processor + items: + description: MetadataNamespaces defines metadata namespaces + that can be used to forward or receive dynamic metadata + properties: + typed: + description: Specifies a list of metadata namespaces + whose values, if present, will be passed to the ext_proc + service as a protobuf::Any. + items: + type: string + type: array + untyped: + description: Specifies a list of metadata namespaces + whose values, if present, will be passed to the ext_proc + service as an opaque protobuf::Struct. + items: + type: string + type: array + type: object + type: array + type: object + processingMode: + description: 'ProcessingMode defines how request and response + headers and body are processed Default: request and response + headers are sent, bodies are not sent' + properties: + request: + description: Defines header and body treatment for requests + properties: + body: + default: None + description: Defines body processing mode + enum: + - None + - Streamed + - Buffered + - BufferedPartial + type: string + header: + default: Send + description: Defines header processing mode + enum: + - Default + - Send + - Skip + type: string + type: object + response: + description: Defines header and body treatment for responses + properties: + body: + default: None + description: Defines body processing mode + enum: + - None + - Streamed + - Buffered + - BufferedPartial + type: string + header: + default: Send + description: Defines header processing mode + enum: + - Default + - Send + - Skip + type: string + type: object + type: object + service: + description: Service defines the configuration of the external + processing service + properties: + backendRef: + description: BackendObjectReference references a Kubernetes + object that represents the backend server to which the processing + requests will be sent. Only service Kind is supported for + now. + 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 the Kubernetes resource kind of + the referent. For example \"Service\". \n Defaults to + \"Service\" when not specified. \n ExternalName services + can refer to CNAME DNS records that may live outside + of the cluster and as such are difficult to reason about + in terms of conformance. They also may not be safe to + forward to (see CVE-2021-25740 for more information). + Implementations SHOULD NOT support ExternalName Services. + \n Support: Core (Services with a type other than ExternalName) + \n Support: Implementation-specific (Services with type + ExternalName)" + 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 different than the local + 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 + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind == ''Service'') + ? has(self.port) : true' + required: + - backendRef + type: object + required: + - service + type: object + x-kubernetes-validations: + - message: group is invalid, only the core API group (specified by + omitting the group field or setting it to an empty string) is + supported + rule: 'has(self.service) ? (!has(self.service.backendRef.group) + || self.service.backendRef.group == "") : true' + - message: kind is invalid, only Service (specified by omitting the + kind field or setting it to 'Service') is supported + rule: 'has(self.service) ? (!has(self.service.backendRef.kind) || + self.service.backendRef.kind == ''Service'') : true' + priority: + description: Priority of the EnvoyExtensionPolicy. If multiple EnvoyExtensionPolices + are applied to the same TargetRef, extensions will execute 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 resource this policy + is being attached to. 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 + sectionName: + description: "SectionName is the name of a section within the + target resource. When unspecified, this targetRef targets the + entire resource. In the following resources, SectionName is + interpreted as the following: \n * Gateway: Listener Name * + Service: Port Name \n If a SectionName is specified, but does + not exist on the targeted object, the Policy must fail to attach, + and the policy implementation should record a `ResolvedRefs` + or similar Condition in the Policy's status." + 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: + - group + - kind + - name + type: object + x-kubernetes-validations: + - message: this policy can only have a targetRef.group of gateway.networking.k8s.io + rule: self.group == 'gateway.networking.k8s.io' + - message: this policy can only have a targetRef.kind of Gateway + rule: self.kind in ['Gateway'] + - message: this policy does not yet support the sectionName field + rule: '!has(self.sectionName)' + required: + - targetRef + type: object + status: + description: Status defines the current status of EnvoyExtensionPolicy. + properties: + conditions: + description: Conditions describe the current conditions of the EnvoyExtensionPolicy. + 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 + type: object + served: true + storage: true + subresources: + status: {} diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml index ad32967e71a..dd909d0d898 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml @@ -81,6 +81,27 @@ spec: items: type: string type: array + filterOrdering: + description: 'FilterOrdering defines the order of Envoy HTTP filter + execution. If a partial filter list is provided, the default order + will apply, and only the listed filters will be re-ordered amongst + themselves. Default: CORS, External-Processing, WASM, External-Authorization, + Basic-Authorization, OAuth2, JWT-Authentication, Fault-Injection, + Local-Rate-Limit, Global-Rate-Limit' + items: + description: EnvoyFilterName is the name of an Envoy HTTP filter + enum: + - CORS + - External-Authorization + - Basic-Authorization + - OAuth2 + - JWT-Authentication + - Fault-Injection + - Fault-Injection + - Local-Rate-Limit + - Global-Rate-Limit + type: string + type: array logging: default: level: diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index 12334c0bf2d..2684a320048 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -18,6 +18,8 @@ API group. - [BackendTrafficPolicyList](#backendtrafficpolicylist) - [ClientTrafficPolicy](#clienttrafficpolicy) - [ClientTrafficPolicyList](#clienttrafficpolicylist) +- [EnvoyExtensionPolicy](#envoyextensionpolicy) +- [EnvoyExtensionPolicyList](#envoyextensionpolicylist) - [EnvoyGateway](#envoygateway) - [EnvoyPatchPolicy](#envoypatchpolicy) - [EnvoyPatchPolicyList](#envoypatchpolicylist) @@ -454,6 +456,68 @@ _Appears in:_ | `defaultValue` | _string_ | false | DefaultValue defines the default value to use if the environment variable is not set. | +#### EnvoyExtensionPolicy + + + +EnvoyExtensionPolicy allows the user to configure various envoy extensibility options for the Gateway. + +_Appears in:_ +- [EnvoyExtensionPolicyList](#envoyextensionpolicylist) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `apiVersion` | _string_ | |`gateway.envoyproxy.io/v1alpha1` +| `kind` | _string_ | |`EnvoyExtensionPolicy` +| `metadata` | _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | true | Refer to Kubernetes API documentation for fields of `metadata`. | +| `spec` | _[EnvoyExtensionPolicySpec](#envoyextensionpolicyspec)_ | true | Spec defines the desired state of EnvoyExtensionPolicy. | + + +#### EnvoyExtensionPolicyList + + + +EnvoyExtensionPolicyList contains a list of EnvoyExtensionPolicy resources. + + + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `apiVersion` | _string_ | |`gateway.envoyproxy.io/v1alpha1` +| `kind` | _string_ | |`EnvoyExtensionPolicyList` +| `metadata` | _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#listmeta-v1-meta)_ | true | Refer to Kubernetes API documentation for fields of `metadata`. | +| `items` | _[EnvoyExtensionPolicy](#envoyextensionpolicy) array_ | true | | + + +#### EnvoyExtensionPolicySpec + + + +EnvoyExtensionPolicySpec defines the desired state of EnvoyExtensionPolicy. + +_Appears in:_ +- [EnvoyExtensionPolicy](#envoyextensionpolicy) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `targetRef` | _[PolicyTargetReferenceWithSectionName](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1alpha2.PolicyTargetReferenceWithSectionName)_ | true | TargetRef is the name of the Gateway resource this policy is being attached to. 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_ | false | Priority of the EnvoyExtensionPolicy. If multiple EnvoyExtensionPolices are applied to the same TargetRef, extensions will execute 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. | +| `extProc` | _[ExtProc](#extproc)_ | false | ExtProc defines the configuration for the external processor extension. | + + + + +#### EnvoyFilterName + +_Underlying type:_ _string_ + +EnvoyFilterName is the name of an Envoy HTTP filter + +_Appears in:_ +- [EnvoyProxySpec](#envoyproxyspec) + + + #### EnvoyGateway @@ -877,6 +941,7 @@ _Appears in:_ | `extraArgs` | _string array_ | false | ExtraArgs defines additional command line options that are provided to Envoy. More info: https://www.envoyproxy.io/docs/envoy/latest/operations/cli#command-line-options Note: some command line options are used internally(e.g. --log-level) so they cannot be provided here. | | `mergeGateways` | _boolean_ | false | MergeGateways defines if Gateway resources should be merged onto the same Envoy Proxy Infrastructure. Setting this field to true would merge all Gateway Listeners under the parent Gateway Class. This means that the port, protocol and hostname tuple must be unique for every listener. If a duplicate listener is detected, the newer listener (based on timestamp) will be rejected and its status will be updated with a "Accepted=False" condition. | | `shutdown` | _[ShutdownConfig](#shutdownconfig)_ | false | Shutdown defines configuration for graceful envoy shutdown process. | +| `filterOrdering` | _[EnvoyFilterName](#envoyfiltername) array_ | false | FilterOrdering defines the order of Envoy HTTP filter execution. If a partial filter list is provided, the default order will apply, and only the listed filters will be re-ordered amongst themselves. Default: CORS, External-Processing, WASM, External-Authorization, Basic-Authorization, OAuth2, JWT-Authentication, Fault-Injection, Local-Rate-Limit, Global-Rate-Limit | @@ -908,6 +973,105 @@ _Appears in:_ | `headersToExtAuth` | _string array_ | false | HeadersToExtAuth defines the client request headers that will be included in the request to the external authorization service. Note: If not specified, the default behavior for gRPC and HTTP external authorization services is different due to backward compatibility reasons. All headers will be included in the check request to a gRPC authorization server. Only the following headers will be included in the check request to an HTTP authorization server: Host, Method, Path, Content-Length, and Authorization. And these headers will always be included to the check request to an HTTP authorization server by default, no matter whether they are specified in HeadersToExtAuth or not. | +#### ExtProc + + + +ExtProc defines the configuration for External Processing. + +_Appears in:_ +- [EnvoyExtensionPolicySpec](#envoyextensionpolicyspec) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `service` | _[ExtProcService](#extprocservice)_ | true | Service defines the configuration of the external processing service | +| `processingMode` | _[ExtProcProcessingMode](#extprocprocessingmode)_ | false | ProcessingMode defines how request and response headers and body are processed Default: request and response headers are sent, bodies are not sent | +| `attributes` | _[ExtProcAttributes](#extprocattributes)_ | false | Attributes defines which envoy request and response attributes are provided as context to external processor Default: no attributes are sent | +| `metadataOptions` | _[ExtProcMetadataOptions](#extprocmetadataoptions)_ | false | MetadataOptions defines options related to the sending and receiving of dynamic metadata Default: no metadata context is sent or received | +| `messageTimeout` | _[Duration](#duration)_ | false | The timeout for a response to be returned from the external processor Default: 200ms | + + +#### ExtProcAttributes + + + +ExtProcAttributes defines which attributes are + +_Appears in:_ +- [ExtProc](#extproc) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `request` | _string array_ | false | defines attributes to send for Request processing | +| `response` | _string array_ | false | defines attributes to send for Response processing | + + +#### ExtProcBodyProcessingMode + +_Underlying type:_ _string_ + + + +_Appears in:_ +- [ProcessingModeOptions](#processingmodeoptions) + + + +#### ExtProcHeaderProcessingMode + +_Underlying type:_ _string_ + + + +_Appears in:_ +- [ProcessingModeOptions](#processingmodeoptions) + + + +#### ExtProcMetadataOptions + + + +ExtProcMetadataOptions defines options related to the sending and receiving of dynamic metadata + +_Appears in:_ +- [ExtProc](#extproc) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `forwardingNamespaces` | _[MetadataNamespaces](#metadatanamespaces) array_ | false | metadata namespaces forwarded to external processor | +| `receivingNamespaces` | _[MetadataNamespaces](#metadatanamespaces) array_ | false | metadata namespaces updatable by external processor | + + +#### ExtProcProcessingMode + + + +ExtProcProcessingMode defines if and how headers and bodies are sent to the service. https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ext_proc/v3/processing_mode.proto#envoy-v3-api-msg-extensions-filters-http-ext-proc-v3-processingmode + +_Appears in:_ +- [ExtProc](#extproc) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `request` | _[ProcessingModeOptions](#processingmodeoptions)_ | false | Defines header and body treatment for requests | +| `response` | _[ProcessingModeOptions](#processingmodeoptions)_ | false | Defines header and body treatment for responses | + + +#### ExtProcService + + + +ExtProcService defines the gRPC External Processing service using the envoy grpc client The processing request and response messages are defined in https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/ext_proc/v3/external_processor.proto + +_Appears in:_ +- [ExtProc](#extproc) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `backendRef` | _[BackendObjectReference](#backendobjectreference)_ | true | BackendObjectReference references a Kubernetes object that represents the backend server to which the processing requests will be sent. Only service Kind is supported for now. | + + #### ExtensionAPISettings @@ -921,6 +1085,7 @@ _Appears in:_ | Field | Type | Required | Description | | --- | --- | --- | --- | | `enableEnvoyPatchPolicy` | _boolean_ | true | EnableEnvoyPatchPolicy enables Envoy Gateway to reconcile and implement the EnvoyPatchPolicy resources. | +| `enableEnvoyExtensionPolicy` | _boolean_ | true | EnableEnvoyExtensionPolicy enables Envoy Gateway to reconcile and implement the EnvoyExtensionPolicy resources. | #### ExtensionHooks @@ -1044,6 +1209,8 @@ _Appears in:_ | `path` | _string_ | true | Path defines the file path used to expose envoy access log(e.g. /dev/stdout). | + + #### GRPCExtAuthService @@ -1595,6 +1762,21 @@ _Appears in:_ +#### MetadataNamespaces + + + +MetadataNamespaces defines metadata namespaces that can be used to forward or receive dynamic metadata + +_Appears in:_ +- [ExtProcMetadataOptions](#extprocmetadataoptions) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `untyped` | _string array_ | false | Specifies a list of metadata namespaces whose values, if present, will be passed to the ext_proc service as an opaque protobuf::Struct. | +| `typed` | _string array_ | false | Specifies a list of metadata namespaces whose values, if present, will be passed to the ext_proc service as a protobuf::Any. | + + #### MetricSinkType _Underlying type:_ _string_ @@ -1731,6 +1913,21 @@ _Appears in:_ | `backOff` | _[BackOffPolicy](#backoffpolicy)_ | false | Backoff is the backoff policy to be applied per retry attempt. gateway uses a fully jittered exponential back-off algorithm for retries. For additional details, see https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/router_filter#config-http-filters-router-x-envoy-max-retries | +#### ProcessingModeOptions + + + + + +_Appears in:_ +- [ExtProcProcessingMode](#extprocprocessingmode) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `header` | _[ExtProcHeaderProcessingMode](#extprocheaderprocessingmode)_ | false | Defines header processing mode | +| `body` | _[ExtProcBodyProcessingMode](#extprocbodyprocessingmode)_ | false | Defines body processing mode | + + #### ProviderType _Underlying type:_ _string_ diff --git a/site/content/en/latest/contributions/design/envoy-extension-policy.md b/site/content/en/latest/contributions/design/envoy-extension-policy.md new file mode 100644 index 00000000000..3348d7b751d --- /dev/null +++ b/site/content/en/latest/contributions/design/envoy-extension-policy.md @@ -0,0 +1,149 @@ +--- +title: "EnvoyExtensionPolicy " +--- + +## Overview + +This design document introduces the `EnvoyExtensionPolicy` API allowing system administrators to configure traffic +processing extensibility policies, based on existing Network and HTTP Envoy proxy [extension points][]. + +## Goals +* Add an API definition to hold settings for configuring extensibility rules on the traffic entering the gateway. + +## Non Goals +* Define the API configuration fields in this API. +* Define the API for the following extension options: + * Native Envoy extensions: custom C++ extensions that must be compiled into the Envoy binary. + * Non-filter extensions: services, matchers, tracers, private key providers, resource monitors, etc. + +## Implementation +`EnvoyExtensionPolicy` is a [Policy Attachment][] type API that can be used to extend [Gateway API][] +to define traffic extension rules. + +`BackendTrafficPolicy` is enhanced to allow users to provide per-route config for Extensions. + +### Example +Here is an example highlighting how a user can configure this API for the External Processing extension. + +```yaml +apiVersion: gateway.networking.k8s.io/v1 +kind: GatewayClass +metadata: + name: eg +spec: + controllerName: gateway.envoyproxy.io/gatewayclass-controller +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: eg + namespace: default +spec: + gatewayClassName: eg + listeners: + - name: https + protocol: HTTPS + port: 443 +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: backend + namespace: default +spec: + parentRefs: + - name: eg + hostnames: + - "www.example.com" + rules: + - backendRefs: + - group: "" + kind: Service + name: backend + port: 3000 + weight: 1 + matches: + - path: + type: PathPrefix + value: / +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyExtensionPolicy +metadata: + name: ext-proc-policy + namespace: default +spec: + priority: 10 + extProc: + service: + backendRef: + group: "" + kind: Service + name: myExtProc + port: 3000 + processingMode: + request: + headers: SEND + body: BUFFERED + response: + headers: SKIP + body: STREAMED + attributes: + request: + - xds.route_metadata + - connection.requested_server_name + response: + - request.path + messageTimeout: 5s + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: eg + namespace: default +``` + +## Features / API Fields +Here is a list of features that can be included in this API +* Network Filters: + * WASM + * Golang +* HTTP Filters: + * External Processing + * Lua + * WASM + * Golang + +## Design Decisions +* This API will only support a single `targetRef` and can bind to a `Gateway` resource or a `HTTPRoute` or `GRPCRoute` or `TCPRoute`. +* Extensions that support both Network and HTTP filter variants (e.g. WASM, Golang) will be translated to the appropriate filter type according to the sort of route that they attach to. +* Extensions that only support HTTP extensibility (Ext-Proc, LUA) can only be attached to HTTP/GRPC Routes. +* A user-defined extension that is added to the request processing flow can have a significant impact on security, + resilience and performance of the proxy. Gateway Operators can restrict access to the extensibility policy using K8s RBAC. +* Extensibility will be disabled by default, and can be enabled using [Envoy Gateway][] API. +* Users may need to customize the order of extension and built-in filter. The [Envoy Proxy][] APIis extended to support user-defined ordering of filters by their type. +* Gateway operators may need to include multiple extensions (e.g. WASM modules developed by different teams and distributed separately). + This API will support attachment of multiple policies. Extension will execute in an order defined by the priority field. +* This API resource MUST be part of same namespace as the targetRef resource +* If the policy targets a resource but cannot attach to it, this information should be reflected + in the Policy Status field using the `Conflicted=True` condition. +* If Policy A has a `targetRef` that includes a `sectionName` i.e. + it targets a specific Listener within a `Gateway` and Policy B has a `targetRef` that targets the same + entire Gateway then + * Policy A will be applied/attached to the specific Listener defined in the `targetRef.SectionName` + * Policy B will be applied to the remaining Listeners within the Gateway. Policy B will have an additional + status condition `Overridden=True`. +* A Policy targeting the most specific scope wins over a policy targeting a lesser specific scope. + i.e. A Policy targeting a `Listener` overrides a Policy targeting the `Gateway` the listener/section is a part of. + + +## Alternatives +* The project can indefinitely wait for these configuration parameters to be part of the [Gateway API][]. +* The project can implement support for HTTP traffic extensions using vendor-specific [Gateway API Route Filters][] + instead of policies. However, this option will is less convenient for definition of gateway-level extensions. + +[extension points]: https://www.envoyproxy.io/docs/envoy/latest/extending/extending +[Policy Attachment]: https://gateway-api.sigs.k8s.io/references/policy-attachment +[Gateway API]: https://gateway-api.sigs.k8s.io/ +[Gateway API Route Filters]: https://gateway-api.sigs.k8s.io/api-types/httproute/#filters-optional +[Envoy Gateway]: ../../api/extension_types/#envoygateway +[Envoy Proxy]: ../../api/extension_types/#envoyproxy From 851def253647bd830e05a0fe4114748aa57a39b7 Mon Sep 17 00:00:00 2001 From: Guy Daich Date: Thu, 14 Mar 2024 18:11:00 -0500 Subject: [PATCH 2/7] review fixes Signed-off-by: Guy Daich --- api/v1alpha1/envoyextensionypolicy_types.go | 13 +- api/v1alpha1/zz_generated.deepcopy.go | 22 -- ....envoyproxy.io_envoyextensionpolicies.yaml | 333 ++++++++++++++---- site/content/en/latest/api/extension_types.md | 2 - .../design/envoy-extension-policy.md | 2 +- 5 files changed, 269 insertions(+), 103 deletions(-) diff --git a/api/v1alpha1/envoyextensionypolicy_types.go b/api/v1alpha1/envoyextensionypolicy_types.go index 30931b93173..63d015cfbb9 100644 --- a/api/v1alpha1/envoyextensionypolicy_types.go +++ b/api/v1alpha1/envoyextensionypolicy_types.go @@ -30,7 +30,7 @@ type EnvoyExtensionPolicy struct { Spec EnvoyExtensionPolicySpec `json:"spec"` // Status defines the current status of EnvoyExtensionPolicy. - Status EnvoyExtensionPolicyStatus `json:"status,omitempty"` + Status gwapiv1a2.PolicyStatus `json:"status,omitempty"` } // EnvoyExtensionPolicySpec defines the desired state of EnvoyExtensionPolicy. @@ -62,17 +62,6 @@ type EnvoyExtensionPolicySpec struct { ExtProc *ExtProc `json:"extProc,omitempty"` } -// EnvoyExtensionPolicyStatus defines the state of EnvoyExtensionPolicy -type EnvoyExtensionPolicyStatus struct { - // Conditions describe the current conditions of the EnvoyExtensionPolicy. - // - // +optional - // +listType=map - // +listMapKey=type - // +kubebuilder:validation:MaxItems=8 - Conditions []metav1.Condition `json:"conditions,omitempty"` -} - //+kubebuilder:object:root=true // EnvoyExtensionPolicyList contains a list of EnvoyExtensionPolicy resources. diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 73d07d15681..430ccf5331d 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -733,28 +733,6 @@ func (in *EnvoyExtensionPolicySpec) DeepCopy() *EnvoyExtensionPolicySpec { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvoyExtensionPolicyStatus) DeepCopyInto(out *EnvoyExtensionPolicyStatus) { - *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 EnvoyExtensionPolicyStatus. -func (in *EnvoyExtensionPolicyStatus) DeepCopy() *EnvoyExtensionPolicyStatus { - if in == nil { - return nil - } - out := new(EnvoyExtensionPolicyStatus) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *EnvoyGateway) DeepCopyInto(out *EnvoyGateway) { *out = *in diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml index 53de9042e55..d3302160f96 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml @@ -322,79 +322,280 @@ spec: status: description: Status defines the current status of EnvoyExtensionPolicy. properties: - conditions: - description: Conditions describe the current conditions of the EnvoyExtensionPolicy. + ancestors: + description: "Ancestors is a list of ancestor resources (usually Gateways) + that are associated with the policy, and the status of the policy + with respect to each ancestor. When this policy attaches to a parent, + the controller that manages the parent and the ancestors MUST add + an entry to this list when the controller first sees the policy + and SHOULD update the entry as appropriate when the relevant ancestor + is modified. \n Note that choosing the relevant ancestor is left + to the Policy designers; an important part of Policy design is designing + the right object level at which to namespace this status. \n Note + also that implementations MUST ONLY populate ancestor status for + the Ancestor resources they are responsible for. Implementations + MUST use the ControllerName field to uniquely identify the entries + in this list that they are responsible for. \n Note that to achieve + this, the list of PolicyAncestorStatus structs MUST be treated as + a map with a composite key, made up of the AncestorRef and ControllerName + fields combined. \n A maximum of 16 ancestors will be represented + in this list. An empty list means the Policy is not relevant for + any ancestors. \n If this slice is full, implementations MUST NOT + add further entries. Instead they MUST consider the policy unimplementable + and signal that on any related resources such as the ancestor that + would be referenced here. For example, if this list was full on + BackendTLSPolicy, no additional Gateways would be able to reference + the Service targeted by the BackendTLSPolicy." 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 }" + description: "PolicyAncestorStatus describes the status of a route + with respect to an associated Ancestor. \n Ancestors refer to + objects that are either the Target of a policy or above it in + terms of object hierarchy. For example, if a policy targets a + Service, the Policy's Ancestors are, in order, the Service, the + HTTPRoute, the Gateway, and the GatewayClass. Almost always, in + this hierarchy, the Gateway will be the most useful object to + place Policy status on, so we recommend that implementations SHOULD + use Gateway as the PolicyAncestorStatus object unless the designers + have a _very_ good reason otherwise. \n In the context of policy + attachment, the Ancestor is used to distinguish which resource + results in a distinct application of this policy. For example, + if a policy targets a Service, it may have a distinct result per + attached Gateway. \n Policies targeting the same resource may + have different effects depending on the ancestors of those resources. + For example, different Gateways targeting the same Service may + have different capabilities, especially if they have different + underlying implementations. \n For example, in BackendTLSPolicy, + the Policy attaches to a Service that is used as a backend in + a HTTPRoute that is itself attached to a Gateway. In this case, + the relevant object for status is the Gateway, and that is the + ancestor object referred to in this status. \n Note that a parent + is also an ancestor, so for objects where the parent is the relevant + object for status, this struct SHOULD still be used. \n This struct + is intended to be used in a slice that's effectively a map, with + a composite key made up of the AncestorRef and the ControllerName." 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 + ancestorRef: + description: AncestorRef corresponds with a ParentRef in the + spec that this PolicyAncestorStatus 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 There are + two kinds of parent resources with \"Core\" support: \n + * Gateway (Gateway conformance profile) * Service (Mesh + conformance profile, experimental, ClusterIP Services + only) \n Support for other resources is Implementation-Specific." + 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 ParentRefs + from a Route to a Service in the same namespace are \"producer\" + routes, which apply default routing rules to inbound connections + from any namespace to the Service. \n ParentRefs from + a Route to a Service in a different namespace are \"consumer\" + routes, and these routing rules are only applied to outbound + connections originating from the same namespace as the + Route, for which the intended destination of the connections + are a Service targeted as a ParentRef of the Route. + \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 + When the parent resource is a Service, this targets a + specific port in the Service spec. When both Port (experimental) + and SectionName are specified, the name and port of the + selected port 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. * Service: Port Name. + When both Port (experimental) and SectionName are specified, + the name and port of the selected listener must match + both specified values. Note that attaching Routes to Services + as Parents is part of experimental Mesh support and is + not supported for any other purpose. \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 + conditions: + description: Conditions describes the status of the Policy with + respect to the given Ancestor. + 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 + 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-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])$ + 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 required: - - lastTransitionTime - - message - - reason - - status - - type + - ancestorRef + - controllerName type: object - maxItems: 8 + maxItems: 16 type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map + required: + - ancestors type: object required: - spec diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index 2684a320048..7219722e332 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -505,8 +505,6 @@ _Appears in:_ | `extProc` | _[ExtProc](#extproc)_ | false | ExtProc defines the configuration for the external processor extension. | - - #### EnvoyFilterName _Underlying type:_ _string_ diff --git a/site/content/en/latest/contributions/design/envoy-extension-policy.md b/site/content/en/latest/contributions/design/envoy-extension-policy.md index 3348d7b751d..0db1bcc7af3 100644 --- a/site/content/en/latest/contributions/design/envoy-extension-policy.md +++ b/site/content/en/latest/contributions/design/envoy-extension-policy.md @@ -120,7 +120,7 @@ Here is a list of features that can be included in this API * A user-defined extension that is added to the request processing flow can have a significant impact on security, resilience and performance of the proxy. Gateway Operators can restrict access to the extensibility policy using K8s RBAC. * Extensibility will be disabled by default, and can be enabled using [Envoy Gateway][] API. -* Users may need to customize the order of extension and built-in filter. The [Envoy Proxy][] APIis extended to support user-defined ordering of filters by their type. +* Users may need to customize the order of extension and built-in filters. This will be addressed in a separate issue. * Gateway operators may need to include multiple extensions (e.g. WASM modules developed by different teams and distributed separately). This API will support attachment of multiple policies. Extension will execute in an order defined by the priority field. * This API resource MUST be part of same namespace as the targetRef resource From 73f63eebf1132b793c83a44d41d33f87d5f07538 Mon Sep 17 00:00:00 2001 From: Guy Daich Date: Thu, 14 Mar 2024 20:12:38 -0500 Subject: [PATCH 3/7] fix lint Signed-off-by: Guy Daich --- .../en/latest/contributions/design/envoy-extension-policy.md | 1 - 1 file changed, 1 deletion(-) diff --git a/site/content/en/latest/contributions/design/envoy-extension-policy.md b/site/content/en/latest/contributions/design/envoy-extension-policy.md index 0db1bcc7af3..bca93c8d5c4 100644 --- a/site/content/en/latest/contributions/design/envoy-extension-policy.md +++ b/site/content/en/latest/contributions/design/envoy-extension-policy.md @@ -146,4 +146,3 @@ Here is a list of features that can be included in this API [Gateway API]: https://gateway-api.sigs.k8s.io/ [Gateway API Route Filters]: https://gateway-api.sigs.k8s.io/api-types/httproute/#filters-optional [Envoy Gateway]: ../../api/extension_types/#envoygateway -[Envoy Proxy]: ../../api/extension_types/#envoyproxy From 81d6c69159f0071e880737111d4f807c409cfdf6 Mon Sep 17 00:00:00 2001 From: Guy Daich Date: Fri, 15 Mar 2024 13:29:01 -0500 Subject: [PATCH 4/7] remove ext-proc, ordering Signed-off-by: Guy Daich --- api/v1alpha1/envoyextensionypolicy_types.go | 5 - api/v1alpha1/envoyproxy_types.go | 26 --- api/v1alpha1/ext_proc_types.go | 130 ----------- api/v1alpha1/zz_generated.deepcopy.go | 191 ---------------- ....envoyproxy.io_envoyextensionpolicies.yaml | 209 ------------------ .../gateway.envoyproxy.io_envoyproxies.yaml | 21 -- site/content/en/latest/api/extension_types.md | 142 ------------ .../design/envoy-extension-policy.md | 2 +- 8 files changed, 1 insertion(+), 725 deletions(-) delete mode 100644 api/v1alpha1/ext_proc_types.go diff --git a/api/v1alpha1/envoyextensionypolicy_types.go b/api/v1alpha1/envoyextensionypolicy_types.go index 63d015cfbb9..3ba0468616b 100644 --- a/api/v1alpha1/envoyextensionypolicy_types.go +++ b/api/v1alpha1/envoyextensionypolicy_types.go @@ -55,11 +55,6 @@ type EnvoyExtensionPolicySpec struct { // // +optional Priority int32 `json:"priority,omitempty"` - - // ExtProc defines the configuration for the external processor extension. - // - // +optional - ExtProc *ExtProc `json:"extProc,omitempty"` } //+kubebuilder:object:root=true diff --git a/api/v1alpha1/envoyproxy_types.go b/api/v1alpha1/envoyproxy_types.go index 266e0d99b50..eb4a4a8a7ee 100644 --- a/api/v1alpha1/envoyproxy_types.go +++ b/api/v1alpha1/envoyproxy_types.go @@ -14,24 +14,6 @@ const ( KindEnvoyProxy = "EnvoyProxy" ) -// +kubebuilder:validation:Enum=CORS;External-Authorization;Basic-Authorization;OAuth2;JWT-Authentication;Fault-Injection;Fault-Injection;Local-Rate-Limit;Global-Rate-Limit -// -// EnvoyFilterName is the name of an Envoy HTTP filter -type EnvoyFilterName string - -const ( - CORSFilterName EnvoyFilterName = "CORS" - ExtAuthFilterName EnvoyFilterName = "External-Authorization" - BasicAuthFilterName EnvoyFilterName = "Basic-Authorization" - OAuth2FilterName EnvoyFilterName = "OAuth2" - JWTAuthnOAuth2FilterName EnvoyFilterName = "JWT-Authentication" - FaultInjectionFilterName EnvoyFilterName = "Fault-Injection" - LocalRateLimitFilterName EnvoyFilterName = "Local-Rate-Limit" - GlobalRateLimitFilterName EnvoyFilterName = "Global-Rate-Limit" - ExtProcFilterName EnvoyFilterName = "External-Processing" - WASMFilterName EnvoyFilterName = "WASM" -) - // +kubebuilder:object:root=true // +kubebuilder:resource:categories=envoy-gateway,shortName=eproxy // +kubebuilder:subresource:status @@ -104,14 +86,6 @@ type EnvoyProxySpec struct { // // +optional Shutdown *ShutdownConfig `json:"shutdown,omitempty"` - - // FilterOrdering defines the order of Envoy HTTP filter execution. If a partial filter list is provided, the default - // order will apply, and only the listed filters will be re-ordered amongst themselves. - // Default: CORS, External-Processing, WASM, External-Authorization, Basic-Authorization, OAuth2, JWT-Authentication, - // Fault-Injection, Local-Rate-Limit, Global-Rate-Limit - // - // +optional - FilterOrdering []EnvoyFilterName `json:"filterOrdering,omitempty"` } type ProxyTelemetry struct { diff --git a/api/v1alpha1/ext_proc_types.go b/api/v1alpha1/ext_proc_types.go deleted file mode 100644 index b9cbdc2867b..00000000000 --- a/api/v1alpha1/ext_proc_types.go +++ /dev/null @@ -1,130 +0,0 @@ -// 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 ( - gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" -) - -// +kubebuilder:validation:Enum=Default;Send;Skip -type ExtProcHeaderProcessingMode string - -const ( - DefaultExtProcHeaderProcessingMode ExtProcHeaderProcessingMode = "Default" - SendExtProcHeaderProcessingMode ExtProcHeaderProcessingMode = "Send" - SkipExtProcHeaderProcessingMode ExtProcHeaderProcessingMode = "Skip" -) - -// +kubebuilder:validation:Enum=None;Streamed;Buffered;BufferedPartial -type ExtProcBodyProcessingMode string - -const ( - NoneExtProcHeaderProcessingMode ExtProcBodyProcessingMode = "None" - StreamedExtProcHeaderProcessingMode ExtProcBodyProcessingMode = "Streamed" - BufferedExtProcHeaderProcessingMode ExtProcBodyProcessingMode = "Buffered" - BufferedPartialExtProcHeaderProcessingMode ExtProcBodyProcessingMode = "BufferedPartial" -) - -type ProcessingModeOptions struct { - // Defines header processing mode - // - // +kubebuilder:default:=Send - // +optional - Headers *ExtProcHeaderProcessingMode `json:"header,omitempty"` - // Defines body processing mode - // - // +kubebuilder:default:=None - // +optional - Body *ExtProcBodyProcessingMode `json:"body,omitempty"` -} - -// ExtProcProcessingMode defines if and how headers and bodies are sent to the service. -// https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ext_proc/v3/processing_mode.proto#envoy-v3-api-msg-extensions-filters-http-ext-proc-v3-processingmode -type ExtProcProcessingMode struct { - // Defines header and body treatment for requests - // - // +optional - Request *ProcessingModeOptions `json:"request,omitempty"` - // Defines header and body treatment for responses - // - // +optional - Response *ProcessingModeOptions `json:"response,omitempty"` -} - -// ExtProcAttributes defines which attributes are -type ExtProcAttributes struct { - // defines attributes to send for Request processing - // - // +optional - Request []string `json:"request,omitempty"` - // defines attributes to send for Response processing - // - // +optional - Response []string `json:"response,omitempty"` -} - -// MetadataNamespaces defines metadata namespaces that can be used to forward or receive dynamic metadata -type MetadataNamespaces struct { - // Specifies a list of metadata namespaces whose values, if present, will be passed to the ext_proc service as an opaque protobuf::Struct. - // - // +optional - Untyped []string `json:"untyped,omitempty"` - // Specifies a list of metadata namespaces whose values, if present, will be passed to the ext_proc service as a protobuf::Any. - // - // +optional - Typed []string `json:"typed,omitempty"` -} - -// ExtProcMetadataOptions defines options related to the sending and receiving of dynamic metadata -type ExtProcMetadataOptions struct { - // metadata namespaces forwarded to external processor - // - // +optional - ForwardingNamespaces []MetadataNamespaces `json:"forwardingNamespaces,omitempty"` - // metadata namespaces updatable by external processor - // - // +optional - ReceivingNamespaces []MetadataNamespaces `json:"receivingNamespaces,omitempty"` -} - -// +kubebuilder:validation:XValidation:rule="has(self.service) ? (!has(self.service.backendRef.group) || self.service.backendRef.group == \"\") : true", message="group is invalid, only the core API group (specified by omitting the group field or setting it to an empty string) is supported" -// +kubebuilder:validation:XValidation:rule="has(self.service) ? (!has(self.service.backendRef.kind) || self.service.backendRef.kind == 'Service') : true", message="kind is invalid, only Service (specified by omitting the kind field or setting it to 'Service') is supported" -// -// ExtProc defines the configuration for External Processing. -type ExtProc struct { - // Service defines the configuration of the external processing service - Service ExtProcService `json:"service"` - // ProcessingMode defines how request and response headers and body are processed - // Default: request and response headers are sent, bodies are not sent - // - // +optional - ProcessingMode *ExtProcProcessingMode `json:"processingMode,omitempty"` - // Attributes defines which envoy request and response attributes are provided as context to external processor - // Default: no attributes are sent - // - // +optional - Attributes *ExtProcAttributes `json:"attributes,omitempty"` - // MetadataOptions defines options related to the sending and receiving of dynamic metadata - // Default: no metadata context is sent or received - // - // +optional - MetadataOptions *ExtProcMetadataOptions `json:"metadataOptions,omitempty"` - // The timeout for a response to be returned from the external processor - // Default: 200ms - // - // +optional - MessageTimeout *gwapiv1.Duration `json:"messageTimeout,omitempty"` -} - -// ExtProcService defines the gRPC External Processing service using the envoy grpc client -// The processing request and response messages are defined in -// https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/ext_proc/v3/external_processor.proto -type ExtProcService struct { - // BackendObjectReference references a Kubernetes object that represents the - // backend server to which the processing requests will be sent. - // Only service Kind is supported for now. - BackendRef gwapiv1.BackendObjectReference `json:"backendRef"` -} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 430ccf5331d..8ad06fbfcd2 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -716,11 +716,6 @@ func (in *EnvoyExtensionPolicyList) DeepCopyObject() runtime.Object { func (in *EnvoyExtensionPolicySpec) DeepCopyInto(out *EnvoyExtensionPolicySpec) { *out = *in in.TargetRef.DeepCopyInto(&out.TargetRef) - if in.ExtProc != nil { - in, out := &in.ExtProc, &out.ExtProc - *out = new(ExtProc) - (*in).DeepCopyInto(*out) - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyExtensionPolicySpec. @@ -1365,11 +1360,6 @@ func (in *EnvoyProxySpec) DeepCopyInto(out *EnvoyProxySpec) { *out = new(ShutdownConfig) (*in).DeepCopyInto(*out) } - if in.FilterOrdering != nil { - in, out := &in.FilterOrdering, &out.FilterOrdering - *out = make([]EnvoyFilterName, len(*in)) - copy(*out, *in) - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyProxySpec. @@ -1427,137 +1417,6 @@ func (in *ExtAuth) DeepCopy() *ExtAuth { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ExtProc) DeepCopyInto(out *ExtProc) { - *out = *in - in.Service.DeepCopyInto(&out.Service) - if in.ProcessingMode != nil { - in, out := &in.ProcessingMode, &out.ProcessingMode - *out = new(ExtProcProcessingMode) - (*in).DeepCopyInto(*out) - } - if in.Attributes != nil { - in, out := &in.Attributes, &out.Attributes - *out = new(ExtProcAttributes) - (*in).DeepCopyInto(*out) - } - if in.MetadataOptions != nil { - in, out := &in.MetadataOptions, &out.MetadataOptions - *out = new(ExtProcMetadataOptions) - (*in).DeepCopyInto(*out) - } - if in.MessageTimeout != nil { - in, out := &in.MessageTimeout, &out.MessageTimeout - *out = new(apisv1.Duration) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtProc. -func (in *ExtProc) DeepCopy() *ExtProc { - if in == nil { - return nil - } - out := new(ExtProc) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ExtProcAttributes) DeepCopyInto(out *ExtProcAttributes) { - *out = *in - if in.Request != nil { - in, out := &in.Request, &out.Request - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Response != nil { - in, out := &in.Response, &out.Response - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtProcAttributes. -func (in *ExtProcAttributes) DeepCopy() *ExtProcAttributes { - if in == nil { - return nil - } - out := new(ExtProcAttributes) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ExtProcMetadataOptions) DeepCopyInto(out *ExtProcMetadataOptions) { - *out = *in - if in.ForwardingNamespaces != nil { - in, out := &in.ForwardingNamespaces, &out.ForwardingNamespaces - *out = make([]MetadataNamespaces, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.ReceivingNamespaces != nil { - in, out := &in.ReceivingNamespaces, &out.ReceivingNamespaces - *out = make([]MetadataNamespaces, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtProcMetadataOptions. -func (in *ExtProcMetadataOptions) DeepCopy() *ExtProcMetadataOptions { - if in == nil { - return nil - } - out := new(ExtProcMetadataOptions) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ExtProcProcessingMode) DeepCopyInto(out *ExtProcProcessingMode) { - *out = *in - if in.Request != nil { - in, out := &in.Request, &out.Request - *out = new(ProcessingModeOptions) - (*in).DeepCopyInto(*out) - } - if in.Response != nil { - in, out := &in.Response, &out.Response - *out = new(ProcessingModeOptions) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtProcProcessingMode. -func (in *ExtProcProcessingMode) DeepCopy() *ExtProcProcessingMode { - if in == nil { - return nil - } - out := new(ExtProcProcessingMode) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ExtProcService) DeepCopyInto(out *ExtProcService) { - *out = *in - in.BackendRef.DeepCopyInto(&out.BackendRef) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtProcService. -func (in *ExtProcService) DeepCopy() *ExtProcService { - if in == nil { - return nil - } - out := new(ExtProcService) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ExtensionAPISettings) DeepCopyInto(out *ExtensionAPISettings) { *out = *in @@ -2608,31 +2467,6 @@ func (in *LocalRateLimit) DeepCopy() *LocalRateLimit { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *MetadataNamespaces) DeepCopyInto(out *MetadataNamespaces) { - *out = *in - if in.Untyped != nil { - in, out := &in.Untyped, &out.Untyped - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Typed != nil { - in, out := &in.Typed, &out.Typed - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetadataNamespaces. -func (in *MetadataNamespaces) DeepCopy() *MetadataNamespaces { - if in == nil { - return nil - } - out := new(MetadataNamespaces) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OIDC) DeepCopyInto(out *OIDC) { *out = *in @@ -2812,31 +2646,6 @@ func (in *PerRetryPolicy) DeepCopy() *PerRetryPolicy { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ProcessingModeOptions) DeepCopyInto(out *ProcessingModeOptions) { - *out = *in - if in.Headers != nil { - in, out := &in.Headers, &out.Headers - *out = new(ExtProcHeaderProcessingMode) - **out = **in - } - if in.Body != nil { - in, out := &in.Body, &out.Body - *out = new(ExtProcBodyProcessingMode) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProcessingModeOptions. -func (in *ProcessingModeOptions) DeepCopy() *ProcessingModeOptions { - if in == nil { - return nil - } - out := new(ProcessingModeOptions) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ProxyAccessLog) DeepCopyInto(out *ProxyAccessLog) { *out = *in diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml index d3302160f96..fae1d37e18d 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml @@ -44,215 +44,6 @@ spec: spec: description: Spec defines the desired state of EnvoyExtensionPolicy. properties: - extProc: - description: ExtProc defines the configuration for the external processor - extension. - properties: - attributes: - description: 'Attributes defines which envoy request and response - attributes are provided as context to external processor Default: - no attributes are sent' - properties: - request: - description: defines attributes to send for Request processing - items: - type: string - type: array - response: - description: defines attributes to send for Response processing - items: - type: string - type: array - type: object - messageTimeout: - description: 'The timeout for a response to be returned from the - external processor Default: 200ms' - pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ - type: string - metadataOptions: - description: 'MetadataOptions defines options related to the sending - and receiving of dynamic metadata Default: no metadata context - is sent or received' - properties: - forwardingNamespaces: - description: metadata namespaces forwarded to external processor - items: - description: MetadataNamespaces defines metadata namespaces - that can be used to forward or receive dynamic metadata - properties: - typed: - description: Specifies a list of metadata namespaces - whose values, if present, will be passed to the ext_proc - service as a protobuf::Any. - items: - type: string - type: array - untyped: - description: Specifies a list of metadata namespaces - whose values, if present, will be passed to the ext_proc - service as an opaque protobuf::Struct. - items: - type: string - type: array - type: object - type: array - receivingNamespaces: - description: metadata namespaces updatable by external processor - items: - description: MetadataNamespaces defines metadata namespaces - that can be used to forward or receive dynamic metadata - properties: - typed: - description: Specifies a list of metadata namespaces - whose values, if present, will be passed to the ext_proc - service as a protobuf::Any. - items: - type: string - type: array - untyped: - description: Specifies a list of metadata namespaces - whose values, if present, will be passed to the ext_proc - service as an opaque protobuf::Struct. - items: - type: string - type: array - type: object - type: array - type: object - processingMode: - description: 'ProcessingMode defines how request and response - headers and body are processed Default: request and response - headers are sent, bodies are not sent' - properties: - request: - description: Defines header and body treatment for requests - properties: - body: - default: None - description: Defines body processing mode - enum: - - None - - Streamed - - Buffered - - BufferedPartial - type: string - header: - default: Send - description: Defines header processing mode - enum: - - Default - - Send - - Skip - type: string - type: object - response: - description: Defines header and body treatment for responses - properties: - body: - default: None - description: Defines body processing mode - enum: - - None - - Streamed - - Buffered - - BufferedPartial - type: string - header: - default: Send - description: Defines header processing mode - enum: - - Default - - Send - - Skip - type: string - type: object - type: object - service: - description: Service defines the configuration of the external - processing service - properties: - backendRef: - description: BackendObjectReference references a Kubernetes - object that represents the backend server to which the processing - requests will be sent. Only service Kind is supported for - now. - 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 the Kubernetes resource kind of - the referent. For example \"Service\". \n Defaults to - \"Service\" when not specified. \n ExternalName services - can refer to CNAME DNS records that may live outside - of the cluster and as such are difficult to reason about - in terms of conformance. They also may not be safe to - forward to (see CVE-2021-25740 for more information). - Implementations SHOULD NOT support ExternalName Services. - \n Support: Core (Services with a type other than ExternalName) - \n Support: Implementation-specific (Services with type - ExternalName)" - 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 different than the local - 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 - x-kubernetes-validations: - - message: Must have port for Service reference - rule: '(size(self.group) == 0 && self.kind == ''Service'') - ? has(self.port) : true' - required: - - backendRef - type: object - required: - - service - type: object - x-kubernetes-validations: - - message: group is invalid, only the core API group (specified by - omitting the group field or setting it to an empty string) is - supported - rule: 'has(self.service) ? (!has(self.service.backendRef.group) - || self.service.backendRef.group == "") : true' - - message: kind is invalid, only Service (specified by omitting the - kind field or setting it to 'Service') is supported - rule: 'has(self.service) ? (!has(self.service.backendRef.kind) || - self.service.backendRef.kind == ''Service'') : true' priority: description: Priority of the EnvoyExtensionPolicy. If multiple EnvoyExtensionPolices are applied to the same TargetRef, extensions will execute in the diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml index dd909d0d898..ad32967e71a 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml @@ -81,27 +81,6 @@ spec: items: type: string type: array - filterOrdering: - description: 'FilterOrdering defines the order of Envoy HTTP filter - execution. If a partial filter list is provided, the default order - will apply, and only the listed filters will be re-ordered amongst - themselves. Default: CORS, External-Processing, WASM, External-Authorization, - Basic-Authorization, OAuth2, JWT-Authentication, Fault-Injection, - Local-Rate-Limit, Global-Rate-Limit' - items: - description: EnvoyFilterName is the name of an Envoy HTTP filter - enum: - - CORS - - External-Authorization - - Basic-Authorization - - OAuth2 - - JWT-Authentication - - Fault-Injection - - Fault-Injection - - Local-Rate-Limit - - Global-Rate-Limit - type: string - type: array logging: default: level: diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index 7219722e332..0e4840df524 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -502,18 +502,6 @@ _Appears in:_ | --- | --- | --- | --- | | `targetRef` | _[PolicyTargetReferenceWithSectionName](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1alpha2.PolicyTargetReferenceWithSectionName)_ | true | TargetRef is the name of the Gateway resource this policy is being attached to. 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_ | false | Priority of the EnvoyExtensionPolicy. If multiple EnvoyExtensionPolices are applied to the same TargetRef, extensions will execute 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. | -| `extProc` | _[ExtProc](#extproc)_ | false | ExtProc defines the configuration for the external processor extension. | - - -#### EnvoyFilterName - -_Underlying type:_ _string_ - -EnvoyFilterName is the name of an Envoy HTTP filter - -_Appears in:_ -- [EnvoyProxySpec](#envoyproxyspec) - #### EnvoyGateway @@ -939,7 +927,6 @@ _Appears in:_ | `extraArgs` | _string array_ | false | ExtraArgs defines additional command line options that are provided to Envoy. More info: https://www.envoyproxy.io/docs/envoy/latest/operations/cli#command-line-options Note: some command line options are used internally(e.g. --log-level) so they cannot be provided here. | | `mergeGateways` | _boolean_ | false | MergeGateways defines if Gateway resources should be merged onto the same Envoy Proxy Infrastructure. Setting this field to true would merge all Gateway Listeners under the parent Gateway Class. This means that the port, protocol and hostname tuple must be unique for every listener. If a duplicate listener is detected, the newer listener (based on timestamp) will be rejected and its status will be updated with a "Accepted=False" condition. | | `shutdown` | _[ShutdownConfig](#shutdownconfig)_ | false | Shutdown defines configuration for graceful envoy shutdown process. | -| `filterOrdering` | _[EnvoyFilterName](#envoyfiltername) array_ | false | FilterOrdering defines the order of Envoy HTTP filter execution. If a partial filter list is provided, the default order will apply, and only the listed filters will be re-ordered amongst themselves. Default: CORS, External-Processing, WASM, External-Authorization, Basic-Authorization, OAuth2, JWT-Authentication, Fault-Injection, Local-Rate-Limit, Global-Rate-Limit | @@ -971,105 +958,6 @@ _Appears in:_ | `headersToExtAuth` | _string array_ | false | HeadersToExtAuth defines the client request headers that will be included in the request to the external authorization service. Note: If not specified, the default behavior for gRPC and HTTP external authorization services is different due to backward compatibility reasons. All headers will be included in the check request to a gRPC authorization server. Only the following headers will be included in the check request to an HTTP authorization server: Host, Method, Path, Content-Length, and Authorization. And these headers will always be included to the check request to an HTTP authorization server by default, no matter whether they are specified in HeadersToExtAuth or not. | -#### ExtProc - - - -ExtProc defines the configuration for External Processing. - -_Appears in:_ -- [EnvoyExtensionPolicySpec](#envoyextensionpolicyspec) - -| Field | Type | Required | Description | -| --- | --- | --- | --- | -| `service` | _[ExtProcService](#extprocservice)_ | true | Service defines the configuration of the external processing service | -| `processingMode` | _[ExtProcProcessingMode](#extprocprocessingmode)_ | false | ProcessingMode defines how request and response headers and body are processed Default: request and response headers are sent, bodies are not sent | -| `attributes` | _[ExtProcAttributes](#extprocattributes)_ | false | Attributes defines which envoy request and response attributes are provided as context to external processor Default: no attributes are sent | -| `metadataOptions` | _[ExtProcMetadataOptions](#extprocmetadataoptions)_ | false | MetadataOptions defines options related to the sending and receiving of dynamic metadata Default: no metadata context is sent or received | -| `messageTimeout` | _[Duration](#duration)_ | false | The timeout for a response to be returned from the external processor Default: 200ms | - - -#### ExtProcAttributes - - - -ExtProcAttributes defines which attributes are - -_Appears in:_ -- [ExtProc](#extproc) - -| Field | Type | Required | Description | -| --- | --- | --- | --- | -| `request` | _string array_ | false | defines attributes to send for Request processing | -| `response` | _string array_ | false | defines attributes to send for Response processing | - - -#### ExtProcBodyProcessingMode - -_Underlying type:_ _string_ - - - -_Appears in:_ -- [ProcessingModeOptions](#processingmodeoptions) - - - -#### ExtProcHeaderProcessingMode - -_Underlying type:_ _string_ - - - -_Appears in:_ -- [ProcessingModeOptions](#processingmodeoptions) - - - -#### ExtProcMetadataOptions - - - -ExtProcMetadataOptions defines options related to the sending and receiving of dynamic metadata - -_Appears in:_ -- [ExtProc](#extproc) - -| Field | Type | Required | Description | -| --- | --- | --- | --- | -| `forwardingNamespaces` | _[MetadataNamespaces](#metadatanamespaces) array_ | false | metadata namespaces forwarded to external processor | -| `receivingNamespaces` | _[MetadataNamespaces](#metadatanamespaces) array_ | false | metadata namespaces updatable by external processor | - - -#### ExtProcProcessingMode - - - -ExtProcProcessingMode defines if and how headers and bodies are sent to the service. https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ext_proc/v3/processing_mode.proto#envoy-v3-api-msg-extensions-filters-http-ext-proc-v3-processingmode - -_Appears in:_ -- [ExtProc](#extproc) - -| Field | Type | Required | Description | -| --- | --- | --- | --- | -| `request` | _[ProcessingModeOptions](#processingmodeoptions)_ | false | Defines header and body treatment for requests | -| `response` | _[ProcessingModeOptions](#processingmodeoptions)_ | false | Defines header and body treatment for responses | - - -#### ExtProcService - - - -ExtProcService defines the gRPC External Processing service using the envoy grpc client The processing request and response messages are defined in https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/ext_proc/v3/external_processor.proto - -_Appears in:_ -- [ExtProc](#extproc) - -| Field | Type | Required | Description | -| --- | --- | --- | --- | -| `backendRef` | _[BackendObjectReference](#backendobjectreference)_ | true | BackendObjectReference references a Kubernetes object that represents the backend server to which the processing requests will be sent. Only service Kind is supported for now. | - - #### ExtensionAPISettings @@ -1760,21 +1648,6 @@ _Appears in:_ -#### MetadataNamespaces - - - -MetadataNamespaces defines metadata namespaces that can be used to forward or receive dynamic metadata - -_Appears in:_ -- [ExtProcMetadataOptions](#extprocmetadataoptions) - -| Field | Type | Required | Description | -| --- | --- | --- | --- | -| `untyped` | _string array_ | false | Specifies a list of metadata namespaces whose values, if present, will be passed to the ext_proc service as an opaque protobuf::Struct. | -| `typed` | _string array_ | false | Specifies a list of metadata namespaces whose values, if present, will be passed to the ext_proc service as a protobuf::Any. | - - #### MetricSinkType _Underlying type:_ _string_ @@ -1911,21 +1784,6 @@ _Appears in:_ | `backOff` | _[BackOffPolicy](#backoffpolicy)_ | false | Backoff is the backoff policy to be applied per retry attempt. gateway uses a fully jittered exponential back-off algorithm for retries. For additional details, see https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/router_filter#config-http-filters-router-x-envoy-max-retries | -#### ProcessingModeOptions - - - - - -_Appears in:_ -- [ExtProcProcessingMode](#extprocprocessingmode) - -| Field | Type | Required | Description | -| --- | --- | --- | --- | -| `header` | _[ExtProcHeaderProcessingMode](#extprocheaderprocessingmode)_ | false | Defines header processing mode | -| `body` | _[ExtProcBodyProcessingMode](#extprocbodyprocessingmode)_ | false | Defines body processing mode | - - #### ProviderType _Underlying type:_ _string_ diff --git a/site/content/en/latest/contributions/design/envoy-extension-policy.md b/site/content/en/latest/contributions/design/envoy-extension-policy.md index bca93c8d5c4..f118258dc26 100644 --- a/site/content/en/latest/contributions/design/envoy-extension-policy.md +++ b/site/content/en/latest/contributions/design/envoy-extension-policy.md @@ -75,7 +75,7 @@ metadata: spec: priority: 10 extProc: - service: + - service: backendRef: group: "" kind: Service From 4a4818545ef8b0e7bceb955a078f7916bf08fcf0 Mon Sep 17 00:00:00 2001 From: Guy Daich Date: Fri, 15 Mar 2024 13:50:59 -0500 Subject: [PATCH 5/7] fix gen Signed-off-by: Guy Daich --- api/v1alpha1/zz_generated.deepcopy.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index a92226d5aa9..440fd91eb21 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -1668,12 +1668,12 @@ func (in *FilterOrdering) DeepCopyInto(out *FilterOrdering) { *out = *in if in.DrainTimeout != nil { in, out := &in.DrainTimeout, &out.DrainTimeout - *out = new(v1.Duration) + *out = new(metav1.Duration) **out = **in } if in.MinDrainDuration != nil { in, out := &in.MinDrainDuration, &out.MinDrainDuration - *out = new(v1.Duration) + *out = new(metav1.Duration) **out = **in } } From 5475dc217e122a2fbb2e1bdaf50b0b5bfae18c92 Mon Sep 17 00:00:00 2001 From: Guy Daich Date: Fri, 15 Mar 2024 14:32:47 -0500 Subject: [PATCH 6/7] remove ordering, add CEL, fix api and design doc Signed-off-by: Guy Daich --- api/v1alpha1/envoyextensionypolicy_types.go | 2 +- api/v1alpha1/envoyproxy_types.go | 14 -- api/v1alpha1/zz_generated.deepcopy.go | 25 --- ....envoyproxy.io_envoyextensionpolicies.yaml | 5 +- site/content/en/latest/api/extension_types.md | 2 - .../design/envoy-extension-policy.md | 19 +- .../envoyextensionpolicy_test.go | 186 ++++++++++++++++++ 7 files changed, 208 insertions(+), 45 deletions(-) create mode 100644 test/cel-validation/envoyextensionpolicy_test.go diff --git a/api/v1alpha1/envoyextensionypolicy_types.go b/api/v1alpha1/envoyextensionypolicy_types.go index 3ba0468616b..4b14955be42 100644 --- a/api/v1alpha1/envoyextensionypolicy_types.go +++ b/api/v1alpha1/envoyextensionypolicy_types.go @@ -36,7 +36,7 @@ type EnvoyExtensionPolicy struct { // EnvoyExtensionPolicySpec defines the desired state of EnvoyExtensionPolicy. type EnvoyExtensionPolicySpec struct { // +kubebuilder:validation:XValidation:rule="self.group == 'gateway.networking.k8s.io'", message="this policy can only have a targetRef.group of gateway.networking.k8s.io" - // +kubebuilder:validation:XValidation:rule="self.kind in ['Gateway']", message="this policy can only have a targetRef.kind of Gateway" + // +kubebuilder:validation:XValidation:rule="self.kind in ['Gateway', 'HTTPRoute', 'GRPCRoute', 'UDPRoute', 'TCPRoute', 'TLSRoute']", message="this policy can only have a targetRef.kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute" // +kubebuilder:validation:XValidation:rule="!has(self.sectionName)",message="this policy does not yet support the sectionName field" // // TargetRef is the name of the Gateway resource this policy diff --git a/api/v1alpha1/envoyproxy_types.go b/api/v1alpha1/envoyproxy_types.go index eb4a4a8a7ee..a8bb1505717 100644 --- a/api/v1alpha1/envoyproxy_types.go +++ b/api/v1alpha1/envoyproxy_types.go @@ -134,20 +134,6 @@ type ShutdownConfig struct { MinDrainDuration *metav1.Duration `json:"minDrainDuration,omitempty"` } -// FilterOrdering defines the ordering of well-known. -type FilterOrdering struct { - // DrainTimeout defines the graceful drain timeout. This should be less than the pod's terminationGracePeriodSeconds. - // If unspecified, defaults to 600 seconds. - // - // +optional - DrainTimeout *metav1.Duration `json:"drainTimeout,omitempty"` - // MinDrainDuration defines the minimum drain duration allowing time for endpoint deprogramming to complete. - // If unspecified, defaults to 5 seconds. - // - // +optional - MinDrainDuration *metav1.Duration `json:"minDrainDuration,omitempty"` -} - // EnvoyProxyKubernetesProvider defines configuration for the Kubernetes resource // provider. type EnvoyProxyKubernetesProvider struct { diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 440fd91eb21..def26e867ad 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -1663,31 +1663,6 @@ func (in *FileEnvoyProxyAccessLog) DeepCopy() *FileEnvoyProxyAccessLog { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *FilterOrdering) DeepCopyInto(out *FilterOrdering) { - *out = *in - if in.DrainTimeout != nil { - in, out := &in.DrainTimeout, &out.DrainTimeout - *out = new(metav1.Duration) - **out = **in - } - if in.MinDrainDuration != nil { - in, out := &in.MinDrainDuration, &out.MinDrainDuration - *out = new(metav1.Duration) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FilterOrdering. -func (in *FilterOrdering) DeepCopy() *FilterOrdering { - if in == nil { - return nil - } - out := new(FilterOrdering) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GRPCExtAuthService) DeepCopyInto(out *GRPCExtAuthService) { *out = *in diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml index fae1d37e18d..842dd9ce336 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml @@ -103,8 +103,9 @@ spec: x-kubernetes-validations: - message: this policy can only have a targetRef.group of gateway.networking.k8s.io rule: self.group == 'gateway.networking.k8s.io' - - message: this policy can only have a targetRef.kind of Gateway - rule: self.kind in ['Gateway'] + - message: this policy can only have a targetRef.kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute + rule: self.kind in ['Gateway', 'HTTPRoute', 'GRPCRoute', 'UDPRoute', + 'TCPRoute', 'TLSRoute'] - message: this policy does not yet support the sectionName field rule: '!has(self.sectionName)' required: diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index 4d65f3924ed..1a2839f27f7 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -1125,8 +1125,6 @@ _Appears in:_ | `path` | _string_ | true | Path defines the file path used to expose envoy access log(e.g. /dev/stdout). | - - #### GRPCExtAuthService diff --git a/site/content/en/latest/contributions/design/envoy-extension-policy.md b/site/content/en/latest/contributions/design/envoy-extension-policy.md index f118258dc26..b02f9f3beb5 100644 --- a/site/content/en/latest/contributions/design/envoy-extension-policy.md +++ b/site/content/en/latest/contributions/design/envoy-extension-policy.md @@ -7,6 +7,14 @@ title: "EnvoyExtensionPolicy " This design document introduces the `EnvoyExtensionPolicy` API allowing system administrators to configure traffic processing extensibility policies, based on existing Network and HTTP Envoy proxy [extension points][]. +Envoy Gateway already provides two methods of control plane extensibility that can be used to achieve this functionality: +* [Envoy Patch Policy][] can be used to patch Listener filters and HTTP Connection Manager filters. +* [Envoy Extension Manager][] can be used to programmatically mutate Listener filters and HTTP Connection Manager filters. + +These approaches require a high level of Envoy and Envoy Gateway expertise and may create a significant operational +burden for users (see [Alternatives][] for more details). For this reason, this document proposes to support Envoy +data plane extensibility options as first class citizens of Envoy Gateway. + ## Goals * Add an API definition to hold settings for configuring extensibility rules on the traffic entering the gateway. @@ -140,9 +148,18 @@ Here is a list of features that can be included in this API * The project can indefinitely wait for these configuration parameters to be part of the [Gateway API][]. * The project can implement support for HTTP traffic extensions using vendor-specific [Gateway API Route Filters][] instead of policies. However, this option will is less convenient for definition of gateway-level extensions. - +* Users can leverage the existing [Envoy Patch Policy][] to inject extension filters. However, Envoy Gateway strives + to provide a simple abstraction for common use cases and easy operations. Envoy patches require a high level of + end-user Envoy expertise, and knowledge of how Envoy Gateway generates XDS. Such patches may be too difficult + and fragile for some users to maintain. +* Users can leverage the existing [Envoy Extension Manager][] to inject extension filters. However, this requires a + significant investment by users to build and operate an extension manager alongside Envoy Gateway. + [extension points]: https://www.envoyproxy.io/docs/envoy/latest/extending/extending [Policy Attachment]: https://gateway-api.sigs.k8s.io/references/policy-attachment [Gateway API]: https://gateway-api.sigs.k8s.io/ [Gateway API Route Filters]: https://gateway-api.sigs.k8s.io/api-types/httproute/#filters-optional [Envoy Gateway]: ../../api/extension_types/#envoygateway +[Envoy Patch Policy]: ../../api/extension_types/#envoypatchpolicy +[Envoy Extension Manager]: ../extending-envoy-gateway.md +[Alternatives]: #Alternatives diff --git a/test/cel-validation/envoyextensionpolicy_test.go b/test/cel-validation/envoyextensionpolicy_test.go new file mode 100644 index 00000000000..89c7d6fe5b3 --- /dev/null +++ b/test/cel-validation/envoyextensionpolicy_test.go @@ -0,0 +1,186 @@ +// 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. + +//go:build celvalidation +// +build celvalidation + +package celvalidation + +import ( + "context" + "fmt" + "strings" + "testing" + "time" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" +) + +func TestEnvoyExtensionPolicyTarget(t *testing.T) { + ctx := context.Background() + baseBTP := egv1a1.EnvoyExtensionPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "eep", + Namespace: metav1.NamespaceDefault, + }, + Spec: egv1a1.EnvoyExtensionPolicySpec{}, + } + + sectionName := gwapiv1a2.SectionName("foo") + + cases := []struct { + desc string + mutate func(btp *egv1a1.EnvoyExtensionPolicy) + mutateStatus func(btp *egv1a1.EnvoyExtensionPolicy) + wantErrors []string + }{ + { + desc: "valid gateway targetRef", + mutate: func(btp *egv1a1.EnvoyExtensionPolicy) { + btp.Spec = egv1a1.EnvoyExtensionPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "valid httproute targetRef", + mutate: func(btp *egv1a1.EnvoyExtensionPolicy) { + btp.Spec = egv1a1.EnvoyExtensionPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("HTTPRoute"), + Name: gwapiv1a2.ObjectName("httpbin-route"), + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "no targetRef", + mutate: func(btp *egv1a1.EnvoyExtensionPolicy) { + btp.Spec = egv1a1.EnvoyExtensionPolicySpec{} + }, + wantErrors: []string{ + "spec.targetRef.kind: Invalid value: \"\": spec.targetRef.kind in body should be at least 1 chars long", + "spec.targetRef.name: Invalid value: \"\": spec.targetRef.name in body should be at least 1 chars long", + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.group of gateway.networking.k8s.io", + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute", + }, + }, + { + desc: "targetRef unsupported kind", + mutate: func(btp *egv1a1.EnvoyExtensionPolicy) { + btp.Spec = egv1a1.EnvoyExtensionPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("foo"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + } + }, + wantErrors: []string{ + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute", + }, + }, + { + desc: "targetRef unsupported group", + mutate: func(btp *egv1a1.EnvoyExtensionPolicy) { + btp.Spec = egv1a1.EnvoyExtensionPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("foo"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + } + }, + wantErrors: []string{ + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.group of gateway.networking.k8s.io", + }, + }, + { + desc: "targetRef unsupported group and kind", + mutate: func(btp *egv1a1.EnvoyExtensionPolicy) { + btp.Spec = egv1a1.EnvoyExtensionPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("foo"), + Kind: gwapiv1a2.Kind("bar"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + } + }, + wantErrors: []string{ + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.group of gateway.networking.k8s.io", + "spec.targetRef: Invalid value: \"object\": this policy can only have a targetRef.kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute", + }, + }, + { + desc: "sectionName disabled until supported", + mutate: func(btp *egv1a1.EnvoyExtensionPolicy) { + btp.Spec = egv1a1.EnvoyExtensionPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + SectionName: §ionName, + }, + } + }, + wantErrors: []string{ + "spec.targetRef: Invalid value: \"object\": this policy does not yet support the sectionName field", + }, + }, + } + + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + btp := baseBTP.DeepCopy() + btp.Name = fmt.Sprintf("btp-%v", time.Now().UnixNano()) + + if tc.mutate != nil { + tc.mutate(btp) + } + err := c.Create(ctx, btp) + + if tc.mutateStatus != nil { + tc.mutateStatus(btp) + err = c.Status().Update(ctx, btp) + } + + if (len(tc.wantErrors) != 0) != (err != nil) { + t.Fatalf("Unexpected response while creating EnvoyExtensionPolicy; got err=\n%v\n;want error=%v", err, tc.wantErrors) + } + + var missingErrorStrings []string + for _, wantError := range tc.wantErrors { + if !strings.Contains(strings.ToLower(err.Error()), strings.ToLower(wantError)) { + missingErrorStrings = append(missingErrorStrings, wantError) + } + } + if len(missingErrorStrings) != 0 { + t.Errorf("Unexpected response while creating EnvoyExtensionPolicy; got err=\n%v\n;missing strings within error=%q", err, missingErrorStrings) + } + }) + } +} From b0dea04a315b9c045699ee274f0c9474e7a244d2 Mon Sep 17 00:00:00 2001 From: Guy Daich Date: Fri, 15 Mar 2024 15:35:12 -0500 Subject: [PATCH 7/7] fix CEL names Signed-off-by: Guy Daich --- .../envoyextensionpolicy_test.go | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/test/cel-validation/envoyextensionpolicy_test.go b/test/cel-validation/envoyextensionpolicy_test.go index 89c7d6fe5b3..4a179c84ec3 100644 --- a/test/cel-validation/envoyextensionpolicy_test.go +++ b/test/cel-validation/envoyextensionpolicy_test.go @@ -23,7 +23,7 @@ import ( func TestEnvoyExtensionPolicyTarget(t *testing.T) { ctx := context.Background() - baseBTP := egv1a1.EnvoyExtensionPolicy{ + baseeep := egv1a1.EnvoyExtensionPolicy{ ObjectMeta: metav1.ObjectMeta{ Name: "eep", Namespace: metav1.NamespaceDefault, @@ -35,14 +35,14 @@ func TestEnvoyExtensionPolicyTarget(t *testing.T) { cases := []struct { desc string - mutate func(btp *egv1a1.EnvoyExtensionPolicy) - mutateStatus func(btp *egv1a1.EnvoyExtensionPolicy) + mutate func(eep *egv1a1.EnvoyExtensionPolicy) + mutateStatus func(eep *egv1a1.EnvoyExtensionPolicy) wantErrors []string }{ { desc: "valid gateway targetRef", - mutate: func(btp *egv1a1.EnvoyExtensionPolicy) { - btp.Spec = egv1a1.EnvoyExtensionPolicySpec{ + mutate: func(eep *egv1a1.EnvoyExtensionPolicy) { + eep.Spec = egv1a1.EnvoyExtensionPolicySpec{ TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ Group: gwapiv1a2.Group("gateway.networking.k8s.io"), @@ -56,8 +56,8 @@ func TestEnvoyExtensionPolicyTarget(t *testing.T) { }, { desc: "valid httproute targetRef", - mutate: func(btp *egv1a1.EnvoyExtensionPolicy) { - btp.Spec = egv1a1.EnvoyExtensionPolicySpec{ + mutate: func(eep *egv1a1.EnvoyExtensionPolicy) { + eep.Spec = egv1a1.EnvoyExtensionPolicySpec{ TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ Group: gwapiv1a2.Group("gateway.networking.k8s.io"), @@ -71,8 +71,8 @@ func TestEnvoyExtensionPolicyTarget(t *testing.T) { }, { desc: "no targetRef", - mutate: func(btp *egv1a1.EnvoyExtensionPolicy) { - btp.Spec = egv1a1.EnvoyExtensionPolicySpec{} + mutate: func(eep *egv1a1.EnvoyExtensionPolicy) { + eep.Spec = egv1a1.EnvoyExtensionPolicySpec{} }, wantErrors: []string{ "spec.targetRef.kind: Invalid value: \"\": spec.targetRef.kind in body should be at least 1 chars long", @@ -83,8 +83,8 @@ func TestEnvoyExtensionPolicyTarget(t *testing.T) { }, { desc: "targetRef unsupported kind", - mutate: func(btp *egv1a1.EnvoyExtensionPolicy) { - btp.Spec = egv1a1.EnvoyExtensionPolicySpec{ + mutate: func(eep *egv1a1.EnvoyExtensionPolicy) { + eep.Spec = egv1a1.EnvoyExtensionPolicySpec{ TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ Group: gwapiv1a2.Group("gateway.networking.k8s.io"), @@ -100,8 +100,8 @@ func TestEnvoyExtensionPolicyTarget(t *testing.T) { }, { desc: "targetRef unsupported group", - mutate: func(btp *egv1a1.EnvoyExtensionPolicy) { - btp.Spec = egv1a1.EnvoyExtensionPolicySpec{ + mutate: func(eep *egv1a1.EnvoyExtensionPolicy) { + eep.Spec = egv1a1.EnvoyExtensionPolicySpec{ TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ Group: gwapiv1a2.Group("foo"), @@ -117,8 +117,8 @@ func TestEnvoyExtensionPolicyTarget(t *testing.T) { }, { desc: "targetRef unsupported group and kind", - mutate: func(btp *egv1a1.EnvoyExtensionPolicy) { - btp.Spec = egv1a1.EnvoyExtensionPolicySpec{ + mutate: func(eep *egv1a1.EnvoyExtensionPolicy) { + eep.Spec = egv1a1.EnvoyExtensionPolicySpec{ TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ Group: gwapiv1a2.Group("foo"), @@ -135,8 +135,8 @@ func TestEnvoyExtensionPolicyTarget(t *testing.T) { }, { desc: "sectionName disabled until supported", - mutate: func(btp *egv1a1.EnvoyExtensionPolicy) { - btp.Spec = egv1a1.EnvoyExtensionPolicySpec{ + mutate: func(eep *egv1a1.EnvoyExtensionPolicy) { + eep.Spec = egv1a1.EnvoyExtensionPolicySpec{ TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ Group: gwapiv1a2.Group("gateway.networking.k8s.io"), @@ -155,17 +155,17 @@ func TestEnvoyExtensionPolicyTarget(t *testing.T) { for _, tc := range cases { t.Run(tc.desc, func(t *testing.T) { - btp := baseBTP.DeepCopy() - btp.Name = fmt.Sprintf("btp-%v", time.Now().UnixNano()) + eep := baseeep.DeepCopy() + eep.Name = fmt.Sprintf("eep-%v", time.Now().UnixNano()) if tc.mutate != nil { - tc.mutate(btp) + tc.mutate(eep) } - err := c.Create(ctx, btp) + err := c.Create(ctx, eep) if tc.mutateStatus != nil { - tc.mutateStatus(btp) - err = c.Status().Update(ctx, btp) + tc.mutateStatus(eep) + err = c.Status().Update(ctx, eep) } if (len(tc.wantErrors) != 0) != (err != nil) {