| /* | |
| 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 debugger | |
| import ( | |
| "sort" | |
| "strings" | |
| "k8s.io/api/core/v1" | |
| "k8s.io/apimachinery/pkg/labels" | |
| corelisters "k8s.io/client-go/listers/core/v1" | |
| "k8s.io/klog/v2" | |
| "k8s.io/kubernetes/pkg/scheduler/framework" | |
| internalcache "k8s.io/kubernetes/pkg/scheduler/internal/cache" | |
| internalqueue "k8s.io/kubernetes/pkg/scheduler/internal/queue" | |
| ) | |
| // CacheComparer is an implementation of the Scheduler's cache comparer. | |
| type CacheComparer struct { | |
| NodeLister corelisters.NodeLister | |
| PodLister corelisters.PodLister | |
| Cache internalcache.Cache | |
| PodQueue internalqueue.SchedulingQueue | |
| } | |
| // Compare compares the nodes and pods of NodeLister with Cache.Snapshot. | |
| func (c *CacheComparer) Compare(logger klog.Logger) error { | |
| logger.V(3).Info("Cache comparer started") | |
| defer logger.V(3).Info("Cache comparer finished") | |
| nodes, err := c.NodeLister.List(labels.Everything()) | |
| if err != nil { | |
| return err | |
| } | |
| pods, err := c.PodLister.List(labels.Everything()) | |
| if err != nil { | |
| return err | |
| } | |
| dump := c.Cache.Dump() | |
| pendingPods, _ := c.PodQueue.PendingPods() | |
| if missed, redundant := c.CompareNodes(nodes, dump.Nodes); len(missed)+len(redundant) != 0 { | |
| logger.Info("Cache mismatch", "missedNodes", missed, "redundantNodes", redundant) | |
| } | |
| if missed, redundant := c.ComparePods(pods, pendingPods, dump.Nodes); len(missed)+len(redundant) != 0 { | |
| logger.Info("Cache mismatch", "missedPods", missed, "redundantPods", redundant) | |
| } | |
| return nil | |
| } | |
| // CompareNodes compares actual nodes with cached nodes. | |
| func (c *CacheComparer) CompareNodes(nodes []*v1.Node, nodeinfos map[string]*framework.NodeInfo) (missed, redundant []string) { | |
| actual := []string{} | |
| for _, node := range nodes { | |
| actual = append(actual, node.Name) | |
| } | |
| cached := []string{} | |
| for nodeName := range nodeinfos { | |
| cached = append(cached, nodeName) | |
| } | |
| return compareStrings(actual, cached) | |
| } | |
| // ComparePods compares actual pods with cached pods. | |
| func (c *CacheComparer) ComparePods(pods, waitingPods []*v1.Pod, nodeinfos map[string]*framework.NodeInfo) (missed, redundant []string) { | |
| actual := []string{} | |
| for _, pod := range pods { | |
| actual = append(actual, string(pod.UID)) | |
| } | |
| cached := []string{} | |
| for _, nodeinfo := range nodeinfos { | |
| for _, p := range nodeinfo.Pods { | |
| cached = append(cached, string(p.Pod.UID)) | |
| } | |
| } | |
| for _, pod := range waitingPods { | |
| cached = append(cached, string(pod.UID)) | |
| } | |
| return compareStrings(actual, cached) | |
| } | |
| func compareStrings(actual, cached []string) (missed, redundant []string) { | |
| missed, redundant = []string{}, []string{} | |
| sort.Strings(actual) | |
| sort.Strings(cached) | |
| compare := func(i, j int) int { | |
| if i == len(actual) { | |
| return 1 | |
| } else if j == len(cached) { | |
| return -1 | |
| } | |
| return strings.Compare(actual[i], cached[j]) | |
| } | |
| for i, j := 0, 0; i < len(actual) || j < len(cached); { | |
| switch compare(i, j) { | |
| case 0: | |
| i++ | |
| j++ | |
| case -1: | |
| missed = append(missed, actual[i]) | |
| i++ | |
| case 1: | |
| redundant = append(redundant, cached[j]) | |
| j++ | |
| } | |
| } | |
| return | |
| } |