k8s.io/kubernetes@v1.29.3/pkg/kubelet/container/cache_test.go (about) 1 /* 2 Copyright 2015 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 container 18 19 import ( 20 "fmt" 21 "strconv" 22 "testing" 23 "time" 24 25 "github.com/stretchr/testify/assert" 26 "k8s.io/apimachinery/pkg/types" 27 ) 28 29 func newTestCache() *cache { 30 c := NewCache() 31 return c.(*cache) 32 } 33 34 func TestCacheNotInitialized(t *testing.T) { 35 cache := newTestCache() 36 // If the global timestamp is not set, always return nil. 37 d := cache.getIfNewerThan(types.UID("1234"), time.Time{}) 38 assert.True(t, d == nil, "should return nil since cache is not initialized") 39 } 40 41 func getTestPodIDAndStatus(numContainers int) (types.UID, *PodStatus) { 42 id := types.UID(strconv.FormatInt(time.Now().UnixNano(), 10)) 43 name := fmt.Sprintf("cache-foo-%s", string(id)) 44 namespace := "ns" 45 var status *PodStatus 46 if numContainers > 0 { 47 status = &PodStatus{ID: id, Name: name, Namespace: namespace} 48 } else { 49 status = &PodStatus{ID: id} 50 } 51 for i := 0; i < numContainers; i++ { 52 status.ContainerStatuses = append(status.ContainerStatuses, &Status{Name: strconv.Itoa(i)}) 53 } 54 return id, status 55 } 56 57 func TestGetIfNewerThanWhenPodExists(t *testing.T) { 58 cache := newTestCache() 59 timestamp := time.Now() 60 61 cases := []struct { 62 cacheTime time.Time 63 modified time.Time 64 expected bool 65 }{ 66 { 67 // Both the global cache timestamp and the modified time are newer 68 // than the timestamp. 69 cacheTime: timestamp.Add(time.Second), 70 modified: timestamp, 71 expected: true, 72 }, 73 { 74 // Global cache timestamp is newer, but the pod entry modified 75 // time is older than the given timestamp. This means that the 76 // entry is up-to-date even though it hasn't changed for a while. 77 cacheTime: timestamp.Add(time.Second), 78 modified: timestamp.Add(-time.Second * 10), 79 expected: true, 80 }, 81 { 82 // Global cache timestamp is older, but the pod entry modified 83 // time is newer than the given timestamp. This means that the 84 // entry is up-to-date but the rest of the cache are still being 85 // updated. 86 cacheTime: timestamp.Add(-time.Second), 87 modified: timestamp.Add(time.Second * 3), 88 expected: true, 89 }, 90 { 91 // Both the global cache timestamp and the modified time are older 92 // than the given timestamp. 93 cacheTime: timestamp.Add(-time.Second), 94 modified: timestamp.Add(-time.Second), 95 expected: false, 96 }, 97 } 98 for i, c := range cases { 99 podID, status := getTestPodIDAndStatus(2) 100 cache.UpdateTime(c.cacheTime) 101 cache.Set(podID, status, nil, c.modified) 102 d := cache.getIfNewerThan(podID, timestamp) 103 assert.Equal(t, c.expected, d != nil, "test[%d]", i) 104 } 105 } 106 107 func TestGetPodNewerThanWhenPodDoesNotExist(t *testing.T) { 108 cache := newTestCache() 109 cacheTime := time.Now() 110 cache.UpdateTime(cacheTime) 111 podID := types.UID("1234") 112 113 cases := []struct { 114 timestamp time.Time 115 expected bool 116 }{ 117 { 118 timestamp: cacheTime.Add(-time.Second), 119 expected: true, 120 }, 121 { 122 timestamp: cacheTime.Add(time.Second), 123 expected: false, 124 }, 125 } 126 for i, c := range cases { 127 d := cache.getIfNewerThan(podID, c.timestamp) 128 assert.Equal(t, c.expected, d != nil, "test[%d]", i) 129 } 130 } 131 132 func TestCacheSetAndGet(t *testing.T) { 133 cache := NewCache() 134 cases := []struct { 135 numContainers int 136 error error 137 }{ 138 {numContainers: 3, error: nil}, 139 {numContainers: 2, error: fmt.Errorf("unable to get status")}, 140 {numContainers: 0, error: nil}, 141 } 142 for i, c := range cases { 143 podID, status := getTestPodIDAndStatus(c.numContainers) 144 cache.Set(podID, status, c.error, time.Time{}) 145 // Read back the status and error stored in cache and make sure they 146 // match the original ones. 147 actualStatus, actualErr := cache.Get(podID) 148 assert.Equal(t, status, actualStatus, "test[%d]", i) 149 assert.Equal(t, c.error, actualErr, "test[%d]", i) 150 } 151 } 152 153 func TestCacheGetPodDoesNotExist(t *testing.T) { 154 cache := NewCache() 155 podID, status := getTestPodIDAndStatus(0) 156 // If the pod does not exist in cache, cache should return an status 157 // object with id filled. 158 actualStatus, actualErr := cache.Get(podID) 159 assert.Equal(t, status, actualStatus) 160 assert.Equal(t, nil, actualErr) 161 } 162 163 func TestDelete(t *testing.T) { 164 cache := &cache{pods: map[types.UID]*data{}} 165 // Write a new pod status into the cache. 166 podID, status := getTestPodIDAndStatus(3) 167 cache.Set(podID, status, nil, time.Time{}) 168 actualStatus, actualErr := cache.Get(podID) 169 assert.Equal(t, status, actualStatus) 170 assert.Equal(t, nil, actualErr) 171 // Delete the pod from cache, and verify that we get an empty status. 172 cache.Delete(podID) 173 expectedStatus := &PodStatus{ID: podID} 174 actualStatus, actualErr = cache.Get(podID) 175 assert.Equal(t, expectedStatus, actualStatus) 176 assert.Equal(t, nil, actualErr) 177 } 178 179 func verifyNotification(t *testing.T, ch chan *data, expectNotification bool) { 180 if expectNotification { 181 assert.True(t, len(ch) > 0, "Did not receive notification") 182 } else { 183 assert.True(t, len(ch) < 1, "Should not have triggered the notification") 184 } 185 // Drain the channel. 186 for i := 0; i < len(ch); i++ { 187 <-ch 188 } 189 } 190 191 func TestRegisterNotification(t *testing.T) { 192 cache := newTestCache() 193 cacheTime := time.Now() 194 cache.UpdateTime(cacheTime) 195 196 podID, status := getTestPodIDAndStatus(1) 197 ch := cache.subscribe(podID, cacheTime.Add(time.Second)) 198 verifyNotification(t, ch, false) 199 cache.Set(podID, status, nil, cacheTime.Add(time.Second)) 200 // The Set operation should've triggered the notification. 201 verifyNotification(t, ch, true) 202 203 podID, _ = getTestPodIDAndStatus(1) 204 205 ch = cache.subscribe(podID, cacheTime.Add(time.Second)) 206 verifyNotification(t, ch, false) 207 cache.UpdateTime(cacheTime.Add(time.Second * 2)) 208 // The advance of cache timestamp should've triggered the notification. 209 verifyNotification(t, ch, true) 210 }