k8s.io/kubernetes@v1.29.3/test/e2e_node/checkpoint_container.go (about) 1 /* 2 Copyright 2022 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 e2enode 18 19 import ( 20 "archive/tar" 21 "context" 22 "encoding/json" 23 "fmt" 24 "io" 25 "net/http" 26 "os" 27 "strings" 28 "time" 29 30 "github.com/onsi/ginkgo/v2" 31 v1 "k8s.io/api/core/v1" 32 apierrors "k8s.io/apimachinery/pkg/api/errors" 33 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 34 clientset "k8s.io/client-go/kubernetes" 35 restclient "k8s.io/client-go/rest" 36 "k8s.io/kubernetes/test/e2e/framework" 37 e2epod "k8s.io/kubernetes/test/e2e/framework/pod" 38 "k8s.io/kubernetes/test/e2e/nodefeature" 39 testutils "k8s.io/kubernetes/test/utils" 40 imageutils "k8s.io/kubernetes/test/utils/image" 41 admissionapi "k8s.io/pod-security-admission/api" 42 ) 43 44 const ( 45 // timeout for proxy requests. 46 proxyTimeout = 2 * time.Minute 47 ) 48 49 type checkpointResult struct { 50 Items []string `json:"items"` 51 } 52 53 // proxyPostRequest performs a post on a node proxy endpoint given the nodename and rest client. 54 func proxyPostRequest(ctx context.Context, c clientset.Interface, node, endpoint string, port int) (restclient.Result, error) { 55 // proxy tends to hang in some cases when Node is not ready. Add an artificial timeout for this call. #22165 56 var result restclient.Result 57 finished := make(chan struct{}, 1) 58 go func() { 59 result = c.CoreV1().RESTClient().Post(). 60 Resource("nodes"). 61 SubResource("proxy"). 62 Name(fmt.Sprintf("%v:%v", node, port)). 63 Suffix(endpoint). 64 Do(ctx) 65 66 finished <- struct{}{} 67 }() 68 select { 69 case <-finished: 70 return result, nil 71 case <-ctx.Done(): 72 return restclient.Result{}, nil 73 case <-time.After(proxyTimeout): 74 return restclient.Result{}, nil 75 } 76 } 77 78 var _ = SIGDescribe("Checkpoint Container", nodefeature.CheckpointContainer, func() { 79 f := framework.NewDefaultFramework("checkpoint-container-test") 80 f.NamespacePodSecurityLevel = admissionapi.LevelBaseline 81 ginkgo.It("will checkpoint a container out of a pod", func(ctx context.Context) { 82 ginkgo.By("creating a target pod") 83 podClient := e2epod.NewPodClient(f) 84 pod := podClient.CreateSync(ctx, &v1.Pod{ 85 ObjectMeta: metav1.ObjectMeta{Name: "checkpoint-container-pod"}, 86 Spec: v1.PodSpec{ 87 Containers: []v1.Container{ 88 { 89 Name: "test-container-1", 90 Image: imageutils.GetE2EImage(imageutils.BusyBox), 91 Command: []string{"/bin/sleep"}, 92 Args: []string{"10000"}, 93 }, 94 }, 95 }, 96 }) 97 98 p, err := podClient.Get( 99 ctx, 100 pod.Name, 101 metav1.GetOptions{}, 102 ) 103 104 framework.ExpectNoError(err) 105 isReady, err := testutils.PodRunningReady(p) 106 framework.ExpectNoError(err) 107 if !isReady { 108 framework.Failf("pod %q should be ready", p.Name) 109 } 110 111 framework.Logf( 112 "About to checkpoint container %q on %q", 113 pod.Spec.Containers[0].Name, 114 pod.Spec.NodeName, 115 ) 116 result, err := proxyPostRequest( 117 ctx, 118 f.ClientSet, 119 pod.Spec.NodeName, 120 fmt.Sprintf( 121 "checkpoint/%s/%s/%s", 122 f.Namespace.Name, 123 pod.Name, 124 pod.Spec.Containers[0].Name, 125 ), 126 framework.KubeletPort, 127 ) 128 129 framework.ExpectNoError(err) 130 131 err = result.Error() 132 if err != nil { 133 statusError, ok := err.(*apierrors.StatusError) 134 if !ok { 135 framework.Failf("got error %#v, expected StatusError", err) 136 } 137 // If we are testing against a kubelet with ContainerCheckpoint == false 138 // we should get a 404. So a 404 is (also) a good sign. 139 if (int(statusError.ErrStatus.Code)) == http.StatusNotFound { 140 ginkgo.Skip("Feature 'ContainerCheckpoint' is not enabled and not available") 141 return 142 } 143 144 // If the container engine has not implemented the Checkpoint CRI API 145 // we will get 500 and a message with 146 // '(rpc error: code = Unimplemented desc = unknown method CheckpointContainer' 147 if (int(statusError.ErrStatus.Code)) == http.StatusInternalServerError { 148 if strings.Contains( 149 statusError.ErrStatus.Message, 150 "(rpc error: code = Unimplemented desc = unknown method CheckpointContainer", 151 ) { 152 ginkgo.Skip("Container engine does not implement 'CheckpointContainer'") 153 return 154 } 155 } 156 framework.Failf("Unexpected status code (%d) during 'CheckpointContainer'", statusError.ErrStatus.Code) 157 } 158 159 framework.ExpectNoError(err) 160 161 // Checkpointing actually worked. Verify that the checkpoint exists and that 162 // it is a checkpoint. 163 164 raw, err := result.Raw() 165 framework.ExpectNoError(err) 166 answer := checkpointResult{} 167 err = json.Unmarshal(raw, &answer) 168 framework.ExpectNoError(err) 169 170 for _, item := range answer.Items { 171 // Check that the file exists 172 _, err := os.Stat(item) 173 framework.ExpectNoError(err) 174 // Check the content of the tar file 175 // At least looking for the following files 176 // * spec.dump 177 // * config.dump 178 // * checkpoint/inventory.img 179 // If these files exist in the checkpoint archive it is 180 // probably a complete checkpoint. 181 checkForFiles := map[string]bool{ 182 "spec.dump": false, 183 "config.dump": false, 184 "checkpoint/inventory.img": false, 185 } 186 fileReader, err := os.Open(item) 187 framework.ExpectNoError(err) 188 tr := tar.NewReader(fileReader) 189 for { 190 hdr, err := tr.Next() 191 if err == io.EOF { 192 // End of archive 193 break 194 } 195 framework.ExpectNoError(err) 196 if _, key := checkForFiles[hdr.Name]; key { 197 checkForFiles[hdr.Name] = true 198 } 199 } 200 for fileName := range checkForFiles { 201 if !checkForFiles[fileName] { 202 framework.Failf("File %q not found in checkpoint archive %q", fileName, item) 203 } 204 } 205 // cleanup checkpoint archive 206 os.RemoveAll(item) 207 } 208 }) 209 })