config_test.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. /*
  2. Copyright 2014 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 config
  14. import (
  15. "io/ioutil"
  16. "math/rand"
  17. "os"
  18. "reflect"
  19. "sort"
  20. "strconv"
  21. "testing"
  22. "time"
  23. "k8s.io/api/core/v1"
  24. apiequality "k8s.io/apimachinery/pkg/api/equality"
  25. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  26. "k8s.io/apimachinery/pkg/types"
  27. "k8s.io/client-go/kubernetes/scheme"
  28. "k8s.io/client-go/tools/record"
  29. "k8s.io/kubernetes/pkg/apis/core"
  30. "k8s.io/kubernetes/pkg/kubelet/checkpoint"
  31. "k8s.io/kubernetes/pkg/kubelet/checkpointmanager"
  32. kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
  33. "k8s.io/kubernetes/pkg/securitycontext"
  34. )
  35. const (
  36. TestSource = "test"
  37. )
  38. func expectEmptyChannel(t *testing.T, ch <-chan interface{}) {
  39. select {
  40. case update := <-ch:
  41. t.Errorf("Expected no update in channel, Got %v", update)
  42. default:
  43. }
  44. }
  45. type sortedPods []*v1.Pod
  46. func (s sortedPods) Len() int {
  47. return len(s)
  48. }
  49. func (s sortedPods) Swap(i, j int) {
  50. s[i], s[j] = s[j], s[i]
  51. }
  52. func (s sortedPods) Less(i, j int) bool {
  53. return s[i].Namespace < s[j].Namespace
  54. }
  55. func CreateValidPod(name, namespace string) *v1.Pod {
  56. return &v1.Pod{
  57. ObjectMeta: metav1.ObjectMeta{
  58. UID: types.UID(name + namespace), // for the purpose of testing, this is unique enough
  59. Name: name,
  60. Namespace: namespace,
  61. },
  62. Spec: v1.PodSpec{
  63. RestartPolicy: v1.RestartPolicyAlways,
  64. DNSPolicy: v1.DNSClusterFirst,
  65. Containers: []v1.Container{
  66. {
  67. Name: "ctr",
  68. Image: "image",
  69. ImagePullPolicy: "IfNotPresent",
  70. SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(),
  71. TerminationMessagePolicy: v1.TerminationMessageReadFile,
  72. },
  73. },
  74. },
  75. }
  76. }
  77. func CreatePodUpdate(op kubetypes.PodOperation, source string, pods ...*v1.Pod) kubetypes.PodUpdate {
  78. return kubetypes.PodUpdate{Pods: pods, Op: op, Source: source}
  79. }
  80. func createPodConfigTesterByChannel(mode PodConfigNotificationMode, channelName string) (chan<- interface{}, <-chan kubetypes.PodUpdate, *PodConfig) {
  81. eventBroadcaster := record.NewBroadcaster()
  82. config := NewPodConfig(mode, eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "kubelet"}))
  83. channel := config.Channel(channelName)
  84. ch := config.Updates()
  85. return channel, ch, config
  86. }
  87. func createPodConfigTester(mode PodConfigNotificationMode) (chan<- interface{}, <-chan kubetypes.PodUpdate, *PodConfig) {
  88. eventBroadcaster := record.NewBroadcaster()
  89. config := NewPodConfig(mode, eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "kubelet"}))
  90. channel := config.Channel(TestSource)
  91. ch := config.Updates()
  92. return channel, ch, config
  93. }
  94. func expectPodUpdate(t *testing.T, ch <-chan kubetypes.PodUpdate, expected ...kubetypes.PodUpdate) {
  95. for i := range expected {
  96. update := <-ch
  97. sort.Sort(sortedPods(update.Pods))
  98. sort.Sort(sortedPods(expected[i].Pods))
  99. // Make copies of the expected/actual update to compare all fields
  100. // except for "Pods", which are compared separately below.
  101. expectedCopy, updateCopy := expected[i], update
  102. expectedCopy.Pods, updateCopy.Pods = nil, nil
  103. if !apiequality.Semantic.DeepEqual(expectedCopy, updateCopy) {
  104. t.Fatalf("Expected %#v, Got %#v", expectedCopy, updateCopy)
  105. }
  106. if len(expected[i].Pods) != len(update.Pods) {
  107. t.Fatalf("Expected %#v, Got %#v", expected[i], update)
  108. }
  109. // Compare pods one by one. This is necessary because we don't want to
  110. // compare local annotations.
  111. for j := range expected[i].Pods {
  112. if podsDifferSemantically(expected[i].Pods[j], update.Pods[j]) || !reflect.DeepEqual(expected[i].Pods[j].Status, update.Pods[j].Status) {
  113. t.Fatalf("Expected %#v, Got %#v", expected[i].Pods[j], update.Pods[j])
  114. }
  115. }
  116. }
  117. expectNoPodUpdate(t, ch)
  118. }
  119. func expectNoPodUpdate(t *testing.T, ch <-chan kubetypes.PodUpdate) {
  120. select {
  121. case update := <-ch:
  122. t.Errorf("Expected no update in channel, Got %#v", update)
  123. default:
  124. }
  125. }
  126. func TestNewPodAdded(t *testing.T) {
  127. channel, ch, config := createPodConfigTester(PodConfigNotificationIncremental)
  128. // see an update
  129. podUpdate := CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "new"))
  130. channel <- podUpdate
  131. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "new")))
  132. config.Sync()
  133. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.SET, kubetypes.AllSource, CreateValidPod("foo", "new")))
  134. }
  135. func TestNewPodAddedInvalidNamespace(t *testing.T) {
  136. channel, ch, config := createPodConfigTester(PodConfigNotificationIncremental)
  137. // see an update
  138. podUpdate := CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", ""))
  139. channel <- podUpdate
  140. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "")))
  141. config.Sync()
  142. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.SET, kubetypes.AllSource, CreateValidPod("foo", "")))
  143. }
  144. func TestNewPodAddedDefaultNamespace(t *testing.T) {
  145. channel, ch, config := createPodConfigTester(PodConfigNotificationIncremental)
  146. // see an update
  147. podUpdate := CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "default"))
  148. channel <- podUpdate
  149. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "default")))
  150. config.Sync()
  151. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.SET, kubetypes.AllSource, CreateValidPod("foo", "default")))
  152. }
  153. func TestNewPodAddedDifferentNamespaces(t *testing.T) {
  154. channel, ch, config := createPodConfigTester(PodConfigNotificationIncremental)
  155. // see an update
  156. podUpdate := CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "default"))
  157. channel <- podUpdate
  158. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "default")))
  159. // see an update in another namespace
  160. podUpdate = CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "new"))
  161. channel <- podUpdate
  162. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "new")))
  163. config.Sync()
  164. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.SET, kubetypes.AllSource, CreateValidPod("foo", "default"), CreateValidPod("foo", "new")))
  165. }
  166. func TestInvalidPodFiltered(t *testing.T) {
  167. channel, ch, _ := createPodConfigTester(PodConfigNotificationIncremental)
  168. // see an update
  169. podUpdate := CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "new"))
  170. channel <- podUpdate
  171. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "new")))
  172. // add an invalid update
  173. podUpdate = CreatePodUpdate(kubetypes.UPDATE, TestSource, &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}})
  174. channel <- podUpdate
  175. expectNoPodUpdate(t, ch)
  176. }
  177. func TestNewPodAddedSnapshotAndUpdates(t *testing.T) {
  178. channel, ch, config := createPodConfigTester(PodConfigNotificationSnapshotAndUpdates)
  179. // see an set
  180. podUpdate := CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "new"))
  181. channel <- podUpdate
  182. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.SET, TestSource, CreateValidPod("foo", "new")))
  183. config.Sync()
  184. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.SET, kubetypes.AllSource, CreateValidPod("foo", "new")))
  185. // container updates are separated as UPDATE
  186. pod := *podUpdate.Pods[0]
  187. pod.Spec.Containers = []v1.Container{{Name: "bar", Image: "test", ImagePullPolicy: v1.PullIfNotPresent, TerminationMessagePolicy: v1.TerminationMessageReadFile}}
  188. channel <- CreatePodUpdate(kubetypes.ADD, TestSource, &pod)
  189. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.UPDATE, TestSource, &pod))
  190. }
  191. func TestNewPodAddedSnapshot(t *testing.T) {
  192. channel, ch, config := createPodConfigTester(PodConfigNotificationSnapshot)
  193. // see an set
  194. podUpdate := CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "new"))
  195. channel <- podUpdate
  196. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.SET, TestSource, CreateValidPod("foo", "new")))
  197. config.Sync()
  198. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.SET, kubetypes.AllSource, CreateValidPod("foo", "new")))
  199. // container updates are separated as UPDATE
  200. pod := *podUpdate.Pods[0]
  201. pod.Spec.Containers = []v1.Container{{Name: "bar", Image: "test", ImagePullPolicy: v1.PullIfNotPresent, TerminationMessagePolicy: v1.TerminationMessageReadFile}}
  202. channel <- CreatePodUpdate(kubetypes.ADD, TestSource, &pod)
  203. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.SET, TestSource, &pod))
  204. }
  205. func TestNewPodAddedUpdatedRemoved(t *testing.T) {
  206. channel, ch, _ := createPodConfigTester(PodConfigNotificationIncremental)
  207. // should register an add
  208. podUpdate := CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "new"))
  209. channel <- podUpdate
  210. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "new")))
  211. // should ignore ADDs that are identical
  212. expectNoPodUpdate(t, ch)
  213. // an kubetypes.ADD should be converted to kubetypes.UPDATE
  214. pod := CreateValidPod("foo", "new")
  215. pod.Spec.Containers = []v1.Container{{Name: "bar", Image: "test", ImagePullPolicy: v1.PullIfNotPresent, TerminationMessagePolicy: v1.TerminationMessageReadFile}}
  216. podUpdate = CreatePodUpdate(kubetypes.ADD, TestSource, pod)
  217. channel <- podUpdate
  218. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.UPDATE, TestSource, pod))
  219. podUpdate = CreatePodUpdate(kubetypes.REMOVE, TestSource, CreateValidPod("foo", "new"))
  220. channel <- podUpdate
  221. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.REMOVE, TestSource, pod))
  222. }
  223. func TestNewPodAddedDelete(t *testing.T) {
  224. channel, ch, _ := createPodConfigTester(PodConfigNotificationIncremental)
  225. // should register an add
  226. addedPod := CreateValidPod("foo", "new")
  227. podUpdate := CreatePodUpdate(kubetypes.ADD, TestSource, addedPod)
  228. channel <- podUpdate
  229. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.ADD, TestSource, addedPod))
  230. // mark this pod as deleted
  231. timestamp := metav1.NewTime(time.Now())
  232. deletedPod := CreateValidPod("foo", "new")
  233. deletedPod.ObjectMeta.DeletionTimestamp = &timestamp
  234. podUpdate = CreatePodUpdate(kubetypes.DELETE, TestSource, deletedPod)
  235. channel <- podUpdate
  236. // the existing pod should be gracefully deleted
  237. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.DELETE, TestSource, addedPod))
  238. }
  239. func TestNewPodAddedUpdatedSet(t *testing.T) {
  240. channel, ch, _ := createPodConfigTester(PodConfigNotificationIncremental)
  241. // should register an add
  242. podUpdate := CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "new"), CreateValidPod("foo2", "new"), CreateValidPod("foo3", "new"))
  243. channel <- podUpdate
  244. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "new"), CreateValidPod("foo2", "new"), CreateValidPod("foo3", "new")))
  245. // should ignore ADDs that are identical
  246. expectNoPodUpdate(t, ch)
  247. // should be converted to an kubetypes.ADD, kubetypes.REMOVE, and kubetypes.UPDATE
  248. pod := CreateValidPod("foo2", "new")
  249. pod.Spec.Containers = []v1.Container{{Name: "bar", Image: "test", ImagePullPolicy: v1.PullIfNotPresent, TerminationMessagePolicy: v1.TerminationMessageReadFile}}
  250. podUpdate = CreatePodUpdate(kubetypes.SET, TestSource, pod, CreateValidPod("foo3", "new"), CreateValidPod("foo4", "new"))
  251. channel <- podUpdate
  252. expectPodUpdate(t, ch,
  253. CreatePodUpdate(kubetypes.REMOVE, TestSource, CreateValidPod("foo", "new")),
  254. CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo4", "new")),
  255. CreatePodUpdate(kubetypes.UPDATE, TestSource, pod))
  256. }
  257. func TestNewPodAddedSetReconciled(t *testing.T) {
  258. // Create and touch new test pods, return the new pods and touched pod. We should create new pod list
  259. // before touching to avoid data race.
  260. newTestPods := func(touchStatus, touchSpec bool) ([]*v1.Pod, *v1.Pod) {
  261. pods := []*v1.Pod{
  262. CreateValidPod("changeable-pod-0", "new"),
  263. CreateValidPod("constant-pod-1", "new"),
  264. CreateValidPod("constant-pod-2", "new"),
  265. }
  266. if touchStatus {
  267. pods[0].Status = v1.PodStatus{Message: strconv.Itoa(rand.Int())}
  268. }
  269. if touchSpec {
  270. pods[0].Spec.Containers[0].Name = strconv.Itoa(rand.Int())
  271. }
  272. return pods, pods[0]
  273. }
  274. for _, op := range []kubetypes.PodOperation{
  275. kubetypes.ADD,
  276. kubetypes.SET,
  277. } {
  278. var podWithStatusChange *v1.Pod
  279. pods, _ := newTestPods(false, false)
  280. channel, ch, _ := createPodConfigTester(PodConfigNotificationIncremental)
  281. // Use SET to initialize the config, especially initialize the source set
  282. channel <- CreatePodUpdate(kubetypes.SET, TestSource, pods...)
  283. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.ADD, TestSource, pods...))
  284. // If status is not changed, no reconcile should be triggered
  285. channel <- CreatePodUpdate(op, TestSource, pods...)
  286. expectNoPodUpdate(t, ch)
  287. // If the pod status is changed and not updated, a reconcile should be triggered
  288. pods, podWithStatusChange = newTestPods(true, false)
  289. channel <- CreatePodUpdate(op, TestSource, pods...)
  290. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.RECONCILE, TestSource, podWithStatusChange))
  291. // If the pod status is changed, but the pod is also updated, no reconcile should be triggered
  292. pods, podWithStatusChange = newTestPods(true, true)
  293. channel <- CreatePodUpdate(op, TestSource, pods...)
  294. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.UPDATE, TestSource, podWithStatusChange))
  295. }
  296. }
  297. func TestInitialEmptySet(t *testing.T) {
  298. for _, test := range []struct {
  299. mode PodConfigNotificationMode
  300. op kubetypes.PodOperation
  301. }{
  302. {PodConfigNotificationIncremental, kubetypes.ADD},
  303. {PodConfigNotificationSnapshot, kubetypes.SET},
  304. {PodConfigNotificationSnapshotAndUpdates, kubetypes.SET},
  305. } {
  306. channel, ch, _ := createPodConfigTester(test.mode)
  307. // should register an empty PodUpdate operation
  308. podUpdate := CreatePodUpdate(kubetypes.SET, TestSource)
  309. channel <- podUpdate
  310. expectPodUpdate(t, ch, CreatePodUpdate(test.op, TestSource))
  311. // should ignore following empty sets
  312. podUpdate = CreatePodUpdate(kubetypes.SET, TestSource)
  313. channel <- podUpdate
  314. podUpdate = CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo", "new"))
  315. channel <- podUpdate
  316. expectPodUpdate(t, ch, CreatePodUpdate(test.op, TestSource, CreateValidPod("foo", "new")))
  317. }
  318. }
  319. func TestPodUpdateAnnotations(t *testing.T) {
  320. channel, ch, _ := createPodConfigTester(PodConfigNotificationIncremental)
  321. pod := CreateValidPod("foo2", "new")
  322. pod.Annotations = make(map[string]string)
  323. pod.Annotations["kubernetes.io/blah"] = "blah"
  324. clone := pod.DeepCopy()
  325. podUpdate := CreatePodUpdate(kubetypes.SET, TestSource, CreateValidPod("foo1", "new"), clone, CreateValidPod("foo3", "new"))
  326. channel <- podUpdate
  327. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.ADD, TestSource, CreateValidPod("foo1", "new"), pod, CreateValidPod("foo3", "new")))
  328. pod.Annotations["kubernetes.io/blah"] = "superblah"
  329. podUpdate = CreatePodUpdate(kubetypes.SET, TestSource, CreateValidPod("foo1", "new"), pod, CreateValidPod("foo3", "new"))
  330. channel <- podUpdate
  331. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.UPDATE, TestSource, pod))
  332. pod.Annotations["kubernetes.io/otherblah"] = "doh"
  333. podUpdate = CreatePodUpdate(kubetypes.SET, TestSource, CreateValidPod("foo1", "new"), pod, CreateValidPod("foo3", "new"))
  334. channel <- podUpdate
  335. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.UPDATE, TestSource, pod))
  336. delete(pod.Annotations, "kubernetes.io/blah")
  337. podUpdate = CreatePodUpdate(kubetypes.SET, TestSource, CreateValidPod("foo1", "new"), pod, CreateValidPod("foo3", "new"))
  338. channel <- podUpdate
  339. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.UPDATE, TestSource, pod))
  340. }
  341. func TestPodUpdateLabels(t *testing.T) {
  342. channel, ch, _ := createPodConfigTester(PodConfigNotificationIncremental)
  343. pod := CreateValidPod("foo2", "new")
  344. pod.Labels = make(map[string]string)
  345. pod.Labels["key"] = "value"
  346. clone := pod.DeepCopy()
  347. podUpdate := CreatePodUpdate(kubetypes.SET, TestSource, clone)
  348. channel <- podUpdate
  349. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.ADD, TestSource, pod))
  350. pod.Labels["key"] = "newValue"
  351. podUpdate = CreatePodUpdate(kubetypes.SET, TestSource, pod)
  352. channel <- podUpdate
  353. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.UPDATE, TestSource, pod))
  354. }
  355. func TestPodRestore(t *testing.T) {
  356. tmpDir, _ := ioutil.TempDir("", "")
  357. defer os.RemoveAll(tmpDir)
  358. pod := CreateValidPod("api-server", "kube-default")
  359. pod.Annotations = make(map[string]string)
  360. pod.Annotations["kubernetes.io/config.source"] = kubetypes.ApiserverSource
  361. pod.Annotations[core.BootstrapCheckpointAnnotationKey] = "true"
  362. // Create Checkpointer
  363. checkpointManager, err := checkpointmanager.NewCheckpointManager(tmpDir)
  364. if err != nil {
  365. t.Fatalf("failed to initialize checkpoint manager: %v", err)
  366. }
  367. if err := checkpoint.WritePod(checkpointManager, pod); err != nil {
  368. t.Fatalf("Error writing checkpoint for pod: %v", pod.GetName())
  369. }
  370. // Restore checkpoint
  371. channel, ch, config := createPodConfigTesterByChannel(PodConfigNotificationIncremental, kubetypes.ApiserverSource)
  372. if err := config.Restore(tmpDir, channel); err != nil {
  373. t.Fatalf("Restore returned error: %v", err)
  374. }
  375. expectPodUpdate(t, ch, CreatePodUpdate(kubetypes.RESTORE, kubetypes.ApiserverSource, pod))
  376. // Verify Restore only happen once
  377. if err := config.Restore(tmpDir, channel); err != nil {
  378. t.Fatalf("The second restore returned error: %v", err)
  379. }
  380. expectNoPodUpdate(t, ch)
  381. }