admission_test.go 47 KB

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