...

Source file src/k8s.io/kubernetes/test/e2e/node/pre_stop.go

Documentation: k8s.io/kubernetes/test/e2e/node

     1  /*
     2  Copyright 2014 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 node
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"net"
    24  	"time"
    25  
    26  	v1 "k8s.io/api/core/v1"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/util/uuid"
    29  	"k8s.io/apimachinery/pkg/util/wait"
    30  	clientset "k8s.io/client-go/kubernetes"
    31  	"k8s.io/kubernetes/pkg/cluster/ports"
    32  	"k8s.io/kubernetes/test/e2e/framework"
    33  	e2ekubelet "k8s.io/kubernetes/test/e2e/framework/kubelet"
    34  	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
    35  	imageutils "k8s.io/kubernetes/test/utils/image"
    36  	admissionapi "k8s.io/pod-security-admission/api"
    37  
    38  	"github.com/onsi/ginkgo/v2"
    39  )
    40  
    41  // State partially cloned from webserver.go
    42  type State struct {
    43  	Received map[string]int
    44  }
    45  
    46  func testPreStop(ctx context.Context, c clientset.Interface, ns string) {
    47  	// This is the server that will receive the preStop notification
    48  	podDescr := e2epod.NewAgnhostPod(ns, "server", nil, nil, []v1.ContainerPort{{ContainerPort: 8080}}, "nettest")
    49  	ginkgo.By(fmt.Sprintf("Creating server pod %s in namespace %s", podDescr.Name, ns))
    50  	podDescr, err := c.CoreV1().Pods(ns).Create(ctx, podDescr, metav1.CreateOptions{})
    51  	framework.ExpectNoError(err, fmt.Sprintf("creating pod %s", podDescr.Name))
    52  
    53  	// At the end of the test, clean up by removing the pod.
    54  	ginkgo.DeferCleanup(func(ctx context.Context) error {
    55  		ginkgo.By("Deleting the server pod")
    56  		return c.CoreV1().Pods(ns).Delete(ctx, podDescr.Name, metav1.DeleteOptions{})
    57  	})
    58  
    59  	ginkgo.By("Waiting for pods to come up.")
    60  	err = e2epod.WaitForPodRunningInNamespace(ctx, c, podDescr)
    61  	framework.ExpectNoError(err, "waiting for server pod to start")
    62  
    63  	val := "{\"Source\": \"prestop\"}"
    64  
    65  	podOut, err := c.CoreV1().Pods(ns).Get(ctx, podDescr.Name, metav1.GetOptions{})
    66  	framework.ExpectNoError(err, "getting pod info")
    67  
    68  	podURL := net.JoinHostPort(podOut.Status.PodIP, "8080")
    69  
    70  	preStopDescr := &v1.Pod{
    71  		ObjectMeta: metav1.ObjectMeta{
    72  			Name: "tester",
    73  		},
    74  		Spec: v1.PodSpec{
    75  			Containers: []v1.Container{
    76  				{
    77  					Name:    "tester",
    78  					Image:   imageutils.GetE2EImage(imageutils.BusyBox),
    79  					Command: []string{"sleep", "600"},
    80  					Lifecycle: &v1.Lifecycle{
    81  						PreStop: &v1.LifecycleHandler{
    82  							Exec: &v1.ExecAction{
    83  								Command: []string{
    84  									"wget", "-O-", "--post-data=" + val, fmt.Sprintf("http://%s/write", podURL),
    85  								},
    86  							},
    87  						},
    88  					},
    89  				},
    90  			},
    91  		},
    92  	}
    93  
    94  	ginkgo.By(fmt.Sprintf("Creating tester pod %s in namespace %s", preStopDescr.Name, ns))
    95  	preStopDescr, err = c.CoreV1().Pods(ns).Create(ctx, preStopDescr, metav1.CreateOptions{})
    96  	framework.ExpectNoError(err, fmt.Sprintf("creating pod %s", preStopDescr.Name))
    97  	deletePreStop := true
    98  
    99  	// At the end of the test, clean up by removing the pod.
   100  	ginkgo.DeferCleanup(func(ctx context.Context) error {
   101  		if deletePreStop {
   102  			ginkgo.By("Deleting the tester pod")
   103  			return c.CoreV1().Pods(ns).Delete(ctx, preStopDescr.Name, metav1.DeleteOptions{})
   104  		}
   105  		return nil
   106  	})
   107  
   108  	err = e2epod.WaitForPodRunningInNamespace(ctx, c, preStopDescr)
   109  	framework.ExpectNoError(err, "waiting for tester pod to start")
   110  
   111  	// Delete the pod with the preStop handler.
   112  	ginkgo.By("Deleting pre-stop pod")
   113  	if err := c.CoreV1().Pods(ns).Delete(ctx, preStopDescr.Name, metav1.DeleteOptions{}); err == nil {
   114  		deletePreStop = false
   115  	}
   116  	framework.ExpectNoError(err, fmt.Sprintf("deleting pod: %s", preStopDescr.Name))
   117  
   118  	// Validate that the server received the web poke.
   119  	err = wait.Poll(time.Second*5, time.Second*60, func() (bool, error) {
   120  
   121  		ctx, cancel := context.WithTimeout(ctx, framework.SingleCallTimeout)
   122  		defer cancel()
   123  
   124  		var body []byte
   125  		body, err = c.CoreV1().RESTClient().Get().
   126  			Namespace(ns).
   127  			Resource("pods").
   128  			SubResource("proxy").
   129  			Name(podDescr.Name).
   130  			Suffix("read").
   131  			DoRaw(ctx)
   132  
   133  		if err != nil {
   134  			if ctx.Err() != nil {
   135  				framework.Failf("Error validating prestop: %v", err)
   136  				return true, err
   137  			}
   138  			ginkgo.By(fmt.Sprintf("Error validating prestop: %v", err))
   139  		} else {
   140  			framework.Logf("Saw: %s", string(body))
   141  			state := State{}
   142  			err := json.Unmarshal(body, &state)
   143  			if err != nil {
   144  				framework.Logf("Error parsing: %v", err)
   145  				return false, nil
   146  			}
   147  			if state.Received["prestop"] != 0 {
   148  				return true, nil
   149  			}
   150  		}
   151  		return false, nil
   152  	})
   153  	framework.ExpectNoError(err, "validating pre-stop.")
   154  }
   155  
   156  var _ = SIGDescribe("PreStop", func() {
   157  	f := framework.NewDefaultFramework("prestop")
   158  	f.NamespacePodSecurityLevel = admissionapi.LevelBaseline
   159  	var podClient *e2epod.PodClient
   160  	ginkgo.BeforeEach(func() {
   161  		podClient = e2epod.NewPodClient(f)
   162  	})
   163  
   164  	/*
   165  		Release: v1.9
   166  		Testname: Pods, prestop hook
   167  		Description: Create a server pod with a rest endpoint '/write' that changes state.Received field. Create a Pod with a pre-stop handle that posts to the /write endpoint on the server Pod. Verify that the Pod with pre-stop hook is running. Delete the Pod with the pre-stop hook. Before the Pod is deleted, pre-stop handler MUST be called when configured. Verify that the Pod is deleted and a call to prestop hook is verified by checking the status received on the server Pod.
   168  	*/
   169  	framework.ConformanceIt("should call prestop when killing a pod", func(ctx context.Context) {
   170  		testPreStop(ctx, f.ClientSet, f.Namespace.Name)
   171  	})
   172  
   173  	ginkgo.It("graceful pod terminated should wait until preStop hook completes the process", func(ctx context.Context) {
   174  		gracefulTerminationPeriodSeconds := int64(30)
   175  		ginkgo.By("creating the pod")
   176  		name := "pod-prestop-hook-" + string(uuid.NewUUID())
   177  		pod := getPodWithpreStopLifeCycle(name)
   178  
   179  		ginkgo.By("submitting the pod to kubernetes")
   180  		podClient.Create(ctx, pod)
   181  
   182  		ginkgo.By("waiting for pod running")
   183  		framework.ExpectNoError(e2epod.WaitForPodNameRunningInNamespace(ctx, f.ClientSet, pod.Name, f.Namespace.Name))
   184  
   185  		var err error
   186  		pod, err = podClient.Get(ctx, pod.Name, metav1.GetOptions{})
   187  		framework.ExpectNoError(err, "failed to GET scheduled pod")
   188  
   189  		ginkgo.By("deleting the pod gracefully")
   190  		err = podClient.Delete(ctx, pod.Name, *metav1.NewDeleteOptions(gracefulTerminationPeriodSeconds))
   191  		framework.ExpectNoError(err, "failed to delete pod")
   192  
   193  		// wait for less than the gracePeriod termination ensuring the
   194  		// preStop hook is still executing.
   195  		time.Sleep(15 * time.Second)
   196  
   197  		ginkgo.By("verifying the pod is running while in the graceful period termination")
   198  		result := &v1.PodList{}
   199  		err = wait.Poll(time.Second*5, time.Second*60, func() (bool, error) {
   200  			client, err := e2ekubelet.ProxyRequest(ctx, f.ClientSet, pod.Spec.NodeName, "pods", ports.KubeletPort)
   201  			framework.ExpectNoError(err, "failed to get the pods of the node")
   202  			err = client.Into(result)
   203  			framework.ExpectNoError(err, "failed to parse the pods of the node")
   204  
   205  			for _, kubeletPod := range result.Items {
   206  				if pod.Name != kubeletPod.Name {
   207  					continue
   208  				} else if kubeletPod.Status.Phase == v1.PodRunning {
   209  					framework.Logf("pod is running")
   210  					return true, err
   211  				}
   212  			}
   213  			return false, err
   214  		})
   215  
   216  		framework.ExpectNoError(err, "validate-pod-is-running")
   217  	})
   218  })
   219  
   220  func getPodWithpreStopLifeCycle(name string) *v1.Pod {
   221  	return &v1.Pod{
   222  		ObjectMeta: metav1.ObjectMeta{
   223  			Name: name,
   224  		},
   225  		Spec: v1.PodSpec{
   226  			Containers: []v1.Container{
   227  				{
   228  					Name:  "nginx",
   229  					Image: imageutils.GetE2EImage(imageutils.Nginx),
   230  					Lifecycle: &v1.Lifecycle{
   231  						PreStop: &v1.LifecycleHandler{
   232  							Exec: &v1.ExecAction{
   233  								Command: []string{"sh", "-c", "while true; do echo preStop; sleep 1; done"},
   234  							},
   235  						},
   236  					},
   237  				},
   238  			},
   239  		},
   240  	}
   241  }
   242  

View as plain text