cache_test.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. /*
  2. Copyright 2015 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package container
  14. import (
  15. "fmt"
  16. "strconv"
  17. "testing"
  18. "time"
  19. "github.com/stretchr/testify/assert"
  20. "k8s.io/kubernetes/pkg/types"
  21. )
  22. func newTestCache() *cache {
  23. c := NewCache()
  24. return c.(*cache)
  25. }
  26. func TestCacheNotInitialized(t *testing.T) {
  27. cache := newTestCache()
  28. // If the global timestamp is not set, always return nil.
  29. d := cache.getIfNewerThan(types.UID("1234"), time.Time{})
  30. assert.True(t, d == nil, "should return nil since cache is not initialized")
  31. }
  32. func getTestPodIDAndStatus(numContainers int) (types.UID, *PodStatus) {
  33. id := types.UID(strconv.FormatInt(time.Now().UnixNano(), 10))
  34. name := fmt.Sprintf("cache-foo-%s", string(id))
  35. namespace := "ns"
  36. var status *PodStatus
  37. if numContainers > 0 {
  38. status = &PodStatus{ID: id, Name: name, Namespace: namespace}
  39. } else {
  40. status = &PodStatus{ID: id}
  41. }
  42. for i := 0; i < numContainers; i++ {
  43. status.ContainerStatuses = append(status.ContainerStatuses, &ContainerStatus{Name: string(i)})
  44. }
  45. return id, status
  46. }
  47. func TestGetIfNewerThanWhenPodExists(t *testing.T) {
  48. cache := newTestCache()
  49. timestamp := time.Now()
  50. cases := []struct {
  51. cacheTime time.Time
  52. modified time.Time
  53. expected bool
  54. }{
  55. {
  56. // Both the global cache timestamp and the modified time are newer
  57. // than the timestamp.
  58. cacheTime: timestamp.Add(time.Second),
  59. modified: timestamp,
  60. expected: true,
  61. },
  62. {
  63. // Global cache timestamp is newer, but the pod entry modified
  64. // time is older than the given timestamp. This means that the
  65. // entry is up-to-date even though it hasn't changed for a while.
  66. cacheTime: timestamp.Add(time.Second),
  67. modified: timestamp.Add(-time.Second * 10),
  68. expected: true,
  69. },
  70. {
  71. // Global cache timestamp is older, but the pod entry modified
  72. // time is newer than the given timestamp. This means that the
  73. // entry is up-to-date but the rest of the cache are still being
  74. // updated.
  75. cacheTime: timestamp.Add(-time.Second),
  76. modified: timestamp.Add(time.Second * 3),
  77. expected: true,
  78. },
  79. {
  80. // Both the global cache timestamp and the modified time are older
  81. // than the given timestamp.
  82. cacheTime: timestamp.Add(-time.Second),
  83. modified: timestamp.Add(-time.Second),
  84. expected: false,
  85. },
  86. }
  87. for i, c := range cases {
  88. podID, status := getTestPodIDAndStatus(2)
  89. cache.UpdateTime(c.cacheTime)
  90. cache.Set(podID, status, nil, c.modified)
  91. d := cache.getIfNewerThan(podID, timestamp)
  92. assert.Equal(t, c.expected, d != nil, "test[%d]", i)
  93. }
  94. }
  95. func TestGetPodNewerThanWhenPodDoesNotExist(t *testing.T) {
  96. cache := newTestCache()
  97. cacheTime := time.Now()
  98. cache.UpdateTime(cacheTime)
  99. podID := types.UID("1234")
  100. cases := []struct {
  101. timestamp time.Time
  102. expected bool
  103. }{
  104. {
  105. timestamp: cacheTime.Add(-time.Second),
  106. expected: true,
  107. },
  108. {
  109. timestamp: cacheTime.Add(time.Second),
  110. expected: false,
  111. },
  112. }
  113. for i, c := range cases {
  114. d := cache.getIfNewerThan(podID, c.timestamp)
  115. assert.Equal(t, c.expected, d != nil, "test[%d]", i)
  116. }
  117. }
  118. func TestCacheSetAndGet(t *testing.T) {
  119. cache := NewCache()
  120. cases := []struct {
  121. numContainers int
  122. error error
  123. }{
  124. {numContainers: 3, error: nil},
  125. {numContainers: 2, error: fmt.Errorf("unable to get status")},
  126. {numContainers: 0, error: nil},
  127. }
  128. for i, c := range cases {
  129. podID, status := getTestPodIDAndStatus(c.numContainers)
  130. cache.Set(podID, status, c.error, time.Time{})
  131. // Read back the status and error stored in cache and make sure they
  132. // match the original ones.
  133. actualStatus, actualErr := cache.Get(podID)
  134. assert.Equal(t, status, actualStatus, "test[%d]", i)
  135. assert.Equal(t, c.error, actualErr, "test[%d]", i)
  136. }
  137. }
  138. func TestCacheGetPodDoesNotExist(t *testing.T) {
  139. cache := NewCache()
  140. podID, status := getTestPodIDAndStatus(0)
  141. // If the pod does not exist in cache, cache should return an status
  142. // object with id filled.
  143. actualStatus, actualErr := cache.Get(podID)
  144. assert.Equal(t, status, actualStatus)
  145. assert.Equal(t, nil, actualErr)
  146. }
  147. func TestDelete(t *testing.T) {
  148. cache := &cache{pods: map[types.UID]*data{}}
  149. // Write a new pod status into the cache.
  150. podID, status := getTestPodIDAndStatus(3)
  151. cache.Set(podID, status, nil, time.Time{})
  152. actualStatus, actualErr := cache.Get(podID)
  153. assert.Equal(t, status, actualStatus)
  154. assert.Equal(t, nil, actualErr)
  155. // Delete the pod from cache, and verify that we get an empty status.
  156. cache.Delete(podID)
  157. expectedStatus := &PodStatus{ID: podID}
  158. actualStatus, actualErr = cache.Get(podID)
  159. assert.Equal(t, expectedStatus, actualStatus)
  160. assert.Equal(t, nil, actualErr)
  161. }
  162. func verifyNotification(t *testing.T, ch chan *data, expectNotification bool) {
  163. if expectNotification {
  164. assert.True(t, len(ch) > 0, "Did not receive notification")
  165. } else {
  166. assert.True(t, len(ch) < 1, "Should not have triggered the notification")
  167. }
  168. // Drain the channel.
  169. for i := 0; i < len(ch); i++ {
  170. <-ch
  171. }
  172. }
  173. func TestRegisterNotification(t *testing.T) {
  174. cache := newTestCache()
  175. cacheTime := time.Now()
  176. cache.UpdateTime(cacheTime)
  177. podID, status := getTestPodIDAndStatus(1)
  178. ch := cache.subscribe(podID, cacheTime.Add(time.Second))
  179. verifyNotification(t, ch, false)
  180. cache.Set(podID, status, nil, cacheTime.Add(time.Second))
  181. // The Set operation should've triggered the notification.
  182. verifyNotification(t, ch, true)
  183. podID, _ = getTestPodIDAndStatus(1)
  184. ch = cache.subscribe(podID, cacheTime.Add(time.Second))
  185. verifyNotification(t, ch, false)
  186. cache.UpdateTime(cacheTime.Add(time.Second * 2))
  187. // The advance of cache timestamp should've triggered the notification.
  188. verifyNotification(t, ch, true)
  189. }