sigs.k8s.io/cluster-api@v1.7.1/bootstrap/kubeadm/types/utils.go (about) 1 /* 2 Copyright 2021 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // Package utils contains Kubeadm utility types. 18 package utils 19 20 import ( 21 "github.com/blang/semver/v4" 22 "github.com/pkg/errors" 23 "k8s.io/apimachinery/pkg/runtime" 24 "k8s.io/apimachinery/pkg/runtime/schema" 25 "k8s.io/apimachinery/pkg/runtime/serializer" 26 "sigs.k8s.io/controller-runtime/pkg/conversion" 27 "sigs.k8s.io/controller-runtime/pkg/scheme" 28 29 bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1" 30 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/types/upstreamv1beta2" 31 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/types/upstreamv1beta3" 32 "sigs.k8s.io/cluster-api/util/version" 33 ) 34 35 var ( 36 v1beta2KubeadmVersion = semver.MustParse("1.15.0") 37 v1beta3KubeadmVersion = semver.MustParse("1.22.0") 38 39 clusterConfigurationVersionTypeMap = map[schema.GroupVersion]conversion.Convertible{ 40 upstreamv1beta3.GroupVersion: &upstreamv1beta3.ClusterConfiguration{}, 41 upstreamv1beta2.GroupVersion: &upstreamv1beta2.ClusterConfiguration{}, 42 } 43 44 clusterStatusVersionTypeMap = map[schema.GroupVersion]conversion.Convertible{ 45 // ClusterStatus has been removed in v1beta3, so we don't need an entry for v1beta3 46 upstreamv1beta2.GroupVersion: &upstreamv1beta2.ClusterStatus{}, 47 } 48 49 initConfigurationVersionTypeMap = map[schema.GroupVersion]conversion.Convertible{ 50 upstreamv1beta3.GroupVersion: &upstreamv1beta3.InitConfiguration{}, 51 upstreamv1beta2.GroupVersion: &upstreamv1beta2.InitConfiguration{}, 52 } 53 54 joinConfigurationVersionTypeMap = map[schema.GroupVersion]conversion.Convertible{ 55 upstreamv1beta3.GroupVersion: &upstreamv1beta3.JoinConfiguration{}, 56 upstreamv1beta2.GroupVersion: &upstreamv1beta2.JoinConfiguration{}, 57 } 58 ) 59 60 // KubeVersionToKubeadmAPIGroupVersion maps a Kubernetes version to the correct Kubeadm API Group supported. 61 func KubeVersionToKubeadmAPIGroupVersion(v semver.Version) (schema.GroupVersion, error) { 62 switch { 63 case version.Compare(v, v1beta2KubeadmVersion, version.WithoutPreReleases()) < 0: 64 return schema.GroupVersion{}, errors.New("the bootstrap provider for kubeadm doesn't support Kubernetes version lower than v1.15.0") 65 case version.Compare(v, v1beta3KubeadmVersion, version.WithoutPreReleases()) < 0: 66 // NOTE: All the Kubernetes version >= v1.15 and < v1.22 should use the kubeadm API version v1beta2 67 return upstreamv1beta2.GroupVersion, nil 68 default: 69 // NOTE: All the Kubernetes version greater or equal to v1.22 should use the kubeadm API version v1beta3. 70 // Also future Kubernetes versions (not yet released at the time of writing this code) are going to use v1beta3, 71 // no matter if kubeadm API versions newer than v1beta3 could be introduced by those release. 72 // This is acceptable because v1beta3 will be supported by kubeadm until the deprecation cycle completes 73 // (9 months minimum after the deprecation date, not yet announced now); this gives Cluster API project time to 74 // introduce support for newer releases without blocking users to deploy newer version of Kubernetes. 75 return upstreamv1beta3.GroupVersion, nil 76 } 77 } 78 79 // MarshalClusterConfigurationForVersion converts a Cluster API ClusterConfiguration type to the kubeadm API type 80 // for the given Kubernetes Version. 81 // NOTE: This assumes Kubernetes Version equals to kubeadm version. 82 func MarshalClusterConfigurationForVersion(obj *bootstrapv1.ClusterConfiguration, version semver.Version) (string, error) { 83 return marshalForVersion(obj, version, clusterConfigurationVersionTypeMap) 84 } 85 86 // MarshalClusterStatusForVersion converts a Cluster API ClusterStatus type to the kubeadm API type 87 // for the given Kubernetes Version. 88 // NOTE: This assumes Kubernetes Version equals to kubeadm version. 89 func MarshalClusterStatusForVersion(obj *bootstrapv1.ClusterStatus, version semver.Version) (string, error) { 90 return marshalForVersion(obj, version, clusterStatusVersionTypeMap) 91 } 92 93 // MarshalInitConfigurationForVersion converts a Cluster API InitConfiguration type to the kubeadm API type 94 // for the given Kubernetes Version. 95 // NOTE: This assumes Kubernetes Version equals to kubeadm version. 96 func MarshalInitConfigurationForVersion(obj *bootstrapv1.InitConfiguration, version semver.Version) (string, error) { 97 return marshalForVersion(obj, version, initConfigurationVersionTypeMap) 98 } 99 100 // MarshalJoinConfigurationForVersion converts a Cluster API JoinConfiguration type to the kubeadm API type 101 // for the given Kubernetes Version. 102 // NOTE: This assumes Kubernetes Version equals to kubeadm version. 103 func MarshalJoinConfigurationForVersion(obj *bootstrapv1.JoinConfiguration, version semver.Version) (string, error) { 104 return marshalForVersion(obj, version, joinConfigurationVersionTypeMap) 105 } 106 107 func marshalForVersion(obj conversion.Hub, version semver.Version, kubeadmObjVersionTypeMap map[schema.GroupVersion]conversion.Convertible) (string, error) { 108 kubeadmAPIGroupVersion, err := KubeVersionToKubeadmAPIGroupVersion(version) 109 if err != nil { 110 return "", err 111 } 112 113 targetKubeadmObj, ok := kubeadmObjVersionTypeMap[kubeadmAPIGroupVersion] 114 if !ok { 115 return "", errors.Errorf("missing KubeadmAPI type mapping for version %s", kubeadmAPIGroupVersion) 116 } 117 118 targetKubeadmObj = targetKubeadmObj.DeepCopyObject().(conversion.Convertible) 119 if err := targetKubeadmObj.ConvertFrom(obj); err != nil { 120 return "", errors.Wrapf(err, "failed to convert to KubeadmAPI type for version %s", kubeadmAPIGroupVersion) 121 } 122 123 codecs, err := getCodecsFor(kubeadmAPIGroupVersion, targetKubeadmObj) 124 if err != nil { 125 return "", err 126 } 127 128 yaml, err := toYaml(targetKubeadmObj, kubeadmAPIGroupVersion, codecs) 129 if err != nil { 130 return "", errors.Wrapf(err, "failed to generate yaml for the Kubeadm API for version %s", kubeadmAPIGroupVersion) 131 } 132 return string(yaml), nil 133 } 134 135 func getCodecsFor(gv schema.GroupVersion, obj runtime.Object) (serializer.CodecFactory, error) { 136 sb := &scheme.Builder{GroupVersion: gv} 137 sb.Register(obj) 138 kubeadmScheme, err := sb.Build() 139 if err != nil { 140 return serializer.CodecFactory{}, errors.Wrapf(err, "failed to build scheme for kubeadm types conversions") 141 } 142 return serializer.NewCodecFactory(kubeadmScheme), nil 143 } 144 145 func toYaml(obj runtime.Object, gv runtime.GroupVersioner, codecs serializer.CodecFactory) ([]byte, error) { 146 info, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), runtime.ContentTypeYAML) 147 if !ok { 148 return []byte{}, errors.Errorf("unsupported media type %q", runtime.ContentTypeYAML) 149 } 150 151 encoder := codecs.EncoderForVersion(info.Serializer, gv) 152 return runtime.Encode(encoder, obj) 153 } 154 155 // UnmarshalClusterConfiguration tries to translate a Kubeadm API yaml back to the Cluster API ClusterConfiguration type. 156 // NOTE: The yaml could be any of the known formats for the kubeadm ClusterConfiguration type. 157 func UnmarshalClusterConfiguration(yaml string) (*bootstrapv1.ClusterConfiguration, error) { 158 obj := &bootstrapv1.ClusterConfiguration{} 159 if err := unmarshalFromVersions(yaml, clusterConfigurationVersionTypeMap, obj); err != nil { 160 return nil, err 161 } 162 return obj, nil 163 } 164 165 // UnmarshalClusterStatus tries to translate a Kubeadm API yaml back to the Cluster API ClusterStatus type. 166 // NOTE: The yaml could be any of the known formats for the kubeadm ClusterStatus type. 167 func UnmarshalClusterStatus(yaml string) (*bootstrapv1.ClusterStatus, error) { 168 obj := &bootstrapv1.ClusterStatus{} 169 if err := unmarshalFromVersions(yaml, clusterStatusVersionTypeMap, obj); err != nil { 170 return nil, err 171 } 172 return obj, nil 173 } 174 175 // UnmarshalInitConfiguration tries to translate a Kubeadm API yaml back to the InitConfiguration type. 176 // NOTE: The yaml could be any of the known formats for the kubeadm InitConfiguration type. 177 func UnmarshalInitConfiguration(yaml string) (*bootstrapv1.InitConfiguration, error) { 178 obj := &bootstrapv1.InitConfiguration{} 179 if err := unmarshalFromVersions(yaml, initConfigurationVersionTypeMap, obj); err != nil { 180 return nil, err 181 } 182 return obj, nil 183 } 184 185 // UnmarshalJoinConfiguration tries to translate a Kubeadm API yaml back to the JoinConfiguration type. 186 // NOTE: The yaml could be any of the known formats for the kubeadm JoinConfiguration type. 187 func UnmarshalJoinConfiguration(yaml string) (*bootstrapv1.JoinConfiguration, error) { 188 obj := &bootstrapv1.JoinConfiguration{} 189 if err := unmarshalFromVersions(yaml, joinConfigurationVersionTypeMap, obj); err != nil { 190 return nil, err 191 } 192 return obj, nil 193 } 194 195 func unmarshalFromVersions(yaml string, kubeadmAPIVersions map[schema.GroupVersion]conversion.Convertible, capiObj conversion.Hub) error { 196 // For each know kubeadm API version 197 for gv, obj := range kubeadmAPIVersions { 198 // Tries conversion from yaml to the corresponding kubeadmObj 199 kubeadmObj := obj.DeepCopyObject() 200 gvk := kubeadmObj.GetObjectKind().GroupVersionKind() 201 codecs, err := getCodecsFor(gv, kubeadmObj) 202 if err != nil { 203 return errors.Wrapf(err, "failed to build scheme for kubeadm types conversions") 204 } 205 206 _, _, err = codecs.UniversalDeserializer().Decode([]byte(yaml), &gvk, kubeadmObj) 207 if err == nil { 208 // If conversion worked, then converts the kubeadmObj (spoke) back to the Cluster API ClusterConfiguration type (hub). 209 if err := kubeadmObj.(conversion.Convertible).ConvertTo(capiObj); err != nil { 210 return errors.Wrapf(err, "failed to convert kubeadm types to Cluster API types") 211 } 212 return nil 213 } 214 } 215 return errors.New("unknown kubeadm types") 216 }