mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 20:24:09 +00:00
Pick up dispatcher refactor changes from cel-go
This commit is contained in:
parent
35669cc69c
commit
ae6243b431
@ -24,10 +24,6 @@ import (
|
|||||||
|
|
||||||
"github.com/google/cel-go/cel"
|
"github.com/google/cel-go/cel"
|
||||||
"github.com/google/cel-go/checker"
|
"github.com/google/cel-go/checker"
|
||||||
"github.com/google/cel-go/checker/decls"
|
|
||||||
expr "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
|
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
|
|
||||||
apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
||||||
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/library"
|
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/library"
|
||||||
@ -104,7 +100,7 @@ func Compile(s *schema.Structural, isResourceRoot bool, perCallLimit uint64) ([]
|
|||||||
}
|
}
|
||||||
celRules := s.Extensions.XValidations
|
celRules := s.Extensions.XValidations
|
||||||
|
|
||||||
var propDecls []*expr.Decl
|
var propDecls []cel.EnvOption
|
||||||
var root *celmodel.DeclType
|
var root *celmodel.DeclType
|
||||||
var ok bool
|
var ok bool
|
||||||
baseEnv, err := getBaseEnv()
|
baseEnv, err := getBaseEnv()
|
||||||
@ -132,9 +128,9 @@ func Compile(s *schema.Structural, isResourceRoot bool, perCallLimit uint64) ([]
|
|||||||
}
|
}
|
||||||
root = rootDecl.MaybeAssignTypeName(scopedTypeName)
|
root = rootDecl.MaybeAssignTypeName(scopedTypeName)
|
||||||
}
|
}
|
||||||
propDecls = append(propDecls, decls.NewVar(ScopedVarName, root.ExprType()))
|
propDecls = append(propDecls, cel.Variable(ScopedVarName, root.CelType()))
|
||||||
propDecls = append(propDecls, decls.NewVar(OldScopedVarName, root.ExprType()))
|
propDecls = append(propDecls, cel.Variable(OldScopedVarName, root.CelType()))
|
||||||
opts = append(opts, cel.Declarations(propDecls...))
|
opts = append(opts, propDecls...)
|
||||||
env, err := baseEnv.Extend(opts...)
|
env, err := baseEnv.Extend(opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -161,7 +157,7 @@ func compileRule(rule apiextensions.ValidationRule, env *cel.Env, perCallLimit u
|
|||||||
compilationResult.Error = &Error{ErrorTypeInvalid, "compilation failed: " + issues.String()}
|
compilationResult.Error = &Error{ErrorTypeInvalid, "compilation failed: " + issues.String()}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !proto.Equal(ast.ResultType(), decls.Bool) {
|
if ast.OutputType() != cel.BoolType {
|
||||||
compilationResult.Error = &Error{ErrorTypeInvalid, "cel expression must evaluate to a bool"}
|
compilationResult.Error = &Error{ErrorTypeInvalid, "cel expression must evaluate to a bool"}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -20,13 +20,12 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/google/cel-go/cel"
|
"github.com/google/cel-go/cel"
|
||||||
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLibraryCompatibility(t *testing.T) {
|
func TestLibraryCompatibility(t *testing.T) {
|
||||||
functionNames := map[string]struct{}{}
|
functionNames := map[string]struct{}{}
|
||||||
|
|
||||||
decls := map[cel.Library][]*exprpb.Decl{
|
decls := map[cel.Library]map[string][]cel.FunctionOpt{
|
||||||
urlsLib: urlLibraryDecls,
|
urlsLib: urlLibraryDecls,
|
||||||
listsLib: listsLibraryDecls,
|
listsLib: listsLibraryDecls,
|
||||||
regexLib: regexLibraryDecls,
|
regexLib: regexLibraryDecls,
|
||||||
@ -34,9 +33,9 @@ func TestLibraryCompatibility(t *testing.T) {
|
|||||||
if len(k8sExtensionLibs) != len(decls) {
|
if len(k8sExtensionLibs) != len(decls) {
|
||||||
t.Errorf("Expected the same number of libraries in the ExtensionLibs as are tested for compatibility")
|
t.Errorf("Expected the same number of libraries in the ExtensionLibs as are tested for compatibility")
|
||||||
}
|
}
|
||||||
for _, l := range decls {
|
for _, decl := range decls {
|
||||||
for _, d := range l {
|
for name := range decl {
|
||||||
functionNames[d.GetName()] = struct{}{}
|
functionNames[name] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,12 +20,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/google/cel-go/cel"
|
"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"
|
||||||
"github.com/google/cel-go/common/types/ref"
|
"github.com/google/cel-go/common/types/ref"
|
||||||
"github.com/google/cel-go/common/types/traits"
|
"github.com/google/cel-go/common/types/traits"
|
||||||
"github.com/google/cel-go/interpreter/functions"
|
"github.com/google/cel-go/interpreter/functions"
|
||||||
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Lists provides a CEL function library extension of list utility functions.
|
// Lists provides a CEL function library extension of list utility functions.
|
||||||
@ -101,120 +99,64 @@ var listsLib = &lists{}
|
|||||||
|
|
||||||
type lists struct{}
|
type lists struct{}
|
||||||
|
|
||||||
var paramA = decls.NewTypeParamType("A")
|
var paramA = cel.TypeParamType("A")
|
||||||
|
|
||||||
// CEL typeParams can be used to constraint to a specific trait (e.g. traits.ComparableType) if the 1st operand is the type to constrain.
|
// CEL typeParams can be used to constraint to a specific trait (e.g. traits.ComparableType) if the 1st operand is the type to constrain.
|
||||||
// But the functions we need to constrain are <list<paramType>>, not just <paramType>.
|
// But the functions we need to constrain are <list<paramType>>, not just <paramType>.
|
||||||
var summableTypes = map[string]*exprpb.Type{"int": decls.Int, "uint": decls.Uint, "double": decls.Double, "duration": decls.Duration}
|
var summableTypes = map[string]*cel.Type{"int": cel.IntType, "uint": cel.UintType, "double": cel.DoubleType, "duration": cel.DurationType}
|
||||||
var comparableTypes = map[string]*exprpb.Type{"bool": decls.Bool, "int": decls.Int, "uint": decls.Uint, "double": decls.Double,
|
var zeroValuesOfSummableTypes = map[string]ref.Val{
|
||||||
"duration": decls.Duration, "timestamp": decls.Timestamp, "string": decls.String, "bytes": decls.Bytes}
|
"int": types.Int(0),
|
||||||
|
"uint": types.Uint(0),
|
||||||
|
"double": types.Double(0.0),
|
||||||
|
"duration": types.Duration{Duration: 0},
|
||||||
|
}
|
||||||
|
var comparableTypes = map[string]*cel.Type{"bool": cel.BoolType, "int": cel.IntType, "uint": cel.UintType, "double": cel.DoubleType,
|
||||||
|
"duration": cel.DurationType, "timestamp": cel.TimestampType, "string": cel.StringType, "bytes": cel.BytesType}
|
||||||
|
|
||||||
// WARNING: All library additions or modifications must follow
|
// WARNING: All library additions or modifications must follow
|
||||||
// https://github.com/kubernetes/enhancements/tree/master/keps/sig-api-machinery/2876-crd-validation-expression-language#function-library-updates
|
// https://github.com/kubernetes/enhancements/tree/master/keps/sig-api-machinery/2876-crd-validation-expression-language#function-library-updates
|
||||||
var listsLibraryDecls = []*exprpb.Decl{
|
var listsLibraryDecls = map[string][]cel.FunctionOpt{
|
||||||
decls.NewFunction("isSorted",
|
"isSorted": templatedOverloads(comparableTypes, func(name string, paramType *cel.Type) cel.FunctionOpt {
|
||||||
templatedOverloads(comparableTypes, func(name string, paramType *exprpb.Type) *exprpb.Decl_FunctionDecl_Overload {
|
return cel.MemberOverload(fmt.Sprintf("list_%s_is_sorted_bool", name),
|
||||||
return decls.NewInstanceOverload(fmt.Sprintf("list_%s_is_sorted_bool", name),
|
[]*cel.Type{cel.ListType(paramType)}, cel.BoolType, cel.UnaryBinding(isSorted))
|
||||||
[]*exprpb.Type{decls.NewListType(paramType)},
|
}),
|
||||||
decls.Bool)
|
"sum": templatedOverloads(summableTypes, func(name string, paramType *cel.Type) cel.FunctionOpt {
|
||||||
})...,
|
return cel.MemberOverload(fmt.Sprintf("list_%s_sum_%s", name, name),
|
||||||
),
|
[]*cel.Type{cel.ListType(paramType)}, paramType, cel.UnaryBinding(func(list ref.Val) ref.Val {
|
||||||
decls.NewFunction("sum",
|
return sum(
|
||||||
templatedOverloads(summableTypes, func(name string, paramType *exprpb.Type) *exprpb.Decl_FunctionDecl_Overload {
|
func() ref.Val {
|
||||||
return decls.NewInstanceOverload(fmt.Sprintf("list_%s_sum_%s", name, name),
|
return zeroValuesOfSummableTypes[name]
|
||||||
[]*exprpb.Type{decls.NewListType(paramType)},
|
})(list)
|
||||||
paramType)
|
}))
|
||||||
})...,
|
}),
|
||||||
),
|
"max": templatedOverloads(comparableTypes, func(name string, paramType *cel.Type) cel.FunctionOpt {
|
||||||
decls.NewFunction("max",
|
return cel.MemberOverload(fmt.Sprintf("list_%s_max_%s", name, name),
|
||||||
templatedOverloads(comparableTypes, func(name string, paramType *exprpb.Type) *exprpb.Decl_FunctionDecl_Overload {
|
[]*cel.Type{cel.ListType(paramType)}, paramType, cel.UnaryBinding(max()))
|
||||||
return decls.NewInstanceOverload(fmt.Sprintf("list_%s_max_%s", name, name),
|
}),
|
||||||
[]*exprpb.Type{decls.NewListType(paramType)},
|
"min": templatedOverloads(comparableTypes, func(name string, paramType *cel.Type) cel.FunctionOpt {
|
||||||
paramType)
|
return cel.MemberOverload(fmt.Sprintf("list_%s_min_%s", name, name),
|
||||||
})...,
|
[]*cel.Type{cel.ListType(paramType)}, paramType, cel.UnaryBinding(min()))
|
||||||
),
|
}),
|
||||||
decls.NewFunction("min",
|
"indexOf": {
|
||||||
templatedOverloads(comparableTypes, func(name string, paramType *exprpb.Type) *exprpb.Decl_FunctionDecl_Overload {
|
cel.MemberOverload("list_a_index_of_int", []*cel.Type{cel.ListType(paramA), paramA}, cel.IntType,
|
||||||
return decls.NewInstanceOverload(fmt.Sprintf("list_%s_min_%s", name, name),
|
cel.BinaryBinding(indexOf)),
|
||||||
[]*exprpb.Type{decls.NewListType(paramType)},
|
},
|
||||||
paramType)
|
"lastIndexOf": {
|
||||||
})...,
|
cel.MemberOverload("list_a_last_index_of_int", []*cel.Type{cel.ListType(paramA), paramA}, cel.IntType,
|
||||||
),
|
cel.BinaryBinding(lastIndexOf)),
|
||||||
decls.NewFunction("indexOf",
|
},
|
||||||
decls.NewInstanceOverload("list_a_index_of_int",
|
|
||||||
[]*exprpb.Type{decls.NewListType(paramA), paramA},
|
|
||||||
decls.Int),
|
|
||||||
),
|
|
||||||
decls.NewFunction("lastIndexOf",
|
|
||||||
decls.NewInstanceOverload("list_a_last_index_of_int",
|
|
||||||
[]*exprpb.Type{decls.NewListType(paramA), paramA},
|
|
||||||
decls.Int),
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*lists) CompileOptions() []cel.EnvOption {
|
func (*lists) CompileOptions() []cel.EnvOption {
|
||||||
return []cel.EnvOption{
|
options := []cel.EnvOption{}
|
||||||
cel.Declarations(listsLibraryDecls...),
|
for name, overloads := range listsLibraryDecls {
|
||||||
|
options = append(options, cel.Function(name, overloads...))
|
||||||
}
|
}
|
||||||
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*lists) ProgramOptions() []cel.ProgramOption {
|
func (*lists) ProgramOptions() []cel.ProgramOption {
|
||||||
return []cel.ProgramOption{
|
return []cel.ProgramOption{}
|
||||||
cel.Functions(
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "isSorted",
|
|
||||||
Unary: isSorted,
|
|
||||||
},
|
|
||||||
// if 'sum' is called directly, it is via dynamic dispatch, and we infer the type from the 1st element of the
|
|
||||||
// list if it has one, otherwise we return int64(0)
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "sum",
|
|
||||||
Unary: dynSum(),
|
|
||||||
},
|
|
||||||
// use overload names for sum so an initial accumulator value can be assigned to each
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "list_int_sum_int",
|
|
||||||
Unary: sum(func() ref.Val {
|
|
||||||
return types.Int(0)
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "list_uint_sum_uint",
|
|
||||||
Unary: sum(func() ref.Val {
|
|
||||||
return types.Uint(0)
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "list_double_sum_double",
|
|
||||||
Unary: sum(func() ref.Val {
|
|
||||||
return types.Double(0.0)
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "list_duration_sum_duration",
|
|
||||||
Unary: sum(func() ref.Val {
|
|
||||||
return types.Duration{Duration: 0}
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "max",
|
|
||||||
Unary: max(),
|
|
||||||
},
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "min",
|
|
||||||
Unary: min(),
|
|
||||||
},
|
|
||||||
// use overload names for indexOf and lastIndexOf to de-conflict with function of same name in strings extension library
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "list_a_index_of_int",
|
|
||||||
Binary: indexOf,
|
|
||||||
},
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "list_a_last_index_of_int",
|
|
||||||
Binary: lastIndexOf,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func isSorted(val ref.Val) ref.Val {
|
func isSorted(val ref.Val) ref.Val {
|
||||||
@ -375,8 +317,8 @@ func lastIndexOf(list ref.Val, item ref.Val) ref.Val {
|
|||||||
|
|
||||||
// templatedOverloads returns overloads for each of the provided types. The template function is called with each type
|
// templatedOverloads returns overloads for each of the provided types. The template function is called with each type
|
||||||
// name (map key) and type to construct the overloads.
|
// name (map key) and type to construct the overloads.
|
||||||
func templatedOverloads(types map[string]*exprpb.Type, template func(name string, t *exprpb.Type) *exprpb.Decl_FunctionDecl_Overload) []*exprpb.Decl_FunctionDecl_Overload {
|
func templatedOverloads(types map[string]*cel.Type, template func(name string, t *cel.Type) cel.FunctionOpt) []cel.FunctionOpt {
|
||||||
overloads := make([]*exprpb.Decl_FunctionDecl_Overload, len(types))
|
overloads := make([]cel.FunctionOpt, len(types))
|
||||||
i := 0
|
i := 0
|
||||||
for name, t := range types {
|
for name, t := range types {
|
||||||
overloads[i] = template(name, t)
|
overloads[i] = template(name, t)
|
||||||
|
@ -20,12 +20,9 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
"github.com/google/cel-go/cel"
|
"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"
|
||||||
"github.com/google/cel-go/common/types/ref"
|
"github.com/google/cel-go/common/types/ref"
|
||||||
"github.com/google/cel-go/interpreter"
|
"github.com/google/cel-go/interpreter"
|
||||||
"github.com/google/cel-go/interpreter/functions"
|
|
||||||
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Regex provides a CEL function library extension of regex utility functions.
|
// Regex provides a CEL function library extension of regex utility functions.
|
||||||
@ -55,59 +52,33 @@ var regexLib = ®ex{}
|
|||||||
|
|
||||||
type regex struct{}
|
type regex struct{}
|
||||||
|
|
||||||
var regexLibraryDecls = []*exprpb.Decl{
|
var regexLibraryDecls = map[string][]cel.FunctionOpt{
|
||||||
|
"find": {
|
||||||
decls.NewFunction("find",
|
cel.MemberOverload("string_find_string", []*cel.Type{cel.StringType, cel.StringType}, cel.StringType,
|
||||||
decls.NewInstanceOverload("string_find_string",
|
cel.BinaryBinding(find))},
|
||||||
[]*exprpb.Type{decls.String, decls.String},
|
"findAll": {
|
||||||
decls.String),
|
cel.MemberOverload("string_find_all_string", []*cel.Type{cel.StringType, cel.StringType},
|
||||||
),
|
cel.ListType(cel.StringType),
|
||||||
decls.NewFunction("findAll",
|
cel.BinaryBinding(func(str, regex ref.Val) ref.Val {
|
||||||
decls.NewInstanceOverload("string_find_all_string",
|
return findAll(str, regex, types.Int(-1))
|
||||||
[]*exprpb.Type{decls.String, decls.String},
|
})),
|
||||||
decls.NewListType(decls.String)),
|
cel.MemberOverload("string_find_all_string_int",
|
||||||
decls.NewInstanceOverload("string_find_all_string_int",
|
[]*cel.Type{cel.StringType, cel.StringType, cel.IntType},
|
||||||
[]*exprpb.Type{decls.String, decls.String, decls.Int},
|
cel.ListType(cel.StringType),
|
||||||
decls.NewListType(decls.String)),
|
cel.FunctionBinding(findAll)),
|
||||||
),
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*regex) CompileOptions() []cel.EnvOption {
|
func (*regex) CompileOptions() []cel.EnvOption {
|
||||||
return []cel.EnvOption{
|
options := []cel.EnvOption{}
|
||||||
cel.Declarations(regexLibraryDecls...),
|
for name, overloads := range regexLibraryDecls {
|
||||||
|
options = append(options, cel.Function(name, overloads...))
|
||||||
}
|
}
|
||||||
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*regex) ProgramOptions() []cel.ProgramOption {
|
func (*regex) ProgramOptions() []cel.ProgramOption {
|
||||||
return []cel.ProgramOption{
|
return []cel.ProgramOption{}
|
||||||
cel.Functions(
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "find",
|
|
||||||
Binary: find,
|
|
||||||
},
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "string_find_string",
|
|
||||||
Binary: find,
|
|
||||||
},
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "findAll",
|
|
||||||
Binary: func(str, regex ref.Val) ref.Val {
|
|
||||||
return findAll(str, regex, types.Int(-1))
|
|
||||||
},
|
|
||||||
Function: findAll,
|
|
||||||
},
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "string_find_all_string",
|
|
||||||
Binary: func(str, regex ref.Val) ref.Val {
|
|
||||||
return findAll(str, regex, types.Int(-1))
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "string_find_all_string_int",
|
|
||||||
Function: findAll,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func find(strVal ref.Val, regexVal ref.Val) ref.Val {
|
func find(strVal ref.Val, regexVal ref.Val) ref.Val {
|
||||||
|
@ -20,12 +20,8 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/google/cel-go/cel"
|
"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"
|
||||||
"github.com/google/cel-go/common/types/ref"
|
"github.com/google/cel-go/common/types/ref"
|
||||||
"github.com/google/cel-go/interpreter/functions"
|
|
||||||
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
|
|
||||||
|
|
||||||
"k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model"
|
"k8s.io/apiextensions-apiserver/third_party/forked/celopenapi/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -111,124 +107,44 @@ var urlsLib = &urls{}
|
|||||||
|
|
||||||
type urls struct{}
|
type urls struct{}
|
||||||
|
|
||||||
var urlLibraryDecls = []*exprpb.Decl{
|
var urlLibraryDecls = map[string][]cel.FunctionOpt{
|
||||||
decls.NewFunction("url",
|
"url": {
|
||||||
decls.NewOverload("string_to_url",
|
cel.Overload("string_to_url", []*cel.Type{cel.StringType}, model.URLType,
|
||||||
[]*exprpb.Type{decls.String},
|
cel.UnaryBinding(stringToUrl))},
|
||||||
model.URLObject),
|
"getScheme": {
|
||||||
),
|
cel.MemberOverload("url_get_scheme", []*cel.Type{model.URLType}, cel.StringType,
|
||||||
decls.NewFunction("getScheme",
|
cel.UnaryBinding(getScheme))},
|
||||||
decls.NewInstanceOverload("url_get_scheme",
|
"getHost": {
|
||||||
[]*exprpb.Type{model.URLObject},
|
cel.MemberOverload("url_get_host", []*cel.Type{model.URLType}, cel.StringType,
|
||||||
decls.String),
|
cel.UnaryBinding(getHost))},
|
||||||
),
|
"getHostname": {
|
||||||
decls.NewFunction("getHost",
|
cel.MemberOverload("url_get_hostname", []*cel.Type{model.URLType}, cel.StringType,
|
||||||
decls.NewInstanceOverload("url_get_host",
|
cel.UnaryBinding(getHostname))},
|
||||||
[]*exprpb.Type{model.URLObject},
|
"getPort": {
|
||||||
decls.String),
|
cel.MemberOverload("url_get_port", []*cel.Type{model.URLType}, cel.StringType,
|
||||||
),
|
cel.UnaryBinding(getPort))},
|
||||||
decls.NewFunction("getHostname",
|
"getEscapedPath": {
|
||||||
decls.NewInstanceOverload("url_get_hostname",
|
cel.MemberOverload("url_get_escaped_path", []*cel.Type{model.URLType}, cel.StringType,
|
||||||
[]*exprpb.Type{model.URLObject},
|
cel.UnaryBinding(getEscapedPath))},
|
||||||
decls.String),
|
"getQuery": {
|
||||||
),
|
cel.MemberOverload("url_get_query", []*cel.Type{model.URLType},
|
||||||
decls.NewFunction("getPort",
|
cel.MapType(cel.StringType, cel.ListType(cel.StringType)),
|
||||||
decls.NewInstanceOverload("url_get_port",
|
cel.UnaryBinding(getQuery))},
|
||||||
[]*exprpb.Type{model.URLObject},
|
"isURL": {
|
||||||
decls.String),
|
cel.Overload("is_url_string", []*cel.Type{cel.StringType}, cel.BoolType,
|
||||||
),
|
cel.UnaryBinding(isURL))},
|
||||||
decls.NewFunction("getEscapedPath",
|
|
||||||
decls.NewInstanceOverload("url_get_escaped_path",
|
|
||||||
[]*exprpb.Type{model.URLObject},
|
|
||||||
decls.String),
|
|
||||||
),
|
|
||||||
decls.NewFunction("getQuery",
|
|
||||||
decls.NewInstanceOverload("url_get_query",
|
|
||||||
[]*exprpb.Type{model.URLObject},
|
|
||||||
decls.NewMapType(decls.String, decls.NewListType(decls.String))),
|
|
||||||
),
|
|
||||||
decls.NewFunction("isURL",
|
|
||||||
decls.NewOverload("is_url_string",
|
|
||||||
[]*exprpb.Type{decls.String},
|
|
||||||
decls.Bool),
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*urls) CompileOptions() []cel.EnvOption {
|
func (*urls) CompileOptions() []cel.EnvOption {
|
||||||
return []cel.EnvOption{
|
options := []cel.EnvOption{}
|
||||||
cel.Declarations(urlLibraryDecls...),
|
for name, overloads := range urlLibraryDecls {
|
||||||
|
options = append(options, cel.Function(name, overloads...))
|
||||||
}
|
}
|
||||||
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*urls) ProgramOptions() []cel.ProgramOption {
|
func (*urls) ProgramOptions() []cel.ProgramOption {
|
||||||
return []cel.ProgramOption{
|
return []cel.ProgramOption{}
|
||||||
cel.Functions(
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "url",
|
|
||||||
Unary: stringToUrl,
|
|
||||||
},
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "string_to_url",
|
|
||||||
Unary: stringToUrl,
|
|
||||||
},
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "getScheme",
|
|
||||||
Unary: getScheme,
|
|
||||||
},
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "url_get_scheme",
|
|
||||||
Unary: getScheme,
|
|
||||||
},
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "getHost",
|
|
||||||
Unary: getHost,
|
|
||||||
},
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "url_get_host",
|
|
||||||
Unary: getHost,
|
|
||||||
},
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "getHostname",
|
|
||||||
Unary: getHostname,
|
|
||||||
},
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "url_get_hostname",
|
|
||||||
Unary: getHostname,
|
|
||||||
},
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "getPort",
|
|
||||||
Unary: getPort,
|
|
||||||
},
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "url_get_port",
|
|
||||||
Unary: getPort,
|
|
||||||
},
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "getEscapedPath",
|
|
||||||
Unary: getEscapedPath,
|
|
||||||
},
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "url_get_escaped_path",
|
|
||||||
Unary: getEscapedPath,
|
|
||||||
},
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "getQuery",
|
|
||||||
Unary: getQuery,
|
|
||||||
},
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "url_get_query",
|
|
||||||
Unary: getQuery,
|
|
||||||
},
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "isURL",
|
|
||||||
Unary: isURL,
|
|
||||||
},
|
|
||||||
&functions.Overload{
|
|
||||||
Operator: "is_url_string",
|
|
||||||
Unary: isURL,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func stringToUrl(arg ref.Val) ref.Val {
|
func stringToUrl(arg ref.Val) ref.Val {
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/google/cel-go/cel"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/cel-go/checker/decls"
|
"github.com/google/cel-go/checker/decls"
|
||||||
@ -72,7 +73,7 @@ func SchemaDeclType(s *schema.Structural, isResourceRoot bool) *DeclType {
|
|||||||
// To validate requirements on both the int and string representation:
|
// To validate requirements on both the int and string representation:
|
||||||
// `type(intOrStringField) == int ? intOrStringField < 5 : double(intOrStringField.replace('%', '')) < 0.5
|
// `type(intOrStringField) == int ? intOrStringField < 5 : double(intOrStringField.replace('%', '')) < 0.5
|
||||||
//
|
//
|
||||||
dyn := newSimpleType("dyn", decls.Dyn, nil)
|
dyn := newSimpleType("dyn", decls.Dyn, cel.DynType, nil)
|
||||||
// handle x-kubernetes-int-or-string by returning the max length of the largest possible string
|
// handle x-kubernetes-int-or-string by returning the max length of the largest possible string
|
||||||
dyn.MaxElements = maxRequestSizeBytes - 2
|
dyn.MaxElements = maxRequestSizeBytes - 2
|
||||||
return dyn
|
return dyn
|
||||||
@ -149,7 +150,7 @@ func SchemaDeclType(s *schema.Structural, isResourceRoot bool) *DeclType {
|
|||||||
if s.ValueValidation != nil {
|
if s.ValueValidation != nil {
|
||||||
switch s.ValueValidation.Format {
|
switch s.ValueValidation.Format {
|
||||||
case "byte":
|
case "byte":
|
||||||
byteWithMaxLength := newSimpleType("bytes", decls.Bytes, types.Bytes([]byte{}))
|
byteWithMaxLength := newSimpleType("bytes", decls.Bytes, cel.BytesType, types.Bytes([]byte{}))
|
||||||
if s.ValueValidation.MaxLength != nil {
|
if s.ValueValidation.MaxLength != nil {
|
||||||
byteWithMaxLength.MaxElements = zeroIfNegative(*s.ValueValidation.MaxLength)
|
byteWithMaxLength.MaxElements = zeroIfNegative(*s.ValueValidation.MaxLength)
|
||||||
} else {
|
} else {
|
||||||
@ -157,16 +158,16 @@ func SchemaDeclType(s *schema.Structural, isResourceRoot bool) *DeclType {
|
|||||||
}
|
}
|
||||||
return byteWithMaxLength
|
return byteWithMaxLength
|
||||||
case "duration":
|
case "duration":
|
||||||
durationWithMaxLength := newSimpleType("duration", decls.Duration, types.Duration{Duration: time.Duration(0)})
|
durationWithMaxLength := newSimpleType("duration", decls.Duration, cel.DurationType, types.Duration{Duration: time.Duration(0)})
|
||||||
durationWithMaxLength.MaxElements = estimateMaxStringLengthPerRequest(s)
|
durationWithMaxLength.MaxElements = estimateMaxStringLengthPerRequest(s)
|
||||||
return durationWithMaxLength
|
return durationWithMaxLength
|
||||||
case "date", "date-time":
|
case "date", "date-time":
|
||||||
timestampWithMaxLength := newSimpleType("timestamp", decls.Timestamp, types.Timestamp{Time: time.Time{}})
|
timestampWithMaxLength := newSimpleType("timestamp", decls.Timestamp, cel.TimestampType, types.Timestamp{Time: time.Time{}})
|
||||||
timestampWithMaxLength.MaxElements = estimateMaxStringLengthPerRequest(s)
|
timestampWithMaxLength.MaxElements = estimateMaxStringLengthPerRequest(s)
|
||||||
return timestampWithMaxLength
|
return timestampWithMaxLength
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
strWithMaxLength := newSimpleType("string", decls.String, types.String(""))
|
strWithMaxLength := newSimpleType("string", decls.String, cel.StringType, types.String(""))
|
||||||
if s.ValueValidation != nil && s.ValueValidation.MaxLength != nil {
|
if s.ValueValidation != nil && s.ValueValidation.MaxLength != nil {
|
||||||
// multiply the user-provided max length by 4 in the case of an otherwise-untyped string
|
// multiply the user-provided max length by 4 in the case of an otherwise-untyped string
|
||||||
// we do this because the OpenAPIv3 spec indicates that maxLength is specified in runes/code points,
|
// we do this because the OpenAPIv3 spec indicates that maxLength is specified in runes/code points,
|
||||||
|
@ -42,6 +42,7 @@ func NewListType(elem *DeclType, maxItems int64) *DeclType {
|
|||||||
ElemType: elem,
|
ElemType: elem,
|
||||||
MaxElements: maxItems,
|
MaxElements: maxItems,
|
||||||
exprType: decls.NewListType(elem.ExprType()),
|
exprType: decls.NewListType(elem.ExprType()),
|
||||||
|
celType: cel.ListType(elem.CelType()),
|
||||||
defaultValue: NewListValue(),
|
defaultValue: NewListValue(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -54,6 +55,7 @@ func NewMapType(key, elem *DeclType, maxProperties int64) *DeclType {
|
|||||||
ElemType: elem,
|
ElemType: elem,
|
||||||
MaxElements: maxProperties,
|
MaxElements: maxProperties,
|
||||||
exprType: decls.NewMapType(key.ExprType(), elem.ExprType()),
|
exprType: decls.NewMapType(key.ExprType(), elem.ExprType()),
|
||||||
|
celType: cel.MapType(key.CelType(), elem.CelType()),
|
||||||
defaultValue: NewMapValue(),
|
defaultValue: NewMapValue(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -64,6 +66,7 @@ func NewObjectType(name string, fields map[string]*DeclField) *DeclType {
|
|||||||
name: name,
|
name: name,
|
||||||
Fields: fields,
|
Fields: fields,
|
||||||
exprType: decls.NewObjectType(name),
|
exprType: decls.NewObjectType(name),
|
||||||
|
celType: cel.ObjectType(name),
|
||||||
traitMask: traits.FieldTesterType | traits.IndexerType,
|
traitMask: traits.FieldTesterType | traits.IndexerType,
|
||||||
}
|
}
|
||||||
t.defaultValue = NewObjectValue(t)
|
t.defaultValue = NewObjectValue(t)
|
||||||
@ -75,6 +78,7 @@ func NewObjectTypeRef(name string) *DeclType {
|
|||||||
t := &DeclType{
|
t := &DeclType{
|
||||||
name: name,
|
name: name,
|
||||||
exprType: decls.NewObjectType(name),
|
exprType: decls.NewObjectType(name),
|
||||||
|
celType: cel.ObjectType(name),
|
||||||
traitMask: traits.FieldTesterType | traits.IndexerType,
|
traitMask: traits.FieldTesterType | traits.IndexerType,
|
||||||
}
|
}
|
||||||
return t
|
return t
|
||||||
@ -89,13 +93,15 @@ func NewTypeParam(name string) *DeclType {
|
|||||||
name: name,
|
name: name,
|
||||||
TypeParam: true,
|
TypeParam: true,
|
||||||
exprType: decls.NewTypeParamType(name),
|
exprType: decls.NewTypeParamType(name),
|
||||||
|
celType: cel.TypeParamType(name),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSimpleType(name string, exprType *exprpb.Type, zeroVal ref.Val) *DeclType {
|
func newSimpleType(name string, exprType *exprpb.Type, celType *cel.Type, zeroVal ref.Val) *DeclType {
|
||||||
return &DeclType{
|
return &DeclType{
|
||||||
name: name,
|
name: name,
|
||||||
exprType: exprType,
|
exprType: exprType,
|
||||||
|
celType: celType,
|
||||||
defaultValue: zeroVal,
|
defaultValue: zeroVal,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -113,6 +119,7 @@ type DeclType struct {
|
|||||||
MaxElements int64
|
MaxElements int64
|
||||||
|
|
||||||
exprType *exprpb.Type
|
exprType *exprpb.Type
|
||||||
|
celType *cel.Type
|
||||||
traitMask int
|
traitMask int
|
||||||
defaultValue ref.Val
|
defaultValue ref.Val
|
||||||
}
|
}
|
||||||
@ -158,6 +165,7 @@ func (t *DeclType) MaybeAssignTypeName(name string) *DeclType {
|
|||||||
TypeParam: t.TypeParam,
|
TypeParam: t.TypeParam,
|
||||||
Metadata: t.Metadata,
|
Metadata: t.Metadata,
|
||||||
exprType: decls.NewObjectType(name),
|
exprType: decls.NewObjectType(name),
|
||||||
|
celType: cel.ObjectType(name),
|
||||||
traitMask: t.traitMask,
|
traitMask: t.traitMask,
|
||||||
defaultValue: t.defaultValue,
|
defaultValue: t.defaultValue,
|
||||||
}
|
}
|
||||||
@ -186,6 +194,11 @@ func (t *DeclType) ExprType() *exprpb.Type {
|
|||||||
return t.exprType
|
return t.exprType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CelType returns the CEL type of this declaration.
|
||||||
|
func (t *DeclType) CelType() *cel.Type {
|
||||||
|
return t.celType
|
||||||
|
}
|
||||||
|
|
||||||
// FindField returns the DeclField with the given name if present.
|
// FindField returns the DeclField with the given name if present.
|
||||||
func (t *DeclType) FindField(name string) (*DeclField, bool) {
|
func (t *DeclType) FindField(name string) (*DeclField, bool) {
|
||||||
f, found := t.Fields[name]
|
f, found := t.Fields[name]
|
||||||
@ -377,9 +390,7 @@ func (rt *RuleTypes) EnvOptions(tp ref.TypeProvider) ([]cel.EnvOption, error) {
|
|||||||
return []cel.EnvOption{
|
return []cel.EnvOption{
|
||||||
cel.CustomTypeProvider(rtWithTypes),
|
cel.CustomTypeProvider(rtWithTypes),
|
||||||
cel.CustomTypeAdapter(rtWithTypes),
|
cel.CustomTypeAdapter(rtWithTypes),
|
||||||
cel.Declarations(
|
cel.Variable("rule", rt.ruleSchemaDeclTypes.root.CelType()),
|
||||||
decls.NewVar("rule", rt.ruleSchemaDeclTypes.root.ExprType()),
|
|
||||||
),
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -517,42 +528,42 @@ type schemaTypeProvider struct {
|
|||||||
var (
|
var (
|
||||||
// AnyType is equivalent to the CEL 'protobuf.Any' type in that the value may have any of the
|
// AnyType is equivalent to the CEL 'protobuf.Any' type in that the value may have any of the
|
||||||
// types supported.
|
// types supported.
|
||||||
AnyType = newSimpleType("any", decls.Any, nil)
|
AnyType = newSimpleType("any", decls.Any, cel.AnyType, nil)
|
||||||
|
|
||||||
// BoolType is equivalent to the CEL 'bool' type.
|
// BoolType is equivalent to the CEL 'bool' type.
|
||||||
BoolType = newSimpleType("bool", decls.Bool, types.False)
|
BoolType = newSimpleType("bool", decls.Bool, cel.BoolType, types.False)
|
||||||
|
|
||||||
// BytesType is equivalent to the CEL 'bytes' type.
|
// BytesType is equivalent to the CEL 'bytes' type.
|
||||||
BytesType = newSimpleType("bytes", decls.Bytes, types.Bytes([]byte{}))
|
BytesType = newSimpleType("bytes", decls.Bytes, cel.BytesType, types.Bytes([]byte{}))
|
||||||
|
|
||||||
// DoubleType is equivalent to the CEL 'double' type which is a 64-bit floating point value.
|
// DoubleType is equivalent to the CEL 'double' type which is a 64-bit floating point value.
|
||||||
DoubleType = newSimpleType("double", decls.Double, types.Double(0))
|
DoubleType = newSimpleType("double", decls.Double, cel.DoubleType, types.Double(0))
|
||||||
|
|
||||||
// DurationType is equivalent to the CEL 'duration' type.
|
// DurationType is equivalent to the CEL 'duration' type.
|
||||||
DurationType = newSimpleType("duration", decls.Duration, types.Duration{Duration: time.Duration(0)})
|
DurationType = newSimpleType("duration", decls.Duration, cel.DurationType, types.Duration{Duration: time.Duration(0)})
|
||||||
|
|
||||||
// DateType is equivalent to the CEL 'date' type.
|
// DateType is equivalent to the CEL 'date' type.
|
||||||
DateType = newSimpleType("date", decls.Timestamp, types.Timestamp{Time: time.Time{}})
|
DateType = newSimpleType("date", decls.Timestamp, cel.TimestampType, types.Timestamp{Time: time.Time{}})
|
||||||
|
|
||||||
// DynType is the equivalent of the CEL 'dyn' concept which indicates that the type will be
|
// DynType is the equivalent of the CEL 'dyn' concept which indicates that the type will be
|
||||||
// determined at runtime rather than compile time.
|
// determined at runtime rather than compile time.
|
||||||
DynType = newSimpleType("dyn", decls.Dyn, nil)
|
DynType = newSimpleType("dyn", decls.Dyn, cel.DynType, nil)
|
||||||
|
|
||||||
// IntType is equivalent to the CEL 'int' type which is a 64-bit signed int.
|
// IntType is equivalent to the CEL 'int' type which is a 64-bit signed int.
|
||||||
IntType = newSimpleType("int", decls.Int, types.IntZero)
|
IntType = newSimpleType("int", decls.Int, cel.IntType, types.IntZero)
|
||||||
|
|
||||||
// NullType is equivalent to the CEL 'null_type'.
|
// NullType is equivalent to the CEL 'null_type'.
|
||||||
NullType = newSimpleType("null_type", decls.Null, types.NullValue)
|
NullType = newSimpleType("null_type", decls.Null, cel.NullType, types.NullValue)
|
||||||
|
|
||||||
// StringType is equivalent to the CEL 'string' type which is expected to be a UTF-8 string.
|
// StringType is equivalent to the CEL 'string' type which is expected to be a UTF-8 string.
|
||||||
// StringType values may either be string literals or expression strings.
|
// StringType values may either be string literals or expression strings.
|
||||||
StringType = newSimpleType("string", decls.String, types.String(""))
|
StringType = newSimpleType("string", decls.String, cel.StringType, types.String(""))
|
||||||
|
|
||||||
// TimestampType corresponds to the well-known protobuf.Timestamp type supported within CEL.
|
// TimestampType corresponds to the well-known protobuf.Timestamp type supported within CEL.
|
||||||
TimestampType = newSimpleType("timestamp", decls.Timestamp, types.Timestamp{Time: time.Time{}})
|
TimestampType = newSimpleType("timestamp", decls.Timestamp, cel.TimestampType, types.Timestamp{Time: time.Time{}})
|
||||||
|
|
||||||
// UintType is equivalent to the CEL 'uint' type.
|
// UintType is equivalent to the CEL 'uint' type.
|
||||||
UintType = newSimpleType("uint", decls.Uint, types.Uint(0))
|
UintType = newSimpleType("uint", decls.Uint, cel.UintType, types.Uint(0))
|
||||||
|
|
||||||
// ListType is equivalent to the CEL 'list' type.
|
// ListType is equivalent to the CEL 'list' type.
|
||||||
ListType = NewListType(AnyType, noMaxLength)
|
ListType = NewListType(AnyType, noMaxLength)
|
||||||
|
@ -16,6 +16,7 @@ package model
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/google/cel-go/cel"
|
||||||
"net/url"
|
"net/url"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
@ -32,6 +33,7 @@ type URL struct {
|
|||||||
var (
|
var (
|
||||||
URLObject = decls.NewObjectType("kubernetes.URL")
|
URLObject = decls.NewObjectType("kubernetes.URL")
|
||||||
typeValue = types.NewTypeValue("kubernetes.URL")
|
typeValue = types.NewTypeValue("kubernetes.URL")
|
||||||
|
URLType = cel.ObjectType("kubernetes.URL")
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConvertToNative implements ref.Val.ConvertToNative.
|
// ConvertToNative implements ref.Val.ConvertToNative.
|
||||||
|
Loading…
Reference in New Issue
Block a user