projected_downwardapi.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. /*
  2. Copyright 2014 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package common
  14. import (
  15. "fmt"
  16. "time"
  17. "k8s.io/api/core/v1"
  18. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  19. "k8s.io/apimachinery/pkg/util/uuid"
  20. "k8s.io/kubernetes/test/e2e/framework"
  21. imageutils "k8s.io/kubernetes/test/utils/image"
  22. "github.com/onsi/ginkgo"
  23. "github.com/onsi/gomega"
  24. )
  25. var _ = ginkgo.Describe("[sig-storage] Projected downwardAPI", func() {
  26. f := framework.NewDefaultFramework("projected")
  27. // How long to wait for a log pod to be displayed
  28. const podLogTimeout = 2 * time.Minute
  29. var podClient *framework.PodClient
  30. ginkgo.BeforeEach(func() {
  31. podClient = f.PodClient()
  32. })
  33. /*
  34. Release : v1.9
  35. Testname: Projected Volume, DownwardAPI, pod name
  36. Description: A Pod is created with a projected volume source for downwardAPI with pod name, cpu and memory limits and cpu and memory requests. Pod MUST be able to read the pod name from the mounted DownwardAPIVolumeFiles.
  37. */
  38. framework.ConformanceIt("should provide podname only [NodeConformance]", func() {
  39. podName := "downwardapi-volume-" + string(uuid.NewUUID())
  40. pod := downwardAPIVolumePodForSimpleTest(podName, "/etc/podinfo/podname")
  41. f.TestContainerOutput("downward API volume plugin", pod, 0, []string{
  42. fmt.Sprintf("%s\n", podName),
  43. })
  44. })
  45. /*
  46. Release : v1.9
  47. Testname: Projected Volume, DownwardAPI, volume mode 0400
  48. Description: A Pod is created with a projected volume source for downwardAPI with pod name, cpu and memory limits and cpu and memory requests. The default mode for the volume mount is set to 0400. Pod MUST be able to read the pod name from the mounted DownwardAPIVolumeFiles and the volume mode must be -r—-—————.
  49. This test is marked LinuxOnly since Windows does not support setting specific file permissions.
  50. */
  51. framework.ConformanceIt("should set DefaultMode on files [LinuxOnly] [NodeConformance]", func() {
  52. podName := "downwardapi-volume-" + string(uuid.NewUUID())
  53. defaultMode := int32(0400)
  54. pod := projectedDownwardAPIVolumePodForModeTest(podName, "/etc/podinfo/podname", nil, &defaultMode)
  55. f.TestContainerOutput("downward API volume plugin", pod, 0, []string{
  56. "mode of file \"/etc/podinfo/podname\": -r--------",
  57. })
  58. })
  59. /*
  60. Release : v1.9
  61. Testname: Projected Volume, DownwardAPI, volume mode 0400
  62. Description: A Pod is created with a projected volume source for downwardAPI with pod name, cpu and memory limits and cpu and memory requests. The default mode for the volume mount is set to 0400. Pod MUST be able to read the pod name from the mounted DownwardAPIVolumeFiles and the volume mode must be -r—-—————.
  63. This test is marked LinuxOnly since Windows does not support setting specific file permissions.
  64. */
  65. framework.ConformanceIt("should set mode on item file [LinuxOnly] [NodeConformance]", func() {
  66. podName := "downwardapi-volume-" + string(uuid.NewUUID())
  67. mode := int32(0400)
  68. pod := projectedDownwardAPIVolumePodForModeTest(podName, "/etc/podinfo/podname", &mode, nil)
  69. f.TestContainerOutput("downward API volume plugin", pod, 0, []string{
  70. "mode of file \"/etc/podinfo/podname\": -r--------",
  71. })
  72. })
  73. ginkgo.It("should provide podname as non-root with fsgroup [NodeFeature:FSGroup]", func() {
  74. podName := "metadata-volume-" + string(uuid.NewUUID())
  75. uid := int64(1001)
  76. gid := int64(1234)
  77. pod := downwardAPIVolumePodForSimpleTest(podName, "/etc/podinfo/podname")
  78. pod.Spec.SecurityContext = &v1.PodSecurityContext{
  79. RunAsUser: &uid,
  80. FSGroup: &gid,
  81. }
  82. f.TestContainerOutput("downward API volume plugin", pod, 0, []string{
  83. fmt.Sprintf("%s\n", podName),
  84. })
  85. })
  86. ginkgo.It("should provide podname as non-root with fsgroup and defaultMode [NodeFeature:FSGroup]", func() {
  87. podName := "metadata-volume-" + string(uuid.NewUUID())
  88. uid := int64(1001)
  89. gid := int64(1234)
  90. mode := int32(0440) /* setting fsGroup sets mode to at least 440 */
  91. pod := projectedDownwardAPIVolumePodForModeTest(podName, "/etc/podinfo/podname", &mode, nil)
  92. pod.Spec.SecurityContext = &v1.PodSecurityContext{
  93. RunAsUser: &uid,
  94. FSGroup: &gid,
  95. }
  96. f.TestContainerOutput("downward API volume plugin", pod, 0, []string{
  97. "mode of file \"/etc/podinfo/podname\": -r--r-----",
  98. })
  99. })
  100. /*
  101. Release : v1.9
  102. Testname: Projected Volume, DownwardAPI, update labels
  103. Description: A Pod is created with a projected volume source for downwardAPI with pod name, cpu and memory limits and cpu and memory requests and label items. Pod MUST be able to read the labels from the mounted DownwardAPIVolumeFiles. Labels are then updated. Pod MUST be able to read the updated values for the Labels.
  104. */
  105. framework.ConformanceIt("should update labels on modification [NodeConformance]", func() {
  106. labels := map[string]string{}
  107. labels["key1"] = "value1"
  108. labels["key2"] = "value2"
  109. podName := "labelsupdate" + string(uuid.NewUUID())
  110. pod := projectedDownwardAPIVolumePodForUpdateTest(podName, labels, map[string]string{}, "/etc/podinfo/labels")
  111. containerName := "client-container"
  112. ginkgo.By("Creating the pod")
  113. podClient.CreateSync(pod)
  114. gomega.Eventually(func() (string, error) {
  115. return framework.GetPodLogs(f.ClientSet, f.Namespace.Name, podName, containerName)
  116. },
  117. podLogTimeout, framework.Poll).Should(gomega.ContainSubstring("key1=\"value1\"\n"))
  118. //modify labels
  119. podClient.Update(podName, func(pod *v1.Pod) {
  120. pod.Labels["key3"] = "value3"
  121. })
  122. gomega.Eventually(func() (string, error) {
  123. return framework.GetPodLogs(f.ClientSet, f.Namespace.Name, pod.Name, containerName)
  124. },
  125. podLogTimeout, framework.Poll).Should(gomega.ContainSubstring("key3=\"value3\"\n"))
  126. })
  127. /*
  128. Release : v1.9
  129. Testname: Projected Volume, DownwardAPI, update annotation
  130. Description: A Pod is created with a projected volume source for downwardAPI with pod name, cpu and memory limits and cpu and memory requests and annotation items. Pod MUST be able to read the annotations from the mounted DownwardAPIVolumeFiles. Annotations are then updated. Pod MUST be able to read the updated values for the Annotations.
  131. */
  132. framework.ConformanceIt("should update annotations on modification [NodeConformance]", func() {
  133. annotations := map[string]string{}
  134. annotations["builder"] = "bar"
  135. podName := "annotationupdate" + string(uuid.NewUUID())
  136. pod := projectedDownwardAPIVolumePodForUpdateTest(podName, map[string]string{}, annotations, "/etc/podinfo/annotations")
  137. containerName := "client-container"
  138. ginkgo.By("Creating the pod")
  139. podClient.CreateSync(pod)
  140. pod, err := podClient.Get(pod.Name, metav1.GetOptions{})
  141. framework.ExpectNoError(err, "Failed to get pod %q", pod.Name)
  142. gomega.Eventually(func() (string, error) {
  143. return framework.GetPodLogs(f.ClientSet, f.Namespace.Name, pod.Name, containerName)
  144. },
  145. podLogTimeout, framework.Poll).Should(gomega.ContainSubstring("builder=\"bar\"\n"))
  146. //modify annotations
  147. podClient.Update(podName, func(pod *v1.Pod) {
  148. pod.Annotations["builder"] = "foo"
  149. })
  150. gomega.Eventually(func() (string, error) {
  151. return framework.GetPodLogs(f.ClientSet, f.Namespace.Name, pod.Name, containerName)
  152. },
  153. podLogTimeout, framework.Poll).Should(gomega.ContainSubstring("builder=\"foo\"\n"))
  154. })
  155. /*
  156. Release : v1.9
  157. Testname: Projected Volume, DownwardAPI, CPU limits
  158. Description: A Pod is created with a projected volume source for downwardAPI with pod name, cpu and memory limits and cpu and memory requests. Pod MUST be able to read the cpu limits from the mounted DownwardAPIVolumeFiles.
  159. */
  160. framework.ConformanceIt("should provide container's cpu limit [NodeConformance]", func() {
  161. podName := "downwardapi-volume-" + string(uuid.NewUUID())
  162. pod := downwardAPIVolumeForContainerResources(podName, "/etc/podinfo/cpu_limit")
  163. f.TestContainerOutput("downward API volume plugin", pod, 0, []string{
  164. fmt.Sprintf("2\n"),
  165. })
  166. })
  167. /*
  168. Release : v1.9
  169. Testname: Projected Volume, DownwardAPI, memory limits
  170. Description: A Pod is created with a projected volume source for downwardAPI with pod name, cpu and memory limits and cpu and memory requests. Pod MUST be able to read the memory limits from the mounted DownwardAPIVolumeFiles.
  171. */
  172. framework.ConformanceIt("should provide container's memory limit [NodeConformance]", func() {
  173. podName := "downwardapi-volume-" + string(uuid.NewUUID())
  174. pod := downwardAPIVolumeForContainerResources(podName, "/etc/podinfo/memory_limit")
  175. f.TestContainerOutput("downward API volume plugin", pod, 0, []string{
  176. fmt.Sprintf("67108864\n"),
  177. })
  178. })
  179. /*
  180. Release : v1.9
  181. Testname: Projected Volume, DownwardAPI, CPU request
  182. Description: A Pod is created with a projected volume source for downwardAPI with pod name, cpu and memory limits and cpu and memory requests. Pod MUST be able to read the cpu request from the mounted DownwardAPIVolumeFiles.
  183. */
  184. framework.ConformanceIt("should provide container's cpu request [NodeConformance]", func() {
  185. podName := "downwardapi-volume-" + string(uuid.NewUUID())
  186. pod := downwardAPIVolumeForContainerResources(podName, "/etc/podinfo/cpu_request")
  187. f.TestContainerOutput("downward API volume plugin", pod, 0, []string{
  188. fmt.Sprintf("1\n"),
  189. })
  190. })
  191. /*
  192. Release : v1.9
  193. Testname: Projected Volume, DownwardAPI, memory request
  194. Description: A Pod is created with a projected volume source for downwardAPI with pod name, cpu and memory limits and cpu and memory requests. Pod MUST be able to read the memory request from the mounted DownwardAPIVolumeFiles.
  195. */
  196. framework.ConformanceIt("should provide container's memory request [NodeConformance]", func() {
  197. podName := "downwardapi-volume-" + string(uuid.NewUUID())
  198. pod := downwardAPIVolumeForContainerResources(podName, "/etc/podinfo/memory_request")
  199. f.TestContainerOutput("downward API volume plugin", pod, 0, []string{
  200. fmt.Sprintf("33554432\n"),
  201. })
  202. })
  203. /*
  204. Release : v1.9
  205. Testname: Projected Volume, DownwardAPI, CPU limit, node allocatable
  206. Description: A Pod is created with a projected volume source for downwardAPI with pod name, cpu and memory limits and cpu and memory requests. The CPU and memory resources for requests and limits are NOT specified for the container. Pod MUST be able to read the default cpu limits from the mounted DownwardAPIVolumeFiles.
  207. */
  208. framework.ConformanceIt("should provide node allocatable (cpu) as default cpu limit if the limit is not set [NodeConformance]", func() {
  209. podName := "downwardapi-volume-" + string(uuid.NewUUID())
  210. pod := downwardAPIVolumeForDefaultContainerResources(podName, "/etc/podinfo/cpu_limit")
  211. f.TestContainerOutputRegexp("downward API volume plugin", pod, 0, []string{"[1-9]"})
  212. })
  213. /*
  214. Release : v1.9
  215. Testname: Projected Volume, DownwardAPI, memory limit, node allocatable
  216. Description: A Pod is created with a projected volume source for downwardAPI with pod name, cpu and memory limits and cpu and memory requests. The CPU and memory resources for requests and limits are NOT specified for the container. Pod MUST be able to read the default memory limits from the mounted DownwardAPIVolumeFiles.
  217. */
  218. framework.ConformanceIt("should provide node allocatable (memory) as default memory limit if the limit is not set [NodeConformance]", func() {
  219. podName := "downwardapi-volume-" + string(uuid.NewUUID())
  220. pod := downwardAPIVolumeForDefaultContainerResources(podName, "/etc/podinfo/memory_limit")
  221. f.TestContainerOutputRegexp("downward API volume plugin", pod, 0, []string{"[1-9]"})
  222. })
  223. })
  224. func projectedDownwardAPIVolumePodForModeTest(name, filePath string, itemMode, defaultMode *int32) *v1.Pod {
  225. pod := projectedDownwardAPIVolumeBasePod(name, nil, nil)
  226. pod.Spec.Containers = []v1.Container{
  227. {
  228. Name: "client-container",
  229. Image: imageutils.GetE2EImage(imageutils.Mounttest),
  230. Command: []string{"/mounttest", "--file_mode=" + filePath},
  231. VolumeMounts: []v1.VolumeMount{
  232. {
  233. Name: "podinfo",
  234. MountPath: "/etc/podinfo",
  235. },
  236. },
  237. },
  238. }
  239. if itemMode != nil {
  240. pod.Spec.Volumes[0].VolumeSource.Projected.Sources[0].DownwardAPI.Items[0].Mode = itemMode
  241. }
  242. if defaultMode != nil {
  243. pod.Spec.Volumes[0].VolumeSource.Projected.DefaultMode = defaultMode
  244. }
  245. return pod
  246. }
  247. func projectedDownwardAPIVolumePodForUpdateTest(name string, labels, annotations map[string]string, filePath string) *v1.Pod {
  248. pod := projectedDownwardAPIVolumeBasePod(name, labels, annotations)
  249. pod.Spec.Containers = []v1.Container{
  250. {
  251. Name: "client-container",
  252. Image: imageutils.GetE2EImage(imageutils.Mounttest),
  253. Command: []string{"/mounttest", "--break_on_expected_content=false", "--retry_time=1200", "--file_content_in_loop=" + filePath},
  254. VolumeMounts: []v1.VolumeMount{
  255. {
  256. Name: "podinfo",
  257. MountPath: "/etc/podinfo",
  258. ReadOnly: false,
  259. },
  260. },
  261. },
  262. }
  263. applyLabelsAndAnnotationsToProjectedDownwardAPIPod(labels, annotations, pod)
  264. return pod
  265. }
  266. func projectedDownwardAPIVolumeBasePod(name string, labels, annotations map[string]string) *v1.Pod {
  267. pod := &v1.Pod{
  268. ObjectMeta: metav1.ObjectMeta{
  269. Name: name,
  270. Labels: labels,
  271. Annotations: annotations,
  272. },
  273. Spec: v1.PodSpec{
  274. Volumes: []v1.Volume{
  275. {
  276. Name: "podinfo",
  277. VolumeSource: v1.VolumeSource{
  278. Projected: &v1.ProjectedVolumeSource{
  279. Sources: []v1.VolumeProjection{
  280. {
  281. DownwardAPI: &v1.DownwardAPIProjection{
  282. Items: []v1.DownwardAPIVolumeFile{
  283. {
  284. Path: "podname",
  285. FieldRef: &v1.ObjectFieldSelector{
  286. APIVersion: "v1",
  287. FieldPath: "metadata.name",
  288. },
  289. },
  290. {
  291. Path: "cpu_limit",
  292. ResourceFieldRef: &v1.ResourceFieldSelector{
  293. ContainerName: "client-container",
  294. Resource: "limits.cpu",
  295. },
  296. },
  297. {
  298. Path: "cpu_request",
  299. ResourceFieldRef: &v1.ResourceFieldSelector{
  300. ContainerName: "client-container",
  301. Resource: "requests.cpu",
  302. },
  303. },
  304. {
  305. Path: "memory_limit",
  306. ResourceFieldRef: &v1.ResourceFieldSelector{
  307. ContainerName: "client-container",
  308. Resource: "limits.memory",
  309. },
  310. },
  311. {
  312. Path: "memory_request",
  313. ResourceFieldRef: &v1.ResourceFieldSelector{
  314. ContainerName: "client-container",
  315. Resource: "requests.memory",
  316. },
  317. },
  318. },
  319. },
  320. },
  321. },
  322. },
  323. },
  324. },
  325. },
  326. RestartPolicy: v1.RestartPolicyNever,
  327. },
  328. }
  329. return pod
  330. }
  331. func applyLabelsAndAnnotationsToProjectedDownwardAPIPod(labels, annotations map[string]string, pod *v1.Pod) {
  332. if len(labels) > 0 {
  333. pod.Spec.Volumes[0].VolumeSource.Projected.Sources[0].DownwardAPI.Items = append(pod.Spec.Volumes[0].VolumeSource.Projected.Sources[0].DownwardAPI.Items, v1.DownwardAPIVolumeFile{
  334. Path: "labels",
  335. FieldRef: &v1.ObjectFieldSelector{
  336. APIVersion: "v1",
  337. FieldPath: "metadata.labels",
  338. },
  339. })
  340. }
  341. if len(annotations) > 0 {
  342. pod.Spec.Volumes[0].VolumeSource.Projected.Sources[0].DownwardAPI.Items = append(pod.Spec.Volumes[0].VolumeSource.Projected.Sources[0].DownwardAPI.Items, v1.DownwardAPIVolumeFile{
  343. Path: "annotations",
  344. FieldRef: &v1.ObjectFieldSelector{
  345. APIVersion: "v1",
  346. FieldPath: "metadata.annotations",
  347. },
  348. })
  349. }
  350. }