1
2
3
4 package openapi
5
6 import (
7 "encoding/json"
8 "fmt"
9 "os"
10 "path/filepath"
11 "reflect"
12 "strings"
13 "sync"
14
15 openapi_v2 "github.com/google/gnostic-models/openapiv2"
16 "google.golang.org/protobuf/proto"
17 "k8s.io/kube-openapi/pkg/validation/spec"
18 "sigs.k8s.io/kustomize/kyaml/errors"
19 "sigs.k8s.io/kustomize/kyaml/openapi/kubernetesapi"
20 "sigs.k8s.io/kustomize/kyaml/openapi/kustomizationapi"
21 "sigs.k8s.io/kustomize/kyaml/yaml"
22 k8syaml "sigs.k8s.io/yaml"
23 )
24
25 var (
26
27
28
29
30
31
32
33
34
35 schemaLock sync.RWMutex
36
37
38 kubernetesOpenAPIVersion string
39
40
41 globalSchema openapiData
42
43
44 customSchema []byte
45 )
46
47
48
49 type schemaParseStatus uint32
50
51 const (
52 schemaNotParsed schemaParseStatus = iota
53 schemaParseDelayed
54 schemaParsed
55 )
56
57
58
59 type openapiData struct {
60
61 schema spec.Schema
62
63
64 schemaByResourceType map[yaml.TypeMeta]*spec.Schema
65
66
67
68 namespaceabilityByResourceType map[yaml.TypeMeta]bool
69
70
71
72 noUseBuiltInSchema bool
73
74
75
76 schemaInit bool
77
78
79
80 defaultBuiltInSchemaParseStatus schemaParseStatus
81 }
82
83 type format string
84
85 const (
86 JsonOrYaml format = "jsonOrYaml"
87 Proto format = "proto"
88 )
89
90
91
92
93
94 var precomputedIsNamespaceScoped = map[yaml.TypeMeta]bool{
95 {APIVersion: "admissionregistration.k8s.io/v1", Kind: "MutatingWebhookConfiguration"}: false,
96 {APIVersion: "admissionregistration.k8s.io/v1", Kind: "ValidatingWebhookConfiguration"}: false,
97 {APIVersion: "admissionregistration.k8s.io/v1beta1", Kind: "MutatingWebhookConfiguration"}: false,
98 {APIVersion: "admissionregistration.k8s.io/v1beta1", Kind: "ValidatingWebhookConfiguration"}: false,
99 {APIVersion: "apiextensions.k8s.io/v1", Kind: "CustomResourceDefinition"}: false,
100 {APIVersion: "apiextensions.k8s.io/v1beta1", Kind: "CustomResourceDefinition"}: false,
101 {APIVersion: "apiregistration.k8s.io/v1", Kind: "APIService"}: false,
102 {APIVersion: "apiregistration.k8s.io/v1beta1", Kind: "APIService"}: false,
103 {APIVersion: "apps/v1", Kind: "ControllerRevision"}: true,
104 {APIVersion: "apps/v1", Kind: "DaemonSet"}: true,
105 {APIVersion: "apps/v1", Kind: "Deployment"}: true,
106 {APIVersion: "apps/v1", Kind: "ReplicaSet"}: true,
107 {APIVersion: "apps/v1", Kind: "StatefulSet"}: true,
108 {APIVersion: "autoscaling/v1", Kind: "HorizontalPodAutoscaler"}: true,
109 {APIVersion: "autoscaling/v1", Kind: "Scale"}: true,
110 {APIVersion: "autoscaling/v2beta1", Kind: "HorizontalPodAutoscaler"}: true,
111 {APIVersion: "autoscaling/v2beta2", Kind: "HorizontalPodAutoscaler"}: true,
112 {APIVersion: "batch/v1", Kind: "CronJob"}: true,
113 {APIVersion: "batch/v1", Kind: "Job"}: true,
114 {APIVersion: "batch/v1beta1", Kind: "CronJob"}: true,
115 {APIVersion: "certificates.k8s.io/v1", Kind: "CertificateSigningRequest"}: false,
116 {APIVersion: "certificates.k8s.io/v1beta1", Kind: "CertificateSigningRequest"}: false,
117 {APIVersion: "coordination.k8s.io/v1", Kind: "Lease"}: true,
118 {APIVersion: "coordination.k8s.io/v1beta1", Kind: "Lease"}: true,
119 {APIVersion: "discovery.k8s.io/v1", Kind: "EndpointSlice"}: true,
120 {APIVersion: "discovery.k8s.io/v1beta1", Kind: "EndpointSlice"}: true,
121 {APIVersion: "events.k8s.io/v1", Kind: "Event"}: true,
122 {APIVersion: "events.k8s.io/v1beta1", Kind: "Event"}: true,
123 {APIVersion: "extensions/v1beta1", Kind: "Ingress"}: true,
124 {APIVersion: "flowcontrol.apiserver.k8s.io/v1beta1", Kind: "FlowSchema"}: false,
125 {APIVersion: "flowcontrol.apiserver.k8s.io/v1beta1", Kind: "PriorityLevelConfiguration"}: false,
126 {APIVersion: "networking.k8s.io/v1", Kind: "Ingress"}: true,
127 {APIVersion: "networking.k8s.io/v1", Kind: "IngressClass"}: false,
128 {APIVersion: "networking.k8s.io/v1", Kind: "NetworkPolicy"}: true,
129 {APIVersion: "networking.k8s.io/v1beta1", Kind: "Ingress"}: true,
130 {APIVersion: "networking.k8s.io/v1beta1", Kind: "IngressClass"}: false,
131 {APIVersion: "node.k8s.io/v1", Kind: "RuntimeClass"}: false,
132 {APIVersion: "node.k8s.io/v1beta1", Kind: "RuntimeClass"}: false,
133 {APIVersion: "policy/v1", Kind: "PodDisruptionBudget"}: true,
134 {APIVersion: "policy/v1beta1", Kind: "PodDisruptionBudget"}: true,
135 {APIVersion: "policy/v1beta1", Kind: "PodSecurityPolicy"}: false,
136 {APIVersion: "rbac.authorization.k8s.io/v1", Kind: "ClusterRole"}: false,
137 {APIVersion: "rbac.authorization.k8s.io/v1", Kind: "ClusterRoleBinding"}: false,
138 {APIVersion: "rbac.authorization.k8s.io/v1", Kind: "Role"}: true,
139 {APIVersion: "rbac.authorization.k8s.io/v1", Kind: "RoleBinding"}: true,
140 {APIVersion: "rbac.authorization.k8s.io/v1beta1", Kind: "ClusterRole"}: false,
141 {APIVersion: "rbac.authorization.k8s.io/v1beta1", Kind: "ClusterRoleBinding"}: false,
142 {APIVersion: "rbac.authorization.k8s.io/v1beta1", Kind: "Role"}: true,
143 {APIVersion: "rbac.authorization.k8s.io/v1beta1", Kind: "RoleBinding"}: true,
144 {APIVersion: "scheduling.k8s.io/v1", Kind: "PriorityClass"}: false,
145 {APIVersion: "scheduling.k8s.io/v1beta1", Kind: "PriorityClass"}: false,
146 {APIVersion: "storage.k8s.io/v1", Kind: "CSIDriver"}: false,
147 {APIVersion: "storage.k8s.io/v1", Kind: "CSINode"}: false,
148 {APIVersion: "storage.k8s.io/v1", Kind: "StorageClass"}: false,
149 {APIVersion: "storage.k8s.io/v1", Kind: "VolumeAttachment"}: false,
150 {APIVersion: "storage.k8s.io/v1beta1", Kind: "CSIDriver"}: false,
151 {APIVersion: "storage.k8s.io/v1beta1", Kind: "CSINode"}: false,
152 {APIVersion: "storage.k8s.io/v1beta1", Kind: "CSIStorageCapacity"}: true,
153 {APIVersion: "storage.k8s.io/v1beta1", Kind: "StorageClass"}: false,
154 {APIVersion: "storage.k8s.io/v1beta1", Kind: "VolumeAttachment"}: false,
155 {APIVersion: "v1", Kind: "ComponentStatus"}: false,
156 {APIVersion: "v1", Kind: "ConfigMap"}: true,
157 {APIVersion: "v1", Kind: "Endpoints"}: true,
158 {APIVersion: "v1", Kind: "Event"}: true,
159 {APIVersion: "v1", Kind: "LimitRange"}: true,
160 {APIVersion: "v1", Kind: "Namespace"}: false,
161 {APIVersion: "v1", Kind: "Node"}: false,
162 {APIVersion: "v1", Kind: "NodeProxyOptions"}: false,
163 {APIVersion: "v1", Kind: "PersistentVolume"}: false,
164 {APIVersion: "v1", Kind: "PersistentVolumeClaim"}: true,
165 {APIVersion: "v1", Kind: "Pod"}: true,
166 {APIVersion: "v1", Kind: "PodAttachOptions"}: true,
167 {APIVersion: "v1", Kind: "PodExecOptions"}: true,
168 {APIVersion: "v1", Kind: "PodPortForwardOptions"}: true,
169 {APIVersion: "v1", Kind: "PodProxyOptions"}: true,
170 {APIVersion: "v1", Kind: "PodTemplate"}: true,
171 {APIVersion: "v1", Kind: "ReplicationController"}: true,
172 {APIVersion: "v1", Kind: "ResourceQuota"}: true,
173 {APIVersion: "v1", Kind: "Secret"}: true,
174 {APIVersion: "v1", Kind: "Service"}: true,
175 {APIVersion: "v1", Kind: "ServiceAccount"}: true,
176 {APIVersion: "v1", Kind: "ServiceProxyOptions"}: true,
177 }
178
179
180 type ResourceSchema struct {
181
182 Schema *spec.Schema
183 }
184
185
186 func (rs *ResourceSchema) IsMissingOrNull() bool {
187 if rs == nil || rs.Schema == nil {
188 return true
189 }
190 return reflect.DeepEqual(*rs.Schema, spec.Schema{})
191 }
192
193
194
195
196
197 func SchemaForResourceType(t yaml.TypeMeta) *ResourceSchema {
198 initSchema()
199 rs, found := globalSchema.schemaByResourceType[t]
200 if !found {
201 return nil
202 }
203 return &ResourceSchema{Schema: rs}
204 }
205
206
207
208 const SupplementaryOpenAPIFieldName = "openAPI"
209
210 const Definitions = "definitions"
211
212
213
214
215
216
217 func SchemaFromFile(path string) (*spec.Schema, error) {
218 object, err := parseOpenAPI(path)
219 if err != nil {
220 return nil, err
221 }
222
223 return schemaUsingField(object, SupplementaryOpenAPIFieldName)
224 }
225
226
227
228 func DefinitionRefs(openAPIPath string) ([]string, error) {
229 object, err := parseOpenAPI(openAPIPath)
230 if err != nil {
231 return nil, err
232 }
233 return definitionRefsFromRNode(object)
234 }
235
236
237
238 func definitionRefsFromRNode(object *yaml.RNode) ([]string, error) {
239 definitions, err := object.Pipe(yaml.Lookup(SupplementaryOpenAPIFieldName, Definitions))
240 if definitions == nil {
241 return nil, err
242 }
243 if err != nil {
244 return nil, err
245 }
246 return definitions.Fields()
247 }
248
249
250 func parseOpenAPI(openAPIPath string) (*yaml.RNode, error) {
251 b, err := os.ReadFile(openAPIPath)
252 if err != nil {
253 return nil, err
254 }
255
256 object, err := yaml.Parse(string(b))
257 if err != nil {
258 return nil, errors.Errorf("invalid file %q: %v", openAPIPath, err)
259 }
260 return object, nil
261 }
262
263
264
265 func schemaUsingField(object *yaml.RNode, field string) (*spec.Schema, error) {
266 if field != "" {
267
268 m := object.Field(field)
269 if m.IsNilOrEmpty() {
270
271 return nil, nil
272 }
273 object = m.Value
274 }
275
276 oAPI, err := object.String()
277 if err != nil {
278 return nil, err
279 }
280
281
282
283 var o interface{}
284 err = yaml.Unmarshal([]byte(oAPI), &o)
285 if err != nil {
286 return nil, err
287 }
288 j, err := json.Marshal(o)
289 if err != nil {
290 return nil, err
291 }
292
293 var sc spec.Schema
294 err = sc.UnmarshalJSON(j)
295 if err != nil {
296 return nil, err
297 }
298
299 return &sc, nil
300 }
301
302
303 func AddSchema(s []byte) error {
304 return parse(s, JsonOrYaml)
305 }
306
307
308 func ResetOpenAPI() {
309 schemaLock.Lock()
310 defer schemaLock.Unlock()
311
312 globalSchema = openapiData{}
313 customSchema = nil
314 kubernetesOpenAPIVersion = ""
315 }
316
317
318 func AddDefinitions(definitions spec.Definitions) {
319
320 if globalSchema.schemaByResourceType == nil {
321 globalSchema.schemaByResourceType = map[yaml.TypeMeta]*spec.Schema{}
322 }
323 if globalSchema.schema.Definitions == nil {
324 globalSchema.schema.Definitions = spec.Definitions{}
325 }
326
327
328 for k := range definitions {
329
330
331 d := definitions[k]
332
333
334 globalSchema.schema.Definitions[k] = d
335 gvk, found := d.VendorExtensible.Extensions[kubernetesGVKExtensionKey]
336 if !found {
337 continue
338 }
339
340 exts, ok := gvk.([]interface{})
341 if !ok {
342 continue
343 }
344
345 for i := range exts {
346 typeMeta, ok := toTypeMeta(exts[i])
347 if !ok {
348 continue
349 }
350 globalSchema.schemaByResourceType[typeMeta] = &d
351 }
352 }
353 }
354
355 func toTypeMeta(ext interface{}) (yaml.TypeMeta, bool) {
356 m, ok := ext.(map[string]interface{})
357 if !ok {
358 return yaml.TypeMeta{}, false
359 }
360
361 apiVersion := m[versionKey].(string)
362 if g, ok := m[groupKey].(string); ok && g != "" {
363 apiVersion = g + "/" + apiVersion
364 }
365 return yaml.TypeMeta{Kind: m[kindKey].(string), APIVersion: apiVersion}, true
366 }
367
368
369 func Resolve(ref *spec.Ref, schema *spec.Schema) (*spec.Schema, error) {
370 return resolve(schema, ref)
371 }
372
373
374 func Schema() *spec.Schema {
375 return rootSchema()
376 }
377
378
379
380 func GetSchema(s string, schema *spec.Schema) (*ResourceSchema, error) {
381 var sc spec.Schema
382 if err := sc.UnmarshalJSON([]byte(s)); err != nil {
383 return nil, errors.Wrap(err)
384 }
385 if sc.Ref.String() != "" {
386 r, err := Resolve(&sc.Ref, schema)
387 if err != nil {
388 return nil, errors.Wrap(err)
389 }
390 sc = *r
391 }
392
393 return &ResourceSchema{Schema: &sc}, nil
394 }
395
396
397
398
399
400
401
402
403 func IsNamespaceScoped(typeMeta yaml.TypeMeta) (bool, bool) {
404 if isNamespaceScoped, found := precomputedIsNamespaceScoped[typeMeta]; found {
405 return isNamespaceScoped, found
406 }
407 if isInitSchemaNeededForNamespaceScopeCheck() {
408 initSchema()
409 }
410 isNamespaceScoped, found := globalSchema.namespaceabilityByResourceType[typeMeta]
411 return isNamespaceScoped, found
412 }
413
414
415
416
417 func isInitSchemaNeededForNamespaceScopeCheck() bool {
418 schemaLock.Lock()
419 defer schemaLock.Unlock()
420
421 if globalSchema.schemaInit {
422 return false
423 }
424 if customSchema != nil {
425 return true
426 }
427 if kubernetesOpenAPIVersion == "" || kubernetesOpenAPIVersion == kubernetesOpenAPIDefaultVersion {
428
429
430
431
432 if globalSchema.defaultBuiltInSchemaParseStatus == schemaNotParsed {
433
434
435 globalSchema.defaultBuiltInSchemaParseStatus = schemaParseDelayed
436 }
437 return false
438 }
439
440 return true
441 }
442
443
444
445
446
447 func IsCertainlyClusterScoped(typeMeta yaml.TypeMeta) bool {
448 nsScoped, found := IsNamespaceScoped(typeMeta)
449 return found && !nsScoped
450 }
451
452
453
454
455 func SuppressBuiltInSchemaUse() {
456 globalSchema.noUseBuiltInSchema = true
457 }
458
459
460 func (rs *ResourceSchema) Elements() *ResourceSchema {
461
462 initSchema()
463
464 if len(rs.Schema.Type) != 1 || rs.Schema.Type[0] != "array" {
465
466 return nil
467 }
468 if rs == nil || rs.Schema == nil || rs.Schema.Items == nil {
469
470 return nil
471 }
472 s := *rs.Schema.Items.Schema
473 for s.Ref.String() != "" {
474 sc, e := Resolve(&s.Ref, Schema())
475 if e != nil {
476 return nil
477 }
478 s = *sc
479 }
480 return &ResourceSchema{Schema: &s}
481 }
482
483 const Elements = "[]"
484
485
486
487
488
489
490 func (rs *ResourceSchema) Lookup(path ...string) *ResourceSchema {
491 s := rs
492 for _, p := range path {
493 if s == nil {
494 break
495 }
496 if p == Elements {
497 s = s.Elements()
498 continue
499 }
500 s = s.Field(p)
501 }
502 return s
503 }
504
505
506 func (rs *ResourceSchema) Field(field string) *ResourceSchema {
507
508 initSchema()
509
510
511 s, found := rs.Schema.Properties[field]
512 switch {
513 case found:
514
515 case rs.Schema.AdditionalProperties != nil && rs.Schema.AdditionalProperties.Schema != nil:
516
517
518 s = *rs.Schema.AdditionalProperties.Schema
519 default:
520
521 return nil
522 }
523
524
525 for s.Ref.String() != "" {
526 sc, e := Resolve(&s.Ref, Schema())
527 if e != nil {
528 return nil
529 }
530 s = *sc
531 }
532
533
534 return &ResourceSchema{Schema: &s}
535 }
536
537
538 func (rs *ResourceSchema) PatchStrategyAndKeyList() (string, []string) {
539 ps, found := rs.Schema.Extensions[kubernetesPatchStrategyExtensionKey]
540 if !found {
541
542 return "", []string{}
543 }
544 mkList, found := rs.Schema.Extensions[kubernetesMergeKeyMapList]
545 if found {
546
547 mkListStr := make([]string, len(mkList.([]interface{})))
548 for i, v := range mkList.([]interface{}) {
549 mkListStr[i] = v.(string)
550 }
551 return ps.(string), mkListStr
552 }
553 mk, found := rs.Schema.Extensions[kubernetesMergeKeyExtensionKey]
554 if !found {
555
556 return ps.(string), []string{}
557 }
558 return ps.(string), []string{mk.(string)}
559 }
560
561
562 func (rs *ResourceSchema) PatchStrategyAndKey() (string, string) {
563 ps, found := rs.Schema.Extensions[kubernetesPatchStrategyExtensionKey]
564 if !found {
565
566 return "", ""
567 }
568
569 mk, found := rs.Schema.Extensions[kubernetesMergeKeyExtensionKey]
570 if !found {
571
572 mk = ""
573 }
574 return ps.(string), mk.(string)
575 }
576
577 const (
578
579
580 kubernetesOpenAPIDefaultVersion = kubernetesapi.DefaultOpenAPI
581
582
583
584 kustomizationAPIAssetName = "kustomizationapi/swagger.json"
585
586
587
588 kubernetesGVKExtensionKey = "x-kubernetes-group-version-kind"
589
590
591
592 kubernetesMergeKeyExtensionKey = "x-kubernetes-patch-merge-key"
593
594
595
596 kubernetesPatchStrategyExtensionKey = "x-kubernetes-patch-strategy"
597
598
599
600 kubernetesMergeKeyMapList = "x-kubernetes-list-map-keys"
601
602
603 groupKey = "group"
604
605 versionKey = "version"
606
607 kindKey = "kind"
608 )
609
610
611 func SetSchema(openAPIField map[string]string, schema []byte, reset bool) error {
612 schemaLock.Lock()
613 defer schemaLock.Unlock()
614
615
616 schemaIsSet := (kubernetesOpenAPIVersion != "") || customSchema != nil
617 if schemaIsSet && !reset {
618 return nil
619 }
620
621 version, versionProvided := openAPIField["version"]
622
623
624 if schema != nil {
625 if versionProvided {
626 return fmt.Errorf("builtin version and custom schema provided, cannot use both")
627 }
628 customSchema = schema
629 kubernetesOpenAPIVersion = "custom"
630
631 globalSchema.schemaInit = false
632 return nil
633 }
634
635
636 kubernetesOpenAPIVersion = version
637 if kubernetesOpenAPIVersion == "" {
638 return nil
639 }
640 if _, ok := kubernetesapi.OpenAPIMustAsset[kubernetesOpenAPIVersion]; !ok {
641 return fmt.Errorf("the specified OpenAPI version is not built in")
642 }
643
644 customSchema = nil
645
646 globalSchema.schemaInit = false
647 return nil
648 }
649
650
651 func GetSchemaVersion() string {
652 schemaLock.RLock()
653 defer schemaLock.RUnlock()
654
655 switch {
656 case kubernetesOpenAPIVersion == "" && customSchema == nil:
657 return kubernetesOpenAPIDefaultVersion
658 case customSchema != nil:
659 return "using custom schema from file provided"
660 default:
661 return kubernetesOpenAPIVersion
662 }
663 }
664
665
666 func initSchema() {
667 schemaLock.Lock()
668 defer schemaLock.Unlock()
669
670 if globalSchema.schemaInit {
671 return
672 }
673 globalSchema.schemaInit = true
674
675
676 if customSchema != nil {
677 err := parse(customSchema, JsonOrYaml)
678 if err != nil {
679 panic(fmt.Errorf("invalid schema file: %w", err))
680 }
681 } else {
682 if kubernetesOpenAPIVersion == "" || kubernetesOpenAPIVersion == kubernetesOpenAPIDefaultVersion {
683 parseBuiltinSchema(kubernetesOpenAPIDefaultVersion)
684 globalSchema.defaultBuiltInSchemaParseStatus = schemaParsed
685 } else {
686 parseBuiltinSchema(kubernetesOpenAPIVersion)
687 }
688 }
689
690 if globalSchema.defaultBuiltInSchemaParseStatus == schemaParseDelayed {
691 parseBuiltinSchema(kubernetesOpenAPIDefaultVersion)
692 globalSchema.defaultBuiltInSchemaParseStatus = schemaParsed
693 }
694
695 if err := parse(kustomizationapi.MustAsset(kustomizationAPIAssetName), JsonOrYaml); err != nil {
696
697 panic(err)
698 }
699 }
700
701
702 func parseBuiltinSchema(version string) {
703 if globalSchema.noUseBuiltInSchema {
704
705 return
706 }
707
708 assetName := filepath.Join(
709 "kubernetesapi",
710 strings.ReplaceAll(version, ".", "_"),
711 "swagger.pb")
712
713 if err := parse(kubernetesapi.OpenAPIMustAsset[version](assetName), Proto); err != nil {
714
715 panic(err)
716 }
717 }
718
719
720 func parse(b []byte, format format) error {
721 var swagger spec.Swagger
722 switch {
723 case format == Proto:
724 doc := &openapi_v2.Document{}
725
726 if err := proto.Unmarshal(b, doc); err != nil {
727 return fmt.Errorf("openapi proto unmarshalling failed: %w", err)
728 }
729
730 _, err := swagger.FromGnostic(doc)
731 if err != nil {
732 return errors.Wrap(err)
733 }
734
735 case format == JsonOrYaml:
736 if len(b) > 0 && b[0] != byte('{') {
737 var err error
738 b, err = k8syaml.YAMLToJSON(b)
739 if err != nil {
740 return errors.Wrap(err)
741 }
742 }
743 if err := swagger.UnmarshalJSON(b); err != nil {
744 return errors.Wrap(err)
745 }
746 }
747
748 AddDefinitions(swagger.Definitions)
749 findNamespaceability(swagger.Paths)
750 return nil
751 }
752
753
754
755
756
757
758 func findNamespaceability(paths *spec.Paths) {
759 if globalSchema.namespaceabilityByResourceType == nil {
760 globalSchema.namespaceabilityByResourceType = make(map[yaml.TypeMeta]bool)
761 }
762
763 if paths == nil {
764 return
765 }
766
767 for path, pathInfo := range paths.Paths {
768 if pathInfo.Get == nil {
769 continue
770 }
771 gvk, found := pathInfo.Get.VendorExtensible.Extensions[kubernetesGVKExtensionKey]
772 if !found {
773 continue
774 }
775 typeMeta, found := toTypeMeta(gvk)
776 if !found {
777 continue
778 }
779
780 if strings.Contains(path, "namespaces/{namespace}") {
781
782
783 globalSchema.namespaceabilityByResourceType[typeMeta] = true
784 } else if _, found := globalSchema.namespaceabilityByResourceType[typeMeta]; !found {
785
786
787 globalSchema.namespaceabilityByResourceType[typeMeta] = false
788 }
789 }
790 }
791
792 func resolve(root interface{}, ref *spec.Ref) (*spec.Schema, error) {
793 if s, ok := root.(*spec.Schema); ok && s == nil {
794 return nil, nil
795 }
796 res, _, err := ref.GetPointer().Get(root)
797 if err != nil {
798 return nil, errors.Wrap(err)
799 }
800 switch sch := res.(type) {
801 case spec.Schema:
802 return &sch, nil
803 case *spec.Schema:
804 return sch, nil
805 case map[string]interface{}:
806 b, err := json.Marshal(sch)
807 if err != nil {
808 return nil, err
809 }
810 newSch := new(spec.Schema)
811 if err = json.Unmarshal(b, newSch); err != nil {
812 return nil, err
813 }
814 return newSch, nil
815 default:
816 return nil, errors.Wrap(fmt.Errorf("unknown type for the resolved reference"))
817 }
818 }
819
820 func rootSchema() *spec.Schema {
821 initSchema()
822 return &globalSchema.schema
823 }
824
View as plain text