...
1
16
17 package validation
18
19 import (
20 "bytes"
21 "encoding/json"
22 "fmt"
23
24 ejson "github.com/exponent-io/jsonpath"
25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26 utilerrors "k8s.io/apimachinery/pkg/util/errors"
27 "k8s.io/cli-runtime/pkg/resource"
28 "k8s.io/klog/v2"
29 )
30
31
32 type Schema interface {
33 ValidateBytes(data []byte) error
34 }
35
36
37 type NullSchema struct{}
38
39
40 func (NullSchema) ValidateBytes(data []byte) error { return nil }
41
42
43 type NoDoubleKeySchema struct{}
44
45
46 func (NoDoubleKeySchema) ValidateBytes(data []byte) error {
47 var list []error
48 if err := validateNoDuplicateKeys(data, "metadata", "labels"); err != nil {
49 list = append(list, err)
50 }
51 if err := validateNoDuplicateKeys(data, "metadata", "annotations"); err != nil {
52 list = append(list, err)
53 }
54 return utilerrors.NewAggregate(list)
55 }
56
57 func validateNoDuplicateKeys(data []byte, path ...string) error {
58 r := ejson.NewDecoder(bytes.NewReader(data))
59
60
61
62 ifacePath := []interface{}{}
63 for ix := range path {
64 ifacePath = append(ifacePath, path[ix])
65 }
66 found, err := r.SeekTo(ifacePath...)
67 if err != nil {
68 return err
69 }
70 if !found {
71 return nil
72 }
73 seen := map[string]bool{}
74 for {
75 tok, err := r.Token()
76 if err != nil {
77 return err
78 }
79 switch t := tok.(type) {
80 case json.Delim:
81 if t.String() == "}" {
82 return nil
83 }
84 case ejson.KeyString:
85 if seen[string(t)] {
86 return fmt.Errorf("duplicate key: %s", string(t))
87 }
88 seen[string(t)] = true
89 }
90 }
91 }
92
93
94 type ConjunctiveSchema []Schema
95
96
97 func (c ConjunctiveSchema) ValidateBytes(data []byte) error {
98 var list []error
99 schemas := []Schema(c)
100 for ix := range schemas {
101 if err := schemas[ix].ValidateBytes(data); err != nil {
102 list = append(list, err)
103 }
104 }
105 return utilerrors.NewAggregate(list)
106 }
107
108 func NewParamVerifyingSchema(s Schema, verifier resource.Verifier, directive string) Schema {
109 return ¶mVerifyingSchema{
110 schema: s,
111 verifier: verifier,
112 directive: directive,
113 }
114 }
115
116
117
118
119
120
121 type paramVerifyingSchema struct {
122 schema Schema
123 verifier resource.Verifier
124 directive string
125 }
126
127
128 func (c *paramVerifyingSchema) ValidateBytes(data []byte) error {
129 obj, err := parse(data)
130 if err != nil {
131 return err
132 }
133
134 gvk, errs := getObjectKind(obj)
135 if errs != nil {
136 return utilerrors.NewAggregate(errs)
137 }
138
139 err = c.verifier.HasSupport(gvk)
140 if resource.IsParamUnsupportedError(err) {
141 switch c.directive {
142 case metav1.FieldValidationStrict:
143 return c.schema.ValidateBytes(data)
144 case metav1.FieldValidationWarn:
145 klog.Warningf("cannot perform warn validation if server-side field validation is unsupported, skipping validation")
146 default:
147
148 klog.Warningf("unexpected field validation directive: %s, skipping validation", c.directive)
149 }
150 return nil
151 }
152 return err
153 }
154
View as plain text