validation.go 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885
  1. /*
  2. Copyright 2015 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. "fmt"
  16. "net"
  17. "reflect"
  18. "regexp"
  19. "strconv"
  20. "strings"
  21. "k8s.io/kubernetes/pkg/api"
  22. "k8s.io/kubernetes/pkg/api/unversioned"
  23. unversionedvalidation "k8s.io/kubernetes/pkg/api/unversioned/validation"
  24. apivalidation "k8s.io/kubernetes/pkg/api/validation"
  25. "k8s.io/kubernetes/pkg/apis/extensions"
  26. "k8s.io/kubernetes/pkg/labels"
  27. "k8s.io/kubernetes/pkg/security/apparmor"
  28. psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
  29. "k8s.io/kubernetes/pkg/util/intstr"
  30. "k8s.io/kubernetes/pkg/util/sets"
  31. "k8s.io/kubernetes/pkg/util/validation"
  32. "k8s.io/kubernetes/pkg/util/validation/field"
  33. )
  34. func ValidateThirdPartyResourceUpdate(update, old *extensions.ThirdPartyResource) field.ErrorList {
  35. allErrs := field.ErrorList{}
  36. allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))...)
  37. allErrs = append(allErrs, ValidateThirdPartyResource(update)...)
  38. return allErrs
  39. }
  40. func ValidateThirdPartyResourceName(name string, prefix bool) []string {
  41. // Make sure it's a valid DNS subdomain
  42. if msgs := apivalidation.NameIsDNSSubdomain(name, prefix); len(msgs) != 0 {
  43. return msgs
  44. }
  45. // Make sure it's at least three segments (kind + two-segment group name)
  46. if !prefix {
  47. parts := strings.Split(name, ".")
  48. if len(parts) < 3 {
  49. return []string{"must be at least three segments long: <kind>.<domain>.<tld>"}
  50. }
  51. }
  52. return nil
  53. }
  54. func ValidateThirdPartyResource(obj *extensions.ThirdPartyResource) field.ErrorList {
  55. allErrs := field.ErrorList{}
  56. allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&obj.ObjectMeta, false, ValidateThirdPartyResourceName, field.NewPath("metadata"))...)
  57. versions := sets.String{}
  58. for ix := range obj.Versions {
  59. version := &obj.Versions[ix]
  60. if len(version.Name) == 0 {
  61. allErrs = append(allErrs, field.Invalid(field.NewPath("versions").Index(ix).Child("name"), version, "must not be empty"))
  62. } else {
  63. for _, msg := range validation.IsDNS1123Label(version.Name) {
  64. allErrs = append(allErrs, field.Invalid(field.NewPath("versions").Index(ix).Child("name"), version, msg))
  65. }
  66. }
  67. if versions.Has(version.Name) {
  68. allErrs = append(allErrs, field.Duplicate(field.NewPath("versions").Index(ix).Child("name"), version))
  69. }
  70. versions.Insert(version.Name)
  71. }
  72. return allErrs
  73. }
  74. // ValidateDaemonSet tests if required fields in the DaemonSet are set.
  75. func ValidateDaemonSet(ds *extensions.DaemonSet) field.ErrorList {
  76. allErrs := apivalidation.ValidateObjectMeta(&ds.ObjectMeta, true, ValidateDaemonSetName, field.NewPath("metadata"))
  77. allErrs = append(allErrs, ValidateDaemonSetSpec(&ds.Spec, field.NewPath("spec"))...)
  78. return allErrs
  79. }
  80. // ValidateDaemonSetUpdate tests if required fields in the DaemonSet are set.
  81. func ValidateDaemonSetUpdate(ds, oldDS *extensions.DaemonSet) field.ErrorList {
  82. allErrs := apivalidation.ValidateObjectMetaUpdate(&ds.ObjectMeta, &oldDS.ObjectMeta, field.NewPath("metadata"))
  83. allErrs = append(allErrs, ValidateDaemonSetSpec(&ds.Spec, field.NewPath("spec"))...)
  84. return allErrs
  85. }
  86. // validateDaemonSetStatus validates a DaemonSetStatus
  87. func validateDaemonSetStatus(status *extensions.DaemonSetStatus, fldPath *field.Path) field.ErrorList {
  88. allErrs := field.ErrorList{}
  89. allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.CurrentNumberScheduled), fldPath.Child("currentNumberScheduled"))...)
  90. allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.NumberMisscheduled), fldPath.Child("numberMisscheduled"))...)
  91. allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.DesiredNumberScheduled), fldPath.Child("desiredNumberScheduled"))...)
  92. return allErrs
  93. }
  94. // ValidateDaemonSetStatus validates tests if required fields in the DaemonSet Status section
  95. func ValidateDaemonSetStatusUpdate(ds, oldDS *extensions.DaemonSet) field.ErrorList {
  96. allErrs := apivalidation.ValidateObjectMetaUpdate(&ds.ObjectMeta, &oldDS.ObjectMeta, field.NewPath("metadata"))
  97. allErrs = append(allErrs, validateDaemonSetStatus(&ds.Status, field.NewPath("status"))...)
  98. return allErrs
  99. }
  100. // ValidateDaemonSetSpec tests if required fields in the DaemonSetSpec are set.
  101. func ValidateDaemonSetSpec(spec *extensions.DaemonSetSpec, fldPath *field.Path) field.ErrorList {
  102. allErrs := field.ErrorList{}
  103. allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, fldPath.Child("selector"))...)
  104. selector, err := unversioned.LabelSelectorAsSelector(spec.Selector)
  105. if err == nil && !selector.Matches(labels.Set(spec.Template.Labels)) {
  106. allErrs = append(allErrs, field.Invalid(fldPath.Child("template", "metadata", "labels"), spec.Template.Labels, "`selector` does not match template `labels`"))
  107. }
  108. if spec.Selector != nil && len(spec.Selector.MatchLabels)+len(spec.Selector.MatchExpressions) == 0 {
  109. allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "empty selector is not valid for daemonset."))
  110. }
  111. allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(&spec.Template, fldPath.Child("template"))...)
  112. // Daemons typically run on more than one node, so mark Read-Write persistent disks as invalid.
  113. allErrs = append(allErrs, apivalidation.ValidateReadOnlyPersistentDisks(spec.Template.Spec.Volumes, fldPath.Child("template", "spec", "volumes"))...)
  114. // RestartPolicy has already been first-order validated as per ValidatePodTemplateSpec().
  115. if spec.Template.Spec.RestartPolicy != api.RestartPolicyAlways {
  116. allErrs = append(allErrs, field.NotSupported(fldPath.Child("template", "spec", "restartPolicy"), spec.Template.Spec.RestartPolicy, []string{string(api.RestartPolicyAlways)}))
  117. }
  118. return allErrs
  119. }
  120. // ValidateDaemonSetName can be used to check whether the given daemon set name is valid.
  121. // Prefix indicates this name will be used as part of generation, in which case
  122. // trailing dashes are allowed.
  123. var ValidateDaemonSetName = apivalidation.NameIsDNSSubdomain
  124. // Validates that the given name can be used as a deployment name.
  125. var ValidateDeploymentName = apivalidation.NameIsDNSSubdomain
  126. func ValidatePositiveIntOrPercent(intOrPercent intstr.IntOrString, fldPath *field.Path) field.ErrorList {
  127. allErrs := field.ErrorList{}
  128. switch intOrPercent.Type {
  129. case intstr.String:
  130. for _, msg := range validation.IsValidPercent(intOrPercent.StrVal) {
  131. allErrs = append(allErrs, field.Invalid(fldPath, intOrPercent, msg))
  132. }
  133. case intstr.Int:
  134. allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(intOrPercent.IntValue()), fldPath)...)
  135. default:
  136. allErrs = append(allErrs, field.Invalid(fldPath, intOrPercent, "must be an integer or percentage (e.g '5%%')"))
  137. }
  138. return allErrs
  139. }
  140. func getPercentValue(intOrStringValue intstr.IntOrString) (int, bool) {
  141. if intOrStringValue.Type != intstr.String {
  142. return 0, false
  143. }
  144. if len(validation.IsValidPercent(intOrStringValue.StrVal)) != 0 {
  145. return 0, false
  146. }
  147. value, _ := strconv.Atoi(intOrStringValue.StrVal[:len(intOrStringValue.StrVal)-1])
  148. return value, true
  149. }
  150. func getIntOrPercentValue(intOrStringValue intstr.IntOrString) int {
  151. value, isPercent := getPercentValue(intOrStringValue)
  152. if isPercent {
  153. return value
  154. }
  155. return intOrStringValue.IntValue()
  156. }
  157. func IsNotMoreThan100Percent(intOrStringValue intstr.IntOrString, fldPath *field.Path) field.ErrorList {
  158. allErrs := field.ErrorList{}
  159. value, isPercent := getPercentValue(intOrStringValue)
  160. if !isPercent || value <= 100 {
  161. return nil
  162. }
  163. allErrs = append(allErrs, field.Invalid(fldPath, intOrStringValue, "must not be greater than 100%"))
  164. return allErrs
  165. }
  166. func ValidateRollingUpdateDeployment(rollingUpdate *extensions.RollingUpdateDeployment, fldPath *field.Path) field.ErrorList {
  167. allErrs := field.ErrorList{}
  168. allErrs = append(allErrs, ValidatePositiveIntOrPercent(rollingUpdate.MaxUnavailable, fldPath.Child("maxUnavailable"))...)
  169. allErrs = append(allErrs, ValidatePositiveIntOrPercent(rollingUpdate.MaxSurge, fldPath.Child("maxSurge"))...)
  170. if getIntOrPercentValue(rollingUpdate.MaxUnavailable) == 0 && getIntOrPercentValue(rollingUpdate.MaxSurge) == 0 {
  171. // Both MaxSurge and MaxUnavailable cannot be zero.
  172. allErrs = append(allErrs, field.Invalid(fldPath.Child("maxUnavailable"), rollingUpdate.MaxUnavailable, "may not be 0 when `maxSurge` is 0"))
  173. }
  174. // Validate that MaxUnavailable is not more than 100%.
  175. allErrs = append(allErrs, IsNotMoreThan100Percent(rollingUpdate.MaxUnavailable, fldPath.Child("maxUnavailable"))...)
  176. return allErrs
  177. }
  178. func ValidateDeploymentStrategy(strategy *extensions.DeploymentStrategy, fldPath *field.Path) field.ErrorList {
  179. allErrs := field.ErrorList{}
  180. switch strategy.Type {
  181. case extensions.RecreateDeploymentStrategyType:
  182. if strategy.RollingUpdate != nil {
  183. allErrs = append(allErrs, field.Forbidden(fldPath.Child("rollingUpdate"), "may not be specified when strategy `type` is '"+string(extensions.RecreateDeploymentStrategyType+"'")))
  184. }
  185. case extensions.RollingUpdateDeploymentStrategyType:
  186. // This should never happen since it's set and checked in defaults.go
  187. if strategy.RollingUpdate == nil {
  188. allErrs = append(allErrs, field.Required(fldPath.Child("rollingUpdate"), "this should be defaulted and never be nil"))
  189. } else {
  190. allErrs = append(allErrs, ValidateRollingUpdateDeployment(strategy.RollingUpdate, fldPath.Child("rollingUpdate"))...)
  191. }
  192. default:
  193. validValues := []string{string(extensions.RecreateDeploymentStrategyType), string(extensions.RollingUpdateDeploymentStrategyType)}
  194. allErrs = append(allErrs, field.NotSupported(fldPath, strategy, validValues))
  195. }
  196. return allErrs
  197. }
  198. func ValidateRollback(rollback *extensions.RollbackConfig, fldPath *field.Path) field.ErrorList {
  199. allErrs := field.ErrorList{}
  200. v := rollback.Revision
  201. allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(v), fldPath.Child("version"))...)
  202. return allErrs
  203. }
  204. // Validates given deployment spec.
  205. func ValidateDeploymentSpec(spec *extensions.DeploymentSpec, fldPath *field.Path) field.ErrorList {
  206. allErrs := field.ErrorList{}
  207. allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...)
  208. if spec.Selector == nil {
  209. allErrs = append(allErrs, field.Required(fldPath.Child("selector"), ""))
  210. } else {
  211. allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, fldPath.Child("selector"))...)
  212. if len(spec.Selector.MatchLabels)+len(spec.Selector.MatchExpressions) == 0 {
  213. allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "empty selector is not valid for deployment."))
  214. }
  215. }
  216. selector, err := unversioned.LabelSelectorAsSelector(spec.Selector)
  217. if err != nil {
  218. allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "invalid label selector."))
  219. } else {
  220. allErrs = append(allErrs, ValidatePodTemplateSpecForReplicaSet(&spec.Template, selector, spec.Replicas, fldPath.Child("template"))...)
  221. }
  222. allErrs = append(allErrs, ValidateDeploymentStrategy(&spec.Strategy, fldPath.Child("strategy"))...)
  223. allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.MinReadySeconds), fldPath.Child("minReadySeconds"))...)
  224. if spec.RevisionHistoryLimit != nil {
  225. // zero is a valid RevisionHistoryLimit
  226. allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.RevisionHistoryLimit), fldPath.Child("revisionHistoryLimit"))...)
  227. }
  228. if spec.RollbackTo != nil {
  229. allErrs = append(allErrs, ValidateRollback(spec.RollbackTo, fldPath.Child("rollback"))...)
  230. }
  231. return allErrs
  232. }
  233. // Validates given deployment status.
  234. func ValidateDeploymentStatus(status *extensions.DeploymentStatus, fldPath *field.Path) field.ErrorList {
  235. allErrs := field.ErrorList{}
  236. allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(status.ObservedGeneration, fldPath.Child("observedGeneration"))...)
  237. allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.Replicas), fldPath.Child("replicas"))...)
  238. allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.UpdatedReplicas), fldPath.Child("updatedReplicas"))...)
  239. allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.AvailableReplicas), fldPath.Child("availableReplicas"))...)
  240. allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.UnavailableReplicas), fldPath.Child("unavailableReplicas"))...)
  241. return allErrs
  242. }
  243. func ValidateDeploymentUpdate(update, old *extensions.Deployment) field.ErrorList {
  244. allErrs := apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))
  245. allErrs = append(allErrs, ValidateDeploymentSpec(&update.Spec, field.NewPath("spec"))...)
  246. return allErrs
  247. }
  248. func ValidateDeploymentStatusUpdate(update, old *extensions.Deployment) field.ErrorList {
  249. allErrs := apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))
  250. allErrs = append(allErrs, ValidateDeploymentStatus(&update.Status, field.NewPath("status"))...)
  251. return allErrs
  252. }
  253. func ValidateDeployment(obj *extensions.Deployment) field.ErrorList {
  254. allErrs := apivalidation.ValidateObjectMeta(&obj.ObjectMeta, true, ValidateDeploymentName, field.NewPath("metadata"))
  255. allErrs = append(allErrs, ValidateDeploymentSpec(&obj.Spec, field.NewPath("spec"))...)
  256. return allErrs
  257. }
  258. func ValidateDeploymentRollback(obj *extensions.DeploymentRollback) field.ErrorList {
  259. allErrs := apivalidation.ValidateAnnotations(obj.UpdatedAnnotations, field.NewPath("updatedAnnotations"))
  260. if len(obj.Name) == 0 {
  261. allErrs = append(allErrs, field.Required(field.NewPath("name"), "name is required"))
  262. }
  263. allErrs = append(allErrs, ValidateRollback(&obj.RollbackTo, field.NewPath("rollback"))...)
  264. return allErrs
  265. }
  266. func ValidateThirdPartyResourceDataUpdate(update, old *extensions.ThirdPartyResourceData) field.ErrorList {
  267. return apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))
  268. }
  269. func ValidateThirdPartyResourceData(obj *extensions.ThirdPartyResourceData) field.ErrorList {
  270. return apivalidation.ValidateObjectMeta(&obj.ObjectMeta, true, apivalidation.NameIsDNSLabel, field.NewPath("metadata"))
  271. }
  272. // ValidateIngress tests if required fields in the Ingress are set.
  273. func ValidateIngress(ingress *extensions.Ingress) field.ErrorList {
  274. allErrs := apivalidation.ValidateObjectMeta(&ingress.ObjectMeta, true, ValidateIngressName, field.NewPath("metadata"))
  275. allErrs = append(allErrs, ValidateIngressSpec(&ingress.Spec, field.NewPath("spec"))...)
  276. return allErrs
  277. }
  278. // ValidateIngressName validates that the given name can be used as an Ingress name.
  279. var ValidateIngressName = apivalidation.NameIsDNSSubdomain
  280. func validateIngressTLS(spec *extensions.IngressSpec, fldPath *field.Path) field.ErrorList {
  281. allErrs := field.ErrorList{}
  282. // TODO: Perform a more thorough validation of spec.TLS.Hosts that takes
  283. // the wildcard spec from RFC 6125 into account.
  284. for _, itls := range spec.TLS {
  285. for i, host := range itls.Hosts {
  286. if strings.Contains(host, "*") {
  287. for _, msg := range validation.IsWildcardDNS1123Subdomain(host) {
  288. allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("hosts"), host, msg))
  289. }
  290. continue
  291. }
  292. for _, msg := range validation.IsDNS1123Subdomain(host) {
  293. allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("hosts"), host, msg))
  294. }
  295. }
  296. }
  297. return allErrs
  298. }
  299. // ValidateIngressSpec tests if required fields in the IngressSpec are set.
  300. func ValidateIngressSpec(spec *extensions.IngressSpec, fldPath *field.Path) field.ErrorList {
  301. allErrs := field.ErrorList{}
  302. // TODO: Is a default backend mandatory?
  303. if spec.Backend != nil {
  304. allErrs = append(allErrs, validateIngressBackend(spec.Backend, fldPath.Child("backend"))...)
  305. } else if len(spec.Rules) == 0 {
  306. allErrs = append(allErrs, field.Invalid(fldPath, spec.Rules, "either `backend` or `rules` must be specified"))
  307. }
  308. if len(spec.Rules) > 0 {
  309. allErrs = append(allErrs, validateIngressRules(spec.Rules, fldPath.Child("rules"))...)
  310. }
  311. if len(spec.TLS) > 0 {
  312. allErrs = append(allErrs, validateIngressTLS(spec, fldPath.Child("tls"))...)
  313. }
  314. return allErrs
  315. }
  316. // ValidateIngressUpdate tests if required fields in the Ingress are set.
  317. func ValidateIngressUpdate(ingress, oldIngress *extensions.Ingress) field.ErrorList {
  318. allErrs := apivalidation.ValidateObjectMetaUpdate(&ingress.ObjectMeta, &oldIngress.ObjectMeta, field.NewPath("metadata"))
  319. allErrs = append(allErrs, ValidateIngressSpec(&ingress.Spec, field.NewPath("spec"))...)
  320. return allErrs
  321. }
  322. // ValidateIngressStatusUpdate tests if required fields in the Ingress are set when updating status.
  323. func ValidateIngressStatusUpdate(ingress, oldIngress *extensions.Ingress) field.ErrorList {
  324. allErrs := apivalidation.ValidateObjectMetaUpdate(&ingress.ObjectMeta, &oldIngress.ObjectMeta, field.NewPath("metadata"))
  325. allErrs = append(allErrs, apivalidation.ValidateLoadBalancerStatus(&ingress.Status.LoadBalancer, field.NewPath("status", "loadBalancer"))...)
  326. return allErrs
  327. }
  328. func validateIngressRules(ingressRules []extensions.IngressRule, fldPath *field.Path) field.ErrorList {
  329. allErrs := field.ErrorList{}
  330. if len(ingressRules) == 0 {
  331. return append(allErrs, field.Required(fldPath, ""))
  332. }
  333. for i, ih := range ingressRules {
  334. if len(ih.Host) > 0 {
  335. if isIP := (net.ParseIP(ih.Host) != nil); isIP {
  336. allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("host"), ih.Host, "must be a DNS name, not an IP address"))
  337. }
  338. // TODO: Ports and ips are allowed in the host part of a url
  339. // according to RFC 3986, consider allowing them.
  340. if strings.Contains(ih.Host, "*") {
  341. for _, msg := range validation.IsWildcardDNS1123Subdomain(ih.Host) {
  342. allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("host"), ih.Host, msg))
  343. }
  344. continue
  345. }
  346. for _, msg := range validation.IsDNS1123Subdomain(ih.Host) {
  347. allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("host"), ih.Host, msg))
  348. }
  349. }
  350. allErrs = append(allErrs, validateIngressRuleValue(&ih.IngressRuleValue, fldPath.Index(0))...)
  351. }
  352. return allErrs
  353. }
  354. func validateIngressRuleValue(ingressRule *extensions.IngressRuleValue, fldPath *field.Path) field.ErrorList {
  355. allErrs := field.ErrorList{}
  356. if ingressRule.HTTP != nil {
  357. allErrs = append(allErrs, validateHTTPIngressRuleValue(ingressRule.HTTP, fldPath.Child("http"))...)
  358. }
  359. return allErrs
  360. }
  361. func validateHTTPIngressRuleValue(httpIngressRuleValue *extensions.HTTPIngressRuleValue, fldPath *field.Path) field.ErrorList {
  362. allErrs := field.ErrorList{}
  363. if len(httpIngressRuleValue.Paths) == 0 {
  364. allErrs = append(allErrs, field.Required(fldPath.Child("paths"), ""))
  365. }
  366. for i, rule := range httpIngressRuleValue.Paths {
  367. if len(rule.Path) > 0 {
  368. if !strings.HasPrefix(rule.Path, "/") {
  369. allErrs = append(allErrs, field.Invalid(fldPath.Child("paths").Index(i).Child("path"), rule.Path, "must be an absolute path"))
  370. }
  371. // TODO: More draconian path regex validation.
  372. // Path must be a valid regex. This is the basic requirement.
  373. // In addition to this any characters not allowed in a path per
  374. // RFC 3986 section-3.3 cannot appear as a literal in the regex.
  375. // Consider the example: http://host/valid?#bar, everything after
  376. // the last '/' is a valid regex that matches valid#bar, which
  377. // isn't a valid path, because the path terminates at the first ?
  378. // or #. A more sophisticated form of validation would detect that
  379. // the user is confusing url regexes with path regexes.
  380. _, err := regexp.CompilePOSIX(rule.Path)
  381. if err != nil {
  382. allErrs = append(allErrs, field.Invalid(fldPath.Child("paths").Index(i).Child("path"), rule.Path, "must be a valid regex"))
  383. }
  384. }
  385. allErrs = append(allErrs, validateIngressBackend(&rule.Backend, fldPath.Child("backend"))...)
  386. }
  387. return allErrs
  388. }
  389. // validateIngressBackend tests if a given backend is valid.
  390. func validateIngressBackend(backend *extensions.IngressBackend, fldPath *field.Path) field.ErrorList {
  391. allErrs := field.ErrorList{}
  392. // All backends must reference a single local service by name, and a single service port by name or number.
  393. if len(backend.ServiceName) == 0 {
  394. return append(allErrs, field.Required(fldPath.Child("serviceName"), ""))
  395. } else {
  396. for _, msg := range apivalidation.ValidateServiceName(backend.ServiceName, false) {
  397. allErrs = append(allErrs, field.Invalid(fldPath.Child("serviceName"), backend.ServiceName, msg))
  398. }
  399. }
  400. allErrs = append(allErrs, apivalidation.ValidatePortNumOrName(backend.ServicePort, fldPath.Child("servicePort"))...)
  401. return allErrs
  402. }
  403. func ValidateScale(scale *extensions.Scale) field.ErrorList {
  404. allErrs := field.ErrorList{}
  405. allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&scale.ObjectMeta, true, apivalidation.NameIsDNSSubdomain, field.NewPath("metadata"))...)
  406. if scale.Spec.Replicas < 0 {
  407. allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "replicas"), scale.Spec.Replicas, "must be greater than or equal to 0"))
  408. }
  409. return allErrs
  410. }
  411. // ValidateReplicaSetName can be used to check whether the given ReplicaSet
  412. // name is valid.
  413. // Prefix indicates this name will be used as part of generation, in which case
  414. // trailing dashes are allowed.
  415. var ValidateReplicaSetName = apivalidation.NameIsDNSSubdomain
  416. // ValidateReplicaSet tests if required fields in the ReplicaSet are set.
  417. func ValidateReplicaSet(rs *extensions.ReplicaSet) field.ErrorList {
  418. allErrs := apivalidation.ValidateObjectMeta(&rs.ObjectMeta, true, ValidateReplicaSetName, field.NewPath("metadata"))
  419. allErrs = append(allErrs, ValidateReplicaSetSpec(&rs.Spec, field.NewPath("spec"))...)
  420. return allErrs
  421. }
  422. // ValidateReplicaSetUpdate tests if required fields in the ReplicaSet are set.
  423. func ValidateReplicaSetUpdate(rs, oldRs *extensions.ReplicaSet) field.ErrorList {
  424. allErrs := field.ErrorList{}
  425. allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&rs.ObjectMeta, &oldRs.ObjectMeta, field.NewPath("metadata"))...)
  426. allErrs = append(allErrs, ValidateReplicaSetSpec(&rs.Spec, field.NewPath("spec"))...)
  427. return allErrs
  428. }
  429. // ValidateReplicaSetStatusUpdate tests if required fields in the ReplicaSet are set.
  430. func ValidateReplicaSetStatusUpdate(rs, oldRs *extensions.ReplicaSet) field.ErrorList {
  431. allErrs := field.ErrorList{}
  432. allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&rs.ObjectMeta, &oldRs.ObjectMeta, field.NewPath("metadata"))...)
  433. allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(rs.Status.Replicas), field.NewPath("status", "replicas"))...)
  434. allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(rs.Status.FullyLabeledReplicas), field.NewPath("status", "fullyLabeledReplicas"))...)
  435. allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(rs.Status.ObservedGeneration), field.NewPath("status", "observedGeneration"))...)
  436. return allErrs
  437. }
  438. // ValidateReplicaSetSpec tests if required fields in the ReplicaSet spec are set.
  439. func ValidateReplicaSetSpec(spec *extensions.ReplicaSetSpec, fldPath *field.Path) field.ErrorList {
  440. allErrs := field.ErrorList{}
  441. allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...)
  442. if spec.Selector == nil {
  443. allErrs = append(allErrs, field.Required(fldPath.Child("selector"), ""))
  444. } else {
  445. allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, fldPath.Child("selector"))...)
  446. if len(spec.Selector.MatchLabels)+len(spec.Selector.MatchExpressions) == 0 {
  447. allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "empty selector is not valid for deployment."))
  448. }
  449. }
  450. selector, err := unversioned.LabelSelectorAsSelector(spec.Selector)
  451. if err != nil {
  452. allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "invalid label selector."))
  453. } else {
  454. allErrs = append(allErrs, ValidatePodTemplateSpecForReplicaSet(&spec.Template, selector, spec.Replicas, fldPath.Child("template"))...)
  455. }
  456. return allErrs
  457. }
  458. // Validates the given template and ensures that it is in accordance with the desired selector and replicas.
  459. func ValidatePodTemplateSpecForReplicaSet(template *api.PodTemplateSpec, selector labels.Selector, replicas int32, fldPath *field.Path) field.ErrorList {
  460. allErrs := field.ErrorList{}
  461. if template == nil {
  462. allErrs = append(allErrs, field.Required(fldPath, ""))
  463. } else {
  464. if !selector.Empty() {
  465. // Verify that the ReplicaSet selector matches the labels in template.
  466. labels := labels.Set(template.Labels)
  467. if !selector.Matches(labels) {
  468. allErrs = append(allErrs, field.Invalid(fldPath.Child("metadata", "labels"), template.Labels, "`selector` does not match template `labels`"))
  469. }
  470. }
  471. allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(template, fldPath)...)
  472. if replicas > 1 {
  473. allErrs = append(allErrs, apivalidation.ValidateReadOnlyPersistentDisks(template.Spec.Volumes, fldPath.Child("spec", "volumes"))...)
  474. }
  475. // RestartPolicy has already been first-order validated as per ValidatePodTemplateSpec().
  476. if template.Spec.RestartPolicy != api.RestartPolicyAlways {
  477. allErrs = append(allErrs, field.NotSupported(fldPath.Child("spec", "restartPolicy"), template.Spec.RestartPolicy, []string{string(api.RestartPolicyAlways)}))
  478. }
  479. }
  480. return allErrs
  481. }
  482. // ValidatePodSecurityPolicyName can be used to check whether the given
  483. // pod security policy name is valid.
  484. // Prefix indicates this name will be used as part of generation, in which case
  485. // trailing dashes are allowed.
  486. var ValidatePodSecurityPolicyName = apivalidation.NameIsDNSSubdomain
  487. func ValidatePodSecurityPolicy(psp *extensions.PodSecurityPolicy) field.ErrorList {
  488. allErrs := field.ErrorList{}
  489. allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&psp.ObjectMeta, false, ValidatePodSecurityPolicyName, field.NewPath("metadata"))...)
  490. allErrs = append(allErrs, ValidatePodSecurityPolicySpecificAnnotations(psp.Annotations, field.NewPath("metadata").Child("annotations"))...)
  491. allErrs = append(allErrs, ValidatePodSecurityPolicySpec(&psp.Spec, field.NewPath("spec"))...)
  492. return allErrs
  493. }
  494. func ValidatePodSecurityPolicySpec(spec *extensions.PodSecurityPolicySpec, fldPath *field.Path) field.ErrorList {
  495. allErrs := field.ErrorList{}
  496. allErrs = append(allErrs, validatePSPRunAsUser(fldPath.Child("runAsUser"), &spec.RunAsUser)...)
  497. allErrs = append(allErrs, validatePSPSELinux(fldPath.Child("seLinux"), &spec.SELinux)...)
  498. allErrs = append(allErrs, validatePSPSupplementalGroup(fldPath.Child("supplementalGroups"), &spec.SupplementalGroups)...)
  499. allErrs = append(allErrs, validatePSPFSGroup(fldPath.Child("fsGroup"), &spec.FSGroup)...)
  500. allErrs = append(allErrs, validatePodSecurityPolicyVolumes(fldPath, spec.Volumes)...)
  501. allErrs = append(allErrs, validatePSPCapsAgainstDrops(spec.RequiredDropCapabilities, spec.DefaultAddCapabilities, field.NewPath("defaultAddCapabilities"))...)
  502. allErrs = append(allErrs, validatePSPCapsAgainstDrops(spec.RequiredDropCapabilities, spec.AllowedCapabilities, field.NewPath("allowedCapabilities"))...)
  503. return allErrs
  504. }
  505. func ValidatePodSecurityPolicySpecificAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
  506. allErrs := field.ErrorList{}
  507. if p := annotations[apparmor.DefaultProfileAnnotationKey]; p != "" {
  508. if err := apparmor.ValidateProfileFormat(p); err != nil {
  509. allErrs = append(allErrs, field.Invalid(fldPath.Key(apparmor.DefaultProfileAnnotationKey), p, err.Error()))
  510. }
  511. }
  512. if allowed := annotations[apparmor.AllowedProfilesAnnotationKey]; allowed != "" {
  513. for _, p := range strings.Split(allowed, ",") {
  514. if err := apparmor.ValidateProfileFormat(p); err != nil {
  515. allErrs = append(allErrs, field.Invalid(fldPath.Key(apparmor.AllowedProfilesAnnotationKey), allowed, err.Error()))
  516. }
  517. }
  518. }
  519. sysctlAnnotation := annotations[extensions.SysctlsPodSecurityPolicyAnnotationKey]
  520. sysctlFldPath := fldPath.Key(extensions.SysctlsPodSecurityPolicyAnnotationKey)
  521. sysctls, err := extensions.SysctlsFromPodSecurityPolicyAnnotation(sysctlAnnotation)
  522. if err != nil {
  523. allErrs = append(allErrs, field.Invalid(sysctlFldPath, sysctlAnnotation, err.Error()))
  524. } else {
  525. allErrs = append(allErrs, validatePodSecurityPolicySysctls(sysctlFldPath, sysctls)...)
  526. }
  527. return allErrs
  528. }
  529. // validatePSPSELinux validates the SELinux fields of PodSecurityPolicy.
  530. func validatePSPSELinux(fldPath *field.Path, seLinux *extensions.SELinuxStrategyOptions) field.ErrorList {
  531. allErrs := field.ErrorList{}
  532. // ensure the selinux strategy has a valid rule
  533. supportedSELinuxRules := sets.NewString(string(extensions.SELinuxStrategyMustRunAs),
  534. string(extensions.SELinuxStrategyRunAsAny))
  535. if !supportedSELinuxRules.Has(string(seLinux.Rule)) {
  536. allErrs = append(allErrs, field.NotSupported(fldPath.Child("rule"), seLinux.Rule, supportedSELinuxRules.List()))
  537. }
  538. return allErrs
  539. }
  540. // validatePSPRunAsUser validates the RunAsUser fields of PodSecurityPolicy.
  541. func validatePSPRunAsUser(fldPath *field.Path, runAsUser *extensions.RunAsUserStrategyOptions) field.ErrorList {
  542. allErrs := field.ErrorList{}
  543. // ensure the user strategy has a valid rule
  544. supportedRunAsUserRules := sets.NewString(string(extensions.RunAsUserStrategyMustRunAs),
  545. string(extensions.RunAsUserStrategyMustRunAsNonRoot),
  546. string(extensions.RunAsUserStrategyRunAsAny))
  547. if !supportedRunAsUserRules.Has(string(runAsUser.Rule)) {
  548. allErrs = append(allErrs, field.NotSupported(fldPath.Child("rule"), runAsUser.Rule, supportedRunAsUserRules.List()))
  549. }
  550. // validate range settings
  551. for idx, rng := range runAsUser.Ranges {
  552. allErrs = append(allErrs, validateIDRanges(fldPath.Child("ranges").Index(idx), rng)...)
  553. }
  554. return allErrs
  555. }
  556. // validatePSPFSGroup validates the FSGroupStrategyOptions fields of the PodSecurityPolicy.
  557. func validatePSPFSGroup(fldPath *field.Path, groupOptions *extensions.FSGroupStrategyOptions) field.ErrorList {
  558. allErrs := field.ErrorList{}
  559. supportedRules := sets.NewString(
  560. string(extensions.FSGroupStrategyMustRunAs),
  561. string(extensions.FSGroupStrategyRunAsAny),
  562. )
  563. if !supportedRules.Has(string(groupOptions.Rule)) {
  564. allErrs = append(allErrs, field.NotSupported(fldPath.Child("rule"), groupOptions.Rule, supportedRules.List()))
  565. }
  566. for idx, rng := range groupOptions.Ranges {
  567. allErrs = append(allErrs, validateIDRanges(fldPath.Child("ranges").Index(idx), rng)...)
  568. }
  569. return allErrs
  570. }
  571. // validatePSPSupplementalGroup validates the SupplementalGroupsStrategyOptions fields of the PodSecurityPolicy.
  572. func validatePSPSupplementalGroup(fldPath *field.Path, groupOptions *extensions.SupplementalGroupsStrategyOptions) field.ErrorList {
  573. allErrs := field.ErrorList{}
  574. supportedRules := sets.NewString(
  575. string(extensions.SupplementalGroupsStrategyRunAsAny),
  576. string(extensions.SupplementalGroupsStrategyMustRunAs),
  577. )
  578. if !supportedRules.Has(string(groupOptions.Rule)) {
  579. allErrs = append(allErrs, field.NotSupported(fldPath.Child("rule"), groupOptions.Rule, supportedRules.List()))
  580. }
  581. for idx, rng := range groupOptions.Ranges {
  582. allErrs = append(allErrs, validateIDRanges(fldPath.Child("ranges").Index(idx), rng)...)
  583. }
  584. return allErrs
  585. }
  586. // validatePodSecurityPolicyVolumes validates the volume fields of PodSecurityPolicy.
  587. func validatePodSecurityPolicyVolumes(fldPath *field.Path, volumes []extensions.FSType) field.ErrorList {
  588. allErrs := field.ErrorList{}
  589. allowed := psputil.GetAllFSTypesAsSet()
  590. // add in the * value since that is a pseudo type that is not included by default
  591. allowed.Insert(string(extensions.All))
  592. for _, v := range volumes {
  593. if !allowed.Has(string(v)) {
  594. allErrs = append(allErrs, field.NotSupported(fldPath.Child("volumes"), v, allowed.List()))
  595. }
  596. }
  597. return allErrs
  598. }
  599. const sysctlPatternSegmentFmt string = "([a-z0-9][-_a-z0-9]*)?[a-z0-9*]"
  600. const SysctlPatternFmt string = "(" + apivalidation.SysctlSegmentFmt + "\\.)*" + sysctlPatternSegmentFmt
  601. var sysctlPatternRegexp = regexp.MustCompile("^" + SysctlPatternFmt + "$")
  602. func IsValidSysctlPattern(name string) bool {
  603. if len(name) > apivalidation.SysctlMaxLength {
  604. return false
  605. }
  606. return sysctlPatternRegexp.MatchString(name)
  607. }
  608. // validatePodSecurityPolicySysctls validates the sysctls fields of PodSecurityPolicy.
  609. func validatePodSecurityPolicySysctls(fldPath *field.Path, sysctls []string) field.ErrorList {
  610. allErrs := field.ErrorList{}
  611. for i, s := range sysctls {
  612. if !IsValidSysctlPattern(string(s)) {
  613. allErrs = append(
  614. allErrs,
  615. field.Invalid(fldPath.Index(i), sysctls[i], fmt.Sprintf("must have at most %d characters and match regex %s",
  616. apivalidation.SysctlMaxLength,
  617. SysctlPatternFmt,
  618. )),
  619. )
  620. }
  621. }
  622. return allErrs
  623. }
  624. // validateIDRanges ensures the range is valid.
  625. func validateIDRanges(fldPath *field.Path, rng extensions.IDRange) field.ErrorList {
  626. allErrs := field.ErrorList{}
  627. // if 0 <= Min <= Max then we do not need to validate max. It is always greater than or
  628. // equal to 0 and Min.
  629. if rng.Min < 0 {
  630. allErrs = append(allErrs, field.Invalid(fldPath.Child("min"), rng.Min, "min cannot be negative"))
  631. }
  632. if rng.Max < 0 {
  633. allErrs = append(allErrs, field.Invalid(fldPath.Child("max"), rng.Max, "max cannot be negative"))
  634. }
  635. if rng.Min > rng.Max {
  636. allErrs = append(allErrs, field.Invalid(fldPath.Child("min"), rng.Min, "min cannot be greater than max"))
  637. }
  638. return allErrs
  639. }
  640. // validatePSPCapsAgainstDrops ensures an allowed cap is not listed in the required drops.
  641. func validatePSPCapsAgainstDrops(requiredDrops []api.Capability, capsToCheck []api.Capability, fldPath *field.Path) field.ErrorList {
  642. allErrs := field.ErrorList{}
  643. if requiredDrops == nil {
  644. return allErrs
  645. }
  646. for _, cap := range capsToCheck {
  647. if hasCap(cap, requiredDrops) {
  648. allErrs = append(allErrs, field.Invalid(fldPath, cap,
  649. fmt.Sprintf("capability is listed in %s and requiredDropCapabilities", fldPath.String())))
  650. }
  651. }
  652. return allErrs
  653. }
  654. // hasCap checks for needle in haystack.
  655. func hasCap(needle api.Capability, haystack []api.Capability) bool {
  656. for _, c := range haystack {
  657. if needle == c {
  658. return true
  659. }
  660. }
  661. return false
  662. }
  663. // ValidatePodSecurityPolicyUpdate validates a PSP for updates.
  664. func ValidatePodSecurityPolicyUpdate(old *extensions.PodSecurityPolicy, new *extensions.PodSecurityPolicy) field.ErrorList {
  665. allErrs := field.ErrorList{}
  666. allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&old.ObjectMeta, &new.ObjectMeta, field.NewPath("metadata"))...)
  667. allErrs = append(allErrs, ValidatePodSecurityPolicySpec(&new.Spec, field.NewPath("spec"))...)
  668. return allErrs
  669. }
  670. // ValidateNetworkPolicyName can be used to check whether the given networkpolicy
  671. // name is valid.
  672. func ValidateNetworkPolicyName(name string, prefix bool) []string {
  673. return apivalidation.NameIsDNSSubdomain(name, prefix)
  674. }
  675. // ValidateNetworkPolicySpec tests if required fields in the networkpolicy spec are set.
  676. func ValidateNetworkPolicySpec(spec *extensions.NetworkPolicySpec, fldPath *field.Path) field.ErrorList {
  677. allErrs := field.ErrorList{}
  678. allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(&spec.PodSelector, fldPath.Child("podSelector"))...)
  679. // Validate ingress rules.
  680. for _, i := range spec.Ingress {
  681. // TODO: Update From to be a pointer to slice as soon as auto-generation supports it.
  682. for _, f := range i.From {
  683. numFroms := 0
  684. if f.PodSelector != nil {
  685. numFroms++
  686. allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(f.PodSelector, fldPath.Child("podSelector"))...)
  687. }
  688. if f.NamespaceSelector != nil {
  689. if numFroms > 0 {
  690. allErrs = append(allErrs, field.Forbidden(fldPath, "may not specify more than 1 from type"))
  691. } else {
  692. numFroms++
  693. allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(f.NamespaceSelector, fldPath.Child("namespaces"))...)
  694. }
  695. }
  696. if numFroms == 0 {
  697. // At least one of PodSelector and NamespaceSelector must be defined.
  698. allErrs = append(allErrs, field.Required(fldPath, "must specify a from type"))
  699. }
  700. }
  701. }
  702. return allErrs
  703. }
  704. // ValidateNetworkPolicy validates a networkpolicy.
  705. func ValidateNetworkPolicy(np *extensions.NetworkPolicy) field.ErrorList {
  706. allErrs := apivalidation.ValidateObjectMeta(&np.ObjectMeta, true, ValidateNetworkPolicyName, field.NewPath("metadata"))
  707. allErrs = append(allErrs, ValidateNetworkPolicySpec(&np.Spec, field.NewPath("spec"))...)
  708. return allErrs
  709. }
  710. // ValidateNetworkPolicyUpdate tests if an update to a NetworkPolicy is valid.
  711. func ValidateNetworkPolicyUpdate(update, old *extensions.NetworkPolicy) field.ErrorList {
  712. allErrs := field.ErrorList{}
  713. allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))...)
  714. if !reflect.DeepEqual(update.Spec, old.Spec) {
  715. allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), "updates to networkpolicy spec are forbidden."))
  716. }
  717. return allErrs
  718. }
  719. // ValidateStorageClass validates a StorageClass.
  720. func ValidateStorageClass(storageClass *extensions.StorageClass) field.ErrorList {
  721. allErrs := apivalidation.ValidateObjectMeta(&storageClass.ObjectMeta, false, apivalidation.NameIsDNSSubdomain, field.NewPath("metadata"))
  722. allErrs = append(allErrs, validateProvisioner(storageClass.Provisioner, field.NewPath("provisioner"))...)
  723. allErrs = append(allErrs, validateParameters(storageClass.Parameters, field.NewPath("parameters"))...)
  724. return allErrs
  725. }
  726. // ValidateStorageClassUpdate tests if an update to StorageClass is valid.
  727. func ValidateStorageClassUpdate(storageClass, oldStorageClass *extensions.StorageClass) field.ErrorList {
  728. allErrs := apivalidation.ValidateObjectMetaUpdate(&storageClass.ObjectMeta, &oldStorageClass.ObjectMeta, field.NewPath("metadata"))
  729. if !reflect.DeepEqual(oldStorageClass.Parameters, storageClass.Parameters) {
  730. allErrs = append(allErrs, field.Forbidden(field.NewPath("parameters"), "updates to parameters are forbidden."))
  731. }
  732. if strings.Compare(storageClass.Provisioner, oldStorageClass.Provisioner) != 0 {
  733. allErrs = append(allErrs, field.Forbidden(field.NewPath("provisioner"), "updates to provisioner are forbidden."))
  734. }
  735. return allErrs
  736. }
  737. // validateProvisioner tests if provisioner is a valid qualified name.
  738. func validateProvisioner(provisioner string, fldPath *field.Path) field.ErrorList {
  739. allErrs := field.ErrorList{}
  740. if len(provisioner) == 0 {
  741. allErrs = append(allErrs, field.Required(fldPath, provisioner))
  742. }
  743. if len(provisioner) > 0 {
  744. for _, msg := range validation.IsQualifiedName(strings.ToLower(provisioner)) {
  745. allErrs = append(allErrs, field.Invalid(fldPath, provisioner, msg))
  746. }
  747. }
  748. return allErrs
  749. }
  750. const maxProvisionerParameterSize = 256 * (1 << 10) // 256 kB
  751. const maxProvisionerParameterLen = 512
  752. // validateParameters tests that keys are qualified names and that provisionerParameter are < 256kB.
  753. func validateParameters(params map[string]string, fldPath *field.Path) field.ErrorList {
  754. var totalSize int64
  755. allErrs := field.ErrorList{}
  756. if len(params) > maxProvisionerParameterLen {
  757. allErrs = append(allErrs, field.TooLong(fldPath, "Provisioner Parameters exceeded max allowed", maxProvisionerParameterLen))
  758. return allErrs
  759. }
  760. for k, v := range params {
  761. if len(k) < 1 {
  762. allErrs = append(allErrs, field.Invalid(fldPath, k, "field can not be empty."))
  763. }
  764. totalSize += (int64)(len(k)) + (int64)(len(v))
  765. }
  766. if totalSize > maxProvisionerParameterSize {
  767. allErrs = append(allErrs, field.TooLong(fldPath, "", maxProvisionerParameterSize))
  768. }
  769. return allErrs
  770. }