k8s.io/apiserver@v0.31.1/pkg/cel/mutation/common/val.go (about)

     1  /*
     2  Copyright 2024 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package common
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  
    23  	"github.com/google/cel-go/common/types"
    24  	"github.com/google/cel-go/common/types/ref"
    25  	"github.com/google/cel-go/common/types/traits"
    26  )
    27  
    28  // ObjectVal is the CEL Val for an object that is constructed via the object
    29  // construction syntax.
    30  type ObjectVal struct {
    31  	typeRef TypeRef
    32  	fields  map[string]ref.Val
    33  }
    34  
    35  // NewObjectVal creates an ObjectVal by its TypeRef and its fields.
    36  func NewObjectVal(typeRef TypeRef, fields map[string]ref.Val) *ObjectVal {
    37  	return &ObjectVal{
    38  		typeRef: typeRef,
    39  		fields:  fields,
    40  	}
    41  }
    42  
    43  var _ ref.Val = (*ObjectVal)(nil)
    44  var _ traits.Zeroer = (*ObjectVal)(nil)
    45  
    46  // ConvertToNative converts the object to map[string]any.
    47  // All nested lists are converted into []any native type.
    48  //
    49  // It returns an error if the target type is not map[string]any,
    50  // or any recursive conversion fails.
    51  func (v *ObjectVal) ConvertToNative(typeDesc reflect.Type) (any, error) {
    52  	var result map[string]any
    53  	if typeDesc != reflect.TypeOf(result) {
    54  		return nil, fmt.Errorf("unable to convert to %v", typeDesc)
    55  	}
    56  	result = make(map[string]any, len(v.fields))
    57  	for k, v := range v.fields {
    58  		converted, err := convertField(v)
    59  		if err != nil {
    60  			return nil, fmt.Errorf("fail to convert field %q: %w", k, err)
    61  		}
    62  		result[k] = converted
    63  	}
    64  	return result, nil
    65  }
    66  
    67  // ConvertToType supports type conversions between CEL value types supported by the expression language.
    68  func (v *ObjectVal) ConvertToType(typeValue ref.Type) ref.Val {
    69  	switch typeValue {
    70  	case v.typeRef:
    71  		return v
    72  	case types.TypeType:
    73  		return v.typeRef.CELType()
    74  	}
    75  	return types.NewErr("unsupported conversion into %v", typeValue)
    76  }
    77  
    78  // Equal returns true if the `other` value has the same type and content as the implementing struct.
    79  func (v *ObjectVal) Equal(other ref.Val) ref.Val {
    80  	if rhs, ok := other.(*ObjectVal); ok {
    81  		return types.Bool(reflect.DeepEqual(v.fields, rhs.fields))
    82  	}
    83  	return types.Bool(false)
    84  }
    85  
    86  // Type returns the TypeValue of the value.
    87  func (v *ObjectVal) Type() ref.Type {
    88  	return v.typeRef.CELType()
    89  }
    90  
    91  // Value returns its value as a map[string]any.
    92  func (v *ObjectVal) Value() any {
    93  	var result any
    94  	var object map[string]any
    95  	result, err := v.ConvertToNative(reflect.TypeOf(object))
    96  	if err != nil {
    97  		return types.WrapErr(err)
    98  	}
    99  	return result
   100  }
   101  
   102  // IsZeroValue indicates whether the object is the zero value for the type.
   103  // For the ObjectVal, it is zero value if and only if the fields map is empty.
   104  func (v *ObjectVal) IsZeroValue() bool {
   105  	return len(v.fields) == 0
   106  }
   107  
   108  // convertField converts a referred ref.Val to its expected type.
   109  // For objects, the expected type is map[string]any
   110  // For lists, the expected type is []any
   111  // For maps, the expected type is map[string]any
   112  // For anything else, it is converted via value.Value()
   113  //
   114  // It will return an error if the request type is a map but the key
   115  // is not a string.
   116  func convertField(value ref.Val) (any, error) {
   117  	// special handling for lists, where the elements are converted with Value() instead of ConvertToNative
   118  	// to allow them to become native value of any type.
   119  	if listOfVal, ok := value.Value().([]ref.Val); ok {
   120  		var result []any
   121  		for _, v := range listOfVal {
   122  			result = append(result, v.Value())
   123  		}
   124  		return result, nil
   125  	}
   126  	// unstructured maps, as seen in annotations
   127  	// map keys must be strings
   128  	if mapOfVal, ok := value.Value().(map[ref.Val]ref.Val); ok {
   129  		result := make(map[string]any)
   130  		for k, v := range mapOfVal {
   131  			stringKey, ok := k.Value().(string)
   132  			if !ok {
   133  				return nil, fmt.Errorf("map key %q is of type %t, not string", k, k)
   134  			}
   135  			result[stringKey] = v.Value()
   136  		}
   137  		return result, nil
   138  	}
   139  	return value.Value(), nil
   140  }