mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-31 05:40:42 +00:00 
			
		
		
		
	When using JSON as output format, we want objectReference values to be
represented as a struct. For example, "item" is such a value:
{"ts":1678135015708.349,"caller":"garbagecollector/garbagecollector.go:595","msg":"classify object references","v":5,"item":{"name":"dra-test-driver-g4tkd","namespace":"dra-1830","apiVersion":"v1","uid":"c3f88616-7282-488c-887c-3f04291e6f4f"},"solid":null,"dangling":[{"apiVersion":"apps/v1","kind":"ReplicaSet","name":"dra-test-driver","uid":"dbe9a90c-9dfd-4ad0-8395-e5fa228f9851","controller":true,"blockOwnerDeletion":true}],"waitingForDependentsDeletion":null}
		
	
		
			
				
	
	
		
			239 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			239 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
| Copyright 2016 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 garbagecollector
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"sync"
 | |
| 
 | |
| 	"github.com/go-logr/logr"
 | |
| 
 | |
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | |
| 	"k8s.io/apimachinery/pkg/types"
 | |
| )
 | |
| 
 | |
| type objectReference struct {
 | |
| 	metav1.OwnerReference
 | |
| 	// This is needed by the dynamic client
 | |
| 	Namespace string
 | |
| }
 | |
| 
 | |
| // String is used when logging an objectReference in text format.
 | |
| func (s objectReference) String() string {
 | |
| 	return fmt.Sprintf("[%s/%s, namespace: %s, name: %s, uid: %s]", s.APIVersion, s.Kind, s.Namespace, s.Name, s.UID)
 | |
| }
 | |
| 
 | |
| // MarshalLog is used when logging an objectReference in JSON format.
 | |
| func (s objectReference) MarshalLog() interface{} {
 | |
| 	return struct {
 | |
| 		Name       string    `json:"name"`
 | |
| 		Namespace  string    `json:"namespace"`
 | |
| 		APIVersion string    `json:"apiVersion"`
 | |
| 		UID        types.UID `json:"uid"`
 | |
| 	}{
 | |
| 		Namespace:  s.Namespace,
 | |
| 		Name:       s.Name,
 | |
| 		APIVersion: s.APIVersion,
 | |
| 		UID:        s.UID,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| var _ fmt.Stringer = objectReference{}
 | |
| var _ logr.Marshaler = objectReference{}
 | |
| 
 | |
| // The single-threaded GraphBuilder.processGraphChanges() is the sole writer of the
 | |
| // nodes. The multi-threaded GarbageCollector.attemptToDeleteItem() reads the nodes.
 | |
| // WARNING: node has different locks on different fields. setters and getters
 | |
| // use the respective locks, so the return values of the getters can be
 | |
| // inconsistent.
 | |
| type node struct {
 | |
| 	identity objectReference
 | |
| 	// dependents will be read by the orphan() routine, we need to protect it with a lock.
 | |
| 	dependentsLock sync.RWMutex
 | |
| 	// dependents are the nodes that have node.identity as a
 | |
| 	// metadata.ownerReference.
 | |
| 	dependents map[*node]struct{}
 | |
| 	// this is set by processGraphChanges() if the object has non-nil DeletionTimestamp
 | |
| 	// and has the FinalizerDeleteDependents.
 | |
| 	deletingDependents     bool
 | |
| 	deletingDependentsLock sync.RWMutex
 | |
| 	// this records if the object's deletionTimestamp is non-nil.
 | |
| 	beingDeleted     bool
 | |
| 	beingDeletedLock sync.RWMutex
 | |
| 	// this records if the object was constructed virtually and never observed via informer event
 | |
| 	virtual     bool
 | |
| 	virtualLock sync.RWMutex
 | |
| 	// when processing an Update event, we need to compare the updated
 | |
| 	// ownerReferences with the owners recorded in the graph.
 | |
| 	owners []metav1.OwnerReference
 | |
| }
 | |
| 
 | |
| // clone() must only be called from the single-threaded GraphBuilder.processGraphChanges()
 | |
| func (n *node) clone() *node {
 | |
| 	c := &node{
 | |
| 		identity:           n.identity,
 | |
| 		dependents:         make(map[*node]struct{}, len(n.dependents)),
 | |
| 		deletingDependents: n.deletingDependents,
 | |
| 		beingDeleted:       n.beingDeleted,
 | |
| 		virtual:            n.virtual,
 | |
| 		owners:             make([]metav1.OwnerReference, 0, len(n.owners)),
 | |
| 	}
 | |
| 	for dep := range n.dependents {
 | |
| 		c.dependents[dep] = struct{}{}
 | |
| 	}
 | |
| 	for _, owner := range n.owners {
 | |
| 		c.owners = append(c.owners, owner)
 | |
| 	}
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| // An object is on a one way trip to its final deletion if it starts being
 | |
| // deleted, so we only provide a function to set beingDeleted to true.
 | |
| func (n *node) markBeingDeleted() {
 | |
| 	n.beingDeletedLock.Lock()
 | |
| 	defer n.beingDeletedLock.Unlock()
 | |
| 	n.beingDeleted = true
 | |
| }
 | |
| 
 | |
| func (n *node) isBeingDeleted() bool {
 | |
| 	n.beingDeletedLock.RLock()
 | |
| 	defer n.beingDeletedLock.RUnlock()
 | |
| 	return n.beingDeleted
 | |
| }
 | |
| 
 | |
| func (n *node) markObserved() {
 | |
| 	n.virtualLock.Lock()
 | |
| 	defer n.virtualLock.Unlock()
 | |
| 	n.virtual = false
 | |
| }
 | |
| func (n *node) isObserved() bool {
 | |
| 	n.virtualLock.RLock()
 | |
| 	defer n.virtualLock.RUnlock()
 | |
| 	return !n.virtual
 | |
| }
 | |
| 
 | |
| func (n *node) markDeletingDependents() {
 | |
| 	n.deletingDependentsLock.Lock()
 | |
| 	defer n.deletingDependentsLock.Unlock()
 | |
| 	n.deletingDependents = true
 | |
| }
 | |
| 
 | |
| func (n *node) isDeletingDependents() bool {
 | |
| 	n.deletingDependentsLock.RLock()
 | |
| 	defer n.deletingDependentsLock.RUnlock()
 | |
| 	return n.deletingDependents
 | |
| }
 | |
| 
 | |
| func (n *node) addDependent(dependent *node) {
 | |
| 	n.dependentsLock.Lock()
 | |
| 	defer n.dependentsLock.Unlock()
 | |
| 	n.dependents[dependent] = struct{}{}
 | |
| }
 | |
| 
 | |
| func (n *node) deleteDependent(dependent *node) {
 | |
| 	n.dependentsLock.Lock()
 | |
| 	defer n.dependentsLock.Unlock()
 | |
| 	delete(n.dependents, dependent)
 | |
| }
 | |
| 
 | |
| func (n *node) dependentsLength() int {
 | |
| 	n.dependentsLock.RLock()
 | |
| 	defer n.dependentsLock.RUnlock()
 | |
| 	return len(n.dependents)
 | |
| }
 | |
| 
 | |
| // Note that this function does not provide any synchronization guarantees;
 | |
| // items could be added to or removed from ownerNode.dependents the moment this
 | |
| // function returns.
 | |
| func (n *node) getDependents() []*node {
 | |
| 	n.dependentsLock.RLock()
 | |
| 	defer n.dependentsLock.RUnlock()
 | |
| 	var ret []*node
 | |
| 	for dep := range n.dependents {
 | |
| 		ret = append(ret, dep)
 | |
| 	}
 | |
| 	return ret
 | |
| }
 | |
| 
 | |
| // blockingDependents returns the dependents that are blocking the deletion of
 | |
| // n, i.e., the dependent that has an ownerReference pointing to n, and
 | |
| // the BlockOwnerDeletion field of that ownerReference is true.
 | |
| // Note that this function does not provide any synchronization guarantees;
 | |
| // items could be added to or removed from ownerNode.dependents the moment this
 | |
| // function returns.
 | |
| func (n *node) blockingDependents() []*node {
 | |
| 	dependents := n.getDependents()
 | |
| 	var ret []*node
 | |
| 	for _, dep := range dependents {
 | |
| 		for _, owner := range dep.owners {
 | |
| 			if owner.UID == n.identity.UID && owner.BlockOwnerDeletion != nil && *owner.BlockOwnerDeletion {
 | |
| 				ret = append(ret, dep)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return ret
 | |
| }
 | |
| 
 | |
| // ownerReferenceCoordinates returns an owner reference containing only the coordinate fields
 | |
| // from the input reference (uid, name, kind, apiVersion)
 | |
| func ownerReferenceCoordinates(ref metav1.OwnerReference) metav1.OwnerReference {
 | |
| 	return metav1.OwnerReference{
 | |
| 		UID:        ref.UID,
 | |
| 		Name:       ref.Name,
 | |
| 		Kind:       ref.Kind,
 | |
| 		APIVersion: ref.APIVersion,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // ownerReferenceMatchesCoordinates returns true if all of the coordinate fields match
 | |
| // between the two references (uid, name, kind, apiVersion)
 | |
| func ownerReferenceMatchesCoordinates(a, b metav1.OwnerReference) bool {
 | |
| 	return a.UID == b.UID && a.Name == b.Name && a.Kind == b.Kind && a.APIVersion == b.APIVersion
 | |
| }
 | |
| 
 | |
| // String renders node as a string using fmt. Acquires a read lock to ensure the
 | |
| // reflective dump of dependents doesn't race with any concurrent writes.
 | |
| func (n *node) String() string {
 | |
| 	n.dependentsLock.RLock()
 | |
| 	defer n.dependentsLock.RUnlock()
 | |
| 	return fmt.Sprintf("%#v", n)
 | |
| }
 | |
| 
 | |
| type concurrentUIDToNode struct {
 | |
| 	uidToNodeLock sync.RWMutex
 | |
| 	uidToNode     map[types.UID]*node
 | |
| }
 | |
| 
 | |
| func (m *concurrentUIDToNode) Write(node *node) {
 | |
| 	m.uidToNodeLock.Lock()
 | |
| 	defer m.uidToNodeLock.Unlock()
 | |
| 	m.uidToNode[node.identity.UID] = node
 | |
| }
 | |
| 
 | |
| func (m *concurrentUIDToNode) Read(uid types.UID) (*node, bool) {
 | |
| 	m.uidToNodeLock.RLock()
 | |
| 	defer m.uidToNodeLock.RUnlock()
 | |
| 	n, ok := m.uidToNode[uid]
 | |
| 	return n, ok
 | |
| }
 | |
| 
 | |
| func (m *concurrentUIDToNode) Delete(uid types.UID) {
 | |
| 	m.uidToNodeLock.Lock()
 | |
| 	defer m.uidToNodeLock.Unlock()
 | |
| 	delete(m.uidToNode, uid)
 | |
| }
 |