...

Source file src/k8s.io/kubernetes/pkg/kubelet/kuberuntime/kuberuntime_termination_order.go

Documentation: k8s.io/kubernetes/pkg/kubelet/kuberuntime

     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  }
   115  

View as plain text