validation.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. /*
  2. Copyright 2017 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. "errors"
  16. "k8s.io/apimachinery/pkg/runtime/schema"
  17. utilerrors "k8s.io/apimachinery/pkg/util/errors"
  18. "k8s.io/apimachinery/pkg/util/json"
  19. "k8s.io/apimachinery/pkg/util/yaml"
  20. "k8s.io/kube-openapi/pkg/util/proto/validation"
  21. "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
  22. )
  23. // SchemaValidation validates the object against an OpenAPI schema.
  24. type SchemaValidation struct {
  25. resources openapi.Resources
  26. }
  27. // NewSchemaValidation creates a new SchemaValidation that can be used
  28. // to validate objects.
  29. func NewSchemaValidation(resources openapi.Resources) *SchemaValidation {
  30. return &SchemaValidation{
  31. resources: resources,
  32. }
  33. }
  34. // ValidateBytes will validates the object against using the Resources
  35. // object.
  36. func (v *SchemaValidation) ValidateBytes(data []byte) error {
  37. obj, err := parse(data)
  38. if err != nil {
  39. return err
  40. }
  41. gvk, errs := getObjectKind(obj)
  42. if errs != nil {
  43. return utilerrors.NewAggregate(errs)
  44. }
  45. if (gvk == schema.GroupVersionKind{Version: "v1", Kind: "List"}) {
  46. return utilerrors.NewAggregate(v.validateList(obj))
  47. }
  48. return utilerrors.NewAggregate(v.validateResource(obj, gvk))
  49. }
  50. func (v *SchemaValidation) validateList(object interface{}) []error {
  51. fields, ok := object.(map[string]interface{})
  52. if !ok || fields == nil {
  53. return []error{errors.New("invalid object to validate")}
  54. }
  55. allErrors := []error{}
  56. if _, ok := fields["items"].([]interface{}); !ok {
  57. return []error{errors.New("invalid object to validate")}
  58. }
  59. for _, item := range fields["items"].([]interface{}) {
  60. if gvk, errs := getObjectKind(item); errs != nil {
  61. allErrors = append(allErrors, errs...)
  62. } else {
  63. allErrors = append(allErrors, v.validateResource(item, gvk)...)
  64. }
  65. }
  66. return allErrors
  67. }
  68. func (v *SchemaValidation) validateResource(obj interface{}, gvk schema.GroupVersionKind) []error {
  69. resource := v.resources.LookupResource(gvk)
  70. if resource == nil {
  71. // resource is not present, let's just skip validation.
  72. return nil
  73. }
  74. return validation.ValidateModel(obj, resource, gvk.Kind)
  75. }
  76. func parse(data []byte) (interface{}, error) {
  77. var obj interface{}
  78. out, err := yaml.ToJSON(data)
  79. if err != nil {
  80. return nil, err
  81. }
  82. if err := json.Unmarshal(out, &obj); err != nil {
  83. return nil, err
  84. }
  85. return obj, nil
  86. }
  87. func getObjectKind(object interface{}) (schema.GroupVersionKind, []error) {
  88. var listErrors []error
  89. fields, ok := object.(map[string]interface{})
  90. if !ok || fields == nil {
  91. listErrors = append(listErrors, errors.New("invalid object to validate"))
  92. return schema.GroupVersionKind{}, listErrors
  93. }
  94. var group string
  95. var version string
  96. apiVersion := fields["apiVersion"]
  97. if apiVersion == nil {
  98. listErrors = append(listErrors, errors.New("apiVersion not set"))
  99. } else if _, ok := apiVersion.(string); !ok {
  100. listErrors = append(listErrors, errors.New("apiVersion isn't string type"))
  101. } else {
  102. gv, err := schema.ParseGroupVersion(apiVersion.(string))
  103. if err != nil {
  104. listErrors = append(listErrors, err)
  105. } else {
  106. group = gv.Group
  107. version = gv.Version
  108. }
  109. }
  110. kind := fields["kind"]
  111. if kind == nil {
  112. listErrors = append(listErrors, errors.New("kind not set"))
  113. } else if _, ok := kind.(string); !ok {
  114. listErrors = append(listErrors, errors.New("kind isn't string type"))
  115. }
  116. if listErrors != nil {
  117. return schema.GroupVersionKind{}, listErrors
  118. }
  119. return schema.GroupVersionKind{Group: group, Version: version, Kind: kind.(string)}, nil
  120. }