1/* 2Copyright 2017 The Kubernetes Authors. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package openapi 18 19import ( 20 openapi_v2 "github.com/googleapis/gnostic/openapiv2" 21 22 "k8s.io/apimachinery/pkg/runtime/schema" 23 "k8s.io/kube-openapi/pkg/util/proto" 24) 25 26// Resources interface describe a resources provider, that can give you 27// resource based on group-version-kind. 28type Resources interface { 29 LookupResource(gvk schema.GroupVersionKind) proto.Schema 30} 31 32// groupVersionKindExtensionKey is the key used to lookup the 33// GroupVersionKind value for an object definition from the 34// definition's "extensions" map. 35const groupVersionKindExtensionKey = "x-kubernetes-group-version-kind" 36 37// document is an implementation of `Resources`. It looks for 38// resources in an openapi Schema. 39type document struct { 40 // Maps gvk to model name 41 resources map[schema.GroupVersionKind]string 42 models proto.Models 43} 44 45var _ Resources = &document{} 46 47// NewOpenAPIData creates a new `Resources` out of the openapi document 48func NewOpenAPIData(doc *openapi_v2.Document) (Resources, error) { 49 models, err := proto.NewOpenAPIData(doc) 50 if err != nil { 51 return nil, err 52 } 53 54 resources := map[schema.GroupVersionKind]string{} 55 for _, modelName := range models.ListModels() { 56 model := models.LookupModel(modelName) 57 if model == nil { 58 panic("ListModels returns a model that can't be looked-up.") 59 } 60 gvkList := parseGroupVersionKind(model) 61 for _, gvk := range gvkList { 62 if len(gvk.Kind) > 0 { 63 resources[gvk] = modelName 64 } 65 } 66 } 67 68 return &document{ 69 resources: resources, 70 models: models, 71 }, nil 72} 73 74func (d *document) LookupResource(gvk schema.GroupVersionKind) proto.Schema { 75 modelName, found := d.resources[gvk] 76 if !found { 77 return nil 78 } 79 return d.models.LookupModel(modelName) 80} 81 82// Get and parse GroupVersionKind from the extension. Returns empty if it doesn't have one. 83func parseGroupVersionKind(s proto.Schema) []schema.GroupVersionKind { 84 extensions := s.GetExtensions() 85 86 gvkListResult := []schema.GroupVersionKind{} 87 88 // Get the extensions 89 gvkExtension, ok := extensions[groupVersionKindExtensionKey] 90 if !ok { 91 return []schema.GroupVersionKind{} 92 } 93 94 // gvk extension must be a list of at least 1 element. 95 gvkList, ok := gvkExtension.([]interface{}) 96 if !ok { 97 return []schema.GroupVersionKind{} 98 } 99 100 for _, gvk := range gvkList { 101 // gvk extension list must be a map with group, version, and 102 // kind fields 103 gvkMap, ok := gvk.(map[interface{}]interface{}) 104 if !ok { 105 continue 106 } 107 group, ok := gvkMap["group"].(string) 108 if !ok { 109 continue 110 } 111 version, ok := gvkMap["version"].(string) 112 if !ok { 113 continue 114 } 115 kind, ok := gvkMap["kind"].(string) 116 if !ok { 117 continue 118 } 119 120 gvkListResult = append(gvkListResult, schema.GroupVersionKind{ 121 Group: group, 122 Version: version, 123 Kind: kind, 124 }) 125 } 126 127 return gvkListResult 128} 129