...

Source file src/k8s.io/kubernetes/pkg/kubelet/kuberuntime/kuberuntime_termination_order_test.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  	"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  }
   293  

View as plain text