forked from open-policy-agent/opa
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbuiltins.go
206 lines (177 loc) · 7.32 KB
/
builtins.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
// Copyright 2016 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
package topdown
import (
"context"
"encoding/binary"
"fmt"
"io"
"math/rand"
"github.com/open-policy-agent/opa/ast"
"github.com/open-policy-agent/opa/metrics"
"github.com/open-policy-agent/opa/topdown/builtins"
"github.com/open-policy-agent/opa/topdown/cache"
"github.com/open-policy-agent/opa/topdown/print"
"github.com/open-policy-agent/opa/tracing"
)
type (
// FunctionalBuiltin1 is deprecated. Use BuiltinFunc instead.
FunctionalBuiltin1 func(op1 ast.Value) (output ast.Value, err error)
// FunctionalBuiltin2 is deprecated. Use BuiltinFunc instead.
FunctionalBuiltin2 func(op1, op2 ast.Value) (output ast.Value, err error)
// FunctionalBuiltin3 is deprecated. Use BuiltinFunc instead.
FunctionalBuiltin3 func(op1, op2, op3 ast.Value) (output ast.Value, err error)
// FunctionalBuiltin4 is deprecated. Use BuiltinFunc instead.
FunctionalBuiltin4 func(op1, op2, op3, op4 ast.Value) (output ast.Value, err error)
// 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
}
// BuiltinFunc defines an interface for implementing built-in functions.
// The built-in function is called with the plugged operands from the call
// (including the output operands.) The implementation should evaluate the
// operands and invoke the iterator for each successful/defined output
// value.
BuiltinFunc func(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error
)
// Rand returns a random number generator based on the Seed for this built-in
// context. The random number will be re-used across multiple calls to this
// function. If a random number generator cannot be created, an error is
// returned.
func (bctx *BuiltinContext) Rand() (*rand.Rand, error) {
if bctx.rand != nil {
return bctx.rand, nil
}
seed, err := readInt64(bctx.Seed)
if err != nil {
return nil, err
}
bctx.rand = rand.New(rand.NewSource(seed))
return bctx.rand, nil
}
// RegisterBuiltinFunc adds a new built-in function to the evaluation engine.
func RegisterBuiltinFunc(name string, f BuiltinFunc) {
builtinFunctions[name] = builtinErrorWrapper(name, f)
}
// RegisterFunctionalBuiltin1 is deprecated use RegisterBuiltinFunc instead.
func RegisterFunctionalBuiltin1(name string, fun FunctionalBuiltin1) {
builtinFunctions[name] = functionalWrapper1(name, fun)
}
// RegisterFunctionalBuiltin2 is deprecated use RegisterBuiltinFunc instead.
func RegisterFunctionalBuiltin2(name string, fun FunctionalBuiltin2) {
builtinFunctions[name] = functionalWrapper2(name, fun)
}
// RegisterFunctionalBuiltin3 is deprecated use RegisterBuiltinFunc instead.
func RegisterFunctionalBuiltin3(name string, fun FunctionalBuiltin3) {
builtinFunctions[name] = functionalWrapper3(name, fun)
}
// RegisterFunctionalBuiltin4 is deprecated use RegisterBuiltinFunc instead.
func RegisterFunctionalBuiltin4(name string, fun FunctionalBuiltin4) {
builtinFunctions[name] = functionalWrapper4(name, fun)
}
// GetBuiltin returns a built-in function implementation, nil if no built-in found.
func GetBuiltin(name string) BuiltinFunc {
return builtinFunctions[name]
}
// BuiltinEmpty is deprecated.
type BuiltinEmpty struct{}
func (BuiltinEmpty) Error() string {
return "<empty>"
}
var builtinFunctions = map[string]BuiltinFunc{}
func builtinErrorWrapper(name string, fn BuiltinFunc) BuiltinFunc {
return func(bctx BuiltinContext, args []*ast.Term, iter func(*ast.Term) error) error {
err := fn(bctx, args, iter)
if err == nil {
return nil
}
return handleBuiltinErr(name, bctx.Location, err)
}
}
func functionalWrapper1(name string, fn FunctionalBuiltin1) BuiltinFunc {
return func(bctx BuiltinContext, args []*ast.Term, iter func(*ast.Term) error) error {
result, err := fn(args[0].Value)
if err == nil {
return iter(ast.NewTerm(result))
}
return handleBuiltinErr(name, bctx.Location, err)
}
}
func functionalWrapper2(name string, fn FunctionalBuiltin2) BuiltinFunc {
return func(bctx BuiltinContext, args []*ast.Term, iter func(*ast.Term) error) error {
result, err := fn(args[0].Value, args[1].Value)
if err == nil {
return iter(ast.NewTerm(result))
}
return handleBuiltinErr(name, bctx.Location, err)
}
}
func functionalWrapper3(name string, fn FunctionalBuiltin3) BuiltinFunc {
return func(bctx BuiltinContext, args []*ast.Term, iter func(*ast.Term) error) error {
result, err := fn(args[0].Value, args[1].Value, args[2].Value)
if err == nil {
return iter(ast.NewTerm(result))
}
return handleBuiltinErr(name, bctx.Location, err)
}
}
func functionalWrapper4(name string, fn FunctionalBuiltin4) BuiltinFunc {
return func(bctx BuiltinContext, args []*ast.Term, iter func(*ast.Term) error) error {
result, err := fn(args[0].Value, args[1].Value, args[2].Value, args[3].Value)
if err == nil {
return iter(ast.NewTerm(result))
}
if _, empty := err.(BuiltinEmpty); empty {
return nil
}
return handleBuiltinErr(name, bctx.Location, err)
}
}
func handleBuiltinErr(name string, loc *ast.Location, err error) error {
switch err := err.(type) {
case BuiltinEmpty:
return nil
case *Error, Halt:
return err
case builtins.ErrOperand:
return &Error{
Code: TypeErr,
Message: fmt.Sprintf("%v: %v", string(name), err.Error()),
Location: loc,
}
default:
return &Error{
Code: BuiltinErr,
Message: fmt.Sprintf("%v: %v", string(name), err.Error()),
Location: loc,
}
}
}
func readInt64(r io.Reader) (int64, error) {
bs := make([]byte, 8)
n, err := io.ReadFull(r, bs)
if n != len(bs) || err != nil {
return 0, err
}
return int64(binary.BigEndian.Uint64(bs)), nil
}