k8s.io/kubernetes@v1.29.3/test/e2e_node/standalone_test.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 e2enode 18 19 import ( 20 "context" 21 "crypto/tls" 22 "encoding/json" 23 "fmt" 24 "io" 25 "net/http" 26 "os" 27 "strings" 28 "time" 29 30 v1 "k8s.io/api/core/v1" 31 "k8s.io/apimachinery/pkg/api/resource" 32 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 33 "k8s.io/apimachinery/pkg/runtime/schema" 34 "k8s.io/apimachinery/pkg/util/uuid" 35 "k8s.io/cli-runtime/pkg/printers" 36 "k8s.io/kubernetes/pkg/cluster/ports" 37 "k8s.io/kubernetes/test/e2e/feature" 38 "k8s.io/kubernetes/test/e2e/framework" 39 imageutils "k8s.io/kubernetes/test/utils/image" 40 admissionapi "k8s.io/pod-security-admission/api" 41 42 "github.com/onsi/ginkgo/v2" 43 "github.com/onsi/gomega" 44 apierrors "k8s.io/apimachinery/pkg/api/errors" 45 testutils "k8s.io/kubernetes/test/utils" 46 ) 47 48 var _ = SIGDescribe(feature.StandaloneMode, func() { 49 f := framework.NewDefaultFramework("static-pod") 50 f.NamespacePodSecurityLevel = admissionapi.LevelBaseline 51 ginkgo.Context("when creating a static pod", func() { 52 var ns, podPath, staticPodName string 53 54 ginkgo.It("the pod should be running", func(ctx context.Context) { 55 ns = f.Namespace.Name 56 staticPodName = "static-pod-" + string(uuid.NewUUID()) 57 podPath = kubeletCfg.StaticPodPath 58 59 err := createBasicStaticPod(podPath, staticPodName, ns) 60 framework.ExpectNoError(err) 61 62 gomega.Eventually(ctx, func(ctx context.Context) error { 63 pod, err := getPodFromStandaloneKubelet(ctx, ns, staticPodName) 64 if err != nil { 65 return fmt.Errorf("error getting pod(%v/%v) from standalone kubelet: %v", ns, staticPodName, err) 66 } 67 68 isReady, err := testutils.PodRunningReady(pod) 69 if err != nil { 70 return fmt.Errorf("error checking if pod (%v/%v) is running ready: %v", ns, staticPodName, err) 71 } 72 if !isReady { 73 return fmt.Errorf("pod (%v/%v) is not running", ns, staticPodName) 74 } 75 return nil 76 }, f.Timeouts.PodStart, time.Second*5).Should(gomega.BeNil()) 77 }) 78 79 ginkgo.AfterEach(func(ctx context.Context) { 80 ginkgo.By(fmt.Sprintf("delete the static pod (%v/%v)", ns, staticPodName)) 81 err := deleteStaticPod(podPath, staticPodName, ns) 82 framework.ExpectNoError(err) 83 84 ginkgo.By(fmt.Sprintf("wait for pod to disappear (%v/%v)", ns, staticPodName)) 85 gomega.Eventually(ctx, func(ctx context.Context) error { 86 _, err := getPodFromStandaloneKubelet(ctx, ns, staticPodName) 87 88 if apierrors.IsNotFound(err) { 89 return nil 90 } 91 return fmt.Errorf("pod (%v/%v) still exists", ns, staticPodName) 92 }).Should(gomega.Succeed()) 93 }) 94 }) 95 }) 96 97 func createBasicStaticPod(dir, name, namespace string) error { 98 podSpec := &v1.Pod{ 99 TypeMeta: metav1.TypeMeta{ 100 Kind: "Pod", 101 APIVersion: "v1", 102 }, 103 ObjectMeta: metav1.ObjectMeta{ 104 Name: name, 105 Namespace: namespace, 106 }, 107 Spec: v1.PodSpec{ 108 RestartPolicy: v1.RestartPolicyAlways, 109 Containers: []v1.Container{ 110 { 111 Name: "regular1", 112 Image: imageutils.GetE2EImage(imageutils.BusyBox), 113 Command: []string{ 114 "/bin/sh", "-c", "touch /tmp/healthy; sleep 10000", 115 }, 116 Resources: v1.ResourceRequirements{ 117 Requests: v1.ResourceList{ 118 v1.ResourceMemory: resource.MustParse("15Mi"), 119 }, 120 Limits: v1.ResourceList{ 121 v1.ResourceMemory: resource.MustParse("15Mi"), 122 }, 123 }, 124 ReadinessProbe: &v1.Probe{ 125 InitialDelaySeconds: 2, 126 TimeoutSeconds: 2, 127 ProbeHandler: v1.ProbeHandler{ 128 Exec: &v1.ExecAction{ 129 Command: []string{"/bin/sh", "-c", "cat /tmp/healthy"}, 130 }, 131 }, 132 }, 133 }, 134 }, 135 }, 136 } 137 138 file := staticPodPath(dir, name, namespace) 139 f, err := os.OpenFile(file, os.O_RDWR|os.O_TRUNC|os.O_CREATE, 0666) 140 if err != nil { 141 return err 142 } 143 defer f.Close() 144 145 y := printers.YAMLPrinter{} 146 y.PrintObj(podSpec, f) 147 148 return nil 149 } 150 151 func getPodFromStandaloneKubelet(ctx context.Context, podNamespace string, podName string) (*v1.Pod, error) { 152 endpoint := fmt.Sprintf("http://127.0.0.1:%d/pods", ports.KubeletReadOnlyPort) 153 // TODO: we do not need TLS and bearer token for this test 154 tr := &http.Transport{ 155 TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 156 } 157 client := &http.Client{Transport: tr} 158 req, err := http.NewRequest("GET", endpoint, nil) 159 framework.ExpectNoError(err) 160 req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", framework.TestContext.BearerToken)) 161 req.Header.Add("Accept", "application/json") 162 163 resp, err := client.Do(req) 164 if err != nil { 165 framework.Logf("Failed to get /pods: %v", err) 166 return nil, err 167 } 168 defer resp.Body.Close() 169 170 if resp.StatusCode != 200 { 171 framework.Logf("/pods response status not 200. Response was: %+v", resp) 172 return nil, fmt.Errorf("/pods response was not 200: %v", err) 173 } 174 175 respBody, err := io.ReadAll(resp.Body) 176 if err != nil { 177 return nil, fmt.Errorf("/pods response was unable to be read: %v", err) 178 } 179 180 pods, err := decodePods(respBody) 181 if err != nil { 182 return nil, fmt.Errorf("unable to decode /pods: %v", err) 183 } 184 185 for _, p := range pods.Items { 186 // Static pods has a node name suffix so comparing as substring 187 p := p 188 if strings.Contains(p.Name, podName) && strings.Contains(p.Namespace, podNamespace) { 189 return &p, nil 190 } 191 } 192 193 return nil, apierrors.NewNotFound(schema.GroupResource{Resource: "pods"}, podName) 194 } 195 196 // Decodes the http response from /configz and returns a kubeletconfig.KubeletConfiguration (internal type). 197 func decodePods(respBody []byte) (*v1.PodList, error) { 198 // This hack because /pods reports the following structure: 199 // {"kind":"PodList","apiVersion":"v1","metadata":{},"items":[{"metadata":{"name":"kube-dns-autoscaler-758c4689b9-htpqj","generateName":"kube-dns-autoscaler-758c4689b9-", 200 201 var pods v1.PodList 202 err := json.Unmarshal(respBody, &pods) 203 if err != nil { 204 return nil, err 205 } 206 207 return &pods, nil 208 }