-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathflags.go
98 lines (89 loc) · 2.61 KB
/
flags.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
package main
import (
"encoding/json"
"fmt"
"github.com/Knetic/govaluate"
"github.com/jgadling/pennant/pkg/evaluators"
)
// Flag defines a feature as some metadata and a collection of policies
type Flag struct {
Name string `json:"name"`
Description string `json:"description"`
DefaultValue bool `json:"default"`
Policies []Policy `json:"policies"`
Version uint64 `json:"-"` // Yeah this abstraction is leaky :(
}
// Policy is a govaluate-compatible expression that returns true or false
type Policy struct {
Comment string `json:"comment"`
Rules string `json:"rules"`
ParsedExpr *govaluate.EvaluableExpression `json:"-"`
}
// LoadFlagJSON loads a flag but doesn't parse the policies yet
func LoadFlagJSON(flagData []byte) (*Flag, error) {
flag := Flag{}
if err := json.Unmarshal(flagData, &flag); err != nil {
return &flag, fmt.Errorf("can't parse flag %s", flagData)
}
return &flag, nil
}
// LoadAndParseFlag loads a flag and parses the policy expressions
func LoadAndParseFlag(flagData []byte) (*Flag, error) {
flag := Flag{}
if err := json.Unmarshal(flagData, &flag); err != nil {
return &flag, fmt.Errorf("can't parse flag %s", flagData)
}
err := flag.Parse()
return &flag, err
}
// Parse and cache all the policy expressions for this flag. This needs to be
// done before GetResult can be invoked.
func (f *Flag) Parse() error {
fallbackExpr, _ := govaluate.NewEvaluableExpressionWithFunctions("false", evaluators.GetLibraryFunctions())
for i := range f.Policies {
policy := &f.Policies[i]
expr, err := govaluate.NewEvaluableExpressionWithFunctions(policy.Rules, evaluators.GetLibraryFunctions())
if err != nil {
policy.ParsedExpr = fallbackExpr
return err
}
policy.ParsedExpr = expr
}
return nil
}
// GetValue compares a document to the flag policies and returns the boolean
// result of the evaluation.
func (f *Flag) GetValue(params map[string]interface{}) bool {
returnval := f.DefaultValue
if returnval {
return returnval
}
messages := make(chan bool)
for i := range f.Policies {
go func(policy *Policy) {
if policy.ParsedExpr == nil {
logger.Debugf("null value exception")
messages <- false
return
}
res, err := policy.ParsedExpr.Evaluate(params)
if err != nil {
logger.Debugf("err is %v", err)
messages <- false
return
}
if res == true {
messages <- true
}
messages <- false
}(&f.Policies[i])
}
// Wait for responses.
for i := 0; i < len(f.Policies); i++ {
if <-messages {
// First true means we can stop waiting
return true
}
}
return false
}