-
Notifications
You must be signed in to change notification settings - Fork 28
/
labeling_filter.go
144 lines (119 loc) · 4.75 KB
/
labeling_filter.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
package filters
import (
"errors"
"fmt"
"net/http"
"github.com/tidwall/sjson"
"github.com/Peripli/service-manager/pkg/query"
"github.com/tidwall/gjson"
"github.com/Peripli/service-manager/pkg/log"
"github.com/Peripli/service-manager/pkg/web"
)
const LabelCriteriaFilterNameSuffix = "CriteriaFilter"
const ResourceLabelingFilterNameSuffix = "LabelingFilter"
// NewLabelingFilters returns set of filters which applies resource labeling rules
func NewLabelingFilters(labelName, labelKey string, basePaths []string, extractValueFunc func(request *web.Request) (string, error)) []web.Filter {
return []web.Filter{
newLabelCriteriaFilter(labelName, labelKey, basePaths, extractValueFunc),
newResourceLabelingFilter(labelName, labelKey, basePaths, extractValueFunc),
}
}
// newLabelCriteriaFilter creates a new LabelingFilter from the specified settings that filters the returned resources based on a filtering label
func newLabelCriteriaFilter(labelName, labelKey string, bastPaths []string, extractValueFunc func(request *web.Request) (string, error)) *LabelingFilter {
return &LabelingFilter{
LabelKey: labelKey,
FilterName: labelName + LabelCriteriaFilterNameSuffix,
BasePaths: bastPaths,
Methods: []string{http.MethodGet, http.MethodPatch, http.MethodDelete, http.MethodPost},
ExtractValue: extractValueFunc,
LabelingFunc: func(request *web.Request, labelKey, labelValue string) error {
ctx := request.Context()
criterion := query.ByLabel(query.EqualsOperator, labelKey, labelValue)
var err error
ctx, err = query.AddCriteria(ctx, criterion)
if err != nil {
return fmt.Errorf("could not add label criteria with key %s and value %s: %s", labelKey, labelValue, err)
}
log.C(ctx).Infof("Successfully added label criteria with key %s and value %s to context", labelKey, labelValue)
request.Request = request.WithContext(ctx)
return nil
},
}
}
// newResourceLabelingFilter creates a new LabelingFilter from the specified settings that adds a filtering label when creating resources
func newResourceLabelingFilter(labelName, labelKey string, bastPaths []string, extractValueFunc func(request *web.Request) (string, error)) *LabelingFilter {
return &LabelingFilter{
LabelKey: labelKey,
FilterName: labelName + ResourceLabelingFilterNameSuffix,
BasePaths: bastPaths,
Methods: []string{http.MethodPost},
ExtractValue: extractValueFunc,
LabelingFunc: func(request *web.Request, labelKey, labelValue string) error {
ctx := request.Context()
currentLabelValues := gjson.GetBytes(request.Body, fmt.Sprintf("labels.%s", labelKey)).Raw
var path string
var obj interface{}
if len(currentLabelValues) != 0 {
path = fmt.Sprintf("labels.%s.-1", labelKey)
obj = labelValue
} else {
path = fmt.Sprintf("labels.%s", labelKey)
obj = []string{labelValue}
}
var err error
request.Body, err = sjson.SetBytes(request.Body, path, obj)
if err != nil {
return fmt.Errorf("could not add label with key %s and value %s: %s to request body during resource creation", labelKey, labelValue, err)
}
log.C(ctx).Infof("Successfully added label with key %s and value %s to request body during resource creation", labelKey, labelValue)
return nil
},
}
}
// LabelingFilter applies labeling on the resources based on extracted values
type LabelingFilter struct {
// LabelKey is the key of the label
LabelKey string
ExtractValue func(request *web.Request) (string, error)
LabelingFunc func(request *web.Request, labelKey, labelValue string) error
FilterName string
BasePaths []string
Methods []string
}
// Name implements web.Named and returns the filter name
func (f *LabelingFilter) Name() string {
return f.FilterName
}
// Run implements web.Middleware and attempts to extract label value and apply labeling function
func (f *LabelingFilter) Run(request *web.Request, next web.Handler) (*web.Response, error) {
if f.ExtractValue == nil {
return nil, errors.New("ExtractValue function should be provided")
}
if f.LabelingFunc == nil {
return nil, errors.New("LabelingFunc function should be provided")
}
value, err := f.ExtractValue(request)
if err != nil {
return nil, err
}
if len(value) == 0 {
return next.Handle(request)
}
if err := f.LabelingFunc(request, f.LabelKey, value); err != nil {
return nil, err
}
return next.Handle(request)
}
// FilterMatchers implements web.Filter.FilterMatchers and specifies that the filter should run on configured method
func (f *LabelingFilter) FilterMatchers() []web.FilterMatcher {
matchers := make([]web.FilterMatcher, 0)
for _, basePath := range f.BasePaths {
matchers = append(matchers, web.FilterMatcher{
Matchers: []web.Matcher{
web.Path(basePath + "/**"),
web.Methods(f.Methods...),
},
})
}
return matchers
}