k8s.io/kubernetes@v1.29.3/pkg/kubelet/kuberuntime/kuberuntime_termination_order.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 "time" 21 22 v1 "k8s.io/api/core/v1" 23 24 "k8s.io/kubernetes/pkg/kubelet/types" 25 ) 26 27 // terminationOrdering is used to enforce a termination ordering for sidecar containers. It sets up 28 // dependencies between sidecars and allows the pod termination process to wait until the grace period 29 // expires, or all dependent containers have finished terminating. 30 type terminationOrdering struct { 31 // terminated is a map from container name to a channel, that if closed 32 // indicates that the container with that name was terminated 33 terminated map[string]chan struct{} 34 // prereqs is a map from container name to a list of channel that the container 35 // must wait on to ensure termination ordering 36 prereqs map[string][]chan struct{} 37 } 38 39 // newTerminationOrdering constructs a terminationOrdering based on the pod spec and the currently running containers. 40 func newTerminationOrdering(pod *v1.Pod, runningContainerNames []string) *terminationOrdering { 41 to := &terminationOrdering{ 42 prereqs: map[string][]chan struct{}{}, 43 terminated: map[string]chan struct{}{}, 44 } 45 46 runningContainers := map[string]struct{}{} 47 for _, name := range runningContainerNames { 48 runningContainers[name] = struct{}{} 49 } 50 51 var mainContainerChannels []chan struct{} 52 // sidecar containers need to wait on main containers, so we create a channel per main container 53 // for them to wait on 54 for _, c := range pod.Spec.Containers { 55 channel := make(chan struct{}) 56 to.terminated[c.Name] = channel 57 mainContainerChannels = append(mainContainerChannels, channel) 58 59 // if its not a running container, pre-close the channel so nothing waits on it 60 if _, isRunning := runningContainers[c.Name]; !isRunning { 61 close(channel) 62 } 63 } 64 65 var previousSidecarName string 66 for i := range pod.Spec.InitContainers { 67 // get the init containers in reverse order 68 ic := pod.Spec.InitContainers[len(pod.Spec.InitContainers)-i-1] 69 70 to.terminated[ic.Name] = make(chan struct{}) 71 72 if types.IsRestartableInitContainer(&ic) { 73 // sidecars need to wait for all main containers to exit 74 to.prereqs[ic.Name] = append(to.prereqs[ic.Name], mainContainerChannels...) 75 76 // if there is a later sidecar, this container needs to wait for it to finish 77 if previousSidecarName != "" { 78 to.prereqs[ic.Name] = append(to.prereqs[ic.Name], to.terminated[previousSidecarName]) 79 } 80 previousSidecarName = ic.Name 81 } 82 } 83 return to 84 } 85 86 // waitForTurn waits until it is time for the container with the specified name to begin terminating, up until 87 // the specified grace period. If gracePeriod = 0, there is no wait. 88 func (o *terminationOrdering) waitForTurn(name string, gracePeriod int64) float64 { 89 // if there is no grace period, we don't wait 90 if gracePeriod <= 0 { 91 return 0 92 } 93 94 start := time.Now() 95 remainingGrace := time.NewTimer(time.Duration(gracePeriod) * time.Second) 96 97 for _, c := range o.prereqs[name] { 98 select { 99 case <-c: 100 case <-remainingGrace.C: 101 // grace period expired, so immediately exit 102 return time.Since(start).Seconds() 103 } 104 } 105 106 return time.Since(start).Seconds() 107 } 108 109 // containerTerminated should be called once the container with the speecified name has exited. 110 func (o *terminationOrdering) containerTerminated(name string) { 111 if ch, ok := o.terminated[name]; ok { 112 close(ch) 113 } 114 }