1
16
17 package objectmeta
18
19 import (
20 "strings"
21
22 structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
23 metavalidation "k8s.io/apimachinery/pkg/api/validation"
24 "k8s.io/apimachinery/pkg/api/validation/path"
25 "k8s.io/apimachinery/pkg/runtime/schema"
26 utilvalidation "k8s.io/apimachinery/pkg/util/validation"
27 "k8s.io/apimachinery/pkg/util/validation/field"
28 )
29
30
31
32 func Validate(pth *field.Path, obj interface{}, s *structuralschema.Structural, isResourceRoot bool) field.ErrorList {
33 if isResourceRoot {
34 if s == nil {
35 s = &structuralschema.Structural{}
36 }
37 if !s.XEmbeddedResource {
38 clone := *s
39 clone.XEmbeddedResource = true
40 s = &clone
41 }
42 }
43 return validate(pth, obj, s)
44 }
45
46 func validate(pth *field.Path, x interface{}, s *structuralschema.Structural) field.ErrorList {
47 if s == nil {
48 return nil
49 }
50
51 var allErrs field.ErrorList
52
53 switch x := x.(type) {
54 case map[string]interface{}:
55 if s.XEmbeddedResource {
56 allErrs = append(allErrs, validateEmbeddedResource(pth, x, s)...)
57 }
58
59 for k, v := range x {
60 prop, ok := s.Properties[k]
61 if ok {
62 allErrs = append(allErrs, validate(pth.Child(k), v, &prop)...)
63 } else if s.AdditionalProperties != nil {
64 allErrs = append(allErrs, validate(pth.Key(k), v, s.AdditionalProperties.Structural)...)
65 }
66 }
67 case []interface{}:
68 for i, v := range x {
69 allErrs = append(allErrs, validate(pth.Index(i), v, s.Items)...)
70 }
71 default:
72
73 }
74
75 return allErrs
76 }
77
78 func validateEmbeddedResource(pth *field.Path, x map[string]interface{}, s *structuralschema.Structural) field.ErrorList {
79 var allErrs field.ErrorList
80
81
82 if _, found := x["apiVersion"]; !found {
83 allErrs = append(allErrs, field.Required(pth.Child("apiVersion"), "must not be empty"))
84 }
85 if _, found := x["kind"]; !found {
86 allErrs = append(allErrs, field.Required(pth.Child("kind"), "must not be empty"))
87 }
88
89 for k, v := range x {
90 switch k {
91 case "apiVersion":
92 if apiVersion, ok := v.(string); !ok {
93 allErrs = append(allErrs, field.Invalid(pth.Child("apiVersion"), v, "must be a string"))
94 } else if len(apiVersion) == 0 {
95 allErrs = append(allErrs, field.Invalid(pth.Child("apiVersion"), apiVersion, "must not be empty"))
96 } else if _, err := schema.ParseGroupVersion(apiVersion); err != nil {
97 allErrs = append(allErrs, field.Invalid(pth.Child("apiVersion"), apiVersion, err.Error()))
98 }
99 case "kind":
100 if kind, ok := v.(string); !ok {
101 allErrs = append(allErrs, field.Invalid(pth.Child("kind"), v, "must be a string"))
102 } else if len(kind) == 0 {
103 allErrs = append(allErrs, field.Invalid(pth.Child("kind"), kind, "must not be empty"))
104 } else if errs := utilvalidation.IsDNS1035Label(strings.ToLower(kind)); len(errs) > 0 {
105 allErrs = append(allErrs, field.Invalid(pth.Child("kind"), kind, "may have mixed case, but should otherwise match: "+strings.Join(errs, ",")))
106 }
107 case "metadata":
108 meta, _, err := GetObjectMeta(x, false)
109 if err != nil {
110 allErrs = append(allErrs, field.Invalid(pth.Child("metadata"), v, err.Error()))
111 } else {
112 if len(meta.Name) == 0 {
113 meta.Name = "fakename"
114 }
115 allErrs = append(allErrs, metavalidation.ValidateObjectMeta(meta, len(meta.Namespace) > 0, path.ValidatePathSegmentName, pth.Child("metadata"))...)
116 }
117 }
118 }
119
120 return allErrs
121 }
122
View as plain text