From ad54e06ea58a5bcef27d1dfc3d4ed734b9746b0c Mon Sep 17 00:00:00 2001 From: Guy Daich Date: Tue, 16 Apr 2024 01:17:45 -0500 Subject: [PATCH] api: ext-proc timeout, fail-open, backendRefs (#3087) * extend extproc api Signed-off-by: Guy Daich * fix metadata options Signed-off-by: Guy Daich * rm attributes, metadata and processing mode Signed-off-by: Guy Daich * rm enums Signed-off-by: Guy Daich * rm unrealted CEL Signed-off-by: Guy Daich * sort imports, add optional plural backendRefs Signed-off-by: Guy Daich * fix gen Signed-off-by: Guy Daich --------- Signed-off-by: Guy Daich Co-authored-by: zirain --- api/v1alpha1/ext_proc_types.go | 20 +++- api/v1alpha1/zz_generated.deepcopy.go | 17 ++++ ....envoyproxy.io_envoyextensionpolicies.yaml | 95 ++++++++++++++++++- site/content/en/latest/api/extension_types.md | 6 +- .../envoyextensionpolicy_test.go | 82 ++++++++++++++++ 5 files changed, 217 insertions(+), 3 deletions(-) diff --git a/api/v1alpha1/ext_proc_types.go b/api/v1alpha1/ext_proc_types.go index c94682198b4..7499b9f8e4f 100644 --- a/api/v1alpha1/ext_proc_types.go +++ b/api/v1alpha1/ext_proc_types.go @@ -14,8 +14,26 @@ import ( // // ExtProc defines the configuration for External Processing filter. type ExtProc struct { - // Service defines the configuration of the external processing service + // BackendRef defines the configuration of the external processing service BackendRef ExtProcBackendRef `json:"backendRef"` + + // BackendRefs defines the configuration of the external processing service + // + // +optional + BackendRefs []BackendRef `json:"backendRefs,omitempty"` + + // MessageTimeout is the timeout for a response to be returned from the external processor + // Default: 200ms + // + // +optional + MessageTimeout *gwapiv1.Duration `json:"messageTimeout,omitempty"` + + // FailOpen defines if requests or responses that cannot be processed due to connectivity to the + // external processor are terminated or passed-through. + // Default: false + // + // +optional + FailOpen *bool `json:"failOpen,omitempty"` } // ExtProcService defines the gRPC External Processing service using the envoy grpc client diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 1b0a97fa77a..0055d7669f1 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -1573,6 +1573,23 @@ func (in *ExtAuth) DeepCopy() *ExtAuth { func (in *ExtProc) DeepCopyInto(out *ExtProc) { *out = *in in.BackendRef.DeepCopyInto(&out.BackendRef) + if in.BackendRefs != nil { + in, out := &in.BackendRefs, &out.BackendRefs + *out = make([]BackendRef, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.MessageTimeout != nil { + in, out := &in.MessageTimeout, &out.MessageTimeout + *out = new(v1.Duration) + **out = **in + } + if in.FailOpen != nil { + in, out := &in.FailOpen, &out.FailOpen + *out = new(bool) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtProc. 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 37becdd417f..4861dd1fcc0 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml @@ -58,7 +58,7 @@ spec: filter. properties: backendRef: - description: Service defines the configuration of the external + description: BackendRef defines the configuration of the external processing service properties: group: @@ -134,6 +134,99 @@ spec: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' + backendRefs: + description: BackendRefs defines the configuration of the external + processing service + items: + description: BackendRef defines how an ObjectReference that + is specific to BackendRef. + 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". + + + Defaults to "Service" when not specified. + + + 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. + + + Support: Core (Services with a type other than ExternalName) + + + 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. + + + 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. + + + 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' + type: array + failOpen: + description: |- + FailOpen defines if requests or responses that cannot be processed due to connectivity to the + external processor are terminated or passed-through. + Default: false + type: boolean + messageTimeout: + description: |- + MessageTimeout is 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 required: - backendRef type: object diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index 7c9b355fefb..a9847ac0645 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -192,6 +192,7 @@ BackendRef defines how an ObjectReference that is specific to BackendRef. _Appears in:_ - [ALSEnvoyProxyAccessLog](#alsenvoyproxyaccesslog) +- [ExtProc](#extproc) - [OpenTelemetryEnvoyProxyAccessLog](#opentelemetryenvoyproxyaccesslog) - [ProxyOpenTelemetrySink](#proxyopentelemetrysink) - [TracingProvider](#tracingprovider) @@ -1137,7 +1138,10 @@ _Appears in:_ | Field | Type | Required | Description | | --- | --- | --- | --- | -| `backendRef` | _[ExtProcBackendRef](#extprocbackendref)_ | true | Service defines the configuration of the external processing service | +| `backendRef` | _[ExtProcBackendRef](#extprocbackendref)_ | true | BackendRef defines the configuration of the external processing service | +| `backendRefs` | _[BackendRef](#backendref) array_ | false | BackendRefs defines the configuration of the external processing service | +| `messageTimeout` | _[Duration](#duration)_ | false | MessageTimeout is the timeout for a response to be returned from the external processor
Default: 200ms | +| `failOpen` | _boolean_ | false | FailOpen defines if requests or responses that cannot be processed due to connectivity to the
external processor are terminated or passed-through.
Default: false | #### ExtProcBackendRef diff --git a/test/cel-validation/envoyextensionpolicy_test.go b/test/cel-validation/envoyextensionpolicy_test.go index 4a179c84ec3..2283212cd5c 100644 --- a/test/cel-validation/envoyextensionpolicy_test.go +++ b/test/cel-validation/envoyextensionpolicy_test.go @@ -15,9 +15,12 @@ import ( "testing" "time" + "k8s.io/utils/ptr" + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" ) @@ -151,6 +154,85 @@ func TestEnvoyExtensionPolicyTarget(t *testing.T) { "spec.targetRef: Invalid value: \"object\": this policy does not yet support the sectionName field", }, }, + + // ExtProc + { + desc: "ExtProc with BackendRef", + mutate: func(sp *egv1a1.EnvoyExtensionPolicy) { + sp.Spec = egv1a1.EnvoyExtensionPolicySpec{ + ExtProc: []egv1a1.ExtProc{ + { + BackendRef: egv1a1.ExtProcBackendRef{ + BackendObjectReference: gwapiv1.BackendObjectReference{ + Name: "grpc-proc-service", + Port: ptr.To(gwapiv1.PortNumber(80)), + }, + }, + }, + }, + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: "gateway.networking.k8s.io", + Kind: "Gateway", + Name: "eg", + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "ExtProc with invalid BackendRef Group", + mutate: func(sp *egv1a1.EnvoyExtensionPolicy) { + sp.Spec = egv1a1.EnvoyExtensionPolicySpec{ + ExtProc: []egv1a1.ExtProc{ + { + BackendRef: egv1a1.ExtProcBackendRef{ + BackendObjectReference: gwapiv1.BackendObjectReference{ + Group: ptr.To(gwapiv1.Group("unsupported")), + Name: "grpc-proc-service", + Port: ptr.To(gwapiv1.PortNumber(80)), + }, + }, + }, + }, + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: "gateway.networking.k8s.io", + Kind: "Gateway", + Name: "eg", + }, + }, + } + }, + wantErrors: []string{"spec.extProc[0]: Invalid value: \"object\": group is invalid, only the core API group (specified by omitting the group field or setting it to an empty string) is supported"}, + }, + { + desc: "ExtProc with invalid BackendRef Kind", + mutate: func(sp *egv1a1.EnvoyExtensionPolicy) { + sp.Spec = egv1a1.EnvoyExtensionPolicySpec{ + ExtProc: []egv1a1.ExtProc{ + { + BackendRef: egv1a1.ExtProcBackendRef{ + BackendObjectReference: gwapiv1.BackendObjectReference{ + Kind: ptr.To(gwapiv1.Kind("unsupported")), + Name: "grpc-proc-service", + Port: ptr.To(gwapiv1.PortNumber(80)), + }, + }, + }, + }, + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: "gateway.networking.k8s.io", + Kind: "Gateway", + Name: "eg", + }, + }, + } + }, + wantErrors: []string{"spec.extProc[0]: Invalid value: \"object\": kind is invalid, only Service (specified by omitting the kind field or setting it to 'Service') is supported"}, + }, } for _, tc := range cases {