rebase: update kubernetes to 1.28.0 in main

updating kubernetes to 1.28.0
in the main repo.

Signed-off-by: Madhu Rajanna <madhupr007@gmail.com>
This commit is contained in:
Madhu Rajanna
2023-08-17 07:15:28 +02:00
committed by mergify[bot]
parent b2fdc269c3
commit ff3e84ad67
706 changed files with 45252 additions and 16346 deletions

View File

@ -1,119 +0,0 @@
/*
Copyright 2023 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 (
"github.com/google/cel-go/common/types/ref"
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
)
var _ ref.TypeProvider = (*CompositedTypeProvider)(nil)
var _ ref.TypeAdapter = (*CompositedTypeAdapter)(nil)
// CompositedTypeProvider is the provider that tries each of the underlying
// providers in order, and returns result of the first successful attempt.
type CompositedTypeProvider struct {
// Providers contains the underlying type providers.
// If Providers is empty, the CompositedTypeProvider becomes no-op provider.
Providers []ref.TypeProvider
}
// EnumValue finds out the numeric value of the given enum name.
// The result comes from first provider that returns non-nil.
func (c *CompositedTypeProvider) EnumValue(enumName string) ref.Val {
for _, p := range c.Providers {
val := p.EnumValue(enumName)
if val != nil {
return val
}
}
return nil
}
// FindIdent takes a qualified identifier name and returns a Value if one
// exists. The result comes from first provider that returns non-nil.
func (c *CompositedTypeProvider) FindIdent(identName string) (ref.Val, bool) {
for _, p := range c.Providers {
val, ok := p.FindIdent(identName)
if ok {
return val, ok
}
}
return nil, false
}
// FindType finds the Type given a qualified type name, or return false
// if none of the providers finds the type.
// If any of the providers find the type, the first provider that returns true
// will be the result.
func (c *CompositedTypeProvider) FindType(typeName string) (*exprpb.Type, bool) {
for _, p := range c.Providers {
typ, ok := p.FindType(typeName)
if ok {
return typ, ok
}
}
return nil, false
}
// FindFieldType returns the field type for a checked type value. Returns
// false if none of the providers can find the type.
// If multiple providers can find the field, the result is taken from
// the first that does.
func (c *CompositedTypeProvider) FindFieldType(messageType string, fieldName string) (*ref.FieldType, bool) {
for _, p := range c.Providers {
ft, ok := p.FindFieldType(messageType, fieldName)
if ok {
return ft, ok
}
}
return nil, false
}
// NewValue creates a new type value from a qualified name and map of field
// name to value.
// If multiple providers can create the new type, the first that returns
// non-nil will decide the result.
func (c *CompositedTypeProvider) NewValue(typeName string, fields map[string]ref.Val) ref.Val {
for _, p := range c.Providers {
v := p.NewValue(typeName, fields)
if v != nil {
return v
}
}
return nil
}
// CompositedTypeAdapter is the adapter that tries each of the underlying
// type adapter in order until the first successfully conversion.
type CompositedTypeAdapter struct {
// Adapters contains underlying type adapters.
// If Adapters is empty, the CompositedTypeAdapter becomes a no-op adapter.
Adapters []ref.TypeAdapter
}
// NativeToValue takes the value and convert it into a ref.Val
// The result comes from the first TypeAdapter that returns non-nil.
func (c *CompositedTypeAdapter) NativeToValue(value interface{}) ref.Val {
for _, a := range c.Adapters {
v := a.NativeToValue(value)
if v != nil {
return v
}
}
return nil
}

119
vendor/k8s.io/apiserver/pkg/cel/environment/base.go generated vendored Normal file
View File

@ -0,0 +1,119 @@
/*
Copyright 2023 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 environment
import (
"fmt"
"strconv"
"sync"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/ext"
"golang.org/x/sync/singleflight"
"k8s.io/apimachinery/pkg/util/version"
celconfig "k8s.io/apiserver/pkg/apis/cel"
"k8s.io/apiserver/pkg/cel/library"
)
// DefaultCompatibilityVersion returns a default compatibility version for use with EnvSet
// that guarantees compatibility with CEL features/libraries/parameters understood by
// an n-1 version
//
// This default will be set to no more than n-1 the current Kubernetes major.minor version.
//
// Note that a default version number less than n-1 indicates a wider range of version
// compatibility than strictly required for rollback. A wide range of compatibility is
// desirable because it means that CEL expressions are portable across a wider range
// of Kubernetes versions.
func DefaultCompatibilityVersion() *version.Version {
return version.MajorMinor(1, 27)
}
var baseOpts = []VersionedOptions{
{
// CEL epoch was actually 1.23, but we artificially set it to 1.0 because these
// options should always be present.
IntroducedVersion: version.MajorMinor(1, 0),
EnvOptions: []cel.EnvOption{
cel.HomogeneousAggregateLiterals(),
// Validate function declarations once during base env initialization,
// so they don't need to be evaluated each time a CEL rule is compiled.
// This is a relatively expensive operation.
cel.EagerlyValidateDeclarations(true),
cel.DefaultUTCTimeZone(true),
ext.Strings(ext.StringsVersion(0)),
library.URLs(),
library.Regex(),
library.Lists(),
},
ProgramOptions: []cel.ProgramOption{
cel.EvalOptions(cel.OptOptimize, cel.OptTrackCost),
cel.CostLimit(celconfig.PerCallLimit),
},
},
{
IntroducedVersion: version.MajorMinor(1, 27),
EnvOptions: []cel.EnvOption{
library.Authz(),
},
},
{
IntroducedVersion: version.MajorMinor(1, 28),
EnvOptions: []cel.EnvOption{
cel.CrossTypeNumericComparisons(true),
cel.OptionalTypes(),
library.Quantity(),
},
},
// TODO: switch to ext.Strings version 2 once format() is fixed to work with HomogeneousAggregateLiterals.
}
// 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.
//
// The returned environment contains function libraries, language settings, optimizations and
// runtime cost limits appropriate CEL as it is used in Kubernetes.
//
// The returned environment contains no CEL variable definitions or custom type declarations and
// should be extended to construct environments with the appropriate variable definitions,
// type declarations and any other needed configuration.
func MustBaseEnvSet(ver *version.Version) *EnvSet {
if ver == nil {
panic("version must be non-nil")
}
if len(ver.Components()) < 2 {
panic(fmt.Sprintf("version must contain an major and minor component, but got: %s", ver.String()))
}
key := strconv.FormatUint(uint64(ver.Major()), 10) + "." + strconv.FormatUint(uint64(ver.Minor()), 10)
if entry, ok := baseEnvs.Load(key); ok {
return entry.(*EnvSet)
}
entry, _, _ := baseEnvsSingleflight.Do(key, func() (interface{}, error) {
entry := mustNewEnvSet(ver, baseOpts)
baseEnvs.Store(key, entry)
return entry, nil
})
return entry.(*EnvSet)
}
var (
baseEnvs = sync.Map{}
baseEnvsSingleflight = &singleflight.Group{}
)

View File

@ -0,0 +1,274 @@
/*
Copyright 2023 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 environment
import (
"fmt"
"math"
"github.com/google/cel-go/cel"
"k8s.io/apimachinery/pkg/util/version"
apiservercel "k8s.io/apiserver/pkg/cel"
)
// Type defines the different types of CEL environments used in Kubernetes.
// CEL environments are used to compile and evaluate CEL expressions.
// Environments include:
// - Function libraries
// - Variables
// - Types (both core CEL types and Kubernetes types)
// - Other CEL environment and program options
type Type string
const (
// NewExpressions is used to validate new or modified expressions in
// requests that write expressions to API resources.
//
// This environment type is compatible with a specific Kubernetes
// major/minor version. To ensure safe rollback, this environment type
// may not include all the function libraries, variables, type declarations, and CEL
// language settings available in the StoredExpressions environment type.
//
// NewExpressions must be used to validate (parse, compile, type check)
// all new or modified CEL expressions before they are written to storage.
NewExpressions Type = "NewExpressions"
// StoredExpressions is used to compile and run CEL expressions that have been
// persisted to storage.
//
// This environment type is compatible with CEL expressions that have been
// persisted to storage by all known versions of Kubernetes. This is the most
// permissive environment available.
//
// StoredExpressions is appropriate for use with CEL expressions in
// configuration files.
StoredExpressions Type = "StoredExpressions"
)
// EnvSet manages the creation and extension of CEL environments. Each EnvSet contains
// both an NewExpressions and StoredExpressions environment. EnvSets are created
// and extended using VersionedOptions so that the EnvSet can prepare environments according
// to what options were introduced at which versions.
//
// Each EnvSet is given a compatibility version when it is created, and prepares the
// NewExpressions environment to be compatible with that version. The EnvSet also
// prepares StoredExpressions to be compatible with all known versions of Kubernetes.
type EnvSet struct {
// compatibilityVersion is the version that all configuration in
// the NewExpressions environment is compatible with.
compatibilityVersion *version.Version
// newExpressions is an environment containing only configuration
// in this EnvSet that is enabled at this compatibilityVersion.
newExpressions *cel.Env
// storedExpressions is an environment containing the latest configuration
// in this EnvSet.
storedExpressions *cel.Env
}
func newEnvSet(compatibilityVersion *version.Version, opts []VersionedOptions) (*EnvSet, error) {
base, err := cel.NewEnv()
if err != nil {
return nil, err
}
baseSet := EnvSet{compatibilityVersion: compatibilityVersion, newExpressions: base, storedExpressions: base}
return baseSet.Extend(opts...)
}
func mustNewEnvSet(ver *version.Version, opts []VersionedOptions) *EnvSet {
envSet, err := newEnvSet(ver, opts)
if err != nil {
panic(fmt.Sprintf("Default environment misconfigured: %v", err))
}
return envSet
}
// NewExpressionsEnv returns the NewExpressions environment Type for this EnvSet.
// See NewExpressions for details.
func (e *EnvSet) NewExpressionsEnv() *cel.Env {
return e.newExpressions
}
// StoredExpressionsEnv returns the StoredExpressions environment Type for this EnvSet.
// See StoredExpressions for details.
func (e *EnvSet) StoredExpressionsEnv() *cel.Env {
return e.storedExpressions
}
// Env returns the CEL environment for the given Type.
func (e *EnvSet) Env(envType Type) (*cel.Env, error) {
switch envType {
case NewExpressions:
return e.newExpressions, nil
case StoredExpressions:
return e.storedExpressions, nil
default:
return nil, fmt.Errorf("unsupported environment type: %v", envType)
}
}
// VersionedOptions provides a set of CEL configuration options as well as the version the
// options were introduced and, optionally, the version the options were removed.
type VersionedOptions struct {
// IntroducedVersion is the version at which these options were introduced.
// The NewExpressions environment will only include options introduced at or before the
// compatibility version of the EnvSet.
//
// For example, to configure a CEL environment with an "object" variable bound to a
// resource kind, first create a DeclType from the groupVersionKind of the resource and then
// populate a VersionedOptions with the variable and the type:
//
// schema := schemaResolver.ResolveSchema(groupVersionKind)
// objectType := apiservercel.SchemaDeclType(schema, true)
// ...
// VersionOptions{
// IntroducedVersion: version.MajorMinor(1, 26),
// DeclTypes: []*apiservercel.DeclType{ objectType },
// EnvOptions: []cel.EnvOption{ cel.Variable("object", objectType.CelType()) },
// },
//
// To create an DeclType from a CRD, use a structural schema. For example:
//
// schema := structuralschema.NewStructural(crdJSONProps)
// objectType := apiservercel.SchemaDeclType(schema, true)
//
// Required.
IntroducedVersion *version.Version
// RemovedVersion is the version at which these options were removed.
// The NewExpressions environment will not include options removed at or before the
// compatibility version of the EnvSet.
//
// All option removals must be backward compatible; the removal must either be paired
// with a compatible replacement introduced at the same version, or the removal must be non-breaking.
// The StoredExpressions environment will not include removed options.
//
// A function library may be upgraded by setting the RemovedVersion of the old library
// to the same value as the IntroducedVersion of the new library. The new library must
// be backward compatible with the old library.
//
// For example:
//
// VersionOptions{
// IntroducedVersion: version.MajorMinor(1, 26), RemovedVersion: version.MajorMinor(1, 27),
// EnvOptions: []cel.EnvOption{ libraries.Example(libraries.ExampleVersion(1)) },
// },
// VersionOptions{
// IntroducedVersion: version.MajorMinor(1, 27),
// EnvOptions: []EnvOptions{ libraries.Example(libraries.ExampleVersion(2)) },
// },
//
// Optional.
RemovedVersion *version.Version
// EnvOptions provides CEL EnvOptions. This may be used to add a cel.Variable, a
// cel.Library, or to enable other CEL EnvOptions such as language settings.
//
// If an added cel.Variable has an OpenAPI type, the type must be included in DeclTypes.
EnvOptions []cel.EnvOption
// ProgramOptions provides CEL ProgramOptions. This may be used to set a cel.CostLimit,
// enable optimizations, and set other program level options that should be enabled
// for all programs using this environment.
ProgramOptions []cel.ProgramOption
// DeclTypes provides OpenAPI type declarations to register with the environment.
//
// If cel.Variables added to EnvOptions refer to a OpenAPI type, the type must be included in
// DeclTypes.
DeclTypes []*apiservercel.DeclType
}
// Extend returns an EnvSet based on this EnvSet but extended with given VersionedOptions.
// This EnvSet is not mutated.
// The returned EnvSet has the same compatibility version as the EnvSet that was extended.
//
// Extend is an expensive operation and each call to Extend that adds DeclTypes increases
// the depth of a chain of resolvers. For these reasons, calls to Extend should be kept
// to a minimum.
//
// Some best practices:
//
// - Minimize calls Extend when handling API requests. Where possible, call Extend
// when initializing components.
// - If an EnvSets returned by Extend can be used to compile multiple CEL programs,
// call Extend once and reuse the returned EnvSets.
// - Prefer a single call to Extend with a full list of VersionedOptions over
// making multiple calls to Extend.
func (e *EnvSet) Extend(options ...VersionedOptions) (*EnvSet, error) {
if len(options) > 0 {
newExprOpts, err := e.filterAndBuildOpts(e.newExpressions, e.compatibilityVersion, options)
if err != nil {
return nil, err
}
p, err := e.newExpressions.Extend(newExprOpts)
if err != nil {
return nil, err
}
storedExprOpt, err := e.filterAndBuildOpts(e.storedExpressions, version.MajorMinor(math.MaxUint, math.MaxUint), options)
if err != nil {
return nil, err
}
s, err := e.storedExpressions.Extend(storedExprOpt)
if err != nil {
return nil, err
}
return &EnvSet{compatibilityVersion: e.compatibilityVersion, newExpressions: p, storedExpressions: s}, nil
}
return e, nil
}
func (e *EnvSet) filterAndBuildOpts(base *cel.Env, compatVer *version.Version, opts []VersionedOptions) (cel.EnvOption, error) {
var envOpts []cel.EnvOption
var progOpts []cel.ProgramOption
var declTypes []*apiservercel.DeclType
for _, opt := range opts {
if compatVer.AtLeast(opt.IntroducedVersion) && (opt.RemovedVersion == nil || compatVer.LessThan(opt.RemovedVersion)) {
envOpts = append(envOpts, opt.EnvOptions...)
progOpts = append(progOpts, opt.ProgramOptions...)
declTypes = append(declTypes, opt.DeclTypes...)
}
}
if len(declTypes) > 0 {
provider := apiservercel.NewDeclTypeProvider(declTypes...)
providerOpts, err := provider.EnvOptions(base.TypeProvider())
if err != nil {
return nil, err
}
envOpts = append(envOpts, providerOpts...)
}
combined := cel.Lib(&envLoader{
envOpts: envOpts,
progOpts: progOpts,
})
return combined, nil
}
type envLoader struct {
envOpts []cel.EnvOption
progOpts []cel.ProgramOption
}
func (e *envLoader) CompileOptions() []cel.EnvOption {
return e.envOpts
}
func (e *envLoader) ProgramOptions() []cel.ProgramOption {
return e.progOpts
}

191
vendor/k8s.io/apiserver/pkg/cel/lazy/lazy.go generated vendored Normal file
View File

@ -0,0 +1,191 @@
/*
Copyright 2023 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 lazy
import (
"fmt"
"reflect"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits"
"k8s.io/apiserver/pkg/cel"
)
type GetFieldFunc func(*MapValue) ref.Val
var _ ref.Val = (*MapValue)(nil)
var _ traits.Mapper = (*MapValue)(nil)
// MapValue is a map that lazily evaluate its value when a field is first accessed.
// The map value is not designed to be thread-safe.
type MapValue struct {
typeValue *types.TypeValue
// values are previously evaluated values obtained from callbacks
values map[string]ref.Val
// callbacks are a map of field name to the function that returns the field Val
callbacks map[string]GetFieldFunc
// knownValues are registered names, used for iteration
knownValues []string
}
func NewMapValue(objectType ref.Type) *MapValue {
return &MapValue{
typeValue: types.NewTypeValue(objectType.TypeName(), traits.IndexerType|traits.FieldTesterType|traits.IterableType),
values: map[string]ref.Val{},
callbacks: map[string]GetFieldFunc{},
}
}
// Append adds the given field with its name and callback.
func (m *MapValue) Append(name string, callback GetFieldFunc) {
m.knownValues = append(m.knownValues, name)
m.callbacks[name] = callback
}
// Contains checks if the key is known to the map
func (m *MapValue) Contains(key ref.Val) ref.Val {
v, found := m.Find(key)
if v != nil && types.IsUnknownOrError(v) {
return v
}
return types.Bool(found)
}
// Iterator returns an iterator to traverse the map.
func (m *MapValue) Iterator() traits.Iterator {
return &iterator{parent: m, index: 0}
}
// Size returns the number of currently known fields
func (m *MapValue) Size() ref.Val {
return types.Int(len(m.callbacks))
}
// ConvertToNative returns an error because it is disallowed
func (m *MapValue) ConvertToNative(typeDesc reflect.Type) (any, error) {
return nil, fmt.Errorf("disallowed conversion from %q to %q", m.typeValue.TypeName(), typeDesc.Name())
}
// ConvertToType converts the map to the given type.
// Only its own type and "Type" type are allowed.
func (m *MapValue) ConvertToType(typeVal ref.Type) ref.Val {
switch typeVal {
case m.typeValue:
return m
case types.TypeType:
return m.typeValue
}
return types.NewErr("disallowed conversion from %q to %q", m.typeValue.TypeName(), typeVal.TypeName())
}
// Equal returns true if the other object is the same pointer-wise.
func (m *MapValue) Equal(other ref.Val) ref.Val {
otherMap, ok := other.(*MapValue)
if !ok {
return types.MaybeNoSuchOverloadErr(other)
}
return types.Bool(m == otherMap)
}
// Type returns its registered type.
func (m *MapValue) Type() ref.Type {
return m.typeValue
}
// Value is not allowed.
func (m *MapValue) Value() any {
return types.NoSuchOverloadErr()
}
// resolveField resolves the field. Calls the callback if the value is not yet stored.
func (m *MapValue) resolveField(name string) ref.Val {
v, seen := m.values[name]
if seen {
return v
}
f := m.callbacks[name]
v = f(m)
m.values[name] = v
return v
}
func (m *MapValue) Find(key ref.Val) (ref.Val, bool) {
n, ok := key.(types.String)
if !ok {
return types.MaybeNoSuchOverloadErr(n), true
}
name, ok := cel.Unescape(n.Value().(string))
if !ok {
return nil, false
}
if _, exists := m.callbacks[name]; !exists {
return nil, false
}
return m.resolveField(name), true
}
func (m *MapValue) Get(key ref.Val) ref.Val {
v, found := m.Find(key)
if found {
return v
}
return types.ValOrErr(key, "no such key: %v", key)
}
type iterator struct {
parent *MapValue
index int
}
func (i *iterator) ConvertToNative(typeDesc reflect.Type) (any, error) {
return nil, fmt.Errorf("disallowed conversion to %q", typeDesc.Name())
}
func (i *iterator) ConvertToType(typeValue ref.Type) ref.Val {
return types.NewErr("disallowed conversion o %q", typeValue.TypeName())
}
func (i *iterator) Equal(other ref.Val) ref.Val {
otherIterator, ok := other.(*iterator)
if !ok {
return types.MaybeNoSuchOverloadErr(other)
}
return types.Bool(otherIterator == i)
}
func (i *iterator) Type() ref.Type {
return types.IteratorType
}
func (i *iterator) Value() any {
return nil
}
func (i *iterator) HasNext() ref.Val {
return types.Bool(i.index < len(i.parent.knownValues))
}
func (i *iterator) Next() ref.Val {
ret := i.parent.Get(types.String(i.parent.knownValues[i.index]))
i.index++
return ret
}
var _ traits.Iterator = (*iterator)(nil)

View File

@ -174,6 +174,26 @@ import (
// Examples:
//
// authorizer.path('/healthz').check('GET').reason()
//
// errored
//
// Returns true if the authorization check resulted in an error.
//
// <Decision>.errored() <bool>
//
// Examples:
//
// authorizer.group('').resource('pods').namespace('default').check('create').errored() // Returns true if the authorization check resulted in an error
//
// error
//
// If the authorization check resulted in an error, returns the error. Otherwise, returns the empty string.
//
// <Decision>.error() <string>
//
// Examples:
//
// authorizer.group('').resource('pods').namespace('default').check('create').error()
func Authz() cel.EnvOption {
return cel.Lib(authzLib)
}
@ -209,6 +229,12 @@ var authzLibraryDecls = map[string][]cel.FunctionOpt{
cel.BinaryBinding(pathCheckCheck)),
cel.MemberOverload("resourcecheck_check", []*cel.Type{ResourceCheckType, cel.StringType}, DecisionType,
cel.BinaryBinding(resourceCheckCheck))},
"errored": {
cel.MemberOverload("decision_errored", []*cel.Type{DecisionType}, cel.BoolType,
cel.UnaryBinding(decisionErrored))},
"error": {
cel.MemberOverload("decision_error", []*cel.Type{DecisionType}, cel.StringType,
cel.UnaryBinding(decisionError))},
"allowed": {
cel.MemberOverload("decision_allowed", []*cel.Type{DecisionType}, cel.BoolType,
cel.UnaryBinding(decisionAllowed))},
@ -384,6 +410,27 @@ func resourceCheckCheck(arg1, arg2 ref.Val) ref.Val {
return resourceCheck.Authorize(context.TODO(), apiVerb)
}
func decisionErrored(arg ref.Val) ref.Val {
decision, ok := arg.(decisionVal)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
return types.Bool(decision.err != nil)
}
func decisionError(arg ref.Val) ref.Val {
decision, ok := arg.(decisionVal)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
if decision.err == nil {
return types.String("")
}
return types.String(decision.err.Error())
}
func decisionAllowed(arg ref.Val) ref.Val {
decision, ok := arg.(decisionVal)
if !ok {
@ -478,10 +525,7 @@ func (a pathCheckVal) Authorize(ctx context.Context, verb string) ref.Val {
}
decision, reason, err := a.authorizer.authAuthorizer.Authorize(ctx, attr)
if err != nil {
return types.NewErr("error in authorization check: %v", err)
}
return newDecision(decision, reason)
return newDecision(decision, err, reason)
}
type groupCheckVal struct {
@ -516,18 +560,16 @@ func (a resourceCheckVal) Authorize(ctx context.Context, verb string) ref.Val {
User: a.groupCheck.authorizer.userInfo,
}
decision, reason, err := a.groupCheck.authorizer.authAuthorizer.Authorize(ctx, attr)
if err != nil {
return types.NewErr("error in authorization check: %v", err)
}
return newDecision(decision, reason)
return newDecision(decision, err, reason)
}
func newDecision(authDecision authorizer.Decision, reason string) decisionVal {
return decisionVal{receiverOnlyObjectVal: receiverOnlyVal(DecisionType), authDecision: authDecision, reason: reason}
func newDecision(authDecision authorizer.Decision, err error, reason string) decisionVal {
return decisionVal{receiverOnlyObjectVal: receiverOnlyVal(DecisionType), authDecision: authDecision, err: err, reason: reason}
}
type decisionVal struct {
receiverOnlyObjectVal
err error
authDecision authorizer.Decision
reason string
}

View File

@ -41,7 +41,7 @@ func (l *CostEstimator) CallCost(function, overloadId string, args []ref.Val, re
// This cost is set to allow for only two authorization checks per expression
cost := uint64(350000)
return &cost
case "serviceAccount", "path", "group", "resource", "subresource", "namespace", "name", "allowed", "denied", "reason":
case "serviceAccount", "path", "group", "resource", "subresource", "namespace", "name", "allowed", "reason", "error", "errored":
// All authorization builder and accessor functions have a nominal cost
cost := uint64(1)
return &cost
@ -91,7 +91,7 @@ func (l *CostEstimator) EstimateCallCost(function, overloadId string, target *ch
// An authorization check has a fixed cost
// This cost is set to allow for only two authorization checks per expression
return &checker.CallEstimate{CostEstimate: checker.CostEstimate{Min: 350000, Max: 350000}}
case "serviceAccount", "path", "group", "resource", "subresource", "namespace", "name", "allowed", "denied", "reason":
case "serviceAccount", "path", "group", "resource", "subresource", "namespace", "name", "allowed", "reason", "error", "errored":
// All authorization builder and accessor functions have a nominal cost
return &checker.CallEstimate{CostEstimate: checker.CostEstimate{Min: 1, Max: 1}}
case "isSorted", "sum", "max", "min", "indexOf", "lastIndexOf":

View File

@ -1,35 +0,0 @@
/*
Copyright 2022 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/ext"
"github.com/google/cel-go/interpreter"
)
// ExtensionLibs declares the set of CEL extension libraries available everywhere CEL is used in Kubernetes.
var ExtensionLibs = append(k8sExtensionLibs, ext.Strings())
var k8sExtensionLibs = []cel.EnvOption{
URLs(),
Regex(),
Lists(),
Authz(),
}
var ExtensionLibRegexOptimizations = []*interpreter.RegexOptimization{FindRegexOptimization, FindAllRegexOptimization}

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

@ -0,0 +1,375 @@
/*
Copyright 2023 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 (
"errors"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"k8s.io/apimachinery/pkg/api/resource"
apiservercel "k8s.io/apiserver/pkg/cel"
)
// Quantity provides a CEL function library extension of Kubernetes
// resource.Quantity parsing functions. See `resource.Quantity`
// documentation for more detailed information about the format itself:
// https://pkg.go.dev/k8s.io/apimachinery/pkg/api/resource#Quantity
//
// quantity
//
// Converts a string to a Quantity or results in an error if the string is not a valid Quantity. Refer
// to resource.Quantity documentation for information on accepted patterns.
//
// quantity(<string>) <Quantity>
//
// Examples:
//
// quantity('1.5G') // returns a Quantity
// quantity('200k') // returns a Quantity
// quantity('200K') // error
// quantity('Three') // error
// quantity('Mi') // error
//
// isQuantity
//
// Returns true if a string is a valid Quantity. isQuantity returns true if and
// only if quantity does not result in error.
//
// isQuantity( <string>) <bool>
//
// Examples:
//
// isQuantity('1.3G') // returns true
// isQuantity('1.3Gi') // returns true
// isQuantity('1,3G') // returns false
// isQuantity('10000k') // returns true
// isQuantity('200K') // returns false
// isQuantity('Three') // returns false
// isQuantity('Mi') // returns false
//
// Conversion to Scalars:
//
// - isInteger: returns true if and only if asInteger is safe to call without an error
//
// - asInteger: returns a representation of the current value as an int64 if
// possible or results in an error if conversion would result in overflow
// or loss of precision.
//
// - asApproximateFloat: returns a float64 representation of the quantity which may
// lose precision. If the value of the quantity is outside the range of a float64
// +Inf/-Inf will be returned.
//
// <Quantity>.isInteger() <bool>
// <Quantity>.asInteger() <int>
// <Quantity>.asApproximateFloat() <float>
//
// Examples:
//
// quantity("50000000G").isInteger() // returns true
// quantity("50k").isInteger() // returns true
// quantity("9999999999999999999999999999999999999G").asInteger() // error: cannot convert value to integer
// quantity("9999999999999999999999999999999999999G").isInteger() // returns false
// quantity("50k").asInteger() == 50000 // returns true
// quantity("50k").sub(20000).asApproximateFloat() == 30000 // returns true
//
// Arithmetic
//
// - sign: Returns `1` if the quantity is positive, `-1` if it is negative. `0` if it is zero
//
// - add: Returns sum of two quantities or a quantity and an integer
//
// - sub: Returns difference between two quantities or a quantity and an integer
//
// <Quantity>.sign() <int>
// <Quantity>.add(<quantity>) <quantity>
// <Quantity>.add(<integer>) <quantity>
// <Quantity>.sub(<quantity>) <quantity>
// <Quantity>.sub(<integer>) <quantity>
//
// Examples:
//
// quantity("50k").add("20k") == quantity("70k") // returns true
// quantity("50k").add(20) == quantity("50020") // returns true
// quantity("50k").sub("20k") == quantity("30k") // returns true
// quantity("50k").sub(20000) == quantity("30k") // returns true
// quantity("50k").add(20).sub(quantity("100k")).sub(-50000) == quantity("20") // returns true
//
// 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
//
//
// <Quantity>.isLessThan(<quantity>) <bool>
// <Quantity>.isGreaterThan(<quantity>) <bool>
// <Quantity>.compareTo(<quantity>) <int>
//
// Examples:
//
// quantity("200M").compareTo(quantity("0.2G")) // returns 0
// quantity("50M").compareTo(quantity("50Mi")) // returns -1
// quantity("50Mi").compareTo(quantity("50M")) // returns 1
// quantity("150Mi").isGreaterThan(quantity("100Mi")) // returns true
// quantity("50Mi").isGreaterThan(quantity("100Mi")) // returns false
// quantity("50M").isLessThan(quantity("100M")) // returns true
// quantity("100M").isLessThan(quantity("50M")) // returns false
func Quantity() cel.EnvOption {
return cel.Lib(quantityLib)
}
var quantityLib = &quantity{}
type quantity struct{}
var quantityLibraryDecls = map[string][]cel.FunctionOpt{
"quantity": {
cel.Overload("string_to_quantity", []*cel.Type{cel.StringType}, apiservercel.QuantityType, cel.UnaryBinding((stringToQuantity))),
},
"isQuantity": {
cel.Overload("is_quantity_string", []*cel.Type{cel.StringType}, cel.BoolType, cel.UnaryBinding(isQuantity)),
},
"sign": {
cel.Overload("quantity_sign", []*cel.Type{apiservercel.QuantityType}, cel.IntType, cel.UnaryBinding(quantityGetSign)),
},
"isGreaterThan": {
cel.MemberOverload("quantity_is_greater_than", []*cel.Type{apiservercel.QuantityType, apiservercel.QuantityType}, cel.BoolType, cel.BinaryBinding(quantityIsGreaterThan)),
},
"isLessThan": {
cel.MemberOverload("quantity_is_less_than", []*cel.Type{apiservercel.QuantityType, apiservercel.QuantityType}, cel.BoolType, cel.BinaryBinding(quantityIsLessThan)),
},
"compareTo": {
cel.MemberOverload("quantity_compare_to", []*cel.Type{apiservercel.QuantityType, apiservercel.QuantityType}, cel.IntType, cel.BinaryBinding(quantityCompareTo)),
},
"asApproximateFloat": {
cel.MemberOverload("quantity_get_float", []*cel.Type{apiservercel.QuantityType}, cel.DoubleType, cel.UnaryBinding(quantityGetApproximateFloat)),
},
"asInteger": {
cel.MemberOverload("quantity_get_int", []*cel.Type{apiservercel.QuantityType}, cel.IntType, cel.UnaryBinding(quantityGetValue)),
},
"isInteger": {
cel.MemberOverload("quantity_is_integer", []*cel.Type{apiservercel.QuantityType}, cel.BoolType, cel.UnaryBinding(quantityCanValue)),
},
"add": {
cel.MemberOverload("quantity_add", []*cel.Type{apiservercel.QuantityType, apiservercel.QuantityType}, apiservercel.QuantityType, cel.BinaryBinding(quantityAdd)),
cel.MemberOverload("quantity_add_int", []*cel.Type{apiservercel.QuantityType, cel.IntType}, apiservercel.QuantityType, cel.BinaryBinding(quantityAddInt)),
},
"sub": {
cel.MemberOverload("quantity_sub", []*cel.Type{apiservercel.QuantityType, apiservercel.QuantityType}, apiservercel.QuantityType, cel.BinaryBinding(quantitySub)),
cel.MemberOverload("quantity_sub_int", []*cel.Type{apiservercel.QuantityType, cel.IntType}, apiservercel.QuantityType, cel.BinaryBinding(quantitySubInt)),
},
}
func (*quantity) CompileOptions() []cel.EnvOption {
options := make([]cel.EnvOption, 0, len(quantityLibraryDecls))
for name, overloads := range quantityLibraryDecls {
options = append(options, cel.Function(name, overloads...))
}
return options
}
func (*quantity) ProgramOptions() []cel.ProgramOption {
return []cel.ProgramOption{}
}
func isQuantity(arg ref.Val) ref.Val {
str, ok := arg.Value().(string)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
_, err := resource.ParseQuantity(str)
if err != nil {
return types.Bool(false)
}
return types.Bool(true)
}
func stringToQuantity(arg ref.Val) ref.Val {
str, ok := arg.Value().(string)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
q, err := resource.ParseQuantity(str)
if err != nil {
return types.WrapErr(err)
}
return apiservercel.Quantity{Quantity: &q}
}
func quantityGetApproximateFloat(arg ref.Val) ref.Val {
q, ok := arg.Value().(*resource.Quantity)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
return types.Double(q.AsApproximateFloat64())
}
func quantityCanValue(arg ref.Val) ref.Val {
q, ok := arg.Value().(*resource.Quantity)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
_, success := q.AsInt64()
return types.Bool(success)
}
func quantityGetValue(arg ref.Val) ref.Val {
q, ok := arg.Value().(*resource.Quantity)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
v, success := q.AsInt64()
if !success {
return types.WrapErr(errors.New("cannot convert value to integer"))
}
return types.Int(v)
}
func quantityGetSign(arg ref.Val) ref.Val {
q, ok := arg.Value().(*resource.Quantity)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
return types.Int(q.Sign())
}
func quantityIsGreaterThan(arg ref.Val, other ref.Val) ref.Val {
q, ok := arg.Value().(*resource.Quantity)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
q2, ok := other.Value().(*resource.Quantity)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
return types.Bool(q.Cmp(*q2) == 1)
}
func quantityIsLessThan(arg ref.Val, other ref.Val) ref.Val {
q, ok := arg.Value().(*resource.Quantity)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
q2, ok := other.Value().(*resource.Quantity)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
return types.Bool(q.Cmp(*q2) == -1)
}
func quantityCompareTo(arg ref.Val, other ref.Val) ref.Val {
q, ok := arg.Value().(*resource.Quantity)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
q2, ok := other.Value().(*resource.Quantity)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
return types.Int(q.Cmp(*q2))
}
func quantityAdd(arg ref.Val, other ref.Val) ref.Val {
q, ok := arg.Value().(*resource.Quantity)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
q2, ok := other.Value().(*resource.Quantity)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
copy := *q
copy.Add(*q2)
return &apiservercel.Quantity{
Quantity: &copy,
}
}
func quantityAddInt(arg ref.Val, other ref.Val) ref.Val {
q, ok := arg.Value().(*resource.Quantity)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
q2, ok := other.Value().(int64)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
q2Converted := *resource.NewQuantity(q2, resource.DecimalExponent)
copy := *q
copy.Add(q2Converted)
return &apiservercel.Quantity{
Quantity: &copy,
}
}
func quantitySub(arg ref.Val, other ref.Val) ref.Val {
q, ok := arg.Value().(*resource.Quantity)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
q2, ok := other.Value().(*resource.Quantity)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
copy := *q
copy.Sub(*q2)
return &apiservercel.Quantity{
Quantity: &copy,
}
}
func quantitySubInt(arg ref.Val, other ref.Val) ref.Val {
q, ok := arg.Value().(*resource.Quantity)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
q2, ok := other.Value().(int64)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
q2Converted := *resource.NewQuantity(q2, resource.DecimalExponent)
copy := *q
copy.Sub(q2Converted)
return &apiservercel.Quantity{
Quantity: &copy,
}
}

View File

@ -77,7 +77,9 @@ func (*regex) CompileOptions() []cel.EnvOption {
}
func (*regex) ProgramOptions() []cel.ProgramOption {
return []cel.ProgramOption{}
return []cel.ProgramOption{
cel.OptimizeRegex(FindRegexOptimization, FindAllRegexOptimization),
}
}
func find(strVal ref.Val, regexVal ref.Val) ref.Val {

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

@ -0,0 +1,79 @@
/*
Copyright 2023 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 (
"math"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
)
// Test provides a test() function that returns true.
func Test(options ...TestOption) cel.EnvOption {
t := &testLib{version: math.MaxUint32}
for _, o := range options {
t = o(t)
}
return cel.Lib(t)
}
type testLib struct {
version uint32
}
type TestOption func(*testLib) *testLib
func TestVersion(version uint32) func(lib *testLib) *testLib {
return func(sl *testLib) *testLib {
sl.version = version
return sl
}
}
func (t *testLib) CompileOptions() []cel.EnvOption {
var options []cel.EnvOption
if t.version == 0 {
options = append(options, cel.Function("test",
cel.Overload("test", []*cel.Type{}, cel.BoolType,
cel.FunctionBinding(func(args ...ref.Val) ref.Val {
return types.True
}))))
}
if t.version >= 1 {
options = append(options, cel.Function("test",
cel.Overload("test", []*cel.Type{}, cel.BoolType,
cel.FunctionBinding(func(args ...ref.Val) ref.Val {
// Return false here so tests can observe which version of the function is registered
// Actual function libraries must not break backward compatibility
return types.False
}))))
options = append(options, cel.Function("testV1",
cel.Overload("testV1", []*cel.Type{}, cel.BoolType,
cel.FunctionBinding(func(args ...ref.Val) ref.Val {
return types.True
}))))
}
return options
}
func (*testLib) ProgramOptions() []cel.ProgramOption {
return []cel.ProgramOption{}
}

76
vendor/k8s.io/apiserver/pkg/cel/quantity.go generated vendored Normal file
View File

@ -0,0 +1,76 @@
/*
Copyright 2023 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/google/cel-go/cel"
"github.com/google/cel-go/checker/decls"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"k8s.io/apimachinery/pkg/api/resource"
)
var (
QuantityObject = decls.NewObjectType("kubernetes.Quantity")
quantityTypeValue = types.NewTypeValue("kubernetes.Quantity")
QuantityType = cel.ObjectType("kubernetes.Quantity")
)
// Quantity provdes a CEL representation of a resource.Quantity
type Quantity struct {
*resource.Quantity
}
func (d Quantity) ConvertToNative(typeDesc reflect.Type) (interface{}, error) {
if reflect.TypeOf(d.Quantity).AssignableTo(typeDesc) {
return d.Quantity, nil
}
if reflect.TypeOf("").AssignableTo(typeDesc) {
return d.Quantity.String(), nil
}
return nil, fmt.Errorf("type conversion error from 'Quantity' to '%v'", typeDesc)
}
func (d Quantity) ConvertToType(typeVal ref.Type) ref.Val {
switch typeVal {
case typeValue:
return d
case types.TypeType:
return quantityTypeValue
default:
return types.NewErr("type conversion error from '%s' to '%s'", quantityTypeValue, typeVal)
}
}
func (d Quantity) Equal(other ref.Val) ref.Val {
otherDur, ok := other.(Quantity)
if !ok {
return types.MaybeNoSuchOverloadErr(other)
}
return types.Bool(d.Quantity.Equal(*otherDur.Quantity))
}
func (d Quantity) Type() ref.Type {
return quantityTypeValue
}
func (d Quantity) Value() interface{} {
return d.Quantity
}

View File

@ -1,79 +0,0 @@
/*
Copyright 2022 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 (
"sync"
"github.com/google/cel-go/cel"
)
// Resolver declares methods to find policy templates and related configuration objects.
type Resolver interface {
// FindType returns a DeclType instance corresponding to the given fully-qualified name, if
// present.
FindType(name string) (*DeclType, bool)
}
// NewRegistry create a registry for keeping track of environments and types
// from a base cel.Env expression environment.
func NewRegistry(stdExprEnv *cel.Env) *Registry {
return &Registry{
exprEnvs: map[string]*cel.Env{"": stdExprEnv},
types: map[string]*DeclType{
BoolType.TypeName(): BoolType,
BytesType.TypeName(): BytesType,
DoubleType.TypeName(): DoubleType,
DurationType.TypeName(): DurationType,
IntType.TypeName(): IntType,
NullType.TypeName(): NullType,
StringType.TypeName(): StringType,
TimestampType.TypeName(): TimestampType,
UintType.TypeName(): UintType,
ListType.TypeName(): ListType,
MapType.TypeName(): MapType,
},
}
}
// Registry defines a repository of environment, schema, template, and type definitions.
//
// Registry instances are concurrency-safe.
type Registry struct {
rwMux sync.RWMutex
exprEnvs map[string]*cel.Env
types map[string]*DeclType
}
// FindType implements the Resolver interface method.
func (r *Registry) FindType(name string) (*DeclType, bool) {
r.rwMux.RLock()
defer r.rwMux.RUnlock()
typ, found := r.types[name]
if found {
return typ, true
}
return typ, found
}
// SetType registers a DeclType descriptor by its fully qualified name.
func (r *Registry) SetType(name string, declType *DeclType) error {
r.rwMux.Lock()
defer r.rwMux.Unlock()
r.types[name] = declType
return nil
}

View File

@ -319,44 +319,53 @@ func (f *DeclField) EnumValues() []ref.Val {
return ev
}
// NewRuleTypes returns an Open API Schema-based type-system which is CEL compatible.
func NewRuleTypes(kind string,
declType *DeclType,
res Resolver) (*RuleTypes, error) {
func allTypesForDecl(declTypes []*DeclType) map[string]*DeclType {
if declTypes == nil {
return nil
}
allTypes := map[string]*DeclType{}
for _, declType := range declTypes {
for k, t := range FieldTypeMap(declType.TypeName(), declType) {
allTypes[k] = t
}
}
return allTypes
}
// NewDeclTypeProvider returns an Open API Schema-based type-system which is CEL compatible.
func NewDeclTypeProvider(rootTypes ...*DeclType) *DeclTypeProvider {
// Note, if the schema indicates that it's actually based on another proto
// then prefer the proto definition. For expressions in the proto, a new field
// annotation will be needed to indicate the expected environment and type of
// the expression.
schemaTypes, err := newSchemaTypeProvider(kind, declType)
if err != nil {
return nil, err
allTypes := allTypesForDecl(rootTypes)
return &DeclTypeProvider{
registeredTypes: allTypes,
}
if schemaTypes == nil {
return nil, nil
}
return &RuleTypes{
ruleSchemaDeclTypes: schemaTypes,
resolver: res,
}, nil
}
// RuleTypes extends the CEL ref.TypeProvider interface and provides an Open API Schema-based
// DeclTypeProvider extends the CEL ref.TypeProvider interface and provides an Open API Schema-based
// type-system.
type RuleTypes struct {
ref.TypeProvider
ruleSchemaDeclTypes *schemaTypeProvider
typeAdapter ref.TypeAdapter
resolver Resolver
type DeclTypeProvider struct {
registeredTypes map[string]*DeclType
typeProvider ref.TypeProvider
typeAdapter ref.TypeAdapter
}
func (rt *DeclTypeProvider) EnumValue(enumName string) ref.Val {
return rt.typeProvider.EnumValue(enumName)
}
func (rt *DeclTypeProvider) FindIdent(identName string) (ref.Val, bool) {
return rt.typeProvider.FindIdent(identName)
}
// EnvOptions returns a set of cel.EnvOption values which includes the declaration set
// as well as a custom ref.TypeProvider.
//
// Note, the standard declaration set includes 'rule' which is defined as the top-level rule-schema
// type if one is configured.
//
// If the RuleTypes value is nil, an empty []cel.EnvOption set is returned.
func (rt *RuleTypes) EnvOptions(tp ref.TypeProvider) ([]cel.EnvOption, error) {
// If the DeclTypeProvider value is nil, an empty []cel.EnvOption set is returned.
func (rt *DeclTypeProvider) EnvOptions(tp ref.TypeProvider) ([]cel.EnvOption, error) {
if rt == nil {
return []cel.EnvOption{}, nil
}
@ -367,13 +376,12 @@ func (rt *RuleTypes) EnvOptions(tp ref.TypeProvider) ([]cel.EnvOption, error) {
return []cel.EnvOption{
cel.CustomTypeProvider(rtWithTypes),
cel.CustomTypeAdapter(rtWithTypes),
cel.Variable("rule", rt.ruleSchemaDeclTypes.root.CelType()),
}, nil
}
// WithTypeProvider returns a new RuleTypes that sets the given TypeProvider
// If the original RuleTypes is nil, the returned RuleTypes is still nil.
func (rt *RuleTypes) WithTypeProvider(tp ref.TypeProvider) (*RuleTypes, error) {
// WithTypeProvider returns a new DeclTypeProvider that sets the given TypeProvider
// If the original DeclTypeProvider is nil, the returned DeclTypeProvider is still nil.
func (rt *DeclTypeProvider) WithTypeProvider(tp ref.TypeProvider) (*DeclTypeProvider, error) {
if rt == nil {
return nil, nil
}
@ -382,13 +390,12 @@ func (rt *RuleTypes) WithTypeProvider(tp ref.TypeProvider) (*RuleTypes, error) {
if ok {
ta = tpa
}
rtWithTypes := &RuleTypes{
TypeProvider: tp,
typeAdapter: ta,
ruleSchemaDeclTypes: rt.ruleSchemaDeclTypes,
resolver: rt.resolver,
rtWithTypes := &DeclTypeProvider{
typeProvider: tp,
typeAdapter: ta,
registeredTypes: rt.registeredTypes,
}
for name, declType := range rt.ruleSchemaDeclTypes.types {
for name, declType := range rt.registeredTypes {
tpType, found := tp.FindType(name)
expT, err := declType.ExprType()
if err != nil {
@ -396,7 +403,7 @@ func (rt *RuleTypes) WithTypeProvider(tp ref.TypeProvider) (*RuleTypes, error) {
}
if found && !proto.Equal(tpType, expT) {
return nil, fmt.Errorf(
"type %s definition differs between CEL environment and rule", name)
"type %s definition differs between CEL environment and type provider", name)
}
}
return rtWithTypes, nil
@ -409,7 +416,7 @@ func (rt *RuleTypes) WithTypeProvider(tp ref.TypeProvider) (*RuleTypes, error) {
//
// Note, when the type name is based on the Open API Schema, the name will reflect the object path
// where the type definition appears.
func (rt *RuleTypes) FindType(typeName string) (*exprpb.Type, bool) {
func (rt *DeclTypeProvider) FindType(typeName string) (*exprpb.Type, bool) {
if rt == nil {
return nil, false
}
@ -421,11 +428,11 @@ func (rt *RuleTypes) FindType(typeName string) (*exprpb.Type, bool) {
}
return expT, found
}
return rt.TypeProvider.FindType(typeName)
return rt.typeProvider.FindType(typeName)
}
// FindDeclType returns the CPT type description which can be mapped to a CEL type.
func (rt *RuleTypes) FindDeclType(typeName string) (*DeclType, bool) {
func (rt *DeclTypeProvider) FindDeclType(typeName string) (*DeclType, bool) {
if rt == nil {
return nil, false
}
@ -438,10 +445,10 @@ func (rt *RuleTypes) FindDeclType(typeName string) (*DeclType, bool) {
// If, in the future an object instance rather than a type name were provided, the field
// resolution might more accurately reflect the expected type model. However, in this case
// concessions were made to align with the existing CEL interfaces.
func (rt *RuleTypes) FindFieldType(typeName, fieldName string) (*ref.FieldType, bool) {
func (rt *DeclTypeProvider) FindFieldType(typeName, fieldName string) (*ref.FieldType, bool) {
st, found := rt.findDeclType(typeName)
if !found {
return rt.TypeProvider.FindFieldType(typeName, fieldName)
return rt.typeProvider.FindFieldType(typeName, fieldName)
}
f, found := st.Fields[fieldName]
@ -471,48 +478,63 @@ func (rt *RuleTypes) FindFieldType(typeName, fieldName string) (*ref.FieldType,
// NativeToValue is an implementation of the ref.TypeAdapater interface which supports conversion
// of rule values to CEL ref.Val instances.
func (rt *RuleTypes) NativeToValue(val interface{}) ref.Val {
func (rt *DeclTypeProvider) NativeToValue(val interface{}) ref.Val {
return rt.typeAdapter.NativeToValue(val)
}
// TypeNames returns the list of type names declared within the RuleTypes object.
func (rt *RuleTypes) TypeNames() []string {
typeNames := make([]string, len(rt.ruleSchemaDeclTypes.types))
func (rt *DeclTypeProvider) NewValue(typeName string, fields map[string]ref.Val) ref.Val {
// TODO: implement for OpenAPI types to enable CEL object instantiation, which is needed
// for mutating admission.
return rt.typeProvider.NewValue(typeName, fields)
}
// TypeNames returns the list of type names declared within the DeclTypeProvider object.
func (rt *DeclTypeProvider) TypeNames() []string {
typeNames := make([]string, len(rt.registeredTypes))
i := 0
for name := range rt.ruleSchemaDeclTypes.types {
for name := range rt.registeredTypes {
typeNames[i] = name
i++
}
return typeNames
}
func (rt *RuleTypes) findDeclType(typeName string) (*DeclType, bool) {
declType, found := rt.ruleSchemaDeclTypes.types[typeName]
func (rt *DeclTypeProvider) findDeclType(typeName string) (*DeclType, bool) {
declType, found := rt.registeredTypes[typeName]
if found {
return declType, true
}
declType, found = rt.resolver.FindType(typeName)
if found {
return declType, true
}
return nil, false
declType = findScalar(typeName)
return declType, declType != nil
}
func newSchemaTypeProvider(kind string, declType *DeclType) (*schemaTypeProvider, error) {
if declType == nil {
return nil, nil
func findScalar(typename string) *DeclType {
switch typename {
case BoolType.TypeName():
return BoolType
case BytesType.TypeName():
return BytesType
case DoubleType.TypeName():
return DoubleType
case DurationType.TypeName():
return DurationType
case IntType.TypeName():
return IntType
case NullType.TypeName():
return NullType
case StringType.TypeName():
return StringType
case TimestampType.TypeName():
return TimestampType
case UintType.TypeName():
return UintType
case ListType.TypeName():
return ListType
case MapType.TypeName():
return MapType
default:
return nil
}
root := declType.MaybeAssignTypeName(kind)
types := FieldTypeMap(kind, root)
return &schemaTypeProvider{
root: root,
types: types,
}, nil
}
type schemaTypeProvider struct {
root *DeclType
types map[string]*DeclType
}
var (