@@ -30,18 +30,22 @@ import (
30
30
"time"
31
31
32
32
"github.com/pkg/errors"
33
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
34
+ "k8s.io/apimachinery/pkg/labels"
33
35
"k8s.io/apimachinery/pkg/runtime"
34
36
"k8s.io/apimachinery/pkg/runtime/schema"
35
37
kerrors "k8s.io/apimachinery/pkg/util/errors"
36
38
utilnet "k8s.io/apimachinery/pkg/util/net"
37
39
"k8s.io/apimachinery/pkg/util/validation"
38
40
"k8s.io/client-go/transport"
39
41
"k8s.io/utils/pointer"
42
+ ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
40
43
41
44
runtimev1 "sigs.k8s.io/cluster-api/exp/runtime/api/v1alpha1"
42
45
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
43
46
runtimecatalog "sigs.k8s.io/cluster-api/internal/runtime/catalog"
44
47
runtimeregistry "sigs.k8s.io/cluster-api/internal/runtime/registry"
48
+ "sigs.k8s.io/cluster-api/util"
45
49
)
46
50
47
51
type errCallingExtensionHandler error
@@ -52,13 +56,15 @@ const defaultDiscoveryTimeout = 10 * time.Second
52
56
type Options struct {
53
57
Catalog * runtimecatalog.Catalog
54
58
Registry runtimeregistry.ExtensionRegistry
59
+ Client ctrlclient.Client
55
60
}
56
61
57
62
// New returns a new Client.
58
63
func New (options Options ) Client {
59
64
return & client {
60
65
catalog : options .Catalog ,
61
66
registry : options .Registry ,
67
+ Client : options .Client ,
62
68
}
63
69
}
64
70
@@ -83,17 +89,18 @@ type Client interface {
83
89
Unregister (extensionConfig * runtimev1.ExtensionConfig ) error
84
90
85
91
// CallAllExtensions calls all the ExtensionHandler registered for the hook.
86
- CallAllExtensions (ctx context.Context , hook runtimecatalog.Hook , request runtime.Object , response runtimehooksv1.ResponseObject ) error
92
+ CallAllExtensions (ctx context.Context , hook runtimecatalog.Hook , forObject metav1. Object , request runtime.Object , response runtimehooksv1.ResponseObject ) error
87
93
88
94
// CallExtension calls only the ExtensionHandler with the given name.
89
- CallExtension (ctx context.Context , hook runtimecatalog.Hook , name string , request runtime.Object , response runtimehooksv1.ResponseObject ) error
95
+ CallExtension (ctx context.Context , hook runtimecatalog.Hook , forObject metav1. Object , name string , request runtime.Object , response runtimehooksv1.ResponseObject ) error
90
96
}
91
97
92
98
var _ Client = & client {}
93
99
94
100
type client struct {
95
101
catalog * runtimecatalog.Catalog
96
102
registry runtimeregistry.ExtensionRegistry
103
+ Client ctrlclient.Client
97
104
}
98
105
99
106
func (c * client ) WarmUp (extensionConfigList * runtimev1.ExtensionConfigList ) error {
@@ -176,7 +183,7 @@ func (c *client) Unregister(extensionConfig *runtimev1.ExtensionConfig) error {
176
183
// This ensures we don't end up waiting for timeout from multiple unreachable Extensions.
177
184
// See CallExtension for more details on when an ExtensionHandler returns an error.
178
185
// The aggregate result of the ExtensionHandlers is updated into the response object passed to the function.
179
- func (c * client ) CallAllExtensions (ctx context.Context , hook runtimecatalog.Hook , request runtime.Object , response runtimehooksv1.ResponseObject ) error {
186
+ func (c * client ) CallAllExtensions (ctx context.Context , hook runtimecatalog.Hook , forObject metav1. Object , request runtime.Object , response runtimehooksv1.ResponseObject ) error {
180
187
gvh , err := c .catalog .GroupVersionHook (hook )
181
188
if err != nil {
182
189
return errors .Wrap (err , "failed to compute GroupVersionHook" )
@@ -204,7 +211,16 @@ func (c *client) CallAllExtensions(ctx context.Context, hook runtimecatalog.Hook
204
211
}
205
212
tmpResponse := responseObject .(runtimehooksv1.ResponseObject )
206
213
207
- err = c .CallExtension (ctx , hook , registration .Name , request , tmpResponse )
214
+ // Compute whether the object the call is being made for matches the namespaceSelector
215
+ namespaceMatches , err := c .matchNamespace (ctx , registration .NamespaceSelector , forObject .GetNamespace ())
216
+ if err != nil {
217
+ return errors .Errorf ("ExtensionHandler %q namespaceSelector could not be resolved" , registration .Name )
218
+ }
219
+ // If the object namespace isn't matched by the registration NamespaceSelector skip the call.
220
+ if ! namespaceMatches {
221
+ continue
222
+ }
223
+ err = c .CallExtension (ctx , hook , forObject , registration .Name , request , tmpResponse )
208
224
// If one of the extension handlers fails lets short-circuit here and return early.
209
225
if err != nil {
210
226
return errors .Wrapf (err , "ExtensionHandler %s failed" , registration .Name )
@@ -268,7 +284,7 @@ func lowestNonZeroRetryAfterSeconds(i, j int32) int32 {
268
284
// Nb. FailurePolicy does not affect the following kinds of errors:
269
285
// - Internal errors. Examples: hooks is incompatible with ExtensionHandler, ExtensionHandler information is missing.
270
286
// - Error when ExtensionHandler returns a response with `Status` set to `Failure`.
271
- func (c * client ) CallExtension (ctx context.Context , hook runtimecatalog.Hook , name string , request runtime.Object , response runtimehooksv1.ResponseObject ) error {
287
+ func (c * client ) CallExtension (ctx context.Context , hook runtimecatalog.Hook , forObject metav1. Object , name string , request runtime.Object , response runtimehooksv1.ResponseObject ) error {
272
288
hookGVH , err := c .catalog .GroupVersionHook (hook )
273
289
if err != nil {
274
290
return errors .Wrap (err , "failed to compute GroupVersionHook" )
@@ -290,6 +306,16 @@ func (c *client) CallExtension(ctx context.Context, hook runtimecatalog.Hook, na
290
306
return errors .Errorf ("ExtensionHandler %q does not match group %s, hook %s" , name , hookGVH .Group , hookGVH .Hook )
291
307
}
292
308
309
+ // Compute whether the object the call is being made for matches the namespaceSelector
310
+ namespaceMatches , err := c .matchNamespace (ctx , registration .NamespaceSelector , forObject .GetNamespace ())
311
+ if err != nil {
312
+ return errors .Errorf ("ExtensionHandler %q namespaceSelector could not be resolved" , name )
313
+ }
314
+ // If the object namespace isn't matched by the registration NamespaceSelector return an error.
315
+ if ! namespaceMatches {
316
+ return errors .Errorf ("ExtensionHandler %q namespaceSelector did not match object %s" , name , util .ObjectKey (forObject ))
317
+ }
318
+
293
319
var timeoutDuration time.Duration
294
320
if registration .TimeoutSeconds != nil {
295
321
timeoutDuration = time .Duration (* registration .TimeoutSeconds ) * time .Second
@@ -549,3 +575,22 @@ func defaultDiscoveryResponse(discovery *runtimehooksv1.DiscoveryResponse) *runt
549
575
}
550
576
return discovery
551
577
}
578
+
579
+ // matchNamespace returns true if the passed namespace matches the selector. It returns an error if the namespace does
580
+ // not exist in the API server.
581
+ func (c * client ) matchNamespace (ctx context.Context , selector labels.Selector , namespace string ) (bool , error ) {
582
+ // return early if the selector is empty.
583
+ if selector .Empty () {
584
+ return true , nil
585
+ }
586
+ ns := & metav1.PartialObjectMetadata {}
587
+ ns .SetGroupVersionKind (schema.GroupVersionKind {
588
+ Group : "" ,
589
+ Version : "v1" ,
590
+ Kind : "Namespace" ,
591
+ })
592
+ if err := c .Client .Get (ctx , ctrlclient.ObjectKey {Name : namespace }, ns ); err != nil {
593
+ return false , errors .Wrapf (err , "failed to find namespace %s for extension namespaceSelector" , namespace )
594
+ }
595
+ return selector .Matches (labels .Set (ns .GetLabels ())), nil
596
+ }
0 commit comments