marshal.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. /*
  2. Copyright 2017 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 util
  14. import (
  15. "bufio"
  16. "bytes"
  17. "io"
  18. "github.com/pkg/errors"
  19. "sigs.k8s.io/yaml"
  20. "k8s.io/apimachinery/pkg/runtime"
  21. "k8s.io/apimachinery/pkg/runtime/schema"
  22. "k8s.io/apimachinery/pkg/runtime/serializer"
  23. errorsutil "k8s.io/apimachinery/pkg/util/errors"
  24. utilyaml "k8s.io/apimachinery/pkg/util/yaml"
  25. clientsetscheme "k8s.io/client-go/kubernetes/scheme"
  26. "k8s.io/kubernetes/cmd/kubeadm/app/constants"
  27. )
  28. // MarshalToYaml marshals an object into yaml.
  29. func MarshalToYaml(obj runtime.Object, gv schema.GroupVersion) ([]byte, error) {
  30. return MarshalToYamlForCodecs(obj, gv, clientsetscheme.Codecs)
  31. }
  32. // MarshalToYamlForCodecs marshals an object into yaml using the specified codec
  33. // TODO: Is specifying the gv really needed here?
  34. // TODO: Can we support json out of the box easily here?
  35. func MarshalToYamlForCodecs(obj runtime.Object, gv schema.GroupVersion, codecs serializer.CodecFactory) ([]byte, error) {
  36. const mediaType = runtime.ContentTypeYAML
  37. info, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), mediaType)
  38. if !ok {
  39. return []byte{}, errors.Errorf("unsupported media type %q", mediaType)
  40. }
  41. encoder := codecs.EncoderForVersion(info.Serializer, gv)
  42. return runtime.Encode(encoder, obj)
  43. }
  44. // UnmarshalFromYaml unmarshals yaml into an object.
  45. func UnmarshalFromYaml(buffer []byte, gv schema.GroupVersion) (runtime.Object, error) {
  46. return UnmarshalFromYamlForCodecs(buffer, gv, clientsetscheme.Codecs)
  47. }
  48. // UnmarshalFromYamlForCodecs unmarshals yaml into an object using the specified codec
  49. // TODO: Is specifying the gv really needed here?
  50. // TODO: Can we support json out of the box easily here?
  51. func UnmarshalFromYamlForCodecs(buffer []byte, gv schema.GroupVersion, codecs serializer.CodecFactory) (runtime.Object, error) {
  52. const mediaType = runtime.ContentTypeYAML
  53. info, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), mediaType)
  54. if !ok {
  55. return nil, errors.Errorf("unsupported media type %q", mediaType)
  56. }
  57. decoder := codecs.DecoderToVersion(info.Serializer, gv)
  58. return runtime.Decode(decoder, buffer)
  59. }
  60. // SplitYAMLDocuments reads the YAML bytes per-document, unmarshals the TypeMeta information from each document
  61. // and returns a map between the GroupVersionKind of the document and the document bytes
  62. func SplitYAMLDocuments(yamlBytes []byte) (map[schema.GroupVersionKind][]byte, error) {
  63. gvkmap := map[schema.GroupVersionKind][]byte{}
  64. knownKinds := map[string]bool{}
  65. errs := []error{}
  66. buf := bytes.NewBuffer(yamlBytes)
  67. reader := utilyaml.NewYAMLReader(bufio.NewReader(buf))
  68. for {
  69. typeMetaInfo := runtime.TypeMeta{}
  70. // Read one YAML document at a time, until io.EOF is returned
  71. b, err := reader.Read()
  72. if err == io.EOF {
  73. break
  74. } else if err != nil {
  75. return nil, err
  76. }
  77. if len(b) == 0 {
  78. break
  79. }
  80. // Deserialize the TypeMeta information of this byte slice
  81. if err := yaml.Unmarshal(b, &typeMetaInfo); err != nil {
  82. return nil, err
  83. }
  84. // Require TypeMeta information to be present
  85. if len(typeMetaInfo.APIVersion) == 0 || len(typeMetaInfo.Kind) == 0 {
  86. errs = append(errs, errors.New("invalid configuration: kind and apiVersion is mandatory information that needs to be specified in all YAML documents"))
  87. continue
  88. }
  89. // Check whether the kind has been registered before. If it has, throw an error
  90. if known := knownKinds[typeMetaInfo.Kind]; known {
  91. errs = append(errs, errors.Errorf("invalid configuration: kind %q is specified twice in YAML file", typeMetaInfo.Kind))
  92. continue
  93. }
  94. knownKinds[typeMetaInfo.Kind] = true
  95. // Build a GroupVersionKind object from the deserialized TypeMeta object
  96. gv, err := schema.ParseGroupVersion(typeMetaInfo.APIVersion)
  97. if err != nil {
  98. errs = append(errs, errors.Wrap(err, "unable to parse apiVersion"))
  99. continue
  100. }
  101. gvk := gv.WithKind(typeMetaInfo.Kind)
  102. // Save the mapping between the gvk and the bytes that object consists of
  103. gvkmap[gvk] = b
  104. }
  105. if err := errorsutil.NewAggregate(errs); err != nil {
  106. return nil, err
  107. }
  108. return gvkmap, nil
  109. }
  110. // GroupVersionKindsFromBytes parses the bytes and returns a gvk slice
  111. func GroupVersionKindsFromBytes(b []byte) ([]schema.GroupVersionKind, error) {
  112. gvkmap, err := SplitYAMLDocuments(b)
  113. if err != nil {
  114. return nil, err
  115. }
  116. gvks := []schema.GroupVersionKind{}
  117. for gvk := range gvkmap {
  118. gvks = append(gvks, gvk)
  119. }
  120. return gvks, nil
  121. }
  122. // GroupVersionKindsHasKind returns whether the following gvk slice contains the kind given as a parameter
  123. func GroupVersionKindsHasKind(gvks []schema.GroupVersionKind, kind string) bool {
  124. for _, gvk := range gvks {
  125. if gvk.Kind == kind {
  126. return true
  127. }
  128. }
  129. return false
  130. }
  131. // GroupVersionKindsHasClusterConfiguration returns whether the following gvk slice contains a ClusterConfiguration object
  132. func GroupVersionKindsHasClusterConfiguration(gvks ...schema.GroupVersionKind) bool {
  133. return GroupVersionKindsHasKind(gvks, constants.ClusterConfigurationKind)
  134. }
  135. // GroupVersionKindsHasInitConfiguration returns whether the following gvk slice contains a InitConfiguration object
  136. func GroupVersionKindsHasInitConfiguration(gvks ...schema.GroupVersionKind) bool {
  137. return GroupVersionKindsHasKind(gvks, constants.InitConfigurationKind)
  138. }
  139. // GroupVersionKindsHasJoinConfiguration returns whether the following gvk slice contains a JoinConfiguration object
  140. func GroupVersionKindsHasJoinConfiguration(gvks ...schema.GroupVersionKind) bool {
  141. return GroupVersionKindsHasKind(gvks, constants.JoinConfigurationKind)
  142. }