bump dependencies

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
This commit is contained in:
Jörn Friedrich Dreyer
2024-10-18 17:35:30 +02:00
parent 290477b494
commit ca113f5751
522 changed files with 93151 additions and 33036 deletions
+1 -1
View File
@@ -520,7 +520,7 @@ func attachRuleAnnotations(mod *Module) {
var j int
var found bool
for i, a := range cpy {
if rule.Ref().Equal(a.GetTargetPath()) {
if rule.Ref().GroundPrefix().Equal(a.GetTargetPath()) {
if a.Scope == annotationScopeDocument {
rule.Annotations = append(rule.Annotations, a)
} else if a.Scope == annotationScopeRule && rule.Loc().Row > a.Location.Row {
+36 -11
View File
@@ -33,15 +33,18 @@ type typeChecker struct {
allowNet []string
input types.Type
allowUndefinedFuncs bool
schemaTypes map[string]types.Type
}
// newTypeChecker returns a new typeChecker object that has no errors.
func newTypeChecker() *typeChecker {
tc := &typeChecker{}
tc.exprCheckers = map[string]exprChecker{
"eq": tc.checkExprEq,
return &typeChecker{
builtins: make(map[string]*Builtin),
schemaTypes: make(map[string]types.Type),
exprCheckers: map[string]exprChecker{
"eq": checkExprEq,
},
}
return tc
}
func (tc *typeChecker) newEnv(exist *TypeEnv) *TypeEnv {
@@ -196,20 +199,42 @@ func (tc *typeChecker) checkClosures(env *TypeEnv, expr *Expr) Errors {
return result
}
func (tc *typeChecker) getSchemaType(schemaAnnot *SchemaAnnotation, rule *Rule) (types.Type, *Error) {
if refType, exists := tc.schemaTypes[schemaAnnot.Schema.String()]; exists {
return refType, nil
}
refType, err := processAnnotation(tc.ss, schemaAnnot, rule, tc.allowNet)
if err != nil {
return nil, err
}
if refType == nil {
return nil, nil
}
tc.schemaTypes[schemaAnnot.Schema.String()] = refType
return refType, nil
}
func (tc *typeChecker) checkRule(env *TypeEnv, as *AnnotationSet, rule *Rule) {
env = env.wrap()
schemaAnnots := getRuleAnnotation(as, rule)
for _, schemaAnnot := range schemaAnnots {
ref, refType, err := processAnnotation(tc.ss, schemaAnnot, rule, tc.allowNet)
refType, err := tc.getSchemaType(schemaAnnot, rule)
if err != nil {
tc.err([]*Error{err})
continue
}
ref := schemaAnnot.Path
if ref == nil && refType == nil {
continue
}
prefixRef, t := getPrefix(env, ref)
if t == nil || len(prefixRef) == len(ref) {
env.tree.Put(ref, refType)
@@ -404,7 +429,7 @@ func (tc *typeChecker) checkExprBuiltin(env *TypeEnv, expr *Expr) *Error {
return nil
}
func (tc *typeChecker) checkExprEq(env *TypeEnv, expr *Expr) *Error {
func checkExprEq(env *TypeEnv, expr *Expr) *Error {
pre := getArgTypes(env, expr.Operands())
exp := Equality.Decl.FuncArgs()
@@ -1266,17 +1291,17 @@ func getRuleAnnotation(as *AnnotationSet, rule *Rule) (result []*SchemaAnnotatio
return result
}
func processAnnotation(ss *SchemaSet, annot *SchemaAnnotation, rule *Rule, allowNet []string) (Ref, types.Type, *Error) {
func processAnnotation(ss *SchemaSet, annot *SchemaAnnotation, rule *Rule, allowNet []string) (types.Type, *Error) {
var schema interface{}
if annot.Schema != nil {
if ss == nil {
return nil, nil, nil
return nil, nil
}
schema = ss.Get(annot.Schema)
if schema == nil {
return nil, nil, NewError(TypeErr, rule.Location, "undefined schema: %v", annot.Schema)
return nil, NewError(TypeErr, rule.Location, "undefined schema: %v", annot.Schema)
}
} else if annot.Definition != nil {
schema = *annot.Definition
@@ -1284,10 +1309,10 @@ func processAnnotation(ss *SchemaSet, annot *SchemaAnnotation, rule *Rule, allow
tpe, err := loadSchema(schema, allowNet)
if err != nil {
return nil, nil, NewError(TypeErr, rule.Location, err.Error())
return nil, NewError(TypeErr, rule.Location, err.Error())
}
return annot.Path, tpe, nil
return tpe, nil
}
func errAnnotationRedeclared(a *Annotations, other *Location) *Error {
+27 -10
View File
@@ -5332,8 +5332,7 @@ func rewriteDeclaredVarsInExpr(g *localVarGenerator, stack *localDeclaredVars, e
case *Term:
stop, errs = rewriteDeclaredVarsInTerm(g, stack, x, errs, strict)
case *With:
errs = rewriteDeclaredVarsInTermRecursive(g, stack, x.Value, errs, strict)
stop = true
stop, errs = true, rewriteDeclaredVarsInWithRecursive(g, stack, x, errs, strict)
}
return stop
})
@@ -5466,20 +5465,38 @@ func rewriteDeclaredVarsInTerm(g *localVarGenerator, stack *localDeclaredVars, t
}
func rewriteDeclaredVarsInTermRecursive(g *localVarGenerator, stack *localDeclaredVars, term *Term, errs Errors, strict bool) Errors {
WalkNodes(term, func(n Node) bool {
WalkTerms(term, func(t *Term) bool {
var stop bool
switch n := n.(type) {
case *With:
errs = rewriteDeclaredVarsInTermRecursive(g, stack, n.Value, errs, strict)
stop = true
case *Term:
stop, errs = rewriteDeclaredVarsInTerm(g, stack, n, errs, strict)
}
stop, errs = rewriteDeclaredVarsInTerm(g, stack, t, errs, strict)
return stop
})
return errs
}
func rewriteDeclaredVarsInWithRecursive(g *localVarGenerator, stack *localDeclaredVars, w *With, errs Errors, strict bool) Errors {
// NOTE(sr): `with input as` and `with input.a.b.c as` are deliberately skipped here: `input` could
// have been shadowed by a local variable/argument but should NOT be replaced in the `with` target.
//
// We cannot drop `input` from the stack since it's conceivable to do `with input[input] as` where
// the second input is meant to be the local var. It's a terrible idea, but when you're shadowing
// `input` those might be your thing.
errs = rewriteDeclaredVarsInTermRecursive(g, stack, w.Target, errs, strict)
if sdwInput, ok := stack.Declared(InputRootDocument.Value.(Var)); ok { // Was "input" shadowed...
switch value := w.Target.Value.(type) {
case Var:
if sdwInput.Equal(value) { // ...and replaced? If so, fix it
w.Target.Value = InputRootRef
}
case Ref:
if sdwInput.Equal(value[0].Value.(Var)) {
w.Target.Value.(Ref)[0].Value = InputRootDocument.Value
}
}
}
// No special handling of the `with` value
return rewriteDeclaredVarsInTermRecursive(g, stack, w.Value, errs, strict)
}
func rewriteDeclaredVarsInArrayComprehension(g *localVarGenerator, stack *localDeclaredVars, v *ArrayComprehension, errs Errors, strict bool) Errors {
used := NewVarSet()
used.Update(v.Term.Vars())
+38 -6
View File
@@ -30,6 +30,8 @@ var RegoV1CompatibleRef = Ref{VarTerm("rego"), StringTerm("v1")}
// RegoVersion defines the Rego syntax requirements for a module.
type RegoVersion int
const DefaultRegoVersion = RegoVersion(0)
const (
// RegoV0 is the default, original Rego syntax.
RegoV0 RegoVersion = iota
@@ -317,6 +319,31 @@ func (p *Parser) Parse() ([]Statement, []*Comment, Errors) {
for k, v := range futureKeywords {
allowedFutureKeywords[k] = v
}
// For sake of error reporting, we still need to check that keywords in capabilities are known,
for _, kw := range p.po.Capabilities.FutureKeywords {
if _, ok := futureKeywords[kw]; !ok {
return nil, nil, Errors{
&Error{
Code: ParseErr,
Message: fmt.Sprintf("illegal capabilities: unknown keyword: %v", kw),
Location: nil,
},
}
}
}
// and that explicitly requested future keywords are known.
for _, kw := range p.po.FutureKeywords {
if _, ok := allowedFutureKeywords[kw]; !ok {
return nil, nil, Errors{
&Error{
Code: ParseErr,
Message: fmt.Sprintf("unknown future keyword: %v", kw),
Location: nil,
},
}
}
}
} else {
for _, kw := range p.po.Capabilities.FutureKeywords {
var ok bool
@@ -686,6 +713,10 @@ func (p *Parser) parseRules() []*Rule {
// p[x] if ... becomes a single-value rule p[x]
if hasIf && !usesContains && len(rule.Head.Ref()) == 2 {
if !rule.Head.Ref()[1].IsGround() && len(rule.Head.Args) == 0 {
rule.Head.Key = rule.Head.Ref()[1]
}
if rule.Head.Value == nil {
rule.Head.generatedValue = true
rule.Head.Value = BooleanTerm(true).SetLocation(rule.Head.Location)
@@ -2665,15 +2696,16 @@ func (p *Parser) regoV1Import(imp *Import) {
return
}
if p.po.RegoVersion == RegoV1 {
// We're parsing for Rego v1, where the 'rego.v1' import is a no-op.
path := imp.Path.Value.(Ref)
// v1 is only valid option
if len(path) == 1 || !path[1].Equal(RegoV1CompatibleRef[1]) || len(path) > 2 {
p.errorf(imp.Path.Location, "invalid import `%s`, must be `%s`", path, RegoV1CompatibleRef)
return
}
path := imp.Path.Value.(Ref)
if len(path) == 1 || !path[1].Equal(RegoV1CompatibleRef[1]) || len(path) > 2 {
p.errorf(imp.Path.Location, "invalid import `%s`, must be `%s`", path, RegoV1CompatibleRef)
if p.po.RegoVersion == RegoV1 {
// We're parsing for Rego v1, where the 'rego.v1' import is a no-op.
return
}
+35 -5
View File
@@ -103,6 +103,14 @@ func MustParseStatement(input string) Statement {
return parsed
}
func MustParseStatementWithOpts(input string, popts ParserOptions) Statement {
parsed, err := ParseStatementWithOpts(input, popts)
if err != nil {
panic(err)
}
return parsed
}
// MustParseRef returns a parsed reference.
// If an error occurs during parsing, panic.
func MustParseRef(input string) Ref {
@@ -123,6 +131,16 @@ func MustParseRule(input string) *Rule {
return parsed
}
// MustParseRuleWithOpts returns a parsed rule.
// If an error occurs during parsing, panic.
func MustParseRuleWithOpts(input string, opts ParserOptions) *Rule {
parsed, err := ParseRuleWithOpts(input, opts)
if err != nil {
panic(err)
}
return parsed
}
// MustParseTerm returns a parsed term.
// If an error occurs during parsing, panic.
func MustParseTerm(input string) *Term {
@@ -269,11 +287,12 @@ func ParseCompleteDocRuleFromEqExpr(module *Module, lhs, rhs *Term) (*Rule, erro
setJSONOptions(body, &rhs.jsonOptions)
return &Rule{
Location: lhs.Location,
Head: head,
Body: body,
Module: module,
jsonOptions: lhs.jsonOptions,
Location: lhs.Location,
Head: head,
Body: body,
Module: module,
jsonOptions: lhs.jsonOptions,
generatedBody: true,
}, nil
}
@@ -608,6 +627,17 @@ func ParseStatement(input string) (Statement, error) {
return stmts[0], nil
}
func ParseStatementWithOpts(input string, popts ParserOptions) (Statement, error) {
stmts, _, err := ParseStatementsWithOpts("", input, popts)
if err != nil {
return nil, err
}
if len(stmts) != 1 {
return nil, fmt.Errorf("expected exactly one statement")
}
return stmts[0], nil
}
// ParseStatements is deprecated. Use ParseStatementWithOpts instead.
func ParseStatements(filename, input string) ([]Statement, []*Comment, error) {
return ParseStatementsWithOpts(filename, input, ParserOptions{})
+54 -11
View File
@@ -100,7 +100,7 @@ var Wildcard = &Term{Value: Var("_")}
var WildcardPrefix = "$"
// Keywords contains strings that map to language keywords.
var Keywords = KeywordsV0
var Keywords = KeywordsForRegoVersion(DefaultRegoVersion)
var KeywordsV0 = [...]string{
"not",
@@ -134,9 +134,23 @@ var KeywordsV1 = [...]string{
"every",
}
func KeywordsForRegoVersion(v RegoVersion) []string {
switch v {
case RegoV0:
return KeywordsV0[:]
case RegoV1, RegoV0CompatV1:
return KeywordsV1[:]
}
return nil
}
// IsKeyword returns true if s is a language keyword.
func IsKeyword(s string) bool {
for _, x := range Keywords {
return IsInKeywords(s, Keywords)
}
func IsInKeywords(s string, keywords []string) bool {
for _, x := range keywords {
if x == s {
return true
}
@@ -405,7 +419,7 @@ func (mod *Module) String() string {
buf = append(buf, "")
for _, rule := range mod.Rules {
buf = appendAnnotationStrings(buf, rule)
buf = append(buf, rule.String())
buf = append(buf, rule.stringWithOpts(toStringOpts{regoVersion: mod.regoVersion}))
}
}
return strings.Join(buf, "\n")
@@ -770,18 +784,30 @@ func (rule *Rule) Ref() Ref {
}
func (rule *Rule) String() string {
return rule.stringWithOpts(toStringOpts{})
}
type toStringOpts struct {
regoVersion RegoVersion
}
func (rule *Rule) stringWithOpts(opts toStringOpts) string {
buf := []string{}
if rule.Default {
buf = append(buf, "default")
}
buf = append(buf, rule.Head.String())
buf = append(buf, rule.Head.stringWithOpts(opts))
if !rule.Default {
switch opts.regoVersion {
case RegoV1, RegoV0CompatV1:
buf = append(buf, "if")
}
buf = append(buf, "{")
buf = append(buf, rule.Body.String())
buf = append(buf, "}")
}
if rule.Else != nil {
buf = append(buf, rule.Else.elseString())
buf = append(buf, rule.Else.elseString(opts))
}
return strings.Join(buf, " ")
}
@@ -824,7 +850,7 @@ func (rule *Rule) MarshalJSON() ([]byte, error) {
return json.Marshal(data)
}
func (rule *Rule) elseString() string {
func (rule *Rule) elseString(opts toStringOpts) string {
var buf []string
buf = append(buf, "else")
@@ -835,12 +861,17 @@ func (rule *Rule) elseString() string {
buf = append(buf, value.String())
}
switch opts.regoVersion {
case RegoV1, RegoV0CompatV1:
buf = append(buf, "if")
}
buf = append(buf, "{")
buf = append(buf, rule.Body.String())
buf = append(buf, "}")
if rule.Else != nil {
buf = append(buf, rule.Else.elseString())
buf = append(buf, rule.Else.elseString(opts))
}
return strings.Join(buf, " ")
@@ -1000,16 +1031,28 @@ func (head *Head) Equal(other *Head) bool {
}
func (head *Head) String() string {
return head.stringWithOpts(toStringOpts{})
}
func (head *Head) stringWithOpts(opts toStringOpts) string {
buf := strings.Builder{}
buf.WriteString(head.Ref().String())
containsAdded := false
switch {
case len(head.Args) != 0:
buf.WriteString(head.Args.String())
case len(head.Reference) == 1 && head.Key != nil:
buf.WriteRune('[')
buf.WriteString(head.Key.String())
buf.WriteRune(']')
switch opts.regoVersion {
case RegoV0:
buf.WriteRune('[')
buf.WriteString(head.Key.String())
buf.WriteRune(']')
default:
containsAdded = true
buf.WriteString(" contains ")
buf.WriteString(head.Key.String())
}
}
if head.Value != nil {
if head.Assign {
@@ -1018,7 +1061,7 @@ func (head *Head) String() string {
buf.WriteString(" = ")
}
buf.WriteString(head.Value.String())
} else if head.Name == "" && head.Key != nil {
} else if !containsAdded && head.Name == "" && head.Key != nil {
buf.WriteString(" contains ")
buf.WriteString(head.Key.String())
}
+6 -3
View File
@@ -50,9 +50,12 @@ func checkRootDocumentOverrides(node interface{}) Errors {
WalkExprs(node, func(expr *Expr) bool {
if expr.IsAssignment() {
name := expr.Operand(0).String()
if RootDocumentRefs.Contains(RefTerm(VarTerm(name))) {
errors = append(errors, NewError(CompileErr, expr.Location, "variables must not shadow %v (use a different variable name)", name))
// assign() can be called directly, so we need to assert its given first operand exists before checking its name.
if nameOp := expr.Operand(0); nameOp != nil {
name := nameOp.String()
if RootDocumentRefs.Contains(RefTerm(VarTerm(name))) {
errors = append(errors, NewError(CompileErr, expr.Location, "variables must not shadow %v (use a different variable name)", name))
}
}
}
return false
+7 -1
View File
@@ -200,7 +200,10 @@ func (m Manifest) Equal(other Manifest) bool {
if m.RegoVersion != nil && other.RegoVersion != nil && *m.RegoVersion != *other.RegoVersion {
return false
}
if !reflect.DeepEqual(m.FileRegoVersions, other.FileRegoVersions) {
// If both are nil, or both are empty, we consider them equal.
if !(len(m.FileRegoVersions) == 0 && len(other.FileRegoVersions) == 0) &&
!reflect.DeepEqual(m.FileRegoVersions, other.FileRegoVersions) {
return false
}
@@ -1092,6 +1095,9 @@ func (b *Bundle) FormatModulesForRegoVersion(version ast.RegoVersion, preserveMo
opts := format.Opts{}
if preserveModuleRegoVersion {
opts.RegoVersion = module.Parsed.RegoVersion()
opts.ParserOptions = &ast.ParserOptions{
RegoVersion: opts.RegoVersion,
}
} else {
opts.RegoVersion = version
}
File diff suppressed because it is too large Load Diff
+89 -31
View File
@@ -52,7 +52,7 @@ func SourceWithOpts(filename string, src []byte, opts Opts) ([]byte, error) {
} else {
if opts.RegoVersion == ast.RegoV1 {
// If the rego version is V1, we need to parse it as such, to allow for future keywords not being imported.
// Otherwise, we'll default to RegoV0
// Otherwise, we'll default to the default rego-version.
parserOpts.RegoVersion = ast.RegoV1
}
}
@@ -92,6 +92,16 @@ func MustAst(x interface{}) []byte {
return bs
}
// MustAstWithOpts is a helper function to format a Rego AST element. If any errors
// occurs this function will panic. This is mostly used for test
func MustAstWithOpts(x interface{}, opts Opts) []byte {
bs, err := AstWithOpts(x, opts)
if err != nil {
panic(err)
}
return bs
}
// Ast formats a Rego AST element. If the passed value is not a valid AST
// element, Ast returns nil and an error. If AST nodes are missing locations
// an arbitrary location will be used.
@@ -116,7 +126,16 @@ type fmtOpts struct {
// than if they don't.
refHeads bool
regoV1 bool
regoV1 bool
futureKeywords []string
}
func (o fmtOpts) keywords() []string {
if o.regoV1 {
return ast.KeywordsV1[:]
}
kws := ast.KeywordsV0[:]
return append(kws, o.futureKeywords...)
}
func AstWithOpts(x interface{}, opts Opts) ([]byte, error) {
@@ -161,6 +180,10 @@ func AstWithOpts(x interface{}, opts Opts) ([]byte, error) {
}
case *ast.Import:
if kw, ok := future.WhichFutureKeyword(n); ok {
o.futureKeywords = append(o.futureKeywords, kw)
}
switch {
case isRegoV1Compatible(n):
o.contains = true
@@ -190,8 +213,9 @@ func AstWithOpts(x interface{}, opts Opts) ([]byte, error) {
})
w := &writer{
indent: "\t",
errs: make([]*ast.Error, 0),
indent: "\t",
errs: make([]*ast.Error, 0),
fmtOpts: o,
}
switch x := x.(type) {
@@ -209,18 +233,17 @@ func AstWithOpts(x interface{}, opts Opts) ([]byte, error) {
x.Imports = ensureFutureKeywordImport(x.Imports, kw)
}
}
w.writeModule(x, o)
w.writeModule(x)
case *ast.Package:
w.writePackage(x, nil)
case *ast.Import:
w.writeImports([]*ast.Import{x}, nil)
case *ast.Rule:
w.writeRule(x, false /* isElse */, o, nil)
w.writeRule(x, false /* isElse */, nil)
case *ast.Head:
w.writeHead(x,
false, // isDefault
false, // isExpandedConst
o,
nil)
case ast.Body:
w.writeBody(x, nil)
@@ -292,9 +315,10 @@ type writer struct {
beforeEnd *ast.Comment
delay bool
errs ast.Errors
fmtOpts fmtOpts
}
func (w *writer) writeModule(module *ast.Module, o fmtOpts) {
func (w *writer) writeModule(module *ast.Module) {
var pkg *ast.Package
var others []interface{}
var comments []*ast.Comment
@@ -332,7 +356,7 @@ func (w *writer) writeModule(module *ast.Module, o fmtOpts) {
imports, others = gatherImports(others)
comments = w.writeImports(imports, comments)
rules, others = gatherRules(others)
comments = w.writeRules(rules, o, comments)
comments = w.writeRules(rules, comments)
}
for i, c := range comments {
@@ -355,7 +379,15 @@ func (w *writer) writePackage(pkg *ast.Package, comments []*ast.Comment) []*ast.
comments = w.insertComments(comments, pkg.Location)
w.startLine()
w.write(pkg.String())
// Omit head as all packages have the DefaultRootDocument prepended at parse time.
path := make(ast.Ref, len(pkg.Path)-1)
path[0] = ast.VarTerm(string(pkg.Path[1].Value.(ast.String)))
copy(path[1:], pkg.Path[2:])
w.write("package ")
w.writeRef(path)
w.blankLine()
return comments
@@ -370,16 +402,16 @@ func (w *writer) writeComments(comments []*ast.Comment) {
}
}
func (w *writer) writeRules(rules []*ast.Rule, o fmtOpts, comments []*ast.Comment) []*ast.Comment {
func (w *writer) writeRules(rules []*ast.Rule, comments []*ast.Comment) []*ast.Comment {
for _, rule := range rules {
comments = w.insertComments(comments, rule.Location)
comments = w.writeRule(rule, false, o, comments)
comments = w.writeRule(rule, false, comments)
w.blankLine()
}
return comments
}
func (w *writer) writeRule(rule *ast.Rule, isElse bool, o fmtOpts, comments []*ast.Comment) []*ast.Comment {
func (w *writer) writeRule(rule *ast.Rule, isElse bool, comments []*ast.Comment) []*ast.Comment {
if rule == nil {
return comments
}
@@ -398,17 +430,17 @@ func (w *writer) writeRule(rule *ast.Rule, isElse bool, o fmtOpts, comments []*a
// pretend that the rule has no body in this case.
isExpandedConst := rule.Body.Equal(ast.NewBody(ast.NewExpr(ast.BooleanTerm(true)))) && rule.Else == nil
comments = w.writeHead(rule.Head, rule.Default, isExpandedConst, o, comments)
comments = w.writeHead(rule.Head, rule.Default, isExpandedConst, comments)
// this excludes partial sets UNLESS `contains` is used
partialSetException := o.contains || rule.Head.Value != nil
partialSetException := w.fmtOpts.contains || rule.Head.Value != nil
if len(rule.Body) == 0 || isExpandedConst {
w.endLine()
return comments
}
if (o.regoV1 || o.ifs) && partialSetException {
if (w.fmtOpts.regoV1 || w.fmtOpts.ifs) && partialSetException {
w.write(" if")
if len(rule.Body) == 1 {
if rule.Body[0].Location.Row == rule.Head.Location.Row {
@@ -416,7 +448,7 @@ func (w *writer) writeRule(rule *ast.Rule, isElse bool, o fmtOpts, comments []*a
comments = w.writeExpr(rule.Body[0], comments)
w.endLine()
if rule.Else != nil {
comments = w.writeElse(rule, o, comments)
comments = w.writeElse(rule, comments)
}
return comments
}
@@ -444,12 +476,12 @@ func (w *writer) writeRule(rule *ast.Rule, isElse bool, o fmtOpts, comments []*a
w.startLine()
w.write("}")
if rule.Else != nil {
comments = w.writeElse(rule, o, comments)
comments = w.writeElse(rule, comments)
}
return comments
}
func (w *writer) writeElse(rule *ast.Rule, o fmtOpts, comments []*ast.Comment) []*ast.Comment {
func (w *writer) writeElse(rule *ast.Rule, comments []*ast.Comment) []*ast.Comment {
// If there was nothing else on the line before the "else" starts
// then preserve this style of else block, otherwise it will be
// started as an "inline" else eg:
@@ -511,16 +543,16 @@ func (w *writer) writeElse(rule *ast.Rule, o fmtOpts, comments []*ast.Comment) [
rule.Else.Head.Value.Location = rule.Else.Head.Location
}
return w.writeRule(rule.Else, true, o, comments)
return w.writeRule(rule.Else, true, comments)
}
func (w *writer) writeHead(head *ast.Head, isDefault, isExpandedConst bool, o fmtOpts, comments []*ast.Comment) []*ast.Comment {
func (w *writer) writeHead(head *ast.Head, isDefault, isExpandedConst bool, comments []*ast.Comment) []*ast.Comment {
ref := head.Ref()
if head.Key != nil && head.Value == nil && !head.HasDynamicRef() {
ref = ref.GroundPrefix()
}
if o.refHeads || len(ref) == 1 {
w.write(ref.String())
if w.fmtOpts.refHeads || len(ref) == 1 {
w.writeRef(ref)
} else {
w.write(ref[0].String())
w.write("[")
@@ -538,7 +570,7 @@ func (w *writer) writeHead(head *ast.Head, isDefault, isExpandedConst bool, o fm
w.write(")")
}
if head.Key != nil {
if o.contains && head.Value == nil {
if w.fmtOpts.contains && head.Value == nil {
w.write(" contains ")
comments = w.writeTerm(head.Key, comments)
} else if head.Value == nil { // no `if` for p[x] notation
@@ -556,10 +588,9 @@ func (w *writer) writeHead(head *ast.Head, isDefault, isExpandedConst bool, o fm
// * a.b -> a contains "b"
// * a.b.c -> a.b.c := true
// * a.b.c.d -> a.b.c.d := true
isRegoV1RefConst := o.regoV1 && isExpandedConst && head.Key == nil && len(head.Args) == 0
isRegoV1RefConst := w.fmtOpts.regoV1 && isExpandedConst && head.Key == nil && len(head.Args) == 0
if len(head.Args) > 0 &&
head.Location == head.Value.Location &&
if head.Location == head.Value.Location &&
head.Name != "else" &&
ast.Compare(head.Value, ast.BooleanTerm(true)) == 0 &&
!isRegoV1RefConst {
@@ -569,7 +600,7 @@ func (w *writer) writeHead(head *ast.Head, isDefault, isExpandedConst bool, o fm
return comments
}
if head.Assign || o.regoV1 {
if head.Assign || w.fmtOpts.regoV1 {
// preserve assignment operator, and enforce it if formatting for Rego v1
w.write(" := ")
} else {
@@ -847,7 +878,7 @@ var varRegexp = regexp.MustCompile("^[[:alpha:]_][[:alpha:][:digit:]_]*$")
func (w *writer) writeRefStringPath(s ast.String) {
str := string(s)
if varRegexp.MatchString(str) && !ast.IsKeyword(str) {
if varRegexp.MatchString(str) && !ast.IsInKeywords(str, w.fmtOpts.keywords()) {
w.write("." + str)
} else {
w.writeBracketed(s.String())
@@ -1058,7 +1089,7 @@ func (w *writer) writeImports(imports []*ast.Import, comments []*ast.Comment) []
})
for _, i := range group {
w.startLine()
w.write(i.String())
w.writeImport(i)
if c, ok := m[i]; ok {
w.write(" " + c.String())
}
@@ -1070,6 +1101,28 @@ func (w *writer) writeImports(imports []*ast.Import, comments []*ast.Comment) []
return comments
}
func (w *writer) writeImport(imp *ast.Import) {
path := imp.Path.Value.(ast.Ref)
buf := []string{"import"}
if _, ok := future.WhichFutureKeyword(imp); ok {
// We don't want to wrap future.keywords imports in parens, so we create a new writer that doesn't
w2 := writer{
buf: bytes.Buffer{},
}
w2.writeRef(path)
buf = append(buf, w2.buf.String())
} else {
buf = append(buf, path.String())
}
if len(imp.Alias) > 0 {
buf = append(buf, "as "+imp.Alias.String())
}
w.write(strings.Join(buf, " "))
}
type entryWriter func(interface{}, []*ast.Comment) []*ast.Comment
func (w *writer) writeIterable(elements []interface{}, last *ast.Location, close *ast.Location, comments []*ast.Comment, fn entryWriter) []*ast.Comment {
@@ -1496,7 +1549,12 @@ func ensureFutureKeywordImport(imps []*ast.Import, kw string) []*ast.Import {
}
}
imp := &ast.Import{
Path: ast.MustParseTerm("future.keywords." + kw),
// NOTE: This is a hack to not error on the ref containing a keyword already present in v1.
// A cleaner solution would be to instead allow refs to contain keyword terms.
// E.g. in v1, `import future.keywords["in"]` is valid, but `import future.keywords.in` is not
// as it contains a reserved keyword.
Path: ast.MustParseTerm("future.keywords[\"" + kw + "\"]"),
//Path: ast.MustParseTerm("future.keywords." + kw),
}
imp.Location = defaultLocation(imp)
return append(imps, imp)
@@ -749,12 +749,12 @@ opa_set_get,opa_value_hash
opa_set_get,opa_value_compare
opa_number_try_int,opa_atoi64
opa_number_try_int,opa_abort
opa_value_get,opa_abort
opa_value_get,opa_atoi64
opa_value_get,opa_value_hash
opa_value_get,opa_value_compare
opa_value_compare_number,opa_atoi64
opa_value_get,opa_abort
opa_value_compare_number,opa_abort
opa_value_compare_number,opa_atoi64
opa_value_compare_number,opa_number_to_bf
opa_value_compare_number,mpd_qcmp
opa_value_compare_number,mpd_del
@@ -779,10 +779,10 @@ opa_value_compare_set,opa_value_compare_set
opa_value_compare_set,opa_abort
opa_number_hash,opa_atof64
opa_number_hash,opa_abort
opa_value_iter,opa_abort
opa_value_iter,opa_atoi64
opa_value_iter,opa_value_hash
opa_value_iter,opa_value_compare
opa_value_iter,opa_abort
opa_object_keys,opa_malloc
opa_object_keys,opa_free
opa_object_keys,opa_value_compare
@@ -817,7 +817,6 @@ opa_value_merge,opa_malloc
opa_value_merge,opa_value_get
opa_value_merge,__opa_object_insert
opa_value_merge,opa_value_merge
opa_value_merge,opa_abort
opa_value_merge,opa_atoi64
opa_value_merge,opa_value_hash
opa_value_merge,opa_value_compare_number
@@ -825,6 +824,7 @@ opa_value_merge,opa_strncmp
opa_value_merge,opa_value_compare
opa_value_merge,opa_value_compare_object
opa_value_merge,opa_value_compare_set
opa_value_merge,opa_abort
__opa_object_insert,opa_value_hash
__opa_object_insert,opa_value_compare
__opa_object_insert,__opa_value_free
@@ -904,8 +904,8 @@ opa_value_remove_path,opa_value_get
opa_value_remove_path,opa_object_remove
opa_lookup,opa_value_get
opa_lookup,opa_value_iter
opa_lookup,opa_atoi64
opa_lookup,opa_abort
opa_lookup,opa_atoi64
opa_mapping_init,opa_json_parse
opa_mapping_lookup,opa_lookup
node::re2\28std::__1::basic_string<char\2c\20std::__1::char_traits<char>\2c\20std::__1::allocator<char>\20>\20const&\29,std::__1::basic_string<char\2c\20std::__1::char_traits<char>\2c\20std::__1::allocator<char>\20>::assign\28char\20const*\29
1 opa_agg_count opa_value_type
749 opa_set_get opa_value_compare
750 opa_number_try_int opa_atoi64
751 opa_number_try_int opa_abort
opa_value_get opa_abort
752 opa_value_get opa_atoi64
753 opa_value_get opa_value_hash
754 opa_value_get opa_value_compare
755 opa_value_compare_number opa_value_get opa_atoi64 opa_abort
756 opa_value_compare_number opa_abort
757 opa_value_compare_number opa_atoi64
758 opa_value_compare_number opa_number_to_bf
759 opa_value_compare_number mpd_qcmp
760 opa_value_compare_number mpd_del
779 opa_value_compare_set opa_abort
780 opa_number_hash opa_atof64
781 opa_number_hash opa_abort
opa_value_iter opa_abort
782 opa_value_iter opa_atoi64
783 opa_value_iter opa_value_hash
784 opa_value_iter opa_value_compare
785 opa_value_iter opa_abort
786 opa_object_keys opa_malloc
787 opa_object_keys opa_free
788 opa_object_keys opa_value_compare
817 opa_value_merge opa_value_get
818 opa_value_merge __opa_object_insert
819 opa_value_merge opa_value_merge
opa_value_merge opa_abort
820 opa_value_merge opa_atoi64
821 opa_value_merge opa_value_hash
822 opa_value_merge opa_value_compare_number
824 opa_value_merge opa_value_compare
825 opa_value_merge opa_value_compare_object
826 opa_value_merge opa_value_compare_set
827 opa_value_merge opa_abort
828 __opa_object_insert opa_value_hash
829 __opa_object_insert opa_value_compare
830 __opa_object_insert __opa_value_free
904 opa_value_remove_path opa_object_remove
905 opa_lookup opa_value_get
906 opa_lookup opa_value_iter
opa_lookup opa_atoi64
907 opa_lookup opa_abort
908 opa_lookup opa_atoi64
909 opa_mapping_init opa_json_parse
910 opa_mapping_lookup opa_lookup
911 node::re2\28std::__1::basic_string<char\2c\20std::__1::char_traits<char>\2c\20std::__1::allocator<char>\20>\20const&\29 std::__1::basic_string<char\2c\20std::__1::char_traits<char>\2c\20std::__1::allocator<char>\20>::assign\28char\20const*\29
Binary file not shown.
@@ -35,3 +35,15 @@ func IsFutureKeyword(imp *ast.Import, kw string) bool {
path[1].Equal(ast.StringTerm("keywords")) &&
path[2].Equal(ast.StringTerm(kw))
}
func WhichFutureKeyword(imp *ast.Import) (string, bool) {
path := imp.Path.Value.(ast.Ref)
if len(path) == 3 &&
ast.FutureRootDocument.Equal(path[0]) &&
path[1].Equal(ast.StringTerm("keywords")) {
if str, ok := path[2].Value.(ast.String); ok {
return string(str), true
}
}
return "", false
}
+10 -9
View File
@@ -18,13 +18,14 @@ type Result struct {
// EvalOpts define options for performing an evaluation.
type EvalOpts struct {
Input *interface{}
Metrics metrics.Metrics
Entrypoint int32
Time time.Time
Seed io.Reader
InterQueryBuiltinCache cache.InterQueryCache
NDBuiltinCache builtins.NDBCache
PrintHook print.Hook
Capabilities *ast.Capabilities
Input *interface{}
Metrics metrics.Metrics
Entrypoint int32
Time time.Time
Seed io.Reader
InterQueryBuiltinCache cache.InterQueryCache
InterQueryBuiltinValueCache cache.InterQueryValueCache
NDBuiltinCache builtins.NDBCache
PrintHook print.Hook
Capabilities *ast.Capabilities
}
+1 -1
View File
@@ -576,7 +576,7 @@ func (m *Manager) Labels() map[string]string {
return m.Config.Labels
}
// InterQueryBuiltinCacheConfig returns the configuration for the inter-query cache.
// InterQueryBuiltinCacheConfig returns the configuration for the inter-query caches.
func (m *Manager) InterQueryBuiltinCacheConfig() *cache.Config {
m.mtx.Lock()
defer m.mtx.Unlock()
+21 -7
View File
@@ -30,10 +30,11 @@ const (
ec2DefaultTokenPath = "http://169.254.169.254/latest/api/token"
// ref. https://docs.aws.amazon.com/AmazonECS/latest/userguide/task-iam-roles.html
ecsDefaultCredServicePath = "http://169.254.170.2"
ecsRelativePathEnvVar = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"
ecsFullPathEnvVar = "AWS_CONTAINER_CREDENTIALS_FULL_URI"
ecsAuthorizationTokenEnvVar = "AWS_CONTAINER_AUTHORIZATION_TOKEN"
ecsDefaultCredServicePath = "http://169.254.170.2"
ecsRelativePathEnvVar = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"
ecsFullPathEnvVar = "AWS_CONTAINER_CREDENTIALS_FULL_URI"
ecsAuthorizationTokenEnvVar = "AWS_CONTAINER_AUTHORIZATION_TOKEN"
ecsAuthorizationTokenFileEnvVar = "AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE"
// ref. https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html
stsDefaultDomain = "amazonaws.com"
@@ -277,9 +278,22 @@ func (cs *awsMetadataCredentialService) refreshFromService(ctx context.Context)
// if using the AWS_CONTAINER_CREDENTIALS_FULL_URI variable, we need to associate the token
// to the request
if _, useFullPath := os.LookupEnv(ecsFullPathEnvVar); useFullPath {
token, tokenExists := os.LookupEnv(ecsAuthorizationTokenEnvVar)
if !tokenExists {
return errors.New("unable to get ECS metadata authorization token")
var token string
tokenFilePath, tokenFilePathExists := os.LookupEnv(ecsAuthorizationTokenFileEnvVar)
if tokenFilePathExists {
tokenBytes, err := os.ReadFile(tokenFilePath)
if err != nil {
return errors.New("failed to read ECS metadata authorization token from file: " + err.Error())
}
token = string(tokenBytes)
// If token doesn't exist as a file check if it exists as an environment variable
} else {
var tokenExists bool
token, tokenExists = os.LookupEnv(ecsAuthorizationTokenEnvVar)
if !tokenExists {
return errors.New("unable to get ECS metadata authorization token")
}
}
req.Header.Set("Authorization", token)
}
+112 -90
View File
@@ -99,32 +99,33 @@ type preparedQuery struct {
// EvalContext defines the set of options allowed to be set at evaluation
// time. Any other options will need to be set on a new Rego object.
type EvalContext struct {
hasInput bool
time time.Time
seed io.Reader
rawInput *interface{}
parsedInput ast.Value
metrics metrics.Metrics
txn storage.Transaction
instrument bool
instrumentation *topdown.Instrumentation
partialNamespace string
queryTracers []topdown.QueryTracer
compiledQuery compiledQuery
unknowns []string
disableInlining []ast.Ref
parsedUnknowns []*ast.Term
indexing bool
earlyExit bool
interQueryBuiltinCache cache.InterQueryCache
ndBuiltinCache builtins.NDBCache
resolvers []refResolver
sortSets bool
copyMaps bool
printHook print.Hook
capabilities *ast.Capabilities
strictBuiltinErrors bool
virtualCache topdown.VirtualCache
hasInput bool
time time.Time
seed io.Reader
rawInput *interface{}
parsedInput ast.Value
metrics metrics.Metrics
txn storage.Transaction
instrument bool
instrumentation *topdown.Instrumentation
partialNamespace string
queryTracers []topdown.QueryTracer
compiledQuery compiledQuery
unknowns []string
disableInlining []ast.Ref
parsedUnknowns []*ast.Term
indexing bool
earlyExit bool
interQueryBuiltinCache cache.InterQueryCache
interQueryBuiltinValueCache cache.InterQueryValueCache
ndBuiltinCache builtins.NDBCache
resolvers []refResolver
sortSets bool
copyMaps bool
printHook print.Hook
capabilities *ast.Capabilities
strictBuiltinErrors bool
virtualCache topdown.VirtualCache
}
func (e *EvalContext) RawInput() *interface{} {
@@ -147,6 +148,10 @@ func (e *EvalContext) InterQueryBuiltinCache() cache.InterQueryCache {
return e.interQueryBuiltinCache
}
func (e *EvalContext) InterQueryBuiltinValueCache() cache.InterQueryValueCache {
return e.interQueryBuiltinValueCache
}
func (e *EvalContext) PrintHook() print.Hook {
return e.printHook
}
@@ -307,6 +312,14 @@ func EvalInterQueryBuiltinCache(c cache.InterQueryCache) EvalOption {
}
}
// EvalInterQueryBuiltinValueCache sets the inter-query value cache that built-in functions can utilize
// during evaluation.
func EvalInterQueryBuiltinValueCache(c cache.InterQueryValueCache) EvalOption {
return func(e *EvalContext) {
e.interQueryBuiltinValueCache = c
}
}
// EvalNDBuiltinCache sets the non-deterministic builtin cache that built-in functions can
// use during evaluation.
func EvalNDBuiltinCache(c builtins.NDBCache) EvalOption {
@@ -546,64 +559,65 @@ type loadPaths struct {
// Rego constructs a query and can be evaluated to obtain results.
type Rego struct {
query string
parsedQuery ast.Body
compiledQueries map[queryType]compiledQuery
pkg string
parsedPackage *ast.Package
imports []string
parsedImports []*ast.Import
rawInput *interface{}
parsedInput ast.Value
unknowns []string
parsedUnknowns []*ast.Term
disableInlining []string
shallowInlining bool
skipPartialNamespace bool
partialNamespace string
modules []rawModule
parsedModules map[string]*ast.Module
compiler *ast.Compiler
store storage.Store
ownStore bool
txn storage.Transaction
metrics metrics.Metrics
queryTracers []topdown.QueryTracer
tracebuf *topdown.BufferTracer
trace bool
instrumentation *topdown.Instrumentation
instrument bool
capture map[*ast.Expr]ast.Var // map exprs to generated capture vars
termVarID int
dump io.Writer
runtime *ast.Term
time time.Time
seed io.Reader
capabilities *ast.Capabilities
builtinDecls map[string]*ast.Builtin
builtinFuncs map[string]*topdown.Builtin
unsafeBuiltins map[string]struct{}
loadPaths loadPaths
bundlePaths []string
bundles map[string]*bundle.Bundle
skipBundleVerification bool
interQueryBuiltinCache cache.InterQueryCache
ndBuiltinCache builtins.NDBCache
strictBuiltinErrors bool
builtinErrorList *[]topdown.Error
resolvers []refResolver
schemaSet *ast.SchemaSet
target string // target type (wasm, rego, etc.)
opa opa.EvalEngine
generateJSON func(*ast.Term, *EvalContext) (interface{}, error)
printHook print.Hook
enablePrintStatements bool
distributedTacingOpts tracing.Options
strict bool
pluginMgr *plugins.Manager
plugins []TargetPlugin
targetPrepState TargetPluginEval
regoVersion ast.RegoVersion
query string
parsedQuery ast.Body
compiledQueries map[queryType]compiledQuery
pkg string
parsedPackage *ast.Package
imports []string
parsedImports []*ast.Import
rawInput *interface{}
parsedInput ast.Value
unknowns []string
parsedUnknowns []*ast.Term
disableInlining []string
shallowInlining bool
skipPartialNamespace bool
partialNamespace string
modules []rawModule
parsedModules map[string]*ast.Module
compiler *ast.Compiler
store storage.Store
ownStore bool
txn storage.Transaction
metrics metrics.Metrics
queryTracers []topdown.QueryTracer
tracebuf *topdown.BufferTracer
trace bool
instrumentation *topdown.Instrumentation
instrument bool
capture map[*ast.Expr]ast.Var // map exprs to generated capture vars
termVarID int
dump io.Writer
runtime *ast.Term
time time.Time
seed io.Reader
capabilities *ast.Capabilities
builtinDecls map[string]*ast.Builtin
builtinFuncs map[string]*topdown.Builtin
unsafeBuiltins map[string]struct{}
loadPaths loadPaths
bundlePaths []string
bundles map[string]*bundle.Bundle
skipBundleVerification bool
interQueryBuiltinCache cache.InterQueryCache
interQueryBuiltinValueCache cache.InterQueryValueCache
ndBuiltinCache builtins.NDBCache
strictBuiltinErrors bool
builtinErrorList *[]topdown.Error
resolvers []refResolver
schemaSet *ast.SchemaSet
target string // target type (wasm, rego, etc.)
opa opa.EvalEngine
generateJSON func(*ast.Term, *EvalContext) (interface{}, error)
printHook print.Hook
enablePrintStatements bool
distributedTacingOpts tracing.Options
strict bool
pluginMgr *plugins.Manager
plugins []TargetPlugin
targetPrepState TargetPluginEval
regoVersion ast.RegoVersion
}
// Function represents a built-in function that is callable in Rego.
@@ -1114,6 +1128,14 @@ func InterQueryBuiltinCache(c cache.InterQueryCache) func(r *Rego) {
}
}
// InterQueryBuiltinValueCache sets the inter-query value cache that built-in functions can utilize
// during evaluation.
func InterQueryBuiltinValueCache(c cache.InterQueryValueCache) func(r *Rego) {
return func(r *Rego) {
r.interQueryBuiltinValueCache = c
}
}
// NDBuiltinCache sets the non-deterministic builtins cache.
func NDBuiltinCache(c builtins.NDBCache) func(r *Rego) {
return func(r *Rego) {
@@ -1309,6 +1331,7 @@ func (r *Rego) Eval(ctx context.Context) (ResultSet, error) {
EvalInstrument(r.instrument),
EvalTime(r.time),
EvalInterQueryBuiltinCache(r.interQueryBuiltinCache),
EvalInterQueryBuiltinValueCache(r.interQueryBuiltinValueCache),
EvalSeed(r.seed),
}
@@ -1386,6 +1409,7 @@ func (r *Rego) Partial(ctx context.Context) (*PartialQueries, error) {
EvalMetrics(r.metrics),
EvalInstrument(r.instrument),
EvalInterQueryBuiltinCache(r.interQueryBuiltinCache),
EvalInterQueryBuiltinValueCache(r.interQueryBuiltinValueCache),
}
if r.ndBuiltinCache != nil {
@@ -1943,6 +1967,7 @@ func (r *Rego) parseQuery(queryImports []*ast.Import, m metrics.Metrics) (ast.Bo
if err != nil {
return nil, err
}
popts.RegoVersion = r.regoVersion
popts, err = parserOptionsFromRegoVersionImport(queryImports, popts)
if err != nil {
return nil, err
@@ -2106,6 +2131,7 @@ func (r *Rego) eval(ctx context.Context, ectx *EvalContext) (ResultSet, error) {
WithIndexing(ectx.indexing).
WithEarlyExit(ectx.earlyExit).
WithInterQueryBuiltinCache(ectx.interQueryBuiltinCache).
WithInterQueryBuiltinValueCache(ectx.interQueryBuiltinValueCache).
WithStrictBuiltinErrors(r.strictBuiltinErrors).
WithBuiltinErrorList(r.builtinErrorList).
WithSeed(ectx.seed).
@@ -2164,7 +2190,6 @@ func (r *Rego) eval(ctx context.Context, ectx *EvalContext) (ResultSet, error) {
}
func (r *Rego) evalWasm(ctx context.Context, ectx *EvalContext) (ResultSet, error) {
input := ectx.rawInput
if ectx.parsedInput != nil {
i := interface{}(ectx.parsedInput)
@@ -2393,6 +2418,7 @@ func (r *Rego) partial(ctx context.Context, ectx *EvalContext) (*PartialQueries,
WithSkipPartialNamespace(r.skipPartialNamespace).
WithShallowInlining(r.shallowInlining).
WithInterQueryBuiltinCache(ectx.interQueryBuiltinCache).
WithInterQueryBuiltinValueCache(ectx.interQueryBuiltinValueCache).
WithStrictBuiltinErrors(ectx.strictBuiltinErrors).
WithSeed(ectx.seed).
WithPrintHook(ectx.printHook)
@@ -2431,14 +2457,10 @@ func (r *Rego) partial(ctx context.Context, ectx *EvalContext) (*PartialQueries,
return nil, err
}
// If the target rego-version is v0, and the rego.v1 import is available, then we attempt to apply it to support modules.
if r.regoVersion == ast.RegoV0 && (r.capabilities == nil || r.capabilities.ContainsFeature(ast.FeatureRegoV1Import)) {
// If the target rego-version in v0, and the rego.v1 import is available, then we attempt to apply it to support modules.
for i, mod := range support {
if mod.RegoVersion() != ast.RegoV0 {
continue
}
// We can't apply the RegoV0CompatV1 version to the support module if it contains rules or vars that
// conflict with future keywords.
applyRegoVersion := true
+20 -19
View File
@@ -35,25 +35,26 @@ type (
// BuiltinContext contains context from the evaluator that may be used by
// built-in functions.
BuiltinContext struct {
Context context.Context // request context that was passed when query started
Metrics metrics.Metrics // metrics registry for recording built-in specific metrics
Seed io.Reader // randomization source
Time *ast.Term // wall clock time
Cancel Cancel // atomic value that signals evaluation to halt
Runtime *ast.Term // runtime information on the OPA instance
Cache builtins.Cache // built-in function state cache
InterQueryBuiltinCache cache.InterQueryCache // cross-query built-in function state cache
NDBuiltinCache builtins.NDBCache // cache for non-deterministic built-in state
Location *ast.Location // location of built-in call
Tracers []Tracer // Deprecated: Use QueryTracers instead
QueryTracers []QueryTracer // tracer objects for trace() built-in function
TraceEnabled bool // indicates whether tracing is enabled for the evaluation
QueryID uint64 // identifies query being evaluated
ParentID uint64 // identifies parent of query being evaluated
PrintHook print.Hook // provides callback function to use for printing
DistributedTracingOpts tracing.Options // options to be used by distributed tracing.
rand *rand.Rand // randomization source for non-security-sensitive operations
Capabilities *ast.Capabilities
Context context.Context // request context that was passed when query started
Metrics metrics.Metrics // metrics registry for recording built-in specific metrics
Seed io.Reader // randomization source
Time *ast.Term // wall clock time
Cancel Cancel // atomic value that signals evaluation to halt
Runtime *ast.Term // runtime information on the OPA instance
Cache builtins.Cache // built-in function state cache
InterQueryBuiltinCache cache.InterQueryCache // cross-query built-in function state cache
InterQueryBuiltinValueCache cache.InterQueryValueCache // cross-query built-in function state value cache. this cache is useful for scenarios where the entry size cannot be calculated
NDBuiltinCache builtins.NDBCache // cache for non-deterministic built-in state
Location *ast.Location // location of built-in call
Tracers []Tracer // Deprecated: Use QueryTracers instead
QueryTracers []QueryTracer // tracer objects for trace() built-in function
TraceEnabled bool // indicates whether tracing is enabled for the evaluation
QueryID uint64 // identifies query being evaluated
ParentID uint64 // identifies parent of query being evaluated
PrintHook print.Hook // provides callback function to use for printing
DistributedTracingOpts tracing.Options // options to be used by distributed tracing.
rand *rand.Rand // randomization source for non-security-sensitive operations
Capabilities *ast.Capabilities
}
// BuiltinFunc defines an interface for implementing built-in functions.
+106 -3
View File
@@ -18,14 +18,22 @@ import (
)
const (
defaultInterQueryBuiltinValueCacheSize = int(0) // unlimited
defaultMaxSizeBytes = int64(0) // unlimited
defaultForcedEvictionThresholdPercentage = int64(100) // trigger at max_size_bytes
defaultStaleEntryEvictionPeriodSeconds = int64(0) // never
)
// Config represents the configuration of the inter-query cache.
// Config represents the configuration for the inter-query builtin cache.
type Config struct {
InterQueryBuiltinCache InterQueryBuiltinCacheConfig `json:"inter_query_builtin_cache"`
InterQueryBuiltinCache InterQueryBuiltinCacheConfig `json:"inter_query_builtin_cache"`
InterQueryBuiltinValueCache InterQueryBuiltinValueCacheConfig `json:"inter_query_builtin_value_cache"`
}
// InterQueryBuiltinValueCacheConfig represents the configuration of the inter-query value cache that built-in functions can utilize.
// MaxNumEntries - max number of cache entries
type InterQueryBuiltinValueCacheConfig struct {
MaxNumEntries *int `json:"max_num_entries,omitempty"`
}
// InterQueryBuiltinCacheConfig represents the configuration of the inter-query cache that built-in functions can utilize.
@@ -47,7 +55,12 @@ func ParseCachingConfig(raw []byte) (*Config, error) {
*threshold = defaultForcedEvictionThresholdPercentage
period := new(int64)
*period = defaultStaleEntryEvictionPeriodSeconds
return &Config{InterQueryBuiltinCache: InterQueryBuiltinCacheConfig{MaxSizeBytes: maxSize, ForcedEvictionThresholdPercentage: threshold, StaleEntryEvictionPeriodSeconds: period}}, nil
maxInterQueryBuiltinValueCacheSize := new(int)
*maxInterQueryBuiltinValueCacheSize = defaultInterQueryBuiltinValueCacheSize
return &Config{InterQueryBuiltinCache: InterQueryBuiltinCacheConfig{MaxSizeBytes: maxSize, ForcedEvictionThresholdPercentage: threshold, StaleEntryEvictionPeriodSeconds: period},
InterQueryBuiltinValueCache: InterQueryBuiltinValueCacheConfig{MaxNumEntries: maxInterQueryBuiltinValueCacheSize}}, nil
}
var config Config
@@ -89,6 +102,18 @@ func (c *Config) validateAndInjectDefaults() error {
return fmt.Errorf("invalid stale_entry_eviction_period_seconds %v", period)
}
}
if c.InterQueryBuiltinValueCache.MaxNumEntries == nil {
maxSize := new(int)
*maxSize = defaultInterQueryBuiltinValueCacheSize
c.InterQueryBuiltinValueCache.MaxNumEntries = maxSize
} else {
numEntries := *c.InterQueryBuiltinValueCache.MaxNumEntries
if numEntries < 0 {
return fmt.Errorf("invalid max_num_entries %v", numEntries)
}
}
return nil
}
@@ -301,3 +326,81 @@ func (c *cache) cleanStaleValues() (dropped int) {
}
return dropped
}
type InterQueryValueCache interface {
Get(key ast.Value) (value any, found bool)
Insert(key ast.Value, value any) int
Delete(key ast.Value)
UpdateConfig(config *Config)
}
type interQueryValueCache struct {
items map[string]any
config *Config
mtx sync.RWMutex
}
// Get returns the value in the cache for k.
func (c *interQueryValueCache) Get(k ast.Value) (any, bool) {
c.mtx.RLock()
defer c.mtx.RUnlock()
value, ok := c.items[k.String()]
return value, ok
}
// Insert inserts a key k into the cache with value v.
func (c *interQueryValueCache) Insert(k ast.Value, v any) (dropped int) {
c.mtx.Lock()
defer c.mtx.Unlock()
maxEntries := c.maxNumEntries()
if maxEntries > 0 {
if len(c.items) >= maxEntries {
itemsToRemove := len(c.items) - maxEntries + 1
// Delete a (semi-)random key to make room for the new one.
for k := range c.items {
delete(c.items, k)
dropped++
if itemsToRemove == dropped {
break
}
}
}
}
c.items[k.String()] = v
return dropped
}
// Delete deletes the value in the cache for k.
func (c *interQueryValueCache) Delete(k ast.Value) {
c.mtx.Lock()
defer c.mtx.Unlock()
delete(c.items, k.String())
}
// UpdateConfig updates the cache config.
func (c *interQueryValueCache) UpdateConfig(config *Config) {
if config == nil {
return
}
c.mtx.Lock()
defer c.mtx.Unlock()
c.config = config
}
func (c *interQueryValueCache) maxNumEntries() int {
if c.config == nil {
return defaultInterQueryBuiltinValueCacheSize
}
return *c.config.InterQueryBuiltinValueCache.MaxNumEntries
}
func NewInterQueryValueCache(_ context.Context, config *Config) InterQueryValueCache {
return &interQueryValueCache{
items: map[string]any{},
config: config,
}
}
+68 -66
View File
@@ -58,55 +58,56 @@ func (ee deferredEarlyExitError) Error() string {
}
type eval struct {
ctx context.Context
metrics metrics.Metrics
seed io.Reader
time *ast.Term
queryID uint64
queryIDFact *queryIDFactory
parent *eval
caller *eval
cancel Cancel
query ast.Body
queryCompiler ast.QueryCompiler
index int
indexing bool
earlyExit bool
bindings *bindings
store storage.Store
baseCache *baseCache
txn storage.Transaction
compiler *ast.Compiler
input *ast.Term
data *ast.Term
external *resolverTrie
targetStack *refStack
tracers []QueryTracer
traceEnabled bool
traceLastLocation *ast.Location // Last location of a trace event.
plugTraceVars bool
instr *Instrumentation
builtins map[string]*Builtin
builtinCache builtins.Cache
ndBuiltinCache builtins.NDBCache
functionMocks *functionMocksStack
virtualCache VirtualCache
comprehensionCache *comprehensionCache
interQueryBuiltinCache cache.InterQueryCache
saveSet *saveSet
saveStack *saveStack
saveSupport *saveSupport
saveNamespace *ast.Term
skipSaveNamespace bool
inliningControl *inliningControl
genvarprefix string
genvarid int
runtime *ast.Term
builtinErrors *builtinErrors
printHook print.Hook
tracingOpts tracing.Options
findOne bool
strictObjects bool
ctx context.Context
metrics metrics.Metrics
seed io.Reader
time *ast.Term
queryID uint64
queryIDFact *queryIDFactory
parent *eval
caller *eval
cancel Cancel
query ast.Body
queryCompiler ast.QueryCompiler
index int
indexing bool
earlyExit bool
bindings *bindings
store storage.Store
baseCache *baseCache
txn storage.Transaction
compiler *ast.Compiler
input *ast.Term
data *ast.Term
external *resolverTrie
targetStack *refStack
tracers []QueryTracer
traceEnabled bool
traceLastLocation *ast.Location // Last location of a trace event.
plugTraceVars bool
instr *Instrumentation
builtins map[string]*Builtin
builtinCache builtins.Cache
ndBuiltinCache builtins.NDBCache
functionMocks *functionMocksStack
virtualCache VirtualCache
comprehensionCache *comprehensionCache
interQueryBuiltinCache cache.InterQueryCache
interQueryBuiltinValueCache cache.InterQueryValueCache
saveSet *saveSet
saveStack *saveStack
saveSupport *saveSupport
saveNamespace *ast.Term
skipSaveNamespace bool
inliningControl *inliningControl
genvarprefix string
genvarid int
runtime *ast.Term
builtinErrors *builtinErrors
printHook print.Hook
tracingOpts tracing.Options
findOne bool
strictObjects bool
}
func (e *eval) Run(iter evalIterator) error {
@@ -817,23 +818,24 @@ func (e *eval) evalCall(terms []*ast.Term, iter unifyIterator) error {
}
bctx := BuiltinContext{
Context: e.ctx,
Metrics: e.metrics,
Seed: e.seed,
Time: e.time,
Cancel: e.cancel,
Runtime: e.runtime,
Cache: e.builtinCache,
InterQueryBuiltinCache: e.interQueryBuiltinCache,
NDBuiltinCache: e.ndBuiltinCache,
Location: e.query[e.index].Location,
QueryTracers: e.tracers,
TraceEnabled: e.traceEnabled,
QueryID: e.queryID,
ParentID: parentID,
PrintHook: e.printHook,
DistributedTracingOpts: e.tracingOpts,
Capabilities: capabilities,
Context: e.ctx,
Metrics: e.metrics,
Seed: e.seed,
Time: e.time,
Cancel: e.cancel,
Runtime: e.runtime,
Cache: e.builtinCache,
InterQueryBuiltinCache: e.interQueryBuiltinCache,
InterQueryBuiltinValueCache: e.interQueryBuiltinValueCache,
NDBuiltinCache: e.ndBuiltinCache,
Location: e.query[e.index].Location,
QueryTracers: e.tracers,
TraceEnabled: e.traceEnabled,
QueryID: e.queryID,
ParentID: parentID,
PrintHook: e.printHook,
DistributedTracingOpts: e.tracingOpts,
Capabilities: capabilities,
}
eval := evalBuiltin{
+31 -3
View File
@@ -11,11 +11,12 @@ import (
)
const globCacheMaxSize = 100
const globInterQueryValueCacheHits = "rego_builtin_glob_interquery_value_cache_hits"
var globCacheLock = sync.Mutex{}
var globCache map[string]glob.Glob
func builtinGlobMatch(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
func builtinGlobMatch(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
pattern, err := builtins.StringOperand(operands[0].Value, 1)
if err != nil {
return err
@@ -50,14 +51,41 @@ func builtinGlobMatch(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Ter
}
id := builder.String()
m, err := globCompileAndMatch(id, string(pattern), string(match), delimiters)
m, err := globCompileAndMatch(bctx, id, string(pattern), string(match), delimiters)
if err != nil {
return err
}
return iter(ast.BooleanTerm(m))
}
func globCompileAndMatch(id, pattern, match string, delimiters []rune) (bool, error) {
func globCompileAndMatch(bctx BuiltinContext, id, pattern, match string, delimiters []rune) (bool, error) {
if bctx.InterQueryBuiltinValueCache != nil {
val, ok := bctx.InterQueryBuiltinValueCache.Get(ast.String(id))
if ok {
pat, valid := val.(glob.Glob)
if !valid {
// The cache key may exist for a different value type (eg. regex).
// In this case, we calculate the glob and return the result w/o updating the cache.
var err error
if pat, err = glob.Compile(pattern, delimiters...); err != nil {
return false, err
}
return pat.Match(match), nil
}
bctx.Metrics.Counter(globInterQueryValueCacheHits).Incr()
out := pat.Match(match)
return out, nil
}
res, err := glob.Compile(pattern, delimiters...)
if err != nil {
return false, err
}
bctx.InterQueryBuiltinValueCache.Insert(ast.String(id), res)
return res.Match(match), nil
}
globCacheLock.Lock()
defer globCacheLock.Unlock()
p, ok := globCache[id]
+2 -3
View File
@@ -168,6 +168,7 @@ func generateRaiseErrorResult(err error) *ast.Term {
func getHTTPResponse(bctx BuiltinContext, req ast.Object) (*ast.Term, error) {
bctx.Metrics.Timer(httpSendLatencyMetricKey).Start()
defer bctx.Metrics.Timer(httpSendLatencyMetricKey).Stop()
key, err := getKeyFromRequest(req)
if err != nil {
@@ -199,8 +200,6 @@ func getHTTPResponse(bctx BuiltinContext, req ast.Object) (*ast.Term, error) {
}
}
bctx.Metrics.Timer(httpSendLatencyMetricKey).Stop()
return ast.NewTerm(resp), nil
}
@@ -474,7 +473,7 @@ func createHTTPRequest(bctx BuiltinContext, obj ast.Object) (*http.Request, *htt
}
body = bytes.NewBuffer(bodyValBytes)
case "raw_body":
rawBody = bytes.NewBuffer([]byte(strVal))
rawBody = bytes.NewBufferString(strVal)
case "tls_use_system_certs":
tempTLSUseSystemCerts, err := strconv.ParseBool(obj.Get(val).String())
if err != nil {
+110 -101
View File
@@ -27,38 +27,39 @@ type QueryResult map[ast.Var]*ast.Term
// Query provides a configurable interface for performing query evaluation.
type Query struct {
seed io.Reader
time time.Time
cancel Cancel
query ast.Body
queryCompiler ast.QueryCompiler
compiler *ast.Compiler
store storage.Store
txn storage.Transaction
input *ast.Term
external *resolverTrie
tracers []QueryTracer
plugTraceVars bool
unknowns []*ast.Term
partialNamespace string
skipSaveNamespace bool
metrics metrics.Metrics
instr *Instrumentation
disableInlining []ast.Ref
shallowInlining bool
genvarprefix string
runtime *ast.Term
builtins map[string]*Builtin
indexing bool
earlyExit bool
interQueryBuiltinCache cache.InterQueryCache
ndBuiltinCache builtins.NDBCache
strictBuiltinErrors bool
builtinErrorList *[]Error
strictObjects bool
printHook print.Hook
tracingOpts tracing.Options
virtualCache VirtualCache
seed io.Reader
time time.Time
cancel Cancel
query ast.Body
queryCompiler ast.QueryCompiler
compiler *ast.Compiler
store storage.Store
txn storage.Transaction
input *ast.Term
external *resolverTrie
tracers []QueryTracer
plugTraceVars bool
unknowns []*ast.Term
partialNamespace string
skipSaveNamespace bool
metrics metrics.Metrics
instr *Instrumentation
disableInlining []ast.Ref
shallowInlining bool
genvarprefix string
runtime *ast.Term
builtins map[string]*Builtin
indexing bool
earlyExit bool
interQueryBuiltinCache cache.InterQueryCache
interQueryBuiltinValueCache cache.InterQueryValueCache
ndBuiltinCache builtins.NDBCache
strictBuiltinErrors bool
builtinErrorList *[]Error
strictObjects bool
printHook print.Hook
tracingOpts tracing.Options
virtualCache VirtualCache
}
// Builtin represents a built-in function that queries can call.
@@ -246,6 +247,12 @@ func (q *Query) WithInterQueryBuiltinCache(c cache.InterQueryCache) *Query {
return q
}
// WithInterQueryBuiltinValueCache sets the inter-query value cache that built-in functions can utilize.
func (q *Query) WithInterQueryBuiltinValueCache(c cache.InterQueryValueCache) *Query {
q.interQueryBuiltinValueCache = c
return q
}
// WithNDBuiltinCache sets the non-deterministic builtin cache.
func (q *Query) WithNDBuiltinCache(c builtins.NDBCache) *Query {
q.ndBuiltinCache = c
@@ -331,39 +338,40 @@ func (q *Query) PartialRun(ctx context.Context) (partials []ast.Body, support []
}
e := &eval{
ctx: ctx,
metrics: q.metrics,
seed: q.seed,
time: ast.NumberTerm(int64ToJSONNumber(q.time.UnixNano())),
cancel: q.cancel,
query: q.query,
queryCompiler: q.queryCompiler,
queryIDFact: f,
queryID: f.Next(),
bindings: b,
compiler: q.compiler,
store: q.store,
baseCache: newBaseCache(),
targetStack: newRefStack(),
txn: q.txn,
input: q.input,
external: q.external,
tracers: q.tracers,
traceEnabled: len(q.tracers) > 0,
plugTraceVars: q.plugTraceVars,
instr: q.instr,
builtins: q.builtins,
builtinCache: builtins.Cache{},
functionMocks: newFunctionMocksStack(),
interQueryBuiltinCache: q.interQueryBuiltinCache,
ndBuiltinCache: q.ndBuiltinCache,
virtualCache: vc,
comprehensionCache: newComprehensionCache(),
saveSet: newSaveSet(q.unknowns, b, q.instr),
saveStack: newSaveStack(),
saveSupport: newSaveSupport(),
saveNamespace: ast.StringTerm(q.partialNamespace),
skipSaveNamespace: q.skipSaveNamespace,
ctx: ctx,
metrics: q.metrics,
seed: q.seed,
time: ast.NumberTerm(int64ToJSONNumber(q.time.UnixNano())),
cancel: q.cancel,
query: q.query,
queryCompiler: q.queryCompiler,
queryIDFact: f,
queryID: f.Next(),
bindings: b,
compiler: q.compiler,
store: q.store,
baseCache: newBaseCache(),
targetStack: newRefStack(),
txn: q.txn,
input: q.input,
external: q.external,
tracers: q.tracers,
traceEnabled: len(q.tracers) > 0,
plugTraceVars: q.plugTraceVars,
instr: q.instr,
builtins: q.builtins,
builtinCache: builtins.Cache{},
functionMocks: newFunctionMocksStack(),
interQueryBuiltinCache: q.interQueryBuiltinCache,
interQueryBuiltinValueCache: q.interQueryBuiltinValueCache,
ndBuiltinCache: q.ndBuiltinCache,
virtualCache: vc,
comprehensionCache: newComprehensionCache(),
saveSet: newSaveSet(q.unknowns, b, q.instr),
saveStack: newSaveStack(),
saveSupport: newSaveSupport(),
saveNamespace: ast.StringTerm(q.partialNamespace),
skipSaveNamespace: q.skipSaveNamespace,
inliningControl: &inliningControl{
shallow: q.shallowInlining,
},
@@ -516,42 +524,43 @@ func (q *Query) Iter(ctx context.Context, iter func(QueryResult) error) error {
}
e := &eval{
ctx: ctx,
metrics: q.metrics,
seed: q.seed,
time: ast.NumberTerm(int64ToJSONNumber(q.time.UnixNano())),
cancel: q.cancel,
query: q.query,
queryCompiler: q.queryCompiler,
queryIDFact: f,
queryID: f.Next(),
bindings: newBindings(0, q.instr),
compiler: q.compiler,
store: q.store,
baseCache: newBaseCache(),
targetStack: newRefStack(),
txn: q.txn,
input: q.input,
external: q.external,
tracers: q.tracers,
traceEnabled: len(q.tracers) > 0,
plugTraceVars: q.plugTraceVars,
instr: q.instr,
builtins: q.builtins,
builtinCache: builtins.Cache{},
functionMocks: newFunctionMocksStack(),
interQueryBuiltinCache: q.interQueryBuiltinCache,
ndBuiltinCache: q.ndBuiltinCache,
virtualCache: vc,
comprehensionCache: newComprehensionCache(),
genvarprefix: q.genvarprefix,
runtime: q.runtime,
indexing: q.indexing,
earlyExit: q.earlyExit,
builtinErrors: &builtinErrors{},
printHook: q.printHook,
tracingOpts: q.tracingOpts,
strictObjects: q.strictObjects,
ctx: ctx,
metrics: q.metrics,
seed: q.seed,
time: ast.NumberTerm(int64ToJSONNumber(q.time.UnixNano())),
cancel: q.cancel,
query: q.query,
queryCompiler: q.queryCompiler,
queryIDFact: f,
queryID: f.Next(),
bindings: newBindings(0, q.instr),
compiler: q.compiler,
store: q.store,
baseCache: newBaseCache(),
targetStack: newRefStack(),
txn: q.txn,
input: q.input,
external: q.external,
tracers: q.tracers,
traceEnabled: len(q.tracers) > 0,
plugTraceVars: q.plugTraceVars,
instr: q.instr,
builtins: q.builtins,
builtinCache: builtins.Cache{},
functionMocks: newFunctionMocksStack(),
interQueryBuiltinCache: q.interQueryBuiltinCache,
interQueryBuiltinValueCache: q.interQueryBuiltinValueCache,
ndBuiltinCache: q.ndBuiltinCache,
virtualCache: vc,
comprehensionCache: newComprehensionCache(),
genvarprefix: q.genvarprefix,
runtime: q.runtime,
indexing: q.indexing,
earlyExit: q.earlyExit,
builtinErrors: &builtinErrors{},
printHook: q.printHook,
tracingOpts: q.tracingOpts,
strictObjects: q.strictObjects,
}
e.caller = e
q.metrics.Timer(metrics.RegoQueryEval).Start()
+34 -11
View File
@@ -16,6 +16,7 @@ import (
)
const regexCacheMaxSize = 100
const regexInterQueryValueCacheHits = "rego_builtin_regex_interquery_value_cache_hits"
var regexpCacheLock = sync.Mutex{}
var regexpCache map[string]*regexp.Regexp
@@ -35,7 +36,7 @@ func builtinRegexIsValid(_ BuiltinContext, operands []*ast.Term, iter func(*ast.
return iter(ast.BooleanTerm(true))
}
func builtinRegexMatch(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
func builtinRegexMatch(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
s1, err := builtins.StringOperand(operands[0].Value, 1)
if err != nil {
return err
@@ -44,7 +45,7 @@ func builtinRegexMatch(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Te
if err != nil {
return err
}
re, err := getRegexp(string(s1))
re, err := getRegexp(bctx, string(s1))
if err != nil {
return err
}
@@ -81,7 +82,7 @@ func builtinRegexMatchTemplate(_ BuiltinContext, operands []*ast.Term, iter func
return iter(ast.BooleanTerm(re.MatchString(string(match))))
}
func builtinRegexSplit(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
func builtinRegexSplit(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
s1, err := builtins.StringOperand(operands[0].Value, 1)
if err != nil {
return err
@@ -90,7 +91,7 @@ func builtinRegexSplit(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Te
if err != nil {
return err
}
re, err := getRegexp(string(s1))
re, err := getRegexp(bctx, string(s1))
if err != nil {
return err
}
@@ -103,7 +104,29 @@ func builtinRegexSplit(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Te
return iter(ast.NewTerm(ast.NewArray(arr...)))
}
func getRegexp(pat string) (*regexp.Regexp, error) {
func getRegexp(bctx BuiltinContext, pat string) (*regexp.Regexp, error) {
if bctx.InterQueryBuiltinValueCache != nil {
val, ok := bctx.InterQueryBuiltinValueCache.Get(ast.String(pat))
if ok {
res, valid := val.(*regexp.Regexp)
if !valid {
// The cache key may exist for a different value type (eg. glob).
// In this case, we calculate the regex and return the result w/o updating the cache.
return regexp.Compile(pat)
}
bctx.Metrics.Counter(regexInterQueryValueCacheHits).Incr()
return res, nil
}
re, err := regexp.Compile(pat)
if err != nil {
return nil, err
}
bctx.InterQueryBuiltinValueCache.Insert(ast.String(pat), re)
return re, nil
}
regexpCacheLock.Lock()
defer regexpCacheLock.Unlock()
re, ok := regexpCache[pat]
@@ -156,7 +179,7 @@ func builtinGlobsMatch(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Te
return iter(ast.BooleanTerm(ne))
}
func builtinRegexFind(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
func builtinRegexFind(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
s1, err := builtins.StringOperand(operands[0].Value, 1)
if err != nil {
return err
@@ -169,7 +192,7 @@ func builtinRegexFind(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Ter
if err != nil {
return err
}
re, err := getRegexp(string(s1))
re, err := getRegexp(bctx, string(s1))
if err != nil {
return err
}
@@ -182,7 +205,7 @@ func builtinRegexFind(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Ter
return iter(ast.NewTerm(ast.NewArray(arr...)))
}
func builtinRegexFindAllStringSubmatch(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
func builtinRegexFindAllStringSubmatch(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
s1, err := builtins.StringOperand(operands[0].Value, 1)
if err != nil {
return err
@@ -196,7 +219,7 @@ func builtinRegexFindAllStringSubmatch(_ BuiltinContext, operands []*ast.Term, i
return err
}
re, err := getRegexp(string(s1))
re, err := getRegexp(bctx, string(s1))
if err != nil {
return err
}
@@ -214,7 +237,7 @@ func builtinRegexFindAllStringSubmatch(_ BuiltinContext, operands []*ast.Term, i
return iter(ast.NewTerm(ast.NewArray(outer...)))
}
func builtinRegexReplace(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
func builtinRegexReplace(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
base, err := builtins.StringOperand(operands[0].Value, 1)
if err != nil {
return err
@@ -230,7 +253,7 @@ func builtinRegexReplace(_ BuiltinContext, operands []*ast.Term, iter func(*ast.
return err
}
re, err := getRegexp(string(pattern))
re, err := getRegexp(bctx, string(pattern))
if err != nil {
return err
}
+4
View File
@@ -114,6 +114,10 @@ func Reference(x interface{}) *interface{} {
// Unmarshal decodes a YAML, JSON or JSON extension value into the specified type.
func Unmarshal(bs []byte, v interface{}) error {
if len(bs) > 2 && bs[0] == 0xef && bs[1] == 0xbb && bs[2] == 0xbf {
bs = bs[3:] // Strip UTF-8 BOM, see https://www.rfc-editor.org/rfc/rfc8259#section-8.1
}
if json.Valid(bs) {
return unmarshalJSON(bs, v, false)
}
+1 -1
View File
@@ -11,7 +11,7 @@ import (
)
// Version is the canonical version of OPA.
var Version = "0.68.0"
var Version = "0.69.0"
// GoVersion is the version of Go this was built with
var GoVersion = runtime.Version()