From c2646f7a9db6656903e89b5edb70448fc88670a3 Mon Sep 17 00:00:00 2001 From: Khaled Emara Date: Mon, 29 Jul 2024 14:57:20 +0300 Subject: [PATCH] feat(json): reduce reliance on `DocumentToUntyped()` (#10724) Signed-off-by: Khaled Emara Co-authored-by: Mariam Fahmy --- api/kyverno/v1/common_types.go | 25 +++++++++++++++++-- api/kyverno/v1/zz_generated.deepcopy.go | 6 ++--- docs/user/crd/index.html | 8 ++---- docs/user/crd/kyverno.v1.html | 4 +-- .../kyverno/v1/variable.go | 12 ++++----- pkg/engine/context/loaders/variable.go | 12 ++++----- pkg/engine/jsonutils/convert.go | 5 ++++ pkg/validation/policy/validate.go | 4 +-- 8 files changed, 48 insertions(+), 28 deletions(-) diff --git a/api/kyverno/v1/common_types.go b/api/kyverno/v1/common_types.go index cee5da6d71..15b4a2662e 100644 --- a/api/kyverno/v1/common_types.go +++ b/api/kyverno/v1/common_types.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" + "github.com/kyverno/kyverno/api/kyverno" "github.com/kyverno/kyverno/pkg/engine/variables/regex" "github.com/kyverno/kyverno/pkg/pss/utils" "github.com/sigstore/k8s-manifest-sigstore/pkg/k8smanifest" @@ -119,7 +120,9 @@ type ContextEntry struct { type Variable struct { // Value is any arbitrary JSON object representable in YAML or JSON form. // +optional - Value *apiextv1.JSON `json:"value,omitempty" yaml:"value,omitempty"` + // +kubebuilder:validation:Schemaless + // +kubebuilder:pruning:PreserveUnknownFields + Value *kyverno.Any `json:"value,omitempty" yaml:"value,omitempty"` // JMESPath is an optional JMESPath Expression that can be used to // transform the variable. @@ -129,7 +132,25 @@ type Variable struct { // Default is an optional arbitrary JSON object that the variable may take if the JMESPath // expression evaluates to nil // +optional - Default *apiextv1.JSON `json:"default,omitempty" yaml:"default,omitempty"` + // +kubebuilder:validation:Schemaless + // +kubebuilder:pruning:PreserveUnknownFields + Default *kyverno.Any `json:"default,omitempty" yaml:"default,omitempty"` +} + +func (v *Variable) GetValue() any { + return kyverno.FromAny(v.Value) +} + +func (v *Variable) SetValue(in any) { + v.Value = kyverno.ToAny(in) +} + +func (v *Variable) GetDefault() any { + return kyverno.FromAny(v.Default) +} + +func (v *Variable) SetDefault(in any) { + v.Default = kyverno.ToAny(in) } // ImageRegistry defines requests to an OCI/Docker V2 registry to fetch image diff --git a/api/kyverno/v1/zz_generated.deepcopy.go b/api/kyverno/v1/zz_generated.deepcopy.go index 571a9df491..478a57f7e7 100755 --- a/api/kyverno/v1/zz_generated.deepcopy.go +++ b/api/kyverno/v1/zz_generated.deepcopy.go @@ -1678,13 +1678,11 @@ func (in *Variable) DeepCopyInto(out *Variable) { *out = *in if in.Value != nil { in, out := &in.Value, &out.Value - *out = new(apiextensionsv1.JSON) - (*in).DeepCopyInto(*out) + *out = (*in).DeepCopy() } if in.Default != nil { in, out := &in.Default, &out.Default - *out = new(apiextensionsv1.JSON) - (*in).DeepCopyInto(*out) + *out = (*in).DeepCopy() } return } diff --git a/docs/user/crd/index.html b/docs/user/crd/index.html index d755e58da8..f87b0056b5 100644 --- a/docs/user/crd/index.html +++ b/docs/user/crd/index.html @@ -4593,9 +4593,7 @@ Kubernetes meta/v1.LabelSelector value
- -Kubernetes apiextensions/v1.JSON - +github.com/kyverno/kyverno/api/kyverno.Any @@ -4620,9 +4618,7 @@ transform the variable.

default
- -Kubernetes apiextensions/v1.JSON - +github.com/kyverno/kyverno/api/kyverno.Any diff --git a/docs/user/crd/kyverno.v1.html b/docs/user/crd/kyverno.v1.html index 0b7d70cb9d..2ef4588bd3 100644 --- a/docs/user/crd/kyverno.v1.html +++ b/docs/user/crd/kyverno.v1.html @@ -9213,7 +9213,7 @@ by specifying exclusions for Pod Security Standards controls.

- k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1.JSON + github.com/kyverno/kyverno/api/kyverno.Any @@ -9268,7 +9268,7 @@ transform the variable.

- k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1.JSON + github.com/kyverno/kyverno/api/kyverno.Any diff --git a/pkg/client/applyconfigurations/kyverno/v1/variable.go b/pkg/client/applyconfigurations/kyverno/v1/variable.go index 53c44723b5..59f2924796 100644 --- a/pkg/client/applyconfigurations/kyverno/v1/variable.go +++ b/pkg/client/applyconfigurations/kyverno/v1/variable.go @@ -19,15 +19,15 @@ limitations under the License. package v1 import ( - v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + kyverno "github.com/kyverno/kyverno/api/kyverno" ) // VariableApplyConfiguration represents an declarative configuration of the Variable type for use // with apply. type VariableApplyConfiguration struct { - Value *v1.JSON `json:"value,omitempty"` - JMESPath *string `json:"jmesPath,omitempty"` - Default *v1.JSON `json:"default,omitempty"` + Value *kyverno.Any `json:"value,omitempty"` + JMESPath *string `json:"jmesPath,omitempty"` + Default *kyverno.Any `json:"default,omitempty"` } // VariableApplyConfiguration constructs an declarative configuration of the Variable type for use with @@ -39,7 +39,7 @@ func Variable() *VariableApplyConfiguration { // WithValue sets the Value field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the Value field is set to the value of the last call. -func (b *VariableApplyConfiguration) WithValue(value v1.JSON) *VariableApplyConfiguration { +func (b *VariableApplyConfiguration) WithValue(value kyverno.Any) *VariableApplyConfiguration { b.Value = &value return b } @@ -55,7 +55,7 @@ func (b *VariableApplyConfiguration) WithJMESPath(value string) *VariableApplyCo // WithDefault sets the Default field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the Default field is set to the value of the last call. -func (b *VariableApplyConfiguration) WithDefault(value v1.JSON) *VariableApplyConfiguration { +func (b *VariableApplyConfiguration) WithDefault(value kyverno.Any) *VariableApplyConfiguration { b.Default = &value return b } diff --git a/pkg/engine/context/loaders/variable.go b/pkg/engine/context/loaders/variable.go index 22e002464d..270f989045 100644 --- a/pkg/engine/context/loaders/variable.go +++ b/pkg/engine/context/loaders/variable.go @@ -62,24 +62,24 @@ func (vl *variableLoader) loadVariable() (err error) { } var defaultValue interface{} = nil - if entry.Variable.Default != nil { - value, err := jsonutils.DocumentToUntyped(entry.Variable.Default) + if entry.Variable.GetDefault() != nil { + value, err := jsonutils.DocumentToUntyped(entry.Variable.GetDefault()) if err != nil { return fmt.Errorf("invalid default for variable %s", entry.Name) } defaultValue, err = variables.SubstituteAll(logger, ctx, value) if err != nil { - return fmt.Errorf("failed to substitute variables in context entry %s %s: %v", entry.Name, entry.Variable.Default, err) + return fmt.Errorf("failed to substitute variables in context entry %s %s: %v", entry.Name, entry.Variable.GetDefault(), err) } logger.V(4).Info("evaluated default value", "variable name", entry.Name, "jmespath", defaultValue) } var output interface{} = defaultValue - if entry.Variable.Value != nil { - value, _ := jsonutils.DocumentToUntyped(entry.Variable.Value) + if entry.Variable.GetValue() != nil { + value, _ := jsonutils.DocumentToUntyped(entry.Variable.GetValue()) variable, err := variables.SubstituteAll(logger, ctx, value) if err != nil { - return fmt.Errorf("failed to substitute variables in context entry %s %s: %v", entry.Name, entry.Variable.Value, err) + return fmt.Errorf("failed to substitute variables in context entry %s %s: %v", entry.Name, entry.Variable.GetValue(), err) } if path != "" { variable, err := applyJMESPath(vl.jp, path, variable) diff --git a/pkg/engine/jsonutils/convert.go b/pkg/engine/jsonutils/convert.go index 6b38dfc776..64b9d6d1f5 100644 --- a/pkg/engine/jsonutils/convert.go +++ b/pkg/engine/jsonutils/convert.go @@ -7,6 +7,11 @@ var json = jsoniter.ConfigCompatibleWithStandardLibrary // DocumentToUntyped converts a typed object to JSON data // i.e. string, []interface{}, map[string]interface{} func DocumentToUntyped(doc interface{}) (interface{}, error) { + switch doc.(type) { + case string, []any, map[string]any: + return doc, nil + } + jsonDoc, err := json.Marshal(doc) if err != nil { return nil, err diff --git a/pkg/validation/policy/validate.go b/pkg/validation/policy/validate.go index 6a550913cc..359e013e96 100644 --- a/pkg/validation/policy/validate.go +++ b/pkg/validation/policy/validate.go @@ -1303,10 +1303,10 @@ func validateVariable(entry kyvernov1.ContextEntry) error { return fmt.Errorf("failed to parse JMESPath %s: %v", entry.Variable.JMESPath, err) } } - if entry.Variable.Value == nil && jmesPath == "" { + if entry.Variable.GetValue() == nil && jmesPath == "" { return fmt.Errorf("a variable must define a value or a jmesPath expression") } - if entry.Variable.Default != nil && jmesPath == "" { + if entry.Variable.GetDefault() != nil && jmesPath == "" { return fmt.Errorf("a variable must define a default value only when a jmesPath expression is defined") } return nil