validation_test.go 48 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538
  1. /*
  2. Copyright 2016 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 validation
  14. import (
  15. "strings"
  16. "testing"
  17. "k8s.io/apimachinery/pkg/api/resource"
  18. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  19. utilfeature "k8s.io/apiserver/pkg/util/feature"
  20. featuregatetesting "k8s.io/component-base/featuregate/testing"
  21. "k8s.io/kubernetes/pkg/apis/autoscaling"
  22. api "k8s.io/kubernetes/pkg/apis/core"
  23. "k8s.io/kubernetes/pkg/features"
  24. utilpointer "k8s.io/utils/pointer"
  25. )
  26. func TestValidateScale(t *testing.T) {
  27. successCases := []autoscaling.Scale{
  28. {
  29. ObjectMeta: metav1.ObjectMeta{
  30. Name: "frontend",
  31. Namespace: metav1.NamespaceDefault,
  32. },
  33. Spec: autoscaling.ScaleSpec{
  34. Replicas: 1,
  35. },
  36. },
  37. {
  38. ObjectMeta: metav1.ObjectMeta{
  39. Name: "frontend",
  40. Namespace: metav1.NamespaceDefault,
  41. },
  42. Spec: autoscaling.ScaleSpec{
  43. Replicas: 10,
  44. },
  45. },
  46. {
  47. ObjectMeta: metav1.ObjectMeta{
  48. Name: "frontend",
  49. Namespace: metav1.NamespaceDefault,
  50. },
  51. Spec: autoscaling.ScaleSpec{
  52. Replicas: 0,
  53. },
  54. },
  55. }
  56. for _, successCase := range successCases {
  57. if errs := ValidateScale(&successCase); len(errs) != 0 {
  58. t.Errorf("expected success: %v", errs)
  59. }
  60. }
  61. errorCases := []struct {
  62. scale autoscaling.Scale
  63. msg string
  64. }{
  65. {
  66. scale: autoscaling.Scale{
  67. ObjectMeta: metav1.ObjectMeta{
  68. Name: "frontend",
  69. Namespace: metav1.NamespaceDefault,
  70. },
  71. Spec: autoscaling.ScaleSpec{
  72. Replicas: -1,
  73. },
  74. },
  75. msg: "must be greater than or equal to 0",
  76. },
  77. }
  78. for _, c := range errorCases {
  79. if errs := ValidateScale(&c.scale); len(errs) == 0 {
  80. t.Errorf("expected failure for %s", c.msg)
  81. } else if !strings.Contains(errs[0].Error(), c.msg) {
  82. t.Errorf("unexpected error: %v, expected: %s", errs[0], c.msg)
  83. }
  84. }
  85. }
  86. func TestValidateBehavior(t *testing.T) {
  87. maxPolicy := autoscaling.MaxPolicySelect
  88. minPolicy := autoscaling.MinPolicySelect
  89. disabledPolicy := autoscaling.DisabledPolicySelect
  90. incorrectPolicy := autoscaling.ScalingPolicySelect("incorrect")
  91. simplePoliciesList := []autoscaling.HPAScalingPolicy{
  92. {
  93. Type: autoscaling.PercentScalingPolicy,
  94. Value: 10,
  95. PeriodSeconds: 1,
  96. },
  97. {
  98. Type: autoscaling.PodsScalingPolicy,
  99. Value: 1,
  100. PeriodSeconds: 1800,
  101. },
  102. }
  103. successCases := []autoscaling.HorizontalPodAutoscalerBehavior{
  104. {
  105. ScaleUp: nil,
  106. ScaleDown: nil,
  107. },
  108. {
  109. ScaleUp: &autoscaling.HPAScalingRules{
  110. StabilizationWindowSeconds: utilpointer.Int32Ptr(3600),
  111. SelectPolicy: &minPolicy,
  112. Policies: simplePoliciesList,
  113. },
  114. ScaleDown: &autoscaling.HPAScalingRules{
  115. StabilizationWindowSeconds: utilpointer.Int32Ptr(0),
  116. SelectPolicy: &disabledPolicy,
  117. Policies: simplePoliciesList,
  118. },
  119. },
  120. {
  121. ScaleUp: &autoscaling.HPAScalingRules{
  122. StabilizationWindowSeconds: utilpointer.Int32Ptr(120),
  123. SelectPolicy: &maxPolicy,
  124. Policies: []autoscaling.HPAScalingPolicy{
  125. {
  126. Type: autoscaling.PodsScalingPolicy,
  127. Value: 1,
  128. PeriodSeconds: 2,
  129. },
  130. {
  131. Type: autoscaling.PercentScalingPolicy,
  132. Value: 3,
  133. PeriodSeconds: 4,
  134. },
  135. {
  136. Type: autoscaling.PodsScalingPolicy,
  137. Value: 5,
  138. PeriodSeconds: 6,
  139. },
  140. {
  141. Type: autoscaling.PercentScalingPolicy,
  142. Value: 7,
  143. PeriodSeconds: 8,
  144. },
  145. },
  146. },
  147. ScaleDown: &autoscaling.HPAScalingRules{
  148. StabilizationWindowSeconds: utilpointer.Int32Ptr(120),
  149. SelectPolicy: &maxPolicy,
  150. Policies: []autoscaling.HPAScalingPolicy{
  151. {
  152. Type: autoscaling.PodsScalingPolicy,
  153. Value: 1,
  154. PeriodSeconds: 2,
  155. },
  156. {
  157. Type: autoscaling.PercentScalingPolicy,
  158. Value: 3,
  159. PeriodSeconds: 4,
  160. },
  161. {
  162. Type: autoscaling.PodsScalingPolicy,
  163. Value: 5,
  164. PeriodSeconds: 6,
  165. },
  166. {
  167. Type: autoscaling.PercentScalingPolicy,
  168. Value: 7,
  169. PeriodSeconds: 8,
  170. },
  171. },
  172. },
  173. },
  174. }
  175. for _, behavior := range successCases {
  176. hpa := prepareHPAWithBehavior(behavior)
  177. if errs := ValidateHorizontalPodAutoscaler(&hpa); len(errs) != 0 {
  178. t.Errorf("expected success: %v", errs)
  179. }
  180. }
  181. errorCases := []struct {
  182. behavior autoscaling.HorizontalPodAutoscalerBehavior
  183. msg string
  184. }{
  185. {
  186. behavior: autoscaling.HorizontalPodAutoscalerBehavior{
  187. ScaleUp: &autoscaling.HPAScalingRules{
  188. SelectPolicy: &minPolicy,
  189. },
  190. },
  191. msg: "spec.behavior.scaleUp.policies: Required value: must specify at least one Policy",
  192. },
  193. {
  194. behavior: autoscaling.HorizontalPodAutoscalerBehavior{
  195. ScaleUp: &autoscaling.HPAScalingRules{
  196. StabilizationWindowSeconds: utilpointer.Int32Ptr(3601),
  197. SelectPolicy: &minPolicy,
  198. Policies: simplePoliciesList,
  199. },
  200. },
  201. msg: "spec.behavior.scaleUp.stabilizationWindowSeconds: Invalid value: 3601: must be less than or equal to 3600",
  202. },
  203. {
  204. behavior: autoscaling.HorizontalPodAutoscalerBehavior{
  205. ScaleUp: &autoscaling.HPAScalingRules{
  206. Policies: []autoscaling.HPAScalingPolicy{
  207. {
  208. Type: autoscaling.PodsScalingPolicy,
  209. Value: 7,
  210. PeriodSeconds: 1801,
  211. },
  212. },
  213. },
  214. },
  215. msg: "spec.behavior.scaleUp.policies[0].periodSeconds: Invalid value: 1801: must be less than or equal to 1800",
  216. },
  217. {
  218. behavior: autoscaling.HorizontalPodAutoscalerBehavior{
  219. ScaleUp: &autoscaling.HPAScalingRules{
  220. SelectPolicy: &incorrectPolicy,
  221. Policies: []autoscaling.HPAScalingPolicy{
  222. {
  223. Type: autoscaling.PodsScalingPolicy,
  224. Value: 7,
  225. PeriodSeconds: 8,
  226. },
  227. },
  228. },
  229. },
  230. msg: `spec.behavior.scaleUp.selectPolicy: Unsupported value: "incorrect": supported values: "Disabled", "Max", "Min"`,
  231. },
  232. {
  233. behavior: autoscaling.HorizontalPodAutoscalerBehavior{
  234. ScaleUp: &autoscaling.HPAScalingRules{
  235. Policies: []autoscaling.HPAScalingPolicy{
  236. {
  237. Type: autoscaling.HPAScalingPolicyType("hm"),
  238. Value: 7,
  239. PeriodSeconds: 8,
  240. },
  241. },
  242. },
  243. },
  244. msg: `spec.behavior.scaleUp.policies[0].type: Unsupported value: "hm": supported values: "Percent", "Pods"`,
  245. },
  246. {
  247. behavior: autoscaling.HorizontalPodAutoscalerBehavior{
  248. ScaleUp: &autoscaling.HPAScalingRules{
  249. Policies: []autoscaling.HPAScalingPolicy{
  250. {
  251. Type: autoscaling.PodsScalingPolicy,
  252. Value: 8,
  253. },
  254. },
  255. },
  256. },
  257. msg: "spec.behavior.scaleUp.policies[0].periodSeconds: Invalid value: 0: must be greater than zero",
  258. },
  259. {
  260. behavior: autoscaling.HorizontalPodAutoscalerBehavior{
  261. ScaleUp: &autoscaling.HPAScalingRules{
  262. Policies: []autoscaling.HPAScalingPolicy{
  263. {
  264. Type: autoscaling.PodsScalingPolicy,
  265. PeriodSeconds: 8,
  266. },
  267. },
  268. },
  269. },
  270. msg: "spec.behavior.scaleUp.policies[0].value: Invalid value: 0: must be greater than zero",
  271. },
  272. {
  273. behavior: autoscaling.HorizontalPodAutoscalerBehavior{
  274. ScaleUp: &autoscaling.HPAScalingRules{
  275. Policies: []autoscaling.HPAScalingPolicy{
  276. {
  277. Type: autoscaling.PodsScalingPolicy,
  278. PeriodSeconds: -1,
  279. Value: 1,
  280. },
  281. },
  282. },
  283. },
  284. msg: "spec.behavior.scaleUp.policies[0].periodSeconds: Invalid value: -1: must be greater than zero",
  285. },
  286. {
  287. behavior: autoscaling.HorizontalPodAutoscalerBehavior{
  288. ScaleUp: &autoscaling.HPAScalingRules{
  289. Policies: []autoscaling.HPAScalingPolicy{
  290. {
  291. Type: autoscaling.PodsScalingPolicy,
  292. PeriodSeconds: 1,
  293. Value: -1,
  294. },
  295. },
  296. },
  297. },
  298. msg: "spec.behavior.scaleUp.policies[0].value: Invalid value: -1: must be greater than zero",
  299. },
  300. {
  301. behavior: autoscaling.HorizontalPodAutoscalerBehavior{
  302. ScaleDown: &autoscaling.HPAScalingRules{
  303. SelectPolicy: &minPolicy,
  304. },
  305. },
  306. msg: "spec.behavior.scaleDown.policies: Required value: must specify at least one Policy",
  307. },
  308. {
  309. behavior: autoscaling.HorizontalPodAutoscalerBehavior{
  310. ScaleDown: &autoscaling.HPAScalingRules{
  311. StabilizationWindowSeconds: utilpointer.Int32Ptr(3601),
  312. SelectPolicy: &minPolicy,
  313. Policies: simplePoliciesList,
  314. },
  315. },
  316. msg: "spec.behavior.scaleDown.stabilizationWindowSeconds: Invalid value: 3601: must be less than or equal to 3600",
  317. },
  318. {
  319. behavior: autoscaling.HorizontalPodAutoscalerBehavior{
  320. ScaleDown: &autoscaling.HPAScalingRules{
  321. Policies: []autoscaling.HPAScalingPolicy{
  322. {
  323. Type: autoscaling.PercentScalingPolicy,
  324. Value: 7,
  325. PeriodSeconds: 1801,
  326. },
  327. },
  328. },
  329. },
  330. msg: "spec.behavior.scaleDown.policies[0].periodSeconds: Invalid value: 1801: must be less than or equal to 1800",
  331. },
  332. {
  333. behavior: autoscaling.HorizontalPodAutoscalerBehavior{
  334. ScaleDown: &autoscaling.HPAScalingRules{
  335. SelectPolicy: &incorrectPolicy,
  336. Policies: []autoscaling.HPAScalingPolicy{
  337. {
  338. Type: autoscaling.PodsScalingPolicy,
  339. Value: 7,
  340. PeriodSeconds: 8,
  341. },
  342. },
  343. },
  344. },
  345. msg: `spec.behavior.scaleDown.selectPolicy: Unsupported value: "incorrect": supported values: "Disabled", "Max", "Min"`,
  346. },
  347. {
  348. behavior: autoscaling.HorizontalPodAutoscalerBehavior{
  349. ScaleDown: &autoscaling.HPAScalingRules{
  350. Policies: []autoscaling.HPAScalingPolicy{
  351. {
  352. Type: autoscaling.HPAScalingPolicyType("hm"),
  353. Value: 7,
  354. PeriodSeconds: 8,
  355. },
  356. },
  357. },
  358. },
  359. msg: `spec.behavior.scaleDown.policies[0].type: Unsupported value: "hm": supported values: "Percent", "Pods"`,
  360. },
  361. {
  362. behavior: autoscaling.HorizontalPodAutoscalerBehavior{
  363. ScaleDown: &autoscaling.HPAScalingRules{
  364. Policies: []autoscaling.HPAScalingPolicy{
  365. {
  366. Type: autoscaling.PodsScalingPolicy,
  367. Value: 8,
  368. },
  369. },
  370. },
  371. },
  372. msg: "spec.behavior.scaleDown.policies[0].periodSeconds: Invalid value: 0: must be greater than zero",
  373. },
  374. {
  375. behavior: autoscaling.HorizontalPodAutoscalerBehavior{
  376. ScaleDown: &autoscaling.HPAScalingRules{
  377. Policies: []autoscaling.HPAScalingPolicy{
  378. {
  379. Type: autoscaling.PodsScalingPolicy,
  380. PeriodSeconds: 8,
  381. },
  382. },
  383. },
  384. },
  385. msg: "spec.behavior.scaleDown.policies[0].value: Invalid value: 0: must be greater than zero",
  386. },
  387. {
  388. behavior: autoscaling.HorizontalPodAutoscalerBehavior{
  389. ScaleDown: &autoscaling.HPAScalingRules{
  390. Policies: []autoscaling.HPAScalingPolicy{
  391. {
  392. Type: autoscaling.PodsScalingPolicy,
  393. PeriodSeconds: -1,
  394. Value: 1,
  395. },
  396. },
  397. },
  398. },
  399. msg: "spec.behavior.scaleDown.policies[0].periodSeconds: Invalid value: -1: must be greater than zero",
  400. },
  401. {
  402. behavior: autoscaling.HorizontalPodAutoscalerBehavior{
  403. ScaleDown: &autoscaling.HPAScalingRules{
  404. Policies: []autoscaling.HPAScalingPolicy{
  405. {
  406. Type: autoscaling.PodsScalingPolicy,
  407. PeriodSeconds: 1,
  408. Value: -1,
  409. },
  410. },
  411. },
  412. },
  413. msg: "spec.behavior.scaleDown.policies[0].value: Invalid value: -1: must be greater than zero",
  414. },
  415. }
  416. for _, c := range errorCases {
  417. hpa := prepareHPAWithBehavior(c.behavior)
  418. if errs := ValidateHorizontalPodAutoscaler(&hpa); len(errs) == 0 {
  419. t.Errorf("expected failure for %s", c.msg)
  420. } else if !strings.Contains(errs[0].Error(), c.msg) {
  421. t.Errorf("unexpected error: %v, expected: %s", errs[0], c.msg)
  422. }
  423. }
  424. }
  425. func prepareHPAWithBehavior(b autoscaling.HorizontalPodAutoscalerBehavior) autoscaling.HorizontalPodAutoscaler {
  426. return autoscaling.HorizontalPodAutoscaler{
  427. ObjectMeta: metav1.ObjectMeta{
  428. Name: "myautoscaler",
  429. Namespace: metav1.NamespaceDefault,
  430. },
  431. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  432. ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  433. Kind: "ReplicationController",
  434. Name: "myrc",
  435. },
  436. MinReplicas: utilpointer.Int32Ptr(1),
  437. MaxReplicas: 5,
  438. Metrics: []autoscaling.MetricSpec{
  439. {
  440. Type: autoscaling.ResourceMetricSourceType,
  441. Resource: &autoscaling.ResourceMetricSource{
  442. Name: api.ResourceCPU,
  443. Target: autoscaling.MetricTarget{
  444. Type: autoscaling.UtilizationMetricType,
  445. AverageUtilization: utilpointer.Int32Ptr(70),
  446. },
  447. },
  448. },
  449. },
  450. Behavior: &b,
  451. },
  452. }
  453. }
  454. func TestValidateHorizontalPodAutoscaler(t *testing.T) {
  455. metricLabelSelector, err := metav1.ParseToLabelSelector("label=value")
  456. if err != nil {
  457. t.Errorf("unable to parse label selector: %v", err)
  458. }
  459. successCases := []autoscaling.HorizontalPodAutoscaler{
  460. {
  461. ObjectMeta: metav1.ObjectMeta{
  462. Name: "myautoscaler",
  463. Namespace: metav1.NamespaceDefault,
  464. },
  465. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  466. ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  467. Kind: "ReplicationController",
  468. Name: "myrc",
  469. },
  470. MinReplicas: utilpointer.Int32Ptr(1),
  471. MaxReplicas: 5,
  472. Metrics: []autoscaling.MetricSpec{
  473. {
  474. Type: autoscaling.ResourceMetricSourceType,
  475. Resource: &autoscaling.ResourceMetricSource{
  476. Name: api.ResourceCPU,
  477. Target: autoscaling.MetricTarget{
  478. Type: autoscaling.UtilizationMetricType,
  479. AverageUtilization: utilpointer.Int32Ptr(70),
  480. },
  481. },
  482. },
  483. },
  484. },
  485. },
  486. {
  487. ObjectMeta: metav1.ObjectMeta{
  488. Name: "myautoscaler",
  489. Namespace: metav1.NamespaceDefault,
  490. },
  491. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  492. ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  493. Kind: "ReplicationController",
  494. Name: "myrc",
  495. },
  496. MinReplicas: utilpointer.Int32Ptr(1),
  497. MaxReplicas: 5,
  498. },
  499. },
  500. {
  501. ObjectMeta: metav1.ObjectMeta{
  502. Name: "myautoscaler",
  503. Namespace: metav1.NamespaceDefault,
  504. },
  505. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  506. ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  507. Kind: "ReplicationController",
  508. Name: "myrc",
  509. },
  510. MinReplicas: utilpointer.Int32Ptr(1),
  511. MaxReplicas: 5,
  512. Metrics: []autoscaling.MetricSpec{
  513. {
  514. Type: autoscaling.ResourceMetricSourceType,
  515. Resource: &autoscaling.ResourceMetricSource{
  516. Name: api.ResourceCPU,
  517. Target: autoscaling.MetricTarget{
  518. Type: autoscaling.AverageValueMetricType,
  519. AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI),
  520. },
  521. },
  522. },
  523. },
  524. },
  525. },
  526. {
  527. ObjectMeta: metav1.ObjectMeta{
  528. Name: "myautoscaler",
  529. Namespace: metav1.NamespaceDefault,
  530. },
  531. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  532. ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  533. Kind: "ReplicationController",
  534. Name: "myrc",
  535. },
  536. MinReplicas: utilpointer.Int32Ptr(1),
  537. MaxReplicas: 5,
  538. Metrics: []autoscaling.MetricSpec{
  539. {
  540. Type: autoscaling.PodsMetricSourceType,
  541. Pods: &autoscaling.PodsMetricSource{
  542. Metric: autoscaling.MetricIdentifier{
  543. Name: "somemetric",
  544. },
  545. Target: autoscaling.MetricTarget{
  546. Type: autoscaling.AverageValueMetricType,
  547. AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI),
  548. },
  549. },
  550. },
  551. },
  552. },
  553. },
  554. {
  555. ObjectMeta: metav1.ObjectMeta{
  556. Name: "myautoscaler",
  557. Namespace: metav1.NamespaceDefault,
  558. },
  559. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  560. ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  561. Kind: "ReplicationController",
  562. Name: "myrc",
  563. },
  564. MinReplicas: utilpointer.Int32Ptr(1),
  565. MaxReplicas: 5,
  566. Metrics: []autoscaling.MetricSpec{
  567. {
  568. Type: autoscaling.ObjectMetricSourceType,
  569. Object: &autoscaling.ObjectMetricSource{
  570. DescribedObject: autoscaling.CrossVersionObjectReference{
  571. Kind: "ReplicationController",
  572. Name: "myrc",
  573. },
  574. Metric: autoscaling.MetricIdentifier{
  575. Name: "somemetric",
  576. },
  577. Target: autoscaling.MetricTarget{
  578. Type: autoscaling.ValueMetricType,
  579. Value: resource.NewMilliQuantity(300, resource.DecimalSI),
  580. },
  581. },
  582. },
  583. },
  584. },
  585. },
  586. {
  587. ObjectMeta: metav1.ObjectMeta{
  588. Name: "myautoscaler",
  589. Namespace: metav1.NamespaceDefault,
  590. },
  591. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  592. ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  593. Kind: "ReplicationController",
  594. Name: "myrc",
  595. },
  596. MinReplicas: utilpointer.Int32Ptr(1),
  597. MaxReplicas: 5,
  598. Metrics: []autoscaling.MetricSpec{
  599. {
  600. Type: autoscaling.ExternalMetricSourceType,
  601. External: &autoscaling.ExternalMetricSource{
  602. Metric: autoscaling.MetricIdentifier{
  603. Name: "somemetric",
  604. Selector: metricLabelSelector,
  605. },
  606. Target: autoscaling.MetricTarget{
  607. Type: autoscaling.ValueMetricType,
  608. Value: resource.NewMilliQuantity(300, resource.DecimalSI),
  609. },
  610. },
  611. },
  612. },
  613. },
  614. },
  615. {
  616. ObjectMeta: metav1.ObjectMeta{
  617. Name: "myautoscaler",
  618. Namespace: metav1.NamespaceDefault,
  619. },
  620. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  621. ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  622. Kind: "ReplicationController",
  623. Name: "myrc",
  624. },
  625. MinReplicas: utilpointer.Int32Ptr(1),
  626. MaxReplicas: 5,
  627. Metrics: []autoscaling.MetricSpec{
  628. {
  629. Type: autoscaling.ExternalMetricSourceType,
  630. External: &autoscaling.ExternalMetricSource{
  631. Metric: autoscaling.MetricIdentifier{
  632. Name: "somemetric",
  633. Selector: metricLabelSelector,
  634. },
  635. Target: autoscaling.MetricTarget{
  636. Type: autoscaling.AverageValueMetricType,
  637. AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI),
  638. },
  639. },
  640. },
  641. },
  642. },
  643. },
  644. }
  645. for _, successCase := range successCases {
  646. if errs := ValidateHorizontalPodAutoscaler(&successCase); len(errs) != 0 {
  647. t.Errorf("expected success: %v", errs)
  648. }
  649. }
  650. errorCases := []struct {
  651. horizontalPodAutoscaler autoscaling.HorizontalPodAutoscaler
  652. msg string
  653. }{
  654. {
  655. horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
  656. ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
  657. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  658. ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc"},
  659. MinReplicas: utilpointer.Int32Ptr(1),
  660. MaxReplicas: 5,
  661. Metrics: []autoscaling.MetricSpec{
  662. {
  663. Type: autoscaling.ResourceMetricSourceType,
  664. Resource: &autoscaling.ResourceMetricSource{
  665. Name: api.ResourceCPU,
  666. Target: autoscaling.MetricTarget{
  667. Type: autoscaling.UtilizationMetricType,
  668. AverageUtilization: utilpointer.Int32Ptr(70),
  669. },
  670. },
  671. },
  672. },
  673. },
  674. },
  675. msg: "scaleTargetRef.kind: Required",
  676. },
  677. {
  678. horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
  679. ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
  680. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  681. ScaleTargetRef: autoscaling.CrossVersionObjectReference{Kind: "..", Name: "myrc"},
  682. MinReplicas: utilpointer.Int32Ptr(1),
  683. MaxReplicas: 5,
  684. Metrics: []autoscaling.MetricSpec{
  685. {
  686. Type: autoscaling.ResourceMetricSourceType,
  687. Resource: &autoscaling.ResourceMetricSource{
  688. Name: api.ResourceCPU,
  689. Target: autoscaling.MetricTarget{
  690. Type: autoscaling.UtilizationMetricType,
  691. AverageUtilization: utilpointer.Int32Ptr(70),
  692. },
  693. },
  694. },
  695. },
  696. },
  697. },
  698. msg: "scaleTargetRef.kind: Invalid",
  699. },
  700. {
  701. horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
  702. ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
  703. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  704. ScaleTargetRef: autoscaling.CrossVersionObjectReference{Kind: "ReplicationController"},
  705. MinReplicas: utilpointer.Int32Ptr(1),
  706. MaxReplicas: 5,
  707. Metrics: []autoscaling.MetricSpec{
  708. {
  709. Type: autoscaling.ResourceMetricSourceType,
  710. Resource: &autoscaling.ResourceMetricSource{
  711. Name: api.ResourceCPU,
  712. Target: autoscaling.MetricTarget{
  713. Type: autoscaling.UtilizationMetricType,
  714. AverageUtilization: utilpointer.Int32Ptr(70),
  715. },
  716. },
  717. },
  718. },
  719. },
  720. },
  721. msg: "scaleTargetRef.name: Required",
  722. },
  723. {
  724. horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
  725. ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
  726. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  727. ScaleTargetRef: autoscaling.CrossVersionObjectReference{Kind: "ReplicationController", Name: ".."},
  728. MinReplicas: utilpointer.Int32Ptr(1),
  729. MaxReplicas: 5,
  730. Metrics: []autoscaling.MetricSpec{
  731. {
  732. Type: autoscaling.ResourceMetricSourceType,
  733. Resource: &autoscaling.ResourceMetricSource{
  734. Name: api.ResourceCPU,
  735. Target: autoscaling.MetricTarget{
  736. Type: autoscaling.UtilizationMetricType,
  737. AverageUtilization: utilpointer.Int32Ptr(70),
  738. },
  739. },
  740. },
  741. },
  742. },
  743. },
  744. msg: "scaleTargetRef.name: Invalid",
  745. },
  746. {
  747. horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
  748. ObjectMeta: metav1.ObjectMeta{
  749. Name: "myautoscaler",
  750. Namespace: metav1.NamespaceDefault,
  751. },
  752. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  753. ScaleTargetRef: autoscaling.CrossVersionObjectReference{},
  754. MinReplicas: utilpointer.Int32Ptr(-1),
  755. MaxReplicas: 5,
  756. },
  757. },
  758. msg: "must be greater than or equal to 1",
  759. },
  760. {
  761. horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
  762. ObjectMeta: metav1.ObjectMeta{
  763. Name: "myautoscaler",
  764. Namespace: metav1.NamespaceDefault,
  765. },
  766. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  767. ScaleTargetRef: autoscaling.CrossVersionObjectReference{},
  768. MinReplicas: utilpointer.Int32Ptr(7),
  769. MaxReplicas: 5,
  770. },
  771. },
  772. msg: "must be greater than or equal to `minReplicas`",
  773. },
  774. {
  775. horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
  776. ObjectMeta: metav1.ObjectMeta{
  777. Name: "myautoscaler",
  778. Namespace: metav1.NamespaceDefault,
  779. },
  780. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  781. ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
  782. MinReplicas: utilpointer.Int32Ptr(1),
  783. MaxReplicas: 5,
  784. Metrics: []autoscaling.MetricSpec{
  785. {
  786. Type: autoscaling.ResourceMetricSourceType,
  787. Resource: &autoscaling.ResourceMetricSource{
  788. Name: api.ResourceCPU,
  789. Target: autoscaling.MetricTarget{
  790. Type: autoscaling.UtilizationMetricType,
  791. AverageUtilization: utilpointer.Int32Ptr(70),
  792. AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI),
  793. },
  794. },
  795. },
  796. },
  797. },
  798. },
  799. msg: "may not set both a target raw value and a target utilization",
  800. },
  801. {
  802. horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
  803. ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
  804. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  805. ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
  806. MinReplicas: utilpointer.Int32Ptr(1),
  807. MaxReplicas: 5,
  808. Metrics: []autoscaling.MetricSpec{
  809. {
  810. Type: autoscaling.ResourceMetricSourceType,
  811. Resource: &autoscaling.ResourceMetricSource{
  812. Target: autoscaling.MetricTarget{
  813. Type: autoscaling.UtilizationMetricType,
  814. AverageUtilization: utilpointer.Int32Ptr(70),
  815. },
  816. },
  817. },
  818. },
  819. },
  820. },
  821. msg: "must specify a resource name",
  822. },
  823. {
  824. horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
  825. ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
  826. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  827. ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
  828. MinReplicas: utilpointer.Int32Ptr(1),
  829. MaxReplicas: 5,
  830. Metrics: []autoscaling.MetricSpec{
  831. {
  832. Type: autoscaling.ResourceMetricSourceType,
  833. Resource: &autoscaling.ResourceMetricSource{
  834. Name: api.ResourceCPU,
  835. Target: autoscaling.MetricTarget{
  836. Type: autoscaling.UtilizationMetricType,
  837. AverageUtilization: utilpointer.Int32Ptr(-10),
  838. },
  839. },
  840. },
  841. },
  842. },
  843. },
  844. msg: "must be greater than 0",
  845. },
  846. {
  847. horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
  848. ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
  849. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  850. ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
  851. MinReplicas: utilpointer.Int32Ptr(1),
  852. MaxReplicas: 5,
  853. Metrics: []autoscaling.MetricSpec{
  854. {
  855. Type: autoscaling.ResourceMetricSourceType,
  856. Resource: &autoscaling.ResourceMetricSource{
  857. Name: api.ResourceCPU,
  858. Target: autoscaling.MetricTarget{
  859. Type: autoscaling.ValueMetricType,
  860. },
  861. },
  862. },
  863. },
  864. },
  865. },
  866. msg: "must set either a target raw value or a target utilization",
  867. },
  868. {
  869. horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
  870. ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
  871. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  872. ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
  873. MinReplicas: utilpointer.Int32Ptr(1),
  874. MaxReplicas: 5,
  875. Metrics: []autoscaling.MetricSpec{
  876. {
  877. Type: autoscaling.PodsMetricSourceType,
  878. Pods: &autoscaling.PodsMetricSource{
  879. Metric: autoscaling.MetricIdentifier{},
  880. Target: autoscaling.MetricTarget{
  881. Type: autoscaling.ValueMetricType,
  882. AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  883. },
  884. },
  885. },
  886. },
  887. },
  888. },
  889. msg: "must specify a metric name",
  890. },
  891. {
  892. horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
  893. ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
  894. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  895. ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
  896. MinReplicas: utilpointer.Int32Ptr(1),
  897. MaxReplicas: 5,
  898. Metrics: []autoscaling.MetricSpec{
  899. {
  900. Type: autoscaling.PodsMetricSourceType,
  901. Pods: &autoscaling.PodsMetricSource{
  902. Metric: autoscaling.MetricIdentifier{
  903. Name: "somemetric",
  904. },
  905. Target: autoscaling.MetricTarget{
  906. Type: autoscaling.ValueMetricType,
  907. },
  908. },
  909. },
  910. },
  911. },
  912. },
  913. msg: "must specify a positive target averageValue",
  914. },
  915. {
  916. horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
  917. ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
  918. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  919. ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
  920. MinReplicas: utilpointer.Int32Ptr(1),
  921. MaxReplicas: 5,
  922. Metrics: []autoscaling.MetricSpec{
  923. {
  924. Type: autoscaling.ObjectMetricSourceType,
  925. Object: &autoscaling.ObjectMetricSource{
  926. DescribedObject: autoscaling.CrossVersionObjectReference{
  927. Kind: "ReplicationController",
  928. Name: "myrc",
  929. },
  930. Metric: autoscaling.MetricIdentifier{
  931. Name: "somemetric",
  932. },
  933. Target: autoscaling.MetricTarget{
  934. Type: autoscaling.ValueMetricType,
  935. },
  936. },
  937. },
  938. },
  939. },
  940. },
  941. msg: "must set either a target value or averageValue",
  942. },
  943. {
  944. horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
  945. ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
  946. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  947. ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
  948. MinReplicas: utilpointer.Int32Ptr(1),
  949. MaxReplicas: 5,
  950. Metrics: []autoscaling.MetricSpec{
  951. {
  952. Type: autoscaling.ObjectMetricSourceType,
  953. Object: &autoscaling.ObjectMetricSource{
  954. DescribedObject: autoscaling.CrossVersionObjectReference{
  955. Name: "myrc",
  956. },
  957. Metric: autoscaling.MetricIdentifier{
  958. Name: "somemetric",
  959. },
  960. Target: autoscaling.MetricTarget{
  961. Type: autoscaling.ValueMetricType,
  962. Value: resource.NewMilliQuantity(100, resource.DecimalSI),
  963. },
  964. },
  965. },
  966. },
  967. },
  968. },
  969. msg: "object.describedObject.kind: Required",
  970. },
  971. {
  972. horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
  973. ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
  974. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  975. ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
  976. MinReplicas: utilpointer.Int32Ptr(1),
  977. MaxReplicas: 5,
  978. Metrics: []autoscaling.MetricSpec{
  979. {
  980. Type: autoscaling.ObjectMetricSourceType,
  981. Object: &autoscaling.ObjectMetricSource{
  982. DescribedObject: autoscaling.CrossVersionObjectReference{
  983. Kind: "ReplicationController",
  984. Name: "myrc",
  985. },
  986. Target: autoscaling.MetricTarget{
  987. Type: autoscaling.ValueMetricType,
  988. Value: resource.NewMilliQuantity(100, resource.DecimalSI),
  989. },
  990. },
  991. },
  992. },
  993. },
  994. },
  995. msg: "must specify a metric name",
  996. },
  997. {
  998. horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
  999. ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
  1000. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  1001. ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
  1002. MinReplicas: utilpointer.Int32Ptr(1),
  1003. MaxReplicas: 5,
  1004. Metrics: []autoscaling.MetricSpec{
  1005. {
  1006. Type: autoscaling.ExternalMetricSourceType,
  1007. External: &autoscaling.ExternalMetricSource{
  1008. Metric: autoscaling.MetricIdentifier{
  1009. Selector: metricLabelSelector,
  1010. },
  1011. Target: autoscaling.MetricTarget{
  1012. Type: autoscaling.ValueMetricType,
  1013. Value: resource.NewMilliQuantity(300, resource.DecimalSI),
  1014. },
  1015. },
  1016. },
  1017. },
  1018. },
  1019. },
  1020. msg: "must specify a metric name",
  1021. },
  1022. {
  1023. horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
  1024. ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
  1025. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  1026. ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
  1027. MinReplicas: utilpointer.Int32Ptr(1),
  1028. MaxReplicas: 5,
  1029. Metrics: []autoscaling.MetricSpec{
  1030. {
  1031. Type: autoscaling.ExternalMetricSourceType,
  1032. External: &autoscaling.ExternalMetricSource{
  1033. Metric: autoscaling.MetricIdentifier{
  1034. Name: "foo/../",
  1035. Selector: metricLabelSelector,
  1036. },
  1037. Target: autoscaling.MetricTarget{
  1038. Type: autoscaling.ValueMetricType,
  1039. Value: resource.NewMilliQuantity(300, resource.DecimalSI),
  1040. },
  1041. },
  1042. },
  1043. },
  1044. },
  1045. },
  1046. msg: "'/'",
  1047. },
  1048. {
  1049. horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
  1050. ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
  1051. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  1052. ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
  1053. MinReplicas: utilpointer.Int32Ptr(1),
  1054. MaxReplicas: 5,
  1055. Metrics: []autoscaling.MetricSpec{
  1056. {
  1057. Type: autoscaling.ExternalMetricSourceType,
  1058. External: &autoscaling.ExternalMetricSource{
  1059. Metric: autoscaling.MetricIdentifier{
  1060. Name: "somemetric",
  1061. Selector: metricLabelSelector,
  1062. },
  1063. Target: autoscaling.MetricTarget{
  1064. Type: autoscaling.ValueMetricType,
  1065. },
  1066. },
  1067. },
  1068. },
  1069. },
  1070. },
  1071. msg: "must set either a target value for metric or a per-pod target",
  1072. },
  1073. {
  1074. horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
  1075. ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
  1076. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  1077. ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
  1078. MinReplicas: utilpointer.Int32Ptr(1),
  1079. MaxReplicas: 5,
  1080. Metrics: []autoscaling.MetricSpec{
  1081. {
  1082. Type: autoscaling.ExternalMetricSourceType,
  1083. External: &autoscaling.ExternalMetricSource{
  1084. Metric: autoscaling.MetricIdentifier{
  1085. Name: "somemetric",
  1086. Selector: metricLabelSelector,
  1087. },
  1088. Target: autoscaling.MetricTarget{
  1089. Type: autoscaling.ValueMetricType,
  1090. Value: resource.NewMilliQuantity(-300, resource.DecimalSI),
  1091. },
  1092. },
  1093. },
  1094. },
  1095. },
  1096. },
  1097. msg: "must be positive",
  1098. },
  1099. {
  1100. horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
  1101. ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
  1102. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  1103. ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
  1104. MinReplicas: utilpointer.Int32Ptr(1),
  1105. MaxReplicas: 5,
  1106. Metrics: []autoscaling.MetricSpec{
  1107. {
  1108. Type: autoscaling.ExternalMetricSourceType,
  1109. External: &autoscaling.ExternalMetricSource{
  1110. Metric: autoscaling.MetricIdentifier{
  1111. Name: "somemetric",
  1112. Selector: metricLabelSelector,
  1113. },
  1114. Target: autoscaling.MetricTarget{
  1115. Type: autoscaling.ValueMetricType,
  1116. AverageValue: resource.NewMilliQuantity(-300, resource.DecimalSI),
  1117. },
  1118. },
  1119. },
  1120. },
  1121. },
  1122. },
  1123. msg: "must be positive",
  1124. },
  1125. {
  1126. horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
  1127. ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
  1128. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  1129. ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
  1130. MinReplicas: utilpointer.Int32Ptr(1),
  1131. MaxReplicas: 5,
  1132. Metrics: []autoscaling.MetricSpec{
  1133. {
  1134. Type: autoscaling.ExternalMetricSourceType,
  1135. External: &autoscaling.ExternalMetricSource{
  1136. Metric: autoscaling.MetricIdentifier{
  1137. Name: "somemetric",
  1138. Selector: metricLabelSelector,
  1139. },
  1140. Target: autoscaling.MetricTarget{
  1141. Type: autoscaling.ValueMetricType,
  1142. Value: resource.NewMilliQuantity(300, resource.DecimalSI),
  1143. AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI),
  1144. },
  1145. },
  1146. },
  1147. },
  1148. },
  1149. },
  1150. msg: "may not set both a target value for metric and a per-pod target",
  1151. },
  1152. {
  1153. horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
  1154. ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
  1155. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  1156. ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
  1157. MinReplicas: utilpointer.Int32Ptr(1),
  1158. MaxReplicas: 5,
  1159. Metrics: []autoscaling.MetricSpec{
  1160. {
  1161. Type: autoscaling.ExternalMetricSourceType,
  1162. External: &autoscaling.ExternalMetricSource{
  1163. Metric: autoscaling.MetricIdentifier{
  1164. Name: "somemetric",
  1165. Selector: metricLabelSelector,
  1166. },
  1167. Target: autoscaling.MetricTarget{
  1168. Type: "boogity",
  1169. Value: resource.NewMilliQuantity(300, resource.DecimalSI),
  1170. },
  1171. },
  1172. },
  1173. },
  1174. },
  1175. },
  1176. msg: "must be either Utilization, Value, or AverageValue",
  1177. },
  1178. {
  1179. horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
  1180. ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
  1181. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  1182. ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
  1183. MinReplicas: utilpointer.Int32Ptr(1),
  1184. MaxReplicas: 5,
  1185. Metrics: []autoscaling.MetricSpec{
  1186. {
  1187. Type: autoscaling.ExternalMetricSourceType,
  1188. External: &autoscaling.ExternalMetricSource{
  1189. Metric: autoscaling.MetricIdentifier{
  1190. Name: "somemetric",
  1191. Selector: metricLabelSelector,
  1192. },
  1193. Target: autoscaling.MetricTarget{
  1194. Value: resource.NewMilliQuantity(300, resource.DecimalSI),
  1195. },
  1196. },
  1197. },
  1198. },
  1199. },
  1200. },
  1201. msg: "must specify a metric target type",
  1202. },
  1203. {
  1204. horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
  1205. ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
  1206. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  1207. ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
  1208. MinReplicas: utilpointer.Int32Ptr(1),
  1209. MaxReplicas: 5,
  1210. Metrics: []autoscaling.MetricSpec{
  1211. {
  1212. Type: autoscaling.ExternalMetricSourceType,
  1213. External: &autoscaling.ExternalMetricSource{
  1214. Metric: autoscaling.MetricIdentifier{
  1215. Name: "somemetric",
  1216. Selector: metricLabelSelector,
  1217. },
  1218. },
  1219. },
  1220. },
  1221. },
  1222. },
  1223. msg: "must specify a metric target",
  1224. },
  1225. {
  1226. horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
  1227. ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
  1228. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  1229. ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
  1230. MinReplicas: utilpointer.Int32Ptr(1),
  1231. MaxReplicas: 5,
  1232. Metrics: []autoscaling.MetricSpec{
  1233. {},
  1234. },
  1235. },
  1236. },
  1237. msg: "must specify a metric source type",
  1238. },
  1239. {
  1240. horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
  1241. ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
  1242. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  1243. ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
  1244. MinReplicas: utilpointer.Int32Ptr(1),
  1245. MaxReplicas: 5,
  1246. Metrics: []autoscaling.MetricSpec{
  1247. {
  1248. Type: autoscaling.MetricSourceType("InvalidType"),
  1249. },
  1250. },
  1251. },
  1252. },
  1253. msg: "type: Unsupported value",
  1254. },
  1255. {
  1256. horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
  1257. ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
  1258. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  1259. ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
  1260. MinReplicas: utilpointer.Int32Ptr(1),
  1261. MaxReplicas: 5,
  1262. Metrics: []autoscaling.MetricSpec{
  1263. {
  1264. Type: autoscaling.ResourceMetricSourceType,
  1265. Resource: &autoscaling.ResourceMetricSource{
  1266. Name: api.ResourceCPU,
  1267. Target: autoscaling.MetricTarget{
  1268. Type: autoscaling.AverageValueMetricType,
  1269. AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  1270. },
  1271. },
  1272. Pods: &autoscaling.PodsMetricSource{
  1273. Metric: autoscaling.MetricIdentifier{
  1274. Name: "somemetric",
  1275. },
  1276. Target: autoscaling.MetricTarget{
  1277. Type: autoscaling.AverageValueMetricType,
  1278. AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  1279. },
  1280. },
  1281. },
  1282. },
  1283. },
  1284. },
  1285. msg: "must populate the given metric source only",
  1286. },
  1287. }
  1288. for _, c := range errorCases {
  1289. errs := ValidateHorizontalPodAutoscaler(&c.horizontalPodAutoscaler)
  1290. if len(errs) == 0 {
  1291. t.Errorf("expected failure for %q", c.msg)
  1292. } else if !strings.Contains(errs[0].Error(), c.msg) {
  1293. t.Errorf("unexpected error: %q, expected: %q", errs[0], c.msg)
  1294. }
  1295. }
  1296. sourceTypes := map[autoscaling.MetricSourceType]autoscaling.MetricSpec{
  1297. autoscaling.ResourceMetricSourceType: {
  1298. Resource: &autoscaling.ResourceMetricSource{
  1299. Name: api.ResourceCPU,
  1300. Target: autoscaling.MetricTarget{
  1301. Type: autoscaling.AverageValueMetricType,
  1302. AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  1303. },
  1304. },
  1305. },
  1306. autoscaling.PodsMetricSourceType: {
  1307. Pods: &autoscaling.PodsMetricSource{
  1308. Metric: autoscaling.MetricIdentifier{
  1309. Name: "somemetric",
  1310. },
  1311. Target: autoscaling.MetricTarget{
  1312. Type: autoscaling.AverageValueMetricType,
  1313. AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
  1314. },
  1315. },
  1316. },
  1317. autoscaling.ObjectMetricSourceType: {
  1318. Object: &autoscaling.ObjectMetricSource{
  1319. DescribedObject: autoscaling.CrossVersionObjectReference{
  1320. Kind: "ReplicationController",
  1321. Name: "myrc",
  1322. },
  1323. Metric: autoscaling.MetricIdentifier{
  1324. Name: "somemetric",
  1325. },
  1326. Target: autoscaling.MetricTarget{
  1327. Type: autoscaling.ValueMetricType,
  1328. Value: resource.NewMilliQuantity(100, resource.DecimalSI),
  1329. },
  1330. },
  1331. },
  1332. }
  1333. for correctType, spec := range sourceTypes {
  1334. for incorrectType := range sourceTypes {
  1335. if correctType == incorrectType {
  1336. continue
  1337. }
  1338. spec.Type = incorrectType
  1339. errs := ValidateHorizontalPodAutoscaler(&autoscaling.HorizontalPodAutoscaler{
  1340. ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
  1341. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  1342. ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
  1343. MinReplicas: utilpointer.Int32Ptr(1),
  1344. MaxReplicas: 5, Metrics: []autoscaling.MetricSpec{spec},
  1345. },
  1346. })
  1347. expectedMsg := "must populate information for the given metric source"
  1348. if len(errs) == 0 {
  1349. t.Errorf("expected failure with type of %v and spec for %v", incorrectType, correctType)
  1350. } else if !strings.Contains(errs[0].Error(), expectedMsg) {
  1351. t.Errorf("unexpected error: %q, expected %q", errs[0], expectedMsg)
  1352. }
  1353. }
  1354. }
  1355. }
  1356. func prepareMinReplicasCases(t *testing.T, minReplicas int32) []autoscaling.HorizontalPodAutoscaler {
  1357. metricLabelSelector, err := metav1.ParseToLabelSelector("label=value")
  1358. if err != nil {
  1359. t.Errorf("unable to parse label selector: %v", err)
  1360. }
  1361. minReplicasCases := []autoscaling.HorizontalPodAutoscaler{
  1362. {
  1363. ObjectMeta: metav1.ObjectMeta{
  1364. Name: "myautoscaler",
  1365. Namespace: metav1.NamespaceDefault,
  1366. ResourceVersion: "theversion",
  1367. },
  1368. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  1369. ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  1370. Kind: "ReplicationController",
  1371. Name: "myrc",
  1372. },
  1373. MinReplicas: utilpointer.Int32Ptr(minReplicas),
  1374. MaxReplicas: 5,
  1375. Metrics: []autoscaling.MetricSpec{
  1376. {
  1377. Type: autoscaling.ObjectMetricSourceType,
  1378. Object: &autoscaling.ObjectMetricSource{
  1379. DescribedObject: autoscaling.CrossVersionObjectReference{
  1380. Kind: "ReplicationController",
  1381. Name: "myrc",
  1382. },
  1383. Metric: autoscaling.MetricIdentifier{
  1384. Name: "somemetric",
  1385. },
  1386. Target: autoscaling.MetricTarget{
  1387. Type: autoscaling.ValueMetricType,
  1388. Value: resource.NewMilliQuantity(300, resource.DecimalSI),
  1389. },
  1390. },
  1391. },
  1392. },
  1393. },
  1394. },
  1395. {
  1396. ObjectMeta: metav1.ObjectMeta{
  1397. Name: "myautoscaler",
  1398. Namespace: metav1.NamespaceDefault,
  1399. ResourceVersion: "theversion",
  1400. },
  1401. Spec: autoscaling.HorizontalPodAutoscalerSpec{
  1402. ScaleTargetRef: autoscaling.CrossVersionObjectReference{
  1403. Kind: "ReplicationController",
  1404. Name: "myrc",
  1405. },
  1406. MinReplicas: utilpointer.Int32Ptr(minReplicas),
  1407. MaxReplicas: 5,
  1408. Metrics: []autoscaling.MetricSpec{
  1409. {
  1410. Type: autoscaling.ExternalMetricSourceType,
  1411. External: &autoscaling.ExternalMetricSource{
  1412. Metric: autoscaling.MetricIdentifier{
  1413. Name: "somemetric",
  1414. Selector: metricLabelSelector,
  1415. },
  1416. Target: autoscaling.MetricTarget{
  1417. Type: autoscaling.AverageValueMetricType,
  1418. AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI),
  1419. },
  1420. },
  1421. },
  1422. },
  1423. },
  1424. },
  1425. }
  1426. return minReplicasCases
  1427. }
  1428. func TestValidateHorizontalPodAutoscalerScaleToZeroEnabled(t *testing.T) {
  1429. // Enable HPAScaleToZero feature gate.
  1430. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.HPAScaleToZero, true)()
  1431. zeroMinReplicasCases := prepareMinReplicasCases(t, 0)
  1432. for _, successCase := range zeroMinReplicasCases {
  1433. if errs := ValidateHorizontalPodAutoscaler(&successCase); len(errs) != 0 {
  1434. t.Errorf("expected success: %v", errs)
  1435. }
  1436. }
  1437. }
  1438. func TestValidateHorizontalPodAutoscalerScaleToZeroDisabled(t *testing.T) {
  1439. // Disable HPAScaleToZero feature gate.
  1440. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.HPAScaleToZero, false)()
  1441. zeroMinReplicasCases := prepareMinReplicasCases(t, 0)
  1442. errorMsg := "must be greater than or equal to 1"
  1443. for _, errorCase := range zeroMinReplicasCases {
  1444. errs := ValidateHorizontalPodAutoscaler(&errorCase)
  1445. if len(errs) == 0 {
  1446. t.Errorf("expected failure for %q", errorMsg)
  1447. } else if !strings.Contains(errs[0].Error(), errorMsg) {
  1448. t.Errorf("unexpected error: %q, expected: %q", errs[0], errorMsg)
  1449. }
  1450. }
  1451. nonZeroMinReplicasCases := prepareMinReplicasCases(t, 1)
  1452. for _, successCase := range nonZeroMinReplicasCases {
  1453. successCase.Spec.MinReplicas = utilpointer.Int32Ptr(1)
  1454. if errs := ValidateHorizontalPodAutoscaler(&successCase); len(errs) != 0 {
  1455. t.Errorf("expected success: %v", errs)
  1456. }
  1457. }
  1458. }
  1459. func TestValidateHorizontalPodAutoscalerUpdateScaleToZeroEnabled(t *testing.T) {
  1460. // Enable HPAScaleToZero feature gate.
  1461. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.HPAScaleToZero, true)()
  1462. zeroMinReplicasCases := prepareMinReplicasCases(t, 0)
  1463. nonZeroMinReplicasCases := prepareMinReplicasCases(t, 1)
  1464. for i, zeroCase := range zeroMinReplicasCases {
  1465. nonZeroCase := nonZeroMinReplicasCases[i]
  1466. if errs := ValidateHorizontalPodAutoscalerUpdate(&nonZeroCase, &zeroCase); len(errs) != 0 {
  1467. t.Errorf("expected success: %v", errs)
  1468. }
  1469. if errs := ValidateHorizontalPodAutoscalerUpdate(&zeroCase, &nonZeroCase); len(errs) != 0 {
  1470. t.Errorf("expected success: %v", errs)
  1471. }
  1472. }
  1473. }
  1474. func TestValidateHorizontalPodAutoscalerScaleToZeroUpdateDisabled(t *testing.T) {
  1475. // Disable HPAScaleToZero feature gate.
  1476. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.HPAScaleToZero, false)()
  1477. zeroMinReplicasCases := prepareMinReplicasCases(t, 0)
  1478. nonZeroMinReplicasCases := prepareMinReplicasCases(t, 1)
  1479. errorMsg := "must be greater than or equal to 1"
  1480. for i, zeroCase := range zeroMinReplicasCases {
  1481. nonZeroCase := nonZeroMinReplicasCases[i]
  1482. errs := ValidateHorizontalPodAutoscalerUpdate(&zeroCase, &nonZeroCase)
  1483. if len(errs) == 0 {
  1484. t.Errorf("expected failure for %q", errorMsg)
  1485. } else if !strings.Contains(errs[0].Error(), errorMsg) {
  1486. t.Errorf("unexpected error: %q, expected: %q", errs[0], errorMsg)
  1487. }
  1488. if errs := ValidateHorizontalPodAutoscalerUpdate(&zeroCase, &zeroCase); len(errs) != 0 {
  1489. t.Errorf("expected success: %v", errs)
  1490. }
  1491. if errs := ValidateHorizontalPodAutoscalerUpdate(&nonZeroCase, &zeroCase); len(errs) != 0 {
  1492. t.Errorf("expected success: %v", errs)
  1493. }
  1494. }
  1495. }