-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathselector.go
More file actions
198 lines (172 loc) · 6.94 KB
/
Copy pathselector.go
File metadata and controls
198 lines (172 loc) · 6.94 KB
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
// Copyright 2022-2025 FLUIDOS Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package v1alpha1
import (
"encoding/json"
"fmt"
"regexp"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/klog/v2"
)
const (
// TypeMatchFilter is the name of the filter that matches a specific value.
TypeMatchFilter FilterType = "Match"
// TypeRangeFilter is the name of the filter that selects resources within a range.
TypeRangeFilter FilterType = "Range"
)
// FilterType is the type of filter that can be applied to a resource quantity.
type FilterType string
// BooleanFilter is a filter that can be applied to a boolean value.
type BooleanFilter struct {
Data runtime.RawExtension `json:"data"`
}
type NumberFilter struct {
// Name indicates the type of the filter
Name FilterType `json:"name"`
// Filter data
Data runtime.RawExtension `json:"data"`
}
type NumberMatchSelector struct {
// Value is the value to match
Value float64 `json:"value"`
}
type NumberRangeSelector struct {
// Min is the minimum value of the range
Min *float64 `json:"min,omitempty"`
// Max is the maximum value of the range
Max *float64 `json:"max,omitempty"`
}
// StringFilter is a filter that can be applied to a string.
type StringFilter struct {
// Name indicates the type of the filter
Name FilterType `json:"name"`
// Filter data
Data runtime.RawExtension `json:"data"`
}
// StringMatchSelector is a filter that selects strings that match a specific value.
type StringMatchSelector struct {
// Value is the value to match
Value string `json:"value"`
}
// StringRangeSelector is a filter that selects strings within a range.
type StringRangeSelector struct {
// Regex is the regular expression to match
Regex string `json:"regex"`
}
// ResourceQuantityFilter is a filter that can be applied to a resource quantity.
type ResourceQuantityFilter struct {
// Name indicates the type of the filter
Name FilterType `json:"name"`
// Filter data
Data runtime.RawExtension `json:"data"`
}
// ResourceMatchSelector is a filter that selects resources that match a specific value.
type ResourceMatchSelector struct {
// Value is the value to match
Value resource.Quantity `json:"value"`
}
// ResourceRangeSelector is a filter that selects resources within a range.
type ResourceRangeSelector struct {
// Min is the minimum value of the range
Min *resource.Quantity `json:"min,omitempty"`
// Max is the maximum value of the range
Max *resource.Quantity `json:"max,omitempty"`
}
type SelectorName string
var (
ResourceRangeSelectorName = SelectorName("ResourceRangeSelector")
ResourceMatchSelectorName = SelectorName("ResourceMatchSelector")
StringRangeSelectorName = SelectorName("StringRangeSelector")
StringFilterSelectorName = SelectorName("StringFilter")
BooleanFilterSelectorName = SelectorName("BooleanFilter")
NumberRangeSelectorName = SelectorName("NumberRangeFilter")
NumberMatchSelectorName = SelectorName("NumberMatchFilter")
)
type GPUFieldSelector struct {
// Field is the GPU field the filter should evaluate.
Field string `json:"field"`
// Selector is the name of the concrete Selector to enforce for the given data.
//+kubebuilder:validation:Enum=ResourceRangeSelector;ResourceMatchSelector;StringRangeSelector;StringFilter;BooleanFilter;NumberRangeFilter;NumberFilter
Selector SelectorName `json:"filter"`
// Data contains the raw data for the given filter.
Data runtime.RawExtension `json:"data"`
}
// ParseResourceQuantityFilter parses a ResourceQuantityFilter into a FilterType and the corresponding filter data.
// It also provides a set of validation rules for the filter data.
// Particularly for the ResourceRangeSelector, it checks that at least one of min or max is set and that min is less than max if both are set.
func ParseResourceQuantityFilter(rqf *ResourceQuantityFilter) (FilterType, interface{}, error) {
var validationErr error
klog.Infof("Parsing ResourceQuantityFilter %v - Name: %s", rqf, rqf.Name)
switch rqf.Name {
case TypeMatchFilter:
// Unmarshal the data into a ResourceMatchSelector
var rms ResourceMatchSelector
validationErr = json.Unmarshal(rqf.Data.Raw, &rms)
return TypeMatchFilter, rms, validationErr
case TypeRangeFilter:
// Unmarshal the data into a ResourceRangeSelector
var rrs ResourceRangeSelector
validationErr = json.Unmarshal(rqf.Data.Raw, &rrs)
klog.Infof("ResourceRangeSelector: %v", rrs)
// Check that at least one of min or max is set
if rrs.Min == nil && rrs.Max == nil {
klog.Error("at least one of min or max must be set")
validationErr = fmt.Errorf("at least one of min or max must be set")
} else
// If both min and max are set, check that min is less than max
if rrs.Min != nil && rrs.Max != nil {
// Check that the min is less than the max
if rrs.Min != nil && rrs.Max != nil && rrs.Min.Cmp(*rrs.Max) > 0 {
klog.Errorf("min value %s is greater than max value %s", rrs.Min.String(), rrs.Max.String())
validationErr = fmt.Errorf("min value %s is greater than max value %s", rrs.Min.String(), rrs.Max.String())
}
}
return TypeRangeFilter, rrs, validationErr
default:
return "", nil, fmt.Errorf("unknown filter type %s", rqf.Name)
}
}
// ParseStringFilter parses a StringFilter into a FilterType and the corresponding filter data.
// It also provides a set of validation rules for the filter data.
// Particularly for the StringRangeSelector, it checks that regex is set.
func ParseStringFilter(sf *StringFilter) (FilterType, interface{}, error) {
var validationErr error
klog.Infof("Parsing StringFilter %v - Name: %s", sf, sf.Name)
switch sf.Name {
case TypeMatchFilter:
// Unmarshal the data into a StringMatchSelector
var sms StringMatchSelector
validationErr = json.Unmarshal(sf.Data.Raw, &sms)
return TypeMatchFilter, sms, validationErr
case TypeRangeFilter:
// Unmarshal the data into a StringRangeSelector
var srs StringRangeSelector
validationErr = json.Unmarshal(sf.Data.Raw, &srs)
klog.Infof("StringRangeSelector: %v", srs)
// Check that regex is set
if srs.Regex == "" {
klog.Error("regex must be set")
validationErr = fmt.Errorf("regex must be set")
}
// Check that regex is a valid regular expression
if _, err := regexp.Compile(srs.Regex); err != nil {
klog.Errorf("invalid regular expression %s: %v", srs.Regex, err)
validationErr = fmt.Errorf("invalid regular expression %s: %w", srs.Regex, err)
}
return TypeRangeFilter, srs, validationErr
default:
return "", nil, fmt.Errorf("unknown filter type %s", sf.Name)
}
}