From e838fc620e210d06712baf86d73592ff3feb8aef 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 and HTTPRouteFilter Signed-off-by: Rudrakh Panigrahi --- api/v1alpha1/envoyextensionypolicy_types.go | 15 +- api/v1alpha1/lua_types.go | 41 +++++ api/v1alpha1/zz_generated.deepcopy.go | 57 +++++++ ....envoyproxy.io_envoyextensionpolicies.yaml | 64 ++++++- site/content/en/latest/api/extension_types.md | 34 +++- site/content/zh/latest/api/extension_types.md | 34 +++- .../envoyextensionpolicy_test.go | 156 ++++++++++++++++++ 7 files changed, 394 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 cbab194b24e..b4fc751b80d 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 00000000000..84202fb5df5 --- /dev/null +++ b/api/v1alpha1/lua_types.go @@ -0,0 +1,41 @@ +// 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" + +// Lua defines a Lua extension +// Only one of Source or Disabled must be set +// +// +kubebuilder:validation:XValidation:rule="has(self.source) ? (!has(self.disabled) || self.disabled == false) : (has(self.disabled) && self.disabled == true)",message="Exactly one of source or disabled must be set." +type Lua struct { + // Source is the LuaSource that will be executed for this extension. + // + // +optional + Source *LuaSource `json:"source"` + // Disabled is the status of the extension. + // Used for disabling Lua extensions at a route level. + // + // +optional + Disabled *bool `json:"disabled,omitempty"` +} + +// LuaSource contains source code information for a user defined Lua script +// Only one of Inline or ValueRef must be set +// +// +kubebuilder:validation:XValidation:rule="has(self.inline) ? !has(self.valueRef) : has(self.valueRef)",message="Exactly one of inline or valueRef must be set." +type LuaSource struct { + // Inline contains the value as an inline string. + // + // +optional + Inline *string `json:"inline,omitempty"` + // ValueRef contains the contents of the body + // specified as a local object reference. + // Only a reference to ConfigMap is supported. + // + // +optional + ValueRef *gwapiv1.LocalObjectReference `json:"valueRef,omitempty"` +} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index dbc28e6aca2..fc7585e07e5 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. @@ -3841,6 +3848,56 @@ 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.Source != nil { + in, out := &in.Source, &out.Source + *out = new(LuaSource) + (*in).DeepCopyInto(*out) + } + if in.Disabled != nil { + in, out := &in.Disabled, &out.Disabled + *out = new(bool) + **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 *LuaSource) DeepCopyInto(out *LuaSource) { + *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 LuaSource. +func (in *LuaSource) DeepCopy() *LuaSource { + if in == nil { + return nil + } + out := new(LuaSource) + 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 0fbbcafe94e..3d369b021a4 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,68 @@ 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 Source or Disabled must be set + properties: + disabled: + description: |- + Disabled is the status of the extension. + Used for disabling Lua extensions at a route level. + type: boolean + source: + description: Source is the LuaSource that will be executed for + this extension. + properties: + inline: + description: Inline contains the value as an inline string. + type: string + valueRef: + description: |- + ValueRef contains the contents of the body + specified as a local object reference. + Only a reference to ConfigMap is supported. + 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 + type: object + x-kubernetes-validations: + - message: Exactly one of inline or valueRef must be set. + rule: 'has(self.inline) ? !has(self.valueRef) : has(self.valueRef)' + type: object + x-kubernetes-validations: + - message: Exactly one of source or disabled must be set. + rule: 'has(self.source) ? (!has(self.disabled) || self.disabled + == false) : (has(self.disabled) && self.disabled == true)' + 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 5119d756646..a373396205b 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 @@ -2738,6 +2738,38 @@ _Appears in:_ | `error` | LogLevelError defines the "Error" logging level.
| +#### Lua + + + +Lua defines a Lua extension +Only one of Source or Disabled must be set + +_Appears in:_ +- [EnvoyExtensionPolicySpec](#envoyextensionpolicyspec) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `source` | _[LuaSource](#luasource)_ | false | Source is the LuaSource that will be executed for this extension. | +| `disabled` | _boolean_ | false | Disabled is the status of the extension.
Used for disabling Lua extensions at a route level. | + + +#### LuaSource + + + +LuaSource contains source code information for a user defined Lua script +Only one of Inline or ValueRef must be set + +_Appears in:_ +- [Lua](#lua) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `inline` | _string_ | false | Inline contains the value as an inline string. | +| `valueRef` | _[LocalObjectReference](#localobjectreference)_ | false | ValueRef contains the contents of the body
specified as a local object reference.
Only a reference to ConfigMap is supported. | + + #### 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 5119d756646..a373396205b 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 @@ -2738,6 +2738,38 @@ _Appears in:_ | `error` | LogLevelError defines the "Error" logging level.
| +#### Lua + + + +Lua defines a Lua extension +Only one of Source or Disabled must be set + +_Appears in:_ +- [EnvoyExtensionPolicySpec](#envoyextensionpolicyspec) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `source` | _[LuaSource](#luasource)_ | false | Source is the LuaSource that will be executed for this extension. | +| `disabled` | _boolean_ | false | Disabled is the status of the extension.
Used for disabling Lua extensions at a route level. | + + +#### LuaSource + + + +LuaSource contains source code information for a user defined Lua script +Only one of Inline or ValueRef must be set + +_Appears in:_ +- [Lua](#lua) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `inline` | _string_ | false | Inline contains the value as an inline string. | +| `valueRef` | _[LocalObjectReference](#localobjectreference)_ | false | ValueRef contains the contents of the body
specified as a local object reference.
Only a reference to ConfigMap is supported. | + + #### MergeType _Underlying type:_ _string_ diff --git a/test/cel-validation/envoyextensionpolicy_test.go b/test/cel-validation/envoyextensionpolicy_test.go index a1d435e55dc..c03b587dbda 100644 --- a/test/cel-validation/envoyextensionpolicy_test.go +++ b/test/cel-validation/envoyextensionpolicy_test.go @@ -414,6 +414,162 @@ 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{ + { + Source: &egv1a1.LuaSource{ + 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 (configmap)", + mutate: func(sp *egv1a1.EnvoyExtensionPolicy) { + sp.Spec = egv1a1.EnvoyExtensionPolicySpec{ + Lua: []egv1a1.Lua{ + { + Source: &egv1a1.LuaSource{ + ValueRef: &gwapiv1a2.LocalObjectReference{ + Kind: gwapiv1a2.Kind("ConfigMap"), + Name: gwapiv1a2.ObjectName("eg"), + Group: gwapiv1a2.Group("core"), + }, + }, + }, + }, + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetRef: &gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: "gateway.networking.k8s.io", + Kind: "Gateway", + Name: "eg", + }, + }, + }, + } + }, + wantErrors: nil, + }, + { + desc: "Invalid Lua filter (both inline and configmap)", + mutate: func(sp *egv1a1.EnvoyExtensionPolicy) { + sp.Spec = egv1a1.EnvoyExtensionPolicySpec{ + Lua: []egv1a1.Lua{ + { + Source: &egv1a1.LuaSource{ + Inline: ptr.To("function envoy_on_response(response_handle) -- Do something -- end"), + ValueRef: &gwapiv1a2.LocalObjectReference{ + Kind: gwapiv1a2.Kind("ConfigMap"), + Name: gwapiv1a2.ObjectName("eg"), + Group: gwapiv1a2.Group("core"), + }, + }, + }, + }, + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetRef: &gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: "gateway.networking.k8s.io", + Kind: "Gateway", + Name: "eg", + }, + }, + }, + } + }, + wantErrors: []string{ + "spec.lua[0].source: Invalid value: \"object\": Exactly one of inline or valueRef must be set.", + }, + }, + { + desc: "Valid Lua filter for route (disabled)", + mutate: func(sp *egv1a1.EnvoyExtensionPolicy) { + sp.Spec = egv1a1.EnvoyExtensionPolicySpec{ + Lua: []egv1a1.Lua{ + { + Disabled: ptr.To(true), + }, + }, + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetRef: &gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: "gateway.networking.k8s.io", + Kind: "HTTPRoute", + Name: "hr", + }, + }, + }, + } + }, + wantErrors: nil, + }, + { + desc: "Invalid Lua filter for route (disabled false)", + mutate: func(sp *egv1a1.EnvoyExtensionPolicy) { + sp.Spec = egv1a1.EnvoyExtensionPolicySpec{ + Lua: []egv1a1.Lua{ + { + Disabled: ptr.To(false), + }, + }, + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetRef: &gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: "gateway.networking.k8s.io", + Kind: "HTTPRoute", + Name: "hr", + }, + }, + }, + } + }, + wantErrors: []string{ + "spec.lua[0]: Invalid value: \"object\": Exactly one of source or disabled must be set.", + }, + }, + { + desc: "Invalid Lua filter for route", + mutate: func(sp *egv1a1.EnvoyExtensionPolicy) { + sp.Spec = egv1a1.EnvoyExtensionPolicySpec{ + Lua: []egv1a1.Lua{ + { + Source: &egv1a1.LuaSource{ + Inline: ptr.To("function envoy_on_response(response_handle) -- Do something -- end"), + }, + Disabled: ptr.To(true), + }, + }, + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetRef: &gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: "gateway.networking.k8s.io", + Kind: "HTTPRoute", + Name: "hr", + }, + }, + }, + } + }, + wantErrors: []string{ + "spec.lua[0]: Invalid value: \"object\": Exactly one of source or disabled must be set.", + }, + }, { desc: "target selectors without targetRefs or targetRef", mutate: func(sp *egv1a1.EnvoyExtensionPolicy) {