1
16
17 package objectmeta
18
19 import (
20 "sort"
21
22 structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
23 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
24 "k8s.io/apimachinery/pkg/util/validation/field"
25 )
26
27
28
29
30 type CoerceOptions struct {
31
32
33
34
35
36 DropInvalidFields bool
37
38
39 ReturnUnknownFieldPaths bool
40 }
41
42
43
44
45
46
47 func CoerceWithOptions(pth *field.Path, obj interface{}, s *structuralschema.Structural, isResourceRoot bool, opts CoerceOptions) (*field.Error, []string) {
48 if isResourceRoot {
49 if s == nil {
50 s = &structuralschema.Structural{}
51 }
52 if !s.XEmbeddedResource {
53 clone := *s
54 clone.XEmbeddedResource = true
55 s = &clone
56 }
57 }
58 c := coercer{DropInvalidFields: opts.DropInvalidFields, ReturnUnknownFieldPaths: opts.ReturnUnknownFieldPaths}
59 schemaOpts := &structuralschema.UnknownFieldPathOptions{
60 TrackUnknownFieldPaths: opts.ReturnUnknownFieldPaths,
61 }
62 fieldErr := c.coerce(pth, obj, s, schemaOpts)
63 sort.Strings(schemaOpts.UnknownFieldPaths)
64 return fieldErr, schemaOpts.UnknownFieldPaths
65 }
66
67
68 func Coerce(pth *field.Path, obj interface{}, s *structuralschema.Structural, isResourceRoot, dropInvalidFields bool) *field.Error {
69 fieldErr, _ := CoerceWithOptions(pth, obj, s, isResourceRoot, CoerceOptions{DropInvalidFields: dropInvalidFields})
70 return fieldErr
71 }
72
73 type coercer struct {
74 DropInvalidFields bool
75 ReturnUnknownFieldPaths bool
76 }
77
78 func (c *coercer) coerce(pth *field.Path, x interface{}, s *structuralschema.Structural, opts *structuralschema.UnknownFieldPathOptions) *field.Error {
79 if s == nil {
80 return nil
81 }
82 origPathLen := len(opts.ParentPath)
83 defer func() {
84 opts.ParentPath = opts.ParentPath[:origPathLen]
85 }()
86 switch x := x.(type) {
87 case map[string]interface{}:
88 for k, v := range x {
89 if s.XEmbeddedResource {
90 switch k {
91 case "apiVersion", "kind":
92 if _, ok := v.(string); !ok && c.DropInvalidFields {
93 delete(x, k)
94 } else if !ok {
95 return field.Invalid(pth.Child(k), v, "must be a string")
96 }
97 case "metadata":
98 meta, found, unknownFields, err := GetObjectMetaWithOptions(x, ObjectMetaOptions{
99 DropMalformedFields: c.DropInvalidFields,
100 ReturnUnknownFieldPaths: c.ReturnUnknownFieldPaths,
101 ParentPath: pth,
102 })
103 opts.UnknownFieldPaths = append(opts.UnknownFieldPaths, unknownFields...)
104 if err != nil {
105 if !c.DropInvalidFields {
106 return field.Invalid(pth.Child("metadata"), v, err.Error())
107 }
108
109 } else if found {
110 if err := SetObjectMeta(x, meta); err != nil {
111 return field.Invalid(pth.Child("metadata"), v, err.Error())
112 }
113 if meta.CreationTimestamp.IsZero() {
114 unstructured.RemoveNestedField(x, "metadata", "creationTimestamp")
115 }
116 }
117 }
118 }
119 prop, ok := s.Properties[k]
120 if ok {
121 opts.AppendKey(k)
122 if err := c.coerce(pth.Child(k), v, &prop, opts); err != nil {
123 return err
124 }
125 opts.ParentPath = opts.ParentPath[:origPathLen]
126 } else if s.AdditionalProperties != nil {
127 opts.AppendKey(k)
128 if err := c.coerce(pth.Key(k), v, s.AdditionalProperties.Structural, opts); err != nil {
129 return err
130 }
131 opts.ParentPath = opts.ParentPath[:origPathLen]
132 }
133 }
134 case []interface{}:
135 for i, v := range x {
136 opts.AppendIndex(i)
137 if err := c.coerce(pth.Index(i), v, s.Items, opts); err != nil {
138 return err
139 }
140 opts.ParentPath = opts.ParentPath[:origPathLen]
141 }
142 default:
143
144 }
145
146 return nil
147 }
148
View as plain text