validation_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  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/kubernetes/pkg/api"
  18. "k8s.io/kubernetes/pkg/api/unversioned"
  19. "k8s.io/kubernetes/pkg/apis/apps"
  20. )
  21. func TestValidatePetSet(t *testing.T) {
  22. validLabels := map[string]string{"a": "b"}
  23. validPodTemplate := api.PodTemplate{
  24. Template: api.PodTemplateSpec{
  25. ObjectMeta: api.ObjectMeta{
  26. Labels: validLabels,
  27. },
  28. Spec: api.PodSpec{
  29. RestartPolicy: api.RestartPolicyAlways,
  30. DNSPolicy: api.DNSClusterFirst,
  31. Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
  32. },
  33. },
  34. }
  35. invalidLabels := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
  36. invalidPodTemplate := api.PodTemplate{
  37. Template: api.PodTemplateSpec{
  38. Spec: api.PodSpec{
  39. RestartPolicy: api.RestartPolicyAlways,
  40. DNSPolicy: api.DNSClusterFirst,
  41. },
  42. ObjectMeta: api.ObjectMeta{
  43. Labels: invalidLabels,
  44. },
  45. },
  46. }
  47. successCases := []apps.PetSet{
  48. {
  49. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  50. Spec: apps.PetSetSpec{
  51. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  52. Template: validPodTemplate.Template,
  53. },
  54. },
  55. {
  56. ObjectMeta: api.ObjectMeta{Name: "abc-123", Namespace: api.NamespaceDefault},
  57. Spec: apps.PetSetSpec{
  58. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  59. Template: validPodTemplate.Template,
  60. },
  61. },
  62. }
  63. for _, successCase := range successCases {
  64. if errs := ValidatePetSet(&successCase); len(errs) != 0 {
  65. t.Errorf("expected success: %v", errs)
  66. }
  67. }
  68. errorCases := map[string]apps.PetSet{
  69. "zero-length ID": {
  70. ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
  71. Spec: apps.PetSetSpec{
  72. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  73. Template: validPodTemplate.Template,
  74. },
  75. },
  76. "missing-namespace": {
  77. ObjectMeta: api.ObjectMeta{Name: "abc-123"},
  78. Spec: apps.PetSetSpec{
  79. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  80. Template: validPodTemplate.Template,
  81. },
  82. },
  83. "empty selector": {
  84. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  85. Spec: apps.PetSetSpec{
  86. Template: validPodTemplate.Template,
  87. },
  88. },
  89. "selector_doesnt_match": {
  90. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  91. Spec: apps.PetSetSpec{
  92. Selector: &unversioned.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
  93. Template: validPodTemplate.Template,
  94. },
  95. },
  96. "invalid manifest": {
  97. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  98. Spec: apps.PetSetSpec{
  99. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  100. },
  101. },
  102. "negative_replicas": {
  103. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  104. Spec: apps.PetSetSpec{
  105. Replicas: -1,
  106. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  107. },
  108. },
  109. "invalid_label": {
  110. ObjectMeta: api.ObjectMeta{
  111. Name: "abc-123",
  112. Namespace: api.NamespaceDefault,
  113. Labels: map[string]string{
  114. "NoUppercaseOrSpecialCharsLike=Equals": "bar",
  115. },
  116. },
  117. Spec: apps.PetSetSpec{
  118. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  119. Template: validPodTemplate.Template,
  120. },
  121. },
  122. "invalid_label 2": {
  123. ObjectMeta: api.ObjectMeta{
  124. Name: "abc-123",
  125. Namespace: api.NamespaceDefault,
  126. Labels: map[string]string{
  127. "NoUppercaseOrSpecialCharsLike=Equals": "bar",
  128. },
  129. },
  130. Spec: apps.PetSetSpec{
  131. Template: invalidPodTemplate.Template,
  132. },
  133. },
  134. "invalid_annotation": {
  135. ObjectMeta: api.ObjectMeta{
  136. Name: "abc-123",
  137. Namespace: api.NamespaceDefault,
  138. Annotations: map[string]string{
  139. "NoUppercaseOrSpecialCharsLike=Equals": "bar",
  140. },
  141. },
  142. Spec: apps.PetSetSpec{
  143. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  144. Template: validPodTemplate.Template,
  145. },
  146. },
  147. "invalid restart policy 1": {
  148. ObjectMeta: api.ObjectMeta{
  149. Name: "abc-123",
  150. Namespace: api.NamespaceDefault,
  151. },
  152. Spec: apps.PetSetSpec{
  153. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  154. Template: api.PodTemplateSpec{
  155. Spec: api.PodSpec{
  156. RestartPolicy: api.RestartPolicyOnFailure,
  157. DNSPolicy: api.DNSClusterFirst,
  158. Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
  159. },
  160. ObjectMeta: api.ObjectMeta{
  161. Labels: validLabels,
  162. },
  163. },
  164. },
  165. },
  166. "invalid restart policy 2": {
  167. ObjectMeta: api.ObjectMeta{
  168. Name: "abc-123",
  169. Namespace: api.NamespaceDefault,
  170. },
  171. Spec: apps.PetSetSpec{
  172. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  173. Template: api.PodTemplateSpec{
  174. Spec: api.PodSpec{
  175. RestartPolicy: api.RestartPolicyNever,
  176. DNSPolicy: api.DNSClusterFirst,
  177. Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
  178. },
  179. ObjectMeta: api.ObjectMeta{
  180. Labels: validLabels,
  181. },
  182. },
  183. },
  184. },
  185. }
  186. for k, v := range errorCases {
  187. errs := ValidatePetSet(&v)
  188. if len(errs) == 0 {
  189. t.Errorf("expected failure for %s", k)
  190. }
  191. for i := range errs {
  192. field := errs[i].Field
  193. if !strings.HasPrefix(field, "spec.template.") &&
  194. field != "metadata.name" &&
  195. field != "metadata.namespace" &&
  196. field != "spec.selector" &&
  197. field != "spec.template" &&
  198. field != "GCEPersistentDisk.ReadOnly" &&
  199. field != "spec.replicas" &&
  200. field != "spec.template.labels" &&
  201. field != "metadata.annotations" &&
  202. field != "metadata.labels" &&
  203. field != "status.replicas" {
  204. t.Errorf("%s: missing prefix for: %v", k, errs[i])
  205. }
  206. }
  207. }
  208. }
  209. func TestValidatePetSetUpdate(t *testing.T) {
  210. validLabels := map[string]string{"a": "b"}
  211. validPodTemplate := api.PodTemplate{
  212. Template: api.PodTemplateSpec{
  213. ObjectMeta: api.ObjectMeta{
  214. Labels: validLabels,
  215. },
  216. Spec: api.PodSpec{
  217. RestartPolicy: api.RestartPolicyAlways,
  218. DNSPolicy: api.DNSClusterFirst,
  219. Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
  220. },
  221. },
  222. }
  223. readWriteVolumePodTemplate := api.PodTemplate{
  224. Template: api.PodTemplateSpec{
  225. ObjectMeta: api.ObjectMeta{
  226. Labels: validLabels,
  227. },
  228. Spec: api.PodSpec{
  229. RestartPolicy: api.RestartPolicyAlways,
  230. DNSPolicy: api.DNSClusterFirst,
  231. Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
  232. Volumes: []api.Volume{{Name: "gcepd", VolumeSource: api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{PDName: "my-PD", FSType: "ext4", Partition: 1, ReadOnly: false}}}},
  233. },
  234. },
  235. }
  236. invalidLabels := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
  237. invalidPodTemplate := api.PodTemplate{
  238. Template: api.PodTemplateSpec{
  239. Spec: api.PodSpec{
  240. RestartPolicy: api.RestartPolicyAlways,
  241. DNSPolicy: api.DNSClusterFirst,
  242. },
  243. ObjectMeta: api.ObjectMeta{
  244. Labels: invalidLabels,
  245. },
  246. },
  247. }
  248. type psUpdateTest struct {
  249. old apps.PetSet
  250. update apps.PetSet
  251. }
  252. successCases := []psUpdateTest{
  253. {
  254. old: apps.PetSet{
  255. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  256. Spec: apps.PetSetSpec{
  257. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  258. Template: validPodTemplate.Template,
  259. },
  260. },
  261. update: apps.PetSet{
  262. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  263. Spec: apps.PetSetSpec{
  264. Replicas: 3,
  265. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  266. Template: validPodTemplate.Template,
  267. },
  268. },
  269. },
  270. }
  271. for _, successCase := range successCases {
  272. successCase.old.ObjectMeta.ResourceVersion = "1"
  273. successCase.update.ObjectMeta.ResourceVersion = "1"
  274. if errs := ValidatePetSetUpdate(&successCase.update, &successCase.old); len(errs) != 0 {
  275. t.Errorf("expected success: %v", errs)
  276. }
  277. }
  278. errorCases := map[string]psUpdateTest{
  279. "more than one read/write": {
  280. old: apps.PetSet{
  281. ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
  282. Spec: apps.PetSetSpec{
  283. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  284. Template: validPodTemplate.Template,
  285. },
  286. },
  287. update: apps.PetSet{
  288. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  289. Spec: apps.PetSetSpec{
  290. Replicas: 2,
  291. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  292. Template: readWriteVolumePodTemplate.Template,
  293. },
  294. },
  295. },
  296. "updates to a field other than spec.Replicas": {
  297. old: apps.PetSet{
  298. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  299. Spec: apps.PetSetSpec{
  300. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  301. Template: validPodTemplate.Template,
  302. },
  303. },
  304. update: apps.PetSet{
  305. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  306. Spec: apps.PetSetSpec{
  307. Replicas: 1,
  308. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  309. Template: readWriteVolumePodTemplate.Template,
  310. },
  311. },
  312. },
  313. "invalid selector": {
  314. old: apps.PetSet{
  315. ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
  316. Spec: apps.PetSetSpec{
  317. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  318. Template: validPodTemplate.Template,
  319. },
  320. },
  321. update: apps.PetSet{
  322. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  323. Spec: apps.PetSetSpec{
  324. Replicas: 2,
  325. Selector: &unversioned.LabelSelector{MatchLabels: invalidLabels},
  326. Template: validPodTemplate.Template,
  327. },
  328. },
  329. },
  330. "invalid pod": {
  331. old: apps.PetSet{
  332. ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
  333. Spec: apps.PetSetSpec{
  334. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  335. Template: validPodTemplate.Template,
  336. },
  337. },
  338. update: apps.PetSet{
  339. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  340. Spec: apps.PetSetSpec{
  341. Replicas: 2,
  342. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  343. Template: invalidPodTemplate.Template,
  344. },
  345. },
  346. },
  347. "negative replicas": {
  348. old: apps.PetSet{
  349. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  350. Spec: apps.PetSetSpec{
  351. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  352. Template: validPodTemplate.Template,
  353. },
  354. },
  355. update: apps.PetSet{
  356. ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
  357. Spec: apps.PetSetSpec{
  358. Replicas: -1,
  359. Selector: &unversioned.LabelSelector{MatchLabels: validLabels},
  360. Template: validPodTemplate.Template,
  361. },
  362. },
  363. },
  364. }
  365. for testName, errorCase := range errorCases {
  366. if errs := ValidatePetSetUpdate(&errorCase.update, &errorCase.old); len(errs) == 0 {
  367. t.Errorf("expected failure: %s", testName)
  368. }
  369. }
  370. }