1/* 2Copyright 2017 The Kubernetes Authors. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package util 18 19import ( 20 "bufio" 21 "bytes" 22 "io" 23 24 kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" 25 "k8s.io/kubernetes/cmd/kubeadm/app/constants" 26 27 "k8s.io/apimachinery/pkg/runtime" 28 "k8s.io/apimachinery/pkg/runtime/schema" 29 "k8s.io/apimachinery/pkg/runtime/serializer" 30 yamlserializer "k8s.io/apimachinery/pkg/runtime/serializer/yaml" 31 errorsutil "k8s.io/apimachinery/pkg/util/errors" 32 utilyaml "k8s.io/apimachinery/pkg/util/yaml" 33 clientsetscheme "k8s.io/client-go/kubernetes/scheme" 34 35 "github.com/pkg/errors" 36) 37 38// MarshalToYaml marshals an object into yaml. 39func MarshalToYaml(obj runtime.Object, gv schema.GroupVersion) ([]byte, error) { 40 return MarshalToYamlForCodecs(obj, gv, clientsetscheme.Codecs) 41} 42 43// MarshalToYamlForCodecs marshals an object into yaml using the specified codec 44// TODO: Is specifying the gv really needed here? 45// TODO: Can we support json out of the box easily here? 46func MarshalToYamlForCodecs(obj runtime.Object, gv schema.GroupVersion, codecs serializer.CodecFactory) ([]byte, error) { 47 const mediaType = runtime.ContentTypeYAML 48 info, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), mediaType) 49 if !ok { 50 return []byte{}, errors.Errorf("unsupported media type %q", mediaType) 51 } 52 53 encoder := codecs.EncoderForVersion(info.Serializer, gv) 54 return runtime.Encode(encoder, obj) 55} 56 57// UnmarshalFromYaml unmarshals yaml into an object. 58func UnmarshalFromYaml(buffer []byte, gv schema.GroupVersion) (runtime.Object, error) { 59 return UnmarshalFromYamlForCodecs(buffer, gv, clientsetscheme.Codecs) 60} 61 62// UnmarshalFromYamlForCodecs unmarshals yaml into an object using the specified codec 63// TODO: Is specifying the gv really needed here? 64// TODO: Can we support json out of the box easily here? 65func UnmarshalFromYamlForCodecs(buffer []byte, gv schema.GroupVersion, codecs serializer.CodecFactory) (runtime.Object, error) { 66 const mediaType = runtime.ContentTypeYAML 67 info, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), mediaType) 68 if !ok { 69 return nil, errors.Errorf("unsupported media type %q", mediaType) 70 } 71 72 decoder := codecs.DecoderToVersion(info.Serializer, gv) 73 return runtime.Decode(decoder, buffer) 74} 75 76// SplitYAMLDocuments reads the YAML bytes per-document, unmarshals the TypeMeta information from each document 77// and returns a map between the GroupVersionKind of the document and the document bytes 78func SplitYAMLDocuments(yamlBytes []byte) (kubeadmapi.DocumentMap, error) { 79 gvkmap := kubeadmapi.DocumentMap{} 80 knownKinds := map[string]bool{} 81 errs := []error{} 82 buf := bytes.NewBuffer(yamlBytes) 83 reader := utilyaml.NewYAMLReader(bufio.NewReader(buf)) 84 for { 85 // Read one YAML document at a time, until io.EOF is returned 86 b, err := reader.Read() 87 if err == io.EOF { 88 break 89 } else if err != nil { 90 return nil, err 91 } 92 if len(b) == 0 { 93 break 94 } 95 // Deserialize the TypeMeta information of this byte slice 96 gvk, err := yamlserializer.DefaultMetaFactory.Interpret(b) 97 if err != nil { 98 return nil, err 99 } 100 if len(gvk.Group) == 0 || len(gvk.Version) == 0 || len(gvk.Kind) == 0 { 101 return nil, errors.Errorf("invalid configuration for GroupVersionKind %+v: kind and apiVersion is mandatory information that must be specified", gvk) 102 } 103 104 // Check whether the kind has been registered before. If it has, throw an error 105 if known := knownKinds[gvk.Kind]; known { 106 errs = append(errs, errors.Errorf("invalid configuration: kind %q is specified twice in YAML file", gvk.Kind)) 107 continue 108 } 109 knownKinds[gvk.Kind] = true 110 111 // Save the mapping between the gvk and the bytes that object consists of 112 gvkmap[*gvk] = b 113 } 114 if err := errorsutil.NewAggregate(errs); err != nil { 115 return nil, err 116 } 117 return gvkmap, nil 118} 119 120// GroupVersionKindsFromBytes parses the bytes and returns a gvk slice 121func GroupVersionKindsFromBytes(b []byte) ([]schema.GroupVersionKind, error) { 122 gvkmap, err := SplitYAMLDocuments(b) 123 if err != nil { 124 return nil, err 125 } 126 gvks := []schema.GroupVersionKind{} 127 for gvk := range gvkmap { 128 gvks = append(gvks, gvk) 129 } 130 return gvks, nil 131} 132 133// GroupVersionKindsHasKind returns whether the following gvk slice contains the kind given as a parameter 134func GroupVersionKindsHasKind(gvks []schema.GroupVersionKind, kind string) bool { 135 for _, gvk := range gvks { 136 if gvk.Kind == kind { 137 return true 138 } 139 } 140 return false 141} 142 143// GroupVersionKindsHasClusterConfiguration returns whether the following gvk slice contains a ClusterConfiguration object 144func GroupVersionKindsHasClusterConfiguration(gvks ...schema.GroupVersionKind) bool { 145 return GroupVersionKindsHasKind(gvks, constants.ClusterConfigurationKind) 146} 147 148// GroupVersionKindsHasInitConfiguration returns whether the following gvk slice contains a InitConfiguration object 149func GroupVersionKindsHasInitConfiguration(gvks ...schema.GroupVersionKind) bool { 150 return GroupVersionKindsHasKind(gvks, constants.InitConfigurationKind) 151} 152 153// GroupVersionKindsHasJoinConfiguration returns whether the following gvk slice contains a JoinConfiguration object 154func GroupVersionKindsHasJoinConfiguration(gvks ...schema.GroupVersionKind) bool { 155 return GroupVersionKindsHasKind(gvks, constants.JoinConfigurationKind) 156} 157