...

Source file src/k8s.io/kubectl/pkg/util/openapi/openapi.go

Documentation: k8s.io/kubectl/pkg/util/openapi

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package openapi
    18  
    19  import (
    20  	openapi_v2 "github.com/google/gnostic-models/openapiv2"
    21  
    22  	"k8s.io/apimachinery/pkg/runtime/schema"
    23  	"k8s.io/kube-openapi/pkg/util/proto"
    24  	"sigs.k8s.io/yaml"
    25  )
    26  
    27  // OpenAPIResourcesGetter represents a function to return
    28  // OpenAPI V2 resource specifications. Used for lazy-loading
    29  // these resource specifications.
    30  type OpenAPIResourcesGetter interface {
    31  	OpenAPISchema() (Resources, error)
    32  }
    33  
    34  // Resources interface describe a resources provider, that can give you
    35  // resource based on group-version-kind.
    36  type Resources interface {
    37  	LookupResource(gvk schema.GroupVersionKind) proto.Schema
    38  	GetConsumes(gvk schema.GroupVersionKind, operation string) []string
    39  }
    40  
    41  // groupVersionKindExtensionKey is the key used to lookup the
    42  // GroupVersionKind value for an object definition from the
    43  // definition's "extensions" map.
    44  const groupVersionKindExtensionKey = "x-kubernetes-group-version-kind"
    45  
    46  // document is an implementation of `Resources`. It looks for
    47  // resources in an openapi Schema.
    48  type document struct {
    49  	// Maps gvk to model name
    50  	resources map[schema.GroupVersionKind]string
    51  	models    proto.Models
    52  	doc       *openapi_v2.Document
    53  }
    54  
    55  var _ Resources = &document{}
    56  
    57  // NewOpenAPIData creates a new `Resources` out of the openapi document
    58  func NewOpenAPIData(doc *openapi_v2.Document) (Resources, error) {
    59  	models, err := proto.NewOpenAPIData(doc)
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  
    64  	resources := map[schema.GroupVersionKind]string{}
    65  	for _, modelName := range models.ListModels() {
    66  		model := models.LookupModel(modelName)
    67  		if model == nil {
    68  			panic("ListModels returns a model that can't be looked-up.")
    69  		}
    70  		gvkList := parseGroupVersionKind(model)
    71  		for _, gvk := range gvkList {
    72  			if len(gvk.Kind) > 0 {
    73  				resources[gvk] = modelName
    74  			}
    75  		}
    76  	}
    77  
    78  	return &document{
    79  		resources: resources,
    80  		models:    models,
    81  		doc:       doc,
    82  	}, nil
    83  }
    84  
    85  func (d *document) LookupResource(gvk schema.GroupVersionKind) proto.Schema {
    86  	modelName, found := d.resources[gvk]
    87  	if !found {
    88  		return nil
    89  	}
    90  	return d.models.LookupModel(modelName)
    91  }
    92  
    93  func (d *document) GetConsumes(gvk schema.GroupVersionKind, operation string) []string {
    94  	for _, path := range d.doc.GetPaths().GetPath() {
    95  		for _, ex := range path.GetValue().GetPatch().GetVendorExtension() {
    96  			if ex.GetValue().GetYaml() == "" ||
    97  				ex.GetName() != "x-kubernetes-group-version-kind" {
    98  				continue
    99  			}
   100  
   101  			var value map[string]string
   102  			err := yaml.Unmarshal([]byte(ex.GetValue().GetYaml()), &value)
   103  			if err != nil {
   104  				continue
   105  			}
   106  
   107  			if value["group"] == gvk.Group && value["kind"] == gvk.Kind && value["version"] == gvk.Version {
   108  				switch operation {
   109  				case "GET":
   110  					return path.GetValue().GetGet().GetConsumes()
   111  				case "PATCH":
   112  					return path.GetValue().GetPatch().GetConsumes()
   113  				case "HEAD":
   114  					return path.GetValue().GetHead().GetConsumes()
   115  				case "PUT":
   116  					return path.GetValue().GetPut().GetConsumes()
   117  				case "POST":
   118  					return path.GetValue().GetPost().GetConsumes()
   119  				case "OPTIONS":
   120  					return path.GetValue().GetOptions().GetConsumes()
   121  				case "DELETE":
   122  					return path.GetValue().GetDelete().GetConsumes()
   123  				}
   124  			}
   125  		}
   126  	}
   127  
   128  	return nil
   129  }
   130  
   131  // Get and parse GroupVersionKind from the extension. Returns empty if it doesn't have one.
   132  func parseGroupVersionKind(s proto.Schema) []schema.GroupVersionKind {
   133  	extensions := s.GetExtensions()
   134  
   135  	gvkListResult := []schema.GroupVersionKind{}
   136  
   137  	// Get the extensions
   138  	gvkExtension, ok := extensions[groupVersionKindExtensionKey]
   139  	if !ok {
   140  		return []schema.GroupVersionKind{}
   141  	}
   142  
   143  	// gvk extension must be a list of at least 1 element.
   144  	gvkList, ok := gvkExtension.([]interface{})
   145  	if !ok {
   146  		return []schema.GroupVersionKind{}
   147  	}
   148  
   149  	for _, gvk := range gvkList {
   150  		// gvk extension list must be a map with group, version, and
   151  		// kind fields
   152  		gvkMap, ok := gvk.(map[interface{}]interface{})
   153  		if !ok {
   154  			continue
   155  		}
   156  		group, ok := gvkMap["group"].(string)
   157  		if !ok {
   158  			continue
   159  		}
   160  		version, ok := gvkMap["version"].(string)
   161  		if !ok {
   162  			continue
   163  		}
   164  		kind, ok := gvkMap["kind"].(string)
   165  		if !ok {
   166  			continue
   167  		}
   168  
   169  		gvkListResult = append(gvkListResult, schema.GroupVersionKind{
   170  			Group:   group,
   171  			Version: version,
   172  			Kind:    kind,
   173  		})
   174  	}
   175  
   176  	return gvkListResult
   177  }
   178  

View as plain text