k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/controller/garbagecollector/graph_builder.go (about)

     1  /*
     2  Copyright 2016 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package garbagecollector
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"reflect"
    23  	"sync"
    24  	"time"
    25  
    26  	"k8s.io/klog/v2"
    27  
    28  	v1 "k8s.io/api/core/v1"
    29  	eventv1 "k8s.io/api/events/v1"
    30  	"k8s.io/apimachinery/pkg/api/meta"
    31  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    32  	"k8s.io/apimachinery/pkg/runtime/schema"
    33  	"k8s.io/apimachinery/pkg/types"
    34  	utilerrors "k8s.io/apimachinery/pkg/util/errors"
    35  	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
    36  	"k8s.io/apimachinery/pkg/util/sets"
    37  	"k8s.io/apimachinery/pkg/util/wait"
    38  	"k8s.io/client-go/metadata"
    39  	"k8s.io/client-go/tools/cache"
    40  	"k8s.io/client-go/tools/record"
    41  	"k8s.io/client-go/util/workqueue"
    42  	"k8s.io/controller-manager/pkg/informerfactory"
    43  	"k8s.io/kubernetes/pkg/controller/apis/config/scheme"
    44  	"k8s.io/kubernetes/pkg/controller/garbagecollector/metaonly"
    45  )
    46  
    47  type eventType int
    48  
    49  func (e eventType) String() string {
    50  	switch e {
    51  	case addEvent:
    52  		return "add"
    53  	case updateEvent:
    54  		return "update"
    55  	case deleteEvent:
    56  		return "delete"
    57  	default:
    58  		return fmt.Sprintf("unknown(%d)", int(e))
    59  	}
    60  }
    61  
    62  const (
    63  	addEvent eventType = iota
    64  	updateEvent
    65  	deleteEvent
    66  )
    67  
    68  type event struct {
    69  	// virtual indicates this event did not come from an informer, but was constructed artificially
    70  	virtual   bool
    71  	eventType eventType
    72  	obj       interface{}
    73  	// the update event comes with an old object, but it's not used by the garbage collector.
    74  	oldObj interface{}
    75  	gvk    schema.GroupVersionKind
    76  }
    77  
    78  // GraphBuilder processes events supplied by the informers, updates uidToNode,
    79  // a graph that caches the dependencies as we know, and enqueues
    80  // items to the attemptToDelete and attemptToOrphan.
    81  type GraphBuilder struct {
    82  	restMapper meta.RESTMapper
    83  
    84  	// each monitor list/watches a resource, the results are funneled to the
    85  	// dependencyGraphBuilder
    86  	monitors    monitors
    87  	monitorLock sync.RWMutex
    88  	// informersStarted is closed after after all of the controllers have been initialized and are running.
    89  	// After that it is safe to start them here, before that it is not.
    90  	informersStarted <-chan struct{}
    91  
    92  	// stopCh drives shutdown. When a receive from it unblocks, monitors will shut down.
    93  	// This channel is also protected by monitorLock.
    94  	stopCh <-chan struct{}
    95  
    96  	// running tracks whether Run() has been called.
    97  	// it is protected by monitorLock.
    98  	running bool
    99  
   100  	eventRecorder    record.EventRecorder
   101  	eventBroadcaster record.EventBroadcaster
   102  
   103  	metadataClient metadata.Interface
   104  	// monitors are the producer of the graphChanges queue, graphBuilder alters
   105  	// the in-memory graph according to the changes.
   106  	graphChanges workqueue.TypedRateLimitingInterface[*event]
   107  	// uidToNode doesn't require a lock to protect, because only the
   108  	// single-threaded GraphBuilder.processGraphChanges() reads/writes it.
   109  	uidToNode *concurrentUIDToNode
   110  	// GraphBuilder is the producer of attemptToDelete and attemptToOrphan, GC is the consumer.
   111  	attemptToDelete workqueue.TypedRateLimitingInterface[*node]
   112  	attemptToOrphan workqueue.TypedRateLimitingInterface[*node]
   113  	// GraphBuilder and GC share the absentOwnerCache. Objects that are known to
   114  	// be non-existent are added to the cached.
   115  	absentOwnerCache *ReferenceCache
   116  	sharedInformers  informerfactory.InformerFactory
   117  	ignoredResources map[schema.GroupResource]struct{}
   118  }
   119  
   120  // monitor runs a Controller with a local stop channel.
   121  type monitor struct {
   122  	controller cache.Controller
   123  	store      cache.Store
   124  
   125  	// stopCh stops Controller. If stopCh is nil, the monitor is considered to be
   126  	// not yet started.
   127  	stopCh chan struct{}
   128  }
   129  
   130  // Run is intended to be called in a goroutine. Multiple calls of this is an
   131  // error.
   132  func (m *monitor) Run() {
   133  	m.controller.Run(m.stopCh)
   134  }
   135  
   136  type monitors map[schema.GroupVersionResource]*monitor
   137  
   138  func NewDependencyGraphBuilder(
   139  	ctx context.Context,
   140  	metadataClient metadata.Interface,
   141  	mapper meta.ResettableRESTMapper,
   142  	ignoredResources map[schema.GroupResource]struct{},
   143  	sharedInformers informerfactory.InformerFactory,
   144  	informersStarted <-chan struct{},
   145  ) *GraphBuilder {
   146  	eventBroadcaster := record.NewBroadcaster(record.WithContext(ctx))
   147  
   148  	attemptToDelete := workqueue.NewTypedRateLimitingQueueWithConfig(
   149  		workqueue.DefaultTypedControllerRateLimiter[*node](),
   150  		workqueue.TypedRateLimitingQueueConfig[*node]{
   151  			Name: "garbage_collector_attempt_to_delete",
   152  		},
   153  	)
   154  	attemptToOrphan := workqueue.NewTypedRateLimitingQueueWithConfig(
   155  		workqueue.DefaultTypedControllerRateLimiter[*node](),
   156  		workqueue.TypedRateLimitingQueueConfig[*node]{
   157  			Name: "garbage_collector_attempt_to_orphan",
   158  		},
   159  	)
   160  	absentOwnerCache := NewReferenceCache(500)
   161  	graphBuilder := &GraphBuilder{
   162  		eventRecorder:    eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "garbage-collector-controller"}),
   163  		eventBroadcaster: eventBroadcaster,
   164  		metadataClient:   metadataClient,
   165  		informersStarted: informersStarted,
   166  		restMapper:       mapper,
   167  		graphChanges: workqueue.NewTypedRateLimitingQueueWithConfig(
   168  			workqueue.DefaultTypedControllerRateLimiter[*event](),
   169  			workqueue.TypedRateLimitingQueueConfig[*event]{
   170  				Name: "garbage_collector_graph_changes",
   171  			},
   172  		),
   173  		uidToNode: &concurrentUIDToNode{
   174  			uidToNode: make(map[types.UID]*node),
   175  		},
   176  		attemptToDelete:  attemptToDelete,
   177  		attemptToOrphan:  attemptToOrphan,
   178  		absentOwnerCache: absentOwnerCache,
   179  		sharedInformers:  sharedInformers,
   180  		ignoredResources: ignoredResources,
   181  	}
   182  
   183  	return graphBuilder
   184  }
   185  
   186  func (gb *GraphBuilder) controllerFor(logger klog.Logger, resource schema.GroupVersionResource, kind schema.GroupVersionKind) (cache.Controller, cache.Store, error) {
   187  	handlers := cache.ResourceEventHandlerFuncs{
   188  		// add the event to the dependencyGraphBuilder's graphChanges.
   189  		AddFunc: func(obj interface{}) {
   190  			event := &event{
   191  				eventType: addEvent,
   192  				obj:       obj,
   193  				gvk:       kind,
   194  			}
   195  			gb.graphChanges.Add(event)
   196  		},
   197  		UpdateFunc: func(oldObj, newObj interface{}) {
   198  			// TODO: check if there are differences in the ownerRefs,
   199  			// finalizers, and DeletionTimestamp; if not, ignore the update.
   200  			event := &event{
   201  				eventType: updateEvent,
   202  				obj:       newObj,
   203  				oldObj:    oldObj,
   204  				gvk:       kind,
   205  			}
   206  			gb.graphChanges.Add(event)
   207  		},
   208  		DeleteFunc: func(obj interface{}) {
   209  			// delta fifo may wrap the object in a cache.DeletedFinalStateUnknown, unwrap it
   210  			if deletedFinalStateUnknown, ok := obj.(cache.DeletedFinalStateUnknown); ok {
   211  				obj = deletedFinalStateUnknown.Obj
   212  			}
   213  			event := &event{
   214  				eventType: deleteEvent,
   215  				obj:       obj,
   216  				gvk:       kind,
   217  			}
   218  			gb.graphChanges.Add(event)
   219  		},
   220  	}
   221  
   222  	shared, err := gb.sharedInformers.ForResource(resource)
   223  	if err != nil {
   224  		logger.V(4).Error(err, "unable to use a shared informer", "resource", resource, "kind", kind)
   225  		return nil, nil, err
   226  	}
   227  	logger.V(4).Info("using a shared informer", "resource", resource, "kind", kind)
   228  	// need to clone because it's from a shared cache
   229  	shared.Informer().AddEventHandlerWithResyncPeriod(handlers, ResourceResyncTime)
   230  	return shared.Informer().GetController(), shared.Informer().GetStore(), nil
   231  }
   232  
   233  // syncMonitors rebuilds the monitor set according to the supplied resources,
   234  // creating or deleting monitors as necessary. It will return any error
   235  // encountered, but will make an attempt to create a monitor for each resource
   236  // instead of immediately exiting on an error. It may be called before or after
   237  // Run. Monitors are NOT started as part of the sync. To ensure all existing
   238  // monitors are started, call startMonitors.
   239  func (gb *GraphBuilder) syncMonitors(logger klog.Logger, resources map[schema.GroupVersionResource]struct{}) error {
   240  	gb.monitorLock.Lock()
   241  	defer gb.monitorLock.Unlock()
   242  
   243  	toRemove := gb.monitors
   244  	if toRemove == nil {
   245  		toRemove = monitors{}
   246  	}
   247  	current := monitors{}
   248  	errs := []error{}
   249  	kept := 0
   250  	added := 0
   251  	for resource := range resources {
   252  		if _, ok := gb.ignoredResources[resource.GroupResource()]; ok {
   253  			continue
   254  		}
   255  		if m, ok := toRemove[resource]; ok {
   256  			current[resource] = m
   257  			delete(toRemove, resource)
   258  			kept++
   259  			continue
   260  		}
   261  		kind, err := gb.restMapper.KindFor(resource)
   262  		if err != nil {
   263  			errs = append(errs, fmt.Errorf("couldn't look up resource %q: %v", resource, err))
   264  			continue
   265  		}
   266  		c, s, err := gb.controllerFor(logger, resource, kind)
   267  		if err != nil {
   268  			errs = append(errs, fmt.Errorf("couldn't start monitor for resource %q: %v", resource, err))
   269  			continue
   270  		}
   271  		current[resource] = &monitor{store: s, controller: c}
   272  		added++
   273  	}
   274  	gb.monitors = current
   275  
   276  	for _, monitor := range toRemove {
   277  		if monitor.stopCh != nil {
   278  			close(monitor.stopCh)
   279  		}
   280  	}
   281  
   282  	logger.V(4).Info("synced monitors", "added", added, "kept", kept, "removed", len(toRemove))
   283  	// NewAggregate returns nil if errs is 0-length
   284  	return utilerrors.NewAggregate(errs)
   285  }
   286  
   287  // startMonitors ensures the current set of monitors are running. Any newly
   288  // started monitors will also cause shared informers to be started.
   289  //
   290  // If called before Run, startMonitors does nothing (as there is no stop channel
   291  // to support monitor/informer execution).
   292  func (gb *GraphBuilder) startMonitors(logger klog.Logger) {
   293  	gb.monitorLock.Lock()
   294  	defer gb.monitorLock.Unlock()
   295  
   296  	if !gb.running {
   297  		return
   298  	}
   299  
   300  	// we're waiting until after the informer start that happens once all the controllers are initialized.  This ensures
   301  	// that they don't get unexpected events on their work queues.
   302  	<-gb.informersStarted
   303  
   304  	monitors := gb.monitors
   305  	started := 0
   306  	for _, monitor := range monitors {
   307  		if monitor.stopCh == nil {
   308  			monitor.stopCh = make(chan struct{})
   309  			gb.sharedInformers.Start(gb.stopCh)
   310  			go monitor.Run()
   311  			started++
   312  		}
   313  	}
   314  	logger.V(4).Info("started new monitors", "new", started, "current", len(monitors))
   315  }
   316  
   317  // IsResourceSynced returns true if a monitor exists for the given resource and has synced
   318  func (gb *GraphBuilder) IsResourceSynced(resource schema.GroupVersionResource) bool {
   319  	gb.monitorLock.Lock()
   320  	defer gb.monitorLock.Unlock()
   321  	monitor, ok := gb.monitors[resource]
   322  	return ok && monitor.controller.HasSynced()
   323  }
   324  
   325  // IsSynced returns true if any monitors exist AND all those monitors'
   326  // controllers HasSynced functions return true. This means IsSynced could return
   327  // true at one time, and then later return false if all monitors were
   328  // reconstructed.
   329  func (gb *GraphBuilder) IsSynced(logger klog.Logger) bool {
   330  	gb.monitorLock.Lock()
   331  	defer gb.monitorLock.Unlock()
   332  
   333  	if len(gb.monitors) == 0 {
   334  		logger.V(4).Info("garbage controller monitor not synced: no monitors")
   335  		return false
   336  	}
   337  
   338  	for resource, monitor := range gb.monitors {
   339  		if !monitor.controller.HasSynced() {
   340  			logger.V(4).Info("garbage controller monitor not yet synced", "resource", resource)
   341  			return false
   342  		}
   343  	}
   344  	return true
   345  }
   346  
   347  // Run sets the stop channel and starts monitor execution until stopCh is
   348  // closed. Any running monitors will be stopped before Run returns.
   349  func (gb *GraphBuilder) Run(ctx context.Context) {
   350  	logger := klog.FromContext(ctx)
   351  	logger.Info("Running", "component", "GraphBuilder")
   352  	defer logger.Info("Stopping", "component", "GraphBuilder")
   353  
   354  	// Set up the stop channel.
   355  	gb.monitorLock.Lock()
   356  	gb.stopCh = ctx.Done()
   357  	gb.running = true
   358  	gb.monitorLock.Unlock()
   359  
   360  	// Start monitors and begin change processing until the stop channel is
   361  	// closed.
   362  	gb.startMonitors(logger)
   363  	wait.Until(func() { gb.runProcessGraphChanges(logger) }, 1*time.Second, ctx.Done())
   364  
   365  	// Stop any running monitors.
   366  	gb.monitorLock.Lock()
   367  	defer gb.monitorLock.Unlock()
   368  	monitors := gb.monitors
   369  	stopped := 0
   370  	for _, monitor := range monitors {
   371  		if monitor.stopCh != nil {
   372  			stopped++
   373  			close(monitor.stopCh)
   374  		}
   375  	}
   376  
   377  	// reset monitors so that the graph builder can be safely re-run/synced.
   378  	gb.monitors = nil
   379  	logger.Info("stopped monitors", "stopped", stopped, "total", len(monitors))
   380  }
   381  
   382  var ignoredResources = map[schema.GroupResource]struct{}{
   383  	{Group: "", Resource: "events"}:                {},
   384  	{Group: eventv1.GroupName, Resource: "events"}: {},
   385  }
   386  
   387  // DefaultIgnoredResources returns the default set of resources that the garbage collector controller
   388  // should ignore. This is exposed so downstream integrators can have access to the defaults, and add
   389  // to them as necessary when constructing the controller.
   390  func DefaultIgnoredResources() map[schema.GroupResource]struct{} {
   391  	return ignoredResources
   392  }
   393  
   394  // enqueueVirtualDeleteEvent is used to add a virtual delete event to be processed for virtual nodes
   395  // once it is determined they do not have backing objects in storage
   396  func (gb *GraphBuilder) enqueueVirtualDeleteEvent(ref objectReference) {
   397  	gv, _ := schema.ParseGroupVersion(ref.APIVersion)
   398  	gb.graphChanges.Add(&event{
   399  		virtual:   true,
   400  		eventType: deleteEvent,
   401  		gvk:       gv.WithKind(ref.Kind),
   402  		obj: &metaonly.MetadataOnlyObject{
   403  			TypeMeta:   metav1.TypeMeta{APIVersion: ref.APIVersion, Kind: ref.Kind},
   404  			ObjectMeta: metav1.ObjectMeta{Namespace: ref.Namespace, UID: ref.UID, Name: ref.Name},
   405  		},
   406  	})
   407  }
   408  
   409  // addDependentToOwners adds n to owners' dependents list. If the owner does not
   410  // exist in the gb.uidToNode yet, a "virtual" node will be created to represent
   411  // the owner. The "virtual" node will be enqueued to the attemptToDelete, so that
   412  // attemptToDeleteItem() will verify if the owner exists according to the API server.
   413  func (gb *GraphBuilder) addDependentToOwners(logger klog.Logger, n *node, owners []metav1.OwnerReference) {
   414  	// track if some of the referenced owners already exist in the graph and have been observed,
   415  	// and the dependent's ownerRef does not match their observed coordinates
   416  	hasPotentiallyInvalidOwnerReference := false
   417  
   418  	for _, owner := range owners {
   419  		ownerNode, ok := gb.uidToNode.Read(owner.UID)
   420  		if !ok {
   421  			// Create a "virtual" node in the graph for the owner if it doesn't
   422  			// exist in the graph yet.
   423  			ownerNode = &node{
   424  				identity: objectReference{
   425  					OwnerReference: ownerReferenceCoordinates(owner),
   426  					Namespace:      n.identity.Namespace,
   427  				},
   428  				dependents: make(map[*node]struct{}),
   429  				virtual:    true,
   430  			}
   431  			logger.V(5).Info("add virtual item", "identity", ownerNode.identity)
   432  			gb.uidToNode.Write(ownerNode)
   433  		}
   434  		ownerNode.addDependent(n)
   435  		if !ok {
   436  			// Enqueue the virtual node into attemptToDelete.
   437  			// The garbage processor will enqueue a virtual delete
   438  			// event to delete it from the graph if API server confirms this
   439  			// owner doesn't exist.
   440  			gb.attemptToDelete.Add(ownerNode)
   441  		} else if !hasPotentiallyInvalidOwnerReference {
   442  			ownerIsNamespaced := len(ownerNode.identity.Namespace) > 0
   443  			if ownerIsNamespaced && ownerNode.identity.Namespace != n.identity.Namespace {
   444  				if ownerNode.isObserved() {
   445  					// The owner node has been observed via an informer
   446  					// the dependent's namespace doesn't match the observed owner's namespace, this is definitely wrong.
   447  					// cluster-scoped owners can be referenced as an owner from any namespace or cluster-scoped object.
   448  					logger.V(2).Info("item references an owner but does not match namespaces", "item", n.identity, "owner", ownerNode.identity)
   449  					gb.reportInvalidNamespaceOwnerRef(n, owner.UID)
   450  				}
   451  				hasPotentiallyInvalidOwnerReference = true
   452  			} else if !ownerReferenceMatchesCoordinates(owner, ownerNode.identity.OwnerReference) {
   453  				if ownerNode.isObserved() {
   454  					// The owner node has been observed via an informer
   455  					// n's owner reference doesn't match the observed identity, this might be wrong.
   456  					logger.V(2).Info("item references an owner with coordinates that do not match the observed identity", "item", n.identity, "owner", ownerNode.identity)
   457  				}
   458  				hasPotentiallyInvalidOwnerReference = true
   459  			} else if !ownerIsNamespaced && ownerNode.identity.Namespace != n.identity.Namespace && !ownerNode.isObserved() {
   460  				// the ownerNode is cluster-scoped and virtual, and does not match the child node's namespace.
   461  				// the owner could be a missing instance of a namespaced type incorrectly referenced by a cluster-scoped child (issue #98040).
   462  				// enqueue this child to attemptToDelete to verify parent references.
   463  				hasPotentiallyInvalidOwnerReference = true
   464  			}
   465  		}
   466  	}
   467  
   468  	if hasPotentiallyInvalidOwnerReference {
   469  		// Enqueue the potentially invalid dependent node into attemptToDelete.
   470  		// The garbage processor will verify whether the owner references are dangling
   471  		// and delete the dependent if all owner references are confirmed absent.
   472  		gb.attemptToDelete.Add(n)
   473  	}
   474  }
   475  
   476  func (gb *GraphBuilder) reportInvalidNamespaceOwnerRef(n *node, invalidOwnerUID types.UID) {
   477  	var invalidOwnerRef metav1.OwnerReference
   478  	var found = false
   479  	for _, ownerRef := range n.owners {
   480  		if ownerRef.UID == invalidOwnerUID {
   481  			invalidOwnerRef = ownerRef
   482  			found = true
   483  			break
   484  		}
   485  	}
   486  	if !found {
   487  		return
   488  	}
   489  	ref := &v1.ObjectReference{
   490  		Kind:       n.identity.Kind,
   491  		APIVersion: n.identity.APIVersion,
   492  		Namespace:  n.identity.Namespace,
   493  		Name:       n.identity.Name,
   494  		UID:        n.identity.UID,
   495  	}
   496  	invalidIdentity := objectReference{
   497  		OwnerReference: metav1.OwnerReference{
   498  			Kind:       invalidOwnerRef.Kind,
   499  			APIVersion: invalidOwnerRef.APIVersion,
   500  			Name:       invalidOwnerRef.Name,
   501  			UID:        invalidOwnerRef.UID,
   502  		},
   503  		Namespace: n.identity.Namespace,
   504  	}
   505  	gb.eventRecorder.Eventf(ref, v1.EventTypeWarning, "OwnerRefInvalidNamespace", "ownerRef %s does not exist in namespace %q", invalidIdentity, n.identity.Namespace)
   506  }
   507  
   508  // insertNode insert the node to gb.uidToNode; then it finds all owners as listed
   509  // in n.owners, and adds the node to their dependents list.
   510  func (gb *GraphBuilder) insertNode(logger klog.Logger, n *node) {
   511  	gb.uidToNode.Write(n)
   512  	gb.addDependentToOwners(logger, n, n.owners)
   513  }
   514  
   515  // removeDependentFromOwners remove n from owners' dependents list.
   516  func (gb *GraphBuilder) removeDependentFromOwners(n *node, owners []metav1.OwnerReference) {
   517  	for _, owner := range owners {
   518  		ownerNode, ok := gb.uidToNode.Read(owner.UID)
   519  		if !ok {
   520  			continue
   521  		}
   522  		ownerNode.deleteDependent(n)
   523  	}
   524  }
   525  
   526  // removeNode removes the node from gb.uidToNode, then finds all
   527  // owners as listed in n.owners, and removes n from their dependents list.
   528  func (gb *GraphBuilder) removeNode(n *node) {
   529  	gb.uidToNode.Delete(n.identity.UID)
   530  	gb.removeDependentFromOwners(n, n.owners)
   531  }
   532  
   533  type ownerRefPair struct {
   534  	oldRef metav1.OwnerReference
   535  	newRef metav1.OwnerReference
   536  }
   537  
   538  // TODO: profile this function to see if a naive N^2 algorithm performs better
   539  // when the number of references is small.
   540  func referencesDiffs(old []metav1.OwnerReference, new []metav1.OwnerReference) (added []metav1.OwnerReference, removed []metav1.OwnerReference, changed []ownerRefPair) {
   541  	oldUIDToRef := make(map[string]metav1.OwnerReference)
   542  	for _, value := range old {
   543  		oldUIDToRef[string(value.UID)] = value
   544  	}
   545  	oldUIDSet := sets.StringKeySet(oldUIDToRef)
   546  	for _, value := range new {
   547  		newUID := string(value.UID)
   548  		if oldUIDSet.Has(newUID) {
   549  			if !reflect.DeepEqual(oldUIDToRef[newUID], value) {
   550  				changed = append(changed, ownerRefPair{oldRef: oldUIDToRef[newUID], newRef: value})
   551  			}
   552  			oldUIDSet.Delete(newUID)
   553  		} else {
   554  			added = append(added, value)
   555  		}
   556  	}
   557  	for oldUID := range oldUIDSet {
   558  		removed = append(removed, oldUIDToRef[oldUID])
   559  	}
   560  
   561  	return added, removed, changed
   562  }
   563  
   564  func deletionStartsWithFinalizer(oldObj interface{}, newAccessor metav1.Object, matchingFinalizer string) bool {
   565  	// if the new object isn't being deleted, or doesn't have the finalizer we're interested in, return false
   566  	if !beingDeleted(newAccessor) || !hasFinalizer(newAccessor, matchingFinalizer) {
   567  		return false
   568  	}
   569  
   570  	// if the old object is nil, or wasn't being deleted, or didn't have the finalizer, return true
   571  	if oldObj == nil {
   572  		return true
   573  	}
   574  	oldAccessor, err := meta.Accessor(oldObj)
   575  	if err != nil {
   576  		utilruntime.HandleError(fmt.Errorf("cannot access oldObj: %v", err))
   577  		return false
   578  	}
   579  	return !beingDeleted(oldAccessor) || !hasFinalizer(oldAccessor, matchingFinalizer)
   580  }
   581  
   582  func beingDeleted(accessor metav1.Object) bool {
   583  	return accessor.GetDeletionTimestamp() != nil
   584  }
   585  
   586  func hasDeleteDependentsFinalizer(accessor metav1.Object) bool {
   587  	return hasFinalizer(accessor, metav1.FinalizerDeleteDependents)
   588  }
   589  
   590  func hasOrphanFinalizer(accessor metav1.Object) bool {
   591  	return hasFinalizer(accessor, metav1.FinalizerOrphanDependents)
   592  }
   593  
   594  func hasFinalizer(accessor metav1.Object, matchingFinalizer string) bool {
   595  	finalizers := accessor.GetFinalizers()
   596  	for _, finalizer := range finalizers {
   597  		if finalizer == matchingFinalizer {
   598  			return true
   599  		}
   600  	}
   601  	return false
   602  }
   603  
   604  // this function takes newAccessor directly because the caller already
   605  // instantiates an accessor for the newObj.
   606  func startsWaitingForDependentsDeleted(oldObj interface{}, newAccessor metav1.Object) bool {
   607  	return deletionStartsWithFinalizer(oldObj, newAccessor, metav1.FinalizerDeleteDependents)
   608  }
   609  
   610  // this function takes newAccessor directly because the caller already
   611  // instantiates an accessor for the newObj.
   612  func startsWaitingForDependentsOrphaned(oldObj interface{}, newAccessor metav1.Object) bool {
   613  	return deletionStartsWithFinalizer(oldObj, newAccessor, metav1.FinalizerOrphanDependents)
   614  }
   615  
   616  // if an blocking ownerReference points to an object gets removed, or gets set to
   617  // "BlockOwnerDeletion=false", add the object to the attemptToDelete queue.
   618  func (gb *GraphBuilder) addUnblockedOwnersToDeleteQueue(logger klog.Logger, removed []metav1.OwnerReference, changed []ownerRefPair) {
   619  	for _, ref := range removed {
   620  		if ref.BlockOwnerDeletion != nil && *ref.BlockOwnerDeletion {
   621  			node, found := gb.uidToNode.Read(ref.UID)
   622  			if !found {
   623  				logger.V(5).Info("cannot find uid in uidToNode", "uid", ref.UID)
   624  				continue
   625  			}
   626  			gb.attemptToDelete.Add(node)
   627  		}
   628  	}
   629  	for _, c := range changed {
   630  		wasBlocked := c.oldRef.BlockOwnerDeletion != nil && *c.oldRef.BlockOwnerDeletion
   631  		isUnblocked := c.newRef.BlockOwnerDeletion == nil || (c.newRef.BlockOwnerDeletion != nil && !*c.newRef.BlockOwnerDeletion)
   632  		if wasBlocked && isUnblocked {
   633  			node, found := gb.uidToNode.Read(c.newRef.UID)
   634  			if !found {
   635  				logger.V(5).Info("cannot find uid in uidToNode", "uid", c.newRef.UID)
   636  				continue
   637  			}
   638  			gb.attemptToDelete.Add(node)
   639  		}
   640  	}
   641  }
   642  
   643  func (gb *GraphBuilder) processTransitions(logger klog.Logger, oldObj interface{}, newAccessor metav1.Object, n *node) {
   644  	if startsWaitingForDependentsOrphaned(oldObj, newAccessor) {
   645  		logger.V(5).Info("add item to attemptToOrphan", "item", n.identity)
   646  		gb.attemptToOrphan.Add(n)
   647  		return
   648  	}
   649  	if startsWaitingForDependentsDeleted(oldObj, newAccessor) {
   650  		logger.V(2).Info("add item to attemptToDelete, because it's waiting for its dependents to be deleted", "item", n.identity)
   651  		// if the n is added as a "virtual" node, its deletingDependents field is not properly set, so always set it here.
   652  		n.markDeletingDependents()
   653  		for dep := range n.dependents {
   654  			gb.attemptToDelete.Add(dep)
   655  		}
   656  		gb.attemptToDelete.Add(n)
   657  	}
   658  }
   659  
   660  func (gb *GraphBuilder) runProcessGraphChanges(logger klog.Logger) {
   661  	for gb.processGraphChanges(logger) {
   662  	}
   663  }
   664  
   665  func identityFromEvent(event *event, accessor metav1.Object) objectReference {
   666  	return objectReference{
   667  		OwnerReference: metav1.OwnerReference{
   668  			APIVersion: event.gvk.GroupVersion().String(),
   669  			Kind:       event.gvk.Kind,
   670  			UID:        accessor.GetUID(),
   671  			Name:       accessor.GetName(),
   672  		},
   673  		Namespace: accessor.GetNamespace(),
   674  	}
   675  }
   676  
   677  // Dequeueing an event from graphChanges, updating graph, populating dirty_queue.
   678  func (gb *GraphBuilder) processGraphChanges(logger klog.Logger) bool {
   679  	item, quit := gb.graphChanges.Get()
   680  	if quit {
   681  		return false
   682  	}
   683  	defer gb.graphChanges.Done(item)
   684  	event := item
   685  	obj := item.obj
   686  	accessor, err := meta.Accessor(obj)
   687  	if err != nil {
   688  		utilruntime.HandleError(fmt.Errorf("cannot access obj: %v", err))
   689  		return true
   690  	}
   691  
   692  	logger.V(5).Info("GraphBuilder process object",
   693  		"apiVersion", event.gvk.GroupVersion().String(),
   694  		"kind", event.gvk.Kind,
   695  		"object", klog.KObj(accessor),
   696  		"uid", string(accessor.GetUID()),
   697  		"eventType", event.eventType,
   698  		"virtual", event.virtual,
   699  	)
   700  
   701  	// Check if the node already exists
   702  	existingNode, found := gb.uidToNode.Read(accessor.GetUID())
   703  	if found && !event.virtual && !existingNode.isObserved() {
   704  		// this marks the node as having been observed via an informer event
   705  		// 1. this depends on graphChanges only containing add/update events from the actual informer
   706  		// 2. this allows things tracking virtual nodes' existence to stop polling and rely on informer events
   707  		observedIdentity := identityFromEvent(event, accessor)
   708  		if observedIdentity != existingNode.identity {
   709  			// find dependents that don't match the identity we observed
   710  			_, potentiallyInvalidDependents := partitionDependents(existingNode.getDependents(), observedIdentity)
   711  			// add those potentially invalid dependents to the attemptToDelete queue.
   712  			// if their owners are still solid the attemptToDelete will be a no-op.
   713  			// this covers the bad child -> good parent observation sequence.
   714  			// the good parent -> bad child observation sequence is handled in addDependentToOwners
   715  			for _, dep := range potentiallyInvalidDependents {
   716  				if len(observedIdentity.Namespace) > 0 && dep.identity.Namespace != observedIdentity.Namespace {
   717  					// Namespace mismatch, this is definitely wrong
   718  					logger.V(2).Info("item references an owner but does not match namespaces",
   719  						"item", dep.identity,
   720  						"owner", observedIdentity,
   721  					)
   722  					gb.reportInvalidNamespaceOwnerRef(dep, observedIdentity.UID)
   723  				}
   724  				gb.attemptToDelete.Add(dep)
   725  			}
   726  
   727  			// make a copy (so we don't modify the existing node in place), store the observed identity, and replace the virtual node
   728  			logger.V(2).Info("replacing virtual item with observed item",
   729  				"virtual", existingNode.identity,
   730  				"observed", observedIdentity,
   731  			)
   732  			existingNode = existingNode.clone()
   733  			existingNode.identity = observedIdentity
   734  			gb.uidToNode.Write(existingNode)
   735  		}
   736  		existingNode.markObserved()
   737  	}
   738  	switch {
   739  	case (event.eventType == addEvent || event.eventType == updateEvent) && !found:
   740  		newNode := &node{
   741  			identity:           identityFromEvent(event, accessor),
   742  			dependents:         make(map[*node]struct{}),
   743  			owners:             accessor.GetOwnerReferences(),
   744  			deletingDependents: beingDeleted(accessor) && hasDeleteDependentsFinalizer(accessor),
   745  			beingDeleted:       beingDeleted(accessor),
   746  		}
   747  		gb.insertNode(logger, newNode)
   748  		// the underlying delta_fifo may combine a creation and a deletion into
   749  		// one event, so we need to further process the event.
   750  		gb.processTransitions(logger, event.oldObj, accessor, newNode)
   751  	case (event.eventType == addEvent || event.eventType == updateEvent) && found:
   752  		// handle changes in ownerReferences
   753  		added, removed, changed := referencesDiffs(existingNode.owners, accessor.GetOwnerReferences())
   754  		if len(added) != 0 || len(removed) != 0 || len(changed) != 0 {
   755  			// check if the changed dependency graph unblock owners that are
   756  			// waiting for the deletion of their dependents.
   757  			gb.addUnblockedOwnersToDeleteQueue(logger, removed, changed)
   758  			// update the node itself
   759  			existingNode.owners = accessor.GetOwnerReferences()
   760  			// Add the node to its new owners' dependent lists.
   761  			gb.addDependentToOwners(logger, existingNode, added)
   762  			// remove the node from the dependent list of node that are no longer in
   763  			// the node's owners list.
   764  			gb.removeDependentFromOwners(existingNode, removed)
   765  		}
   766  
   767  		if beingDeleted(accessor) {
   768  			existingNode.markBeingDeleted()
   769  		}
   770  		gb.processTransitions(logger, event.oldObj, accessor, existingNode)
   771  	case event.eventType == deleteEvent:
   772  		if !found {
   773  			logger.V(5).Info("item doesn't exist in the graph, this shouldn't happen",
   774  				"item", accessor.GetUID(),
   775  			)
   776  			return true
   777  		}
   778  
   779  		removeExistingNode := true
   780  
   781  		if event.virtual {
   782  			// this is a virtual delete event, not one observed from an informer
   783  			deletedIdentity := identityFromEvent(event, accessor)
   784  			if existingNode.virtual {
   785  
   786  				// our existing node is also virtual, we're not sure of its coordinates.
   787  				// see if any dependents reference this owner with coordinates other than the one we got a virtual delete event for.
   788  				if matchingDependents, nonmatchingDependents := partitionDependents(existingNode.getDependents(), deletedIdentity); len(nonmatchingDependents) > 0 {
   789  
   790  					// some of our dependents disagree on our coordinates, so do not remove the existing virtual node from the graph
   791  					removeExistingNode = false
   792  
   793  					if len(matchingDependents) > 0 {
   794  						// mark the observed deleted identity as absent
   795  						gb.absentOwnerCache.Add(deletedIdentity)
   796  						// attempt to delete dependents that do match the verified deleted identity
   797  						for _, dep := range matchingDependents {
   798  							gb.attemptToDelete.Add(dep)
   799  						}
   800  					}
   801  
   802  					// if the delete event verified existingNode.identity doesn't exist...
   803  					if existingNode.identity == deletedIdentity {
   804  						// find an alternative identity our nonmatching dependents refer to us by
   805  						replacementIdentity := getAlternateOwnerIdentity(nonmatchingDependents, deletedIdentity)
   806  						if replacementIdentity != nil {
   807  							// replace the existing virtual node with a new one with one of our other potential identities
   808  							replacementNode := existingNode.clone()
   809  							replacementNode.identity = *replacementIdentity
   810  							gb.uidToNode.Write(replacementNode)
   811  							// and add the new virtual node back to the attemptToDelete queue
   812  							gb.attemptToDelete.AddRateLimited(replacementNode)
   813  						}
   814  					}
   815  				}
   816  
   817  			} else if existingNode.identity != deletedIdentity {
   818  				// do not remove the existing real node from the graph based on a virtual delete event
   819  				removeExistingNode = false
   820  
   821  				// our existing node which was observed via informer disagrees with the virtual delete event's coordinates
   822  				matchingDependents, _ := partitionDependents(existingNode.getDependents(), deletedIdentity)
   823  
   824  				if len(matchingDependents) > 0 {
   825  					// mark the observed deleted identity as absent
   826  					gb.absentOwnerCache.Add(deletedIdentity)
   827  					// attempt to delete dependents that do match the verified deleted identity
   828  					for _, dep := range matchingDependents {
   829  						gb.attemptToDelete.Add(dep)
   830  					}
   831  				}
   832  			}
   833  		}
   834  
   835  		if removeExistingNode {
   836  			// removeNode updates the graph
   837  			gb.removeNode(existingNode)
   838  			existingNode.dependentsLock.RLock()
   839  			defer existingNode.dependentsLock.RUnlock()
   840  			if len(existingNode.dependents) > 0 {
   841  				gb.absentOwnerCache.Add(identityFromEvent(event, accessor))
   842  			}
   843  			for dep := range existingNode.dependents {
   844  				gb.attemptToDelete.Add(dep)
   845  			}
   846  			for _, owner := range existingNode.owners {
   847  				ownerNode, found := gb.uidToNode.Read(owner.UID)
   848  				if !found || !ownerNode.isDeletingDependents() {
   849  					continue
   850  				}
   851  				// this is to let attempToDeleteItem check if all the owner's
   852  				// dependents are deleted, if so, the owner will be deleted.
   853  				gb.attemptToDelete.Add(ownerNode)
   854  			}
   855  		}
   856  	}
   857  	return true
   858  }
   859  
   860  // partitionDependents divides the provided dependents into a list which have an ownerReference matching the provided identity,
   861  // and ones which have an ownerReference for the given uid that do not match the provided identity.
   862  // Note that a dependent with multiple ownerReferences for the target uid can end up in both lists.
   863  func partitionDependents(dependents []*node, matchOwnerIdentity objectReference) (matching, nonmatching []*node) {
   864  	ownerIsNamespaced := len(matchOwnerIdentity.Namespace) > 0
   865  	for i := range dependents {
   866  		dep := dependents[i]
   867  		foundMatch := false
   868  		foundMismatch := false
   869  		// if the dep namespace matches or the owner is cluster scoped ...
   870  		if ownerIsNamespaced && matchOwnerIdentity.Namespace != dep.identity.Namespace {
   871  			// all references to the parent do not match, since the dependent namespace does not match the owner
   872  			foundMismatch = true
   873  		} else {
   874  			for _, ownerRef := range dep.owners {
   875  				// ... find the ownerRef with a matching uid ...
   876  				if ownerRef.UID == matchOwnerIdentity.UID {
   877  					// ... and check if it matches all coordinates
   878  					if ownerReferenceMatchesCoordinates(ownerRef, matchOwnerIdentity.OwnerReference) {
   879  						foundMatch = true
   880  					} else {
   881  						foundMismatch = true
   882  					}
   883  				}
   884  			}
   885  		}
   886  
   887  		if foundMatch {
   888  			matching = append(matching, dep)
   889  		}
   890  		if foundMismatch {
   891  			nonmatching = append(nonmatching, dep)
   892  		}
   893  	}
   894  	return matching, nonmatching
   895  }
   896  
   897  func referenceLessThan(a, b objectReference) bool {
   898  	// kind/apiVersion are more significant than namespace,
   899  	// so that we get coherent ordering between kinds
   900  	// regardless of whether they are cluster-scoped or namespaced
   901  	if a.Kind != b.Kind {
   902  		return a.Kind < b.Kind
   903  	}
   904  	if a.APIVersion != b.APIVersion {
   905  		return a.APIVersion < b.APIVersion
   906  	}
   907  	// namespace is more significant than name
   908  	if a.Namespace != b.Namespace {
   909  		return a.Namespace < b.Namespace
   910  	}
   911  	// name is more significant than uid
   912  	if a.Name != b.Name {
   913  		return a.Name < b.Name
   914  	}
   915  	// uid is included for completeness, but is expected to be identical
   916  	// when getting alternate identities for an owner since they are keyed by uid
   917  	if a.UID != b.UID {
   918  		return a.UID < b.UID
   919  	}
   920  	return false
   921  }
   922  
   923  // getAlternateOwnerIdentity searches deps for owner references which match
   924  // verifiedAbsentIdentity.UID but differ in apiVersion/kind/name or namespace.
   925  // The first that follows verifiedAbsentIdentity (according to referenceLessThan) is returned.
   926  // If none follow verifiedAbsentIdentity, the first (according to referenceLessThan) is returned.
   927  // If no alternate identities are found, nil is returned.
   928  func getAlternateOwnerIdentity(deps []*node, verifiedAbsentIdentity objectReference) *objectReference {
   929  	absentIdentityIsClusterScoped := len(verifiedAbsentIdentity.Namespace) == 0
   930  
   931  	seenAlternates := map[objectReference]bool{verifiedAbsentIdentity: true}
   932  
   933  	// keep track of the first alternate reference (according to referenceLessThan)
   934  	var first *objectReference
   935  	// keep track of the first reference following verifiedAbsentIdentity (according to referenceLessThan)
   936  	var firstFollowing *objectReference
   937  
   938  	for _, dep := range deps {
   939  		for _, ownerRef := range dep.owners {
   940  			if ownerRef.UID != verifiedAbsentIdentity.UID {
   941  				// skip references that aren't the uid we care about
   942  				continue
   943  			}
   944  
   945  			if ownerReferenceMatchesCoordinates(ownerRef, verifiedAbsentIdentity.OwnerReference) {
   946  				if absentIdentityIsClusterScoped || verifiedAbsentIdentity.Namespace == dep.identity.Namespace {
   947  					// skip references that exactly match verifiedAbsentIdentity
   948  					continue
   949  				}
   950  			}
   951  
   952  			ref := objectReference{OwnerReference: ownerReferenceCoordinates(ownerRef), Namespace: dep.identity.Namespace}
   953  			if absentIdentityIsClusterScoped && ref.APIVersion == verifiedAbsentIdentity.APIVersion && ref.Kind == verifiedAbsentIdentity.Kind {
   954  				// we know this apiVersion/kind is cluster-scoped because of verifiedAbsentIdentity,
   955  				// so clear the namespace from the alternate identity
   956  				ref.Namespace = ""
   957  			}
   958  
   959  			if seenAlternates[ref] {
   960  				// skip references we've already seen
   961  				continue
   962  			}
   963  			seenAlternates[ref] = true
   964  
   965  			if first == nil || referenceLessThan(ref, *first) {
   966  				// this alternate comes first lexically
   967  				first = &ref
   968  			}
   969  			if referenceLessThan(verifiedAbsentIdentity, ref) && (firstFollowing == nil || referenceLessThan(ref, *firstFollowing)) {
   970  				// this alternate is the first following verifiedAbsentIdentity lexically
   971  				firstFollowing = &ref
   972  			}
   973  		}
   974  	}
   975  
   976  	// return the first alternate identity following the verified absent identity, if there is one
   977  	if firstFollowing != nil {
   978  		return firstFollowing
   979  	}
   980  	// otherwise return the first alternate identity
   981  	return first
   982  }
   983  
   984  func (gb *GraphBuilder) GetGraphResources() (
   985  	attemptToDelete workqueue.TypedRateLimitingInterface[*node],
   986  	attemptToOrphan workqueue.TypedRateLimitingInterface[*node],
   987  	absentOwnerCache *ReferenceCache,
   988  ) {
   989  	return gb.attemptToDelete, gb.attemptToOrphan, gb.absentOwnerCache
   990  }
   991  
   992  type Monitor struct {
   993  	Store      cache.Store
   994  	Controller cache.Controller
   995  }
   996  
   997  // GetMonitor returns a monitor for the given resource.
   998  // If the monitor is not synced, it will return an error and the monitor to allow the caller to decide whether to retry.
   999  // If the monitor is not found, it will return only an error.
  1000  func (gb *GraphBuilder) GetMonitor(ctx context.Context, resource schema.GroupVersionResource) (*Monitor, error) {
  1001  	gb.monitorLock.RLock()
  1002  	defer gb.monitorLock.RUnlock()
  1003  
  1004  	var monitor *monitor
  1005  	if m, ok := gb.monitors[resource]; ok {
  1006  		monitor = m
  1007  	} else {
  1008  		for monitorGVR, m := range gb.monitors {
  1009  			if monitorGVR.Group == resource.Group && monitorGVR.Resource == resource.Resource {
  1010  				monitor = m
  1011  				break
  1012  			}
  1013  		}
  1014  	}
  1015  
  1016  	if monitor == nil {
  1017  		return nil, fmt.Errorf("no monitor found for resource %s", resource.String())
  1018  	}
  1019  
  1020  	resourceMonitor := &Monitor{
  1021  		Store:      monitor.store,
  1022  		Controller: monitor.controller,
  1023  	}
  1024  
  1025  	if !cache.WaitForNamedCacheSync(
  1026  		gb.Name(),
  1027  		ctx.Done(),
  1028  		func() bool {
  1029  			return monitor.controller.HasSynced()
  1030  		},
  1031  	) {
  1032  		// returning monitor to allow the caller to decide whether to retry as it can be synced later
  1033  		return resourceMonitor, fmt.Errorf("dependency graph for resource %s is not synced", resource.String())
  1034  	}
  1035  
  1036  	return resourceMonitor, nil
  1037  }
  1038  
  1039  func (gb *GraphBuilder) Name() string {
  1040  	return "dependencygraphbuilder"
  1041  }