1
16
17 package pod
18
19 import (
20 "context"
21 "fmt"
22 "time"
23
24 v1 "k8s.io/api/core/v1"
25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26 "k8s.io/apimachinery/pkg/util/uuid"
27 clientset "k8s.io/client-go/kubernetes"
28 imageutils "k8s.io/kubernetes/test/utils/image"
29 admissionapi "k8s.io/pod-security-admission/api"
30 )
31
32 const (
33 VolumeMountPathTemplate = "/mnt/volume%d"
34 VolumeMountPath1 = "/mnt/volume1"
35 )
36
37
38
39 type Config struct {
40 NS string
41 PVCs []*v1.PersistentVolumeClaim
42 PVCsReadOnly bool
43 InlineVolumeSources []*v1.VolumeSource
44 SecurityLevel admissionapi.Level
45 Command string
46 HostIPC bool
47 HostPID bool
48 SeLinuxLabel *v1.SELinuxOptions
49 FsGroup *int64
50 NodeSelection NodeSelection
51 ImageID imageutils.ImageID
52 PodFSGroupChangePolicy *v1.PodFSGroupChangePolicy
53 }
54
55
56 func CreateUnschedulablePod(ctx context.Context, client clientset.Interface, namespace string, nodeSelector map[string]string, pvclaims []*v1.PersistentVolumeClaim, securityLevel admissionapi.Level, command string) (*v1.Pod, error) {
57 pod := MakePod(namespace, nodeSelector, pvclaims, securityLevel, command)
58 pod, err := client.CoreV1().Pods(namespace).Create(ctx, pod, metav1.CreateOptions{})
59 if err != nil {
60 return nil, fmt.Errorf("pod Create API error: %w", err)
61 }
62
63 err = WaitForPodNameUnschedulableInNamespace(ctx, client, pod.Name, namespace)
64 if err != nil {
65 return pod, fmt.Errorf("pod %q is not Unschedulable: %w", pod.Name, err)
66 }
67
68 pod, err = client.CoreV1().Pods(namespace).Get(ctx, pod.Name, metav1.GetOptions{})
69 if err != nil {
70 return pod, fmt.Errorf("pod Get API error: %w", err)
71 }
72 return pod, nil
73 }
74
75
76 func CreateClientPod(ctx context.Context, c clientset.Interface, ns string, pvc *v1.PersistentVolumeClaim) (*v1.Pod, error) {
77 return CreatePod(ctx, c, ns, nil, []*v1.PersistentVolumeClaim{pvc}, admissionapi.LevelPrivileged, "")
78 }
79
80
81 func CreatePod(ctx context.Context, client clientset.Interface, namespace string, nodeSelector map[string]string, pvclaims []*v1.PersistentVolumeClaim, securityLevel admissionapi.Level, command string) (*v1.Pod, error) {
82 pod := MakePod(namespace, nodeSelector, pvclaims, securityLevel, command)
83 pod, err := client.CoreV1().Pods(namespace).Create(ctx, pod, metav1.CreateOptions{})
84 if err != nil {
85 return nil, fmt.Errorf("pod Create API error: %w", err)
86 }
87
88 err = WaitForPodNameRunningInNamespace(ctx, client, pod.Name, namespace)
89 if err != nil {
90 return pod, fmt.Errorf("pod %q is not Running: %w", pod.Name, err)
91 }
92
93 pod, err = client.CoreV1().Pods(namespace).Get(ctx, pod.Name, metav1.GetOptions{})
94 if err != nil {
95 return pod, fmt.Errorf("pod Get API error: %w", err)
96 }
97 return pod, nil
98 }
99
100
101 func CreateSecPod(ctx context.Context, client clientset.Interface, podConfig *Config, timeout time.Duration) (*v1.Pod, error) {
102 return CreateSecPodWithNodeSelection(ctx, client, podConfig, timeout)
103 }
104
105
106 func CreateSecPodWithNodeSelection(ctx context.Context, client clientset.Interface, podConfig *Config, timeout time.Duration) (*v1.Pod, error) {
107 pod, err := MakeSecPod(podConfig)
108 if err != nil {
109 return nil, fmt.Errorf("Unable to create pod: %w", err)
110 }
111
112 pod, err = client.CoreV1().Pods(podConfig.NS).Create(ctx, pod, metav1.CreateOptions{})
113 if err != nil {
114 return nil, fmt.Errorf("pod Create API error: %w", err)
115 }
116
117
118 err = WaitTimeoutForPodRunningInNamespace(ctx, client, pod.Name, podConfig.NS, timeout)
119 if err != nil {
120 return pod, fmt.Errorf("pod %q is not Running: %w", pod.Name, err)
121 }
122
123 pod, err = client.CoreV1().Pods(podConfig.NS).Get(ctx, pod.Name, metav1.GetOptions{})
124 if err != nil {
125 return pod, fmt.Errorf("pod Get API error: %w", err)
126 }
127 return pod, nil
128 }
129
130
131
132 func MakePod(ns string, nodeSelector map[string]string, pvclaims []*v1.PersistentVolumeClaim, securityLevel admissionapi.Level, command string) *v1.Pod {
133 if len(command) == 0 {
134 command = "trap exit TERM; while true; do sleep 1; done"
135 }
136 podSpec := &v1.Pod{
137 TypeMeta: metav1.TypeMeta{
138 Kind: "Pod",
139 APIVersion: "v1",
140 },
141 ObjectMeta: metav1.ObjectMeta{
142 GenerateName: "pvc-tester-",
143 Namespace: ns,
144 },
145 Spec: v1.PodSpec{
146 Containers: []v1.Container{
147 {
148 Name: "write-pod",
149 Image: GetDefaultTestImage(),
150 Command: GenerateScriptCmd(command),
151 SecurityContext: GenerateContainerSecurityContext(securityLevel),
152 },
153 },
154 RestartPolicy: v1.RestartPolicyOnFailure,
155 },
156 }
157 setVolumes(&podSpec.Spec, pvclaims, nil , false )
158 if nodeSelector != nil {
159 podSpec.Spec.NodeSelector = nodeSelector
160 }
161 if securityLevel == admissionapi.LevelRestricted {
162 podSpec = MustMixinRestrictedPodSecurity(podSpec)
163 }
164
165 return podSpec
166 }
167
168
169
170 func MakeSecPod(podConfig *Config) (*v1.Pod, error) {
171 if podConfig.NS == "" {
172 return nil, fmt.Errorf("Cannot create pod with empty namespace")
173 }
174 if len(podConfig.Command) == 0 {
175 podConfig.Command = "trap exit TERM; while true; do sleep 1; done"
176 }
177
178 podName := "pod-" + string(uuid.NewUUID())
179 if podConfig.FsGroup == nil && !NodeOSDistroIs("windows") {
180 podConfig.FsGroup = func(i int64) *int64 {
181 return &i
182 }(1000)
183 }
184 podSpec := &v1.Pod{
185 TypeMeta: metav1.TypeMeta{
186 Kind: "Pod",
187 APIVersion: "v1",
188 },
189 ObjectMeta: metav1.ObjectMeta{
190 Name: podName,
191 Namespace: podConfig.NS,
192 },
193 Spec: *MakePodSpec(podConfig),
194 }
195 return podSpec, nil
196 }
197
198
199 func MakePodSpec(podConfig *Config) *v1.PodSpec {
200 image := imageutils.BusyBox
201 if podConfig.ImageID != imageutils.None {
202 image = podConfig.ImageID
203 }
204 securityLevel := podConfig.SecurityLevel
205 if securityLevel == "" {
206 securityLevel = admissionapi.LevelBaseline
207 }
208 podSpec := &v1.PodSpec{
209 HostIPC: podConfig.HostIPC,
210 HostPID: podConfig.HostPID,
211 SecurityContext: GeneratePodSecurityContext(podConfig.FsGroup, podConfig.SeLinuxLabel),
212 Containers: []v1.Container{
213 {
214 Name: "write-pod",
215 Image: GetTestImage(image),
216 Command: GenerateScriptCmd(podConfig.Command),
217 SecurityContext: GenerateContainerSecurityContext(securityLevel),
218 },
219 },
220 RestartPolicy: v1.RestartPolicyOnFailure,
221 }
222
223 if podConfig.PodFSGroupChangePolicy != nil {
224 podSpec.SecurityContext.FSGroupChangePolicy = podConfig.PodFSGroupChangePolicy
225 }
226
227 setVolumes(podSpec, podConfig.PVCs, podConfig.InlineVolumeSources, podConfig.PVCsReadOnly)
228 SetNodeSelection(podSpec, podConfig.NodeSelection)
229 return podSpec
230 }
231
232 func setVolumes(podSpec *v1.PodSpec, pvcs []*v1.PersistentVolumeClaim, inlineVolumeSources []*v1.VolumeSource, pvcsReadOnly bool) {
233 var volumeMounts = make([]v1.VolumeMount, 0)
234 var volumeDevices = make([]v1.VolumeDevice, 0)
235 var volumes = make([]v1.Volume, len(pvcs)+len(inlineVolumeSources))
236 volumeIndex := 0
237 for _, pvclaim := range pvcs {
238 volumename := fmt.Sprintf("volume%v", volumeIndex+1)
239 volumeMountPath := fmt.Sprintf(VolumeMountPathTemplate, volumeIndex+1)
240 if pvclaim.Spec.VolumeMode != nil && *pvclaim.Spec.VolumeMode == v1.PersistentVolumeBlock {
241 volumeDevices = append(volumeDevices, v1.VolumeDevice{Name: volumename, DevicePath: volumeMountPath})
242 } else {
243 volumeMounts = append(volumeMounts, v1.VolumeMount{Name: volumename, MountPath: volumeMountPath})
244 }
245 volumes[volumeIndex] = v1.Volume{
246 Name: volumename,
247 VolumeSource: v1.VolumeSource{
248 PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
249 ClaimName: pvclaim.Name,
250 ReadOnly: pvcsReadOnly,
251 },
252 },
253 }
254 volumeIndex++
255 }
256 for _, src := range inlineVolumeSources {
257 volumename := fmt.Sprintf("volume%v", volumeIndex+1)
258 volumeMountPath := fmt.Sprintf(VolumeMountPathTemplate, volumeIndex+1)
259
260 volumeMounts = append(volumeMounts, v1.VolumeMount{Name: volumename, MountPath: volumeMountPath})
261 volumes[volumeIndex] = v1.Volume{Name: volumename, VolumeSource: *src}
262 volumeIndex++
263 }
264 podSpec.Containers[0].VolumeMounts = volumeMounts
265 podSpec.Containers[0].VolumeDevices = volumeDevices
266 podSpec.Volumes = volumes
267 }
268
View as plain text