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