Clean up names in equivalence package.

Remove stutter from names and provide more idiomatic patterns.

This makes call sites that use equivalence cache easier to read.
This commit is contained in:
Jonathan Basseri 2018-05-23 16:42:30 -07:00
parent 31c746d960
commit b571065bc4
6 changed files with 92 additions and 94 deletions

View File

@ -31,19 +31,51 @@ import (
"github.com/golang/glog" "github.com/golang/glog"
) )
// EquivalenceCache saves and reuses the output of predicate functions. Use // Cache saves and reuses the output of predicate functions. Use RunPredicate to
// RunPredicate to get or update the cached results. An appropriate Invalidate* // get or update the cached results. An appropriate Invalidate* function should
// function should be called when some predicate results are no longer valid. // be called when some predicate results are no longer valid.
// //
// Internally, results are keyed by node name, predicate name, and "equivalence // Internally, results are keyed by node name, predicate name, and "equivalence
// class". (Equivalence class is defined in the `EquivalenceClassInfo` type.) // class". (Equivalence class is defined in the `Class` type.) Saved results
// Saved results will be reused until an appropriate invalidation function is // will be reused until an appropriate invalidation function is called.
// called. type Cache struct {
type EquivalenceCache struct {
mu sync.RWMutex mu sync.RWMutex
cache nodeMap cache nodeMap
} }
// NewCache returns an empty Cache.
func NewCache() *Cache {
return &Cache{
cache: make(nodeMap),
}
}
// Class represents a set of pods which are equivalent from the perspective of
// the scheduler. i.e. the scheduler would make the same decision for any pod
// from the same class.
type Class struct {
// Equivalence hash
hash uint64
}
// NewClass returns the equivalence class for a given Pod. The returned Class
// objects will be equal for two Pods in the same class. nil values should not
// be considered equal to each other.
//
// NOTE: Make sure to compare types of Class and not *Class.
// TODO(misterikkit): Return error instead of nil *Class.
func NewClass(pod *v1.Pod) *Class {
equivalencePod := getEquivalencePod(pod)
if equivalencePod != nil {
hash := fnv.New32a()
hashutil.DeepHashObject(hash, equivalencePod)
return &Class{
hash: uint64(hash.Sum32()),
}
}
return nil
}
// nodeMap stores PredicateCaches with node name as the key. // nodeMap stores PredicateCaches with node name as the key.
type nodeMap map[string]predicateMap type nodeMap map[string]predicateMap
@ -59,25 +91,17 @@ type predicateResult struct {
FailReasons []algorithm.PredicateFailureReason FailReasons []algorithm.PredicateFailureReason
} }
// NewEquivalenceCache returns EquivalenceCache to speed up predicates by caching
// result from previous scheduling.
func NewEquivalenceCache() *EquivalenceCache {
return &EquivalenceCache{
cache: make(nodeMap),
}
}
// RunPredicate returns a cached predicate result. In case of a cache miss, the predicate will be // RunPredicate returns a cached predicate result. In case of a cache miss, the predicate will be
// run and its results cached for the next call. // run and its results cached for the next call.
// //
// NOTE: RunPredicate will not update the equivalence cache if the given NodeInfo is stale. // NOTE: RunPredicate will not update the equivalence cache if the given NodeInfo is stale.
func (ec *EquivalenceCache) RunPredicate( func (c *Cache) RunPredicate(
pred algorithm.FitPredicate, pred algorithm.FitPredicate,
predicateKey string, predicateKey string,
pod *v1.Pod, pod *v1.Pod,
meta algorithm.PredicateMetadata, meta algorithm.PredicateMetadata,
nodeInfo *schedulercache.NodeInfo, nodeInfo *schedulercache.NodeInfo,
equivClassInfo *EquivalenceClassInfo, equivClass *Class,
cache schedulercache.Cache, cache schedulercache.Cache,
) (bool, []algorithm.PredicateFailureReason, error) { ) (bool, []algorithm.PredicateFailureReason, error) {
if nodeInfo == nil || nodeInfo.Node() == nil { if nodeInfo == nil || nodeInfo.Node() == nil {
@ -85,7 +109,7 @@ func (ec *EquivalenceCache) RunPredicate(
return false, []algorithm.PredicateFailureReason{}, fmt.Errorf("nodeInfo is nil or node is invalid") return false, []algorithm.PredicateFailureReason{}, fmt.Errorf("nodeInfo is nil or node is invalid")
} }
result, ok := ec.lookupResult(pod.GetName(), nodeInfo.Node().GetName(), predicateKey, equivClassInfo.hash) result, ok := c.lookupResult(pod.GetName(), nodeInfo.Node().GetName(), predicateKey, equivClass.hash)
if ok { if ok {
return result.Fit, result.FailReasons, nil return result.Fit, result.FailReasons, nil
} }
@ -94,13 +118,13 @@ func (ec *EquivalenceCache) RunPredicate(
return fit, reasons, err return fit, reasons, err
} }
if cache != nil { if cache != nil {
ec.updateResult(pod.GetName(), predicateKey, fit, reasons, equivClassInfo.hash, cache, nodeInfo) c.updateResult(pod.GetName(), predicateKey, fit, reasons, equivClass.hash, cache, nodeInfo)
} }
return fit, reasons, nil return fit, reasons, nil
} }
// updateResult updates the cached result of a predicate. // updateResult updates the cached result of a predicate.
func (ec *EquivalenceCache) updateResult( func (c *Cache) updateResult(
podName, predicateKey string, podName, predicateKey string,
fit bool, fit bool,
reasons []algorithm.PredicateFailureReason, reasons []algorithm.PredicateFailureReason,
@ -108,8 +132,8 @@ func (ec *EquivalenceCache) updateResult(
cache schedulercache.Cache, cache schedulercache.Cache,
nodeInfo *schedulercache.NodeInfo, nodeInfo *schedulercache.NodeInfo,
) { ) {
ec.mu.Lock() c.mu.Lock()
defer ec.mu.Unlock() defer c.mu.Unlock()
if nodeInfo == nil || nodeInfo.Node() == nil { if nodeInfo == nil || nodeInfo.Node() == nil {
// This may happen during tests. // This may happen during tests.
return return
@ -119,19 +143,19 @@ func (ec *EquivalenceCache) updateResult(
return return
} }
nodeName := nodeInfo.Node().GetName() nodeName := nodeInfo.Node().GetName()
if _, exist := ec.cache[nodeName]; !exist { if _, exist := c.cache[nodeName]; !exist {
ec.cache[nodeName] = make(predicateMap) c.cache[nodeName] = make(predicateMap)
} }
predicateItem := predicateResult{ predicateItem := predicateResult{
Fit: fit, Fit: fit,
FailReasons: reasons, FailReasons: reasons,
} }
// if cached predicate map already exists, just update the predicate by key // if cached predicate map already exists, just update the predicate by key
if predicates, ok := ec.cache[nodeName][predicateKey]; ok { if predicates, ok := c.cache[nodeName][predicateKey]; ok {
// maps in golang are references, no need to add them back // maps in golang are references, no need to add them back
predicates[equivalenceHash] = predicateItem predicates[equivalenceHash] = predicateItem
} else { } else {
ec.cache[nodeName][predicateKey] = c.cache[nodeName][predicateKey] =
resultMap{ resultMap{
equivalenceHash: predicateItem, equivalenceHash: predicateItem,
} }
@ -141,27 +165,27 @@ func (ec *EquivalenceCache) updateResult(
// lookupResult returns cached predicate results and a bool saying whether a // lookupResult returns cached predicate results and a bool saying whether a
// cache entry was found. // cache entry was found.
func (ec *EquivalenceCache) lookupResult( func (c *Cache) lookupResult(
podName, nodeName, predicateKey string, podName, nodeName, predicateKey string,
equivalenceHash uint64, equivalenceHash uint64,
) (value predicateResult, ok bool) { ) (value predicateResult, ok bool) {
ec.mu.RLock() c.mu.RLock()
defer ec.mu.RUnlock() defer c.mu.RUnlock()
glog.V(5).Infof("Begin to calculate predicate: %v for pod: %s on node: %s based on equivalence cache", glog.V(5).Infof("Begin to calculate predicate: %v for pod: %s on node: %s based on equivalence cache",
predicateKey, podName, nodeName) predicateKey, podName, nodeName)
value, ok = ec.cache[nodeName][predicateKey][equivalenceHash] value, ok = c.cache[nodeName][predicateKey][equivalenceHash]
return value, ok return value, ok
} }
// InvalidatePredicates clears all cached results for the given predicates. // InvalidatePredicates clears all cached results for the given predicates.
func (ec *EquivalenceCache) InvalidatePredicates(predicateKeys sets.String) { func (c *Cache) InvalidatePredicates(predicateKeys sets.String) {
if len(predicateKeys) == 0 { if len(predicateKeys) == 0 {
return return
} }
ec.mu.Lock() c.mu.Lock()
defer ec.mu.Unlock() defer c.mu.Unlock()
// ec.cache uses nodeName as key, so we just iterate it and invalid given predicates // c.cache uses nodeName as key, so we just iterate it and invalid given predicates
for _, predicates := range ec.cache { for _, predicates := range c.cache {
for predicateKey := range predicateKeys { for predicateKey := range predicateKeys {
delete(predicates, predicateKey) delete(predicates, predicateKey)
} }
@ -170,30 +194,30 @@ func (ec *EquivalenceCache) InvalidatePredicates(predicateKeys sets.String) {
} }
// InvalidatePredicatesOnNode clears cached results for the given predicates on one node. // InvalidatePredicatesOnNode clears cached results for the given predicates on one node.
func (ec *EquivalenceCache) InvalidatePredicatesOnNode(nodeName string, predicateKeys sets.String) { func (c *Cache) InvalidatePredicatesOnNode(nodeName string, predicateKeys sets.String) {
if len(predicateKeys) == 0 { if len(predicateKeys) == 0 {
return return
} }
ec.mu.Lock() c.mu.Lock()
defer ec.mu.Unlock() defer c.mu.Unlock()
for predicateKey := range predicateKeys { for predicateKey := range predicateKeys {
delete(ec.cache[nodeName], predicateKey) delete(c.cache[nodeName], predicateKey)
} }
glog.V(5).Infof("Done invalidating cached predicates: %v on node: %s", predicateKeys, nodeName) glog.V(5).Infof("Done invalidating cached predicates: %v on node: %s", predicateKeys, nodeName)
} }
// InvalidateAllPredicatesOnNode clears all cached results for one node. // InvalidateAllPredicatesOnNode clears all cached results for one node.
func (ec *EquivalenceCache) InvalidateAllPredicatesOnNode(nodeName string) { func (c *Cache) InvalidateAllPredicatesOnNode(nodeName string) {
ec.mu.Lock() c.mu.Lock()
defer ec.mu.Unlock() defer c.mu.Unlock()
delete(ec.cache, nodeName) delete(c.cache, nodeName)
glog.V(5).Infof("Done invalidating all cached predicates on node: %s", nodeName) glog.V(5).Infof("Done invalidating all cached predicates on node: %s", nodeName)
} }
// InvalidateCachedPredicateItemForPodAdd is a wrapper of // InvalidateCachedPredicateItemForPodAdd is a wrapper of
// InvalidateCachedPredicateItem for pod add case // InvalidateCachedPredicateItem for pod add case
// TODO: This logic does not belong with the equivalence cache implementation. // TODO: This does not belong with the equivalence cache implementation.
func (ec *EquivalenceCache) InvalidateCachedPredicateItemForPodAdd(pod *v1.Pod, nodeName string) { func (c *Cache) InvalidateCachedPredicateItemForPodAdd(pod *v1.Pod, nodeName string) {
// MatchInterPodAffinity: we assume scheduler can make sure newly bound pod // MatchInterPodAffinity: we assume scheduler can make sure newly bound pod
// will not break the existing inter pod affinity. So we does not need to // will not break the existing inter pod affinity. So we does not need to
// invalidate MatchInterPodAffinity when pod added. // invalidate MatchInterPodAffinity when pod added.
@ -226,30 +250,7 @@ func (ec *EquivalenceCache) InvalidateCachedPredicateItemForPodAdd(pod *v1.Pod,
} }
} }
} }
ec.InvalidatePredicatesOnNode(nodeName, invalidPredicates) c.InvalidatePredicatesOnNode(nodeName, invalidPredicates)
}
// EquivalenceClassInfo holds equivalence hash which is used for checking
// equivalence cache. We will pass this to podFitsOnNode to ensure equivalence
// hash is only calculated per schedule.
type EquivalenceClassInfo struct {
// Equivalence hash.
hash uint64
}
// GetEquivalenceClassInfo returns a hash of the given pod. The hashing function
// returns the same value for any two pods that are equivalent from the
// perspective of scheduling.
func (ec *EquivalenceCache) GetEquivalenceClassInfo(pod *v1.Pod) *EquivalenceClassInfo {
equivalencePod := getEquivalencePod(pod)
if equivalencePod != nil {
hash := fnv.New32a()
hashutil.DeepHashObject(hash, equivalencePod)
return &EquivalenceClassInfo{
hash: uint64(hash.Sum32()),
}
}
return nil
} }
// equivalencePod is the set of pod attributes which must match for two pods to // equivalencePod is the set of pod attributes which must match for two pods to

View File

@ -247,8 +247,8 @@ func TestRunPredicate(t *testing.T) {
pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "p1"}} pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "p1"}}
meta := algorithm.EmptyPredicateMetadataProducer(nil, nil) meta := algorithm.EmptyPredicateMetadataProducer(nil, nil)
ecache := NewEquivalenceCache() ecache := NewCache()
equivClass := ecache.GetEquivalenceClassInfo(pod) equivClass := NewClass(pod)
if test.expectCacheHit { if test.expectCacheHit {
ecache.updateResult(pod.Name, "testPredicate", test.expectFit, test.expectedReasons, equivClass.hash, test.cache, node) ecache.updateResult(pod.Name, "testPredicate", test.expectFit, test.expectedReasons, equivClass.hash, test.cache, node)
} }
@ -339,7 +339,7 @@ func TestUpdateResult(t *testing.T) {
}, },
} }
for _, test := range tests { for _, test := range tests {
ecache := NewEquivalenceCache() ecache := NewCache()
if test.expectPredicateMap { if test.expectPredicateMap {
ecache.cache[test.nodeName] = make(predicateMap) ecache.cache[test.nodeName] = make(predicateMap)
predicateItem := predicateResult{ predicateItem := predicateResult{
@ -473,7 +473,7 @@ func TestLookupResult(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
ecache := NewEquivalenceCache() ecache := NewCache()
node := schedulercache.NewNodeInfo() node := schedulercache.NewNodeInfo()
node.SetNode(&v1.Node{ObjectMeta: metav1.ObjectMeta{Name: test.nodeName}}) node.SetNode(&v1.Node{ObjectMeta: metav1.ObjectMeta{Name: test.nodeName}})
// set cached item to equivalence cache // set cached item to equivalence cache
@ -527,9 +527,6 @@ func TestLookupResult(t *testing.T) {
} }
func TestGetEquivalenceHash(t *testing.T) { func TestGetEquivalenceHash(t *testing.T) {
ecache := NewEquivalenceCache()
pod1 := makeBasicPod("pod1") pod1 := makeBasicPod("pod1")
pod2 := makeBasicPod("pod2") pod2 := makeBasicPod("pod2")
@ -620,7 +617,7 @@ func TestGetEquivalenceHash(t *testing.T) {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
for i, podInfo := range test.podInfoList { for i, podInfo := range test.podInfoList {
testPod := podInfo.pod testPod := podInfo.pod
eclassInfo := ecache.GetEquivalenceClassInfo(testPod) eclassInfo := NewClass(testPod)
if eclassInfo == nil && podInfo.hashIsValid { if eclassInfo == nil && podInfo.hashIsValid {
t.Errorf("Failed: pod %v is expected to have valid hash", testPod) t.Errorf("Failed: pod %v is expected to have valid hash", testPod)
} }
@ -688,7 +685,7 @@ func TestInvalidateCachedPredicateItemOfAllNodes(t *testing.T) {
cache: &upToDateCache{}, cache: &upToDateCache{},
}, },
} }
ecache := NewEquivalenceCache() ecache := NewCache()
for _, test := range tests { for _, test := range tests {
node := schedulercache.NewNodeInfo() node := schedulercache.NewNodeInfo()
@ -760,7 +757,7 @@ func TestInvalidateAllCachedPredicateItemOfNode(t *testing.T) {
cache: &upToDateCache{}, cache: &upToDateCache{},
}, },
} }
ecache := NewEquivalenceCache() ecache := NewCache()
for _, test := range tests { for _, test := range tests {
node := schedulercache.NewNodeInfo() node := schedulercache.NewNodeInfo()

View File

@ -86,7 +86,7 @@ func (f *FitError) Error() string {
type genericScheduler struct { type genericScheduler struct {
cache schedulercache.Cache cache schedulercache.Cache
equivalenceCache *equivalence.EquivalenceCache equivalenceCache *equivalence.Cache
schedulingQueue SchedulingQueue schedulingQueue SchedulingQueue
predicates map[string]algorithm.FitPredicate predicates map[string]algorithm.FitPredicate
priorityMetaProducer algorithm.PriorityMetadataProducer priorityMetaProducer algorithm.PriorityMetadataProducer
@ -343,10 +343,10 @@ func (g *genericScheduler) findNodesThatFit(pod *v1.Pod, nodes []*v1.Node) ([]*v
// We can use the same metadata producer for all nodes. // We can use the same metadata producer for all nodes.
meta := g.predicateMetaProducer(pod, g.cachedNodeInfoMap) meta := g.predicateMetaProducer(pod, g.cachedNodeInfoMap)
var equivCacheInfo *equivalence.EquivalenceClassInfo var equivClass *equivalence.Class
if g.equivalenceCache != nil { if g.equivalenceCache != nil {
// getEquivalenceClassInfo will return immediately if no equivalence pod found // getEquivalenceClassInfo will return immediately if no equivalence pod found
equivCacheInfo = g.equivalenceCache.GetEquivalenceClassInfo(pod) equivClass = equivalence.NewClass(pod)
} }
checkNode := func(i int) { checkNode := func(i int) {
@ -360,7 +360,7 @@ func (g *genericScheduler) findNodesThatFit(pod *v1.Pod, nodes []*v1.Node) ([]*v
g.equivalenceCache, g.equivalenceCache,
g.schedulingQueue, g.schedulingQueue,
g.alwaysCheckAllPredicates, g.alwaysCheckAllPredicates,
equivCacheInfo, equivClass,
) )
if err != nil { if err != nil {
predicateResultLock.Lock() predicateResultLock.Lock()
@ -460,10 +460,10 @@ func podFitsOnNode(
info *schedulercache.NodeInfo, info *schedulercache.NodeInfo,
predicateFuncs map[string]algorithm.FitPredicate, predicateFuncs map[string]algorithm.FitPredicate,
cache schedulercache.Cache, cache schedulercache.Cache,
ecache *equivalence.EquivalenceCache, ecache *equivalence.Cache,
queue SchedulingQueue, queue SchedulingQueue,
alwaysCheckAllPredicates bool, alwaysCheckAllPredicates bool,
equivCacheInfo *equivalence.EquivalenceClassInfo, equivClass *equivalence.Class,
) (bool, []algorithm.PredicateFailureReason, error) { ) (bool, []algorithm.PredicateFailureReason, error) {
var ( var (
eCacheAvailable bool eCacheAvailable bool
@ -500,7 +500,7 @@ func podFitsOnNode(
// Bypass eCache if node has any nominated pods. // Bypass eCache if node has any nominated pods.
// TODO(bsalamat): consider using eCache and adding proper eCache invalidations // TODO(bsalamat): consider using eCache and adding proper eCache invalidations
// when pods are nominated or their nominations change. // when pods are nominated or their nominations change.
eCacheAvailable = equivCacheInfo != nil && !podsAdded eCacheAvailable = equivClass != nil && !podsAdded
for _, predicateKey := range predicates.Ordering() { for _, predicateKey := range predicates.Ordering() {
var ( var (
fit bool fit bool
@ -510,7 +510,7 @@ func podFitsOnNode(
//TODO (yastij) : compute average predicate restrictiveness to export it as Prometheus metric //TODO (yastij) : compute average predicate restrictiveness to export it as Prometheus metric
if predicate, exist := predicateFuncs[predicateKey]; exist { if predicate, exist := predicateFuncs[predicateKey]; exist {
if eCacheAvailable { if eCacheAvailable {
fit, reasons, err = ecache.RunPredicate(predicate, predicateKey, pod, metaToUse, nodeInfoToUse, equivCacheInfo, cache) fit, reasons, err = ecache.RunPredicate(predicate, predicateKey, pod, metaToUse, nodeInfoToUse, equivClass, cache)
} else { } else {
fit, reasons, err = predicate(pod, metaToUse, nodeInfoToUse) fit, reasons, err = predicate(pod, metaToUse, nodeInfoToUse)
} }
@ -1057,7 +1057,7 @@ func podPassesBasicChecks(pod *v1.Pod, pvcLister corelisters.PersistentVolumeCla
// NewGenericScheduler creates a genericScheduler object. // NewGenericScheduler creates a genericScheduler object.
func NewGenericScheduler( func NewGenericScheduler(
cache schedulercache.Cache, cache schedulercache.Cache,
eCache *equivalence.EquivalenceCache, eCache *equivalence.Cache,
podQueue SchedulingQueue, podQueue SchedulingQueue,
predicates map[string]algorithm.FitPredicate, predicates map[string]algorithm.FitPredicate,
predicateMetaProducer algorithm.PredicateMetadataProducer, predicateMetaProducer algorithm.PredicateMetadataProducer,

View File

@ -1367,10 +1367,10 @@ func (c *syncingMockCache) UpdateNodeNameToInfoMap(infoMap map[string]*scheduler
return err return err
} }
// TestEquivalenceCacheInvalidationRace tests that equivalence cache invalidation is correctly // TestCacheInvalidationRace tests that equivalence cache invalidation is correctly
// handled when an invalidation event happens early in a scheduling cycle. Specifically, the event // handled when an invalidation event happens early in a scheduling cycle. Specifically, the event
// occurs after schedulercache is snapshotted and before equivalence cache lock is acquired. // occurs after schedulercache is snapshotted and before equivalence cache lock is acquired.
func TestEquivalenceCacheInvalidationRace(t *testing.T) { func TestCacheInvalidationRace(t *testing.T) {
// Create a predicate that returns false the first time and true on subsequent calls. // Create a predicate that returns false the first time and true on subsequent calls.
podWillFit := false podWillFit := false
var callCount int var callCount int
@ -1394,7 +1394,7 @@ func TestEquivalenceCacheInvalidationRace(t *testing.T) {
cacheInvalidated: make(chan struct{}), cacheInvalidated: make(chan struct{}),
} }
eCache := equivalence.NewEquivalenceCache() eCache := equivalence.NewCache()
// Ensure that equivalence cache invalidation happens after the scheduling cycle starts, but before // Ensure that equivalence cache invalidation happens after the scheduling cycle starts, but before
// the equivalence cache would be updated. // the equivalence cache would be updated.
go func() { go func() {

View File

@ -124,7 +124,7 @@ type configFactory struct {
hardPodAffinitySymmetricWeight int32 hardPodAffinitySymmetricWeight int32
// Equivalence class cache // Equivalence class cache
equivalencePodCache *equivalence.EquivalenceCache equivalencePodCache *equivalence.Cache
// Enable equivalence class cache // Enable equivalence class cache
enableEquivalenceClassCache bool enableEquivalenceClassCache bool
@ -1075,7 +1075,7 @@ func (c *configFactory) CreateFromKeys(predicateKeys, priorityKeys sets.String,
// Init equivalence class cache // Init equivalence class cache
if c.enableEquivalenceClassCache { if c.enableEquivalenceClassCache {
c.equivalencePodCache = equivalence.NewEquivalenceCache() c.equivalencePodCache = equivalence.NewCache()
glog.Info("Created equivalence class cache") glog.Info("Created equivalence class cache")
} }

View File

@ -105,7 +105,7 @@ type Config struct {
SchedulerCache schedulercache.Cache SchedulerCache schedulercache.Cache
// Ecache is used for optimistically invalid affected cache items after // Ecache is used for optimistically invalid affected cache items after
// successfully binding a pod // successfully binding a pod
Ecache *equivalence.EquivalenceCache Ecache *equivalence.Cache
NodeLister algorithm.NodeLister NodeLister algorithm.NodeLister
Algorithm algorithm.ScheduleAlgorithm Algorithm algorithm.ScheduleAlgorithm
GetBinder func(pod *v1.Pod) Binder GetBinder func(pod *v1.Pod) Binder