marshal.go 6.0 KB

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