admission_test.go 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858
  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 limitranger
  14. import (
  15. "fmt"
  16. "strconv"
  17. "testing"
  18. "time"
  19. corev1 "k8s.io/api/core/v1"
  20. apiequality "k8s.io/apimachinery/pkg/api/equality"
  21. "k8s.io/apimachinery/pkg/api/resource"
  22. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  23. "k8s.io/apimachinery/pkg/runtime"
  24. "k8s.io/apimachinery/pkg/util/wait"
  25. "k8s.io/apiserver/pkg/admission"
  26. genericadmissioninitializer "k8s.io/apiserver/pkg/admission/initializer"
  27. admissiontesting "k8s.io/apiserver/pkg/admission/testing"
  28. "k8s.io/client-go/informers"
  29. clientset "k8s.io/client-go/kubernetes"
  30. "k8s.io/client-go/kubernetes/fake"
  31. core "k8s.io/client-go/testing"
  32. api "k8s.io/kubernetes/pkg/apis/core"
  33. v1 "k8s.io/kubernetes/pkg/apis/core/v1"
  34. )
  35. func getComputeResourceList(cpu, memory string) api.ResourceList {
  36. res := api.ResourceList{}
  37. if cpu != "" {
  38. res[api.ResourceCPU] = resource.MustParse(cpu)
  39. }
  40. if memory != "" {
  41. res[api.ResourceMemory] = resource.MustParse(memory)
  42. }
  43. return res
  44. }
  45. func getStorageResourceList(storage string) api.ResourceList {
  46. res := api.ResourceList{}
  47. if storage != "" {
  48. res[api.ResourceStorage] = resource.MustParse(storage)
  49. }
  50. return res
  51. }
  52. func getResourceRequirements(requests, limits api.ResourceList) api.ResourceRequirements {
  53. res := api.ResourceRequirements{}
  54. res.Requests = requests
  55. res.Limits = limits
  56. return res
  57. }
  58. // createLimitRange creates a limit range with the specified data
  59. func createLimitRange(limitType api.LimitType, min, max, defaultLimit, defaultRequest, maxLimitRequestRatio api.ResourceList) corev1.LimitRange {
  60. internalLimitRage := api.LimitRange{
  61. ObjectMeta: metav1.ObjectMeta{
  62. Name: "abc",
  63. Namespace: "test",
  64. },
  65. Spec: api.LimitRangeSpec{
  66. Limits: []api.LimitRangeItem{
  67. {
  68. Type: limitType,
  69. Min: min,
  70. Max: max,
  71. Default: defaultLimit,
  72. DefaultRequest: defaultRequest,
  73. MaxLimitRequestRatio: maxLimitRequestRatio,
  74. },
  75. },
  76. },
  77. }
  78. externalLimitRange := corev1.LimitRange{}
  79. v1.Convert_core_LimitRange_To_v1_LimitRange(&internalLimitRage, &externalLimitRange, nil)
  80. return externalLimitRange
  81. }
  82. func validLimitRange() corev1.LimitRange {
  83. internalLimitRange := api.LimitRange{
  84. ObjectMeta: metav1.ObjectMeta{
  85. Name: "abc",
  86. Namespace: "test",
  87. },
  88. Spec: api.LimitRangeSpec{
  89. Limits: []api.LimitRangeItem{
  90. {
  91. Type: api.LimitTypePod,
  92. Max: getComputeResourceList("200m", "4Gi"),
  93. Min: getComputeResourceList("50m", "2Mi"),
  94. },
  95. {
  96. Type: api.LimitTypeContainer,
  97. Max: getComputeResourceList("100m", "2Gi"),
  98. Min: getComputeResourceList("25m", "1Mi"),
  99. Default: getComputeResourceList("75m", "10Mi"),
  100. DefaultRequest: getComputeResourceList("50m", "5Mi"),
  101. },
  102. },
  103. },
  104. }
  105. externalLimitRange := corev1.LimitRange{}
  106. v1.Convert_core_LimitRange_To_v1_LimitRange(&internalLimitRange, &externalLimitRange, nil)
  107. return externalLimitRange
  108. }
  109. func validLimitRangeNoDefaults() corev1.LimitRange {
  110. internalLimitRange := api.LimitRange{
  111. ObjectMeta: metav1.ObjectMeta{
  112. Name: "abc",
  113. Namespace: "test",
  114. },
  115. Spec: api.LimitRangeSpec{
  116. Limits: []api.LimitRangeItem{
  117. {
  118. Type: api.LimitTypePod,
  119. Max: getComputeResourceList("200m", "4Gi"),
  120. Min: getComputeResourceList("50m", "2Mi"),
  121. },
  122. {
  123. Type: api.LimitTypeContainer,
  124. Max: getComputeResourceList("100m", "2Gi"),
  125. Min: getComputeResourceList("25m", "1Mi"),
  126. },
  127. },
  128. },
  129. }
  130. externalLimitRange := corev1.LimitRange{}
  131. v1.Convert_core_LimitRange_To_v1_LimitRange(&internalLimitRange, &externalLimitRange, nil)
  132. return externalLimitRange
  133. }
  134. func validPod(name string, numContainers int, resources api.ResourceRequirements) api.Pod {
  135. pod := api.Pod{
  136. ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: "test"},
  137. Spec: api.PodSpec{},
  138. }
  139. pod.Spec.Containers = make([]api.Container, 0, numContainers)
  140. for i := 0; i < numContainers; i++ {
  141. pod.Spec.Containers = append(pod.Spec.Containers, api.Container{
  142. Image: "foo:V" + strconv.Itoa(i),
  143. Resources: resources,
  144. Name: "foo-" + strconv.Itoa(i),
  145. })
  146. }
  147. return pod
  148. }
  149. func validPodInit(pod api.Pod, resources ...api.ResourceRequirements) api.Pod {
  150. for i := 0; i < len(resources); i++ {
  151. pod.Spec.InitContainers = append(pod.Spec.InitContainers, api.Container{
  152. Image: "foo:V" + strconv.Itoa(i),
  153. Resources: resources[i],
  154. Name: "foo-" + strconv.Itoa(i),
  155. })
  156. }
  157. return pod
  158. }
  159. func TestDefaultContainerResourceRequirements(t *testing.T) {
  160. limitRange := validLimitRange()
  161. expected := api.ResourceRequirements{
  162. Requests: getComputeResourceList("50m", "5Mi"),
  163. Limits: getComputeResourceList("75m", "10Mi"),
  164. }
  165. actual := defaultContainerResourceRequirements(&limitRange)
  166. if !apiequality.Semantic.DeepEqual(expected, actual) {
  167. t.Errorf("actual.Limits != expected.Limits; %v != %v", actual.Limits, expected.Limits)
  168. t.Errorf("actual.Requests != expected.Requests; %v != %v", actual.Requests, expected.Requests)
  169. t.Errorf("expected != actual; %v != %v", expected, actual)
  170. }
  171. }
  172. func verifyAnnotation(t *testing.T, pod *api.Pod, expected string) {
  173. a, ok := pod.ObjectMeta.Annotations[limitRangerAnnotation]
  174. if !ok {
  175. t.Errorf("No annotation but expected %v", expected)
  176. }
  177. if a != expected {
  178. t.Errorf("Wrong annotation set by Limit Ranger: got %v, expected %v", a, expected)
  179. }
  180. }
  181. func expectNoAnnotation(t *testing.T, pod *api.Pod) {
  182. if a, ok := pod.ObjectMeta.Annotations[limitRangerAnnotation]; ok {
  183. t.Errorf("Expected no annotation but got %v", a)
  184. }
  185. }
  186. func TestMergePodResourceRequirements(t *testing.T) {
  187. limitRange := validLimitRange()
  188. // pod with no resources enumerated should get each resource from default request
  189. expected := getResourceRequirements(getComputeResourceList("", ""), getComputeResourceList("", ""))
  190. pod := validPod("empty-resources", 1, expected)
  191. defaultRequirements := defaultContainerResourceRequirements(&limitRange)
  192. mergePodResourceRequirements(&pod, &defaultRequirements)
  193. for i := range pod.Spec.Containers {
  194. actual := pod.Spec.Containers[i].Resources
  195. if !apiequality.Semantic.DeepEqual(expected, actual) {
  196. t.Errorf("pod %v, expected != actual; %v != %v", pod.Name, expected, actual)
  197. }
  198. }
  199. verifyAnnotation(t, &pod, "LimitRanger plugin set: cpu, memory request for container foo-0; cpu, memory limit for container foo-0")
  200. // pod with some resources enumerated should only merge empty
  201. input := getResourceRequirements(getComputeResourceList("", "512Mi"), getComputeResourceList("", ""))
  202. pod = validPodInit(validPod("limit-memory", 1, input), input)
  203. expected = api.ResourceRequirements{
  204. Requests: api.ResourceList{
  205. api.ResourceCPU: defaultRequirements.Requests[api.ResourceCPU],
  206. api.ResourceMemory: resource.MustParse("512Mi"),
  207. },
  208. Limits: defaultRequirements.Limits,
  209. }
  210. mergePodResourceRequirements(&pod, &defaultRequirements)
  211. for i := range pod.Spec.Containers {
  212. actual := pod.Spec.Containers[i].Resources
  213. if !apiequality.Semantic.DeepEqual(expected, actual) {
  214. t.Errorf("pod %v, expected != actual; %v != %v", pod.Name, expected, actual)
  215. }
  216. }
  217. for i := range pod.Spec.InitContainers {
  218. actual := pod.Spec.InitContainers[i].Resources
  219. if !apiequality.Semantic.DeepEqual(expected, actual) {
  220. t.Errorf("pod %v, expected != actual; %v != %v", pod.Name, expected, actual)
  221. }
  222. }
  223. verifyAnnotation(t, &pod, "LimitRanger plugin set: cpu request for container foo-0; cpu, memory limit for container foo-0")
  224. // pod with all resources enumerated should not merge anything
  225. input = getResourceRequirements(getComputeResourceList("100m", "512Mi"), getComputeResourceList("200m", "1G"))
  226. initInputs := []api.ResourceRequirements{getResourceRequirements(getComputeResourceList("200m", "1G"), getComputeResourceList("400m", "2G"))}
  227. pod = validPodInit(validPod("limit-memory", 1, input), initInputs...)
  228. expected = input
  229. mergePodResourceRequirements(&pod, &defaultRequirements)
  230. for i := range pod.Spec.Containers {
  231. actual := pod.Spec.Containers[i].Resources
  232. if !apiequality.Semantic.DeepEqual(expected, actual) {
  233. t.Errorf("pod %v, expected != actual; %v != %v", pod.Name, expected, actual)
  234. }
  235. }
  236. for i := range pod.Spec.InitContainers {
  237. actual := pod.Spec.InitContainers[i].Resources
  238. if !apiequality.Semantic.DeepEqual(initInputs[i], actual) {
  239. t.Errorf("pod %v, expected != actual; %v != %v", pod.Name, initInputs[i], actual)
  240. }
  241. }
  242. expectNoAnnotation(t, &pod)
  243. }
  244. func TestPodLimitFunc(t *testing.T) {
  245. type testCase struct {
  246. pod api.Pod
  247. limitRange corev1.LimitRange
  248. }
  249. successCases := []testCase{
  250. {
  251. pod: validPod("ctr-min-cpu-request", 1, getResourceRequirements(getComputeResourceList("100m", ""), getComputeResourceList("", ""))),
  252. limitRange: createLimitRange(api.LimitTypeContainer, getComputeResourceList("50m", ""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  253. },
  254. {
  255. pod: validPod("ctr-min-cpu-request-limit", 1, getResourceRequirements(getComputeResourceList("100m", ""), getComputeResourceList("200m", ""))),
  256. limitRange: createLimitRange(api.LimitTypeContainer, getComputeResourceList("50m", ""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  257. },
  258. {
  259. pod: validPod("ctr-min-memory-request", 1, getResourceRequirements(getComputeResourceList("", "60Mi"), getComputeResourceList("", ""))),
  260. limitRange: createLimitRange(api.LimitTypeContainer, getComputeResourceList("", "50Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  261. },
  262. {
  263. pod: validPod("ctr-min-memory-request-limit", 1, getResourceRequirements(getComputeResourceList("", "60Mi"), getComputeResourceList("", "100Mi"))),
  264. limitRange: createLimitRange(api.LimitTypeContainer, getComputeResourceList("", "50Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  265. },
  266. {
  267. pod: validPod("ctr-max-cpu-request-limit", 1, getResourceRequirements(getComputeResourceList("500m", ""), getComputeResourceList("1", ""))),
  268. limitRange: createLimitRange(api.LimitTypeContainer, api.ResourceList{}, getComputeResourceList("2", ""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  269. },
  270. {
  271. pod: validPod("ctr-max-cpu-limit", 1, getResourceRequirements(getComputeResourceList("", ""), getComputeResourceList("1", ""))),
  272. limitRange: createLimitRange(api.LimitTypeContainer, api.ResourceList{}, getComputeResourceList("2", ""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  273. },
  274. {
  275. pod: validPod("ctr-max-mem-request-limit", 1, getResourceRequirements(getComputeResourceList("", "250Mi"), getComputeResourceList("", "500Mi"))),
  276. limitRange: createLimitRange(api.LimitTypeContainer, api.ResourceList{}, getComputeResourceList("", "1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  277. },
  278. {
  279. pod: validPod("ctr-max-cpu-ratio", 1, getResourceRequirements(getComputeResourceList("500m", ""), getComputeResourceList("750m", ""))),
  280. limitRange: createLimitRange(api.LimitTypeContainer, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, getComputeResourceList("1.5", "")),
  281. },
  282. {
  283. pod: validPod("ctr-max-mem-limit", 1, getResourceRequirements(getComputeResourceList("", ""), getComputeResourceList("", "500Mi"))),
  284. limitRange: createLimitRange(api.LimitTypeContainer, api.ResourceList{}, getComputeResourceList("", "1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  285. },
  286. {
  287. pod: validPod("pod-min-cpu-request", 2, getResourceRequirements(getComputeResourceList("75m", ""), getComputeResourceList("", ""))),
  288. limitRange: createLimitRange(api.LimitTypePod, getComputeResourceList("100m", ""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  289. },
  290. {
  291. pod: validPod("pod-min-cpu-request-limit", 2, getResourceRequirements(getComputeResourceList("75m", ""), getComputeResourceList("200m", ""))),
  292. limitRange: createLimitRange(api.LimitTypePod, getComputeResourceList("100m", ""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  293. },
  294. {
  295. pod: validPod("pod-min-memory-request", 2, getResourceRequirements(getComputeResourceList("", "60Mi"), getComputeResourceList("", ""))),
  296. limitRange: createLimitRange(api.LimitTypePod, getComputeResourceList("", "100Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  297. },
  298. {
  299. pod: validPod("pod-min-memory-request-limit", 2, getResourceRequirements(getComputeResourceList("", "60Mi"), getComputeResourceList("", "100Mi"))),
  300. limitRange: createLimitRange(api.LimitTypePod, getComputeResourceList("", "100Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  301. },
  302. {
  303. pod: validPodInit(
  304. validPod("pod-init-min-memory-request", 2, getResourceRequirements(getComputeResourceList("", "60Mi"), getComputeResourceList("", ""))),
  305. getResourceRequirements(getComputeResourceList("", "100Mi"), getComputeResourceList("", "")),
  306. ),
  307. limitRange: createLimitRange(api.LimitTypePod, getComputeResourceList("", "100Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  308. },
  309. {
  310. pod: validPodInit(
  311. validPod("pod-init-min-memory-request-limit", 2, getResourceRequirements(getComputeResourceList("", "60Mi"), getComputeResourceList("", "100Mi"))),
  312. getResourceRequirements(getComputeResourceList("", "80Mi"), getComputeResourceList("", "100Mi")),
  313. ),
  314. limitRange: createLimitRange(api.LimitTypePod, getComputeResourceList("", "100Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  315. },
  316. {
  317. pod: validPod("pod-max-cpu-request-limit", 2, getResourceRequirements(getComputeResourceList("500m", ""), getComputeResourceList("1", ""))),
  318. limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getComputeResourceList("2", ""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  319. },
  320. {
  321. pod: validPod("pod-max-cpu-limit", 2, getResourceRequirements(getComputeResourceList("", ""), getComputeResourceList("1", ""))),
  322. limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getComputeResourceList("2", ""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  323. },
  324. {
  325. pod: validPodInit(
  326. validPod("pod-init-max-cpu-request-limit", 2, getResourceRequirements(getComputeResourceList("500m", ""), getComputeResourceList("1", ""))),
  327. getResourceRequirements(getComputeResourceList("1", ""), getComputeResourceList("2", "")),
  328. getResourceRequirements(getComputeResourceList("1", ""), getComputeResourceList("1", "")),
  329. ),
  330. limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getComputeResourceList("2", ""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  331. },
  332. {
  333. pod: validPodInit(
  334. validPod("pod-init-max-cpu-limit", 2, getResourceRequirements(getComputeResourceList("", ""), getComputeResourceList("1", ""))),
  335. getResourceRequirements(getComputeResourceList("", ""), getComputeResourceList("2", "")),
  336. getResourceRequirements(getComputeResourceList("", ""), getComputeResourceList("2", "")),
  337. ),
  338. limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getComputeResourceList("2", ""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  339. },
  340. {
  341. pod: validPod("pod-max-mem-request-limit", 2, getResourceRequirements(getComputeResourceList("", "250Mi"), getComputeResourceList("", "500Mi"))),
  342. limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getComputeResourceList("", "1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  343. },
  344. {
  345. pod: validPod("pod-max-mem-limit", 2, getResourceRequirements(getComputeResourceList("", ""), getComputeResourceList("", "500Mi"))),
  346. limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getComputeResourceList("", "1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  347. },
  348. {
  349. pod: validPod("pod-max-mem-ratio", 3, getResourceRequirements(getComputeResourceList("", "300Mi"), getComputeResourceList("", "450Mi"))),
  350. limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getComputeResourceList("", "2Gi"), api.ResourceList{}, api.ResourceList{}, getComputeResourceList("", "1.5")),
  351. },
  352. {
  353. pod: validPod("ctr-1-min-local-ephemeral-storage-request", 1, getResourceRequirements(getLocalStorageResourceList("60Mi"), getLocalStorageResourceList(""))),
  354. limitRange: createLimitRange(api.LimitTypeContainer, getLocalStorageResourceList("50Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  355. },
  356. {
  357. pod: validPod("ctr-1-min-local-ephemeral-storage-request-limit", 1, getResourceRequirements(getLocalStorageResourceList("60Mi"), getLocalStorageResourceList("100Mi"))),
  358. limitRange: createLimitRange(api.LimitTypeContainer, getLocalStorageResourceList("50Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  359. },
  360. {
  361. pod: validPod("ctr-1-max-local-ephemeral-storage-request-limit", 1, getResourceRequirements(getLocalStorageResourceList("250Mi"), getLocalStorageResourceList("500Mi"))),
  362. limitRange: createLimitRange(api.LimitTypeContainer, api.ResourceList{}, getLocalStorageResourceList("1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  363. },
  364. {
  365. pod: validPod("ctr-1-max-local-ephemeral-storage-limit", 1, getResourceRequirements(getLocalStorageResourceList(""), getLocalStorageResourceList("500Mi"))),
  366. limitRange: createLimitRange(api.LimitTypeContainer, api.ResourceList{}, getLocalStorageResourceList("1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  367. },
  368. {
  369. pod: validPod("ctr-2-min-local-ephemeral-storage-request", 2, getResourceRequirements(getLocalStorageResourceList("60Mi"), getLocalStorageResourceList(""))),
  370. limitRange: createLimitRange(api.LimitTypeContainer, getLocalStorageResourceList("50Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  371. },
  372. {
  373. pod: validPod("ctr-2-min-local-ephemeral-storage-request-limit", 2, getResourceRequirements(getLocalStorageResourceList("60Mi"), getLocalStorageResourceList("100Mi"))),
  374. limitRange: createLimitRange(api.LimitTypeContainer, getLocalStorageResourceList("50Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  375. },
  376. {
  377. pod: validPod("ctr-2-max-local-ephemeral-storage-request-limit", 2, getResourceRequirements(getLocalStorageResourceList("250Mi"), getLocalStorageResourceList("500Mi"))),
  378. limitRange: createLimitRange(api.LimitTypeContainer, api.ResourceList{}, getLocalStorageResourceList("600Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  379. },
  380. {
  381. pod: validPod("ctr-2-max-local-ephemeral-storage-limit", 2, getResourceRequirements(getLocalStorageResourceList(""), getLocalStorageResourceList("500Mi"))),
  382. limitRange: createLimitRange(api.LimitTypeContainer, api.ResourceList{}, getLocalStorageResourceList("600Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  383. },
  384. {
  385. pod: validPod("pod-min-local-ephemeral-storage-request", 2, getResourceRequirements(getLocalStorageResourceList("60Mi"), getLocalStorageResourceList(""))),
  386. limitRange: createLimitRange(api.LimitTypePod, getLocalStorageResourceList("100Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  387. },
  388. {
  389. pod: validPod("pod-min-local-ephemeral-storage-request-limit", 2, getResourceRequirements(getLocalStorageResourceList("60Mi"), getLocalStorageResourceList("100Mi"))),
  390. limitRange: createLimitRange(api.LimitTypePod, getLocalStorageResourceList("100Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  391. },
  392. {
  393. pod: validPodInit(
  394. validPod("pod-init-min-local-ephemeral-storage-request", 2, getResourceRequirements(getLocalStorageResourceList("60Mi"), getLocalStorageResourceList(""))),
  395. getResourceRequirements(getLocalStorageResourceList("100Mi"), getLocalStorageResourceList("")),
  396. ),
  397. limitRange: createLimitRange(api.LimitTypePod, getLocalStorageResourceList("100Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  398. },
  399. {
  400. pod: validPodInit(
  401. validPod("pod-init-min-local-ephemeral-storage-request-limit", 2, getResourceRequirements(getLocalStorageResourceList("60Mi"), getLocalStorageResourceList("100Mi"))),
  402. getResourceRequirements(getLocalStorageResourceList("80Mi"), getLocalStorageResourceList("100Mi")),
  403. ),
  404. limitRange: createLimitRange(api.LimitTypePod, getLocalStorageResourceList("100Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  405. },
  406. {
  407. pod: validPod("pod-max-local-ephemeral-storage-request-limit", 2, getResourceRequirements(getLocalStorageResourceList("250Mi"), getLocalStorageResourceList("500Mi"))),
  408. limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getLocalStorageResourceList("1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  409. },
  410. {
  411. pod: validPod("pod-max-local-ephemeral-storage-limit", 2, getResourceRequirements(getLocalStorageResourceList(""), getLocalStorageResourceList("500Mi"))),
  412. limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getLocalStorageResourceList("1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  413. },
  414. {
  415. pod: validPod("pod-max-local-ephemeral-storage-ratio", 3, getResourceRequirements(getLocalStorageResourceList("300Mi"), getLocalStorageResourceList("450Mi"))),
  416. limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getLocalStorageResourceList("2Gi"), api.ResourceList{}, api.ResourceList{}, getLocalStorageResourceList("1.5")),
  417. },
  418. }
  419. for i := range successCases {
  420. test := successCases[i]
  421. err := PodMutateLimitFunc(&test.limitRange, &test.pod)
  422. if err != nil {
  423. t.Errorf("Unexpected error for pod: %s, %v", test.pod.Name, err)
  424. }
  425. err = PodValidateLimitFunc(&test.limitRange, &test.pod)
  426. if err != nil {
  427. t.Errorf("Unexpected error for pod: %s, %v", test.pod.Name, err)
  428. }
  429. }
  430. errorCases := []testCase{
  431. {
  432. pod: validPod("ctr-min-cpu-request", 1, getResourceRequirements(getComputeResourceList("40m", ""), getComputeResourceList("", ""))),
  433. limitRange: createLimitRange(api.LimitTypeContainer, getComputeResourceList("50m", ""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  434. },
  435. {
  436. pod: validPod("ctr-min-cpu-request-limit", 1, getResourceRequirements(getComputeResourceList("40m", ""), getComputeResourceList("200m", ""))),
  437. limitRange: createLimitRange(api.LimitTypeContainer, getComputeResourceList("50m", ""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  438. },
  439. {
  440. pod: validPod("ctr-min-cpu-no-request-limit", 1, getResourceRequirements(getComputeResourceList("", ""), getComputeResourceList("", ""))),
  441. limitRange: createLimitRange(api.LimitTypeContainer, getComputeResourceList("50m", ""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  442. },
  443. {
  444. pod: validPod("ctr-min-memory-request", 1, getResourceRequirements(getComputeResourceList("", "40Mi"), getComputeResourceList("", ""))),
  445. limitRange: createLimitRange(api.LimitTypeContainer, getComputeResourceList("", "50Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  446. },
  447. {
  448. pod: validPod("ctr-min-memory-request-limit", 1, getResourceRequirements(getComputeResourceList("", "40Mi"), getComputeResourceList("", "100Mi"))),
  449. limitRange: createLimitRange(api.LimitTypeContainer, getComputeResourceList("", "50Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  450. },
  451. {
  452. pod: validPod("ctr-min-memory-no-request-limit", 1, getResourceRequirements(getComputeResourceList("", ""), getComputeResourceList("", ""))),
  453. limitRange: createLimitRange(api.LimitTypeContainer, getComputeResourceList("", "50Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  454. },
  455. {
  456. pod: validPod("ctr-max-cpu-request-limit", 1, getResourceRequirements(getComputeResourceList("500m", ""), getComputeResourceList("2500m", ""))),
  457. limitRange: createLimitRange(api.LimitTypeContainer, api.ResourceList{}, getComputeResourceList("2", ""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  458. },
  459. {
  460. pod: validPod("ctr-max-cpu-limit", 1, getResourceRequirements(getComputeResourceList("", ""), getComputeResourceList("2500m", ""))),
  461. limitRange: createLimitRange(api.LimitTypeContainer, api.ResourceList{}, getComputeResourceList("2", ""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  462. },
  463. {
  464. pod: validPod("ctr-max-cpu-no-request-limit", 1, getResourceRequirements(getComputeResourceList("", ""), getComputeResourceList("", ""))),
  465. limitRange: createLimitRange(api.LimitTypeContainer, api.ResourceList{}, getComputeResourceList("2", ""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  466. },
  467. {
  468. pod: validPod("ctr-max-cpu-ratio", 1, getResourceRequirements(getComputeResourceList("1250m", ""), getComputeResourceList("2500m", ""))),
  469. limitRange: createLimitRange(api.LimitTypeContainer, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, getComputeResourceList("1", "")),
  470. },
  471. {
  472. pod: validPod("ctr-max-mem-request-limit", 1, getResourceRequirements(getComputeResourceList("", "250Mi"), getComputeResourceList("", "2Gi"))),
  473. limitRange: createLimitRange(api.LimitTypeContainer, api.ResourceList{}, getComputeResourceList("", "1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  474. },
  475. {
  476. pod: validPod("ctr-max-mem-limit", 1, getResourceRequirements(getComputeResourceList("", ""), getComputeResourceList("", "2Gi"))),
  477. limitRange: createLimitRange(api.LimitTypeContainer, api.ResourceList{}, getComputeResourceList("", "1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  478. },
  479. {
  480. pod: validPod("ctr-max-mem-no-request-limit", 1, getResourceRequirements(getComputeResourceList("", ""), getComputeResourceList("", ""))),
  481. limitRange: createLimitRange(api.LimitTypeContainer, api.ResourceList{}, getComputeResourceList("", "1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  482. },
  483. {
  484. pod: validPod("pod-min-cpu-request", 1, getResourceRequirements(getComputeResourceList("75m", ""), getComputeResourceList("", ""))),
  485. limitRange: createLimitRange(api.LimitTypePod, getComputeResourceList("100m", ""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  486. },
  487. {
  488. pod: validPod("pod-min-cpu-request-limit", 1, getResourceRequirements(getComputeResourceList("75m", ""), getComputeResourceList("200m", ""))),
  489. limitRange: createLimitRange(api.LimitTypePod, getComputeResourceList("100m", ""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  490. },
  491. {
  492. pod: validPod("pod-min-memory-request", 1, getResourceRequirements(getComputeResourceList("", "60Mi"), getComputeResourceList("", ""))),
  493. limitRange: createLimitRange(api.LimitTypePod, getComputeResourceList("", "100Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  494. },
  495. {
  496. pod: validPod("pod-min-memory-request-limit", 1, getResourceRequirements(getComputeResourceList("", "60Mi"), getComputeResourceList("", "100Mi"))),
  497. limitRange: createLimitRange(api.LimitTypePod, getComputeResourceList("", "100Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  498. },
  499. {
  500. pod: validPod("pod-max-cpu-request-limit", 3, getResourceRequirements(getComputeResourceList("500m", ""), getComputeResourceList("1", ""))),
  501. limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getComputeResourceList("2", ""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  502. },
  503. {
  504. pod: validPod("pod-max-cpu-limit", 3, getResourceRequirements(getComputeResourceList("", ""), getComputeResourceList("1", ""))),
  505. limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getComputeResourceList("2", ""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  506. },
  507. {
  508. pod: validPod("pod-max-mem-request-limit", 3, getResourceRequirements(getComputeResourceList("", "250Mi"), getComputeResourceList("", "500Mi"))),
  509. limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getComputeResourceList("", "1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  510. },
  511. {
  512. pod: validPod("pod-max-mem-limit", 3, getResourceRequirements(getComputeResourceList("", ""), getComputeResourceList("", "500Mi"))),
  513. limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getComputeResourceList("", "1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  514. },
  515. {
  516. pod: validPodInit(
  517. validPod("pod-init-max-mem-limit", 1, getResourceRequirements(getComputeResourceList("", ""), getComputeResourceList("", "500Mi"))),
  518. getResourceRequirements(getComputeResourceList("", ""), getComputeResourceList("", "1.5Gi")),
  519. ),
  520. limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getComputeResourceList("", "1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  521. },
  522. {
  523. pod: validPod("pod-max-mem-ratio", 3, getResourceRequirements(getComputeResourceList("", "250Mi"), getComputeResourceList("", "500Mi"))),
  524. limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getComputeResourceList("", "2Gi"), api.ResourceList{}, api.ResourceList{}, getComputeResourceList("", "1.5")),
  525. },
  526. {
  527. pod: validPod("ctr-1-min-local-ephemeral-storage-request", 1, getResourceRequirements(getLocalStorageResourceList("40Mi"), getLocalStorageResourceList(""))),
  528. limitRange: createLimitRange(api.LimitTypeContainer, getLocalStorageResourceList("50Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  529. },
  530. {
  531. pod: validPod("ctr-1-min-local-ephemeral-storage-request-limit", 1, getResourceRequirements(getLocalStorageResourceList("40Mi"), getLocalStorageResourceList("100Mi"))),
  532. limitRange: createLimitRange(api.LimitTypeContainer, getLocalStorageResourceList("50Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  533. },
  534. {
  535. pod: validPod("ctr-1-min-local-ephemeral-storage-no-request-limit", 1, getResourceRequirements(getLocalStorageResourceList(""), getLocalStorageResourceList(""))),
  536. limitRange: createLimitRange(api.LimitTypeContainer, getLocalStorageResourceList("50Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  537. },
  538. {
  539. pod: validPod("ctr-1-max-local-ephemeral-storage-request-limit", 1, getResourceRequirements(getLocalStorageResourceList("250Mi"), getLocalStorageResourceList("2Gi"))),
  540. limitRange: createLimitRange(api.LimitTypeContainer, api.ResourceList{}, getLocalStorageResourceList("1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  541. },
  542. {
  543. pod: validPod("ctr-1-max-local-ephemeral-storage-limit", 1, getResourceRequirements(getLocalStorageResourceList(""), getLocalStorageResourceList("2Gi"))),
  544. limitRange: createLimitRange(api.LimitTypeContainer, api.ResourceList{}, getLocalStorageResourceList("1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  545. },
  546. {
  547. pod: validPod("ctr-1-max-local-ephemeral-storage-no-request-limit", 1, getResourceRequirements(getLocalStorageResourceList(""), getLocalStorageResourceList(""))),
  548. limitRange: createLimitRange(api.LimitTypeContainer, api.ResourceList{}, getLocalStorageResourceList("1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  549. },
  550. {
  551. pod: validPod("ctr-2-min-local-ephemeral-storage-request", 2, getResourceRequirements(getLocalStorageResourceList("40Mi"), getLocalStorageResourceList(""))),
  552. limitRange: createLimitRange(api.LimitTypeContainer, getLocalStorageResourceList("50Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  553. },
  554. {
  555. pod: validPod("ctr-2-min-local-ephemeral-storage-request-limit", 2, getResourceRequirements(getLocalStorageResourceList("40Mi"), getLocalStorageResourceList("100Mi"))),
  556. limitRange: createLimitRange(api.LimitTypeContainer, getLocalStorageResourceList("50Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  557. },
  558. {
  559. pod: validPod("ctr-2-min-local-ephemeral-storage-no-request-limit", 2, getResourceRequirements(getLocalStorageResourceList(""), getLocalStorageResourceList(""))),
  560. limitRange: createLimitRange(api.LimitTypeContainer, getLocalStorageResourceList("50Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  561. },
  562. {
  563. pod: validPod("ctr-2-max-local-ephemeral-storage-request-limit", 2, getResourceRequirements(getLocalStorageResourceList("250Mi"), getLocalStorageResourceList("2Gi"))),
  564. limitRange: createLimitRange(api.LimitTypeContainer, api.ResourceList{}, getLocalStorageResourceList("1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  565. },
  566. {
  567. pod: validPod("ctr-2-max-local-ephemeral-storage-limit", 2, getResourceRequirements(getLocalStorageResourceList(""), getLocalStorageResourceList("2Gi"))),
  568. limitRange: createLimitRange(api.LimitTypeContainer, api.ResourceList{}, getLocalStorageResourceList("1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  569. },
  570. {
  571. pod: validPod("ctr-2-max-local-ephemeral-storage-no-request-limit", 2, getResourceRequirements(getLocalStorageResourceList(""), getLocalStorageResourceList(""))),
  572. limitRange: createLimitRange(api.LimitTypeContainer, api.ResourceList{}, getLocalStorageResourceList("1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  573. },
  574. {
  575. pod: validPod("pod-min-local-ephemeral-storage-request", 1, getResourceRequirements(getLocalStorageResourceList("60Mi"), getLocalStorageResourceList(""))),
  576. limitRange: createLimitRange(api.LimitTypePod, getLocalStorageResourceList("100Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  577. },
  578. {
  579. pod: validPod("pod-min-local-ephemeral-storage-request-limit", 1, getResourceRequirements(getLocalStorageResourceList("60Mi"), getLocalStorageResourceList("100Mi"))),
  580. limitRange: createLimitRange(api.LimitTypePod, getLocalStorageResourceList("100Mi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  581. },
  582. {
  583. pod: validPod("pod-max-local-ephemeral-storage-request-limit", 3, getResourceRequirements(getLocalStorageResourceList("250Mi"), getLocalStorageResourceList("500Mi"))),
  584. limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getLocalStorageResourceList("1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  585. },
  586. {
  587. pod: validPod("pod-max-local-ephemeral-storage-limit", 3, getResourceRequirements(getLocalStorageResourceList(""), getLocalStorageResourceList("500Mi"))),
  588. limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getLocalStorageResourceList("1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  589. },
  590. {
  591. pod: validPodInit(
  592. validPod("pod-init-max-local-ephemeral-storage-limit", 1, getResourceRequirements(getLocalStorageResourceList(""), getLocalStorageResourceList("500Mi"))),
  593. getResourceRequirements(getLocalStorageResourceList(""), getLocalStorageResourceList("1.5Gi")),
  594. ),
  595. limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getLocalStorageResourceList("1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  596. },
  597. {
  598. pod: validPod("pod-max-local-ephemeral-storage-ratio", 3, getResourceRequirements(getLocalStorageResourceList("250Mi"), getLocalStorageResourceList("500Mi"))),
  599. limitRange: createLimitRange(api.LimitTypePod, api.ResourceList{}, getLocalStorageResourceList("2Gi"), api.ResourceList{}, api.ResourceList{}, getLocalStorageResourceList("1.5")),
  600. },
  601. }
  602. for i := range errorCases {
  603. test := errorCases[i]
  604. err := PodMutateLimitFunc(&test.limitRange, &test.pod)
  605. if err != nil {
  606. t.Errorf("Unexpected error for pod: %s, %v", test.pod.Name, err)
  607. }
  608. err = PodValidateLimitFunc(&test.limitRange, &test.pod)
  609. if err == nil {
  610. t.Errorf("Expected error for pod: %s", test.pod.Name)
  611. }
  612. }
  613. }
  614. func getLocalStorageResourceList(ephemeralStorage string) api.ResourceList {
  615. res := api.ResourceList{}
  616. if ephemeralStorage != "" {
  617. res[api.ResourceEphemeralStorage] = resource.MustParse(ephemeralStorage)
  618. }
  619. return res
  620. }
  621. func TestPodLimitFuncApplyDefault(t *testing.T) {
  622. limitRange := validLimitRange()
  623. testPod := validPodInit(validPod("foo", 1, getResourceRequirements(api.ResourceList{}, api.ResourceList{})), getResourceRequirements(api.ResourceList{}, api.ResourceList{}))
  624. err := PodMutateLimitFunc(&limitRange, &testPod)
  625. if err != nil {
  626. t.Errorf("Unexpected error for valid pod: %s, %v", testPod.Name, err)
  627. }
  628. for i := range testPod.Spec.Containers {
  629. container := testPod.Spec.Containers[i]
  630. limitMemory := container.Resources.Limits.Memory().String()
  631. limitCPU := container.Resources.Limits.Cpu().String()
  632. requestMemory := container.Resources.Requests.Memory().String()
  633. requestCPU := container.Resources.Requests.Cpu().String()
  634. if limitMemory != "10Mi" {
  635. t.Errorf("Unexpected limit memory value %s", limitMemory)
  636. }
  637. if limitCPU != "75m" {
  638. t.Errorf("Unexpected limit cpu value %s", limitCPU)
  639. }
  640. if requestMemory != "5Mi" {
  641. t.Errorf("Unexpected request memory value %s", requestMemory)
  642. }
  643. if requestCPU != "50m" {
  644. t.Errorf("Unexpected request cpu value %s", requestCPU)
  645. }
  646. }
  647. for i := range testPod.Spec.InitContainers {
  648. container := testPod.Spec.InitContainers[i]
  649. limitMemory := container.Resources.Limits.Memory().String()
  650. limitCPU := container.Resources.Limits.Cpu().String()
  651. requestMemory := container.Resources.Requests.Memory().String()
  652. requestCPU := container.Resources.Requests.Cpu().String()
  653. if limitMemory != "10Mi" {
  654. t.Errorf("Unexpected limit memory value %s", limitMemory)
  655. }
  656. if limitCPU != "75m" {
  657. t.Errorf("Unexpected limit cpu value %s", limitCPU)
  658. }
  659. if requestMemory != "5Mi" {
  660. t.Errorf("Unexpected request memory value %s", requestMemory)
  661. }
  662. if requestCPU != "50m" {
  663. t.Errorf("Unexpected request cpu value %s", requestCPU)
  664. }
  665. }
  666. }
  667. func TestLimitRangerIgnoresSubresource(t *testing.T) {
  668. limitRange := validLimitRangeNoDefaults()
  669. mockClient := newMockClientForTest([]corev1.LimitRange{limitRange})
  670. handler, informerFactory, err := newHandlerForTest(mockClient)
  671. if err != nil {
  672. t.Errorf("unexpected error initializing handler: %v", err)
  673. }
  674. informerFactory.Start(wait.NeverStop)
  675. testPod := validPod("testPod", 1, api.ResourceRequirements{})
  676. err = admissiontesting.WithReinvocationTesting(t, handler).Admit(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
  677. if err != nil {
  678. t.Fatal(err)
  679. }
  680. err = handler.Validate(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
  681. if err == nil {
  682. t.Errorf("Expected an error since the pod did not specify resource limits in its create call")
  683. }
  684. err = handler.Validate(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil), nil)
  685. if err != nil {
  686. t.Errorf("Expected not to call limitranger actions on pod updates")
  687. }
  688. err = handler.Validate(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "status", admission.Update, &metav1.UpdateOptions{}, false, nil), nil)
  689. if err != nil {
  690. t.Errorf("Should have ignored calls to any subresource of pod %v", err)
  691. }
  692. }
  693. func TestLimitRangerAdmitPod(t *testing.T) {
  694. limitRange := validLimitRangeNoDefaults()
  695. mockClient := newMockClientForTest([]corev1.LimitRange{limitRange})
  696. handler, informerFactory, err := newHandlerForTest(mockClient)
  697. if err != nil {
  698. t.Errorf("unexpected error initializing handler: %v", err)
  699. }
  700. informerFactory.Start(wait.NeverStop)
  701. testPod := validPod("testPod", 1, api.ResourceRequirements{})
  702. err = admissiontesting.WithReinvocationTesting(t, handler).Admit(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
  703. if err != nil {
  704. t.Fatal(err)
  705. }
  706. err = handler.Validate(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
  707. if err == nil {
  708. t.Errorf("Expected an error since the pod did not specify resource limits in its create call")
  709. }
  710. err = handler.Validate(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil), nil)
  711. if err != nil {
  712. t.Errorf("Expected not to call limitranger actions on pod updates")
  713. }
  714. err = handler.Validate(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "status", admission.Update, &metav1.UpdateOptions{}, false, nil), nil)
  715. if err != nil {
  716. t.Errorf("Should have ignored calls to any subresource of pod %v", err)
  717. }
  718. // a pod that is undergoing termination should never be blocked
  719. terminatingPod := validPod("terminatingPod", 1, api.ResourceRequirements{})
  720. now := metav1.Now()
  721. terminatingPod.DeletionTimestamp = &now
  722. err = handler.Validate(admission.NewAttributesRecord(&terminatingPod, &terminatingPod, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "terminatingPod", api.Resource("pods").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil), nil)
  723. if err != nil {
  724. t.Errorf("LimitRange should ignore a pod marked for termination")
  725. }
  726. }
  727. // newMockClientForTest creates a mock client that returns a client configured for the specified list of limit ranges
  728. func newMockClientForTest(limitRanges []corev1.LimitRange) *fake.Clientset {
  729. mockClient := &fake.Clientset{}
  730. mockClient.AddReactor("list", "limitranges", func(action core.Action) (bool, runtime.Object, error) {
  731. limitRangeList := &corev1.LimitRangeList{
  732. ListMeta: metav1.ListMeta{
  733. ResourceVersion: fmt.Sprintf("%d", len(limitRanges)),
  734. },
  735. }
  736. for index, value := range limitRanges {
  737. value.ResourceVersion = fmt.Sprintf("%d", index)
  738. limitRangeList.Items = append(limitRangeList.Items, value)
  739. }
  740. return true, limitRangeList, nil
  741. })
  742. return mockClient
  743. }
  744. // newHandlerForTest returns a handler configured for testing.
  745. func newHandlerForTest(c clientset.Interface) (*LimitRanger, informers.SharedInformerFactory, error) {
  746. f := informers.NewSharedInformerFactory(c, 5*time.Minute)
  747. handler, err := NewLimitRanger(&DefaultLimitRangerActions{})
  748. if err != nil {
  749. return nil, f, err
  750. }
  751. pluginInitializer := genericadmissioninitializer.New(c, f, nil)
  752. pluginInitializer.Initialize(handler)
  753. err = admission.ValidateInitialization(handler)
  754. return handler, f, err
  755. }
  756. func validPersistentVolumeClaim(name string, resources api.ResourceRequirements) api.PersistentVolumeClaim {
  757. pvc := api.PersistentVolumeClaim{
  758. ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: "test"},
  759. Spec: api.PersistentVolumeClaimSpec{
  760. Resources: resources,
  761. },
  762. }
  763. return pvc
  764. }
  765. func TestPersistentVolumeClaimLimitFunc(t *testing.T) {
  766. type testCase struct {
  767. pvc api.PersistentVolumeClaim
  768. limitRange corev1.LimitRange
  769. }
  770. successCases := []testCase{
  771. {
  772. pvc: validPersistentVolumeClaim("pvc-is-min-storage-request", getResourceRequirements(getStorageResourceList("1Gi"), getStorageResourceList(""))),
  773. limitRange: createLimitRange(api.LimitTypePersistentVolumeClaim, getStorageResourceList("1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  774. },
  775. {
  776. pvc: validPersistentVolumeClaim("pvc-is-max-storage-request", getResourceRequirements(getStorageResourceList("1Gi"), getStorageResourceList(""))),
  777. limitRange: createLimitRange(api.LimitTypePersistentVolumeClaim, api.ResourceList{}, getStorageResourceList("1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  778. },
  779. {
  780. pvc: validPersistentVolumeClaim("pvc-no-minmax-storage-request", getResourceRequirements(getStorageResourceList("100Gi"), getStorageResourceList(""))),
  781. limitRange: createLimitRange(api.LimitTypePersistentVolumeClaim, getStorageResourceList(""), getStorageResourceList(""), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  782. },
  783. {
  784. pvc: validPersistentVolumeClaim("pvc-within-minmax-storage-request", getResourceRequirements(getStorageResourceList("5Gi"), getStorageResourceList(""))),
  785. limitRange: createLimitRange(api.LimitTypePersistentVolumeClaim, getStorageResourceList("1Gi"), getStorageResourceList("10Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  786. },
  787. }
  788. for i := range successCases {
  789. test := successCases[i]
  790. err := PersistentVolumeClaimValidateLimitFunc(&test.limitRange, &test.pvc)
  791. if err != nil {
  792. t.Errorf("Unexpected error for pvc: %s, %v", test.pvc.Name, err)
  793. }
  794. }
  795. errorCases := []testCase{
  796. {
  797. pvc: validPersistentVolumeClaim("pvc-below-min-storage-request", getResourceRequirements(getStorageResourceList("500Mi"), getStorageResourceList(""))),
  798. limitRange: createLimitRange(api.LimitTypePersistentVolumeClaim, getStorageResourceList("1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  799. },
  800. {
  801. pvc: validPersistentVolumeClaim("pvc-exceeds-max-storage-request", getResourceRequirements(getStorageResourceList("100Gi"), getStorageResourceList(""))),
  802. limitRange: createLimitRange(api.LimitTypePersistentVolumeClaim, getStorageResourceList("1Gi"), getStorageResourceList("1Gi"), api.ResourceList{}, api.ResourceList{}, api.ResourceList{}),
  803. },
  804. }
  805. for i := range errorCases {
  806. test := errorCases[i]
  807. err := PersistentVolumeClaimValidateLimitFunc(&test.limitRange, &test.pvc)
  808. if err == nil {
  809. t.Errorf("Expected error for pvc: %s", test.pvc.Name)
  810. }
  811. }
  812. }