1
16
17 package node
18
19 import (
20 "context"
21 "fmt"
22
23 "github.com/onsi/ginkgo/v2"
24 "github.com/onsi/gomega"
25
26 v1 "k8s.io/api/core/v1"
27 apierrors "k8s.io/apimachinery/pkg/api/errors"
28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29 "k8s.io/apimachinery/pkg/util/uuid"
30 "k8s.io/kubernetes/pkg/kubelet/sysctl"
31
32 "k8s.io/kubernetes/test/e2e/framework"
33 e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
34 e2eoutput "k8s.io/kubernetes/test/e2e/framework/pod/output"
35 "k8s.io/kubernetes/test/e2e/upgrades"
36 imageutils "k8s.io/kubernetes/test/utils/image"
37 )
38
39
40
41
42 type SysctlUpgradeTest struct {
43 validPod *v1.Pod
44 invalidPod *v1.Pod
45 }
46
47
48
49 func (t *SysctlUpgradeTest) Setup(ctx context.Context, f *framework.Framework) {
50 t.validPod = t.verifySafeSysctlWork(ctx, f)
51 t.invalidPod = t.verifyUnsafeSysctlsAreRejected(ctx, f)
52 }
53
54
55
56 func (t *SysctlUpgradeTest) Test(ctx context.Context, f *framework.Framework, done <-chan struct{}, upgrade upgrades.UpgradeType) {
57 <-done
58 switch upgrade {
59 case upgrades.MasterUpgrade, upgrades.ClusterUpgrade:
60 ginkgo.By("Checking the safe sysctl pod keeps running on master upgrade")
61 pod, err := f.ClientSet.CoreV1().Pods(t.validPod.Namespace).Get(ctx, t.validPod.Name, metav1.GetOptions{})
62 framework.ExpectNoError(err)
63 gomega.Expect(pod.Status.Phase).To(gomega.Equal(v1.PodRunning))
64 }
65
66 ginkgo.By("Checking the old unsafe sysctl pod was not suddenly started during an upgrade")
67 pod, err := f.ClientSet.CoreV1().Pods(t.invalidPod.Namespace).Get(ctx, t.invalidPod.Name, metav1.GetOptions{})
68 if err != nil && !apierrors.IsNotFound(err) {
69 framework.ExpectNoError(err)
70 }
71 if err == nil {
72 gomega.Expect(pod.Status.Phase).NotTo(gomega.Equal(v1.PodRunning))
73 }
74
75 t.verifySafeSysctlWork(ctx, f)
76 t.verifyUnsafeSysctlsAreRejected(ctx, f)
77 }
78
79
80 func (t *SysctlUpgradeTest) Teardown(ctx context.Context, f *framework.Framework) {
81
82 }
83
84 func (t *SysctlUpgradeTest) verifySafeSysctlWork(ctx context.Context, f *framework.Framework) *v1.Pod {
85 ginkgo.By("Creating a pod with safe sysctls")
86 safeSysctl := "net.ipv4.ip_local_port_range"
87 safeSysctlValue := "1024 1042"
88 sysctlTestPod("valid-sysctls", map[string]string{safeSysctl: safeSysctlValue})
89 validPod := e2epod.NewPodClient(f).Create(ctx, t.validPod)
90
91 ginkgo.By("Making sure the valid pod launches")
92 _, err := e2epod.NewPodClient(f).WaitForErrorEventOrSuccess(ctx, t.validPod)
93 framework.ExpectNoError(err)
94 e2eoutput.TestContainerOutput(ctx, f, "pod with safe sysctl launched", t.validPod, 0, []string{fmt.Sprintf("%s = %s", safeSysctl, safeSysctlValue)})
95
96 return validPod
97 }
98
99 func (t *SysctlUpgradeTest) verifyUnsafeSysctlsAreRejected(ctx context.Context, f *framework.Framework) *v1.Pod {
100 ginkgo.By("Creating a pod with unsafe sysctls")
101 invalidPod := sysctlTestPod("valid-sysctls-"+string(uuid.NewUUID()), map[string]string{
102 "fs.mount-max": "1000000",
103 })
104 invalidPod = e2epod.NewPodClient(f).Create(ctx, invalidPod)
105
106 ginkgo.By("Making sure the invalid pod failed")
107 ev, err := e2epod.NewPodClient(f).WaitForErrorEventOrSuccess(ctx, invalidPod)
108 framework.ExpectNoError(err)
109 gomega.Expect(ev.Reason).To(gomega.Equal(sysctl.ForbiddenReason))
110
111 return invalidPod
112 }
113
114 func sysctlTestPod(name string, sysctls map[string]string) *v1.Pod {
115 sysctlList := []v1.Sysctl{}
116 keys := []string{}
117 for k, v := range sysctls {
118 sysctlList = append(sysctlList, v1.Sysctl{Name: k, Value: v})
119 keys = append(keys, k)
120 }
121 return &v1.Pod{
122 ObjectMeta: metav1.ObjectMeta{
123 Name: name,
124 },
125 Spec: v1.PodSpec{
126 Containers: []v1.Container{
127 {
128 Name: "test-container",
129 Image: imageutils.GetE2EImage(imageutils.BusyBox),
130 Command: append([]string{"/bin/sysctl"}, keys...),
131 },
132 },
133 RestartPolicy: v1.RestartPolicyNever,
134 SecurityContext: &v1.PodSecurityContext{
135 Sysctls: sysctlList,
136 },
137 },
138 }
139 }
140
View as plain text