/
segment.go
78 lines (63 loc) · 2.29 KB
/
segment.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
package traffic
import (
"math/rand"
"net/http"
"github.com/zalando/skipper/predicates"
"github.com/zalando/skipper/routing"
)
type (
segmentSpec struct{}
segmentPredicate struct{ min, max float64 }
)
type contextKey struct{}
var randomValue contextKey
// NewSegment creates a new traffic segment predicate specification
func NewSegment() routing.WeightedPredicateSpec {
return &segmentSpec{}
}
func (*segmentSpec) Name() string {
return predicates.TrafficSegmentName
}
// Create new predicate instance with two number arguments _min_ and _max_
// from an interval [0, 1] (from zero included to one included) and _min_ <= _max_.
//
// Let _r_ be one-per-request uniform random number value from [0, 1).
// This predicate matches if _r_ belongs to an interval from [_min_, _max_).
// Upper interval boundary _max_ is excluded to simplify definition of
// adjacent intervals - the upper boundary of the first interval
// then equals lower boundary of the next and so on, e.g. [0, 0.25) and [0.25, 1).
//
// This predicate has weight of -1 and therefore does not affect route weight.
//
// Example of routes splitting traffic in 50%+30%+20% proportion:
//
// r50: Path("/test") && TrafficSegment(0.0, 0.5) -> <shunt>;
// r30: Path("/test") && TrafficSegment(0.5, 0.8) -> <shunt>;
// r20: Path("/test") && TrafficSegment(0.8, 1.0) -> <shunt>;
func (*segmentSpec) Create(args []any) (routing.Predicate, error) {
if len(args) != 2 {
return nil, predicates.ErrInvalidPredicateParameters
}
p, ok := &segmentPredicate{}, false
if p.min, ok = args[0].(float64); !ok || p.min < 0 || p.min > 1 {
return nil, predicates.ErrInvalidPredicateParameters
}
if p.max, ok = args[1].(float64); !ok || p.max < 0 || p.max > 1 {
return nil, predicates.ErrInvalidPredicateParameters
}
// min == max defines a never-matching interval, e.g. "owl interval" [0,0)
if p.min > p.max {
return nil, predicates.ErrInvalidPredicateParameters
}
return p, nil
}
// Weight returns -1.
// By returning -1 this predicate does not affect route weight.
func (*segmentSpec) Weight() int {
return -1
}
func (p *segmentPredicate) Match(req *http.Request) bool {
r := routing.FromContext(req.Context(), randomValue, rand.Float64)
// min == max defines a never-matching interval and always yields false
return p.min <= r && r < p.max
}