mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-07-17 20:00:07 +00:00
add e2e tests for static pod and standalone mode
This commit is contained in:
@@ -32,8 +32,10 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/uuid"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||
"k8s.io/kubernetes/test/e2e/framework"
|
||||
e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
|
||||
imageutils "k8s.io/kubernetes/test/utils/image"
|
||||
admissionapi "k8s.io/pod-security-admission/api"
|
||||
"sigs.k8s.io/yaml"
|
||||
@@ -43,7 +45,6 @@ import (
|
||||
"github.com/onsi/gomega"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/cli-runtime/pkg/printers"
|
||||
e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
|
||||
e2evolume "k8s.io/kubernetes/test/e2e/framework/volume"
|
||||
)
|
||||
|
||||
@@ -384,6 +385,103 @@ var _ = SIGDescribe("MirrorPod (Pod Generation)", func() {
|
||||
})
|
||||
})
|
||||
|
||||
var _ = SIGDescribe("MirrorPod with EnvFiles", framework.WithFeatureGate(features.EnvFiles), func() {
|
||||
f := framework.NewDefaultFramework("mirror-pod-envfiles")
|
||||
f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged
|
||||
var ns, podPath, staticPodName, mirrorPodName string
|
||||
|
||||
ginkgo.Context("when creating a static pod with EnvFiles", func() {
|
||||
ginkgo.BeforeEach(func() {
|
||||
ns = f.Namespace.Name
|
||||
staticPodName = "static-pod-envfiles-" + string(uuid.NewUUID())
|
||||
mirrorPodName = staticPodName + "-" + framework.TestContext.NodeName
|
||||
podPath = kubeletCfg.StaticPodPath
|
||||
})
|
||||
|
||||
ginkgo.AfterEach(func(ctx context.Context) {
|
||||
ginkgo.By("delete the static pod")
|
||||
err := deleteStaticPod(podPath, staticPodName, ns)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
ginkgo.By("wait for the mirror pod to disappear")
|
||||
gomega.Eventually(ctx, func(ctx context.Context) error {
|
||||
return checkMirrorPodDisappear(ctx, f.ClientSet, mirrorPodName, ns)
|
||||
}, 2*time.Minute, time.Second*4).Should(gomega.Succeed())
|
||||
})
|
||||
|
||||
ginkgo.It("should be able to consume variables from a file", func(ctx context.Context) {
|
||||
podSpec := v1.PodSpec{
|
||||
InitContainers: []v1.Container{
|
||||
{
|
||||
Name: "setup-envfile",
|
||||
Image: imageutils.GetE2EImage(imageutils.BusyBox),
|
||||
Command: []string{"sh", "-c", `echo CONFIG_1='value1' > /data/config.env && echo CONFIG_2=\'value2\' >> /data/config.env`},
|
||||
VolumeMounts: []v1.VolumeMount{
|
||||
{
|
||||
Name: "config",
|
||||
MountPath: "/data",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "use-envfile",
|
||||
Image: imageutils.GetE2EImage(imageutils.BusyBox),
|
||||
Command: []string{"sh", "-c", "env | grep -E '(CONFIG_1|CONFIG_2)' | sort"},
|
||||
Env: []v1.EnvVar{
|
||||
{
|
||||
Name: "CONFIG_1",
|
||||
ValueFrom: &v1.EnvVarSource{
|
||||
FileKeyRef: &v1.FileKeySelector{
|
||||
VolumeName: "config",
|
||||
Path: "config.env",
|
||||
Key: "CONFIG_1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "CONFIG_2",
|
||||
ValueFrom: &v1.EnvVarSource{
|
||||
FileKeyRef: &v1.FileKeySelector{
|
||||
VolumeName: "config",
|
||||
Path: "config.env",
|
||||
Key: "CONFIG_2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
RestartPolicy: v1.RestartPolicyNever,
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: "config",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
EmptyDir: &v1.EmptyDirVolumeSource{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ginkgo.By("create the static pod with envfiles")
|
||||
err := createStaticPodWithSpec(podPath, staticPodName, ns, podSpec)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
ginkgo.By("wait for the mirror pod to succeed")
|
||||
err = e2epod.WaitForPodSuccessInNamespace(ctx, f.ClientSet, mirrorPodName, ns)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
ginkgo.By("checking the logs of the mirror pod")
|
||||
logs, err := e2epod.GetPodLogs(ctx, f.ClientSet, ns, mirrorPodName, "use-envfile")
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
gomega.Expect(logs).To(gomega.ContainSubstring("CONFIG_1=value1"))
|
||||
gomega.Expect(logs).To(gomega.ContainSubstring("CONFIG_2=value2"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
func podVolumeDirectoryExists(uid types.UID) bool {
|
||||
podVolumePath := fmt.Sprintf("/var/lib/kubelet/pods/%s/volumes/", uid)
|
||||
var podVolumeDirectoryExists bool
|
||||
|
||||
@@ -24,27 +24,151 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/uuid"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/cli-runtime/pkg/printers"
|
||||
"k8s.io/kubernetes/pkg/cluster/ports"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/kubernetes/test/e2e/feature"
|
||||
"k8s.io/kubernetes/test/e2e/framework"
|
||||
testutils "k8s.io/kubernetes/test/utils"
|
||||
imageutils "k8s.io/kubernetes/test/utils/image"
|
||||
admissionapi "k8s.io/pod-security-admission/api"
|
||||
|
||||
"github.com/onsi/ginkgo/v2"
|
||||
"github.com/onsi/gomega"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
testutils "k8s.io/kubernetes/test/utils"
|
||||
)
|
||||
|
||||
var _ = SIGDescribe(feature.StandaloneMode, framework.WithFeatureGate(features.EnvFiles), func() {
|
||||
f := framework.NewDefaultFramework("static-pod-envfiles")
|
||||
f.NamespacePodSecurityLevel = admissionapi.LevelBaseline
|
||||
ginkgo.Context("when creating a static pod with EnvFiles", func() {
|
||||
var ns, podPath, staticPodName string
|
||||
|
||||
ginkgo.It("the pod should be running and consume variables", func(ctx context.Context) {
|
||||
ns = f.Namespace.Name
|
||||
staticPodName = "static-pod-envfiles-" + string(uuid.NewUUID())
|
||||
podPath = kubeletCfg.StaticPodPath
|
||||
|
||||
podSpec := &v1.Pod{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Pod",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: staticPodName,
|
||||
Namespace: ns,
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
InitContainers: []v1.Container{
|
||||
{
|
||||
Name: "setup-envfile",
|
||||
Image: imageutils.GetE2EImage(imageutils.BusyBox),
|
||||
Command: []string{"sh", "-c", `echo CONFIG_1=\'value1\' > /data/config.env && echo CONFIG_2=\'value2\' >> /data/config.env`},
|
||||
VolumeMounts: []v1.VolumeMount{
|
||||
{
|
||||
Name: "config",
|
||||
MountPath: "/data",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "use-envfile",
|
||||
Image: imageutils.GetE2EImage(imageutils.BusyBox),
|
||||
Command: []string{"sh", "-c", "env | grep -E '(CONFIG_1|CONFIG_2)' | sort"},
|
||||
Env: []v1.EnvVar{
|
||||
{
|
||||
Name: "CONFIG_1",
|
||||
ValueFrom: &v1.EnvVarSource{
|
||||
FileKeyRef: &v1.FileKeySelector{
|
||||
VolumeName: "config",
|
||||
Path: "config.env",
|
||||
Key: "CONFIG_1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "CONFIG_2",
|
||||
ValueFrom: &v1.EnvVarSource{
|
||||
FileKeyRef: &v1.FileKeySelector{
|
||||
VolumeName: "config",
|
||||
Path: "config.env",
|
||||
Key: "CONFIG_2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
RestartPolicy: v1.RestartPolicyNever,
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: "config",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
EmptyDir: &v1.EmptyDirVolumeSource{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := scheduleStaticPod(podPath, staticPodName, ns, podSpec)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
gomega.Eventually(ctx, func(ctx context.Context) error {
|
||||
pod, err := getPodFromStandaloneKubelet(ctx, ns, staticPodName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting pod(%v/%v) from standalone kubelet: %w", ns, staticPodName, err)
|
||||
}
|
||||
if pod.Status.Phase == v1.PodSucceeded {
|
||||
return nil
|
||||
}
|
||||
if pod.Status.Phase == v1.PodFailed {
|
||||
logs, err := getPodLogsFromStandaloneKubelet(ctx, ns, staticPodName, "use-envfile")
|
||||
if err != nil {
|
||||
framework.Logf("failed to get logs on pod failure: %v", err)
|
||||
}
|
||||
return fmt.Errorf("pod (%v/%v) failed, logs: %s", ns, staticPodName, logs)
|
||||
}
|
||||
return fmt.Errorf("pod (%v/%v) is not succeeded, phase: %s", ns, staticPodName, pod.Status.Phase)
|
||||
}, f.Timeouts.PodStart, time.Second*5).Should(gomega.Succeed())
|
||||
|
||||
logs, err := getPodLogsFromStandaloneKubelet(ctx, ns, staticPodName, "use-envfile")
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
gomega.Expect(logs).To(gomega.ContainSubstring("CONFIG_1=value1"))
|
||||
gomega.Expect(logs).To(gomega.ContainSubstring("CONFIG_2=value2"))
|
||||
})
|
||||
|
||||
ginkgo.AfterEach(func(ctx context.Context) {
|
||||
ginkgo.By(fmt.Sprintf("delete the static pod (%v/%v)", ns, staticPodName))
|
||||
err := deleteStaticPod(podPath, staticPodName, ns)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
ginkgo.By(fmt.Sprintf("wait for pod to disappear (%v/%v)", ns, staticPodName))
|
||||
gomega.Eventually(ctx, func(ctx context.Context) error {
|
||||
_, err := getPodFromStandaloneKubelet(ctx, ns, staticPodName)
|
||||
|
||||
if apierrors.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("pod (%v/%v) still exists", ns, staticPodName)
|
||||
}).Should(gomega.Succeed())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
var _ = SIGDescribe(feature.StandaloneMode, func() {
|
||||
f := framework.NewDefaultFramework("static-pod")
|
||||
f.NamespacePodSecurityLevel = admissionapi.LevelBaseline
|
||||
@@ -315,6 +439,36 @@ func getPodFromStandaloneKubelet(ctx context.Context, podNamespace string, podNa
|
||||
return nil, apierrors.NewNotFound(schema.GroupResource{Resource: "pods"}, podName)
|
||||
}
|
||||
|
||||
func getPodLogsFromStandaloneKubelet(ctx context.Context, podNamespace string, podName string, containerName string) (string, error) {
|
||||
pod, err := getPodFromStandaloneKubelet(ctx, podNamespace, podName)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get pod %s/%s: %w", podNamespace, podName, err)
|
||||
}
|
||||
|
||||
logCRIDir := "/var/log/pods"
|
||||
podLogDir := filepath.Join(logCRIDir, fmt.Sprintf("%s_%s_%s", pod.Namespace, pod.Name, pod.UID))
|
||||
logFile := filepath.Join(podLogDir, containerName, "0.log")
|
||||
|
||||
var content []byte
|
||||
err = wait.PollUntilContextTimeout(ctx, time.Second, time.Minute, true, func(ctx context.Context) (bool, error) {
|
||||
var errRead error
|
||||
content, errRead = os.ReadFile(logFile)
|
||||
if errRead != nil {
|
||||
if os.IsNotExist(errRead) {
|
||||
return false, nil
|
||||
}
|
||||
return false, errRead
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not read log file %s: %w", logFile, err)
|
||||
}
|
||||
|
||||
return string(content), nil
|
||||
}
|
||||
|
||||
// Decodes the http response from /configz and returns a kubeletconfig.KubeletConfiguration (internal type).
|
||||
func decodePods(respBody []byte) (*v1.PodList, error) {
|
||||
// This hack because /pods reports the following structure:
|
||||
|
||||
Reference in New Issue
Block a user