From 09df0af21baa22062fa51d1b1d80800f512063e1 Mon Sep 17 00:00:00 2001 From: Rudrakh Panigrahi Date: Wed, 18 Dec 2024 07:44:10 +0530 Subject: [PATCH] api: lua support in EnvoyExtensionPolicy Signed-off-by: Rudrakh Panigrahi --- api/v1alpha1/envoyextensionypolicy_types.go | 15 +- api/v1alpha1/lua_types.go | 48 +++++++ api/v1alpha1/zz_generated.deepcopy.go | 37 +++++ ....envoyproxy.io_envoyextensionpolicies.yaml | 69 ++++++++- site/content/en/latest/api/extension_types.md | 34 ++++- site/content/zh/latest/api/extension_types.md | 34 ++++- .../envoyextensionpolicy_test.go | 134 ++++++++++++++++++ 7 files changed, 364 insertions(+), 7 deletions(-) create mode 100644 api/v1alpha1/lua_types.go diff --git a/api/v1alpha1/envoyextensionypolicy_types.go b/api/v1alpha1/envoyextensionypolicy_types.go index cbab194b24e3..b4fc751b80d1 100644 --- a/api/v1alpha1/envoyextensionypolicy_types.go +++ b/api/v1alpha1/envoyextensionypolicy_types.go @@ -32,16 +32,15 @@ type EnvoyExtensionPolicy struct { Status gwapiv1a2.PolicyStatus `json:"status,omitempty"` } -// +kubebuilder:validation:XValidation:rule="(has(self.targetRef) && !has(self.targetRefs)) || (!has(self.targetRef) && has(self.targetRefs)) || (has(self.targetSelectors) && self.targetSelectors.size() > 0) ", message="either targetRef or targetRefs must be used" +// EnvoyExtensionPolicySpec defines the desired state of EnvoyExtensionPolicy. // +// +kubebuilder:validation:XValidation:rule="(has(self.targetRef) && !has(self.targetRefs)) || (!has(self.targetRef) && has(self.targetRefs)) || (has(self.targetSelectors) && self.targetSelectors.size() > 0) ", message="either targetRef or targetRefs must be used" // +kubebuilder:validation:XValidation:rule="has(self.targetRef) ? self.targetRef.group == 'gateway.networking.k8s.io' : true", message="this policy can only have a targetRef.group of gateway.networking.k8s.io" // +kubebuilder:validation:XValidation:rule="has(self.targetRef) ? self.targetRef.kind in ['Gateway', 'HTTPRoute', 'GRPCRoute', 'UDPRoute', 'TCPRoute', 'TLSRoute'] : true", message="this policy can only have a targetRef.kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute" // +kubebuilder:validation:XValidation:rule="has(self.targetRef) ? !has(self.targetRef.sectionName) : true",message="this policy does not yet support the sectionName field" // +kubebuilder:validation:XValidation:rule="has(self.targetRefs) ? self.targetRefs.all(ref, ref.group == 'gateway.networking.k8s.io') : true ", message="this policy can only have a targetRefs[*].group of gateway.networking.k8s.io" // +kubebuilder:validation:XValidation:rule="has(self.targetRefs) ? self.targetRefs.all(ref, ref.kind in ['Gateway', 'HTTPRoute', 'GRPCRoute', 'UDPRoute', 'TCPRoute', 'TLSRoute']) : true ", message="this policy can only have a targetRefs[*].kind of Gateway/HTTPRoute/GRPCRoute/TCPRoute/UDPRoute/TLSRoute" // +kubebuilder:validation:XValidation:rule="has(self.targetRefs) ? self.targetRefs.all(ref, !has(ref.sectionName)) : true",message="this policy does not yet support the sectionName field" -// -// EnvoyExtensionPolicySpec defines the desired state of EnvoyExtensionPolicy. type EnvoyExtensionPolicySpec struct { PolicyTargetReferences `json:",inline"` @@ -54,11 +53,19 @@ type EnvoyExtensionPolicySpec struct { Wasm []Wasm `json:"wasm,omitempty"` // ExtProc is an ordered list of external processing filters - // that should added to the envoy filter chain + // that should be added to the envoy filter chain // // +kubebuilder:validation:MaxItems=16 // +optional ExtProc []ExtProc `json:"extProc,omitempty"` + + // Lua is an ordered list of Lua filters + // that should be added to the envoy filter chain + // + // +kubebuilder:validation:MaxItems=16 + // +optional + // +notImplementedHide + Lua []Lua `json:"lua,omitempty"` } //+kubebuilder:object:root=true diff --git a/api/v1alpha1/lua_types.go b/api/v1alpha1/lua_types.go new file mode 100644 index 000000000000..d8ab4c0dff3e --- /dev/null +++ b/api/v1alpha1/lua_types.go @@ -0,0 +1,48 @@ +// 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" + +// LuaValueType defines the types of values for Lua supported by Envoy Gateway. +// +kubebuilder:validation:Enum=Inline;ValueRef +type LuaValueType string + +const ( + // LuaValueTypeInline defines the "Inline" Lua type. + LuaValueTypeInline LuaValueType = "Inline" + + // LuaValueTypeValueRef defines the "ValueRef" Lua type. + LuaValueTypeValueRef LuaValueType = "ValueRef" +) + +// Lua defines a Lua extension +// Only one of Inline or ValueRef must be set +// +// +kubebuilder:validation:XValidation:rule="(self.type == 'Inline' && has(self.inline) && !has(self.valueRef)) || (self.type == 'ValueRef' && !has(self.inline) && has(self.valueRef))",message="Exactly one of inline or valueRef must be set with correct type." +type Lua struct { + // Type is the type of method to use to read the Lua value. + // Valid values are Inline and ValueRef, default is Inline. + // + // +kubebuilder:default=Inline + // +unionDiscriminator + // +required + Type *LuaValueType `json:"type,omitempty"` + // Inline contains the source code as an inline string. + // + // +optional + // +unionMember + Inline *string `json:"inline,omitempty"` + // ValueRef has the source code specified as a local object reference. + // Only a reference to ConfigMap is supported. + // The value of key `lua` in the ConfigMap will be used. + // If the key is not found, the first value in the ConfigMap will be used. + // + // +kubebuilder:validation:XValidation:rule="self.kind == 'ConfigMap' && (!has(self.group) || self.group == '')",message="Only a reference to an object of kind ConfigMap belonging to default core API group is supported." + // +optional + // +unionMember + ValueRef *gwapiv1.LocalObjectReference `json:"valueRef,omitempty"` +} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index aa6e5c652abe..977ca92be2f3 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -1380,6 +1380,13 @@ func (in *EnvoyExtensionPolicySpec) DeepCopyInto(out *EnvoyExtensionPolicySpec) (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.Lua != nil { + in, out := &in.Lua, &out.Lua + *out = make([]Lua, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyExtensionPolicySpec. @@ -3856,6 +3863,36 @@ 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 *Lua) DeepCopyInto(out *Lua) { + *out = *in + if in.Type != nil { + in, out := &in.Type, &out.Type + *out = new(LuaValueType) + **out = **in + } + if in.Inline != nil { + in, out := &in.Inline, &out.Inline + *out = new(string) + **out = **in + } + if in.ValueRef != nil { + in, out := &in.ValueRef, &out.ValueRef + *out = new(apisv1.LocalObjectReference) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Lua. +func (in *Lua) DeepCopy() *Lua { + if in == nil { + return nil + } + out := new(Lua) + 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 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 0fbbcafe94e8..abdbc0a1c4e2 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml @@ -49,7 +49,7 @@ spec: extProc: description: |- ExtProc is an ordered list of external processing filters - that should added to the envoy filter chain + that should be added to the envoy filter chain items: description: ExtProc defines the configuration for External Processing filter. @@ -973,6 +973,73 @@ spec: == "" || f.group == ''gateway.envoyproxy.io'')) : true' maxItems: 16 type: array + lua: + description: |- + Lua is an ordered list of Lua filters + that should be added to the envoy filter chain + items: + description: |- + Lua defines a Lua extension + Only one of Inline or ValueRef must be set + properties: + inline: + description: Inline contains the source code as an inline string. + type: string + type: + default: Inline + description: |- + Type is the type of method to use to read the Lua value. + Valid values are Inline and ValueRef, default is Inline. + enum: + - Inline + - ValueRef + type: string + valueRef: + description: |- + ValueRef has the source code specified as a local object reference. + Only a reference to ConfigMap is supported. + The value of key `lua` in the ConfigMap will be used. + If the key is not found, the first value in the ConfigMap will be used. + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For example "HTTPRoute" + or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + x-kubernetes-validations: + - message: Only a reference to an object of kind ConfigMap belonging + to default core API group is supported. + rule: self.kind == 'ConfigMap' && (!has(self.group) || self.group + == '') + required: + - type + type: object + x-kubernetes-validations: + - message: Exactly one of inline or valueRef must be set with correct + type. + rule: (self.type == 'Inline' && has(self.inline) && !has(self.valueRef)) + || (self.type == 'ValueRef' && !has(self.inline) && has(self.valueRef)) + maxItems: 16 + type: array targetRef: description: |- TargetRef is the name of the resource this policy is being attached to. diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index e57febb00c4e..5d440e740e39 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -980,7 +980,7 @@ _Appears in:_ | `targetRefs` | _[LocalPolicyTargetReferenceWithSectionName](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1alpha2.LocalPolicyTargetReferenceWithSectionName) array_ | true | TargetRefs are the names of the Gateway resources this policy
is being attached to. | | `targetSelectors` | _[TargetSelector](#targetselector) array_ | true | TargetSelectors allow targeting resources for this policy based on labels | | `wasm` | _[Wasm](#wasm) array_ | false | Wasm is a list of Wasm extensions to be loaded by the Gateway.
Order matters, as the extensions will be loaded in the order they are
defined in this list. | -| `extProc` | _[ExtProc](#extproc) array_ | false | ExtProc is an ordered list of external processing filters
that should added to the envoy filter chain | +| `extProc` | _[ExtProc](#extproc) array_ | false | ExtProc is an ordered list of external processing filters
that should be added to the envoy filter chain | #### EnvoyFilter @@ -2755,6 +2755,38 @@ _Appears in:_ | `error` | LogLevelError defines the "Error" logging level.
| +#### Lua + + + +Lua defines a Lua extension +Only one of Inline or ValueRef must be set + +_Appears in:_ +- [EnvoyExtensionPolicySpec](#envoyextensionpolicyspec) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `type` | _[LuaValueType](#luavaluetype)_ | true | Type is the type of method to use to read the Lua value.
Valid values are Inline and ValueRef, default is Inline. | +| `inline` | _string_ | false | Inline contains the source code as an inline string. | +| `valueRef` | _[LocalObjectReference](#localobjectreference)_ | false | ValueRef has the source code specified as a local object reference.
Only a reference to ConfigMap is supported.
The value of key `lua` in the ConfigMap will be used.
If the key is not found, the first value in the ConfigMap will be used. | + + +#### LuaValueType + +_Underlying type:_ _string_ + +LuaValueType defines the types of values for Lua supported by Envoy Gateway. + +_Appears in:_ +- [Lua](#lua) + +| Value | Description | +| ----- | ----------- | +| `Inline` | LuaValueTypeInline defines the "Inline" Lua type.
| +| `ValueRef` | LuaValueTypeValueRef defines the "ValueRef" Lua type.
| + + #### MergeType _Underlying type:_ _string_ diff --git a/site/content/zh/latest/api/extension_types.md b/site/content/zh/latest/api/extension_types.md index e57febb00c4e..5d440e740e39 100644 --- a/site/content/zh/latest/api/extension_types.md +++ b/site/content/zh/latest/api/extension_types.md @@ -980,7 +980,7 @@ _Appears in:_ | `targetRefs` | _[LocalPolicyTargetReferenceWithSectionName](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1alpha2.LocalPolicyTargetReferenceWithSectionName) array_ | true | TargetRefs are the names of the Gateway resources this policy
is being attached to. | | `targetSelectors` | _[TargetSelector](#targetselector) array_ | true | TargetSelectors allow targeting resources for this policy based on labels | | `wasm` | _[Wasm](#wasm) array_ | false | Wasm is a list of Wasm extensions to be loaded by the Gateway.
Order matters, as the extensions will be loaded in the order they are
defined in this list. | -| `extProc` | _[ExtProc](#extproc) array_ | false | ExtProc is an ordered list of external processing filters
that should added to the envoy filter chain | +| `extProc` | _[ExtProc](#extproc) array_ | false | ExtProc is an ordered list of external processing filters
that should be added to the envoy filter chain | #### EnvoyFilter @@ -2755,6 +2755,38 @@ _Appears in:_ | `error` | LogLevelError defines the "Error" logging level.
| +#### Lua + + + +Lua defines a Lua extension +Only one of Inline or ValueRef must be set + +_Appears in:_ +- [EnvoyExtensionPolicySpec](#envoyextensionpolicyspec) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `type` | _[LuaValueType](#luavaluetype)_ | true | Type is the type of method to use to read the Lua value.
Valid values are Inline and ValueRef, default is Inline. | +| `inline` | _string_ | false | Inline contains the source code as an inline string. | +| `valueRef` | _[LocalObjectReference](#localobjectreference)_ | false | ValueRef has the source code specified as a local object reference.
Only a reference to ConfigMap is supported.
The value of key `lua` in the ConfigMap will be used.
If the key is not found, the first value in the ConfigMap will be used. | + + +#### LuaValueType + +_Underlying type:_ _string_ + +LuaValueType defines the types of values for Lua supported by Envoy Gateway. + +_Appears in:_ +- [Lua](#lua) + +| Value | Description | +| ----- | ----------- | +| `Inline` | LuaValueTypeInline defines the "Inline" Lua type.
| +| `ValueRef` | LuaValueTypeValueRef defines the "ValueRef" Lua type.
| + + #### MergeType _Underlying type:_ _string_ diff --git a/test/cel-validation/envoyextensionpolicy_test.go b/test/cel-validation/envoyextensionpolicy_test.go index a1d435e55dc8..6d0f7148de97 100644 --- a/test/cel-validation/envoyextensionpolicy_test.go +++ b/test/cel-validation/envoyextensionpolicy_test.go @@ -414,6 +414,140 @@ func TestEnvoyExtensionPolicyTarget(t *testing.T) { "spec.extProc[0].processingMode.request.body: Unsupported value: \"not-a-body-mode\": supported values: \"Streamed\", \"Buffered\", \"BufferedPartial\"", }, }, + { + desc: "Valid Lua filter (inline)", + mutate: func(sp *egv1a1.EnvoyExtensionPolicy) { + sp.Spec = egv1a1.EnvoyExtensionPolicySpec{ + Lua: []egv1a1.Lua{ + { + Type: ptr.To(egv1a1.LuaValueTypeInline), + Inline: ptr.To("function envoy_on_response(response_handle) -- Do something -- end"), + }, + }, + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetRef: &gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: "gateway.networking.k8s.io", + Kind: "Gateway", + Name: "eg", + }, + }, + }, + } + }, + wantErrors: nil, + }, + { + desc: "Valid Lua filter (source configmap)", + mutate: func(sp *egv1a1.EnvoyExtensionPolicy) { + sp.Spec = egv1a1.EnvoyExtensionPolicySpec{ + Lua: []egv1a1.Lua{ + { + Type: ptr.To(egv1a1.LuaValueTypeValueRef), + ValueRef: &gwapiv1a2.LocalObjectReference{ + Kind: gwapiv1a2.Kind("ConfigMap"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + }, + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetRef: &gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: "gateway.networking.k8s.io", + Kind: "Gateway", + Name: "eg", + }, + }, + }, + } + }, + wantErrors: nil, + }, + { + desc: "Invalid Lua filter (type inline but source configmap)", + mutate: func(sp *egv1a1.EnvoyExtensionPolicy) { + sp.Spec = egv1a1.EnvoyExtensionPolicySpec{ + Lua: []egv1a1.Lua{ + { + Type: ptr.To(egv1a1.LuaValueTypeInline), + ValueRef: &gwapiv1a2.LocalObjectReference{ + Kind: gwapiv1a2.Kind("ConfigMap"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + }, + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetRef: &gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: "gateway.networking.k8s.io", + Kind: "Gateway", + Name: "eg", + }, + }, + }, + } + }, + wantErrors: []string{ + "spec.lua[0]: Invalid value: \"object\": Exactly one of inline or valueRef must be set with correct type.", + }, + }, + { + desc: "Invalid Lua filter (source object kind not configmap)", + mutate: func(sp *egv1a1.EnvoyExtensionPolicy) { + sp.Spec = egv1a1.EnvoyExtensionPolicySpec{ + Lua: []egv1a1.Lua{ + { + Type: ptr.To(egv1a1.LuaValueTypeValueRef), + ValueRef: &gwapiv1a2.LocalObjectReference{ + Kind: gwapiv1a2.Kind("NotConfigMap"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + }, + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetRef: &gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: "gateway.networking.k8s.io", + Kind: "Gateway", + Name: "eg", + }, + }, + }, + } + }, + wantErrors: []string{ + "spec.lua[0].valueRef: Invalid value: \"object\": Only a reference to an object of kind ConfigMap belonging to default core API group is supported.", + }, + }, + { + desc: "Invalid Lua filter (source both inline and configmap)", + mutate: func(sp *egv1a1.EnvoyExtensionPolicy) { + sp.Spec = egv1a1.EnvoyExtensionPolicySpec{ + Lua: []egv1a1.Lua{ + { + Type: ptr.To(egv1a1.LuaValueTypeInline), + Inline: ptr.To("function envoy_on_response(response_handle) -- Do something -- end"), + ValueRef: &gwapiv1a2.LocalObjectReference{ + Kind: gwapiv1a2.Kind("ConfigMap"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + }, + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetRef: &gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: "gateway.networking.k8s.io", + Kind: "Gateway", + Name: "eg", + }, + }, + }, + } + }, + wantErrors: []string{ + "spec.lua[0]: Invalid value: \"object\": Exactly one of inline or valueRef must be set with correct type.", + }, + }, { desc: "target selectors without targetRefs or targetRef", mutate: func(sp *egv1a1.EnvoyExtensionPolicy) {