rebase: update K8s packages to v0.32.1

Update K8s packages in go.mod to v0.32.1

Signed-off-by: Praveen M <m.praveen@ibm.com>
This commit is contained in:
Praveen M
2025-01-16 09:41:46 +05:30
committed by mergify[bot]
parent 5aef21ea4e
commit 7eb99fc6c9
2442 changed files with 273386 additions and 47788 deletions

127
vendor/k8s.io/apiserver/pkg/cel/common/typeprovider.go generated vendored Normal file
View File

@ -0,0 +1,127 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package common
import (
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
)
// TypeResolver resolves a type by a given name.
type TypeResolver interface {
// Resolve resolves the type by its name.
// This function returns false if the name does not refer to a known object type.
Resolve(name string) (ResolvedType, bool)
}
// ResolvedType refers an object type that can be looked up for its fields.
type ResolvedType interface {
ref.Type
Type() *types.Type
// Field finds the field by the field name, or false if the field is not known.
// This function directly return a FieldType that is known to CEL to be more customizable.
Field(name string) (*types.FieldType, bool)
// FieldNames returns the field names associated with the type, if the type
// is found.
FieldNames() ([]string, bool)
// Val creates an instance for the ResolvedType, given its fields and their values.
Val(fields map[string]ref.Val) ref.Val
}
// ResolverTypeProvider delegates type resolution first to the TypeResolver and then
// to the underlying types.Provider for types not resolved by the TypeResolver.
type ResolverTypeProvider struct {
typeResolver TypeResolver
underlyingTypeProvider types.Provider
}
var _ types.Provider = (*ResolverTypeProvider)(nil)
// FindStructType returns the Type give a qualified type name, by looking it up with
// the DynamicTypeResolver and translating it to CEL Type.
// If the type is not known to the DynamicTypeResolver, the lookup falls back to the underlying
// ResolverTypeProvider instead.
func (p *ResolverTypeProvider) FindStructType(structType string) (*types.Type, bool) {
t, ok := p.typeResolver.Resolve(structType)
if ok {
return types.NewTypeTypeWithParam(t.Type()), true
}
return p.underlyingTypeProvider.FindStructType(structType)
}
// FindStructFieldNames returns the field names associated with the type, if the type
// is found.
func (p *ResolverTypeProvider) FindStructFieldNames(structType string) ([]string, bool) {
t, ok := p.typeResolver.Resolve(structType)
if ok {
return t.FieldNames()
}
return p.underlyingTypeProvider.FindStructFieldNames(structType)
}
// FindStructFieldType returns the field type for a checked type value.
// Returns false if the field could not be found.
func (p *ResolverTypeProvider) FindStructFieldType(structType, fieldName string) (*types.FieldType, bool) {
t, ok := p.typeResolver.Resolve(structType)
if ok {
return t.Field(fieldName)
}
return p.underlyingTypeProvider.FindStructFieldType(structType, fieldName)
}
// NewValue creates a new type value from a qualified name and map of fields.
func (p *ResolverTypeProvider) NewValue(structType string, fields map[string]ref.Val) ref.Val {
t, ok := p.typeResolver.Resolve(structType)
if ok {
return t.Val(fields)
}
return p.underlyingTypeProvider.NewValue(structType, fields)
}
func (p *ResolverTypeProvider) EnumValue(enumName string) ref.Val {
return p.underlyingTypeProvider.EnumValue(enumName)
}
func (p *ResolverTypeProvider) FindIdent(identName string) (ref.Val, bool) {
return p.underlyingTypeProvider.FindIdent(identName)
}
// ResolverEnvOption creates the ResolverTypeProvider with a given DynamicTypeResolver,
// and also returns the CEL ResolverEnvOption to apply it to the env.
func ResolverEnvOption(resolver TypeResolver) cel.EnvOption {
_, envOpt := NewResolverTypeProviderAndEnvOption(resolver)
return envOpt
}
// NewResolverTypeProviderAndEnvOption creates the ResolverTypeProvider with a given DynamicTypeResolver,
// and also returns the CEL ResolverEnvOption to apply it to the env.
func NewResolverTypeProviderAndEnvOption(resolver TypeResolver) (*ResolverTypeProvider, cel.EnvOption) {
tp := &ResolverTypeProvider{typeResolver: resolver}
var envOption cel.EnvOption = func(e *cel.Env) (*cel.Env, error) {
// wrap the existing type provider (acquired from the env)
// and set new type provider for the env.
tp.underlyingTypeProvider = e.CELTypeProvider()
typeProviderOption := cel.CustomTypeProvider(tp)
return typeProviderOption(e)
}
return tp, envOption
}

View File

@ -33,7 +33,8 @@ import (
"k8s.io/apiserver/pkg/cel/library"
genericfeatures "k8s.io/apiserver/pkg/features"
utilfeature "k8s.io/apiserver/pkg/util/feature"
utilversion "k8s.io/apiserver/pkg/util/version"
"k8s.io/component-base/featuregate"
utilversion "k8s.io/component-base/version"
)
// DefaultCompatibilityVersion returns a default compatibility version for use with EnvSet
@ -49,7 +50,7 @@ import (
// A default version number equal to the current Kubernetes major.minor version
// indicates fast forward CEL features that can be used when rollback is no longer needed.
func DefaultCompatibilityVersion() *version.Version {
effectiveVer := utilversion.DefaultComponentGlobalsRegistry.EffectiveVersionFor(utilversion.DefaultKubeComponent)
effectiveVer := featuregate.DefaultComponentGlobalsRegistry.EffectiveVersionFor(featuregate.DefaultKubeComponent)
if effectiveVer == nil {
effectiveVer = utilversion.DefaultKubeEffectiveVersion()
}
@ -71,9 +72,9 @@ var baseOptsWithoutStrictCost = []VersionedOptions{
cel.EagerlyValidateDeclarations(true),
cel.DefaultUTCTimeZone(true),
library.URLs(),
library.Regex(),
library.Lists(),
UnversionedLib(library.URLs),
UnversionedLib(library.Regex),
UnversionedLib(library.Lists),
// cel-go v0.17.7 change the cost of has() from 0 to 1, but also provided the CostEstimatorOptions option to preserve the old behavior, so we enabled it at the same time we bumped our cel version to v0.17.7.
// Since it is a regression fix, we apply it uniformly to all code use v0.17.7.
@ -91,7 +92,7 @@ var baseOptsWithoutStrictCost = []VersionedOptions{
{
IntroducedVersion: version.MajorMinor(1, 27),
EnvOptions: []cel.EnvOption{
library.Authz(),
UnversionedLib(library.Authz),
},
},
{
@ -99,7 +100,7 @@ var baseOptsWithoutStrictCost = []VersionedOptions{
EnvOptions: []cel.EnvOption{
cel.CrossTypeNumericComparisons(true),
cel.OptionalTypes(),
library.Quantity(),
UnversionedLib(library.Quantity),
},
},
// add the new validator in 1.29
@ -138,15 +139,15 @@ var baseOptsWithoutStrictCost = []VersionedOptions{
{
IntroducedVersion: version.MajorMinor(1, 30),
EnvOptions: []cel.EnvOption{
library.IP(),
library.CIDR(),
UnversionedLib(library.IP),
UnversionedLib(library.CIDR),
},
},
// Format Library
{
IntroducedVersion: version.MajorMinor(1, 31),
EnvOptions: []cel.EnvOption{
library.Format(),
UnversionedLib(library.Format),
},
},
// Authz selectors
@ -165,7 +166,14 @@ var baseOptsWithoutStrictCost = []VersionedOptions{
return enabled
},
EnvOptions: []cel.EnvOption{
library.AuthzSelectors(),
UnversionedLib(library.AuthzSelectors),
},
},
// Two variable comprehensions
{
IntroducedVersion: version.MajorMinor(1, 32),
EnvOptions: []cel.EnvOption{
UnversionedLib(ext.TwoVarComprehensions),
},
},
}
@ -191,6 +199,19 @@ var StrictCostOpt = VersionedOptions{
},
}
// cacheBaseEnvs controls whether calls to MustBaseEnvSet are cached.
// Defaults to true, may be disabled by calling DisableBaseEnvSetCachingForTests.
var cacheBaseEnvs = true
// DisableBaseEnvSetCachingForTests clears and disables base env caching.
// This is only intended for unit tests exercising MustBaseEnvSet directly with different enablement options.
// It does not clear other initialization paths that may cache results of calling MustBaseEnvSet.
func DisableBaseEnvSetCachingForTests() {
cacheBaseEnvs = false
baseEnvs.Clear()
baseEnvsWithOption.Clear()
}
// MustBaseEnvSet returns the common CEL base environments for Kubernetes for Version, or panics
// if the version is nil, or does not have major and minor components.
//
@ -216,7 +237,9 @@ func MustBaseEnvSet(ver *version.Version, strictCost bool) *EnvSet {
}
entry, _, _ = baseEnvsSingleflight.Do(key, func() (interface{}, error) {
entry := mustNewEnvSet(ver, baseOpts)
baseEnvs.Store(key, entry)
if cacheBaseEnvs {
baseEnvs.Store(key, entry)
}
return entry, nil
})
} else {
@ -225,7 +248,9 @@ func MustBaseEnvSet(ver *version.Version, strictCost bool) *EnvSet {
}
entry, _, _ = baseEnvsWithOptionSingleflight.Do(key, func() (interface{}, error) {
entry := mustNewEnvSet(ver, baseOptsWithoutStrictCost)
baseEnvsWithOption.Store(key, entry)
if cacheBaseEnvs {
baseEnvsWithOption.Store(key, entry)
}
return entry, nil
})
}
@ -239,3 +264,20 @@ var (
baseEnvsSingleflight = &singleflight.Group{}
baseEnvsWithOptionSingleflight = &singleflight.Group{}
)
// UnversionedLib wraps library initialization calls like ext.Sets() or library.IP()
// to force compilation errors if the call evolves to include a varadic variable option.
//
// This provides automatic detection of a problem that is hard to catch in review--
// If a CEL library used in Kubernetes is unversioned and then become versioned, and we
// fail to set a desired version, the libraries defaults to the latest version, changing
// CEL environment without controlled rollout, bypassing the entire purpose of the base
// environment.
//
// If usages of this function fail to compile: add version=1 argument to all call sites
// that fail compilation while removing the UnversionedLib wrapper. Next, review
// the changes in the library present in higher versions and, if needed, use VersionedOptions to
// the base environment to roll out to a newer version safely.
func UnversionedLib(initializer func() cel.EnvOption) cel.EnvOption {
return initializer()
}

View File

@ -41,11 +41,11 @@ type Format struct {
MaxRegexSize int
}
func (d *Format) ConvertToNative(typeDesc reflect.Type) (interface{}, error) {
func (d Format) ConvertToNative(typeDesc reflect.Type) (interface{}, error) {
return nil, fmt.Errorf("type conversion error from 'Format' to '%v'", typeDesc)
}
func (d *Format) ConvertToType(typeVal ref.Type) ref.Val {
func (d Format) ConvertToType(typeVal ref.Type) ref.Val {
switch typeVal {
case FormatType:
return d
@ -56,18 +56,18 @@ func (d *Format) ConvertToType(typeVal ref.Type) ref.Val {
}
}
func (d *Format) Equal(other ref.Val) ref.Val {
otherDur, ok := other.(*Format)
func (d Format) Equal(other ref.Val) ref.Val {
otherDur, ok := other.(Format)
if !ok {
return types.MaybeNoSuchOverloadErr(other)
}
return types.Bool(d.Name == otherDur.Name)
}
func (d *Format) Type() ref.Type {
func (d Format) Type() ref.Type {
return FormatType
}
func (d *Format) Value() interface{} {
func (d Format) Value() interface{} {
return d
}

View File

@ -232,7 +232,20 @@ var authzLib = &authz{}
type authz struct{}
func (*authz) LibraryName() string {
return "k8s.authz"
return "kubernetes.authz"
}
func (*authz) Types() []*cel.Type {
return []*cel.Type{
AuthorizerType,
PathCheckType,
GroupCheckType,
ResourceCheckType,
DecisionType}
}
func (*authz) declarations() map[string][]cel.FunctionOpt {
return authzLibraryDecls
}
var authzLibraryDecls = map[string][]cel.FunctionOpt{
@ -324,7 +337,15 @@ var authzSelectorsLib = &authzSelectors{}
type authzSelectors struct{}
func (*authzSelectors) LibraryName() string {
return "k8s.authzSelectors"
return "kubernetes.authzSelectors"
}
func (*authzSelectors) Types() []*cel.Type {
return []*cel.Type{ResourceCheckType}
}
func (*authzSelectors) declarations() map[string][]cel.FunctionOpt {
return authzSelectorsLibraryDecls
}
var authzSelectorsLibraryDecls = map[string][]cel.FunctionOpt{

View File

@ -109,7 +109,15 @@ var cidrsLib = &cidrs{}
type cidrs struct{}
func (*cidrs) LibraryName() string {
return "net.cidr"
return "kubernetes.net.cidr"
}
func (*cidrs) declarations() map[string][]cel.FunctionOpt {
return cidrLibraryDecls
}
func (*cidrs) Types() []*cel.Type {
return []*cel.Type{apiservercel.CIDRType, apiservercel.IPType}
}
var cidrLibraryDecls = map[string][]cel.FunctionOpt{

View File

@ -18,14 +18,13 @@ package library
import (
"fmt"
"math"
"github.com/google/cel-go/checker"
"github.com/google/cel-go/common"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits"
"math"
"k8s.io/apiserver/pkg/cel"
)
@ -36,16 +35,25 @@ var panicOnUnknown = false
// builtInFunctions is a list of functions used in cost tests that are not handled by CostEstimator.
var knownUnhandledFunctions = map[string]bool{
"uint": true,
"duration": true,
"bytes": true,
"timestamp": true,
"value": true,
"_==_": true,
"_&&_": true,
"_>_": true,
"!_": true,
"strings.quote": true,
"@not_strictly_false": true,
"uint": true,
"duration": true,
"bytes": true,
"cel.@mapInsert": true,
"timestamp": true,
"strings.quote": true,
"value": true,
"_==_": true,
"_&&_": true,
"_||_": true,
"_>_": true,
"_>=_": true,
"_<_": true,
"_<=_": true,
"!_": true,
"_?_:_": true,
"_+_": true,
"_-_": true,
}
// CostEstimator implements CEL's interpretable.ActualCostEstimator and checker.CostEstimator.
@ -98,7 +106,7 @@ func (l *CostEstimator) CallCost(function, overloadId string, args []ref.Val, re
cost += traversalCost(args[0]) // these O(n) operations all cost roughly the cost of a single traversal
}
return &cost
case "url", "lowerAscii", "upperAscii", "substring", "trim":
case "url", "lowerAscii", "upperAscii", "substring", "trim", "jsonpatch.escapeKey":
if len(args) >= 1 {
cost := uint64(math.Ceil(float64(actualSize(args[0])) * common.StringTraversalCostFactor))
return &cost
@ -201,7 +209,7 @@ func (l *CostEstimator) CallCost(function, overloadId string, args []ref.Val, re
}
case "validate":
if len(args) >= 2 {
format, isFormat := args[0].Value().(*cel.Format)
format, isFormat := args[0].Value().(cel.Format)
if isFormat {
strSize := actualSize(args[1])
@ -235,6 +243,26 @@ func (l *CostEstimator) CallCost(function, overloadId string, args []ref.Val, re
// url accessors
cost := uint64(1)
return &cost
case "_==_":
if len(args) == 2 {
unitCost := uint64(1)
lhs := args[0]
switch lhs.(type) {
case *cel.Quantity, cel.Quantity,
*cel.IP, cel.IP,
*cel.CIDR, cel.CIDR,
*cel.Format, cel.Format, // Formats have a small max size. Format takes pointer receiver.
*cel.URL, cel.URL, // TODO: Computing the actual cost is expensive, and changing this would be a breaking change
*cel.Semver, cel.Semver,
*authorizerVal, authorizerVal, *pathCheckVal, pathCheckVal, *groupCheckVal, groupCheckVal,
*resourceCheckVal, resourceCheckVal, *decisionVal, decisionVal:
return &unitCost
default:
if panicOnUnknown && lhs.Type() != nil && isRegisteredType(lhs.Type().TypeName()) {
panic(fmt.Errorf("CallCost: unhandled equality for Kubernetes type %T", lhs))
}
}
}
}
if panicOnUnknown && !knownUnhandledFunctions[function] {
panic(fmt.Errorf("CallCost: unhandled function %q or args %v", function, args))
@ -275,10 +303,10 @@ func (l *CostEstimator) EstimateCallCost(function, overloadId string, target *ch
return &checker.CallEstimate{CostEstimate: l.sizeEstimate(*target).MultiplyByCostFactor(common.StringTraversalCostFactor)}
}
}
case "url":
case "url", "jsonpatch.escapeKey":
if len(args) == 1 {
sz := l.sizeEstimate(args[0])
return &checker.CallEstimate{CostEstimate: sz.MultiplyByCostFactor(common.StringTraversalCostFactor)}
return &checker.CallEstimate{CostEstimate: sz.MultiplyByCostFactor(common.StringTraversalCostFactor), ResultSize: &sz}
}
case "lowerAscii", "upperAscii", "substring", "trim":
if target != nil {
@ -475,6 +503,40 @@ func (l *CostEstimator) EstimateCallCost(function, overloadId string, target *ch
case "getScheme", "getHostname", "getHost", "getPort", "getEscapedPath", "getQuery":
// url accessors
return &checker.CallEstimate{CostEstimate: checker.CostEstimate{Min: 1, Max: 1}}
case "_==_":
if len(args) == 2 {
lhs := args[0]
rhs := args[1]
if lhs.Type().Equal(rhs.Type()) == types.True {
t := lhs.Type()
if t.Kind() == types.OpaqueKind {
switch t.TypeName() {
case cel.IPType.TypeName(), cel.CIDRType.TypeName():
return &checker.CallEstimate{CostEstimate: checker.CostEstimate{Min: 1, Max: 1}}
}
}
if t.Kind() == types.StructKind {
switch t {
case cel.QuantityType, AuthorizerType, PathCheckType, // O(1) cost equality checks
GroupCheckType, ResourceCheckType, DecisionType, cel.SemverType:
return &checker.CallEstimate{CostEstimate: checker.CostEstimate{Min: 1, Max: 1}}
case cel.FormatType:
return &checker.CallEstimate{CostEstimate: checker.CostEstimate{Min: 1, Max: cel.MaxFormatSize}.MultiplyByCostFactor(common.StringTraversalCostFactor)}
case cel.URLType:
size := checker.SizeEstimate{Min: 1, Max: 1}
rhSize := rhs.ComputedSize()
lhSize := rhs.ComputedSize()
if rhSize != nil && lhSize != nil {
size = rhSize.Union(*lhSize)
}
return &checker.CallEstimate{CostEstimate: checker.CostEstimate{Min: 1, Max: size.Max}.MultiplyByCostFactor(common.StringTraversalCostFactor)}
}
}
if panicOnUnknown && isRegisteredType(t.TypeName()) {
panic(fmt.Errorf("EstimateCallCost: unhandled equality for Kubernetes type %v", t))
}
}
}
}
if panicOnUnknown && !knownUnhandledFunctions[function] {
panic(fmt.Errorf("EstimateCallCost: unhandled function %q, target %v, args %v", function, target, args))

View File

@ -25,6 +25,7 @@ import (
"github.com/google/cel-go/common/decls"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
"k8s.io/apimachinery/pkg/util/validation"
apiservercel "k8s.io/apiserver/pkg/cel"
@ -90,7 +91,15 @@ var formatLib = &format{}
type format struct{}
func (*format) LibraryName() string {
return "format"
return "kubernetes.format"
}
func (*format) Types() []*cel.Type {
return []*cel.Type{apiservercel.FormatType}
}
func (*format) declarations() map[string][]cel.FunctionOpt {
return formatLibraryDecls
}
func ZeroArgumentFunctionBinding(binding func() ref.Val) decls.OverloadOpt {
@ -124,7 +133,7 @@ func (*format) ProgramOptions() []cel.ProgramOption {
return []cel.ProgramOption{}
}
var ConstantFormats map[string]*apiservercel.Format = map[string]*apiservercel.Format{
var ConstantFormats = map[string]apiservercel.Format{
"dns1123Label": {
Name: "DNS1123Label",
ValidateFunc: func(s string) []string { return apimachineryvalidation.NameIsDNSLabel(s, false) },
@ -252,7 +261,7 @@ var formatLibraryDecls = map[string][]cel.FunctionOpt{
}
func formatValidate(arg1, arg2 ref.Val) ref.Val {
f, ok := arg1.Value().(*apiservercel.Format)
f, ok := arg1.Value().(apiservercel.Format)
if !ok {
return types.MaybeNoSuchOverloadErr(arg1)
}

View File

@ -132,7 +132,15 @@ var ipLib = &ip{}
type ip struct{}
func (*ip) LibraryName() string {
return "net.ip"
return "kubernetes.net.ip"
}
func (*ip) declarations() map[string][]cel.FunctionOpt {
return ipLibraryDecls
}
func (*ip) Types() []*cel.Type {
return []*cel.Type{apiservercel.IPType}
}
var ipLibraryDecls = map[string][]cel.FunctionOpt{

89
vendor/k8s.io/apiserver/pkg/cel/library/jsonpatch.go generated vendored Normal file
View File

@ -0,0 +1,89 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package library
import (
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"strings"
)
// JSONPatch provides a CEL function library extension of JSONPatch functions.
//
// jsonpatch.escapeKey
//
// Escapes a string for use as a JSONPatch path key.
//
// jsonpatch.escapeKey(<string>) <string>
//
// Examples:
//
// "/metadata/labels/" + jsonpatch.escapeKey('k8s.io/my~label') // returns "/metadata/labels/k8s.io~1my~0label"
func JSONPatch() cel.EnvOption {
return cel.Lib(jsonPatchLib)
}
var jsonPatchLib = &jsonPatch{}
type jsonPatch struct{}
func (*jsonPatch) LibraryName() string {
return "kubernetes.jsonpatch"
}
func (*jsonPatch) declarations() map[string][]cel.FunctionOpt {
return jsonPatchLibraryDecls
}
func (*jsonPatch) Types() []*cel.Type {
return []*cel.Type{}
}
var jsonPatchLibraryDecls = map[string][]cel.FunctionOpt{
"jsonpatch.escapeKey": {
cel.Overload("string_jsonpatch_escapeKey_string", []*cel.Type{cel.StringType}, cel.StringType,
cel.UnaryBinding(escape)),
},
}
func (*jsonPatch) CompileOptions() []cel.EnvOption {
var options []cel.EnvOption
for name, overloads := range jsonPatchLibraryDecls {
options = append(options, cel.Function(name, overloads...))
}
return options
}
func (*jsonPatch) ProgramOptions() []cel.ProgramOption {
return []cel.ProgramOption{}
}
var jsonPatchReplacer = strings.NewReplacer("/", "~1", "~", "~0")
func escapeKey(k string) string {
return jsonPatchReplacer.Replace(k)
}
func escape(arg ref.Val) ref.Val {
s, ok := arg.Value().(string)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
escaped := escapeKey(s)
return types.String(escaped)
}

61
vendor/k8s.io/apiserver/pkg/cel/library/libraries.go generated vendored Normal file
View File

@ -0,0 +1,61 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package library
import (
"github.com/google/cel-go/cel"
)
// Library represents a CEL library used by kubernetes.
type Library interface {
// SingletonLibrary provides the library name and ensures the library can be safely registered into environments.
cel.SingletonLibrary
// Types provides all custom types introduced by the library.
Types() []*cel.Type
// declarations returns all function declarations provided by the library.
declarations() map[string][]cel.FunctionOpt
}
// KnownLibraries returns all libraries used in Kubernetes.
func KnownLibraries() []Library {
return []Library{
authzLib,
authzSelectorsLib,
listsLib,
regexLib,
urlsLib,
quantityLib,
ipLib,
cidrsLib,
formatLib,
semverLib,
jsonPatchLib,
}
}
func isRegisteredType(typeName string) bool {
for _, lib := range KnownLibraries() {
for _, rt := range lib.Types() {
if rt.TypeName() == typeName {
return true
}
}
}
return false
}

View File

@ -96,7 +96,15 @@ var listsLib = &lists{}
type lists struct{}
func (*lists) LibraryName() string {
return "k8s.lists"
return "kubernetes.lists"
}
func (*lists) Types() []*cel.Type {
return []*cel.Type{}
}
func (*lists) declarations() map[string][]cel.FunctionOpt {
return listsLibraryDecls
}
var paramA = cel.TypeParamType("A")

View File

@ -143,7 +143,15 @@ var quantityLib = &quantity{}
type quantity struct{}
func (*quantity) LibraryName() string {
return "k8s.quantity"
return "kubernetes.quantity"
}
func (*quantity) Types() []*cel.Type {
return []*cel.Type{apiservercel.QuantityType}
}
func (*quantity) declarations() map[string][]cel.FunctionOpt {
return quantityLibraryDecls
}
var quantityLibraryDecls = map[string][]cel.FunctionOpt{

View File

@ -52,7 +52,15 @@ var regexLib = &regex{}
type regex struct{}
func (*regex) LibraryName() string {
return "k8s.regex"
return "kubernetes.regex"
}
func (*regex) Types() []*cel.Type {
return []*cel.Type{}
}
func (*regex) declarations() map[string][]cel.FunctionOpt {
return regexLibraryDecls
}
var regexLibraryDecls = map[string][]cel.FunctionOpt{

247
vendor/k8s.io/apiserver/pkg/cel/library/semverlib.go generated vendored Normal file
View File

@ -0,0 +1,247 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package library
import (
"github.com/blang/semver/v4"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
apiservercel "k8s.io/apiserver/pkg/cel"
)
// Semver provides a CEL function library extension for [semver.Version].
//
// semver
//
// Converts a string to a semantic version or results in an error if the string is not a valid semantic version. Refer
// to semver.org documentation for information on accepted patterns.
//
// semver(<string>) <Semver>
//
// Examples:
//
// semver('1.0.0') // returns a Semver
// semver('0.1.0-alpha.1') // returns a Semver
// semver('200K') // error
// semver('Three') // error
// semver('Mi') // error
//
// isSemver
//
// Returns true if a string is a valid Semver. isSemver returns true if and
// only if semver does not result in error.
//
// isSemver( <string>) <bool>
//
// Examples:
//
// isSemver('1.0.0') // returns true
// isSemver('v1.0') // returns true (tolerant parsing)
// isSemver('hello') // returns false
//
// Conversion to Scalars:
//
// - major/minor/patch: return the major version number as int64.
//
// <Semver>.major() <int>
//
// Examples:
//
// semver("1.2.3").major() // returns 1
//
// Comparisons
//
// - isGreaterThan: Returns true if and only if the receiver is greater than the operand
//
// - isLessThan: Returns true if and only if the receiver is less than the operand
//
// - compareTo: Compares receiver to operand and returns 0 if they are equal, 1 if the receiver is greater, or -1 if the receiver is less than the operand
//
//
// <Semver>.isLessThan(<semver>) <bool>
// <Semver>.isGreaterThan(<semver>) <bool>
// <Semver>.compareTo(<semver>) <int>
//
// Examples:
//
// semver("1.2.3").compareTo(semver("1.2.3")) // returns 0
// semver("1.2.3").compareTo(semver("2.0.0")) // returns -1
// semver("1.2.3").compareTo(semver("0.1.2")) // returns 1
func SemverLib() cel.EnvOption {
return cel.Lib(semverLib)
}
var semverLib = &semverLibType{}
type semverLibType struct{}
func (*semverLibType) LibraryName() string {
return "kubernetes.Semver"
}
func (*semverLibType) Types() []*cel.Type {
return []*cel.Type{apiservercel.SemverType}
}
func (*semverLibType) declarations() map[string][]cel.FunctionOpt {
return map[string][]cel.FunctionOpt{
"semver": {
cel.Overload("string_to_semver", []*cel.Type{cel.StringType}, apiservercel.SemverType, cel.UnaryBinding((stringToSemver))),
},
"isSemver": {
cel.Overload("is_semver_string", []*cel.Type{cel.StringType}, cel.BoolType, cel.UnaryBinding(isSemver)),
},
"isGreaterThan": {
cel.MemberOverload("semver_is_greater_than", []*cel.Type{apiservercel.SemverType, apiservercel.SemverType}, cel.BoolType, cel.BinaryBinding(semverIsGreaterThan)),
},
"isLessThan": {
cel.MemberOverload("semver_is_less_than", []*cel.Type{apiservercel.SemverType, apiservercel.SemverType}, cel.BoolType, cel.BinaryBinding(semverIsLessThan)),
},
"compareTo": {
cel.MemberOverload("semver_compare_to", []*cel.Type{apiservercel.SemverType, apiservercel.SemverType}, cel.IntType, cel.BinaryBinding(semverCompareTo)),
},
"major": {
cel.MemberOverload("semver_major", []*cel.Type{apiservercel.SemverType}, cel.IntType, cel.UnaryBinding(semverMajor)),
},
"minor": {
cel.MemberOverload("semver_minor", []*cel.Type{apiservercel.SemverType}, cel.IntType, cel.UnaryBinding(semverMinor)),
},
"patch": {
cel.MemberOverload("semver_patch", []*cel.Type{apiservercel.SemverType}, cel.IntType, cel.UnaryBinding(semverPatch)),
},
}
}
func (s *semverLibType) CompileOptions() []cel.EnvOption {
// Defined in this function to avoid an initialization order problem.
semverLibraryDecls := s.declarations()
options := make([]cel.EnvOption, 0, len(semverLibraryDecls))
for name, overloads := range semverLibraryDecls {
options = append(options, cel.Function(name, overloads...))
}
return options
}
func (*semverLibType) ProgramOptions() []cel.ProgramOption {
return []cel.ProgramOption{}
}
func isSemver(arg ref.Val) ref.Val {
str, ok := arg.Value().(string)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
// Using semver/v4 here is okay because this function isn't
// used to validate the Kubernetes API. In the CEL base library
// we would have to use the regular expression from
// pkg/apis/resource/structured/namedresources/validation/validation.go.
_, err := semver.Parse(str)
if err != nil {
return types.Bool(false)
}
return types.Bool(true)
}
func stringToSemver(arg ref.Val) ref.Val {
str, ok := arg.Value().(string)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
// Using semver/v4 here is okay because this function isn't
// used to validate the Kubernetes API. In the CEL base library
// we would have to use the regular expression from
// pkg/apis/resource/structured/namedresources/validation/validation.go
// first before parsing.
v, err := semver.Parse(str)
if err != nil {
return types.WrapErr(err)
}
return apiservercel.Semver{Version: v}
}
func semverMajor(arg ref.Val) ref.Val {
v, ok := arg.Value().(semver.Version)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
return types.Int(v.Major)
}
func semverMinor(arg ref.Val) ref.Val {
v, ok := arg.Value().(semver.Version)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
return types.Int(v.Minor)
}
func semverPatch(arg ref.Val) ref.Val {
v, ok := arg.Value().(semver.Version)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
return types.Int(v.Patch)
}
func semverIsGreaterThan(arg ref.Val, other ref.Val) ref.Val {
v, ok := arg.Value().(semver.Version)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
v2, ok := other.Value().(semver.Version)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
return types.Bool(v.Compare(v2) == 1)
}
func semverIsLessThan(arg ref.Val, other ref.Val) ref.Val {
v, ok := arg.Value().(semver.Version)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
v2, ok := other.Value().(semver.Version)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
return types.Bool(v.Compare(v2) == -1)
}
func semverCompareTo(arg ref.Val, other ref.Val) ref.Val {
v, ok := arg.Value().(semver.Version)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
v2, ok := other.Value().(semver.Version)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
return types.Int(v.Compare(v2))
}

View File

@ -38,7 +38,7 @@ type testLib struct {
}
func (*testLib) LibraryName() string {
return "k8s.test"
return "kubernetes.test"
}
type TestOption func(*testLib) *testLib

View File

@ -113,7 +113,15 @@ var urlsLib = &urls{}
type urls struct{}
func (*urls) LibraryName() string {
return "k8s.urls"
return "kubernetes.urls"
}
func (*urls) Types() []*cel.Type {
return []*cel.Type{apiservercel.URLType}
}
func (*urls) declarations() map[string][]cel.FunctionOpt {
return urlLibraryDecls
}
var urlLibraryDecls = map[string][]cel.FunctionOpt{

View File

@ -48,5 +48,7 @@ const (
// MinNumberSize is the length of literal 0
MinNumberSize = 1
// MaxFormatSize is the maximum size we allow for format strings
MaxFormatSize = 64
MaxNameFormatRegexSize = 128
)

View File

@ -0,0 +1,249 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package dynamic
import (
"errors"
"fmt"
"reflect"
"strings"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits"
"google.golang.org/protobuf/types/known/structpb"
)
// ObjectType is the implementation of the Object type for use when compiling
// CEL expressions without schema information about the object.
// This is to provide CEL expressions with access to Object{} types constructors.
type ObjectType struct {
objectType *types.Type
}
func (o *ObjectType) HasTrait(trait int) bool {
return o.objectType.HasTrait(trait)
}
// TypeName returns the name of this ObjectType.
func (o *ObjectType) TypeName() string {
return o.objectType.TypeName()
}
// Val returns an instance given the fields.
func (o *ObjectType) Val(fields map[string]ref.Val) ref.Val {
return NewObjectVal(o.objectType, fields)
}
func (o *ObjectType) Type() *types.Type {
return o.objectType
}
// Field looks up the field by name.
// This is the unstructured version that allows any name as the field name.
// The returned field is of DynType type.
func (o *ObjectType) Field(name string) (*types.FieldType, bool) {
return &types.FieldType{
// for unstructured, we do not check for its type,
// use DynType for all fields.
Type: types.DynType,
IsSet: func(target any) bool {
if m, ok := target.(map[string]any); ok {
_, isSet := m[name]
return isSet
}
return false
},
GetFrom: func(target any) (any, error) {
if m, ok := target.(map[string]any); ok {
return m[name], nil
}
return nil, fmt.Errorf("cannot get field %q", name)
},
}, true
}
func (o *ObjectType) FieldNames() ([]string, bool) {
return nil, true // Field names are not known for dynamic types. All field names are allowed.
}
// NewObjectType creates a ObjectType by the given field name.
func NewObjectType(name string) *ObjectType {
return &ObjectType{
objectType: types.NewObjectType(name),
}
}
// ObjectVal is the CEL Val for an object that is constructed via the Object{} in
// CEL expressions without schema information about the object.
type ObjectVal struct {
objectType *types.Type
fields map[string]ref.Val
}
// NewObjectVal creates an ObjectVal by its ResolvedType and its fields.
func NewObjectVal(objectType *types.Type, fields map[string]ref.Val) *ObjectVal {
return &ObjectVal{
objectType: objectType,
fields: fields,
}
}
var _ ref.Val = (*ObjectVal)(nil)
var _ traits.Zeroer = (*ObjectVal)(nil)
// ConvertToNative converts the object to map[string]any.
// All nested lists are converted into []any native type.
//
// It returns an error if the target type is not map[string]any,
// or any recursive conversion fails.
func (v *ObjectVal) ConvertToNative(typeDesc reflect.Type) (any, error) {
result := make(map[string]any, len(v.fields))
for k, v := range v.fields {
converted, err := convertField(v)
if err != nil {
return nil, fmt.Errorf("fail to convert field %q: %w", k, err)
}
result[k] = converted
}
if typeDesc == reflect.TypeOf(result) {
return result, nil
}
// CEL's builtin data literal values all support conversion to structpb.Value, which
// can then be serialized to JSON. This is convenient for CEL expressions that return
// an arbitrary JSON value, such as our MutatingAdmissionPolicy JSON Patch valueExpression
// field, so we support the conversion here, for Object data literals, as well.
if typeDesc == reflect.TypeOf(&structpb.Value{}) {
return structpb.NewStruct(result)
}
return nil, fmt.Errorf("unable to convert to %v", typeDesc)
}
// ConvertToType supports type conversions between CEL value types supported by the expression language.
func (v *ObjectVal) ConvertToType(typeValue ref.Type) ref.Val {
if v.objectType.TypeName() == typeValue.TypeName() {
return v
}
if typeValue == types.TypeType {
return types.NewTypeTypeWithParam(v.objectType)
}
return types.NewErr("unsupported conversion into %v", typeValue)
}
// Equal returns true if the `other` value has the same type and content as the implementing struct.
func (v *ObjectVal) Equal(other ref.Val) ref.Val {
if rhs, ok := other.(*ObjectVal); ok {
if v.objectType.Equal(rhs.objectType) != types.True {
return types.False
}
return types.Bool(reflect.DeepEqual(v.fields, rhs.fields))
}
return types.False
}
// Type returns the TypeValue of the value.
func (v *ObjectVal) Type() ref.Type {
return types.NewObjectType(v.objectType.TypeName())
}
// Value returns its value as a map[string]any.
func (v *ObjectVal) Value() any {
var result any
var object map[string]any
result, err := v.ConvertToNative(reflect.TypeOf(object))
if err != nil {
return types.WrapErr(err)
}
return result
}
// CheckTypeNamesMatchFieldPathNames transitively checks the CEL object type names of this ObjectVal. Returns all
// found type name mismatch errors.
// Children ObjectVal types under <field> or this ObjectVal
// must have type names of the form "<ObjectVal.TypeName>.<field>", children of that type must have type names of the
// form "<ObjectVal.TypeName>.<field>.<field>" and so on.
// Intermediate maps and lists are unnamed and ignored.
func (v *ObjectVal) CheckTypeNamesMatchFieldPathNames() error {
return errors.Join(typeCheck(v, []string{v.Type().TypeName()})...)
}
func typeCheck(v ref.Val, typeNamePath []string) []error {
var errs []error
if ov, ok := v.(*ObjectVal); ok {
tn := ov.objectType.TypeName()
if strings.Join(typeNamePath, ".") != tn {
errs = append(errs, fmt.Errorf("unexpected type name %q, expected %q, which matches field name path from root Object type", tn, strings.Join(typeNamePath, ".")))
}
for k, f := range ov.fields {
errs = append(errs, typeCheck(f, append(typeNamePath, k))...)
}
}
value := v.Value()
if listOfVal, ok := value.([]ref.Val); ok {
for _, v := range listOfVal {
errs = append(errs, typeCheck(v, typeNamePath)...)
}
}
if mapOfVal, ok := value.(map[ref.Val]ref.Val); ok {
for _, v := range mapOfVal {
errs = append(errs, typeCheck(v, typeNamePath)...)
}
}
return errs
}
// IsZeroValue indicates whether the object is the zero value for the type.
// For the ObjectVal, it is zero value if and only if the fields map is empty.
func (v *ObjectVal) IsZeroValue() bool {
return len(v.fields) == 0
}
// convertField converts a referred ref.Val to its expected type.
// For objects, the expected type is map[string]any
// For lists, the expected type is []any
// For maps, the expected type is map[string]any
// For anything else, it is converted via value.Value()
//
// It will return an error if the request type is a map but the key
// is not a string.
func convertField(value ref.Val) (any, error) {
// special handling for lists, where the elements are converted with Value() instead of ConvertToNative
// to allow them to become native value of any type.
if listOfVal, ok := value.Value().([]ref.Val); ok {
var result []any
for _, v := range listOfVal {
result = append(result, v.Value())
}
return result, nil
}
// unstructured maps, as seen in annotations
// map keys must be strings
if mapOfVal, ok := value.Value().(map[ref.Val]ref.Val); ok {
result := make(map[string]any, len(mapOfVal))
for k, v := range mapOfVal {
stringKey, ok := k.Value().(string)
if !ok {
return nil, fmt.Errorf("map key %q is of type %T, not string", k, k)
}
result[stringKey] = v.Value()
}
return result, nil
}
return value.Value(), nil
}

185
vendor/k8s.io/apiserver/pkg/cel/mutation/jsonpatch.go generated vendored Normal file
View File

@ -0,0 +1,185 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mutation
import (
"fmt"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"reflect"
)
var jsonPatchType = types.NewObjectType(JSONPatchTypeName)
var (
jsonPatchOp = "op"
jsonPatchPath = "path"
jsonPatchFrom = "from"
jsonPatchValue = "value"
)
// JSONPatchType and JSONPatchVal are defined entirely from scratch here because JSONPatchVal
// has a dynamic 'value' field which can not be defined with an OpenAPI schema,
// preventing us from using DeclType and UnstructuredToVal.
// JSONPatchType provides a CEL type for "JSONPatch" operations.
type JSONPatchType struct{}
func (r *JSONPatchType) HasTrait(trait int) bool {
return jsonPatchType.HasTrait(trait)
}
// TypeName returns the name of this ObjectType.
func (r *JSONPatchType) TypeName() string {
return jsonPatchType.TypeName()
}
// Val returns an instance given the fields.
func (r *JSONPatchType) Val(fields map[string]ref.Val) ref.Val {
result := &JSONPatchVal{}
for name, value := range fields {
switch name {
case jsonPatchOp:
if s, ok := value.Value().(string); ok {
result.Op = s
} else {
return types.NewErr("unexpected type %T for JSONPatchType 'op' field", value.Value())
}
case jsonPatchPath:
if s, ok := value.Value().(string); ok {
result.Path = s
} else {
return types.NewErr("unexpected type %T for JSONPatchType 'path' field", value.Value())
}
case jsonPatchFrom:
if s, ok := value.Value().(string); ok {
result.From = s
} else {
return types.NewErr("unexpected type %T for JSONPatchType 'from' field", value.Value())
}
case jsonPatchValue:
result.Val = value
default:
return types.NewErr("unexpected JSONPatchType field: %s", name)
}
}
return result
}
func (r *JSONPatchType) Type() *types.Type {
return jsonPatchType
}
func (r *JSONPatchType) Field(name string) (*types.FieldType, bool) {
var fieldType *types.Type
switch name {
case jsonPatchOp, jsonPatchFrom, jsonPatchPath:
fieldType = cel.StringType
case jsonPatchValue:
fieldType = types.DynType
}
return &types.FieldType{
Type: fieldType,
}, true
}
func (r *JSONPatchType) FieldNames() ([]string, bool) {
return []string{jsonPatchOp, jsonPatchFrom, jsonPatchPath, jsonPatchValue}, true
}
// JSONPatchVal is the ref.Val for a JSONPatch.
type JSONPatchVal struct {
Op, From, Path string
Val ref.Val
}
func (p *JSONPatchVal) ConvertToNative(typeDesc reflect.Type) (any, error) {
if typeDesc == reflect.TypeOf(&JSONPatchVal{}) {
return p, nil
}
return nil, fmt.Errorf("cannot convert to native type: %v", typeDesc)
}
func (p *JSONPatchVal) ConvertToType(typeValue ref.Type) ref.Val {
if typeValue == jsonPatchType {
return p
} else if typeValue == types.TypeType {
return types.NewTypeTypeWithParam(jsonPatchType)
}
return types.NewErr("unsupported type: %s", typeValue.TypeName())
}
func (p *JSONPatchVal) Equal(other ref.Val) ref.Val {
if o, ok := other.(*JSONPatchVal); ok && p != nil && o != nil {
if p.Op != o.Op || p.From != o.From || p.Path != o.Path {
return types.False
}
if (p.Val == nil) != (o.Val == nil) {
return types.False
}
if p.Val == nil {
return types.True
}
return p.Val.Equal(o.Val)
}
return types.False
}
func (p *JSONPatchVal) Get(index ref.Val) ref.Val {
if name, ok := index.Value().(string); ok {
switch name {
case jsonPatchOp:
return types.String(p.Op)
case jsonPatchPath:
return types.String(p.Path)
case jsonPatchFrom:
return types.String(p.From)
case jsonPatchValue:
return p.Val
default:
}
}
return types.NewErr("unsupported indexer: %s", index)
}
func (p *JSONPatchVal) IsSet(field ref.Val) ref.Val {
if name, ok := field.Value().(string); ok {
switch name {
case jsonPatchOp:
return types.Bool(len(p.Op) > 0)
case jsonPatchPath:
return types.Bool(len(p.Path) > 0)
case jsonPatchFrom:
return types.Bool(len(p.From) > 0)
case jsonPatchValue:
return types.Bool(p.Val != nil)
}
}
return types.NewErr("unsupported field: %s", field)
}
func (p *JSONPatchVal) Type() ref.Type {
return jsonPatchType
}
func (p *JSONPatchVal) Value() any {
return p
}
var _ ref.Val = &JSONPatchVal{}

View File

@ -0,0 +1,47 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mutation
import (
"strings"
"k8s.io/apiserver/pkg/cel/common"
"k8s.io/apiserver/pkg/cel/mutation/dynamic"
)
// ObjectTypeName is the name of Object types that are used to declare the types of
// Kubernetes objects in CEL dynamically using the naming scheme "Object.<fieldName>...<fieldName>".
// For example "Object.spec.containers" is the type of the spec.containers field of the object in scope.
const ObjectTypeName = "Object"
// JSONPatchTypeName is the name of the JSONPatch type. This type is typically used to create JSON patches
// in CEL expressions.
const JSONPatchTypeName = "JSONPatch"
// DynamicTypeResolver resolves the Object and JSONPatch types when compiling
// CEL expressions without schema information about the object.
type DynamicTypeResolver struct{}
func (r *DynamicTypeResolver) Resolve(name string) (common.ResolvedType, bool) {
if name == JSONPatchTypeName {
return &JSONPatchType{}, true
}
if name == ObjectTypeName || strings.HasPrefix(name, ObjectTypeName+".") {
return dynamic.NewObjectType(name), true
}
return nil, false
}

73
vendor/k8s.io/apiserver/pkg/cel/semver.go generated vendored Normal file
View File

@ -0,0 +1,73 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cel
import (
"fmt"
"reflect"
"github.com/blang/semver/v4"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
)
var (
SemverType = cel.ObjectType("kubernetes.Semver")
)
// Semver provdes a CEL representation of a [semver.Version].
type Semver struct {
semver.Version
}
func (v Semver) ConvertToNative(typeDesc reflect.Type) (interface{}, error) {
if reflect.TypeOf(v.Version).AssignableTo(typeDesc) {
return v.Version, nil
}
if reflect.TypeOf("").AssignableTo(typeDesc) {
return v.Version.String(), nil
}
return nil, fmt.Errorf("type conversion error from 'Semver' to '%v'", typeDesc)
}
func (v Semver) ConvertToType(typeVal ref.Type) ref.Val {
switch typeVal {
case SemverType:
return v
case types.TypeType:
return SemverType
default:
return types.NewErr("type conversion error from '%s' to '%s'", SemverType, typeVal)
}
}
func (v Semver) Equal(other ref.Val) ref.Val {
otherDur, ok := other.(Semver)
if !ok {
return types.MaybeNoSuchOverloadErr(other)
}
return types.Bool(v.Version.EQ(otherDur.Version))
}
func (v Semver) Type() ref.Type {
return SemverType
}
func (v Semver) Value() interface{} {
return v.Version
}

View File

@ -429,7 +429,7 @@ func (rt *DeclTypeProvider) FindStructType(typeName string) (*types.Type, bool)
declType, found := rt.findDeclType(typeName)
if found {
expT := declType.CelType()
return expT, found
return types.NewTypeTypeWithParam(expT), found
}
return rt.typeProvider.FindStructType(typeName)
}