/
generic_predicates.go
276 lines (257 loc) · 11.1 KB
/
generic_predicates.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
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
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
/*
Copyright 2020 The Kubernetes Authors.
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 predicates
import (
"strings"
"github.com/go-logr/logr"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/cluster-api/util/annotations"
"sigs.k8s.io/cluster-api/util/labels"
)
// All returns a predicate that returns true only if all given predicates return true.
func All(logger logr.Logger, predicates ...predicate.Funcs) predicate.Funcs {
return predicate.Funcs{
UpdateFunc: func(e event.UpdateEvent) bool {
log := logger.WithValues("predicateAggregation", "All")
for _, p := range predicates {
if !p.UpdateFunc(e) {
log.V(6).Info("One of the provided predicates returned false, blocking further processing")
return false
}
}
log.V(6).Info("All provided predicates returned true, allowing further processing")
return true
},
CreateFunc: func(e event.CreateEvent) bool {
log := logger.WithValues("predicateAggregation", "All")
for _, p := range predicates {
if !p.CreateFunc(e) {
log.V(6).Info("One of the provided predicates returned false, blocking further processing")
return false
}
}
log.V(6).Info("All provided predicates returned true, allowing further processing")
return true
},
DeleteFunc: func(e event.DeleteEvent) bool {
log := logger.WithValues("predicateAggregation", "All")
for _, p := range predicates {
if !p.DeleteFunc(e) {
log.V(6).Info("One of the provided predicates returned false, blocking further processing")
return false
}
}
log.V(6).Info("All provided predicates returned true, allowing further processing")
return true
},
GenericFunc: func(e event.GenericEvent) bool {
log := logger.WithValues("predicateAggregation", "All")
for _, p := range predicates {
if !p.GenericFunc(e) {
log.V(6).Info("One of the provided predicates returned false, blocking further processing")
return false
}
}
log.V(6).Info("All provided predicates returned true, allowing further processing")
return true
},
}
}
// Any returns a predicate that returns true only if any given predicate returns true.
func Any(logger logr.Logger, predicates ...predicate.Funcs) predicate.Funcs {
return predicate.Funcs{
UpdateFunc: func(e event.UpdateEvent) bool {
log := logger.WithValues("predicateAggregation", "Any")
for _, p := range predicates {
if p.UpdateFunc(e) {
log.V(6).Info("One of the provided predicates returned true, allowing further processing")
return true
}
}
log.V(6).Info("All of the provided predicates returned false, blocking further processing")
return false
},
CreateFunc: func(e event.CreateEvent) bool {
log := logger.WithValues("predicateAggregation", "Any")
for _, p := range predicates {
if p.CreateFunc(e) {
log.V(6).Info("One of the provided predicates returned true, allowing further processing")
return true
}
}
log.V(6).Info("All of the provided predicates returned false, blocking further processing")
return false
},
DeleteFunc: func(e event.DeleteEvent) bool {
log := logger.WithValues("predicateAggregation", "Any")
for _, p := range predicates {
if p.DeleteFunc(e) {
log.V(6).Info("One of the provided predicates returned true, allowing further processing")
return true
}
}
log.V(6).Info("All of the provided predicates returned false, blocking further processing")
return false
},
GenericFunc: func(e event.GenericEvent) bool {
log := logger.WithValues("predicateAggregation", "Any")
for _, p := range predicates {
if p.GenericFunc(e) {
log.V(6).Info("One of the provided predicates returned true, allowing further processing")
return true
}
}
log.V(6).Info("All of the provided predicates returned false, blocking further processing")
return false
},
}
}
// ResourceHasFilterLabel returns a predicate that returns true only if the provided resource contains
// a label with the WatchLabel key and the configured label value exactly.
func ResourceHasFilterLabel(logger logr.Logger, labelValue string) predicate.Funcs {
return predicate.Funcs{
UpdateFunc: func(e event.UpdateEvent) bool {
return processIfLabelMatch(logger.WithValues("predicate", "ResourceHasFilterLabel", "eventType", "update"), e.ObjectNew, labelValue)
},
CreateFunc: func(e event.CreateEvent) bool {
return processIfLabelMatch(logger.WithValues("predicate", "ResourceHasFilterLabel", "eventType", "create"), e.Object, labelValue)
},
DeleteFunc: func(e event.DeleteEvent) bool {
return processIfLabelMatch(logger.WithValues("predicate", "ResourceHasFilterLabel", "eventType", "delete"), e.Object, labelValue)
},
GenericFunc: func(e event.GenericEvent) bool {
return processIfLabelMatch(logger.WithValues("predicate", "ResourceHasFilterLabel", "eventType", "generic"), e.Object, labelValue)
},
}
}
// ResourceNotPaused returns a Predicate that returns true only if the provided resource does not contain the
// paused annotation.
// This implements a common requirement for all cluster-api and provider controllers skip reconciliation when the paused
// annotation is present for a resource.
// Example use:
//
// func (r *MyReconciler) SetupWithManager(mgr ctrl.Manager, options controller.Options) error {
// controller, err := ctrl.NewControllerManagedBy(mgr).
// For(&v1.MyType{}).
// WithOptions(options).
// WithEventFilter(util.ResourceNotPaused(r.Log)).
// Build(r)
// return err
// }
func ResourceNotPaused(logger logr.Logger) predicate.Funcs {
return predicate.Funcs{
UpdateFunc: func(e event.UpdateEvent) bool {
return processIfNotPaused(logger.WithValues("predicate", "ResourceNotPaused", "eventType", "update"), e.ObjectNew)
},
CreateFunc: func(e event.CreateEvent) bool {
return processIfNotPaused(logger.WithValues("predicate", "ResourceNotPaused", "eventType", "create"), e.Object)
},
DeleteFunc: func(e event.DeleteEvent) bool {
return processIfNotPaused(logger.WithValues("predicate", "ResourceNotPaused", "eventType", "delete"), e.Object)
},
GenericFunc: func(e event.GenericEvent) bool {
return processIfNotPaused(logger.WithValues("predicate", "ResourceNotPaused", "eventType", "generic"), e.Object)
},
}
}
// ResourceNotPausedAndHasFilterLabel returns a predicate that returns true only if the
// ResourceNotPaused and ResourceHasFilterLabel predicates return true.
func ResourceNotPausedAndHasFilterLabel(logger logr.Logger, labelValue string) predicate.Funcs {
return All(logger, ResourceNotPaused(logger), ResourceHasFilterLabel(logger, labelValue))
}
func processIfNotPaused(logger logr.Logger, obj client.Object) bool {
kind := strings.ToLower(obj.GetObjectKind().GroupVersionKind().Kind)
log := logger.WithValues("namespace", obj.GetNamespace(), kind, obj.GetName())
if annotations.HasPaused(obj) {
log.V(4).Info("Resource is paused, will not attempt to map resource")
return false
}
log.V(6).Info("Resource is not paused, will attempt to map resource")
return true
}
func processIfLabelMatch(logger logr.Logger, obj client.Object, labelValue string) bool {
// Return early if no labelValue was set.
if labelValue == "" {
return true
}
kind := strings.ToLower(obj.GetObjectKind().GroupVersionKind().Kind)
log := logger.WithValues("namespace", obj.GetNamespace(), kind, obj.GetName())
if labels.HasWatchLabel(obj, labelValue) {
log.V(6).Info("Resource matches label, will attempt to map resource")
return true
}
log.V(4).Info("Resource does not match label, will not attempt to map resource")
return false
}
// ResourceIsNotExternallyManaged returns a predicate that returns true only if the resource does not contain
// the externally managed annotation.
// This implements a requirement for InfraCluster providers to be able to ignore externally managed
// cluster infrastructure.
func ResourceIsNotExternallyManaged(logger logr.Logger) predicate.Funcs {
return predicate.Funcs{
UpdateFunc: func(e event.UpdateEvent) bool {
return processIfNotExternallyManaged(logger.WithValues("predicate", "ResourceIsNotExternallyManaged", "eventType", "update"), e.ObjectNew)
},
CreateFunc: func(e event.CreateEvent) bool {
return processIfNotExternallyManaged(logger.WithValues("predicate", "ResourceIsNotExternallyManaged", "eventType", "create"), e.Object)
},
DeleteFunc: func(e event.DeleteEvent) bool {
return processIfNotExternallyManaged(logger.WithValues("predicate", "ResourceIsNotExternallyManaged", "eventType", "delete"), e.Object)
},
GenericFunc: func(e event.GenericEvent) bool {
return processIfNotExternallyManaged(logger.WithValues("predicate", "ResourceIsNotExternallyManaged", "eventType", "generic"), e.Object)
},
}
}
func processIfNotExternallyManaged(logger logr.Logger, obj client.Object) bool {
kind := strings.ToLower(obj.GetObjectKind().GroupVersionKind().Kind)
log := logger.WithValues("namespace", obj.GetNamespace(), kind, obj.GetName())
if annotations.IsExternallyManaged(obj) {
log.V(4).Info("Resource is externally managed, will not attempt to map resource")
return false
}
log.V(6).Info("Resource is managed, will attempt to map resource")
return true
}
// ResourceIsTopologyOwned returns a predicate that returns true only if the resource has
// the `topology.cluster.x-k8s.io/owned` label.
func ResourceIsTopologyOwned(logger logr.Logger) predicate.Funcs {
return predicate.Funcs{
UpdateFunc: func(e event.UpdateEvent) bool {
return processIfTopologyOwned(logger.WithValues("predicate", "ResourceIsTopologyOwned", "eventType", "update"), e.ObjectNew)
},
CreateFunc: func(e event.CreateEvent) bool {
return processIfTopologyOwned(logger.WithValues("predicate", "ResourceIsTopologyOwned", "eventType", "create"), e.Object)
},
DeleteFunc: func(e event.DeleteEvent) bool {
return processIfTopologyOwned(logger.WithValues("predicate", "ResourceIsTopologyOwned", "eventType", "delete"), e.Object)
},
GenericFunc: func(e event.GenericEvent) bool {
return processIfTopologyOwned(logger.WithValues("predicate", "ResourceIsTopologyOwned", "eventType", "generic"), e.Object)
},
}
}
func processIfTopologyOwned(logger logr.Logger, obj client.Object) bool {
kind := strings.ToLower(obj.GetObjectKind().GroupVersionKind().Kind)
log := logger.WithValues("namespace", obj.GetNamespace(), kind, obj.GetName())
if labels.IsTopologyOwned(obj) {
log.V(6).Info("Resource is topology owned, will attempt to map resource")
return true
}
// We intentionally log this line only on level 6, because it will be very frequently
// logged for MachineDeployments and MachineSets not owned by a topology.
log.V(6).Info("Resource is not topology owned, will not attempt to map resource")
return false
}