...
1
16
17 package resource
18
19 import (
20 "errors"
21 "fmt"
22
23 openapi_v2 "github.com/google/gnostic-models/openapiv2"
24 yaml "gopkg.in/yaml.v2"
25
26 "k8s.io/apimachinery/pkg/runtime/schema"
27 "k8s.io/client-go/discovery"
28 "k8s.io/client-go/dynamic"
29 )
30
31 func NewQueryParamVerifier(dynamicClient dynamic.Interface, openAPIGetter discovery.OpenAPISchemaInterface, queryParam VerifiableQueryParam) *QueryParamVerifier {
32 return &QueryParamVerifier{
33 finder: NewCRDFinder(CRDFromDynamic(dynamicClient)),
34 openAPIGetter: openAPIGetter,
35 queryParam: queryParam,
36 }
37 }
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 type QueryParamVerifier struct {
55 finder CRDFinder
56 openAPIGetter discovery.OpenAPISchemaInterface
57 queryParam VerifiableQueryParam
58 }
59
60
61 type Verifier interface {
62 HasSupport(gvk schema.GroupVersionKind) error
63 }
64
65
66
67
68 type VerifiableQueryParam string
69
70 const (
71 QueryParamFieldValidation VerifiableQueryParam = "fieldValidation"
72 )
73
74
75 func (v *QueryParamVerifier) HasSupport(gvk schema.GroupVersionKind) error {
76 if (gvk == schema.GroupVersionKind{Version: "v1", Kind: "List"}) {
77 return NewParamUnsupportedError(gvk, v.queryParam)
78 }
79
80 oapi, err := v.openAPIGetter.OpenAPISchema()
81 if err != nil {
82 return fmt.Errorf("failed to download openapi: %v", err)
83 }
84 supports, err := supportsQueryParam(oapi, gvk, v.queryParam)
85 if err != nil {
86
87 supports, _ = supportsQueryParam(oapi, schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Namespace"}, v.queryParam)
88
89 if supports {
90 supports, err = v.finder.HasCRD(gvk.GroupKind())
91 if err != nil {
92 return fmt.Errorf("failed to check CRD: %v", err)
93 }
94 }
95 }
96 if !supports {
97 return NewParamUnsupportedError(gvk, v.queryParam)
98 }
99 return nil
100 }
101
102 type paramUnsupportedError struct {
103 gvk schema.GroupVersionKind
104 param VerifiableQueryParam
105 }
106
107 func NewParamUnsupportedError(gvk schema.GroupVersionKind, param VerifiableQueryParam) error {
108 return ¶mUnsupportedError{
109 gvk: gvk,
110 param: param,
111 }
112 }
113
114 func (e *paramUnsupportedError) Error() string {
115 return fmt.Sprintf("%v doesn't support %s", e.gvk, e.param)
116 }
117
118 func IsParamUnsupportedError(err error) bool {
119 if err == nil {
120 return false
121 }
122 _, ok := err.(*paramUnsupportedError)
123 return ok
124 }
125
126 func hasGVKExtension(extensions []*openapi_v2.NamedAny, gvk schema.GroupVersionKind) bool {
127 for _, extension := range extensions {
128 if extension.GetValue().GetYaml() == "" ||
129 extension.GetName() != "x-kubernetes-group-version-kind" {
130 continue
131 }
132 var value map[string]string
133 err := yaml.Unmarshal([]byte(extension.GetValue().GetYaml()), &value)
134 if err != nil {
135 continue
136 }
137
138 if value["group"] == gvk.Group && value["kind"] == gvk.Kind && value["version"] == gvk.Version {
139 return true
140 }
141 return false
142 }
143 return false
144 }
145
146
147
148
149 func supportsQueryParam(doc *openapi_v2.Document, gvk schema.GroupVersionKind, queryParam VerifiableQueryParam) (bool, error) {
150 globalParams := map[string]*openapi_v2.NamedParameter{}
151 for _, p := range doc.GetParameters().GetAdditionalProperties() {
152 globalParams["#/parameters/"+p.GetName()] = p
153 }
154
155 for _, path := range doc.GetPaths().GetPath() {
156
157 if !hasGVKExtension(path.GetValue().GetPatch().GetVendorExtension(), gvk) {
158 continue
159 }
160 for _, param := range path.GetValue().GetPatch().GetParameters() {
161 if param.GetParameter().GetNonBodyParameter().GetQueryParameterSubSchema().GetName() == string(queryParam) {
162 return true, nil
163 }
164
165
166 if ref := param.GetJsonReference().GetXRef(); ref != "" {
167 if globalParam, ok := globalParams[ref]; ok && globalParam != nil && globalParam.GetValue().GetNonBodyParameter().GetQueryParameterSubSchema().GetName() == string(queryParam) {
168 return true, nil
169 }
170 }
171 }
172 return false, nil
173 }
174
175 return false, errors.New("couldn't find GVK in openapi")
176 }
177
View as plain text