From f0970b6d645623145a33c682aa26cd890c4ec2f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20K=C3=A4ldstr=C3=B6m?= Date: Tue, 3 Jul 2018 21:27:33 +0300 Subject: [PATCH] Stop using/supporting the kubeadm v1alpha1 API --- cmd/kubeadm/app/apis/kubeadm/scheme/scheme.go | 6 +- cmd/kubeadm/app/apis/kubeadm/types.go | 10 --- cmd/kubeadm/app/cmd/config.go | 5 +- cmd/kubeadm/app/preflight/checks.go | 6 +- cmd/kubeadm/app/util/config/masterconfig.go | 71 ++++++---------- .../app/util/config/masterconfig_test.go | 83 +++---------------- cmd/kubeadm/app/util/config/nodeconfig.go | 12 ++- .../app/util/config/nodeconfig_test.go | 13 +-- cmd/kubeadm/app/util/marshal.go | 44 +++++----- cmd/kubeadm/app/util/marshal_test.go | 48 +++++------ 10 files changed, 97 insertions(+), 201 deletions(-) diff --git a/cmd/kubeadm/app/apis/kubeadm/scheme/scheme.go b/cmd/kubeadm/app/apis/kubeadm/scheme/scheme.go index 82f9c92cb13..54f9aa89e98 100644 --- a/cmd/kubeadm/app/apis/kubeadm/scheme/scheme.go +++ b/cmd/kubeadm/app/apis/kubeadm/scheme/scheme.go @@ -23,7 +23,6 @@ import ( "k8s.io/apimachinery/pkg/runtime/serializer" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" - "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2" ) @@ -38,10 +37,9 @@ func init() { AddToScheme(Scheme) } -// AddToScheme builds the Kubeadm scheme using all known versions of the kubeadm api. +// AddToScheme builds the kubeadm scheme using all known versions of the kubeadm api. func AddToScheme(scheme *runtime.Scheme) { utilruntime.Must(kubeadm.AddToScheme(scheme)) - utilruntime.Must(v1alpha1.AddToScheme(scheme)) utilruntime.Must(v1alpha2.AddToScheme(scheme)) - utilruntime.Must(scheme.SetVersionPriority(v1alpha1.SchemeGroupVersion)) + utilruntime.Must(scheme.SetVersionPriority(v1alpha2.SchemeGroupVersion)) } diff --git a/cmd/kubeadm/app/apis/kubeadm/types.go b/cmd/kubeadm/app/apis/kubeadm/types.go index 4391cfe6447..8f4807e9503 100644 --- a/cmd/kubeadm/app/apis/kubeadm/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/types.go @@ -17,8 +17,6 @@ limitations under the License. package kubeadm import ( - fuzz "github.com/google/gofuzz" - v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" kubeletconfigv1beta1 "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/v1beta1" @@ -201,14 +199,6 @@ type Etcd struct { External *ExternalEtcd } -// Fuzz is a dummy function here to get the roundtrip tests working in cmd/kubeadm/app/apis/kubeadm/fuzzer working. -// As we split the monolith-etcd struct into two smaller pieces with pointers and they are mutually exclusive, roundtrip -// tests that randomize all values in this struct isn't feasible. Instead, we override the fuzzing function for .Etcd with -// this func by letting Etcd implement the fuzz.Interface interface. As this func does nothing, we rely on the values given -// in fuzzer/fuzzer.go for the roundtrip tests, which is exactly what we want. -// TODO: Remove this function when we remove the v1alpha1 API -func (e Etcd) Fuzz(c fuzz.Continue) {} - // LocalEtcd describes that kubeadm should run an etcd cluster locally type LocalEtcd struct { diff --git a/cmd/kubeadm/app/cmd/config.go b/cmd/kubeadm/app/cmd/config.go index 91cb0963274..29107478e7d 100644 --- a/cmd/kubeadm/app/cmd/config.go +++ b/cmd/kubeadm/app/cmd/config.go @@ -31,7 +31,6 @@ import ( clientset "k8s.io/client-go/kubernetes" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme" - kubeadmapiv1alpha1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1" kubeadmapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2" cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util" "k8s.io/kubernetes/cmd/kubeadm/app/constants" @@ -161,7 +160,7 @@ func NewCmdConfigMigrate(out io.Writer) *cobra.Command { locally in the CLI tool without ever touching anything in the cluster. In this version of kubeadm, the following API versions are supported: - %s - - %s + - TODO: kubeadm.k8s.io/v1beta1 Further, kubeadm can only write out config of version %q, but read both types. So regardless of what version you pass to the --old-config parameter here, the API object will be @@ -170,7 +169,7 @@ func NewCmdConfigMigrate(out io.Writer) *cobra.Command { In other words, the output of this command is what kubeadm actually would read internally if you submitted this file to "kubeadm init" - `), kubeadmapiv1alpha2.SchemeGroupVersion.String(), kubeadmapiv1alpha1.SchemeGroupVersion.String(), kubeadmapiv1alpha2.SchemeGroupVersion.String()), + `), kubeadmapiv1alpha2.SchemeGroupVersion.String(), kubeadmapiv1alpha2.SchemeGroupVersion.String()), Run: func(cmd *cobra.Command, args []string) { if len(oldCfgPath) == 0 { kubeadmutil.CheckErr(fmt.Errorf("The --old-config flag is mandatory")) diff --git a/cmd/kubeadm/app/preflight/checks.go b/cmd/kubeadm/app/preflight/checks.go index 9eb799719fd..ceed3604fba 100644 --- a/cmd/kubeadm/app/preflight/checks.go +++ b/cmd/kubeadm/app/preflight/checks.go @@ -42,7 +42,7 @@ import ( netutil "k8s.io/apimachinery/pkg/util/net" "k8s.io/apimachinery/pkg/util/sets" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" - kubeadmdefaults "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1" + kubeadmapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" "k8s.io/kubernetes/cmd/kubeadm/app/images" "k8s.io/kubernetes/pkg/registry/core/service/ipallocator" @@ -533,7 +533,7 @@ func (sysver SystemVerificationCheck) Check() (warnings, errors []error) { &system.KernelValidator{Reporter: reporter}} // run the docker validator only with dockershim - if sysver.CRISocket == kubeadmdefaults.DefaultCRISocket { + if sysver.CRISocket == kubeadmapiv1alpha2.DefaultCRISocket { // https://github.com/kubernetes/kubeadm/issues/533 validators = append(validators, &system.DockerValidator{Reporter: reporter}) } @@ -958,7 +958,7 @@ func RunJoinNodeChecks(execer utilsexec.Interface, cfg *kubeadmapi.NodeConfigura // kubeadm init and join commands func addCommonChecks(execer utilsexec.Interface, cfg kubeadmapi.CommonConfiguration, checks []Checker) []Checker { // Check whether or not the CRI socket defined is the default - if cfg.GetCRISocket() != kubeadmdefaults.DefaultCRISocket { + if cfg.GetCRISocket() != kubeadmapiv1alpha2.DefaultCRISocket { checks = append(checks, CRICheck{socket: cfg.GetCRISocket(), exec: execer}) } else { checks = append(checks, ServiceCheck{Service: "docker", CheckIfActive: true}) diff --git a/cmd/kubeadm/app/util/config/masterconfig.go b/cmd/kubeadm/app/util/config/masterconfig.go index 03ef0374372..4dace2edc71 100644 --- a/cmd/kubeadm/app/util/config/masterconfig.go +++ b/cmd/kubeadm/app/util/config/masterconfig.go @@ -30,7 +30,6 @@ import ( bootstraputil "k8s.io/client-go/tools/bootstrap/token/util" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme" - kubeadmapiv1alpha1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1" kubeadmapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" @@ -131,51 +130,12 @@ func ConfigFileAndDefaultsToInternalConfig(cfgPath string, defaultversionedcfg * func BytesToInternalConfig(b []byte) (*kubeadmapi.MasterConfiguration, error) { internalcfg := &kubeadmapi.MasterConfiguration{} - decoded, err := kubeadmutil.LoadYAML(b) - if err != nil { - return nil, fmt.Errorf("unable to decode config from bytes: %v", err) + if err := DetectUnsupportedVersion(b); err != nil { + return nil, err } - // As there was a bug in kubeadm v1.10 and earlier that made the YAML uploaded to the cluster configmap NOT have metav1.TypeMeta information - // we need to populate this here manually. If kind or apiVersion is empty, we know the apiVersion is v1alpha1, as by the time kubeadm had this bug, - // it could only write - // TODO: Remove this "hack" in v1.12 when we know the ConfigMap always contains v1alpha2 content written by kubeadm v1.11. Also, we will drop support for - // v1alpha1 in v1.12 - kind := decoded["kind"] - apiVersion := decoded["apiVersion"] - if kind == nil || len(kind.(string)) == 0 { - decoded["kind"] = "MasterConfiguration" - } - if apiVersion == nil || len(apiVersion.(string)) == 0 { - decoded["apiVersion"] = kubeadmapiv1alpha1.SchemeGroupVersion.String() - } - - // Between v1.9 and v1.10 the proxy componentconfig in the v1alpha1 MasterConfiguration changed unexpectedly, which broke unmarshalling out-of-the-box - // Hence, we need to workaround this bug in the v1alpha1 API - if decoded["apiVersion"] == kubeadmapiv1alpha1.SchemeGroupVersion.String() { - v1alpha1cfg := &kubeadmapiv1alpha1.MasterConfiguration{} - if err := kubeadmapiv1alpha1.Migrate(decoded, v1alpha1cfg, kubeadmscheme.Codecs); err != nil { - return nil, fmt.Errorf("unable to migrate config from previous version: %v", err) - } - - // Default and convert to the internal version - kubeadmscheme.Scheme.Default(v1alpha1cfg) - kubeadmscheme.Scheme.Convert(v1alpha1cfg, internalcfg, nil) - } else if decoded["apiVersion"] == kubeadmapiv1alpha2.SchemeGroupVersion.String() { - v1alpha2cfg := &kubeadmapiv1alpha2.MasterConfiguration{} - if err := runtime.DecodeInto(kubeadmscheme.Codecs.UniversalDecoder(), b, v1alpha2cfg); err != nil { - return nil, fmt.Errorf("unable to decode config: %v", err) - } - - // Default and convert to the internal version - kubeadmscheme.Scheme.Default(v1alpha2cfg) - kubeadmscheme.Scheme.Convert(v1alpha2cfg, internalcfg, nil) - } else { - // TODO: Add support for an upcoming v1alpha2 API - // TODO: In the future, we can unmarshal any two or more external types into the internal object directly using the following syntax. - // Long-term we don't need this if/else clause. In the future this will do - // runtime.DecodeInto(kubeadmscheme.Codecs.UniversalDecoder(kubeadmapiv1alpha2.SchemeGroupVersion, kubeadmapiv2alpha3.SchemeGroupVersion), b, internalcfg) - return nil, fmt.Errorf("unknown API version for kubeadm configuration") + if err := runtime.DecodeInto(kubeadmscheme.Codecs.UniversalDecoder(kubeadmapiv1alpha2.SchemeGroupVersion), b, internalcfg); err != nil { + return nil, err } return defaultAndValidate(internalcfg) @@ -193,6 +153,29 @@ func defaultAndValidate(cfg *kubeadmapi.MasterConfiguration) (*kubeadmapi.Master return cfg, nil } +// DetectUnsupportedVersion reads YAML bytes, extracts the TypeMeta information and errors out with an user-friendly message if the API spec is too old for this kubeadm version +func DetectUnsupportedVersion(b []byte) error { + apiVersionStr, _, err := kubeadmutil.ExtractAPIVersionAndKindFromYAML(b) + if err != nil { + return err + } + + // TODO: On our way to making the kubeadm API beta and higher, give good user output in case they use an old config file with a new kubeadm version, and + // tell them how to upgrade. The support matrix will look something like this now and in the future: + // v1.10 and earlier: v1alpha1 + // v1.11: v1alpha1 read-only, writes only v1alpha2 config + // v1.12: v1alpha2 read-only, writes only v1beta1 config. Warns if the user tries to use v1alpha1 + // v1.13 and v1.14: v1beta1 read-only, writes only v1 config. Warns if the user tries to use v1alpha1 or v1alpha2. + // v1.15: v1 is the only supported format. + oldKnownAPIVersions := map[string]string{ + "kubeadm.k8s.io/v1alpha1": "v1.11", + } + if useKubeadmVersion := oldKnownAPIVersions[apiVersionStr]; len(useKubeadmVersion) != 0 { + return fmt.Errorf("your configuration file seem to use an old API spec. Please use kubeadm %s instead and run 'kubeadm config migrate --old-config old.yaml --new-config new.yaml', which will write the new, similar spec using a newer API version.", useKubeadmVersion) + } + return nil +} + // NormalizeKubernetesVersion resolves version labels, sets alternative // image registry if requested for CI builds, and validates minimal // version that kubeadm supports. diff --git a/cmd/kubeadm/app/util/config/masterconfig_test.go b/cmd/kubeadm/app/util/config/masterconfig_test.go index a5c70038b36..e6317ce58b3 100644 --- a/cmd/kubeadm/app/util/config/masterconfig_test.go +++ b/cmd/kubeadm/app/util/config/masterconfig_test.go @@ -22,28 +22,20 @@ import ( "testing" "github.com/pmezard/go-difflib/difflib" - "github.com/stretchr/testify/require" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme" - "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" ) const ( - master_v1alpha1YAML = "testdata/conversion/master/v1alpha1.yaml" - master_v1alpha1WithoutTypeMetaYAML = "testdata/conversion/master/v1alpha1_without_TypeMeta.yaml" - master_v1alpha2YAML = "testdata/conversion/master/v1alpha2.yaml" - master_internalYAML = "testdata/conversion/master/internal.yaml" - master_incompleteYAML = "testdata/defaulting/master/incomplete.yaml" - master_defaultedYAML = "testdata/defaulting/master/defaulted.yaml" - master_invalidYAML = "testdata/validation/invalid_mastercfg.yaml" - master_beforeUpgradeYAML = "testdata/v1alpha1_upgrade/before.yaml" - master_afterUpgradeYAML = "testdata/v1alpha1_upgrade/after.yaml" + master_v1alpha2YAML = "testdata/conversion/master/v1alpha2.yaml" + master_internalYAML = "testdata/conversion/master/internal.yaml" + master_incompleteYAML = "testdata/defaulting/master/incomplete.yaml" + master_defaultedYAML = "testdata/defaulting/master/defaulted.yaml" + master_invalidYAML = "testdata/validation/invalid_mastercfg.yaml" ) func diff(expected, actual []byte) string { @@ -67,45 +59,27 @@ func TestConfigFileAndDefaultsToInternalConfig(t *testing.T) { }{ // These tests are reading one file, loading it using ConfigFileAndDefaultsToInternalConfig that all of kubeadm is using for unmarshal of our API types, // and then marshals the internal object to the expected groupVersion - { // v1alpha1 (faulty) -> internal - name: "v1alpha1WithoutTypeMetaToInternal", - in: master_v1alpha1WithoutTypeMetaYAML, - out: master_internalYAML, - groupVersion: kubeadm.SchemeGroupVersion, - }, - { // v1alpha1 -> internal - name: "v1alpha1ToInternal", - in: master_v1alpha1YAML, - out: master_internalYAML, - groupVersion: kubeadm.SchemeGroupVersion, - }, { // v1alpha2 -> internal name: "v1alpha2ToInternal", in: master_v1alpha2YAML, out: master_internalYAML, groupVersion: kubeadm.SchemeGroupVersion, }, - { // v1alpha1 (faulty) -> internal -> v1alpha2 - name: "v1alpha1WithoutTypeMetaTov1alpha2", - in: master_v1alpha1WithoutTypeMetaYAML, - out: master_v1alpha2YAML, - groupVersion: v1alpha2.SchemeGroupVersion, - }, - { // v1alpha1 -> internal -> v1alpha2 - name: "v1alpha1Tov1alpha2", - in: master_v1alpha1YAML, + { // v1alpha2 -> internal -> v1alpha2 + name: "v1alpha2Tov1alpha2", + in: master_v1alpha2YAML, out: master_v1alpha2YAML, groupVersion: v1alpha2.SchemeGroupVersion, }, // These tests are reading one file that has only a subset of the fields populated, loading it using ConfigFileAndDefaultsToInternalConfig, // and then marshals the internal object to the expected groupVersion - { // v1alpha1 (faulty) -> default -> validate -> internal -> v1alpha2 + { // v1alpha2 -> default -> validate -> internal -> v1alpha2 name: "incompleteYAMLToDefaultedv1alpha2", in: master_incompleteYAML, out: master_defaultedYAML, groupVersion: v1alpha2.SchemeGroupVersion, }, - { // v1alpha1 (faulty) -> validation should fail + { // v1alpha2 -> validation should fail name: "invalidYAMLShouldFail", in: master_invalidYAML, expectedErr: true, @@ -141,43 +115,6 @@ func TestConfigFileAndDefaultsToInternalConfig(t *testing.T) { } } -// TestUpgrade tests reading a faulty YAML representation of the MasterConfiguration object (as found in kubeadm clusters <= v1.9.x), -// fixes the problems internally and verifies the marshalled output is the expected output -func TestUpgrade(t *testing.T) { - before, err := ioutil.ReadFile(master_beforeUpgradeYAML) - if err != nil { - t.Fatalf("couldn't read test data: %v", err) - } - - afterExpected, err := ioutil.ReadFile(master_afterUpgradeYAML) - if err != nil { - t.Fatalf("couldn't read test data: %v", err) - } - - decoded, err := kubeadmutil.LoadYAML(before) - if err != nil { - t.Fatalf("couldn't unmarshal test yaml: %v", err) - } - - scheme := runtime.NewScheme() - require.NoError(t, v1alpha1.AddToScheme(scheme)) - codecs := serializer.NewCodecFactory(scheme) - - obj := &v1alpha1.MasterConfiguration{} - if err := v1alpha1.Migrate(decoded, obj, codecs); err != nil { - t.Fatalf("couldn't decode migrated object: %v", err) - } - - afterActual, err := kubeadmutil.MarshalToYamlForCodecs(obj, v1alpha1.SchemeGroupVersion, codecs) - if err != nil { - t.Fatalf("couldn't marshal object: %v", err) - } - - if !bytes.Equal(afterExpected, afterActual) { - t.Errorf("v1alpha1 object after unmarshal, conversion and marshal didn't match expected value.\n\tdiff: \n%s\n", diff(afterExpected, afterActual)) - } -} - func TestLowercaseSANs(t *testing.T) { tests := []struct { name string diff --git a/cmd/kubeadm/app/util/config/nodeconfig.go b/cmd/kubeadm/app/util/config/nodeconfig.go index 77bcf8b32f6..bd66627b1f9 100644 --- a/cmd/kubeadm/app/util/config/nodeconfig.go +++ b/cmd/kubeadm/app/util/config/nodeconfig.go @@ -25,7 +25,6 @@ import ( "k8s.io/apimachinery/pkg/runtime" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme" - kubeadmapiv1alpha1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1" kubeadmapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation" "k8s.io/kubernetes/pkg/util/node" @@ -43,14 +42,21 @@ func NodeConfigFileAndDefaultsToInternalConfig(cfgPath string, defaultversionedc if cfgPath != "" { // Loads configuration from config file, if provided - // Nb. --config overrides command line flags + // Nb. --config overrides command line flags, TODO: fix this glog.V(1).Infoln("loading configuration from the given file") b, err := ioutil.ReadFile(cfgPath) if err != nil { return nil, fmt.Errorf("unable to read config from %q [%v]", cfgPath, err) } - runtime.DecodeInto(kubeadmscheme.Codecs.UniversalDecoder(kubeadmapiv1alpha1.SchemeGroupVersion, kubeadmapiv1alpha2.SchemeGroupVersion), b, internalcfg) + + if err := DetectUnsupportedVersion(b); err != nil { + return nil, err + } + + if err := runtime.DecodeInto(kubeadmscheme.Codecs.UniversalDecoder(kubeadmapiv1alpha2.SchemeGroupVersion), b, internalcfg); err != nil { + return nil, err + } } else { // Takes passed flags into account; the defaulting is executed once again enforcing assignement of // static default values to cfg only for values not provided with flags diff --git a/cmd/kubeadm/app/util/config/nodeconfig_test.go b/cmd/kubeadm/app/util/config/nodeconfig_test.go index dcf679815a0..739cfb7d77f 100644 --- a/cmd/kubeadm/app/util/config/nodeconfig_test.go +++ b/cmd/kubeadm/app/util/config/nodeconfig_test.go @@ -29,7 +29,6 @@ import ( ) const ( - node_v1alpha1YAML = "testdata/conversion/node/v1alpha1.yaml" node_v1alpha2YAML = "testdata/conversion/node/v1alpha2.yaml" node_internalYAML = "testdata/conversion/node/internal.yaml" node_incompleteYAML = "testdata/defaulting/node/incomplete.yaml" @@ -45,21 +44,15 @@ func TestNodeConfigFileAndDefaultsToInternalConfig(t *testing.T) { }{ // These tests are reading one file, loading it using NodeConfigFileAndDefaultsToInternalConfig that all of kubeadm is using for unmarshal of our API types, // and then marshals the internal object to the expected groupVersion - { // v1alpha1 -> internal - name: "v1alpha1ToInternal", - in: node_v1alpha1YAML, - out: node_internalYAML, - groupVersion: kubeadm.SchemeGroupVersion, - }, { // v1alpha2 -> internal name: "v1alpha2ToInternal", in: node_v1alpha2YAML, out: node_internalYAML, groupVersion: kubeadm.SchemeGroupVersion, }, - { // v1alpha1 -> internal -> v1alpha2 - name: "v1alpha1WithoutTypeMetaTov1alpha2", - in: node_v1alpha1YAML, + { // v1alpha2 -> internal -> v1alpha2 + name: "v1alpha2Tov1alpha2", + in: node_v1alpha2YAML, out: node_v1alpha2YAML, groupVersion: v1alpha2.SchemeGroupVersion, }, diff --git a/cmd/kubeadm/app/util/marshal.go b/cmd/kubeadm/app/util/marshal.go index aa201cabb7b..c5a1b01611c 100644 --- a/cmd/kubeadm/app/util/marshal.go +++ b/cmd/kubeadm/app/util/marshal.go @@ -26,7 +26,6 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/serializer" clientsetscheme "k8s.io/client-go/kubernetes/scheme" - kubeadmapiv1alpha1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1" ) // MarshalToYaml marshals an object into yaml. @@ -67,37 +66,36 @@ func UnmarshalFromYamlForCodecs(buffer []byte, gv schema.GroupVersion, codecs se return runtime.Decode(decoder, buffer) } -// GroupVersionKindFromBytes parses the bytes and returns the gvk -func GroupVersionKindFromBytes(buffer []byte, codecs serializer.CodecFactory) (schema.GroupVersionKind, error) { - - decoded, err := LoadYAML(buffer) +// ExtractAPIVersionAndKindFromYAML extracts the APIVersion and Kind fields from YAML bytes +func ExtractAPIVersionAndKindFromYAML(b []byte) (string, string, error) { + decoded, err := LoadYAML(b) if err != nil { - return schema.EmptyObjectKind.GroupVersionKind(), fmt.Errorf("unable to decode config from bytes: %v", err) + return "", "", fmt.Errorf("unable to decode config from bytes: %v", err) } - kindStr, apiVersionStr := "", "" - // As there was a bug in kubeadm v1.10 and earlier that made the YAML uploaded to the cluster configmap NOT have metav1.TypeMeta information - // we need to populate this here manually. If kind or apiVersion is empty, we know the apiVersion is v1alpha1, as by the time kubeadm had this bug, - // it could only write - // TODO: Remove this "hack" in v1.12 when we know the ConfigMap always contains v1alpha2 content written by kubeadm v1.11. Also, we will drop support for - // v1alpha1 in v1.12 - kind := decoded["kind"] - apiVersion := decoded["apiVersion"] - if kind == nil || len(kind.(string)) == 0 { - kindStr = "MasterConfiguration" - } else { - kindStr = kind.(string) + kindStr, ok := decoded["kind"].(string) + if !ok || len(kindStr) == 0 { + return "", "", fmt.Errorf("any config file must have the kind field set") } - if apiVersion == nil || len(apiVersion.(string)) == 0 { - apiVersionStr = kubeadmapiv1alpha1.SchemeGroupVersion.String() - } else { - apiVersionStr = apiVersion.(string) + apiVersionStr, ok := decoded["apiVersion"].(string) + if !ok || len(apiVersionStr) == 0 { + return "", "", fmt.Errorf("any config file must have the apiVersion field set") } + return apiVersionStr, kindStr, nil +} + +// GroupVersionKindFromBytes parses the bytes and returns the gvk +// TODO: Find a better way to do this, invoking the API machinery directly without first loading the yaml manually +func GroupVersionKindFromBytes(b []byte, codecs serializer.CodecFactory) (schema.GroupVersionKind, error) { + apiVersionStr, kindStr, err := ExtractAPIVersionAndKindFromYAML(b) + if err != nil { + return schema.EmptyObjectKind.GroupVersionKind(), err + } + gv, err := schema.ParseGroupVersion(apiVersionStr) if err != nil { return schema.EmptyObjectKind.GroupVersionKind(), fmt.Errorf("unable to parse apiVersion: %v", err) } - return gv.WithKind(kindStr), nil } diff --git a/cmd/kubeadm/app/util/marshal_test.go b/cmd/kubeadm/app/util/marshal_test.go index 33c37afc5ab..af2ee64ef9c 100644 --- a/cmd/kubeadm/app/util/marshal_test.go +++ b/cmd/kubeadm/app/util/marshal_test.go @@ -17,13 +17,14 @@ limitations under the License. package util import ( + "encoding/json" "reflect" "testing" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme" - kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1" + kubeadmapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2" ) func TestMarshalUnmarshalYaml(t *testing.T) { @@ -75,51 +76,42 @@ func TestMarshalUnmarshalYaml(t *testing.T) { } func TestMarshalUnmarshalToYamlForCodecs(t *testing.T) { - cfg := &kubeadmapiext.MasterConfiguration{ - API: kubeadmapiext.API{ + cfg := &kubeadmapiv1alpha2.MasterConfiguration{ + TypeMeta: metav1.TypeMeta{ + Kind: "MasterConfiguration", + APIVersion: kubeadmapiv1alpha2.SchemeGroupVersion.String(), + }, + API: kubeadmapiv1alpha2.API{ AdvertiseAddress: "10.100.0.1", BindPort: 4332, }, - NodeName: "testNode", - NoTaintMaster: true, - Networking: kubeadmapiext.Networking{ + NodeRegistration: kubeadmapiv1alpha2.NodeRegistrationOptions{ + Name: "testNode", + CRISocket: "/var/run/cri.sock", + }, + Networking: kubeadmapiv1alpha2.Networking{ ServiceSubnet: "10.100.0.0/24", PodSubnet: "10.100.1.0/24", }, } + scheme.Scheme.Default(cfg) - bytes, err := MarshalToYamlForCodecs(cfg, kubeadmapiext.SchemeGroupVersion, scheme.Codecs) + bytes, err := MarshalToYamlForCodecs(cfg, kubeadmapiv1alpha2.SchemeGroupVersion, scheme.Codecs) if err != nil { t.Fatalf("unexpected error marshalling MasterConfiguration: %v", err) } t.Logf("\n%s", bytes) - obj, err := UnmarshalFromYamlForCodecs(bytes, kubeadmapiext.SchemeGroupVersion, scheme.Codecs) + obj, err := UnmarshalFromYamlForCodecs(bytes, kubeadmapiv1alpha2.SchemeGroupVersion, scheme.Codecs) if err != nil { t.Fatalf("unexpected error unmarshalling MasterConfiguration: %v", err) } - cfg2, ok := obj.(*kubeadmapiext.MasterConfiguration) - if !ok { + cfg2, ok := obj.(*kubeadmapiv1alpha2.MasterConfiguration) + if !ok || cfg2 == nil { t.Fatal("did not get MasterConfiguration back") } - - if cfg2.API.AdvertiseAddress != cfg.API.AdvertiseAddress { - t.Errorf("expected %q, got %q", cfg.API.AdvertiseAddress, cfg2.API.AdvertiseAddress) - } - if cfg2.API.BindPort != cfg.API.BindPort { - t.Errorf("expected %d, got %d", cfg.API.BindPort, cfg2.API.BindPort) - } - if cfg2.NodeName != cfg.NodeName { - t.Errorf("expected %q, got %q", cfg.NodeName, cfg2.NodeName) - } - if cfg2.NoTaintMaster != cfg.NoTaintMaster { - t.Errorf("expected %v, got %v", cfg.NoTaintMaster, cfg2.NoTaintMaster) - } - if cfg2.Networking.ServiceSubnet != cfg.Networking.ServiceSubnet { - t.Errorf("expected %v, got %v", cfg.Networking.ServiceSubnet, cfg2.Networking.ServiceSubnet) - } - if cfg2.Networking.PodSubnet != cfg.Networking.PodSubnet { - t.Errorf("expected %v, got %v", cfg.Networking.PodSubnet, cfg2.Networking.PodSubnet) + if !reflect.DeepEqual(*cfg, *cfg2) { + t.Errorf("expected %v, got %v", *cfg, *cfg2) } }