gvkparser.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. /*
  2. Copyright 2018 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package managedfields
  14. import (
  15. "fmt"
  16. "k8s.io/apimachinery/pkg/runtime/schema"
  17. "k8s.io/kube-openapi/pkg/schemaconv"
  18. "k8s.io/kube-openapi/pkg/util/proto"
  19. "sigs.k8s.io/structured-merge-diff/v4/typed"
  20. )
  21. // groupVersionKindExtensionKey is the key used to lookup the
  22. // GroupVersionKind value for an object definition from the
  23. // definition's "extensions" map.
  24. const groupVersionKindExtensionKey = "x-kubernetes-group-version-kind"
  25. // GvkParser contains a Parser that allows introspecting the schema.
  26. type GvkParser struct {
  27. gvks map[schema.GroupVersionKind]string
  28. parser typed.Parser
  29. }
  30. // Type returns a helper which can produce objects of the given type. Any
  31. // errors are deferred until a further function is called.
  32. func (p *GvkParser) Type(gvk schema.GroupVersionKind) *typed.ParseableType {
  33. typeName, ok := p.gvks[gvk]
  34. if !ok {
  35. return nil
  36. }
  37. t := p.parser.Type(typeName)
  38. return &t
  39. }
  40. // NewGVKParser builds a GVKParser from a proto.Models. This
  41. // will automatically find the proper version of the object, and the
  42. // corresponding schema information.
  43. func NewGVKParser(models proto.Models, preserveUnknownFields bool) (*GvkParser, error) {
  44. typeSchema, err := schemaconv.ToSchemaWithPreserveUnknownFields(models, preserveUnknownFields)
  45. if err != nil {
  46. return nil, fmt.Errorf("failed to convert models to schema: %v", err)
  47. }
  48. parser := GvkParser{
  49. gvks: map[schema.GroupVersionKind]string{},
  50. }
  51. parser.parser = typed.Parser{Schema: *typeSchema}
  52. for _, modelName := range models.ListModels() {
  53. model := models.LookupModel(modelName)
  54. if model == nil {
  55. panic(fmt.Sprintf("ListModels returns a model that can't be looked-up for: %v", modelName))
  56. }
  57. gvkList := parseGroupVersionKind(model)
  58. for _, gvk := range gvkList {
  59. if len(gvk.Kind) > 0 {
  60. _, ok := parser.gvks[gvk]
  61. if ok {
  62. return nil, fmt.Errorf("duplicate entry for %v", gvk)
  63. }
  64. parser.gvks[gvk] = modelName
  65. }
  66. }
  67. }
  68. return &parser, nil
  69. }
  70. // Get and parse GroupVersionKind from the extension. Returns empty if it doesn't have one.
  71. func parseGroupVersionKind(s proto.Schema) []schema.GroupVersionKind {
  72. extensions := s.GetExtensions()
  73. gvkListResult := []schema.GroupVersionKind{}
  74. // Get the extensions
  75. gvkExtension, ok := extensions[groupVersionKindExtensionKey]
  76. if !ok {
  77. return []schema.GroupVersionKind{}
  78. }
  79. // gvk extension must be a list of at least 1 element.
  80. gvkList, ok := gvkExtension.([]interface{})
  81. if !ok {
  82. return []schema.GroupVersionKind{}
  83. }
  84. for _, gvk := range gvkList {
  85. // gvk extension list must be a map with group, version, and
  86. // kind fields
  87. gvkMap, ok := gvk.(map[interface{}]interface{})
  88. if !ok {
  89. continue
  90. }
  91. group, ok := gvkMap["group"].(string)
  92. if !ok {
  93. continue
  94. }
  95. version, ok := gvkMap["version"].(string)
  96. if !ok {
  97. continue
  98. }
  99. kind, ok := gvkMap["kind"].(string)
  100. if !ok {
  101. continue
  102. }
  103. gvkListResult = append(gvkListResult, schema.GroupVersionKind{
  104. Group: group,
  105. Version: version,
  106. Kind: kind,
  107. })
  108. }
  109. return gvkListResult
  110. }