1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00

feat(json): reduce reliance on DocumentToUntyped() (#10724)

Signed-off-by: Khaled Emara <khaled.emara@nirmata.com>
Co-authored-by: Mariam Fahmy <mariam.fahmy@nirmata.com>
This commit is contained in:
Khaled Emara 2024-07-29 14:57:20 +03:00 committed by GitHub
parent 70c1dc6a06
commit c2646f7a9d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 48 additions and 28 deletions

View file

@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/kyverno/kyverno/api/kyverno"
"github.com/kyverno/kyverno/pkg/engine/variables/regex" "github.com/kyverno/kyverno/pkg/engine/variables/regex"
"github.com/kyverno/kyverno/pkg/pss/utils" "github.com/kyverno/kyverno/pkg/pss/utils"
"github.com/sigstore/k8s-manifest-sigstore/pkg/k8smanifest" "github.com/sigstore/k8s-manifest-sigstore/pkg/k8smanifest"
@ -119,7 +120,9 @@ type ContextEntry struct {
type Variable struct { type Variable struct {
// Value is any arbitrary JSON object representable in YAML or JSON form. // Value is any arbitrary JSON object representable in YAML or JSON form.
// +optional // +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 // JMESPath is an optional JMESPath Expression that can be used to
// transform the variable. // 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 // Default is an optional arbitrary JSON object that the variable may take if the JMESPath
// expression evaluates to nil // expression evaluates to nil
// +optional // +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 // ImageRegistry defines requests to an OCI/Docker V2 registry to fetch image

View file

@ -1678,13 +1678,11 @@ func (in *Variable) DeepCopyInto(out *Variable) {
*out = *in *out = *in
if in.Value != nil { if in.Value != nil {
in, out := &in.Value, &out.Value in, out := &in.Value, &out.Value
*out = new(apiextensionsv1.JSON) *out = (*in).DeepCopy()
(*in).DeepCopyInto(*out)
} }
if in.Default != nil { if in.Default != nil {
in, out := &in.Default, &out.Default in, out := &in.Default, &out.Default
*out = new(apiextensionsv1.JSON) *out = (*in).DeepCopy()
(*in).DeepCopyInto(*out)
} }
return return
} }

View file

@ -4593,9 +4593,7 @@ Kubernetes meta/v1.LabelSelector
<td> <td>
<code>value</code><br/> <code>value</code><br/>
<em> <em>
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#json-v1-apiextensions"> github.com/kyverno/kyverno/api/kyverno.Any
Kubernetes apiextensions/v1.JSON
</a>
</em> </em>
</td> </td>
<td> <td>
@ -4620,9 +4618,7 @@ transform the variable.</p>
<td> <td>
<code>default</code><br/> <code>default</code><br/>
<em> <em>
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#json-v1-apiextensions"> github.com/kyverno/kyverno/api/kyverno.Any
Kubernetes apiextensions/v1.JSON
</a>
</em> </em>
</td> </td>
<td> <td>

View file

@ -9213,7 +9213,7 @@ by specifying exclusions for Pod Security Standards controls.</p>
<span style="font-family: monospace">k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1.JSON</span> <span style="font-family: monospace">github.com/kyverno/kyverno/api/kyverno.Any</span>
</td> </td>
@ -9268,7 +9268,7 @@ transform the variable.</p>
<span style="font-family: monospace">k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1.JSON</span> <span style="font-family: monospace">github.com/kyverno/kyverno/api/kyverno.Any</span>
</td> </td>

View file

@ -19,15 +19,15 @@ limitations under the License.
package v1 package v1
import ( 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 // VariableApplyConfiguration represents an declarative configuration of the Variable type for use
// with apply. // with apply.
type VariableApplyConfiguration struct { type VariableApplyConfiguration struct {
Value *v1.JSON `json:"value,omitempty"` Value *kyverno.Any `json:"value,omitempty"`
JMESPath *string `json:"jmesPath,omitempty"` JMESPath *string `json:"jmesPath,omitempty"`
Default *v1.JSON `json:"default,omitempty"` Default *kyverno.Any `json:"default,omitempty"`
} }
// VariableApplyConfiguration constructs an declarative configuration of the Variable type for use with // 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 // 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. // 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. // 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 b.Value = &value
return b 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 // 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. // 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. // 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 b.Default = &value
return b return b
} }

View file

@ -62,24 +62,24 @@ func (vl *variableLoader) loadVariable() (err error) {
} }
var defaultValue interface{} = nil var defaultValue interface{} = nil
if entry.Variable.Default != nil { if entry.Variable.GetDefault() != nil {
value, err := jsonutils.DocumentToUntyped(entry.Variable.Default) value, err := jsonutils.DocumentToUntyped(entry.Variable.GetDefault())
if err != nil { if err != nil {
return fmt.Errorf("invalid default for variable %s", entry.Name) return fmt.Errorf("invalid default for variable %s", entry.Name)
} }
defaultValue, err = variables.SubstituteAll(logger, ctx, value) defaultValue, err = variables.SubstituteAll(logger, ctx, value)
if err != nil { 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) logger.V(4).Info("evaluated default value", "variable name", entry.Name, "jmespath", defaultValue)
} }
var output interface{} = defaultValue var output interface{} = defaultValue
if entry.Variable.Value != nil { if entry.Variable.GetValue() != nil {
value, _ := jsonutils.DocumentToUntyped(entry.Variable.Value) value, _ := jsonutils.DocumentToUntyped(entry.Variable.GetValue())
variable, err := variables.SubstituteAll(logger, ctx, value) variable, err := variables.SubstituteAll(logger, ctx, value)
if err != nil { 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 != "" { if path != "" {
variable, err := applyJMESPath(vl.jp, path, variable) variable, err := applyJMESPath(vl.jp, path, variable)

View file

@ -7,6 +7,11 @@ var json = jsoniter.ConfigCompatibleWithStandardLibrary
// DocumentToUntyped converts a typed object to JSON data // DocumentToUntyped converts a typed object to JSON data
// i.e. string, []interface{}, map[string]interface{} // i.e. string, []interface{}, map[string]interface{}
func DocumentToUntyped(doc interface{}) (interface{}, error) { func DocumentToUntyped(doc interface{}) (interface{}, error) {
switch doc.(type) {
case string, []any, map[string]any:
return doc, nil
}
jsonDoc, err := json.Marshal(doc) jsonDoc, err := json.Marshal(doc)
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -1303,10 +1303,10 @@ func validateVariable(entry kyvernov1.ContextEntry) error {
return fmt.Errorf("failed to parse JMESPath %s: %v", entry.Variable.JMESPath, err) 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") 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 fmt.Errorf("a variable must define a default value only when a jmesPath expression is defined")
} }
return nil return nil