forked from zalando/skipper
-
Notifications
You must be signed in to change notification settings - Fork 0
/
predicate.go
123 lines (99 loc) · 3.01 KB
/
predicate.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
package loadbalancer
import (
"net/http"
"strconv"
"strings"
"github.com/zalando/skipper/predicates"
"github.com/zalando/skipper/routing"
)
const (
GroupPredicateName = "LBGroup"
MemberPredicateName = "LBMember"
)
type groupSpec struct{}
type groupPredicate struct {
group string
}
type memberSpec struct{}
type memberPredicate struct {
group string
indexString string
}
func getGroupDecision(h http.Header, group string) (string, bool) {
for _, header := range h[DecisionHeader] {
decision := strings.Split(header, "=")
if len(decision) != 2 {
continue
}
if decision[0] == group {
return decision[1], true
}
}
return "", false
}
// NewGroup creates a predicate spec identifying the entry route
// of a load balanced route group. E.g. eskip: LBGroup("my-group")
// where the single mandatory string argument is the name of the
// group, used as a reference in the LB decision filter and the
// the group member predicates.
//
// Typically, one such route is used in a load balancer setup and
// it contains the the decision filter (lbDecide("my-group", 4)).
// It is recommended to generate these routes with the
// loadbalancer.Balance() function that expects a single route and
// N backend endpoints as input and returns the loadbalanced set
// of routes representing the group.
func NewGroup() routing.PredicateSpec {
return &groupSpec{}
}
func (s *groupSpec) Name() string { return GroupPredicateName }
func (s *groupSpec) Create(args []interface{}) (routing.Predicate, error) {
if len(args) != 1 {
return nil, predicates.ErrInvalidPredicateParameters
}
group, ok := args[0].(string)
if !ok {
return nil, predicates.ErrInvalidPredicateParameters
}
return &groupPredicate{group: group}, nil
}
func (p *groupPredicate) Match(req *http.Request) bool {
_, has := getGroupDecision(req.Header, p.group)
return !has
}
// NewMember creates a predicate spec identifying a member route
// of a load balanced route group. E.g. eskip: LBMember("my-group", 2)
// where the first argument is the name of the group, while the
// second is the index of the current route.
//
// Typically, these routes are generated with the loadbalancer.Balance()
// function. See the description of LBGroup(), too.
func NewMember() routing.PredicateSpec {
return &memberSpec{}
}
func (s *memberSpec) Name() string { return MemberPredicateName }
func (s *memberSpec) Create(args []interface{}) (routing.Predicate, error) {
if len(args) != 2 {
return nil, predicates.ErrInvalidPredicateParameters
}
group, ok := args[0].(string)
if !ok {
return nil, predicates.ErrInvalidPredicateParameters
}
index, ok := args[1].(int)
if !ok {
findex, ok := args[1].(float64)
if !ok {
return nil, predicates.ErrInvalidPredicateParameters
}
index = int(findex)
}
return &memberPredicate{
group: group,
indexString: strconv.Itoa(index), // we only need it as a string
}, nil
}
func (p *memberPredicate) Match(req *http.Request) bool {
member, _ := getGroupDecision(req.Header, p.group)
return member == p.indexString
}