mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-11-04 07:49:35 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			381 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			381 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
Copyright 2022 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 selectors
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"strings"
 | 
						|
	"sync"
 | 
						|
 | 
						|
	pkglabels "k8s.io/apimachinery/pkg/labels"
 | 
						|
)
 | 
						|
 | 
						|
// BiMultimap is an efficient, bi-directional mapping of object
 | 
						|
// keys. Associations are created by putting keys with a selector.
 | 
						|
type BiMultimap struct {
 | 
						|
	mux sync.RWMutex
 | 
						|
 | 
						|
	// Objects.
 | 
						|
	labeledObjects   map[Key]*labeledObject
 | 
						|
	selectingObjects map[Key]*selectingObject
 | 
						|
 | 
						|
	// Associations.
 | 
						|
	labeledBySelecting map[selectorKey]*labeledObjects
 | 
						|
	selectingByLabeled map[labelsKey]*selectingObjects
 | 
						|
}
 | 
						|
 | 
						|
// NewBiMultimap creates a map.
 | 
						|
func NewBiMultimap() *BiMultimap {
 | 
						|
	return &BiMultimap{
 | 
						|
		labeledObjects:     make(map[Key]*labeledObject),
 | 
						|
		selectingObjects:   make(map[Key]*selectingObject),
 | 
						|
		labeledBySelecting: make(map[selectorKey]*labeledObjects),
 | 
						|
		selectingByLabeled: make(map[labelsKey]*selectingObjects),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Key is a tuple of name and namespace.
 | 
						|
type Key struct {
 | 
						|
	Name      string
 | 
						|
	Namespace string
 | 
						|
}
 | 
						|
 | 
						|
// Parse turns a string in the format namespace/name into a Key.
 | 
						|
func Parse(s string) (key Key) {
 | 
						|
	ns := strings.SplitN(s, "/", 2)
 | 
						|
	if len(ns) == 2 {
 | 
						|
		key.Namespace = ns[0]
 | 
						|
		key.Name = ns[1]
 | 
						|
	} else {
 | 
						|
		key.Name = ns[0]
 | 
						|
	}
 | 
						|
	return key
 | 
						|
}
 | 
						|
 | 
						|
func (k Key) String() string {
 | 
						|
	return fmt.Sprintf("%v/%v", k.Namespace, k.Name)
 | 
						|
}
 | 
						|
 | 
						|
type selectorKey struct {
 | 
						|
	key       string
 | 
						|
	namespace string
 | 
						|
}
 | 
						|
 | 
						|
type selectingObject struct {
 | 
						|
	key      Key
 | 
						|
	selector pkglabels.Selector
 | 
						|
	// selectorKey is a stable serialization of selector for
 | 
						|
	// association caching.
 | 
						|
	selectorKey selectorKey
 | 
						|
}
 | 
						|
 | 
						|
type selectingObjects struct {
 | 
						|
	objects  map[Key]*selectingObject
 | 
						|
	refCount int
 | 
						|
}
 | 
						|
 | 
						|
type labelsKey struct {
 | 
						|
	key       string
 | 
						|
	namespace string
 | 
						|
}
 | 
						|
 | 
						|
type labeledObject struct {
 | 
						|
	key    Key
 | 
						|
	labels map[string]string
 | 
						|
	// labelsKey is a stable serialization of labels for association
 | 
						|
	// caching.
 | 
						|
	labelsKey labelsKey
 | 
						|
}
 | 
						|
 | 
						|
type labeledObjects struct {
 | 
						|
	objects  map[Key]*labeledObject
 | 
						|
	refCount int
 | 
						|
}
 | 
						|
 | 
						|
// Put inserts or updates an object and the incoming associations
 | 
						|
// based on the object labels.
 | 
						|
func (m *BiMultimap) Put(key Key, labels map[string]string) {
 | 
						|
	m.mux.Lock()
 | 
						|
	defer m.mux.Unlock()
 | 
						|
 | 
						|
	labelsKey := labelsKey{
 | 
						|
		key:       pkglabels.Set(labels).String(),
 | 
						|
		namespace: key.Namespace,
 | 
						|
	}
 | 
						|
	if l, ok := m.labeledObjects[key]; ok {
 | 
						|
		// Update labeled object.
 | 
						|
		if labelsKey == l.labelsKey {
 | 
						|
			// No change to labels.
 | 
						|
			return
 | 
						|
		}
 | 
						|
		// Delete before readding.
 | 
						|
		m.delete(key)
 | 
						|
	}
 | 
						|
	// Add labeled object.
 | 
						|
	labels = copyLabels(labels)
 | 
						|
	labeledObject := &labeledObject{
 | 
						|
		key:       key,
 | 
						|
		labels:    labels,
 | 
						|
		labelsKey: labelsKey,
 | 
						|
	}
 | 
						|
	m.labeledObjects[key] = labeledObject
 | 
						|
	// Add associations.
 | 
						|
	if _, ok := m.selectingByLabeled[labelsKey]; !ok {
 | 
						|
		// Cache miss. Scan selecting objects.
 | 
						|
		selecting := &selectingObjects{
 | 
						|
			objects: make(map[Key]*selectingObject),
 | 
						|
		}
 | 
						|
		set := pkglabels.Set(labels)
 | 
						|
		for _, s := range m.selectingObjects {
 | 
						|
			if s.key.Namespace != key.Namespace {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			if s.selector.Matches(set) {
 | 
						|
				selecting.objects[s.key] = s
 | 
						|
			}
 | 
						|
		}
 | 
						|
		// Associate selecting with labeled.
 | 
						|
		m.selectingByLabeled[labelsKey] = selecting
 | 
						|
	}
 | 
						|
	selecting := m.selectingByLabeled[labelsKey]
 | 
						|
	selecting.refCount++
 | 
						|
	for _, sObject := range selecting.objects {
 | 
						|
		// Associate labeled with selecting.
 | 
						|
		labeled := m.labeledBySelecting[sObject.selectorKey]
 | 
						|
		labeled.objects[labeledObject.key] = labeledObject
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Delete removes a labeled object and incoming associations.
 | 
						|
func (m *BiMultimap) Delete(key Key) {
 | 
						|
	m.mux.Lock()
 | 
						|
	defer m.mux.Unlock()
 | 
						|
	m.delete(key)
 | 
						|
}
 | 
						|
 | 
						|
func (m *BiMultimap) delete(key Key) {
 | 
						|
	if _, ok := m.labeledObjects[key]; !ok {
 | 
						|
		// Does not exist.
 | 
						|
		return
 | 
						|
	}
 | 
						|
	labeledObject := m.labeledObjects[key]
 | 
						|
	labelsKey := labeledObject.labelsKey
 | 
						|
	defer delete(m.labeledObjects, key)
 | 
						|
	if _, ok := m.selectingByLabeled[labelsKey]; !ok {
 | 
						|
		// No associations.
 | 
						|
		return
 | 
						|
	}
 | 
						|
	// Remove associations.
 | 
						|
	for _, selectingObject := range m.selectingByLabeled[labelsKey].objects {
 | 
						|
		selectorKey := selectingObject.selectorKey
 | 
						|
		// Delete selectingObject to labeledObject association.
 | 
						|
		delete(m.labeledBySelecting[selectorKey].objects, key)
 | 
						|
	}
 | 
						|
	m.selectingByLabeled[labelsKey].refCount--
 | 
						|
	// Garbage collect labeledObject to selectingObject associations.
 | 
						|
	if m.selectingByLabeled[labelsKey].refCount == 0 {
 | 
						|
		delete(m.selectingByLabeled, labelsKey)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Exists returns true if the labeled object is present in the map.
 | 
						|
func (m *BiMultimap) Exists(key Key) bool {
 | 
						|
	m.mux.RLock()
 | 
						|
	defer m.mux.RUnlock()
 | 
						|
 | 
						|
	_, exists := m.labeledObjects[key]
 | 
						|
	return exists
 | 
						|
}
 | 
						|
 | 
						|
// PutSelector inserts or updates an object with a selector. Associations
 | 
						|
// are created or updated based on the selector.
 | 
						|
func (m *BiMultimap) PutSelector(key Key, selector pkglabels.Selector) {
 | 
						|
	m.mux.Lock()
 | 
						|
	defer m.mux.Unlock()
 | 
						|
 | 
						|
	selectorKey := selectorKey{
 | 
						|
		key:       selector.String(),
 | 
						|
		namespace: key.Namespace,
 | 
						|
	}
 | 
						|
	if s, ok := m.selectingObjects[key]; ok {
 | 
						|
		// Update selecting object.
 | 
						|
		if selectorKey == s.selectorKey {
 | 
						|
			// No change to selector.
 | 
						|
			return
 | 
						|
		}
 | 
						|
		// Delete before readding.
 | 
						|
		m.deleteSelector(key)
 | 
						|
	}
 | 
						|
	// Add selecting object.
 | 
						|
	selectingObject := &selectingObject{
 | 
						|
		key:         key,
 | 
						|
		selector:    selector,
 | 
						|
		selectorKey: selectorKey,
 | 
						|
	}
 | 
						|
	m.selectingObjects[key] = selectingObject
 | 
						|
	// Add associations.
 | 
						|
	if _, ok := m.labeledBySelecting[selectorKey]; !ok {
 | 
						|
		// Cache miss. Scan labeled objects.
 | 
						|
		labeled := &labeledObjects{
 | 
						|
			objects: make(map[Key]*labeledObject),
 | 
						|
		}
 | 
						|
		for _, l := range m.labeledObjects {
 | 
						|
			if l.key.Namespace != key.Namespace {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			set := pkglabels.Set(l.labels)
 | 
						|
			if selector.Matches(set) {
 | 
						|
				labeled.objects[l.key] = l
 | 
						|
			}
 | 
						|
		}
 | 
						|
		// Associate labeled with selecting.
 | 
						|
		m.labeledBySelecting[selectorKey] = labeled
 | 
						|
	}
 | 
						|
	labeled := m.labeledBySelecting[selectorKey]
 | 
						|
	labeled.refCount++
 | 
						|
	for _, labeledObject := range labeled.objects {
 | 
						|
		// Associate selecting with labeled.
 | 
						|
		selecting := m.selectingByLabeled[labeledObject.labelsKey]
 | 
						|
		selecting.objects[selectingObject.key] = selectingObject
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// DeleteSelector deletes a selecting object and associations created by its
 | 
						|
// selector.
 | 
						|
func (m *BiMultimap) DeleteSelector(key Key) {
 | 
						|
	m.mux.Lock()
 | 
						|
	defer m.mux.Unlock()
 | 
						|
	m.deleteSelector(key)
 | 
						|
}
 | 
						|
 | 
						|
func (m *BiMultimap) deleteSelector(key Key) {
 | 
						|
	if _, ok := m.selectingObjects[key]; !ok {
 | 
						|
		// Does not exist.
 | 
						|
		return
 | 
						|
	}
 | 
						|
	selectingObject := m.selectingObjects[key]
 | 
						|
	selectorKey := selectingObject.selectorKey
 | 
						|
	defer delete(m.selectingObjects, key)
 | 
						|
	if _, ok := m.labeledBySelecting[selectorKey]; !ok {
 | 
						|
		// No associations.
 | 
						|
		return
 | 
						|
	}
 | 
						|
	// Remove associations.
 | 
						|
	for _, labeledObject := range m.labeledBySelecting[selectorKey].objects {
 | 
						|
		labelsKey := labeledObject.labelsKey
 | 
						|
		// Delete labeledObject to selectingObject association.
 | 
						|
		delete(m.selectingByLabeled[labelsKey].objects, key)
 | 
						|
	}
 | 
						|
	m.labeledBySelecting[selectorKey].refCount--
 | 
						|
	// Garbage collect selectingObjects to labeledObject associations.
 | 
						|
	if m.labeledBySelecting[selectorKey].refCount == 0 {
 | 
						|
		delete(m.labeledBySelecting, selectorKey)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// SelectorExists returns true if the selecting object is present in the map.
 | 
						|
func (m *BiMultimap) SelectorExists(key Key) bool {
 | 
						|
	m.mux.RLock()
 | 
						|
	defer m.mux.RUnlock()
 | 
						|
 | 
						|
	_, exists := m.selectingObjects[key]
 | 
						|
	return exists
 | 
						|
}
 | 
						|
 | 
						|
// KeepOnly retains only the specified labeled objects and deletes the
 | 
						|
// rest. Like calling Delete for all keys not specified.
 | 
						|
func (m *BiMultimap) KeepOnly(keys []Key) {
 | 
						|
	m.mux.Lock()
 | 
						|
	defer m.mux.Unlock()
 | 
						|
 | 
						|
	keyMap := make(map[Key]bool)
 | 
						|
	for _, k := range keys {
 | 
						|
		keyMap[k] = true
 | 
						|
	}
 | 
						|
	for k := range m.labeledObjects {
 | 
						|
		if !keyMap[k] {
 | 
						|
			m.delete(k)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// KeepOnlySelectors retains only the specified selecting objects and
 | 
						|
// deletes the rest. Like calling DeleteSelector for all keys not
 | 
						|
// specified.
 | 
						|
func (m *BiMultimap) KeepOnlySelectors(keys []Key) {
 | 
						|
	m.mux.Lock()
 | 
						|
	defer m.mux.Unlock()
 | 
						|
 | 
						|
	keyMap := make(map[Key]bool)
 | 
						|
	for _, k := range keys {
 | 
						|
		keyMap[k] = true
 | 
						|
	}
 | 
						|
	for k := range m.selectingObjects {
 | 
						|
		if !keyMap[k] {
 | 
						|
			m.deleteSelector(k)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Select finds objects associated with a selecting object. If the
 | 
						|
// given key was found in the map `ok` will be true. Otherwise false.
 | 
						|
func (m *BiMultimap) Select(key Key) (keys []Key, ok bool) {
 | 
						|
	m.mux.RLock()
 | 
						|
	defer m.mux.RUnlock()
 | 
						|
 | 
						|
	selectingObject, ok := m.selectingObjects[key]
 | 
						|
	if !ok {
 | 
						|
		// Does not exist.
 | 
						|
		return nil, false
 | 
						|
	}
 | 
						|
	keys = make([]Key, 0)
 | 
						|
	if labeled, ok := m.labeledBySelecting[selectingObject.selectorKey]; ok {
 | 
						|
		for _, labeledObject := range labeled.objects {
 | 
						|
			keys = append(keys, labeledObject.key)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return keys, true
 | 
						|
}
 | 
						|
 | 
						|
// ReverseSelect finds objects selecting the given object. If the
 | 
						|
// given key was found in the map `ok` will be true. Otherwise false.
 | 
						|
func (m *BiMultimap) ReverseSelect(key Key) (keys []Key, ok bool) {
 | 
						|
	m.mux.RLock()
 | 
						|
	defer m.mux.RUnlock()
 | 
						|
 | 
						|
	labeledObject, ok := m.labeledObjects[key]
 | 
						|
	if !ok {
 | 
						|
		// Does not exist.
 | 
						|
		return []Key{}, false
 | 
						|
	}
 | 
						|
	keys = make([]Key, 0)
 | 
						|
	if selecting, ok := m.selectingByLabeled[labeledObject.labelsKey]; ok {
 | 
						|
		for _, selectingObject := range selecting.objects {
 | 
						|
			keys = append(keys, selectingObject.key)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return keys, true
 | 
						|
}
 | 
						|
 | 
						|
func copyLabels(labels map[string]string) map[string]string {
 | 
						|
	l := make(map[string]string)
 | 
						|
	for k, v := range labels {
 | 
						|
		l[k] = v
 | 
						|
	}
 | 
						|
	return l
 | 
						|
}
 |