unstructured.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. /*
  2. Copyright 2015 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 runtime
  14. import (
  15. gojson "encoding/json"
  16. "errors"
  17. "fmt"
  18. "io"
  19. "strings"
  20. "k8s.io/kubernetes/pkg/api/unversioned"
  21. "k8s.io/kubernetes/pkg/util/json"
  22. )
  23. // UnstructuredJSONScheme is capable of converting JSON data into the Unstructured
  24. // type, which can be used for generic access to objects without a predefined scheme.
  25. // TODO: move into serializer/json.
  26. var UnstructuredJSONScheme Codec = unstructuredJSONScheme{}
  27. type unstructuredJSONScheme struct{}
  28. func (s unstructuredJSONScheme) Decode(data []byte, _ *unversioned.GroupVersionKind, obj Object) (Object, *unversioned.GroupVersionKind, error) {
  29. var err error
  30. if obj != nil {
  31. err = s.decodeInto(data, obj)
  32. } else {
  33. obj, err = s.decode(data)
  34. }
  35. if err != nil {
  36. return nil, nil, err
  37. }
  38. gvk := obj.GetObjectKind().GroupVersionKind()
  39. if len(gvk.Kind) == 0 {
  40. return nil, &gvk, NewMissingKindErr(string(data))
  41. }
  42. return obj, &gvk, nil
  43. }
  44. func (unstructuredJSONScheme) Encode(obj Object, w io.Writer) error {
  45. switch t := obj.(type) {
  46. case *Unstructured:
  47. return json.NewEncoder(w).Encode(t.Object)
  48. case *UnstructuredList:
  49. items := make([]map[string]interface{}, 0, len(t.Items))
  50. for _, i := range t.Items {
  51. items = append(items, i.Object)
  52. }
  53. t.Object["items"] = items
  54. defer func() { delete(t.Object, "items") }()
  55. return json.NewEncoder(w).Encode(t.Object)
  56. case *Unknown:
  57. // TODO: Unstructured needs to deal with ContentType.
  58. _, err := w.Write(t.Raw)
  59. return err
  60. default:
  61. return json.NewEncoder(w).Encode(t)
  62. }
  63. }
  64. func (s unstructuredJSONScheme) decode(data []byte) (Object, error) {
  65. type detector struct {
  66. Items gojson.RawMessage
  67. }
  68. var det detector
  69. if err := json.Unmarshal(data, &det); err != nil {
  70. return nil, err
  71. }
  72. if det.Items != nil {
  73. list := &UnstructuredList{}
  74. err := s.decodeToList(data, list)
  75. return list, err
  76. }
  77. // No Items field, so it wasn't a list.
  78. unstruct := &Unstructured{}
  79. err := s.decodeToUnstructured(data, unstruct)
  80. return unstruct, err
  81. }
  82. func (s unstructuredJSONScheme) decodeInto(data []byte, obj Object) error {
  83. switch x := obj.(type) {
  84. case *Unstructured:
  85. return s.decodeToUnstructured(data, x)
  86. case *UnstructuredList:
  87. return s.decodeToList(data, x)
  88. case *VersionedObjects:
  89. o, err := s.decode(data)
  90. if err == nil {
  91. x.Objects = []Object{o}
  92. }
  93. return err
  94. default:
  95. return json.Unmarshal(data, x)
  96. }
  97. }
  98. func (unstructuredJSONScheme) decodeToUnstructured(data []byte, unstruct *Unstructured) error {
  99. m := make(map[string]interface{})
  100. if err := json.Unmarshal(data, &m); err != nil {
  101. return err
  102. }
  103. unstruct.Object = m
  104. return nil
  105. }
  106. func (s unstructuredJSONScheme) decodeToList(data []byte, list *UnstructuredList) error {
  107. type decodeList struct {
  108. Items []gojson.RawMessage
  109. }
  110. var dList decodeList
  111. if err := json.Unmarshal(data, &dList); err != nil {
  112. return err
  113. }
  114. if err := json.Unmarshal(data, &list.Object); err != nil {
  115. return err
  116. }
  117. // For typed lists, e.g., a PodList, API server doesn't set each item's
  118. // APIVersion and Kind. We need to set it.
  119. listAPIVersion := list.GetAPIVersion()
  120. listKind := list.GetKind()
  121. itemKind := strings.TrimSuffix(listKind, "List")
  122. delete(list.Object, "items")
  123. list.Items = nil
  124. for _, i := range dList.Items {
  125. unstruct := &Unstructured{}
  126. if err := s.decodeToUnstructured([]byte(i), unstruct); err != nil {
  127. return err
  128. }
  129. // This is hacky. Set the item's Kind and APIVersion to those inferred
  130. // from the List.
  131. if len(unstruct.GetKind()) == 0 && len(unstruct.GetAPIVersion()) == 0 {
  132. unstruct.SetKind(itemKind)
  133. unstruct.SetAPIVersion(listAPIVersion)
  134. }
  135. list.Items = append(list.Items, unstruct)
  136. }
  137. return nil
  138. }
  139. // UnstructuredObjectConverter is an ObjectConverter for use with
  140. // Unstructured objects. Since it has no schema or type information,
  141. // it will only succeed for no-op conversions. This is provided as a
  142. // sane implementation for APIs that require an object converter.
  143. type UnstructuredObjectConverter struct{}
  144. func (UnstructuredObjectConverter) Convert(in, out, context interface{}) error {
  145. unstructIn, ok := in.(*Unstructured)
  146. if !ok {
  147. return fmt.Errorf("input type %T in not valid for unstructured conversion", in)
  148. }
  149. unstructOut, ok := out.(*Unstructured)
  150. if !ok {
  151. return fmt.Errorf("output type %T in not valid for unstructured conversion", out)
  152. }
  153. // maybe deep copy the map? It is documented in the
  154. // ObjectConverter interface that this function is not
  155. // guaranteeed to not mutate the input. Or maybe set the input
  156. // object to nil.
  157. unstructOut.Object = unstructIn.Object
  158. return nil
  159. }
  160. func (UnstructuredObjectConverter) ConvertToVersion(in Object, target GroupVersioner) (Object, error) {
  161. if kind := in.GetObjectKind().GroupVersionKind(); !kind.Empty() {
  162. gvk, ok := target.KindForGroupVersionKinds([]unversioned.GroupVersionKind{kind})
  163. if !ok {
  164. // TODO: should this be a typed error?
  165. return nil, fmt.Errorf("%v is unstructured and is not suitable for converting to %q", kind, target)
  166. }
  167. in.GetObjectKind().SetGroupVersionKind(gvk)
  168. }
  169. return in, nil
  170. }
  171. func (UnstructuredObjectConverter) ConvertFieldLabel(version, kind, label, value string) (string, string, error) {
  172. return "", "", errors.New("unstructured cannot convert field labels")
  173. }