Skip to content

Commit 9bfd3f3

Browse files
authored
Merge pull request #6619 from killianmuldoon/runtimeSDK/namespace-selector
🌱 Runtime sdk/namespace selector
2 parents 911ad53 + b3e0b58 commit 9bfd3f3

File tree

8 files changed

+274
-21
lines changed

8 files changed

+274
-21
lines changed

config/crd/bases/runtime.cluster.x-k8s.io_extensionconfigs.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,8 @@ spec:
9595
type: string
9696
type: object
9797
namespaceSelector:
98-
description: NamespaceSelector decides whether to run the webhook
99-
on an object based on whether the namespace for that object matches
98+
description: NamespaceSelector decides whether to call the hook for
99+
an object based on whether the namespace for that object matches
100100
the selector. Default to the empty LabelSelector, which matches
101101
everything.
102102
properties:

exp/runtime/api/v1alpha1/extensionconfig_types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ type ExtensionConfigSpec struct {
2929
// ClientConfig defines how to communicate with ExtensionHandlers.
3030
ClientConfig ClientConfig `json:"clientConfig"`
3131

32-
// NamespaceSelector decides whether to run the webhook on an object based
32+
// NamespaceSelector decides whether to call the hook for an object based
3333
// on whether the namespace for that object matches the selector.
3434
// Default to the empty LabelSelector, which matches everything.
3535
// +optional

exp/runtime/internal/controllers/extensionconfig_controller.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
8282
if err != nil {
8383
if apierrors.IsNotFound(err) {
8484
// ExtensionConfig not found. Remove from registry.
85-
// First need to add Namespace/Name to empty extensionConfig object.
85+
// First need to add Namespace/Name to empty ExtensionConfig object.
8686
extensionConfig.Name = req.Name
8787
extensionConfig.Namespace = req.Namespace
8888
return r.reconcileDelete(ctx, extensionConfig)
@@ -117,7 +117,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
117117
return ctrl.Result{}, kerrors.NewAggregate(errs)
118118
}
119119

120-
// Register the extensionConfig if it was found and patched without error.
120+
// Register the ExtensionConfig if it was found and patched without error.
121121
if err = r.RuntimeClient.Register(discoveredExtensionConfig); err != nil {
122122
return ctrl.Result{}, errors.Wrapf(err, "failed to register ExtensionConfig %s/%s", extensionConfig.Namespace, extensionConfig.Name)
123123
}

internal/runtime/client/client.go

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,22 @@ import (
3030
"time"
3131

3232
"github.com/pkg/errors"
33+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
34+
"k8s.io/apimachinery/pkg/labels"
3335
"k8s.io/apimachinery/pkg/runtime"
3436
"k8s.io/apimachinery/pkg/runtime/schema"
3537
kerrors "k8s.io/apimachinery/pkg/util/errors"
3638
utilnet "k8s.io/apimachinery/pkg/util/net"
3739
"k8s.io/apimachinery/pkg/util/validation"
3840
"k8s.io/client-go/transport"
3941
"k8s.io/utils/pointer"
42+
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
4043

4144
runtimev1 "sigs.k8s.io/cluster-api/exp/runtime/api/v1alpha1"
4245
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
4346
runtimecatalog "sigs.k8s.io/cluster-api/internal/runtime/catalog"
4447
runtimeregistry "sigs.k8s.io/cluster-api/internal/runtime/registry"
48+
"sigs.k8s.io/cluster-api/util"
4549
)
4650

4751
type errCallingExtensionHandler error
@@ -52,13 +56,15 @@ const defaultDiscoveryTimeout = 10 * time.Second
5256
type Options struct {
5357
Catalog *runtimecatalog.Catalog
5458
Registry runtimeregistry.ExtensionRegistry
59+
Client ctrlclient.Client
5560
}
5661

5762
// New returns a new Client.
5863
func New(options Options) Client {
5964
return &client{
6065
catalog: options.Catalog,
6166
registry: options.Registry,
67+
Client: options.Client,
6268
}
6369
}
6470

@@ -83,17 +89,18 @@ type Client interface {
8389
Unregister(extensionConfig *runtimev1.ExtensionConfig) error
8490

8591
// 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
8793

8894
// 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
9096
}
9197

9298
var _ Client = &client{}
9399

94100
type client struct {
95101
catalog *runtimecatalog.Catalog
96102
registry runtimeregistry.ExtensionRegistry
103+
Client ctrlclient.Client
97104
}
98105

99106
func (c *client) WarmUp(extensionConfigList *runtimev1.ExtensionConfigList) error {
@@ -176,7 +183,7 @@ func (c *client) Unregister(extensionConfig *runtimev1.ExtensionConfig) error {
176183
// This ensures we don't end up waiting for timeout from multiple unreachable Extensions.
177184
// See CallExtension for more details on when an ExtensionHandler returns an error.
178185
// 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 {
180187
gvh, err := c.catalog.GroupVersionHook(hook)
181188
if err != nil {
182189
return errors.Wrap(err, "failed to compute GroupVersionHook")
@@ -204,7 +211,16 @@ func (c *client) CallAllExtensions(ctx context.Context, hook runtimecatalog.Hook
204211
}
205212
tmpResponse := responseObject.(runtimehooksv1.ResponseObject)
206213

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)
208224
// If one of the extension handlers fails lets short-circuit here and return early.
209225
if err != nil {
210226
return errors.Wrapf(err, "ExtensionHandler %s failed", registration.Name)
@@ -268,7 +284,7 @@ func lowestNonZeroRetryAfterSeconds(i, j int32) int32 {
268284
// Nb. FailurePolicy does not affect the following kinds of errors:
269285
// - Internal errors. Examples: hooks is incompatible with ExtensionHandler, ExtensionHandler information is missing.
270286
// - 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 {
272288
hookGVH, err := c.catalog.GroupVersionHook(hook)
273289
if err != nil {
274290
return errors.Wrap(err, "failed to compute GroupVersionHook")
@@ -290,6 +306,16 @@ func (c *client) CallExtension(ctx context.Context, hook runtimecatalog.Hook, na
290306
return errors.Errorf("ExtensionHandler %q does not match group %s, hook %s", name, hookGVH.Group, hookGVH.Hook)
291307
}
292308

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+
293319
var timeoutDuration time.Duration
294320
if registration.TimeoutSeconds != nil {
295321
timeoutDuration = time.Duration(*registration.TimeoutSeconds) * time.Second
@@ -549,3 +575,22 @@ func defaultDiscoveryResponse(discovery *runtimehooksv1.DiscoveryResponse) *runt
549575
}
550576
return discovery
551577
}
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

Comments
 (0)