mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 10:33:35 +00:00
rebase: bump k8s.io/kubernetes from 1.26.2 to 1.27.2
Bumps [k8s.io/kubernetes](https://github.com/kubernetes/kubernetes) from 1.26.2 to 1.27.2. - [Release notes](https://github.com/kubernetes/kubernetes/releases) - [Commits](https://github.com/kubernetes/kubernetes/compare/v1.26.2...v1.27.2) --- updated-dependencies: - dependency-name: k8s.io/kubernetes dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
committed by
mergify[bot]
parent
0e79135419
commit
07b05616a0
76
vendor/github.com/google/cel-go/cel/BUILD.bazel
generated
vendored
Normal file
76
vendor/github.com/google/cel-go/cel/BUILD.bazel
generated
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
package(
|
||||
licenses = ["notice"], # Apache 2.0
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"cel.go",
|
||||
"decls.go",
|
||||
"env.go",
|
||||
"io.go",
|
||||
"library.go",
|
||||
"macro.go",
|
||||
"options.go",
|
||||
"program.go",
|
||||
],
|
||||
importpath = "github.com/google/cel-go/cel",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//checker:go_default_library",
|
||||
"//checker/decls:go_default_library",
|
||||
"//common:go_default_library",
|
||||
"//common/containers:go_default_library",
|
||||
"//common/overloads:go_default_library",
|
||||
"//common/types:go_default_library",
|
||||
"//common/types/pb:go_default_library",
|
||||
"//common/types/ref:go_default_library",
|
||||
"//common/types/traits:go_default_library",
|
||||
"//interpreter:go_default_library",
|
||||
"//interpreter/functions:go_default_library",
|
||||
"//parser:go_default_library",
|
||||
"@org_golang_google_genproto//googleapis/api/expr/v1alpha1:go_default_library",
|
||||
"@org_golang_google_protobuf//proto:go_default_library",
|
||||
"@org_golang_google_protobuf//reflect/protodesc:go_default_library",
|
||||
"@org_golang_google_protobuf//reflect/protoreflect:go_default_library",
|
||||
"@org_golang_google_protobuf//reflect/protoregistry:go_default_library",
|
||||
"@org_golang_google_protobuf//types/descriptorpb:go_default_library",
|
||||
"@org_golang_google_protobuf//types/dynamicpb:go_default_library",
|
||||
"@org_golang_google_protobuf//types/known/anypb:go_default_library",
|
||||
"@org_golang_google_protobuf//types/known/durationpb:go_default_library",
|
||||
"@org_golang_google_protobuf//types/known/timestamppb:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"cel_example_test.go",
|
||||
"cel_test.go",
|
||||
"decls_test.go",
|
||||
"env_test.go",
|
||||
"io_test.go",
|
||||
],
|
||||
data = [
|
||||
"//cel/testdata:gen_test_fds",
|
||||
],
|
||||
embed = [
|
||||
":go_default_library",
|
||||
],
|
||||
deps = [
|
||||
"//common/operators:go_default_library",
|
||||
"//common/overloads:go_default_library",
|
||||
"//common/types:go_default_library",
|
||||
"//common/types/ref:go_default_library",
|
||||
"//common/types/traits:go_default_library",
|
||||
"//test:go_default_library",
|
||||
"//test/proto2pb:go_default_library",
|
||||
"//test/proto3pb:go_default_library",
|
||||
"@io_bazel_rules_go//proto/wkt:descriptor_go_proto",
|
||||
"@org_golang_google_genproto//googleapis/api/expr/v1alpha1:go_default_library",
|
||||
"@org_golang_google_protobuf//proto:go_default_library",
|
||||
"@org_golang_google_protobuf//types/known/structpb:go_default_library",
|
||||
],
|
||||
)
|
19
vendor/github.com/google/cel-go/cel/cel.go
generated
vendored
Normal file
19
vendor/github.com/google/cel-go/cel/cel.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// 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 defines the top-level interface for the Common Expression Language (CEL).
|
||||
//
|
||||
// CEL is a non-Turing complete expression language designed to parse, check, and evaluate
|
||||
// expressions against user-defined environments.
|
||||
package cel
|
1179
vendor/github.com/google/cel-go/cel/decls.go
generated
vendored
Normal file
1179
vendor/github.com/google/cel-go/cel/decls.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
613
vendor/github.com/google/cel-go/cel/env.go
generated
vendored
Normal file
613
vendor/github.com/google/cel-go/cel/env.go
generated
vendored
Normal file
@ -0,0 +1,613 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// 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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/google/cel-go/checker"
|
||||
"github.com/google/cel-go/checker/decls"
|
||||
"github.com/google/cel-go/common"
|
||||
"github.com/google/cel-go/common/containers"
|
||||
"github.com/google/cel-go/common/types"
|
||||
"github.com/google/cel-go/common/types/ref"
|
||||
"github.com/google/cel-go/interpreter"
|
||||
"github.com/google/cel-go/parser"
|
||||
|
||||
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
|
||||
)
|
||||
|
||||
// Source interface representing a user-provided expression.
|
||||
type Source = common.Source
|
||||
|
||||
// Ast representing the checked or unchecked expression, its source, and related metadata such as
|
||||
// source position information.
|
||||
type Ast struct {
|
||||
expr *exprpb.Expr
|
||||
info *exprpb.SourceInfo
|
||||
source Source
|
||||
refMap map[int64]*exprpb.Reference
|
||||
typeMap map[int64]*exprpb.Type
|
||||
}
|
||||
|
||||
// Expr returns the proto serializable instance of the parsed/checked expression.
|
||||
func (ast *Ast) Expr() *exprpb.Expr {
|
||||
return ast.expr
|
||||
}
|
||||
|
||||
// IsChecked returns whether the Ast value has been successfully type-checked.
|
||||
func (ast *Ast) IsChecked() bool {
|
||||
return ast.typeMap != nil && len(ast.typeMap) > 0
|
||||
}
|
||||
|
||||
// SourceInfo returns character offset and newline position information about expression elements.
|
||||
func (ast *Ast) SourceInfo() *exprpb.SourceInfo {
|
||||
return ast.info
|
||||
}
|
||||
|
||||
// ResultType returns the output type of the expression if the Ast has been type-checked, else
|
||||
// returns decls.Dyn as the parse step cannot infer the type.
|
||||
//
|
||||
// Deprecated: use OutputType
|
||||
func (ast *Ast) ResultType() *exprpb.Type {
|
||||
if !ast.IsChecked() {
|
||||
return decls.Dyn
|
||||
}
|
||||
return ast.typeMap[ast.expr.GetId()]
|
||||
}
|
||||
|
||||
// OutputType returns the output type of the expression if the Ast has been type-checked, else
|
||||
// returns cel.DynType as the parse step cannot infer types.
|
||||
func (ast *Ast) OutputType() *Type {
|
||||
t, err := ExprTypeToType(ast.ResultType())
|
||||
if err != nil {
|
||||
return DynType
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// Source returns a view of the input used to create the Ast. This source may be complete or
|
||||
// constructed from the SourceInfo.
|
||||
func (ast *Ast) Source() Source {
|
||||
return ast.source
|
||||
}
|
||||
|
||||
// FormatType converts a type message into a string representation.
|
||||
func FormatType(t *exprpb.Type) string {
|
||||
return checker.FormatCheckedType(t)
|
||||
}
|
||||
|
||||
// Env encapsulates the context necessary to perform parsing, type checking, or generation of
|
||||
// evaluable programs for different expressions.
|
||||
type Env struct {
|
||||
Container *containers.Container
|
||||
functions map[string]*functionDecl
|
||||
declarations []*exprpb.Decl
|
||||
macros []parser.Macro
|
||||
adapter ref.TypeAdapter
|
||||
provider ref.TypeProvider
|
||||
features map[int]bool
|
||||
appliedFeatures map[int]bool
|
||||
|
||||
// Internal parser representation
|
||||
prsr *parser.Parser
|
||||
|
||||
// Internal checker representation
|
||||
chk *checker.Env
|
||||
chkErr error
|
||||
chkOnce sync.Once
|
||||
chkOpts []checker.Option
|
||||
|
||||
// Program options tied to the environment
|
||||
progOpts []ProgramOption
|
||||
}
|
||||
|
||||
// NewEnv creates a program environment configured with the standard library of CEL functions and
|
||||
// macros. The Env value returned can parse and check any CEL program which builds upon the core
|
||||
// features documented in the CEL specification.
|
||||
//
|
||||
// See the EnvOption helper functions for the options that can be used to configure the
|
||||
// environment.
|
||||
func NewEnv(opts ...EnvOption) (*Env, error) {
|
||||
// Extend the statically configured standard environment, disabling eager validation to ensure
|
||||
// the cost of setup for the environment is still just as cheap as it is in v0.11.x and earlier
|
||||
// releases. The user provided options can easily re-enable the eager validation as they are
|
||||
// processed after this default option.
|
||||
stdOpts := append([]EnvOption{EagerlyValidateDeclarations(false)}, opts...)
|
||||
env, err := getStdEnv()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return env.Extend(stdOpts...)
|
||||
}
|
||||
|
||||
// NewCustomEnv creates a custom program environment which is not automatically configured with the
|
||||
// standard library of functions and macros documented in the CEL spec.
|
||||
//
|
||||
// The purpose for using a custom environment might be for subsetting the standard library produced
|
||||
// by the cel.StdLib() function. Subsetting CEL is a core aspect of its design that allows users to
|
||||
// limit the compute and memory impact of a CEL program by controlling the functions and macros
|
||||
// that may appear in a given expression.
|
||||
//
|
||||
// See the EnvOption helper functions for the options that can be used to configure the
|
||||
// environment.
|
||||
func NewCustomEnv(opts ...EnvOption) (*Env, error) {
|
||||
registry, err := types.NewRegistry()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return (&Env{
|
||||
declarations: []*exprpb.Decl{},
|
||||
functions: map[string]*functionDecl{},
|
||||
macros: []parser.Macro{},
|
||||
Container: containers.DefaultContainer,
|
||||
adapter: registry,
|
||||
provider: registry,
|
||||
features: map[int]bool{},
|
||||
appliedFeatures: map[int]bool{},
|
||||
progOpts: []ProgramOption{},
|
||||
}).configure(opts)
|
||||
}
|
||||
|
||||
// Check performs type-checking on the input Ast and yields a checked Ast and/or set of Issues.
|
||||
//
|
||||
// Checking has failed if the returned Issues value and its Issues.Err() value are non-nil.
|
||||
// Issues should be inspected if they are non-nil, but may not represent a fatal error.
|
||||
//
|
||||
// It is possible to have both non-nil Ast and Issues values returned from this call: however,
|
||||
// the mere presence of an Ast does not imply that it is valid for use.
|
||||
func (e *Env) Check(ast *Ast) (*Ast, *Issues) {
|
||||
// Note, errors aren't currently possible on the Ast to ParsedExpr conversion.
|
||||
pe, _ := AstToParsedExpr(ast)
|
||||
|
||||
// Construct the internal checker env, erroring if there is an issue adding the declarations.
|
||||
err := e.initChecker()
|
||||
if err != nil {
|
||||
errs := common.NewErrors(ast.Source())
|
||||
errs.ReportError(common.NoLocation, e.chkErr.Error())
|
||||
return nil, NewIssues(errs)
|
||||
}
|
||||
|
||||
res, errs := checker.Check(pe, ast.Source(), e.chk)
|
||||
if len(errs.GetErrors()) > 0 {
|
||||
return nil, NewIssues(errs)
|
||||
}
|
||||
// Manually create the Ast to ensure that the Ast source information (which may be more
|
||||
// detailed than the information provided by Check), is returned to the caller.
|
||||
return &Ast{
|
||||
source: ast.Source(),
|
||||
expr: res.GetExpr(),
|
||||
info: res.GetSourceInfo(),
|
||||
refMap: res.GetReferenceMap(),
|
||||
typeMap: res.GetTypeMap()}, nil
|
||||
}
|
||||
|
||||
// Compile combines the Parse and Check phases CEL program compilation to produce an Ast and
|
||||
// associated issues.
|
||||
//
|
||||
// If an error is encountered during parsing the Compile step will not continue with the Check
|
||||
// phase. If non-error issues are encountered during Parse, they may be combined with any issues
|
||||
// discovered during Check.
|
||||
//
|
||||
// Note, for parse-only uses of CEL use Parse.
|
||||
func (e *Env) Compile(txt string) (*Ast, *Issues) {
|
||||
return e.CompileSource(common.NewTextSource(txt))
|
||||
}
|
||||
|
||||
// CompileSource combines the Parse and Check phases CEL program compilation to produce an Ast and
|
||||
// associated issues.
|
||||
//
|
||||
// If an error is encountered during parsing the CompileSource step will not continue with the
|
||||
// Check phase. If non-error issues are encountered during Parse, they may be combined with any
|
||||
// issues discovered during Check.
|
||||
//
|
||||
// Note, for parse-only uses of CEL use Parse.
|
||||
func (e *Env) CompileSource(src Source) (*Ast, *Issues) {
|
||||
ast, iss := e.ParseSource(src)
|
||||
if iss.Err() != nil {
|
||||
return nil, iss
|
||||
}
|
||||
checked, iss2 := e.Check(ast)
|
||||
if iss2.Err() != nil {
|
||||
return nil, iss2
|
||||
}
|
||||
return checked, iss2
|
||||
}
|
||||
|
||||
// Extend the current environment with additional options to produce a new Env.
|
||||
//
|
||||
// Note, the extended Env value should not share memory with the original. It is possible, however,
|
||||
// that a CustomTypeAdapter or CustomTypeProvider options could provide values which are mutable.
|
||||
// To ensure separation of state between extended environments either make sure the TypeAdapter and
|
||||
// TypeProvider are immutable, or that their underlying implementations are based on the
|
||||
// ref.TypeRegistry which provides a Copy method which will be invoked by this method.
|
||||
func (e *Env) Extend(opts ...EnvOption) (*Env, error) {
|
||||
if e.chkErr != nil {
|
||||
return nil, e.chkErr
|
||||
}
|
||||
|
||||
// The type-checker is configured with Declarations. The declarations may either be provided
|
||||
// as options which have not yet been validated, or may come from a previous checker instance
|
||||
// whose types have already been validated.
|
||||
chkOptsCopy := make([]checker.Option, len(e.chkOpts))
|
||||
copy(chkOptsCopy, e.chkOpts)
|
||||
|
||||
// Copy the declarations if needed.
|
||||
decsCopy := []*exprpb.Decl{}
|
||||
if e.chk != nil {
|
||||
// If the type-checker has already been instantiated, then the e.declarations have been
|
||||
// valdiated within the chk instance.
|
||||
chkOptsCopy = append(chkOptsCopy, checker.ValidatedDeclarations(e.chk))
|
||||
} else {
|
||||
// If the type-checker has not been instantiated, ensure the unvalidated declarations are
|
||||
// provided to the extended Env instance.
|
||||
decsCopy = make([]*exprpb.Decl, len(e.declarations))
|
||||
copy(decsCopy, e.declarations)
|
||||
}
|
||||
|
||||
// Copy macros and program options
|
||||
macsCopy := make([]parser.Macro, len(e.macros))
|
||||
progOptsCopy := make([]ProgramOption, len(e.progOpts))
|
||||
copy(macsCopy, e.macros)
|
||||
copy(progOptsCopy, e.progOpts)
|
||||
|
||||
// Copy the adapter / provider if they appear to be mutable.
|
||||
adapter := e.adapter
|
||||
provider := e.provider
|
||||
adapterReg, isAdapterReg := e.adapter.(ref.TypeRegistry)
|
||||
providerReg, isProviderReg := e.provider.(ref.TypeRegistry)
|
||||
// In most cases the provider and adapter will be a ref.TypeRegistry;
|
||||
// however, in the rare cases where they are not, they are assumed to
|
||||
// be immutable. Since it is possible to set the TypeProvider separately
|
||||
// from the TypeAdapter, the possible configurations which could use a
|
||||
// TypeRegistry as the base implementation are captured below.
|
||||
if isAdapterReg && isProviderReg {
|
||||
reg := providerReg.Copy()
|
||||
provider = reg
|
||||
// If the adapter and provider are the same object, set the adapter
|
||||
// to the same ref.TypeRegistry as the provider.
|
||||
if adapterReg == providerReg {
|
||||
adapter = reg
|
||||
} else {
|
||||
// Otherwise, make a copy of the adapter.
|
||||
adapter = adapterReg.Copy()
|
||||
}
|
||||
} else if isProviderReg {
|
||||
provider = providerReg.Copy()
|
||||
} else if isAdapterReg {
|
||||
adapter = adapterReg.Copy()
|
||||
}
|
||||
|
||||
featuresCopy := make(map[int]bool, len(e.features))
|
||||
for k, v := range e.features {
|
||||
featuresCopy[k] = v
|
||||
}
|
||||
appliedFeaturesCopy := make(map[int]bool, len(e.appliedFeatures))
|
||||
for k, v := range e.appliedFeatures {
|
||||
appliedFeaturesCopy[k] = v
|
||||
}
|
||||
funcsCopy := make(map[string]*functionDecl, len(e.functions))
|
||||
for k, v := range e.functions {
|
||||
funcsCopy[k] = v
|
||||
}
|
||||
|
||||
// TODO: functions copy needs to happen here.
|
||||
ext := &Env{
|
||||
Container: e.Container,
|
||||
declarations: decsCopy,
|
||||
functions: funcsCopy,
|
||||
macros: macsCopy,
|
||||
progOpts: progOptsCopy,
|
||||
adapter: adapter,
|
||||
features: featuresCopy,
|
||||
appliedFeatures: appliedFeaturesCopy,
|
||||
provider: provider,
|
||||
chkOpts: chkOptsCopy,
|
||||
}
|
||||
return ext.configure(opts)
|
||||
}
|
||||
|
||||
// HasFeature checks whether the environment enables the given feature
|
||||
// flag, as enumerated in options.go.
|
||||
func (e *Env) HasFeature(flag int) bool {
|
||||
enabled, has := e.features[flag]
|
||||
return has && enabled
|
||||
}
|
||||
|
||||
// Parse parses the input expression value `txt` to a Ast and/or a set of Issues.
|
||||
//
|
||||
// This form of Parse creates a Source value for the input `txt` and forwards to the
|
||||
// ParseSource method.
|
||||
func (e *Env) Parse(txt string) (*Ast, *Issues) {
|
||||
src := common.NewTextSource(txt)
|
||||
return e.ParseSource(src)
|
||||
}
|
||||
|
||||
// ParseSource parses the input source to an Ast and/or set of Issues.
|
||||
//
|
||||
// Parsing has failed if the returned Issues value and its Issues.Err() value is non-nil.
|
||||
// Issues should be inspected if they are non-nil, but may not represent a fatal error.
|
||||
//
|
||||
// It is possible to have both non-nil Ast and Issues values returned from this call; however,
|
||||
// the mere presence of an Ast does not imply that it is valid for use.
|
||||
func (e *Env) ParseSource(src Source) (*Ast, *Issues) {
|
||||
res, errs := e.prsr.Parse(src)
|
||||
if len(errs.GetErrors()) > 0 {
|
||||
return nil, &Issues{errs: errs}
|
||||
}
|
||||
// Manually create the Ast to ensure that the text source information is propagated on
|
||||
// subsequent calls to Check.
|
||||
return &Ast{
|
||||
source: src,
|
||||
expr: res.GetExpr(),
|
||||
info: res.GetSourceInfo()}, nil
|
||||
}
|
||||
|
||||
// Program generates an evaluable instance of the Ast within the environment (Env).
|
||||
func (e *Env) Program(ast *Ast, opts ...ProgramOption) (Program, error) {
|
||||
optSet := e.progOpts
|
||||
if len(opts) != 0 {
|
||||
mergedOpts := []ProgramOption{}
|
||||
mergedOpts = append(mergedOpts, e.progOpts...)
|
||||
mergedOpts = append(mergedOpts, opts...)
|
||||
optSet = mergedOpts
|
||||
}
|
||||
return newProgram(e, ast, optSet)
|
||||
}
|
||||
|
||||
// TypeAdapter returns the `ref.TypeAdapter` configured for the environment.
|
||||
func (e *Env) TypeAdapter() ref.TypeAdapter {
|
||||
return e.adapter
|
||||
}
|
||||
|
||||
// TypeProvider returns the `ref.TypeProvider` configured for the environment.
|
||||
func (e *Env) TypeProvider() ref.TypeProvider {
|
||||
return e.provider
|
||||
}
|
||||
|
||||
// UnknownVars returns an interpreter.PartialActivation which marks all variables
|
||||
// declared in the Env as unknown AttributePattern values.
|
||||
//
|
||||
// Note, the UnknownVars will behave the same as an interpreter.EmptyActivation
|
||||
// unless the PartialAttributes option is provided as a ProgramOption.
|
||||
func (e *Env) UnknownVars() interpreter.PartialActivation {
|
||||
var unknownPatterns []*interpreter.AttributePattern
|
||||
for _, d := range e.declarations {
|
||||
switch d.GetDeclKind().(type) {
|
||||
case *exprpb.Decl_Ident:
|
||||
unknownPatterns = append(unknownPatterns,
|
||||
interpreter.NewAttributePattern(d.GetName()))
|
||||
}
|
||||
}
|
||||
part, _ := PartialVars(
|
||||
interpreter.EmptyActivation(),
|
||||
unknownPatterns...)
|
||||
return part
|
||||
}
|
||||
|
||||
// ResidualAst takes an Ast and its EvalDetails to produce a new Ast which only contains the
|
||||
// attribute references which are unknown.
|
||||
//
|
||||
// Residual expressions are beneficial in a few scenarios:
|
||||
//
|
||||
// - Optimizing constant expression evaluations away.
|
||||
// - Indexing and pruning expressions based on known input arguments.
|
||||
// - Surfacing additional requirements that are needed in order to complete an evaluation.
|
||||
// - Sharing the evaluation of an expression across multiple machines/nodes.
|
||||
//
|
||||
// For example, if an expression targets a 'resource' and 'request' attribute and the possible
|
||||
// values for the resource are known, a PartialActivation could mark the 'request' as an unknown
|
||||
// interpreter.AttributePattern and the resulting ResidualAst would be reduced to only the parts
|
||||
// of the expression that reference the 'request'.
|
||||
//
|
||||
// Note, the expression ids within the residual AST generated through this method have no
|
||||
// correlation to the expression ids of the original AST.
|
||||
//
|
||||
// See the PartialVars helper for how to construct a PartialActivation.
|
||||
//
|
||||
// TODO: Consider adding an option to generate a Program.Residual to avoid round-tripping to an
|
||||
// Ast format and then Program again.
|
||||
func (e *Env) ResidualAst(a *Ast, details *EvalDetails) (*Ast, error) {
|
||||
pruned := interpreter.PruneAst(a.Expr(), details.State())
|
||||
expr, err := AstToString(ParsedExprToAst(&exprpb.ParsedExpr{Expr: pruned}))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parsed, iss := e.Parse(expr)
|
||||
if iss != nil && iss.Err() != nil {
|
||||
return nil, iss.Err()
|
||||
}
|
||||
if !a.IsChecked() {
|
||||
return parsed, nil
|
||||
}
|
||||
checked, iss := e.Check(parsed)
|
||||
if iss != nil && iss.Err() != nil {
|
||||
return nil, iss.Err()
|
||||
}
|
||||
return checked, nil
|
||||
}
|
||||
|
||||
// EstimateCost estimates the cost of a type checked CEL expression using the length estimates of input data and
|
||||
// extension functions provided by estimator.
|
||||
func (e *Env) EstimateCost(ast *Ast, estimator checker.CostEstimator) (checker.CostEstimate, error) {
|
||||
checked, err := AstToCheckedExpr(ast)
|
||||
if err != nil {
|
||||
return checker.CostEstimate{}, fmt.Errorf("EsimateCost could not inspect Ast: %v", err)
|
||||
}
|
||||
return checker.Cost(checked, estimator), nil
|
||||
}
|
||||
|
||||
// configure applies a series of EnvOptions to the current environment.
|
||||
func (e *Env) configure(opts []EnvOption) (*Env, error) {
|
||||
// Customized the environment using the provided EnvOption values. If an error is
|
||||
// generated at any step this, will be returned as a nil Env with a non-nil error.
|
||||
var err error
|
||||
for _, opt := range opts {
|
||||
e, err = opt(e)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// If the default UTC timezone fix has been enabled, make sure the library is configured
|
||||
if e.HasFeature(featureDefaultUTCTimeZone) {
|
||||
if _, found := e.appliedFeatures[featureDefaultUTCTimeZone]; !found {
|
||||
e, err = Lib(timeUTCLibrary{})(e)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// record that the feature has been applied since it will generate declarations
|
||||
// and functions which will be propagated on Extend() calls and which should only
|
||||
// be registered once.
|
||||
e.appliedFeatures[featureDefaultUTCTimeZone] = true
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize all of the functions configured within the environment.
|
||||
for _, fn := range e.functions {
|
||||
err = fn.init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Configure the parser.
|
||||
prsrOpts := []parser.Option{parser.Macros(e.macros...)}
|
||||
if e.HasFeature(featureEnableMacroCallTracking) {
|
||||
prsrOpts = append(prsrOpts, parser.PopulateMacroCalls(true))
|
||||
}
|
||||
e.prsr, err = parser.NewParser(prsrOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Ensure that the checker init happens eagerly rather than lazily.
|
||||
if e.HasFeature(featureEagerlyValidateDeclarations) {
|
||||
err := e.initChecker()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
||||
|
||||
func (e *Env) initChecker() error {
|
||||
e.chkOnce.Do(func() {
|
||||
chkOpts := []checker.Option{}
|
||||
chkOpts = append(chkOpts, e.chkOpts...)
|
||||
chkOpts = append(chkOpts,
|
||||
checker.HomogeneousAggregateLiterals(
|
||||
e.HasFeature(featureDisableDynamicAggregateLiterals)),
|
||||
checker.CrossTypeNumericComparisons(
|
||||
e.HasFeature(featureCrossTypeNumericComparisons)))
|
||||
|
||||
ce, err := checker.NewEnv(e.Container, e.provider, chkOpts...)
|
||||
if err != nil {
|
||||
e.chkErr = err
|
||||
return
|
||||
}
|
||||
// Add the statically configured declarations.
|
||||
err = ce.Add(e.declarations...)
|
||||
if err != nil {
|
||||
e.chkErr = err
|
||||
return
|
||||
}
|
||||
// Add the function declarations which are derived from the FunctionDecl instances.
|
||||
for _, fn := range e.functions {
|
||||
fnDecl, err := functionDeclToExprDecl(fn)
|
||||
if err != nil {
|
||||
e.chkErr = err
|
||||
return
|
||||
}
|
||||
err = ce.Add(fnDecl)
|
||||
if err != nil {
|
||||
e.chkErr = err
|
||||
return
|
||||
}
|
||||
}
|
||||
// Add function declarations here separately.
|
||||
e.chk = ce
|
||||
})
|
||||
return e.chkErr
|
||||
}
|
||||
|
||||
// Issues defines methods for inspecting the error details of parse and check calls.
|
||||
//
|
||||
// Note: in the future, non-fatal warnings and notices may be inspectable via the Issues struct.
|
||||
type Issues struct {
|
||||
errs *common.Errors
|
||||
}
|
||||
|
||||
// NewIssues returns an Issues struct from a common.Errors object.
|
||||
func NewIssues(errs *common.Errors) *Issues {
|
||||
return &Issues{
|
||||
errs: errs,
|
||||
}
|
||||
}
|
||||
|
||||
// Err returns an error value if the issues list contains one or more errors.
|
||||
func (i *Issues) Err() error {
|
||||
if i == nil {
|
||||
return nil
|
||||
}
|
||||
if len(i.Errors()) > 0 {
|
||||
return errors.New(i.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Errors returns the collection of errors encountered in more granular detail.
|
||||
func (i *Issues) Errors() []common.Error {
|
||||
if i == nil {
|
||||
return []common.Error{}
|
||||
}
|
||||
return i.errs.GetErrors()
|
||||
}
|
||||
|
||||
// Append collects the issues from another Issues struct into a new Issues object.
|
||||
func (i *Issues) Append(other *Issues) *Issues {
|
||||
if i == nil {
|
||||
return other
|
||||
}
|
||||
if other == nil {
|
||||
return i
|
||||
}
|
||||
return NewIssues(i.errs.Append(other.errs.GetErrors()))
|
||||
}
|
||||
|
||||
// String converts the issues to a suitable display string.
|
||||
func (i *Issues) String() string {
|
||||
if i == nil {
|
||||
return ""
|
||||
}
|
||||
return i.errs.ToDisplayString()
|
||||
}
|
||||
|
||||
// getStdEnv lazy initializes the CEL standard environment.
|
||||
func getStdEnv() (*Env, error) {
|
||||
stdEnvInit.Do(func() {
|
||||
stdEnv, stdEnvErr = NewCustomEnv(StdLib(), EagerlyValidateDeclarations(true))
|
||||
})
|
||||
return stdEnv, stdEnvErr
|
||||
}
|
||||
|
||||
var (
|
||||
stdEnvInit sync.Once
|
||||
stdEnv *Env
|
||||
stdEnvErr error
|
||||
)
|
280
vendor/github.com/google/cel-go/cel/io.go
generated
vendored
Normal file
280
vendor/github.com/google/cel-go/cel/io.go
generated
vendored
Normal file
@ -0,0 +1,280 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// 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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/google/cel-go/common"
|
||||
"github.com/google/cel-go/common/types"
|
||||
"github.com/google/cel-go/common/types/ref"
|
||||
"github.com/google/cel-go/common/types/traits"
|
||||
"github.com/google/cel-go/parser"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
|
||||
anypb "google.golang.org/protobuf/types/known/anypb"
|
||||
)
|
||||
|
||||
// CheckedExprToAst converts a checked expression proto message to an Ast.
|
||||
func CheckedExprToAst(checkedExpr *exprpb.CheckedExpr) *Ast {
|
||||
return CheckedExprToAstWithSource(checkedExpr, nil)
|
||||
}
|
||||
|
||||
// CheckedExprToAstWithSource converts a checked expression proto message to an Ast,
|
||||
// using the provided Source as the textual contents.
|
||||
//
|
||||
// In general the source is not necessary unless the AST has been modified between the
|
||||
// `Parse` and `Check` calls as an `Ast` created from the `Parse` step will carry the source
|
||||
// through future calls.
|
||||
//
|
||||
// Prefer CheckedExprToAst if loading expressions from storage.
|
||||
func CheckedExprToAstWithSource(checkedExpr *exprpb.CheckedExpr, src Source) *Ast {
|
||||
refMap := checkedExpr.GetReferenceMap()
|
||||
if refMap == nil {
|
||||
refMap = map[int64]*exprpb.Reference{}
|
||||
}
|
||||
typeMap := checkedExpr.GetTypeMap()
|
||||
if typeMap == nil {
|
||||
typeMap = map[int64]*exprpb.Type{}
|
||||
}
|
||||
si := checkedExpr.GetSourceInfo()
|
||||
if si == nil {
|
||||
si = &exprpb.SourceInfo{}
|
||||
}
|
||||
if src == nil {
|
||||
src = common.NewInfoSource(si)
|
||||
}
|
||||
return &Ast{
|
||||
expr: checkedExpr.GetExpr(),
|
||||
info: si,
|
||||
source: src,
|
||||
refMap: refMap,
|
||||
typeMap: typeMap,
|
||||
}
|
||||
}
|
||||
|
||||
// AstToCheckedExpr converts an Ast to an protobuf CheckedExpr value.
|
||||
//
|
||||
// If the Ast.IsChecked() returns false, this conversion method will return an error.
|
||||
func AstToCheckedExpr(a *Ast) (*exprpb.CheckedExpr, error) {
|
||||
if !a.IsChecked() {
|
||||
return nil, fmt.Errorf("cannot convert unchecked ast")
|
||||
}
|
||||
return &exprpb.CheckedExpr{
|
||||
Expr: a.Expr(),
|
||||
SourceInfo: a.SourceInfo(),
|
||||
ReferenceMap: a.refMap,
|
||||
TypeMap: a.typeMap,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ParsedExprToAst converts a parsed expression proto message to an Ast.
|
||||
func ParsedExprToAst(parsedExpr *exprpb.ParsedExpr) *Ast {
|
||||
return ParsedExprToAstWithSource(parsedExpr, nil)
|
||||
}
|
||||
|
||||
// ParsedExprToAstWithSource converts a parsed expression proto message to an Ast,
|
||||
// using the provided Source as the textual contents.
|
||||
//
|
||||
// In general you only need this if you need to recheck a previously checked
|
||||
// expression, or if you need to separately check a subset of an expression.
|
||||
//
|
||||
// Prefer ParsedExprToAst if loading expressions from storage.
|
||||
func ParsedExprToAstWithSource(parsedExpr *exprpb.ParsedExpr, src Source) *Ast {
|
||||
si := parsedExpr.GetSourceInfo()
|
||||
if si == nil {
|
||||
si = &exprpb.SourceInfo{}
|
||||
}
|
||||
if src == nil {
|
||||
src = common.NewInfoSource(si)
|
||||
}
|
||||
return &Ast{
|
||||
expr: parsedExpr.GetExpr(),
|
||||
info: si,
|
||||
source: src,
|
||||
}
|
||||
}
|
||||
|
||||
// AstToParsedExpr converts an Ast to an protobuf ParsedExpr value.
|
||||
func AstToParsedExpr(a *Ast) (*exprpb.ParsedExpr, error) {
|
||||
return &exprpb.ParsedExpr{
|
||||
Expr: a.Expr(),
|
||||
SourceInfo: a.SourceInfo(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AstToString converts an Ast back to a string if possible.
|
||||
//
|
||||
// Note, the conversion may not be an exact replica of the original expression, but will produce
|
||||
// a string that is semantically equivalent and whose textual representation is stable.
|
||||
func AstToString(a *Ast) (string, error) {
|
||||
expr := a.Expr()
|
||||
info := a.SourceInfo()
|
||||
return parser.Unparse(expr, info)
|
||||
}
|
||||
|
||||
// RefValueToValue converts between ref.Val and api.expr.Value.
|
||||
// The result Value is the serialized proto form. The ref.Val must not be error or unknown.
|
||||
func RefValueToValue(res ref.Val) (*exprpb.Value, error) {
|
||||
switch res.Type() {
|
||||
case types.BoolType:
|
||||
return &exprpb.Value{
|
||||
Kind: &exprpb.Value_BoolValue{BoolValue: res.Value().(bool)}}, nil
|
||||
case types.BytesType:
|
||||
return &exprpb.Value{
|
||||
Kind: &exprpb.Value_BytesValue{BytesValue: res.Value().([]byte)}}, nil
|
||||
case types.DoubleType:
|
||||
return &exprpb.Value{
|
||||
Kind: &exprpb.Value_DoubleValue{DoubleValue: res.Value().(float64)}}, nil
|
||||
case types.IntType:
|
||||
return &exprpb.Value{
|
||||
Kind: &exprpb.Value_Int64Value{Int64Value: res.Value().(int64)}}, nil
|
||||
case types.ListType:
|
||||
l := res.(traits.Lister)
|
||||
sz := l.Size().(types.Int)
|
||||
elts := make([]*exprpb.Value, 0, int64(sz))
|
||||
for i := types.Int(0); i < sz; i++ {
|
||||
v, err := RefValueToValue(l.Get(i))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
elts = append(elts, v)
|
||||
}
|
||||
return &exprpb.Value{
|
||||
Kind: &exprpb.Value_ListValue{
|
||||
ListValue: &exprpb.ListValue{Values: elts}}}, nil
|
||||
case types.MapType:
|
||||
mapper := res.(traits.Mapper)
|
||||
sz := mapper.Size().(types.Int)
|
||||
entries := make([]*exprpb.MapValue_Entry, 0, int64(sz))
|
||||
for it := mapper.Iterator(); it.HasNext().(types.Bool); {
|
||||
k := it.Next()
|
||||
v := mapper.Get(k)
|
||||
kv, err := RefValueToValue(k)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vv, err := RefValueToValue(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entries = append(entries, &exprpb.MapValue_Entry{Key: kv, Value: vv})
|
||||
}
|
||||
return &exprpb.Value{
|
||||
Kind: &exprpb.Value_MapValue{
|
||||
MapValue: &exprpb.MapValue{Entries: entries}}}, nil
|
||||
case types.NullType:
|
||||
return &exprpb.Value{
|
||||
Kind: &exprpb.Value_NullValue{}}, nil
|
||||
case types.StringType:
|
||||
return &exprpb.Value{
|
||||
Kind: &exprpb.Value_StringValue{StringValue: res.Value().(string)}}, nil
|
||||
case types.TypeType:
|
||||
typeName := res.(ref.Type).TypeName()
|
||||
return &exprpb.Value{Kind: &exprpb.Value_TypeValue{TypeValue: typeName}}, nil
|
||||
case types.UintType:
|
||||
return &exprpb.Value{
|
||||
Kind: &exprpb.Value_Uint64Value{Uint64Value: res.Value().(uint64)}}, nil
|
||||
default:
|
||||
any, err := res.ConvertToNative(anyPbType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &exprpb.Value{
|
||||
Kind: &exprpb.Value_ObjectValue{ObjectValue: any.(*anypb.Any)}}, nil
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
typeNameToTypeValue = map[string]*types.TypeValue{
|
||||
"bool": types.BoolType,
|
||||
"bytes": types.BytesType,
|
||||
"double": types.DoubleType,
|
||||
"null_type": types.NullType,
|
||||
"int": types.IntType,
|
||||
"list": types.ListType,
|
||||
"map": types.MapType,
|
||||
"string": types.StringType,
|
||||
"type": types.TypeType,
|
||||
"uint": types.UintType,
|
||||
}
|
||||
|
||||
anyPbType = reflect.TypeOf(&anypb.Any{})
|
||||
)
|
||||
|
||||
// ValueToRefValue converts between exprpb.Value and ref.Val.
|
||||
func ValueToRefValue(adapter ref.TypeAdapter, v *exprpb.Value) (ref.Val, error) {
|
||||
switch v.Kind.(type) {
|
||||
case *exprpb.Value_NullValue:
|
||||
return types.NullValue, nil
|
||||
case *exprpb.Value_BoolValue:
|
||||
return types.Bool(v.GetBoolValue()), nil
|
||||
case *exprpb.Value_Int64Value:
|
||||
return types.Int(v.GetInt64Value()), nil
|
||||
case *exprpb.Value_Uint64Value:
|
||||
return types.Uint(v.GetUint64Value()), nil
|
||||
case *exprpb.Value_DoubleValue:
|
||||
return types.Double(v.GetDoubleValue()), nil
|
||||
case *exprpb.Value_StringValue:
|
||||
return types.String(v.GetStringValue()), nil
|
||||
case *exprpb.Value_BytesValue:
|
||||
return types.Bytes(v.GetBytesValue()), nil
|
||||
case *exprpb.Value_ObjectValue:
|
||||
any := v.GetObjectValue()
|
||||
msg, err := anypb.UnmarshalNew(any, proto.UnmarshalOptions{DiscardUnknown: true})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return adapter.NativeToValue(msg), nil
|
||||
case *exprpb.Value_MapValue:
|
||||
m := v.GetMapValue()
|
||||
entries := make(map[ref.Val]ref.Val)
|
||||
for _, entry := range m.Entries {
|
||||
key, err := ValueToRefValue(adapter, entry.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pb, err := ValueToRefValue(adapter, entry.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entries[key] = pb
|
||||
}
|
||||
return adapter.NativeToValue(entries), nil
|
||||
case *exprpb.Value_ListValue:
|
||||
l := v.GetListValue()
|
||||
elts := make([]ref.Val, len(l.Values))
|
||||
for i, e := range l.Values {
|
||||
rv, err := ValueToRefValue(adapter, e)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
elts[i] = rv
|
||||
}
|
||||
return adapter.NativeToValue(elts), nil
|
||||
case *exprpb.Value_TypeValue:
|
||||
typeName := v.GetTypeValue()
|
||||
tv, ok := typeNameToTypeValue[typeName]
|
||||
if ok {
|
||||
return tv, nil
|
||||
}
|
||||
return types.NewObjectTypeValue(typeName), nil
|
||||
}
|
||||
return nil, errors.New("unknown value")
|
||||
}
|
343
vendor/github.com/google/cel-go/cel/library.go
generated
vendored
Normal file
343
vendor/github.com/google/cel-go/cel/library.go
generated
vendored
Normal file
@ -0,0 +1,343 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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 (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/cel-go/checker"
|
||||
"github.com/google/cel-go/common/overloads"
|
||||
"github.com/google/cel-go/common/types"
|
||||
"github.com/google/cel-go/common/types/ref"
|
||||
"github.com/google/cel-go/interpreter/functions"
|
||||
)
|
||||
|
||||
// Library provides a collection of EnvOption and ProgramOption values used to configure a CEL
|
||||
// environment for a particular use case or with a related set of functionality.
|
||||
//
|
||||
// Note, the ProgramOption values provided by a library are expected to be static and not vary
|
||||
// between calls to Env.Program(). If there is a need for such dynamic configuration, prefer to
|
||||
// configure these options outside the Library and within the Env.Program() call directly.
|
||||
type Library interface {
|
||||
// CompileOptions returns a collection of functional options for configuring the Parse / Check
|
||||
// environment.
|
||||
CompileOptions() []EnvOption
|
||||
|
||||
// ProgramOptions returns a collection of functional options which should be included in every
|
||||
// Program generated from the Env.Program() call.
|
||||
ProgramOptions() []ProgramOption
|
||||
}
|
||||
|
||||
// Lib creates an EnvOption out of a Library, allowing libraries to be provided as functional args,
|
||||
// and to be linked to each other.
|
||||
func Lib(l Library) EnvOption {
|
||||
return func(e *Env) (*Env, error) {
|
||||
var err error
|
||||
for _, opt := range l.CompileOptions() {
|
||||
e, err = opt(e)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
e.progOpts = append(e.progOpts, l.ProgramOptions()...)
|
||||
return e, nil
|
||||
}
|
||||
}
|
||||
|
||||
// StdLib returns an EnvOption for the standard library of CEL functions and macros.
|
||||
func StdLib() EnvOption {
|
||||
return Lib(stdLibrary{})
|
||||
}
|
||||
|
||||
// stdLibrary implements the Library interface and provides functional options for the core CEL
|
||||
// features documented in the specification.
|
||||
type stdLibrary struct{}
|
||||
|
||||
// EnvOptions returns options for the standard CEL function declarations and macros.
|
||||
func (stdLibrary) CompileOptions() []EnvOption {
|
||||
return []EnvOption{
|
||||
Declarations(checker.StandardDeclarations()...),
|
||||
Macros(StandardMacros...),
|
||||
}
|
||||
}
|
||||
|
||||
// ProgramOptions returns function implementations for the standard CEL functions.
|
||||
func (stdLibrary) ProgramOptions() []ProgramOption {
|
||||
return []ProgramOption{
|
||||
Functions(functions.StandardOverloads()...),
|
||||
}
|
||||
}
|
||||
|
||||
type timeUTCLibrary struct{}
|
||||
|
||||
func (timeUTCLibrary) CompileOptions() []EnvOption {
|
||||
return timeOverloadDeclarations
|
||||
}
|
||||
|
||||
func (timeUTCLibrary) ProgramOptions() []ProgramOption {
|
||||
return []ProgramOption{}
|
||||
}
|
||||
|
||||
// Declarations and functions which enable using UTC on time.Time inputs when the timezone is unspecified
|
||||
// in the CEL expression.
|
||||
var (
|
||||
utcTZ = types.String("UTC")
|
||||
|
||||
timeOverloadDeclarations = []EnvOption{
|
||||
Function(overloads.TimeGetHours,
|
||||
MemberOverload(overloads.DurationToHours, []*Type{DurationType}, IntType,
|
||||
UnaryBinding(func(dur ref.Val) ref.Val {
|
||||
d := dur.(types.Duration)
|
||||
return types.Int(d.Hours())
|
||||
}))),
|
||||
Function(overloads.TimeGetMinutes,
|
||||
MemberOverload(overloads.DurationToMinutes, []*Type{DurationType}, IntType,
|
||||
UnaryBinding(func(dur ref.Val) ref.Val {
|
||||
d := dur.(types.Duration)
|
||||
return types.Int(d.Minutes())
|
||||
}))),
|
||||
Function(overloads.TimeGetSeconds,
|
||||
MemberOverload(overloads.DurationToSeconds, []*Type{DurationType}, IntType,
|
||||
UnaryBinding(func(dur ref.Val) ref.Val {
|
||||
d := dur.(types.Duration)
|
||||
return types.Int(d.Seconds())
|
||||
}))),
|
||||
Function(overloads.TimeGetMilliseconds,
|
||||
MemberOverload(overloads.DurationToMilliseconds, []*Type{DurationType}, IntType,
|
||||
UnaryBinding(func(dur ref.Val) ref.Val {
|
||||
d := dur.(types.Duration)
|
||||
return types.Int(d.Milliseconds())
|
||||
}))),
|
||||
Function(overloads.TimeGetFullYear,
|
||||
MemberOverload(overloads.TimestampToYear, []*Type{TimestampType}, IntType,
|
||||
UnaryBinding(func(ts ref.Val) ref.Val {
|
||||
return timestampGetFullYear(ts, utcTZ)
|
||||
}),
|
||||
),
|
||||
MemberOverload(overloads.TimestampToYearWithTz, []*Type{TimestampType, StringType}, IntType,
|
||||
BinaryBinding(timestampGetFullYear),
|
||||
),
|
||||
),
|
||||
Function(overloads.TimeGetMonth,
|
||||
MemberOverload(overloads.TimestampToMonth, []*Type{TimestampType}, IntType,
|
||||
UnaryBinding(func(ts ref.Val) ref.Val {
|
||||
return timestampGetMonth(ts, utcTZ)
|
||||
}),
|
||||
),
|
||||
MemberOverload(overloads.TimestampToMonthWithTz, []*Type{TimestampType, StringType}, IntType,
|
||||
BinaryBinding(timestampGetMonth),
|
||||
),
|
||||
),
|
||||
Function(overloads.TimeGetDayOfYear,
|
||||
MemberOverload(overloads.TimestampToDayOfYear, []*Type{TimestampType}, IntType,
|
||||
UnaryBinding(func(ts ref.Val) ref.Val {
|
||||
return timestampGetDayOfYear(ts, utcTZ)
|
||||
}),
|
||||
),
|
||||
MemberOverload(overloads.TimestampToDayOfYearWithTz, []*Type{TimestampType, StringType}, IntType,
|
||||
BinaryBinding(func(ts, tz ref.Val) ref.Val {
|
||||
return timestampGetDayOfYear(ts, tz)
|
||||
}),
|
||||
),
|
||||
),
|
||||
Function(overloads.TimeGetDayOfMonth,
|
||||
MemberOverload(overloads.TimestampToDayOfMonthZeroBased, []*Type{TimestampType}, IntType,
|
||||
UnaryBinding(func(ts ref.Val) ref.Val {
|
||||
return timestampGetDayOfMonthZeroBased(ts, utcTZ)
|
||||
}),
|
||||
),
|
||||
MemberOverload(overloads.TimestampToDayOfMonthZeroBasedWithTz, []*Type{TimestampType, StringType}, IntType,
|
||||
BinaryBinding(timestampGetDayOfMonthZeroBased),
|
||||
),
|
||||
),
|
||||
Function(overloads.TimeGetDate,
|
||||
MemberOverload(overloads.TimestampToDayOfMonthOneBased, []*Type{TimestampType}, IntType,
|
||||
UnaryBinding(func(ts ref.Val) ref.Val {
|
||||
return timestampGetDayOfMonthOneBased(ts, utcTZ)
|
||||
}),
|
||||
),
|
||||
MemberOverload(overloads.TimestampToDayOfMonthOneBasedWithTz, []*Type{TimestampType, StringType}, IntType,
|
||||
BinaryBinding(timestampGetDayOfMonthOneBased),
|
||||
),
|
||||
),
|
||||
Function(overloads.TimeGetDayOfWeek,
|
||||
MemberOverload(overloads.TimestampToDayOfWeek, []*Type{TimestampType}, IntType,
|
||||
UnaryBinding(func(ts ref.Val) ref.Val {
|
||||
return timestampGetDayOfWeek(ts, utcTZ)
|
||||
}),
|
||||
),
|
||||
MemberOverload(overloads.TimestampToDayOfWeekWithTz, []*Type{TimestampType, StringType}, IntType,
|
||||
BinaryBinding(timestampGetDayOfWeek),
|
||||
),
|
||||
),
|
||||
Function(overloads.TimeGetHours,
|
||||
MemberOverload(overloads.TimestampToHours, []*Type{TimestampType}, IntType,
|
||||
UnaryBinding(func(ts ref.Val) ref.Val {
|
||||
return timestampGetHours(ts, utcTZ)
|
||||
}),
|
||||
),
|
||||
MemberOverload(overloads.TimestampToHoursWithTz, []*Type{TimestampType, StringType}, IntType,
|
||||
BinaryBinding(timestampGetHours),
|
||||
),
|
||||
),
|
||||
Function(overloads.TimeGetMinutes,
|
||||
MemberOverload(overloads.TimestampToMinutes, []*Type{TimestampType}, IntType,
|
||||
UnaryBinding(func(ts ref.Val) ref.Val {
|
||||
return timestampGetMinutes(ts, utcTZ)
|
||||
}),
|
||||
),
|
||||
MemberOverload(overloads.TimestampToMinutesWithTz, []*Type{TimestampType, StringType}, IntType,
|
||||
BinaryBinding(timestampGetMinutes),
|
||||
),
|
||||
),
|
||||
Function(overloads.TimeGetSeconds,
|
||||
MemberOverload(overloads.TimestampToSeconds, []*Type{TimestampType}, IntType,
|
||||
UnaryBinding(func(ts ref.Val) ref.Val {
|
||||
return timestampGetSeconds(ts, utcTZ)
|
||||
}),
|
||||
),
|
||||
MemberOverload(overloads.TimestampToSecondsWithTz, []*Type{TimestampType, StringType}, IntType,
|
||||
BinaryBinding(timestampGetSeconds),
|
||||
),
|
||||
),
|
||||
Function(overloads.TimeGetMilliseconds,
|
||||
MemberOverload(overloads.TimestampToMilliseconds, []*Type{TimestampType}, IntType,
|
||||
UnaryBinding(func(ts ref.Val) ref.Val {
|
||||
return timestampGetMilliseconds(ts, utcTZ)
|
||||
}),
|
||||
),
|
||||
MemberOverload(overloads.TimestampToMillisecondsWithTz, []*Type{TimestampType, StringType}, IntType,
|
||||
BinaryBinding(timestampGetMilliseconds),
|
||||
),
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
func timestampGetFullYear(ts, tz ref.Val) ref.Val {
|
||||
t, err := inTimeZone(ts, tz)
|
||||
if err != nil {
|
||||
return types.NewErr(err.Error())
|
||||
}
|
||||
return types.Int(t.Year())
|
||||
}
|
||||
|
||||
func timestampGetMonth(ts, tz ref.Val) ref.Val {
|
||||
t, err := inTimeZone(ts, tz)
|
||||
if err != nil {
|
||||
return types.NewErr(err.Error())
|
||||
}
|
||||
// CEL spec indicates that the month should be 0-based, but the Time value
|
||||
// for Month() is 1-based.
|
||||
return types.Int(t.Month() - 1)
|
||||
}
|
||||
|
||||
func timestampGetDayOfYear(ts, tz ref.Val) ref.Val {
|
||||
t, err := inTimeZone(ts, tz)
|
||||
if err != nil {
|
||||
return types.NewErr(err.Error())
|
||||
}
|
||||
return types.Int(t.YearDay() - 1)
|
||||
}
|
||||
|
||||
func timestampGetDayOfMonthZeroBased(ts, tz ref.Val) ref.Val {
|
||||
t, err := inTimeZone(ts, tz)
|
||||
if err != nil {
|
||||
return types.NewErr(err.Error())
|
||||
}
|
||||
return types.Int(t.Day() - 1)
|
||||
}
|
||||
|
||||
func timestampGetDayOfMonthOneBased(ts, tz ref.Val) ref.Val {
|
||||
t, err := inTimeZone(ts, tz)
|
||||
if err != nil {
|
||||
return types.NewErr(err.Error())
|
||||
}
|
||||
return types.Int(t.Day())
|
||||
}
|
||||
|
||||
func timestampGetDayOfWeek(ts, tz ref.Val) ref.Val {
|
||||
t, err := inTimeZone(ts, tz)
|
||||
if err != nil {
|
||||
return types.NewErr(err.Error())
|
||||
}
|
||||
return types.Int(t.Weekday())
|
||||
}
|
||||
|
||||
func timestampGetHours(ts, tz ref.Val) ref.Val {
|
||||
t, err := inTimeZone(ts, tz)
|
||||
if err != nil {
|
||||
return types.NewErr(err.Error())
|
||||
}
|
||||
return types.Int(t.Hour())
|
||||
}
|
||||
|
||||
func timestampGetMinutes(ts, tz ref.Val) ref.Val {
|
||||
t, err := inTimeZone(ts, tz)
|
||||
if err != nil {
|
||||
return types.NewErr(err.Error())
|
||||
}
|
||||
return types.Int(t.Minute())
|
||||
}
|
||||
|
||||
func timestampGetSeconds(ts, tz ref.Val) ref.Val {
|
||||
t, err := inTimeZone(ts, tz)
|
||||
if err != nil {
|
||||
return types.NewErr(err.Error())
|
||||
}
|
||||
return types.Int(t.Second())
|
||||
}
|
||||
|
||||
func timestampGetMilliseconds(ts, tz ref.Val) ref.Val {
|
||||
t, err := inTimeZone(ts, tz)
|
||||
if err != nil {
|
||||
return types.NewErr(err.Error())
|
||||
}
|
||||
return types.Int(t.Nanosecond() / 1000000)
|
||||
}
|
||||
|
||||
func inTimeZone(ts, tz ref.Val) (time.Time, error) {
|
||||
t := ts.(types.Timestamp)
|
||||
val := string(tz.(types.String))
|
||||
ind := strings.Index(val, ":")
|
||||
if ind == -1 {
|
||||
loc, err := time.LoadLocation(val)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
return t.In(loc), nil
|
||||
}
|
||||
|
||||
// If the input is not the name of a timezone (for example, 'US/Central'), it should be a numerical offset from UTC
|
||||
// in the format ^(+|-)(0[0-9]|1[0-4]):[0-5][0-9]$. The numerical input is parsed in terms of hours and minutes.
|
||||
hr, err := strconv.Atoi(string(val[0:ind]))
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
min, err := strconv.Atoi(string(val[ind+1:]))
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
var offset int
|
||||
if string(val[0]) == "-" {
|
||||
offset = hr*60 - min
|
||||
} else {
|
||||
offset = hr*60 + min
|
||||
}
|
||||
secondsEastOfUTC := int((time.Duration(offset) * time.Minute).Seconds())
|
||||
timezone := time.FixedZone("", secondsEastOfUTC)
|
||||
return t.In(timezone), nil
|
||||
}
|
139
vendor/github.com/google/cel-go/cel/macro.go
generated
vendored
Normal file
139
vendor/github.com/google/cel-go/cel/macro.go
generated
vendored
Normal file
@ -0,0 +1,139 @@
|
||||
// Copyright 2022 Google LLC
|
||||
//
|
||||
// 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"
|
||||
"github.com/google/cel-go/parser"
|
||||
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
|
||||
)
|
||||
|
||||
// Macro describes a function signature to match and the MacroExpander to apply.
|
||||
//
|
||||
// Note: when a Macro should apply to multiple overloads (based on arg count) of a given function,
|
||||
// a Macro should be created per arg-count or as a var arg macro.
|
||||
type Macro = parser.Macro
|
||||
|
||||
// MacroExpander converts a call and its associated arguments into a new CEL abstract syntax tree, or an error
|
||||
// if the input arguments are not suitable for the expansion requirements for the macro in question.
|
||||
//
|
||||
// The MacroExpander accepts as arguments a MacroExprHelper as well as the arguments used in the function call
|
||||
// and produces as output an Expr ast node.
|
||||
//
|
||||
// Note: when the Macro.IsReceiverStyle() method returns true, the target argument will be nil.
|
||||
type MacroExpander = parser.MacroExpander
|
||||
|
||||
// MacroExprHelper exposes helper methods for creating new expressions within a CEL abstract syntax tree.
|
||||
type MacroExprHelper = parser.ExprHelper
|
||||
|
||||
// NewGlobalMacro creates a Macro for a global function with the specified arg count.
|
||||
func NewGlobalMacro(function string, argCount int, expander MacroExpander) Macro {
|
||||
return parser.NewGlobalMacro(function, argCount, expander)
|
||||
}
|
||||
|
||||
// NewReceiverMacro creates a Macro for a receiver function matching the specified arg count.
|
||||
func NewReceiverMacro(function string, argCount int, expander MacroExpander) Macro {
|
||||
return parser.NewReceiverMacro(function, argCount, expander)
|
||||
}
|
||||
|
||||
// NewGlobalVarArgMacro creates a Macro for a global function with a variable arg count.
|
||||
func NewGlobalVarArgMacro(function string, expander MacroExpander) Macro {
|
||||
return parser.NewGlobalVarArgMacro(function, expander)
|
||||
}
|
||||
|
||||
// NewReceiverVarArgMacro creates a Macro for a receiver function matching a variable arg count.
|
||||
func NewReceiverVarArgMacro(function string, expander MacroExpander) Macro {
|
||||
return parser.NewReceiverVarArgMacro(function, expander)
|
||||
}
|
||||
|
||||
// HasMacroExpander expands the input call arguments into a presence test, e.g. has(<operand>.field)
|
||||
func HasMacroExpander(meh MacroExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *common.Error) {
|
||||
return parser.MakeHas(meh, target, args)
|
||||
}
|
||||
|
||||
// ExistsMacroExpander expands the input call arguments into a comprehension that returns true if any of the
|
||||
// elements in the range match the predicate expressions:
|
||||
// <iterRange>.exists(<iterVar>, <predicate>)
|
||||
func ExistsMacroExpander(meh MacroExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *common.Error) {
|
||||
return parser.MakeExists(meh, target, args)
|
||||
}
|
||||
|
||||
// ExistsOneMacroExpander expands the input call arguments into a comprehension that returns true if exactly
|
||||
// one of the elements in the range match the predicate expressions:
|
||||
// <iterRange>.exists_one(<iterVar>, <predicate>)
|
||||
func ExistsOneMacroExpander(meh MacroExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *common.Error) {
|
||||
return parser.MakeExistsOne(meh, target, args)
|
||||
}
|
||||
|
||||
// MapMacroExpander expands the input call arguments into a comprehension that transforms each element in the
|
||||
// input to produce an output list.
|
||||
//
|
||||
// There are two call patterns supported by map:
|
||||
// <iterRange>.map(<iterVar>, <transform>)
|
||||
// <iterRange>.map(<iterVar>, <predicate>, <transform>)
|
||||
// In the second form only iterVar values which return true when provided to the predicate expression
|
||||
// are transformed.
|
||||
func MapMacroExpander(meh MacroExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *common.Error) {
|
||||
return parser.MakeMap(meh, target, args)
|
||||
}
|
||||
|
||||
// FilterMacroExpander expands the input call arguments into a comprehension which produces a list which contains
|
||||
// only elements which match the provided predicate expression:
|
||||
// <iterRange>.filter(<iterVar>, <predicate>)
|
||||
func FilterMacroExpander(meh MacroExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *common.Error) {
|
||||
return parser.MakeFilter(meh, target, args)
|
||||
}
|
||||
|
||||
var (
|
||||
// Aliases to each macro in the CEL standard environment.
|
||||
// Note: reassigning these macro variables may result in undefined behavior.
|
||||
|
||||
// HasMacro expands "has(m.f)" which tests the presence of a field, avoiding the need to
|
||||
// specify the field as a string.
|
||||
HasMacro = parser.HasMacro
|
||||
|
||||
// AllMacro expands "range.all(var, predicate)" into a comprehension which ensures that all
|
||||
// elements in the range satisfy the predicate.
|
||||
AllMacro = parser.AllMacro
|
||||
|
||||
// ExistsMacro expands "range.exists(var, predicate)" into a comprehension which ensures that
|
||||
// some element in the range satisfies the predicate.
|
||||
ExistsMacro = parser.ExistsMacro
|
||||
|
||||
// ExistsOneMacro expands "range.exists_one(var, predicate)", which is true if for exactly one
|
||||
// element in range the predicate holds.
|
||||
ExistsOneMacro = parser.ExistsOneMacro
|
||||
|
||||
// MapMacro expands "range.map(var, function)" into a comprehension which applies the function
|
||||
// to each element in the range to produce a new list.
|
||||
MapMacro = parser.MapMacro
|
||||
|
||||
// MapFilterMacro expands "range.map(var, predicate, function)" into a comprehension which
|
||||
// first filters the elements in the range by the predicate, then applies the transform function
|
||||
// to produce a new list.
|
||||
MapFilterMacro = parser.MapFilterMacro
|
||||
|
||||
// FilterMacro expands "range.filter(var, predicate)" into a comprehension which filters
|
||||
// elements in the range, producing a new list from the elements that satisfy the predicate.
|
||||
FilterMacro = parser.FilterMacro
|
||||
|
||||
// StandardMacros provides an alias to all the CEL macros defined in the standard environment.
|
||||
StandardMacros = []Macro{
|
||||
HasMacro, AllMacro, ExistsMacro, ExistsOneMacro, MapMacro, MapFilterMacro, FilterMacro,
|
||||
}
|
||||
|
||||
// NoMacros provides an alias to an empty list of macros
|
||||
NoMacros = []Macro{}
|
||||
)
|
543
vendor/github.com/google/cel-go/cel/options.go
generated
vendored
Normal file
543
vendor/github.com/google/cel-go/cel/options.go
generated
vendored
Normal file
@ -0,0 +1,543 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// 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"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/reflect/protodesc"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
"google.golang.org/protobuf/reflect/protoregistry"
|
||||
"google.golang.org/protobuf/types/dynamicpb"
|
||||
|
||||
"github.com/google/cel-go/checker/decls"
|
||||
"github.com/google/cel-go/common/containers"
|
||||
"github.com/google/cel-go/common/types/pb"
|
||||
"github.com/google/cel-go/common/types/ref"
|
||||
"github.com/google/cel-go/interpreter"
|
||||
"github.com/google/cel-go/interpreter/functions"
|
||||
|
||||
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
|
||||
descpb "google.golang.org/protobuf/types/descriptorpb"
|
||||
)
|
||||
|
||||
// These constants beginning with "Feature" enable optional behavior in
|
||||
// the library. See the documentation for each constant to see its
|
||||
// effects, compatibility restrictions, and standard conformance.
|
||||
const (
|
||||
_ = iota
|
||||
|
||||
// Disallow heterogeneous aggregate (list, map) literals.
|
||||
// Note, it is still possible to have heterogeneous aggregates when
|
||||
// provided as variables to the expression, as well as via conversion
|
||||
// of well-known dynamic types, or with unchecked expressions.
|
||||
// Affects checking. Provides a subset of standard behavior.
|
||||
featureDisableDynamicAggregateLiterals
|
||||
|
||||
// Enable the tracking of function call expressions replaced by macros.
|
||||
featureEnableMacroCallTracking
|
||||
|
||||
// Enable the use of cross-type numeric comparisons at the type-checker.
|
||||
featureCrossTypeNumericComparisons
|
||||
|
||||
// Enable eager validation of declarations to ensure that Env values created
|
||||
// with `Extend` inherit a validated list of declarations from the parent Env.
|
||||
featureEagerlyValidateDeclarations
|
||||
|
||||
// Enable the use of the default UTC timezone when a timezone is not specified
|
||||
// on a CEL timestamp operation. This fixes the scenario where the input time
|
||||
// is not already in UTC.
|
||||
featureDefaultUTCTimeZone
|
||||
)
|
||||
|
||||
// EnvOption is a functional interface for configuring the environment.
|
||||
type EnvOption func(e *Env) (*Env, error)
|
||||
|
||||
// ClearMacros options clears all parser macros.
|
||||
//
|
||||
// Clearing macros will ensure CEL expressions can only contain linear evaluation paths, as
|
||||
// comprehensions such as `all` and `exists` are enabled only via macros.
|
||||
func ClearMacros() EnvOption {
|
||||
return func(e *Env) (*Env, error) {
|
||||
e.macros = NoMacros
|
||||
return e, nil
|
||||
}
|
||||
}
|
||||
|
||||
// CustomTypeAdapter swaps the default ref.TypeAdapter implementation with a custom one.
|
||||
//
|
||||
// Note: This option must be specified before the Types and TypeDescs options when used together.
|
||||
func CustomTypeAdapter(adapter ref.TypeAdapter) EnvOption {
|
||||
return func(e *Env) (*Env, error) {
|
||||
e.adapter = adapter
|
||||
return e, nil
|
||||
}
|
||||
}
|
||||
|
||||
// CustomTypeProvider swaps the default ref.TypeProvider implementation with a custom one.
|
||||
//
|
||||
// Note: This option must be specified before the Types and TypeDescs options when used together.
|
||||
func CustomTypeProvider(provider ref.TypeProvider) EnvOption {
|
||||
return func(e *Env) (*Env, error) {
|
||||
e.provider = provider
|
||||
return e, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Declarations option extends the declaration set configured in the environment.
|
||||
//
|
||||
// Note: Declarations will by default be appended to the pre-existing declaration set configured
|
||||
// for the environment. The NewEnv call builds on top of the standard CEL declarations. For a
|
||||
// purely custom set of declarations use NewCustomEnv.
|
||||
func Declarations(decls ...*exprpb.Decl) EnvOption {
|
||||
return func(e *Env) (*Env, error) {
|
||||
e.declarations = append(e.declarations, decls...)
|
||||
return e, nil
|
||||
}
|
||||
}
|
||||
|
||||
// EagerlyValidateDeclarations ensures that any collisions between configured declarations are caught
|
||||
// at the time of the `NewEnv` call.
|
||||
//
|
||||
// Eagerly validating declarations is also useful for bootstrapping a base `cel.Env` value.
|
||||
// Calls to base `Env.Extend()` will be significantly faster when declarations are eagerly validated
|
||||
// as declarations will be collision-checked at most once and only incrementally by way of `Extend`
|
||||
//
|
||||
// Disabled by default as not all environments are used for type-checking.
|
||||
func EagerlyValidateDeclarations(enabled bool) EnvOption {
|
||||
return features(featureEagerlyValidateDeclarations, enabled)
|
||||
}
|
||||
|
||||
// HomogeneousAggregateLiterals option ensures that list and map literal entry types must agree
|
||||
// during type-checking.
|
||||
//
|
||||
// Note, it is still possible to have heterogeneous aggregates when provided as variables to the
|
||||
// expression, as well as via conversion of well-known dynamic types, or with unchecked
|
||||
// expressions.
|
||||
func HomogeneousAggregateLiterals() EnvOption {
|
||||
return features(featureDisableDynamicAggregateLiterals, true)
|
||||
}
|
||||
|
||||
// Macros option extends the macro set configured in the environment.
|
||||
//
|
||||
// Note: This option must be specified after ClearMacros if used together.
|
||||
func Macros(macros ...Macro) EnvOption {
|
||||
return func(e *Env) (*Env, error) {
|
||||
e.macros = append(e.macros, macros...)
|
||||
return e, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Container sets the container for resolving variable names. Defaults to an empty container.
|
||||
//
|
||||
// If all references within an expression are relative to a protocol buffer package, then
|
||||
// specifying a container of `google.type` would make it possible to write expressions such as
|
||||
// `Expr{expression: 'a < b'}` instead of having to write `google.type.Expr{...}`.
|
||||
func Container(name string) EnvOption {
|
||||
return func(e *Env) (*Env, error) {
|
||||
cont, err := e.Container.Extend(containers.Name(name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
e.Container = cont
|
||||
return e, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Abbrevs configures a set of simple names as abbreviations for fully-qualified names.
|
||||
//
|
||||
// An abbreviation (abbrev for short) is a simple name that expands to a fully-qualified name.
|
||||
// Abbreviations can be useful when working with variables, functions, and especially types from
|
||||
// multiple namespaces:
|
||||
//
|
||||
// // CEL object construction
|
||||
// qual.pkg.version.ObjTypeName{
|
||||
// field: alt.container.ver.FieldTypeName{value: ...}
|
||||
// }
|
||||
//
|
||||
// Only one the qualified names above may be used as the CEL container, so at least one of these
|
||||
// references must be a long qualified name within an otherwise short CEL program. Using the
|
||||
// following abbreviations, the program becomes much simpler:
|
||||
//
|
||||
// // CEL Go option
|
||||
// Abbrevs("qual.pkg.version.ObjTypeName", "alt.container.ver.FieldTypeName")
|
||||
// // Simplified Object construction
|
||||
// ObjTypeName{field: FieldTypeName{value: ...}}
|
||||
//
|
||||
// There are a few rules for the qualified names and the simple abbreviations generated from them:
|
||||
// - Qualified names must be dot-delimited, e.g. `package.subpkg.name`.
|
||||
// - The last element in the qualified name is the abbreviation.
|
||||
// - Abbreviations must not collide with each other.
|
||||
// - The abbreviation must not collide with unqualified names in use.
|
||||
//
|
||||
// Abbreviations are distinct from container-based references in the following important ways:
|
||||
// - Abbreviations must expand to a fully-qualified name.
|
||||
// - Expanded abbreviations do not participate in namespace resolution.
|
||||
// - Abbreviation expansion is done instead of the container search for a matching identifier.
|
||||
// - Containers follow C++ namespace resolution rules with searches from the most qualified name
|
||||
// to the least qualified name.
|
||||
// - Container references within the CEL program may be relative, and are resolved to fully
|
||||
// qualified names at either type-check time or program plan time, whichever comes first.
|
||||
//
|
||||
// If there is ever a case where an identifier could be in both the container and as an
|
||||
// abbreviation, the abbreviation wins as this will ensure that the meaning of a program is
|
||||
// preserved between compilations even as the container evolves.
|
||||
func Abbrevs(qualifiedNames ...string) EnvOption {
|
||||
return func(e *Env) (*Env, error) {
|
||||
cont, err := e.Container.Extend(containers.Abbrevs(qualifiedNames...))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
e.Container = cont
|
||||
return e, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Types adds one or more type declarations to the environment, allowing for construction of
|
||||
// type-literals whose definitions are included in the common expression built-in set.
|
||||
//
|
||||
// The input types may either be instances of `proto.Message` or `ref.Type`. Any other type
|
||||
// provided to this option will result in an error.
|
||||
//
|
||||
// Well-known protobuf types within the `google.protobuf.*` package are included in the standard
|
||||
// environment by default.
|
||||
//
|
||||
// Note: This option must be specified after the CustomTypeProvider option when used together.
|
||||
func Types(addTypes ...interface{}) EnvOption {
|
||||
return func(e *Env) (*Env, error) {
|
||||
reg, isReg := e.provider.(ref.TypeRegistry)
|
||||
if !isReg {
|
||||
return nil, fmt.Errorf("custom types not supported by provider: %T", e.provider)
|
||||
}
|
||||
for _, t := range addTypes {
|
||||
switch v := t.(type) {
|
||||
case proto.Message:
|
||||
fdMap := pb.CollectFileDescriptorSet(v)
|
||||
for _, fd := range fdMap {
|
||||
err := reg.RegisterDescriptor(fd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
case ref.Type:
|
||||
err := reg.RegisterType(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported type: %T", t)
|
||||
}
|
||||
}
|
||||
return e, nil
|
||||
}
|
||||
}
|
||||
|
||||
// TypeDescs adds type declarations from any protoreflect.FileDescriptor, protoregistry.Files,
|
||||
// google.protobuf.FileDescriptorProto or google.protobuf.FileDescriptorSet provided.
|
||||
//
|
||||
// Note that messages instantiated from these descriptors will be *dynamicpb.Message values
|
||||
// rather than the concrete message type.
|
||||
//
|
||||
// TypeDescs are hermetic to a single Env object, but may be copied to other Env values via
|
||||
// extension or by re-using the same EnvOption with another NewEnv() call.
|
||||
func TypeDescs(descs ...interface{}) EnvOption {
|
||||
return func(e *Env) (*Env, error) {
|
||||
reg, isReg := e.provider.(ref.TypeRegistry)
|
||||
if !isReg {
|
||||
return nil, fmt.Errorf("custom types not supported by provider: %T", e.provider)
|
||||
}
|
||||
// Scan the input descriptors for FileDescriptorProto messages and accumulate them into a
|
||||
// synthetic FileDescriptorSet as the FileDescriptorProto messages may refer to each other
|
||||
// and will not resolve properly unless they are part of the same set.
|
||||
var fds *descpb.FileDescriptorSet
|
||||
for _, d := range descs {
|
||||
switch f := d.(type) {
|
||||
case *descpb.FileDescriptorProto:
|
||||
if fds == nil {
|
||||
fds = &descpb.FileDescriptorSet{
|
||||
File: []*descpb.FileDescriptorProto{},
|
||||
}
|
||||
}
|
||||
fds.File = append(fds.File, f)
|
||||
}
|
||||
}
|
||||
if fds != nil {
|
||||
if err := registerFileSet(reg, fds); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for _, d := range descs {
|
||||
switch f := d.(type) {
|
||||
case *protoregistry.Files:
|
||||
if err := registerFiles(reg, f); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case protoreflect.FileDescriptor:
|
||||
if err := reg.RegisterDescriptor(f); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case *descpb.FileDescriptorSet:
|
||||
if err := registerFileSet(reg, f); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case *descpb.FileDescriptorProto:
|
||||
// skip, handled as a synthetic file descriptor set.
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported type descriptor: %T", d)
|
||||
}
|
||||
}
|
||||
return e, nil
|
||||
}
|
||||
}
|
||||
|
||||
func registerFileSet(reg ref.TypeRegistry, fileSet *descpb.FileDescriptorSet) error {
|
||||
files, err := protodesc.NewFiles(fileSet)
|
||||
if err != nil {
|
||||
return fmt.Errorf("protodesc.NewFiles(%v) failed: %v", fileSet, err)
|
||||
}
|
||||
return registerFiles(reg, files)
|
||||
}
|
||||
|
||||
func registerFiles(reg ref.TypeRegistry, files *protoregistry.Files) error {
|
||||
var err error
|
||||
files.RangeFiles(func(fd protoreflect.FileDescriptor) bool {
|
||||
err = reg.RegisterDescriptor(fd)
|
||||
return err == nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// ProgramOption is a functional interface for configuring evaluation bindings and behaviors.
|
||||
type ProgramOption func(p *prog) (*prog, error)
|
||||
|
||||
// CustomDecorator appends an InterpreterDecorator to the program.
|
||||
//
|
||||
// InterpretableDecorators can be used to inspect, alter, or replace the Program plan.
|
||||
func CustomDecorator(dec interpreter.InterpretableDecorator) ProgramOption {
|
||||
return func(p *prog) (*prog, error) {
|
||||
p.decorators = append(p.decorators, dec)
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Functions adds function overloads that extend or override the set of CEL built-ins.
|
||||
//
|
||||
// Deprecated: use Function() instead to declare the function, its overload signatures,
|
||||
// and the overload implementations.
|
||||
func Functions(funcs ...*functions.Overload) ProgramOption {
|
||||
return func(p *prog) (*prog, error) {
|
||||
if err := p.dispatcher.Add(funcs...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Globals sets the global variable values for a given program. These values may be shadowed by
|
||||
// variables with the same name provided to the Eval() call. If Globals is used in a Library with
|
||||
// a Lib EnvOption, vars may shadow variables provided by previously added libraries.
|
||||
//
|
||||
// The vars value may either be an `interpreter.Activation` instance or a `map[string]interface{}`.
|
||||
func Globals(vars interface{}) ProgramOption {
|
||||
return func(p *prog) (*prog, error) {
|
||||
defaultVars, err := interpreter.NewActivation(vars)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if p.defaultVars != nil {
|
||||
defaultVars = interpreter.NewHierarchicalActivation(p.defaultVars, defaultVars)
|
||||
}
|
||||
p.defaultVars = defaultVars
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
|
||||
// OptimizeRegex provides a way to replace the InterpretableCall for regex functions. This can be used
|
||||
// to compile regex string constants at program creation time and report any errors and then use the
|
||||
// compiled regex for all regex function invocations.
|
||||
func OptimizeRegex(regexOptimizations ...*interpreter.RegexOptimization) ProgramOption {
|
||||
return func(p *prog) (*prog, error) {
|
||||
p.regexOptimizations = append(p.regexOptimizations, regexOptimizations...)
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
|
||||
// EvalOption indicates an evaluation option that may affect the evaluation behavior or information
|
||||
// in the output result.
|
||||
type EvalOption int
|
||||
|
||||
const (
|
||||
// OptTrackState will cause the runtime to return an immutable EvalState value in the Result.
|
||||
OptTrackState EvalOption = 1 << iota
|
||||
|
||||
// OptExhaustiveEval causes the runtime to disable short-circuits and track state.
|
||||
OptExhaustiveEval EvalOption = 1<<iota | OptTrackState
|
||||
|
||||
// OptOptimize precomputes functions and operators with constants as arguments at program
|
||||
// creation time. It also pre-compiles regex pattern constants passed to 'matches', reports any compilation errors
|
||||
// at program creation and uses the compiled regex pattern for all 'matches' function invocations.
|
||||
// This flag is useful when the expression will be evaluated repeatedly against
|
||||
// a series of different inputs.
|
||||
OptOptimize EvalOption = 1 << iota
|
||||
|
||||
// OptPartialEval enables the evaluation of a partial state where the input data that may be
|
||||
// known to be missing, either as top-level variables, or somewhere within a variable's object
|
||||
// member graph.
|
||||
//
|
||||
// By itself, OptPartialEval does not change evaluation behavior unless the input to the
|
||||
// Program Eval() call is created via PartialVars().
|
||||
OptPartialEval EvalOption = 1 << iota
|
||||
|
||||
// OptTrackCost enables the runtime cost calculation while validation and return cost within evalDetails
|
||||
// cost calculation is available via func ActualCost()
|
||||
OptTrackCost EvalOption = 1 << iota
|
||||
)
|
||||
|
||||
// EvalOptions sets one or more evaluation options which may affect the evaluation or Result.
|
||||
func EvalOptions(opts ...EvalOption) ProgramOption {
|
||||
return func(p *prog) (*prog, error) {
|
||||
for _, opt := range opts {
|
||||
p.evalOpts |= opt
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
|
||||
// InterruptCheckFrequency configures the number of iterations within a comprehension to evaluate
|
||||
// before checking whether the function evaluation has been interrupted.
|
||||
func InterruptCheckFrequency(checkFrequency uint) ProgramOption {
|
||||
return func(p *prog) (*prog, error) {
|
||||
p.interruptCheckFrequency = checkFrequency
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
|
||||
// CostTracking enables cost tracking and registers a ActualCostEstimator that can optionally provide a runtime cost estimate for any function calls.
|
||||
func CostTracking(costEstimator interpreter.ActualCostEstimator) ProgramOption {
|
||||
return func(p *prog) (*prog, error) {
|
||||
p.callCostEstimator = costEstimator
|
||||
p.evalOpts |= OptTrackCost
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
|
||||
// CostLimit enables cost tracking and sets configures program evaluation to exit early with a
|
||||
// "runtime cost limit exceeded" error if the runtime cost exceeds the costLimit.
|
||||
// The CostLimit is a metric that corresponds to the number and estimated expense of operations
|
||||
// performed while evaluating an expression. It is indicative of CPU usage, not memory usage.
|
||||
func CostLimit(costLimit uint64) ProgramOption {
|
||||
return func(p *prog) (*prog, error) {
|
||||
p.costLimit = &costLimit
|
||||
p.evalOpts |= OptTrackCost
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
|
||||
func fieldToCELType(field protoreflect.FieldDescriptor) (*exprpb.Type, error) {
|
||||
if field.Kind() == protoreflect.MessageKind || field.Kind() == protoreflect.GroupKind {
|
||||
msgName := (string)(field.Message().FullName())
|
||||
wellKnownType, found := pb.CheckedWellKnowns[msgName]
|
||||
if found {
|
||||
return wellKnownType, nil
|
||||
}
|
||||
return decls.NewObjectType(msgName), nil
|
||||
}
|
||||
if primitiveType, found := pb.CheckedPrimitives[field.Kind()]; found {
|
||||
return primitiveType, nil
|
||||
}
|
||||
if field.Kind() == protoreflect.EnumKind {
|
||||
return decls.Int, nil
|
||||
}
|
||||
return nil, fmt.Errorf("field %s type %s not implemented", field.FullName(), field.Kind().String())
|
||||
}
|
||||
|
||||
func fieldToDecl(field protoreflect.FieldDescriptor) (*exprpb.Decl, error) {
|
||||
name := string(field.Name())
|
||||
if field.IsMap() {
|
||||
mapKey := field.MapKey()
|
||||
mapValue := field.MapValue()
|
||||
keyType, err := fieldToCELType(mapKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
valueType, err := fieldToCELType(mapValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return decls.NewVar(name, decls.NewMapType(keyType, valueType)), nil
|
||||
}
|
||||
if field.IsList() {
|
||||
elemType, err := fieldToCELType(field)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return decls.NewVar(name, decls.NewListType(elemType)), nil
|
||||
}
|
||||
celType, err := fieldToCELType(field)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return decls.NewVar(name, celType), nil
|
||||
}
|
||||
|
||||
// DeclareContextProto returns an option to extend CEL environment with declarations from the given context proto.
|
||||
// Each field of the proto defines a variable of the same name in the environment.
|
||||
// https://github.com/google/cel-spec/blob/master/doc/langdef.md#evaluation-environment
|
||||
func DeclareContextProto(descriptor protoreflect.MessageDescriptor) EnvOption {
|
||||
return func(e *Env) (*Env, error) {
|
||||
var decls []*exprpb.Decl
|
||||
fields := descriptor.Fields()
|
||||
for i := 0; i < fields.Len(); i++ {
|
||||
field := fields.Get(i)
|
||||
decl, err := fieldToDecl(field)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decls = append(decls, decl)
|
||||
}
|
||||
var err error
|
||||
e, err = Declarations(decls...)(e)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Types(dynamicpb.NewMessage(descriptor))(e)
|
||||
}
|
||||
}
|
||||
|
||||
// EnableMacroCallTracking ensures that call expressions which are replaced by macros
|
||||
// are tracked in the `SourceInfo` of parsed and checked expressions.
|
||||
func EnableMacroCallTracking() EnvOption {
|
||||
return features(featureEnableMacroCallTracking, true)
|
||||
}
|
||||
|
||||
// CrossTypeNumericComparisons makes it possible to compare across numeric types, e.g. double < int
|
||||
func CrossTypeNumericComparisons(enabled bool) EnvOption {
|
||||
return features(featureCrossTypeNumericComparisons, enabled)
|
||||
}
|
||||
|
||||
// DefaultUTCTimeZone ensures that time-based operations use the UTC timezone rather than the
|
||||
// input time's local timezone.
|
||||
func DefaultUTCTimeZone(enabled bool) EnvOption {
|
||||
return features(featureDefaultUTCTimeZone, enabled)
|
||||
}
|
||||
|
||||
// features sets the given feature flags. See list of Feature constants above.
|
||||
func features(flag int, enabled bool) EnvOption {
|
||||
return func(e *Env) (*Env, error) {
|
||||
e.features[flag] = enabled
|
||||
return e, nil
|
||||
}
|
||||
}
|
567
vendor/github.com/google/cel-go/cel/program.go
generated
vendored
Normal file
567
vendor/github.com/google/cel-go/cel/program.go
generated
vendored
Normal file
@ -0,0 +1,567 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// 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 (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"sync"
|
||||
|
||||
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
|
||||
|
||||
"github.com/google/cel-go/common/types"
|
||||
"github.com/google/cel-go/common/types/ref"
|
||||
"github.com/google/cel-go/interpreter"
|
||||
)
|
||||
|
||||
// Program is an evaluable view of an Ast.
|
||||
type Program interface {
|
||||
// Eval returns the result of an evaluation of the Ast and environment against the input vars.
|
||||
//
|
||||
// The vars value may either be an `interpreter.Activation` or a `map[string]interface{}`.
|
||||
//
|
||||
// If the `OptTrackState`, `OptTrackCost` or `OptExhaustiveEval` flags are used, the `details` response will
|
||||
// be non-nil. Given this caveat on `details`, the return state from evaluation will be:
|
||||
//
|
||||
// * `val`, `details`, `nil` - Successful evaluation of a non-error result.
|
||||
// * `val`, `details`, `err` - Successful evaluation to an error result.
|
||||
// * `nil`, `details`, `err` - Unsuccessful evaluation.
|
||||
//
|
||||
// An unsuccessful evaluation is typically the result of a series of incompatible `EnvOption`
|
||||
// or `ProgramOption` values used in the creation of the evaluation environment or executable
|
||||
// program.
|
||||
Eval(interface{}) (ref.Val, *EvalDetails, error)
|
||||
|
||||
// ContextEval evaluates the program with a set of input variables and a context object in order
|
||||
// to support cancellation and timeouts. This method must be used in conjunction with the
|
||||
// InterruptCheckFrequency() option for cancellation interrupts to be impact evaluation.
|
||||
//
|
||||
// The vars value may either be an `interpreter.Activation` or `map[string]interface{}`.
|
||||
//
|
||||
// The output contract for `ContextEval` is otherwise identical to the `Eval` method.
|
||||
ContextEval(context.Context, interface{}) (ref.Val, *EvalDetails, error)
|
||||
}
|
||||
|
||||
// NoVars returns an empty Activation.
|
||||
func NoVars() interpreter.Activation {
|
||||
return interpreter.EmptyActivation()
|
||||
}
|
||||
|
||||
// PartialVars returns a PartialActivation which contains variables and a set of AttributePattern
|
||||
// values that indicate variables or parts of variables whose value are not yet known.
|
||||
//
|
||||
// The `vars` value may either be an interpreter.Activation or any valid input to the
|
||||
// interpreter.NewActivation call.
|
||||
func PartialVars(vars interface{},
|
||||
unknowns ...*interpreter.AttributePattern) (interpreter.PartialActivation, error) {
|
||||
return interpreter.NewPartialActivation(vars, unknowns...)
|
||||
}
|
||||
|
||||
// AttributePattern returns an AttributePattern that matches a top-level variable. The pattern is
|
||||
// mutable, and its methods support the specification of one or more qualifier patterns.
|
||||
//
|
||||
// For example, the AttributePattern(`a`).QualString(`b`) represents a variable access `a` with a
|
||||
// string field or index qualification `b`. This pattern will match Attributes `a`, and `a.b`,
|
||||
// but not `a.c`.
|
||||
//
|
||||
// When using a CEL expression within a container, e.g. a package or namespace, the variable name
|
||||
// in the pattern must match the qualified name produced during the variable namespace resolution.
|
||||
// For example, when variable `a` is declared within an expression whose container is `ns.app`, the
|
||||
// fully qualified variable name may be `ns.app.a`, `ns.a`, or `a` per the CEL namespace resolution
|
||||
// rules. Pick the fully qualified variable name that makes sense within the container as the
|
||||
// AttributePattern `varName` argument.
|
||||
//
|
||||
// See the interpreter.AttributePattern and interpreter.AttributeQualifierPattern for more info
|
||||
// about how to create and manipulate AttributePattern values.
|
||||
func AttributePattern(varName string) *interpreter.AttributePattern {
|
||||
return interpreter.NewAttributePattern(varName)
|
||||
}
|
||||
|
||||
// EvalDetails holds additional information observed during the Eval() call.
|
||||
type EvalDetails struct {
|
||||
state interpreter.EvalState
|
||||
costTracker *interpreter.CostTracker
|
||||
}
|
||||
|
||||
// State of the evaluation, non-nil if the OptTrackState or OptExhaustiveEval is specified
|
||||
// within EvalOptions.
|
||||
func (ed *EvalDetails) State() interpreter.EvalState {
|
||||
return ed.state
|
||||
}
|
||||
|
||||
// ActualCost returns the tracked cost through the course of execution when `CostTracking` is enabled.
|
||||
// Otherwise, returns nil if the cost was not enabled.
|
||||
func (ed *EvalDetails) ActualCost() *uint64 {
|
||||
if ed.costTracker == nil {
|
||||
return nil
|
||||
}
|
||||
cost := ed.costTracker.ActualCost()
|
||||
return &cost
|
||||
}
|
||||
|
||||
// prog is the internal implementation of the Program interface.
|
||||
type prog struct {
|
||||
*Env
|
||||
evalOpts EvalOption
|
||||
defaultVars interpreter.Activation
|
||||
dispatcher interpreter.Dispatcher
|
||||
interpreter interpreter.Interpreter
|
||||
interruptCheckFrequency uint
|
||||
|
||||
// Intermediate state used to configure the InterpretableDecorator set provided
|
||||
// to the initInterpretable call.
|
||||
decorators []interpreter.InterpretableDecorator
|
||||
regexOptimizations []*interpreter.RegexOptimization
|
||||
|
||||
// Interpretable configured from an Ast and aggregate decorator set based on program options.
|
||||
interpretable interpreter.Interpretable
|
||||
callCostEstimator interpreter.ActualCostEstimator
|
||||
costLimit *uint64
|
||||
}
|
||||
|
||||
func (p *prog) clone() *prog {
|
||||
return &prog{
|
||||
Env: p.Env,
|
||||
evalOpts: p.evalOpts,
|
||||
defaultVars: p.defaultVars,
|
||||
dispatcher: p.dispatcher,
|
||||
interpreter: p.interpreter,
|
||||
interruptCheckFrequency: p.interruptCheckFrequency,
|
||||
}
|
||||
}
|
||||
|
||||
// newProgram creates a program instance with an environment, an ast, and an optional list of
|
||||
// ProgramOption values.
|
||||
//
|
||||
// If the program cannot be configured the prog will be nil, with a non-nil error response.
|
||||
func newProgram(e *Env, ast *Ast, opts []ProgramOption) (Program, error) {
|
||||
// Build the dispatcher, interpreter, and default program value.
|
||||
disp := interpreter.NewDispatcher()
|
||||
|
||||
// Ensure the default attribute factory is set after the adapter and provider are
|
||||
// configured.
|
||||
p := &prog{
|
||||
Env: e,
|
||||
decorators: []interpreter.InterpretableDecorator{},
|
||||
dispatcher: disp,
|
||||
}
|
||||
|
||||
// Configure the program via the ProgramOption values.
|
||||
var err error
|
||||
for _, opt := range opts {
|
||||
p, err = opt(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Add the function bindings created via Function() options.
|
||||
for _, fn := range e.functions {
|
||||
bindings, err := fn.bindings()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = disp.Add(bindings...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Set the attribute factory after the options have been set.
|
||||
var attrFactory interpreter.AttributeFactory
|
||||
if p.evalOpts&OptPartialEval == OptPartialEval {
|
||||
attrFactory = interpreter.NewPartialAttributeFactory(e.Container, e.adapter, e.provider)
|
||||
} else {
|
||||
attrFactory = interpreter.NewAttributeFactory(e.Container, e.adapter, e.provider)
|
||||
}
|
||||
interp := interpreter.NewInterpreter(disp, e.Container, e.provider, e.adapter, attrFactory)
|
||||
p.interpreter = interp
|
||||
|
||||
// Translate the EvalOption flags into InterpretableDecorator instances.
|
||||
decorators := make([]interpreter.InterpretableDecorator, len(p.decorators))
|
||||
copy(decorators, p.decorators)
|
||||
|
||||
// Enable interrupt checking if there's a non-zero check frequency
|
||||
if p.interruptCheckFrequency > 0 {
|
||||
decorators = append(decorators, interpreter.InterruptableEval())
|
||||
}
|
||||
// Enable constant folding first.
|
||||
if p.evalOpts&OptOptimize == OptOptimize {
|
||||
decorators = append(decorators, interpreter.Optimize())
|
||||
p.regexOptimizations = append(p.regexOptimizations, interpreter.MatchesRegexOptimization)
|
||||
}
|
||||
// Enable regex compilation of constants immediately after folding constants.
|
||||
if len(p.regexOptimizations) > 0 {
|
||||
decorators = append(decorators, interpreter.CompileRegexConstants(p.regexOptimizations...))
|
||||
}
|
||||
|
||||
// Enable exhaustive eval, state tracking and cost tracking last since they require a factory.
|
||||
if p.evalOpts&(OptExhaustiveEval|OptTrackState|OptTrackCost) != 0 {
|
||||
factory := func(state interpreter.EvalState, costTracker *interpreter.CostTracker) (Program, error) {
|
||||
costTracker.Estimator = p.callCostEstimator
|
||||
costTracker.Limit = p.costLimit
|
||||
// Limit capacity to guarantee a reallocation when calling 'append(decs, ...)' below. This
|
||||
// prevents the underlying memory from being shared between factory function calls causing
|
||||
// undesired mutations.
|
||||
decs := decorators[:len(decorators):len(decorators)]
|
||||
var observers []interpreter.EvalObserver
|
||||
|
||||
if p.evalOpts&(OptExhaustiveEval|OptTrackState) != 0 {
|
||||
// EvalStateObserver is required for OptExhaustiveEval.
|
||||
observers = append(observers, interpreter.EvalStateObserver(state))
|
||||
}
|
||||
if p.evalOpts&OptTrackCost == OptTrackCost {
|
||||
observers = append(observers, interpreter.CostObserver(costTracker))
|
||||
}
|
||||
|
||||
// Enable exhaustive eval over a basic observer since it offers a superset of features.
|
||||
if p.evalOpts&OptExhaustiveEval == OptExhaustiveEval {
|
||||
decs = append(decs, interpreter.ExhaustiveEval(), interpreter.Observe(observers...))
|
||||
} else if len(observers) > 0 {
|
||||
decs = append(decs, interpreter.Observe(observers...))
|
||||
}
|
||||
|
||||
return p.clone().initInterpretable(ast, decs)
|
||||
}
|
||||
return newProgGen(factory)
|
||||
}
|
||||
return p.initInterpretable(ast, decorators)
|
||||
}
|
||||
|
||||
func (p *prog) initInterpretable(ast *Ast, decs []interpreter.InterpretableDecorator) (*prog, error) {
|
||||
// Unchecked programs do not contain type and reference information and may be slower to execute.
|
||||
if !ast.IsChecked() {
|
||||
interpretable, err :=
|
||||
p.interpreter.NewUncheckedInterpretable(ast.Expr(), decs...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.interpretable = interpretable
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// When the AST has been checked it contains metadata that can be used to speed up program execution.
|
||||
var checked *exprpb.CheckedExpr
|
||||
checked, err := AstToCheckedExpr(ast)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
interpretable, err := p.interpreter.NewInterpretable(checked, decs...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.interpretable = interpretable
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// Eval implements the Program interface method.
|
||||
func (p *prog) Eval(input interface{}) (v ref.Val, det *EvalDetails, err error) {
|
||||
// Configure error recovery for unexpected panics during evaluation. Note, the use of named
|
||||
// return values makes it possible to modify the error response during the recovery
|
||||
// function.
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
switch t := r.(type) {
|
||||
case interpreter.EvalCancelledError:
|
||||
err = t
|
||||
default:
|
||||
err = fmt.Errorf("internal error: %v", r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
// Build a hierarchical activation if there are default vars set.
|
||||
var vars interpreter.Activation
|
||||
switch v := input.(type) {
|
||||
case interpreter.Activation:
|
||||
vars = v
|
||||
case map[string]interface{}:
|
||||
vars = activationPool.Setup(v)
|
||||
defer activationPool.Put(vars)
|
||||
default:
|
||||
return nil, nil, fmt.Errorf("invalid input, wanted Activation or map[string]interface{}, got: (%T)%v", input, input)
|
||||
}
|
||||
if p.defaultVars != nil {
|
||||
vars = interpreter.NewHierarchicalActivation(p.defaultVars, vars)
|
||||
}
|
||||
v = p.interpretable.Eval(vars)
|
||||
// The output of an internal Eval may have a value (`v`) that is a types.Err. This step
|
||||
// translates the CEL value to a Go error response. This interface does not quite match the
|
||||
// RPC signature which allows for multiple errors to be returned, but should be sufficient.
|
||||
if types.IsError(v) {
|
||||
err = v.(*types.Err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ContextEval implements the Program interface.
|
||||
func (p *prog) ContextEval(ctx context.Context, input interface{}) (ref.Val, *EvalDetails, error) {
|
||||
if ctx == nil {
|
||||
return nil, nil, fmt.Errorf("context can not be nil")
|
||||
}
|
||||
// Configure the input, making sure to wrap Activation inputs in the special ctxActivation which
|
||||
// exposes the #interrupted variable and manages rate-limited checks of the ctx.Done() state.
|
||||
var vars interpreter.Activation
|
||||
switch v := input.(type) {
|
||||
case interpreter.Activation:
|
||||
vars = ctxActivationPool.Setup(v, ctx.Done(), p.interruptCheckFrequency)
|
||||
defer ctxActivationPool.Put(vars)
|
||||
case map[string]interface{}:
|
||||
rawVars := activationPool.Setup(v)
|
||||
defer activationPool.Put(rawVars)
|
||||
vars = ctxActivationPool.Setup(rawVars, ctx.Done(), p.interruptCheckFrequency)
|
||||
defer ctxActivationPool.Put(vars)
|
||||
default:
|
||||
return nil, nil, fmt.Errorf("invalid input, wanted Activation or map[string]interface{}, got: (%T)%v", input, input)
|
||||
}
|
||||
return p.Eval(vars)
|
||||
}
|
||||
|
||||
// Cost implements the Coster interface method.
|
||||
func (p *prog) Cost() (min, max int64) {
|
||||
return estimateCost(p.interpretable)
|
||||
}
|
||||
|
||||
// progFactory is a helper alias for marking a program creation factory function.
|
||||
type progFactory func(interpreter.EvalState, *interpreter.CostTracker) (Program, error)
|
||||
|
||||
// progGen holds a reference to a progFactory instance and implements the Program interface.
|
||||
type progGen struct {
|
||||
factory progFactory
|
||||
}
|
||||
|
||||
// newProgGen tests the factory object by calling it once and returns a factory-based Program if
|
||||
// the test is successful.
|
||||
func newProgGen(factory progFactory) (Program, error) {
|
||||
// Test the factory to make sure that configuration errors are spotted at config
|
||||
_, err := factory(interpreter.NewEvalState(), &interpreter.CostTracker{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &progGen{factory: factory}, nil
|
||||
}
|
||||
|
||||
// Eval implements the Program interface method.
|
||||
func (gen *progGen) Eval(input interface{}) (ref.Val, *EvalDetails, error) {
|
||||
// The factory based Eval() differs from the standard evaluation model in that it generates a
|
||||
// new EvalState instance for each call to ensure that unique evaluations yield unique stateful
|
||||
// results.
|
||||
state := interpreter.NewEvalState()
|
||||
costTracker := &interpreter.CostTracker{}
|
||||
det := &EvalDetails{state: state, costTracker: costTracker}
|
||||
|
||||
// Generate a new instance of the interpretable using the factory configured during the call to
|
||||
// newProgram(). It is incredibly unlikely that the factory call will generate an error given
|
||||
// the factory test performed within the Program() call.
|
||||
p, err := gen.factory(state, costTracker)
|
||||
if err != nil {
|
||||
return nil, det, err
|
||||
}
|
||||
|
||||
// Evaluate the input, returning the result and the 'state' within EvalDetails.
|
||||
v, _, err := p.Eval(input)
|
||||
if err != nil {
|
||||
return v, det, err
|
||||
}
|
||||
return v, det, nil
|
||||
}
|
||||
|
||||
// ContextEval implements the Program interface method.
|
||||
func (gen *progGen) ContextEval(ctx context.Context, input interface{}) (ref.Val, *EvalDetails, error) {
|
||||
if ctx == nil {
|
||||
return nil, nil, fmt.Errorf("context can not be nil")
|
||||
}
|
||||
// The factory based Eval() differs from the standard evaluation model in that it generates a
|
||||
// new EvalState instance for each call to ensure that unique evaluations yield unique stateful
|
||||
// results.
|
||||
state := interpreter.NewEvalState()
|
||||
costTracker := &interpreter.CostTracker{}
|
||||
det := &EvalDetails{state: state, costTracker: costTracker}
|
||||
|
||||
// Generate a new instance of the interpretable using the factory configured during the call to
|
||||
// newProgram(). It is incredibly unlikely that the factory call will generate an error given
|
||||
// the factory test performed within the Program() call.
|
||||
p, err := gen.factory(state, costTracker)
|
||||
if err != nil {
|
||||
return nil, det, err
|
||||
}
|
||||
|
||||
// Evaluate the input, returning the result and the 'state' within EvalDetails.
|
||||
v, _, err := p.ContextEval(ctx, input)
|
||||
if err != nil {
|
||||
return v, det, err
|
||||
}
|
||||
return v, det, nil
|
||||
}
|
||||
|
||||
// Cost implements the Coster interface method.
|
||||
func (gen *progGen) Cost() (min, max int64) {
|
||||
// Use an empty state value since no evaluation is performed.
|
||||
p, err := gen.factory(emptyEvalState, nil)
|
||||
if err != nil {
|
||||
return 0, math.MaxInt64
|
||||
}
|
||||
return estimateCost(p)
|
||||
}
|
||||
|
||||
// EstimateCost returns the heuristic cost interval for the program.
|
||||
func EstimateCost(p Program) (min, max int64) {
|
||||
return estimateCost(p)
|
||||
}
|
||||
|
||||
func estimateCost(i interface{}) (min, max int64) {
|
||||
c, ok := i.(interpreter.Coster)
|
||||
if !ok {
|
||||
return 0, math.MaxInt64
|
||||
}
|
||||
return c.Cost()
|
||||
}
|
||||
|
||||
type ctxEvalActivation struct {
|
||||
parent interpreter.Activation
|
||||
interrupt <-chan struct{}
|
||||
interruptCheckCount uint
|
||||
interruptCheckFrequency uint
|
||||
}
|
||||
|
||||
// ResolveName implements the Activation interface method, but adds a special #interrupted variable
|
||||
// which is capable of testing whether a 'done' signal is provided from a context.Context channel.
|
||||
func (a *ctxEvalActivation) ResolveName(name string) (interface{}, bool) {
|
||||
if name == "#interrupted" {
|
||||
a.interruptCheckCount++
|
||||
if a.interruptCheckCount%a.interruptCheckFrequency == 0 {
|
||||
select {
|
||||
case <-a.interrupt:
|
||||
return true, true
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
return a.parent.ResolveName(name)
|
||||
}
|
||||
|
||||
func (a *ctxEvalActivation) Parent() interpreter.Activation {
|
||||
return a.parent
|
||||
}
|
||||
|
||||
func newCtxEvalActivationPool() *ctxEvalActivationPool {
|
||||
return &ctxEvalActivationPool{
|
||||
Pool: sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &ctxEvalActivation{}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type ctxEvalActivationPool struct {
|
||||
sync.Pool
|
||||
}
|
||||
|
||||
// Setup initializes a pooled Activation with the ability check for context.Context cancellation
|
||||
func (p *ctxEvalActivationPool) Setup(vars interpreter.Activation, done <-chan struct{}, interruptCheckRate uint) *ctxEvalActivation {
|
||||
a := p.Pool.Get().(*ctxEvalActivation)
|
||||
a.parent = vars
|
||||
a.interrupt = done
|
||||
a.interruptCheckCount = 0
|
||||
a.interruptCheckFrequency = interruptCheckRate
|
||||
return a
|
||||
}
|
||||
|
||||
type evalActivation struct {
|
||||
vars map[string]interface{}
|
||||
lazyVars map[string]interface{}
|
||||
}
|
||||
|
||||
// ResolveName looks up the value of the input variable name, if found.
|
||||
//
|
||||
// Lazy bindings may be supplied within the map-based input in either of the following forms:
|
||||
// - func() interface{}
|
||||
// - func() ref.Val
|
||||
//
|
||||
// The lazy binding will only be invoked once per evaluation.
|
||||
//
|
||||
// Values which are not represented as ref.Val types on input may be adapted to a ref.Val using
|
||||
// the ref.TypeAdapter configured in the environment.
|
||||
func (a *evalActivation) ResolveName(name string) (interface{}, bool) {
|
||||
v, found := a.vars[name]
|
||||
if !found {
|
||||
return nil, false
|
||||
}
|
||||
switch obj := v.(type) {
|
||||
case func() ref.Val:
|
||||
if resolved, found := a.lazyVars[name]; found {
|
||||
return resolved, true
|
||||
}
|
||||
lazy := obj()
|
||||
a.lazyVars[name] = lazy
|
||||
return lazy, true
|
||||
case func() interface{}:
|
||||
if resolved, found := a.lazyVars[name]; found {
|
||||
return resolved, true
|
||||
}
|
||||
lazy := obj()
|
||||
a.lazyVars[name] = lazy
|
||||
return lazy, true
|
||||
default:
|
||||
return obj, true
|
||||
}
|
||||
}
|
||||
|
||||
// Parent implements the interpreter.Activation interface
|
||||
func (a *evalActivation) Parent() interpreter.Activation {
|
||||
return nil
|
||||
}
|
||||
|
||||
func newEvalActivationPool() *evalActivationPool {
|
||||
return &evalActivationPool{
|
||||
Pool: sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &evalActivation{lazyVars: make(map[string]interface{})}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type evalActivationPool struct {
|
||||
sync.Pool
|
||||
}
|
||||
|
||||
// Setup initializes a pooled Activation object with the map input.
|
||||
func (p *evalActivationPool) Setup(vars map[string]interface{}) *evalActivation {
|
||||
a := p.Pool.Get().(*evalActivation)
|
||||
a.vars = vars
|
||||
return a
|
||||
}
|
||||
|
||||
func (p *evalActivationPool) Put(value interface{}) {
|
||||
a := value.(*evalActivation)
|
||||
for k := range a.lazyVars {
|
||||
delete(a.lazyVars, k)
|
||||
}
|
||||
p.Pool.Put(a)
|
||||
}
|
||||
|
||||
var (
|
||||
emptyEvalState = interpreter.NewEvalState()
|
||||
|
||||
// activationPool is an internally managed pool of Activation values that wrap map[string]interface{} inputs
|
||||
activationPool = newEvalActivationPool()
|
||||
|
||||
// ctxActivationPool is an internally managed pool of Activation values that expose a special #interrupted variable
|
||||
ctxActivationPool = newCtxEvalActivationPool()
|
||||
)
|
Reference in New Issue
Block a user