Skip to content

Commit

Permalink
Make CABPK use conversions for generating the kubeadm config for the …
Browse files Browse the repository at this point in the history
…target Kubernetes version
  • Loading branch information
fabriziopandini committed Mar 11, 2021
1 parent dfc8b97 commit 15a6ffb
Show file tree
Hide file tree
Showing 5 changed files with 615 additions and 285 deletions.
8 changes: 4 additions & 4 deletions bootstrap/kubeadm/controllers/kubeadmconfig_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ func (r *KubeadmConfigReconciler) handleClusterNotInitialized(ctx context.Contex
},
}
}
initdata, err := kubeadmv1beta1.ConfigurationToYAMLForVersion(scope.Config.Spec.InitConfiguration, scope.ConfigOwner.KubernetesVersion())
initdata, err := kubeadmtypes.MarshalInitConfigurationForVersion(scope.Config.Spec.InitConfiguration, kubernetesVersion)
if err != nil {
scope.Error(err, "Failed to marshal init configuration")
return ctrl.Result{}, err
Expand All @@ -393,7 +393,7 @@ func (r *KubeadmConfigReconciler) handleClusterNotInitialized(ctx context.Contex
// injects into config.ClusterConfiguration values from top level object
r.reconcileTopLevelObjectSettings(ctx, scope.Cluster, machine, scope.Config)

clusterdata, err := kubeadmv1beta1.ConfigurationToYAMLForVersion(scope.Config.Spec.ClusterConfiguration, scope.ConfigOwner.KubernetesVersion())
clusterdata, err := kubeadmtypes.MarshalClusterConfigurationForVersion(scope.Config.Spec.ClusterConfiguration, kubernetesVersion)
if err != nil {
scope.Error(err, "Failed to marshal cluster configuration")
return ctrl.Result{}, err
Expand Down Expand Up @@ -475,7 +475,7 @@ func (r *KubeadmConfigReconciler) joinWorker(ctx context.Context, scope *Scope)
return res, nil
}

joinData, err := kubeadmv1beta1.ConfigurationToYAMLForVersion(scope.Config.Spec.JoinConfiguration, scope.ConfigOwner.KubernetesVersion())
joinData, err := kubeadmtypes.MarshalJoinConfigurationForVersion(scope.Config.Spec.JoinConfiguration, scope.ConfigOwner.KubernetesVersion())
if err != nil {
scope.Error(err, "Failed to marshal join configuration")
return ctrl.Result{}, err
Expand Down Expand Up @@ -556,7 +556,7 @@ func (r *KubeadmConfigReconciler) joinControlplane(ctx context.Context, scope *S
return res, nil
}

joinData, err := kubeadmv1beta1.ConfigurationToYAMLForVersion(scope.Config.Spec.JoinConfiguration, scope.ConfigOwner.KubernetesVersion())
joinData, err := kubeadmtypes.MarshalJoinConfigurationForVersion(scope.Config.Spec.JoinConfiguration, scope.ConfigOwner.KubernetesVersion())
if err != nil {
scope.Error(err, "Failed to marshal join configuration")
return ctrl.Result{}, err
Expand Down
195 changes: 195 additions & 0 deletions bootstrap/kubeadm/types/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
/*
Copyright 2021 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package utils

import (
"github.com/pkg/errors"
runtime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
versionutil "k8s.io/apimachinery/pkg/util/version"
bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1alpha4"
"sigs.k8s.io/cluster-api/bootstrap/kubeadm/types/v1beta1"
"sigs.k8s.io/cluster-api/bootstrap/kubeadm/types/v1beta2"
"sigs.k8s.io/controller-runtime/pkg/conversion"
"sigs.k8s.io/controller-runtime/pkg/scheme"
)

var (
clusterConfigurationVersionTypeMap = map[schema.GroupVersion]conversion.Convertible{
v1beta2.GroupVersion: &v1beta2.ClusterConfiguration{},
v1beta1.GroupVersion: &v1beta1.ClusterConfiguration{},
}

clusterStatusVersionTypeMap = map[schema.GroupVersion]conversion.Convertible{
v1beta2.GroupVersion: &v1beta2.ClusterStatus{},
v1beta1.GroupVersion: &v1beta1.ClusterStatus{},
}

initConfigurationVersionTypeMap = map[schema.GroupVersion]conversion.Convertible{
v1beta2.GroupVersion: &v1beta2.InitConfiguration{},
v1beta1.GroupVersion: &v1beta1.InitConfiguration{},
}

joinConfigurationVersionTypeMap = map[schema.GroupVersion]conversion.Convertible{
v1beta2.GroupVersion: &v1beta2.JoinConfiguration{},
v1beta1.GroupVersion: &v1beta1.JoinConfiguration{},
}
)

func KubeVersionToKubeadmAPIGroupVersion(version string) (schema.GroupVersion, error) {
if version == "" {
return schema.GroupVersion{}, errors.New("version cannot be empty")
}
semVersion, err := versionutil.ParseSemantic(version)
if err != nil {
return schema.GroupVersion{}, errors.Wrap(err, "error parsing the Kubernetes version")
}
switch {
case semVersion.LessThan(versionutil.MustParseSemantic("v1.13.0")):
return schema.GroupVersion{}, errors.New("the bootstrap provider for kubeadm doesn't support Kubernetes version lower than v1.13.0")
case semVersion.LessThan(versionutil.MustParseSemantic("v1.15.0")):
// NOTE: All the Kubernetes version >= v1.13 and < v1.15 should use the kubeadm API version v1beta1
return v1beta1.GroupVersion, nil
default:
// NOTE: All the Kubernetes version greater or equal to v1.15 should use the kubeadm API version v1beta2.
// Also future Kubernetes versions (not yet released at the time of writing this code) are going to use v1beta2,
// no matter if kubeadm API versions newer than v1beta2 could be introduced by those release.
// This is acceptable because but v1beta2 will be supported by kubeadm until the deprecation cycle completes
// (9 months minimum after the deprecation date, not yet announced now); this gives Cluster API project time to
// introduce support for newer releases without blocking users to deploy newer version of Kubernetes.
return v1beta2.GroupVersion, nil
}
}

// MarshalClusterConfigurationForVersion converts a Cluster API ClusterConfiguration type to the kubeadm API type
// for the given Kubernetes Version.
// NOTE: This assumes Kubernetes Version equals to kubeadm version.
func MarshalClusterConfigurationForVersion(obj *bootstrapv1.ClusterConfiguration, version string) (string, error) {
return marshalForVersion(obj, version, clusterConfigurationVersionTypeMap)
}

// MarshalClusterStatusForVersion converts a Cluster API ClusterStatus type to the kubeadm API type
// for the given Kubernetes Version.
// NOTE: This assumes Kubernetes Version equals to kubeadm version.
func MarshalClusterStatusForVersion(obj *bootstrapv1.ClusterStatus, version string) (string, error) {
return marshalForVersion(obj, version, clusterStatusVersionTypeMap)
}

// MarshalInitConfigurationForVersion converts a Cluster API InitConfiguration type to the kubeadm API type
// for the given Kubernetes Version.
// NOTE: This assumes Kubernetes Version equals to kubeadm version.
func MarshalInitConfigurationForVersion(obj *bootstrapv1.InitConfiguration, version string) (string, error) {
return marshalForVersion(obj, version, initConfigurationVersionTypeMap)
}

// MarshalJoinConfigurationForVersion converts a Cluster API JoinConfiguration type to the kubeadm API type
// for the given Kubernetes Version.
// NOTE: This assumes Kubernetes Version equals to kubeadm version.
func MarshalJoinConfigurationForVersion(obj *bootstrapv1.JoinConfiguration, version string) (string, error) {
return marshalForVersion(obj, version, joinConfigurationVersionTypeMap)
}

func marshalForVersion(obj conversion.Hub, version string, kubeadmObjVersionTypeMap map[schema.GroupVersion]conversion.Convertible) (string, error) {
kubeadmAPIGroupVersion, err := KubeVersionToKubeadmAPIGroupVersion(version)
if err != nil {
return "", err
}

targetKubeadmObj, ok := kubeadmObjVersionTypeMap[kubeadmAPIGroupVersion]
if !ok {
return "", errors.Errorf("missing KubeadmAPI type mapping for version %s", kubeadmAPIGroupVersion)
}

targetKubeadmObj = targetKubeadmObj.DeepCopyObject().(conversion.Convertible)
if err := targetKubeadmObj.ConvertFrom(obj); err != nil {
return "", errors.Wrapf(err, "failed to convert to KubeadmAPI type for version %s", kubeadmAPIGroupVersion)
}

codecs, err := getCodecsFor(kubeadmAPIGroupVersion, targetKubeadmObj)
if err != nil {
return "", err
}

yaml, err := toYaml(targetKubeadmObj, kubeadmAPIGroupVersion, codecs)
if err != nil {
return "", errors.Wrapf(err, "failed to generate yaml for the Kubeadm API for version %s", kubeadmAPIGroupVersion)
}
return string(yaml), nil
}

func getCodecsFor(gv schema.GroupVersion, obj runtime.Object) (serializer.CodecFactory, error) {
sb := &scheme.Builder{GroupVersion: gv}
sb.Register(obj)
kubeadmScheme, err := sb.Build()
if err != nil {
return serializer.CodecFactory{}, errors.Wrapf(err, "failed to build scheme for kubeadm types conversions")
}
return serializer.NewCodecFactory(kubeadmScheme), nil
}

func toYaml(obj runtime.Object, gv runtime.GroupVersioner, codecs serializer.CodecFactory) ([]byte, error) {
info, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), runtime.ContentTypeYAML)
if !ok {
return []byte{}, errors.Errorf("unsupported media type %q", runtime.ContentTypeYAML)
}

encoder := codecs.EncoderForVersion(info.Serializer, gv)
return runtime.Encode(encoder, obj)
}

// UnmarshalClusterConfiguration tries to translate a Kubeadm API yaml back to the Cluster API ClusterConfiguration type.
// NOTE: The yaml could be any of the known formats for the kubeadm ClusterConfiguration type.
func UnmarshalClusterConfiguration(yaml string) (*bootstrapv1.ClusterConfiguration, error) {
obj := &bootstrapv1.ClusterConfiguration{}
if err := unmarshalFromVersions(yaml, clusterConfigurationVersionTypeMap, obj); err != nil {
return nil, err
}
return obj, nil
}

// UnmarshalClusterStatus tries to translate a Kubeadm API yaml back to the Cluster API ClusterStatus type.
// NOTE: The yaml could be any of the known formats for the kubeadm ClusterStatus type.
func UnmarshalClusterStatus(yaml string) (*bootstrapv1.ClusterStatus, error) {
obj := &bootstrapv1.ClusterStatus{}
if err := unmarshalFromVersions(yaml, clusterStatusVersionTypeMap, obj); err != nil {
return nil, err
}
return obj, nil
}

func unmarshalFromVersions(yaml string, kubeadmAPIVersions map[schema.GroupVersion]conversion.Convertible, capiObj conversion.Hub) error {
// For each know kubeadm API version
for gv, obj := range kubeadmAPIVersions {
// Tries conversion from yaml to the corresponding kubeadmObj
kubeadmObj := obj.DeepCopyObject()
gvk := kubeadmObj.GetObjectKind().GroupVersionKind()
codecs, err := getCodecsFor(gv, kubeadmObj)
if err != nil {
return errors.Wrapf(err, "failed to build scheme for kubeadm types conversions")
}

if _, _, err := codecs.UniversalDeserializer().Decode([]byte(yaml), &gvk, kubeadmObj); err == nil {
// If conversion worked, then converts the kubeadmObj (spoke) back to the Cluster API ClusterConfiguration type (hub).
if err := kubeadmObj.(conversion.Convertible).ConvertTo(capiObj); err != nil {
return errors.Wrapf(err, "failed to convert kubeadm types to Cluster API types")
}
return nil
}
}
return errors.New("unknown kubeadm types")
}
Loading

0 comments on commit 15a6ffb

Please sign in to comment.