mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			171 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			171 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
Copyright 2024 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 leaderelection
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/blang/semver/v4"
 | 
						|
	v1 "k8s.io/api/coordination/v1"
 | 
						|
	v1alpha1 "k8s.io/api/coordination/v1alpha1"
 | 
						|
	"k8s.io/klog/v2"
 | 
						|
	"k8s.io/utils/clock"
 | 
						|
)
 | 
						|
 | 
						|
func pickBestLeaderOldestEmulationVersion(candidates []*v1alpha1.LeaseCandidate) *v1alpha1.LeaseCandidate {
 | 
						|
	var electee *v1alpha1.LeaseCandidate
 | 
						|
	for _, c := range candidates {
 | 
						|
		if !validLeaseCandidateForOldestEmulationVersion(c) {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		if electee == nil || compare(electee, c) > 0 {
 | 
						|
			electee = c
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if electee == nil {
 | 
						|
		klog.Infof("pickBestLeader: none found")
 | 
						|
	} else {
 | 
						|
		klog.Infof("pickBestLeader: %s %s", electee.Namespace, electee.Name)
 | 
						|
	}
 | 
						|
	return electee
 | 
						|
}
 | 
						|
 | 
						|
// topologicalSortWithOneRoot has a caveat that there may only be one root (indegree=0) node in a valid ordering.
 | 
						|
func topologicalSortWithOneRoot(graph map[v1.CoordinatedLeaseStrategy][]v1.CoordinatedLeaseStrategy) []v1.CoordinatedLeaseStrategy {
 | 
						|
	inDegree := make(map[v1.CoordinatedLeaseStrategy]int)
 | 
						|
	for node := range graph {
 | 
						|
		inDegree[node] = 0
 | 
						|
	}
 | 
						|
	for _, neighbors := range graph {
 | 
						|
		for _, neighbor := range neighbors {
 | 
						|
			inDegree[neighbor]++
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	var queue []v1.CoordinatedLeaseStrategy
 | 
						|
	for vertex, degree := range inDegree {
 | 
						|
		if degree == 0 {
 | 
						|
			queue = append(queue, vertex)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// If multiple nodes have indegree of 0, multiple strategies are non-superceding and is a conflict.
 | 
						|
	if len(queue) > 1 {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	var sorted []v1.CoordinatedLeaseStrategy
 | 
						|
	for len(queue) > 0 {
 | 
						|
		vertex := queue[0]
 | 
						|
		queue = queue[1:]
 | 
						|
		sorted = append(sorted, vertex)
 | 
						|
 | 
						|
		for _, neighbor := range graph[vertex] {
 | 
						|
			inDegree[neighbor]--
 | 
						|
			if inDegree[neighbor] == 0 {
 | 
						|
				queue = append(queue, neighbor)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if len(sorted) != len(graph) {
 | 
						|
		return nil // Cycle detected
 | 
						|
	}
 | 
						|
 | 
						|
	return sorted
 | 
						|
}
 | 
						|
 | 
						|
func pickBestStrategy(candidates []*v1alpha1.LeaseCandidate) (v1.CoordinatedLeaseStrategy, error) {
 | 
						|
	graph := make(map[v1.CoordinatedLeaseStrategy][]v1.CoordinatedLeaseStrategy)
 | 
						|
	nilStrategy := v1.CoordinatedLeaseStrategy("")
 | 
						|
	for _, c := range candidates {
 | 
						|
		for i := range len(c.Spec.PreferredStrategies) - 1 {
 | 
						|
			graph[c.Spec.PreferredStrategies[i]] = append(graph[c.Spec.PreferredStrategies[i]], c.Spec.PreferredStrategies[i+1])
 | 
						|
		}
 | 
						|
		if _, ok := graph[c.Spec.PreferredStrategies[len(c.Spec.PreferredStrategies)-1]]; !ok {
 | 
						|
			graph[c.Spec.PreferredStrategies[len(c.Spec.PreferredStrategies)-1]] = []v1.CoordinatedLeaseStrategy{}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	sorted := topologicalSortWithOneRoot(graph)
 | 
						|
	if sorted == nil {
 | 
						|
		return nilStrategy, fmt.Errorf("invalid strategy")
 | 
						|
	}
 | 
						|
 | 
						|
	return sorted[0], nil
 | 
						|
}
 | 
						|
 | 
						|
func validLeaseCandidateForOldestEmulationVersion(l *v1alpha1.LeaseCandidate) bool {
 | 
						|
	_, err := semver.ParseTolerant(l.Spec.EmulationVersion)
 | 
						|
	if err != nil {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
	_, err = semver.ParseTolerant(l.Spec.BinaryVersion)
 | 
						|
	return err == nil
 | 
						|
}
 | 
						|
 | 
						|
func getEmulationVersionOrZero(l *v1alpha1.LeaseCandidate) semver.Version {
 | 
						|
	value := l.Spec.EmulationVersion
 | 
						|
	v, err := semver.ParseTolerant(value)
 | 
						|
	if err != nil {
 | 
						|
		return semver.Version{}
 | 
						|
	}
 | 
						|
	return v
 | 
						|
}
 | 
						|
 | 
						|
func getBinaryVersionOrZero(l *v1alpha1.LeaseCandidate) semver.Version {
 | 
						|
	value := l.Spec.BinaryVersion
 | 
						|
	v, err := semver.ParseTolerant(value)
 | 
						|
	if err != nil {
 | 
						|
		return semver.Version{}
 | 
						|
	}
 | 
						|
	return v
 | 
						|
}
 | 
						|
 | 
						|
// -1: lhs better, 1: rhs better
 | 
						|
func compare(lhs, rhs *v1alpha1.LeaseCandidate) int {
 | 
						|
	l := getEmulationVersionOrZero(lhs)
 | 
						|
	r := getEmulationVersionOrZero(rhs)
 | 
						|
	result := l.Compare(r)
 | 
						|
	if result == 0 {
 | 
						|
		l := getBinaryVersionOrZero(lhs)
 | 
						|
		r := getBinaryVersionOrZero(rhs)
 | 
						|
		result = l.Compare(r)
 | 
						|
	}
 | 
						|
	if result == 0 {
 | 
						|
		if lhs.CreationTimestamp.After(rhs.CreationTimestamp.Time) {
 | 
						|
			return 1
 | 
						|
		}
 | 
						|
		return -1
 | 
						|
	}
 | 
						|
	return result
 | 
						|
}
 | 
						|
 | 
						|
func isLeaseExpired(clock clock.Clock, lease *v1.Lease) bool {
 | 
						|
	currentTime := clock.Now()
 | 
						|
	return lease.Spec.RenewTime == nil ||
 | 
						|
		lease.Spec.LeaseDurationSeconds == nil ||
 | 
						|
		lease.Spec.RenewTime.Add(time.Duration(*lease.Spec.LeaseDurationSeconds)*time.Second).Before(currentTime)
 | 
						|
}
 | 
						|
 | 
						|
func isLeaseCandidateExpired(clock clock.Clock, lease *v1alpha1.LeaseCandidate) bool {
 | 
						|
	currentTime := clock.Now()
 | 
						|
	return lease.Spec.RenewTime == nil ||
 | 
						|
		lease.Spec.RenewTime.Add(leaseCandidateValidDuration).Before(currentTime)
 | 
						|
}
 |