2020-10-21 05:49:41 +00:00
|
|
|
/*
|
|
|
|
Copyright 2018 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 handler
|
|
|
|
|
|
|
|
import (
|
2023-06-01 17:01:19 +00:00
|
|
|
"context"
|
2020-10-21 05:49:41 +00:00
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"k8s.io/apimachinery/pkg/api/meta"
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
|
|
"k8s.io/apimachinery/pkg/types"
|
|
|
|
"k8s.io/client-go/util/workqueue"
|
2023-06-01 17:01:19 +00:00
|
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
2020-10-21 05:49:41 +00:00
|
|
|
"sigs.k8s.io/controller-runtime/pkg/event"
|
|
|
|
logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
|
|
|
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
|
|
|
)
|
|
|
|
|
2024-05-13 20:57:03 +00:00
|
|
|
var _ EventHandler = &enqueueRequestForOwner[client.Object]{}
|
2020-10-21 05:49:41 +00:00
|
|
|
|
2023-06-01 17:01:19 +00:00
|
|
|
var log = logf.RuntimeLog.WithName("eventhandler").WithName("enqueueRequestForOwner")
|
|
|
|
|
|
|
|
// OwnerOption modifies an EnqueueRequestForOwner EventHandler.
|
2024-05-13 20:57:03 +00:00
|
|
|
type OwnerOption func(e enqueueRequestForOwnerInterface)
|
2020-10-21 05:49:41 +00:00
|
|
|
|
|
|
|
// EnqueueRequestForOwner enqueues Requests for the Owners of an object. E.g. the object that created
|
|
|
|
// the object that was the source of the Event.
|
|
|
|
//
|
|
|
|
// If a ReplicaSet creates Pods, users may reconcile the ReplicaSet in response to Pod Events using:
|
|
|
|
//
|
|
|
|
// - a source.Kind Source with Type of Pod.
|
|
|
|
//
|
2023-06-01 17:01:19 +00:00
|
|
|
// - a handler.enqueueRequestForOwner EventHandler with an OwnerType of ReplicaSet and OnlyControllerOwner set to true.
|
|
|
|
func EnqueueRequestForOwner(scheme *runtime.Scheme, mapper meta.RESTMapper, ownerType client.Object, opts ...OwnerOption) EventHandler {
|
2024-05-13 20:57:03 +00:00
|
|
|
return TypedEnqueueRequestForOwner[client.Object](scheme, mapper, ownerType, opts...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TypedEnqueueRequestForOwner enqueues Requests for the Owners of an object. E.g. the object that created
|
|
|
|
// the object that was the source of the Event.
|
|
|
|
//
|
|
|
|
// If a ReplicaSet creates Pods, users may reconcile the ReplicaSet in response to Pod Events using:
|
|
|
|
//
|
|
|
|
// - a source.Kind Source with Type of Pod.
|
|
|
|
//
|
|
|
|
// - a handler.typedEnqueueRequestForOwner EventHandler with an OwnerType of ReplicaSet and OnlyControllerOwner set to true.
|
|
|
|
//
|
|
|
|
// TypedEnqueueRequestForOwner is experimental and subject to future change.
|
|
|
|
func TypedEnqueueRequestForOwner[T client.Object](scheme *runtime.Scheme, mapper meta.RESTMapper, ownerType client.Object, opts ...OwnerOption) TypedEventHandler[T] {
|
|
|
|
e := &enqueueRequestForOwner[T]{
|
2023-06-01 17:01:19 +00:00
|
|
|
ownerType: ownerType,
|
|
|
|
mapper: mapper,
|
|
|
|
}
|
|
|
|
if err := e.parseOwnerTypeGroupKind(scheme); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
for _, opt := range opts {
|
|
|
|
opt(e)
|
|
|
|
}
|
|
|
|
return e
|
|
|
|
}
|
|
|
|
|
|
|
|
// OnlyControllerOwner if provided will only look at the first OwnerReference with Controller: true.
|
|
|
|
func OnlyControllerOwner() OwnerOption {
|
2024-05-13 20:57:03 +00:00
|
|
|
return func(e enqueueRequestForOwnerInterface) {
|
|
|
|
e.setIsController(true)
|
2023-06-01 17:01:19 +00:00
|
|
|
}
|
|
|
|
}
|
2020-10-21 05:49:41 +00:00
|
|
|
|
2024-05-13 20:57:03 +00:00
|
|
|
type enqueueRequestForOwnerInterface interface {
|
|
|
|
setIsController(bool)
|
|
|
|
}
|
|
|
|
|
|
|
|
type enqueueRequestForOwner[T client.Object] struct {
|
2023-06-01 17:01:19 +00:00
|
|
|
// ownerType is the type of the Owner object to look for in OwnerReferences. Only Group and Kind are compared.
|
|
|
|
ownerType runtime.Object
|
|
|
|
|
|
|
|
// isController if set will only look at the first OwnerReference with Controller: true.
|
|
|
|
isController bool
|
2020-10-21 05:49:41 +00:00
|
|
|
|
|
|
|
// groupKind is the cached Group and Kind from OwnerType
|
|
|
|
groupKind schema.GroupKind
|
|
|
|
|
|
|
|
// mapper maps GroupVersionKinds to Resources
|
|
|
|
mapper meta.RESTMapper
|
|
|
|
}
|
|
|
|
|
2024-05-13 20:57:03 +00:00
|
|
|
func (e *enqueueRequestForOwner[T]) setIsController(isController bool) {
|
|
|
|
e.isController = isController
|
|
|
|
}
|
|
|
|
|
2021-06-25 05:02:01 +00:00
|
|
|
// Create implements EventHandler.
|
2024-05-13 20:57:03 +00:00
|
|
|
func (e *enqueueRequestForOwner[T]) Create(ctx context.Context, evt event.TypedCreateEvent[T], q workqueue.RateLimitingInterface) {
|
2021-06-25 05:02:01 +00:00
|
|
|
reqs := map[reconcile.Request]empty{}
|
|
|
|
e.getOwnerReconcileRequest(evt.Object, reqs)
|
|
|
|
for req := range reqs {
|
2020-10-21 05:49:41 +00:00
|
|
|
q.Add(req)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-25 05:02:01 +00:00
|
|
|
// Update implements EventHandler.
|
2024-05-13 20:57:03 +00:00
|
|
|
func (e *enqueueRequestForOwner[T]) Update(ctx context.Context, evt event.TypedUpdateEvent[T], q workqueue.RateLimitingInterface) {
|
2021-06-25 05:02:01 +00:00
|
|
|
reqs := map[reconcile.Request]empty{}
|
|
|
|
e.getOwnerReconcileRequest(evt.ObjectOld, reqs)
|
|
|
|
e.getOwnerReconcileRequest(evt.ObjectNew, reqs)
|
|
|
|
for req := range reqs {
|
2020-10-21 05:49:41 +00:00
|
|
|
q.Add(req)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-25 05:02:01 +00:00
|
|
|
// Delete implements EventHandler.
|
2024-05-13 20:57:03 +00:00
|
|
|
func (e *enqueueRequestForOwner[T]) Delete(ctx context.Context, evt event.TypedDeleteEvent[T], q workqueue.RateLimitingInterface) {
|
2021-06-25 05:02:01 +00:00
|
|
|
reqs := map[reconcile.Request]empty{}
|
|
|
|
e.getOwnerReconcileRequest(evt.Object, reqs)
|
|
|
|
for req := range reqs {
|
2020-10-21 05:49:41 +00:00
|
|
|
q.Add(req)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-25 05:02:01 +00:00
|
|
|
// Generic implements EventHandler.
|
2024-05-13 20:57:03 +00:00
|
|
|
func (e *enqueueRequestForOwner[T]) Generic(ctx context.Context, evt event.TypedGenericEvent[T], q workqueue.RateLimitingInterface) {
|
2021-06-25 05:02:01 +00:00
|
|
|
reqs := map[reconcile.Request]empty{}
|
|
|
|
e.getOwnerReconcileRequest(evt.Object, reqs)
|
|
|
|
for req := range reqs {
|
2020-10-21 05:49:41 +00:00
|
|
|
q.Add(req)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// parseOwnerTypeGroupKind parses the OwnerType into a Group and Kind and caches the result. Returns false
|
|
|
|
// if the OwnerType could not be parsed using the scheme.
|
2024-05-13 20:57:03 +00:00
|
|
|
func (e *enqueueRequestForOwner[T]) parseOwnerTypeGroupKind(scheme *runtime.Scheme) error {
|
2020-10-21 05:49:41 +00:00
|
|
|
// Get the kinds of the type
|
2023-06-01 17:01:19 +00:00
|
|
|
kinds, _, err := scheme.ObjectKinds(e.ownerType)
|
2020-10-21 05:49:41 +00:00
|
|
|
if err != nil {
|
2023-06-01 17:01:19 +00:00
|
|
|
log.Error(err, "Could not get ObjectKinds for OwnerType", "owner type", fmt.Sprintf("%T", e.ownerType))
|
2020-10-21 05:49:41 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
// Expect only 1 kind. If there is more than one kind this is probably an edge case such as ListOptions.
|
|
|
|
if len(kinds) != 1 {
|
2023-06-01 17:01:19 +00:00
|
|
|
err := fmt.Errorf("expected exactly 1 kind for OwnerType %T, but found %s kinds", e.ownerType, kinds)
|
|
|
|
log.Error(nil, "expected exactly 1 kind for OwnerType", "owner type", fmt.Sprintf("%T", e.ownerType), "kinds", kinds)
|
2020-10-21 05:49:41 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
// Cache the Group and Kind for the OwnerType
|
|
|
|
e.groupKind = schema.GroupKind{Group: kinds[0].Group, Kind: kinds[0].Kind}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-06-25 05:02:01 +00:00
|
|
|
// getOwnerReconcileRequest looks at object and builds a map of reconcile.Request to reconcile
|
2020-10-21 05:49:41 +00:00
|
|
|
// owners of object that match e.OwnerType.
|
2024-05-13 20:57:03 +00:00
|
|
|
func (e *enqueueRequestForOwner[T]) getOwnerReconcileRequest(object metav1.Object, result map[reconcile.Request]empty) {
|
2020-10-21 05:49:41 +00:00
|
|
|
// Iterate through the OwnerReferences looking for a match on Group and Kind against what was requested
|
|
|
|
// by the user
|
|
|
|
for _, ref := range e.getOwnersReferences(object) {
|
|
|
|
// Parse the Group out of the OwnerReference to compare it to what was parsed out of the requested OwnerType
|
|
|
|
refGV, err := schema.ParseGroupVersion(ref.APIVersion)
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err, "Could not parse OwnerReference APIVersion",
|
|
|
|
"api version", ref.APIVersion)
|
2021-06-25 05:02:01 +00:00
|
|
|
return
|
2020-10-21 05:49:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Compare the OwnerReference Group and Kind against the OwnerType Group and Kind specified by the user.
|
|
|
|
// If the two match, create a Request for the objected referred to by
|
|
|
|
// the OwnerReference. Use the Name from the OwnerReference and the Namespace from the
|
|
|
|
// object in the event.
|
|
|
|
if ref.Kind == e.groupKind.Kind && refGV.Group == e.groupKind.Group {
|
|
|
|
// Match found - add a Request for the object referred to in the OwnerReference
|
|
|
|
request := reconcile.Request{NamespacedName: types.NamespacedName{
|
|
|
|
Name: ref.Name,
|
|
|
|
}}
|
|
|
|
|
2023-06-01 17:01:19 +00:00
|
|
|
// if owner is not namespaced then we should not set the namespace
|
2020-10-21 05:49:41 +00:00
|
|
|
mapping, err := e.mapper.RESTMapping(e.groupKind, refGV.Version)
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err, "Could not retrieve rest mapping", "kind", e.groupKind)
|
2021-06-25 05:02:01 +00:00
|
|
|
return
|
2020-10-21 05:49:41 +00:00
|
|
|
}
|
|
|
|
if mapping.Scope.Name() != meta.RESTScopeNameRoot {
|
|
|
|
request.Namespace = object.GetNamespace()
|
|
|
|
}
|
|
|
|
|
2021-06-25 05:02:01 +00:00
|
|
|
result[request] = empty{}
|
2020-10-21 05:49:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-01 17:01:19 +00:00
|
|
|
// getOwnersReferences returns the OwnerReferences for an object as specified by the enqueueRequestForOwner
|
2020-10-21 05:49:41 +00:00
|
|
|
// - if IsController is true: only take the Controller OwnerReference (if found)
|
2021-06-25 05:02:01 +00:00
|
|
|
// - if IsController is false: take all OwnerReferences.
|
2024-05-13 20:57:03 +00:00
|
|
|
func (e *enqueueRequestForOwner[T]) getOwnersReferences(object metav1.Object) []metav1.OwnerReference {
|
2020-10-21 05:49:41 +00:00
|
|
|
if object == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// If not filtered as Controller only, then use all the OwnerReferences
|
2023-06-01 17:01:19 +00:00
|
|
|
if !e.isController {
|
2020-10-21 05:49:41 +00:00
|
|
|
return object.GetOwnerReferences()
|
|
|
|
}
|
|
|
|
// If filtered to a Controller, only take the Controller OwnerReference
|
|
|
|
if ownerRef := metav1.GetControllerOf(object); ownerRef != nil {
|
|
|
|
return []metav1.OwnerReference{*ownerRef}
|
|
|
|
}
|
|
|
|
// No Controller OwnerReference found
|
|
|
|
return nil
|
|
|
|
}
|