1/* 2Copyright 2016 The Kubernetes Authors. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package kubelet 18 19import ( 20 "fmt" 21 "sync" 22 23 "github.com/golang/groupcache/lru" 24 "k8s.io/apimachinery/pkg/types" 25 kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" 26) 27 28// ReasonCache stores the failure reason of the latest container start 29// in a string, keyed by <pod_UID>_<container_name>. The goal is to 30// propagate this reason to the container status. This endeavor is 31// "best-effort" for two reasons: 32// 1. The cache is not persisted. 33// 2. We use an LRU cache to avoid extra garbage collection work. This 34// means that some entries may be recycled before a pod has been 35// deleted. 36// TODO(random-liu): Use more reliable cache which could collect garbage of failed pod. 37// TODO(random-liu): Move reason cache to somewhere better. 38type ReasonCache struct { 39 lock sync.Mutex 40 cache *lru.Cache 41} 42 43// ReasonItem is the cached item in ReasonCache 44type ReasonItem struct { 45 Err error 46 Message string 47} 48 49// maxReasonCacheEntries is the cache entry number in lru cache. 1000 is a proper number 50// for our 100 pods per node target. If we support more pods per node in the future, we 51// may want to increase the number. 52const maxReasonCacheEntries = 1000 53 54// NewReasonCache creates an instance of 'ReasonCache'. 55func NewReasonCache() *ReasonCache { 56 return &ReasonCache{cache: lru.New(maxReasonCacheEntries)} 57} 58 59func (c *ReasonCache) composeKey(uid types.UID, name string) string { 60 return fmt.Sprintf("%s_%s", uid, name) 61} 62 63// add adds error reason into the cache 64func (c *ReasonCache) add(uid types.UID, name string, reason error, message string) { 65 c.lock.Lock() 66 defer c.lock.Unlock() 67 c.cache.Add(c.composeKey(uid, name), ReasonItem{reason, message}) 68} 69 70// Update updates the reason cache with the SyncPodResult. Only SyncResult with 71// StartContainer action will change the cache. 72func (c *ReasonCache) Update(uid types.UID, result kubecontainer.PodSyncResult) { 73 for _, r := range result.SyncResults { 74 if r.Action != kubecontainer.StartContainer { 75 continue 76 } 77 name := r.Target.(string) 78 if r.Error != nil { 79 c.add(uid, name, r.Error, r.Message) 80 } else { 81 c.Remove(uid, name) 82 } 83 } 84} 85 86// Remove removes error reason from the cache 87func (c *ReasonCache) Remove(uid types.UID, name string) { 88 c.lock.Lock() 89 defer c.lock.Unlock() 90 c.cache.Remove(c.composeKey(uid, name)) 91} 92 93// Get gets error reason from the cache. The return values are error reason, error message and 94// whether an error reason is found in the cache. If no error reason is found, empty string will 95// be returned for error reason and error message. 96func (c *ReasonCache) Get(uid types.UID, name string) (*ReasonItem, bool) { 97 c.lock.Lock() 98 defer c.lock.Unlock() 99 value, ok := c.cache.Get(c.composeKey(uid, name)) 100 if !ok { 101 return nil, false 102 } 103 info := value.(ReasonItem) 104 return &info, true 105} 106