k8s.io/kubernetes@v1.29.3/pkg/kubelet/kuberuntime/kuberuntime_termination_order_test.go (about) 1 /* 2 Copyright 2023 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 kuberuntime 18 19 import ( 20 "sync" 21 "testing" 22 "time" 23 24 v1 "k8s.io/api/core/v1" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 ) 27 28 func TestTerminationOrderingSidecarStopAfterMain(t *testing.T) { 29 restartPolicy := v1.ContainerRestartPolicyAlways 30 pod := &v1.Pod{ 31 ObjectMeta: metav1.ObjectMeta{ 32 UID: "12345678", 33 Name: "bar", 34 Namespace: "new", 35 }, 36 Spec: v1.PodSpec{ 37 InitContainers: []v1.Container{ 38 { 39 Name: "init", 40 Image: "busybox", 41 ImagePullPolicy: v1.PullIfNotPresent, 42 RestartPolicy: &restartPolicy, 43 }, 44 }, 45 Containers: []v1.Container{ 46 { 47 Name: "main", 48 Image: "busybox", 49 ImagePullPolicy: v1.PullIfNotPresent, 50 }, 51 }, 52 }, 53 } 54 to := newTerminationOrdering(pod, getContainerNames(pod)) 55 56 var wg sync.WaitGroup 57 wg.Add(1) 58 var sidecarWaitDelay int64 59 var mainWaitDelay int64 60 go func() { 61 sidecarWaitDelay = int64(to.waitForTurn("init", 30)) 62 to.containerTerminated("init") 63 wg.Done() 64 }() 65 66 wg.Add(1) 67 go func() { 68 mainWaitDelay = int64(to.waitForTurn("main", 0)) 69 time.Sleep(1 * time.Second) 70 to.containerTerminated("main") 71 wg.Done() 72 }() 73 wg.Wait() 74 if sidecarWaitDelay != 1 { 75 t.Errorf("expected sidecar to wait for main container to exit, got delay of %d", sidecarWaitDelay) 76 } 77 if mainWaitDelay != 0 { 78 t.Errorf("expected main container to not wait to exit, got delay of %d", mainWaitDelay) 79 } 80 } 81 82 func TestTerminationOrderingSidecarsInReverseOrder(t *testing.T) { 83 restartPolicy := v1.ContainerRestartPolicyAlways 84 pod := &v1.Pod{ 85 ObjectMeta: metav1.ObjectMeta{ 86 UID: "12345678", 87 Name: "bar", 88 Namespace: "new", 89 }, 90 Spec: v1.PodSpec{ 91 InitContainers: []v1.Container{ 92 { 93 Name: "sc1", 94 Image: "busybox", 95 ImagePullPolicy: v1.PullIfNotPresent, 96 RestartPolicy: &restartPolicy, 97 }, 98 { 99 Name: "sc2", 100 Image: "busybox", 101 ImagePullPolicy: v1.PullIfNotPresent, 102 RestartPolicy: &restartPolicy, 103 }, 104 { 105 Name: "sc3", 106 Image: "busybox", 107 ImagePullPolicy: v1.PullIfNotPresent, 108 RestartPolicy: &restartPolicy, 109 }, 110 }, 111 Containers: []v1.Container{ 112 { 113 Name: "main", 114 Image: "busybox", 115 ImagePullPolicy: v1.PullIfNotPresent, 116 }, 117 }, 118 }, 119 } 120 to := newTerminationOrdering(pod, getContainerNames(pod)) 121 122 var wg sync.WaitGroup 123 var delays sync.Map 124 125 waitAndExit := func(name string) { 126 delay := int64(to.waitForTurn(name, 30)) 127 delays.Store(name, delay) 128 time.Sleep(1 * time.Second) 129 to.containerTerminated(name) 130 wg.Done() 131 } 132 for _, ic := range pod.Spec.InitContainers { 133 wg.Add(1) 134 go waitAndExit(ic.Name) 135 } 136 for _, c := range pod.Spec.Containers { 137 wg.Add(1) 138 go waitAndExit(c.Name) 139 } 140 141 // wait for our simulated containers to exit 142 wg.Wait() 143 144 getDelay := func(name string) int64 { 145 delay, ok := delays.Load(name) 146 if !ok { 147 t.Errorf("unable to find delay for container %s", name) 148 } 149 return delay.(int64) 150 } 151 152 for _, tc := range []struct { 153 containerName string 154 expectedDelay int64 155 }{ 156 // sidecars should exit in reverse order, so 157 // sc1 = 3 (main container + sc3 + sc2) 158 { 159 containerName: "sc1", 160 expectedDelay: 3, 161 }, 162 // sc2 = 2 (main container + sc3) 163 { 164 containerName: "sc2", 165 expectedDelay: 2, 166 }, 167 // sc3 = 1 (main container) 168 { 169 containerName: "sc3", 170 expectedDelay: 1, 171 }, 172 // main container = 0 delay, nothing to wait on 173 { 174 containerName: "main", 175 expectedDelay: 0, 176 }, 177 } { 178 if got := getDelay(tc.containerName); got != tc.expectedDelay { 179 t.Errorf("expected delay for container %s = %d, got %d", tc.containerName, tc.expectedDelay, got) 180 } 181 } 182 } 183 184 func TestTerminationOrderingObeysGrace(t *testing.T) { 185 restartPolicy := v1.ContainerRestartPolicyAlways 186 pod := &v1.Pod{ 187 ObjectMeta: metav1.ObjectMeta{ 188 UID: "12345678", 189 Name: "bar", 190 Namespace: "new", 191 }, 192 Spec: v1.PodSpec{ 193 InitContainers: []v1.Container{ 194 { 195 Name: "sc1", 196 Image: "busybox", 197 ImagePullPolicy: v1.PullIfNotPresent, 198 RestartPolicy: &restartPolicy, 199 }, 200 { 201 Name: "sc2", 202 Image: "busybox", 203 ImagePullPolicy: v1.PullIfNotPresent, 204 RestartPolicy: &restartPolicy, 205 }, 206 { 207 Name: "sc3", 208 Image: "busybox", 209 ImagePullPolicy: v1.PullIfNotPresent, 210 RestartPolicy: &restartPolicy, 211 }, 212 }, 213 Containers: []v1.Container{ 214 { 215 Name: "main", 216 Image: "busybox", 217 ImagePullPolicy: v1.PullIfNotPresent, 218 }, 219 }, 220 }, 221 } 222 to := newTerminationOrdering(pod, getContainerNames(pod)) 223 224 var wg sync.WaitGroup 225 var delays sync.Map 226 227 waitAndExit := func(name string) { 228 // just a two second grace period which is not long enough for all of the waits to finish 229 delay := int64(to.waitForTurn(name, 2)) 230 delays.Store(name, delay) 231 time.Sleep(1 * time.Second) 232 to.containerTerminated(name) 233 wg.Done() 234 } 235 for _, ic := range pod.Spec.InitContainers { 236 wg.Add(1) 237 go waitAndExit(ic.Name) 238 } 239 for _, c := range pod.Spec.Containers { 240 wg.Add(1) 241 go waitAndExit(c.Name) 242 } 243 244 // wait for our simulated containers to exit 245 wg.Wait() 246 247 getDelay := func(name string) int64 { 248 delay, ok := delays.Load(name) 249 if !ok { 250 t.Errorf("unable to find delay for container %s", name) 251 } 252 return delay.(int64) 253 } 254 255 for _, tc := range []struct { 256 containerName string 257 expectedDelay int64 258 }{ 259 { 260 containerName: "sc1", 261 // overall grace period limits the amount of time waited here 262 expectedDelay: 2, 263 }, 264 { 265 containerName: "sc2", 266 expectedDelay: 2, 267 }, 268 { 269 containerName: "sc3", 270 expectedDelay: 1, 271 }, 272 { 273 containerName: "main", 274 expectedDelay: 0, 275 }, 276 } { 277 if got := getDelay(tc.containerName); got != tc.expectedDelay { 278 t.Errorf("expected delay for container %s = %d, got %d", tc.containerName, tc.expectedDelay, got) 279 } 280 } 281 } 282 283 func getContainerNames(p *v1.Pod) []string { 284 var running []string 285 for _, ic := range p.Spec.InitContainers { 286 running = append(running, ic.Name) 287 } 288 for _, c := range p.Spec.Containers { 289 running = append(running, c.Name) 290 } 291 return running 292 }