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