Stop using/supporting the kubeadm v1alpha1 API

This commit is contained in:
Lucas Käldström 2018-07-03 21:27:33 +03:00
parent 2a54f83ef1
commit f0970b6d64
No known key found for this signature in database
GPG Key ID: 3FA3783D77751514
10 changed files with 97 additions and 201 deletions

View File

@ -23,7 +23,6 @@ import (
"k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/apimachinery/pkg/runtime/serializer"
utilruntime "k8s.io/apimachinery/pkg/util/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" "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" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2"
) )
@ -38,10 +37,9 @@ func init() {
AddToScheme(Scheme) 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) { func AddToScheme(scheme *runtime.Scheme) {
utilruntime.Must(kubeadm.AddToScheme(scheme)) utilruntime.Must(kubeadm.AddToScheme(scheme))
utilruntime.Must(v1alpha1.AddToScheme(scheme))
utilruntime.Must(v1alpha2.AddToScheme(scheme)) utilruntime.Must(v1alpha2.AddToScheme(scheme))
utilruntime.Must(scheme.SetVersionPriority(v1alpha1.SchemeGroupVersion)) utilruntime.Must(scheme.SetVersionPriority(v1alpha2.SchemeGroupVersion))
} }

View File

@ -17,8 +17,6 @@ limitations under the License.
package kubeadm package kubeadm
import ( import (
fuzz "github.com/google/gofuzz"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kubeletconfigv1beta1 "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/v1beta1" kubeletconfigv1beta1 "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/v1beta1"
@ -201,14 +199,6 @@ type Etcd struct {
External *ExternalEtcd 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 // LocalEtcd describes that kubeadm should run an etcd cluster locally
type LocalEtcd struct { type LocalEtcd struct {

View File

@ -31,7 +31,6 @@ import (
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme" 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" kubeadmapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util" cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
"k8s.io/kubernetes/cmd/kubeadm/app/constants" "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. locally in the CLI tool without ever touching anything in the cluster.
In this version of kubeadm, the following API versions are supported: In this version of kubeadm, the following API versions are supported:
- %s - %s
- %s - TODO: kubeadm.k8s.io/v1beta1
Further, kubeadm can only write out config of version %q, but read both types. 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 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 In other words, the output of this command is what kubeadm actually would read internally if you
submitted this file to "kubeadm init" 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) { Run: func(cmd *cobra.Command, args []string) {
if len(oldCfgPath) == 0 { if len(oldCfgPath) == 0 {
kubeadmutil.CheckErr(fmt.Errorf("The --old-config flag is mandatory")) kubeadmutil.CheckErr(fmt.Errorf("The --old-config flag is mandatory"))

View File

@ -42,7 +42,7 @@ import (
netutil "k8s.io/apimachinery/pkg/util/net" netutil "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" 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" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/images" "k8s.io/kubernetes/cmd/kubeadm/app/images"
"k8s.io/kubernetes/pkg/registry/core/service/ipallocator" "k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
@ -533,7 +533,7 @@ func (sysver SystemVerificationCheck) Check() (warnings, errors []error) {
&system.KernelValidator{Reporter: reporter}} &system.KernelValidator{Reporter: reporter}}
// run the docker validator only with dockershim // run the docker validator only with dockershim
if sysver.CRISocket == kubeadmdefaults.DefaultCRISocket { if sysver.CRISocket == kubeadmapiv1alpha2.DefaultCRISocket {
// https://github.com/kubernetes/kubeadm/issues/533 // https://github.com/kubernetes/kubeadm/issues/533
validators = append(validators, &system.DockerValidator{Reporter: reporter}) validators = append(validators, &system.DockerValidator{Reporter: reporter})
} }
@ -958,7 +958,7 @@ func RunJoinNodeChecks(execer utilsexec.Interface, cfg *kubeadmapi.NodeConfigura
// kubeadm init and join commands // kubeadm init and join commands
func addCommonChecks(execer utilsexec.Interface, cfg kubeadmapi.CommonConfiguration, checks []Checker) []Checker { func addCommonChecks(execer utilsexec.Interface, cfg kubeadmapi.CommonConfiguration, checks []Checker) []Checker {
// Check whether or not the CRI socket defined is the default // 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}) checks = append(checks, CRICheck{socket: cfg.GetCRISocket(), exec: execer})
} else { } else {
checks = append(checks, ServiceCheck{Service: "docker", CheckIfActive: true}) checks = append(checks, ServiceCheck{Service: "docker", CheckIfActive: true})

View File

@ -30,7 +30,6 @@ import (
bootstraputil "k8s.io/client-go/tools/bootstrap/token/util" bootstraputil "k8s.io/client-go/tools/bootstrap/token/util"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme" 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" kubeadmapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
@ -131,51 +130,12 @@ func ConfigFileAndDefaultsToInternalConfig(cfgPath string, defaultversionedcfg *
func BytesToInternalConfig(b []byte) (*kubeadmapi.MasterConfiguration, error) { func BytesToInternalConfig(b []byte) (*kubeadmapi.MasterConfiguration, error) {
internalcfg := &kubeadmapi.MasterConfiguration{} internalcfg := &kubeadmapi.MasterConfiguration{}
decoded, err := kubeadmutil.LoadYAML(b) if err := DetectUnsupportedVersion(b); err != nil {
if err != nil { return nil, err
return nil, fmt.Errorf("unable to decode config from bytes: %v", 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 if err := runtime.DecodeInto(kubeadmscheme.Codecs.UniversalDecoder(kubeadmapiv1alpha2.SchemeGroupVersion), b, internalcfg); err != nil {
// 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, return nil, err
// 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")
} }
return defaultAndValidate(internalcfg) return defaultAndValidate(internalcfg)
@ -193,6 +153,29 @@ func defaultAndValidate(cfg *kubeadmapi.MasterConfiguration) (*kubeadmapi.Master
return cfg, nil 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 // NormalizeKubernetesVersion resolves version labels, sets alternative
// image registry if requested for CI builds, and validates minimal // image registry if requested for CI builds, and validates minimal
// version that kubeadm supports. // version that kubeadm supports.

View File

@ -22,28 +22,20 @@ import (
"testing" "testing"
"github.com/pmezard/go-difflib/difflib" "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/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" "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/scheme"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
) )
const ( const (
master_v1alpha1YAML = "testdata/conversion/master/v1alpha1.yaml" master_v1alpha2YAML = "testdata/conversion/master/v1alpha2.yaml"
master_v1alpha1WithoutTypeMetaYAML = "testdata/conversion/master/v1alpha1_without_TypeMeta.yaml" master_internalYAML = "testdata/conversion/master/internal.yaml"
master_v1alpha2YAML = "testdata/conversion/master/v1alpha2.yaml" master_incompleteYAML = "testdata/defaulting/master/incomplete.yaml"
master_internalYAML = "testdata/conversion/master/internal.yaml" master_defaultedYAML = "testdata/defaulting/master/defaulted.yaml"
master_incompleteYAML = "testdata/defaulting/master/incomplete.yaml" master_invalidYAML = "testdata/validation/invalid_mastercfg.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"
) )
func diff(expected, actual []byte) string { 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, // 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 // 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 { // v1alpha2 -> internal
name: "v1alpha2ToInternal", name: "v1alpha2ToInternal",
in: master_v1alpha2YAML, in: master_v1alpha2YAML,
out: master_internalYAML, out: master_internalYAML,
groupVersion: kubeadm.SchemeGroupVersion, groupVersion: kubeadm.SchemeGroupVersion,
}, },
{ // v1alpha1 (faulty) -> internal -> v1alpha2 { // v1alpha2 -> internal -> v1alpha2
name: "v1alpha1WithoutTypeMetaTov1alpha2", name: "v1alpha2Tov1alpha2",
in: master_v1alpha1WithoutTypeMetaYAML, in: master_v1alpha2YAML,
out: master_v1alpha2YAML,
groupVersion: v1alpha2.SchemeGroupVersion,
},
{ // v1alpha1 -> internal -> v1alpha2
name: "v1alpha1Tov1alpha2",
in: master_v1alpha1YAML,
out: master_v1alpha2YAML, out: master_v1alpha2YAML,
groupVersion: v1alpha2.SchemeGroupVersion, groupVersion: v1alpha2.SchemeGroupVersion,
}, },
// These tests are reading one file that has only a subset of the fields populated, loading it using ConfigFileAndDefaultsToInternalConfig, // 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 // and then marshals the internal object to the expected groupVersion
{ // v1alpha1 (faulty) -> default -> validate -> internal -> v1alpha2 { // v1alpha2 -> default -> validate -> internal -> v1alpha2
name: "incompleteYAMLToDefaultedv1alpha2", name: "incompleteYAMLToDefaultedv1alpha2",
in: master_incompleteYAML, in: master_incompleteYAML,
out: master_defaultedYAML, out: master_defaultedYAML,
groupVersion: v1alpha2.SchemeGroupVersion, groupVersion: v1alpha2.SchemeGroupVersion,
}, },
{ // v1alpha1 (faulty) -> validation should fail { // v1alpha2 -> validation should fail
name: "invalidYAMLShouldFail", name: "invalidYAMLShouldFail",
in: master_invalidYAML, in: master_invalidYAML,
expectedErr: true, 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) { func TestLowercaseSANs(t *testing.T) {
tests := []struct { tests := []struct {
name string name string

View File

@ -25,7 +25,6 @@ import (
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme" 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" kubeadmapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
"k8s.io/kubernetes/pkg/util/node" "k8s.io/kubernetes/pkg/util/node"
@ -43,14 +42,21 @@ func NodeConfigFileAndDefaultsToInternalConfig(cfgPath string, defaultversionedc
if cfgPath != "" { if cfgPath != "" {
// Loads configuration from config file, if provided // 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") glog.V(1).Infoln("loading configuration from the given file")
b, err := ioutil.ReadFile(cfgPath) b, err := ioutil.ReadFile(cfgPath)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to read config from %q [%v]", cfgPath, err) 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 { } else {
// Takes passed flags into account; the defaulting is executed once again enforcing assignement of // 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 // static default values to cfg only for values not provided with flags

View File

@ -29,7 +29,6 @@ import (
) )
const ( const (
node_v1alpha1YAML = "testdata/conversion/node/v1alpha1.yaml"
node_v1alpha2YAML = "testdata/conversion/node/v1alpha2.yaml" node_v1alpha2YAML = "testdata/conversion/node/v1alpha2.yaml"
node_internalYAML = "testdata/conversion/node/internal.yaml" node_internalYAML = "testdata/conversion/node/internal.yaml"
node_incompleteYAML = "testdata/defaulting/node/incomplete.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, // 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 // 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 { // v1alpha2 -> internal
name: "v1alpha2ToInternal", name: "v1alpha2ToInternal",
in: node_v1alpha2YAML, in: node_v1alpha2YAML,
out: node_internalYAML, out: node_internalYAML,
groupVersion: kubeadm.SchemeGroupVersion, groupVersion: kubeadm.SchemeGroupVersion,
}, },
{ // v1alpha1 -> internal -> v1alpha2 { // v1alpha2 -> internal -> v1alpha2
name: "v1alpha1WithoutTypeMetaTov1alpha2", name: "v1alpha2Tov1alpha2",
in: node_v1alpha1YAML, in: node_v1alpha2YAML,
out: node_v1alpha2YAML, out: node_v1alpha2YAML,
groupVersion: v1alpha2.SchemeGroupVersion, groupVersion: v1alpha2.SchemeGroupVersion,
}, },

View File

@ -26,7 +26,6 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/apimachinery/pkg/runtime/serializer"
clientsetscheme "k8s.io/client-go/kubernetes/scheme" clientsetscheme "k8s.io/client-go/kubernetes/scheme"
kubeadmapiv1alpha1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
) )
// MarshalToYaml marshals an object into yaml. // MarshalToYaml marshals an object into yaml.
@ -67,37 +66,36 @@ func UnmarshalFromYamlForCodecs(buffer []byte, gv schema.GroupVersion, codecs se
return runtime.Decode(decoder, buffer) return runtime.Decode(decoder, buffer)
} }
// GroupVersionKindFromBytes parses the bytes and returns the gvk // ExtractAPIVersionAndKindFromYAML extracts the APIVersion and Kind fields from YAML bytes
func GroupVersionKindFromBytes(buffer []byte, codecs serializer.CodecFactory) (schema.GroupVersionKind, error) { func ExtractAPIVersionAndKindFromYAML(b []byte) (string, string, error) {
decoded, err := LoadYAML(b)
decoded, err := LoadYAML(buffer)
if err != nil { 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 kindStr, ok := decoded["kind"].(string)
// 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, if !ok || len(kindStr) == 0 {
// it could only write return "", "", fmt.Errorf("any config file must have the kind field set")
// 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)
} }
if apiVersion == nil || len(apiVersion.(string)) == 0 { apiVersionStr, ok := decoded["apiVersion"].(string)
apiVersionStr = kubeadmapiv1alpha1.SchemeGroupVersion.String() if !ok || len(apiVersionStr) == 0 {
} else { return "", "", fmt.Errorf("any config file must have the apiVersion field set")
apiVersionStr = apiVersion.(string)
} }
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) gv, err := schema.ParseGroupVersion(apiVersionStr)
if err != nil { if err != nil {
return schema.EmptyObjectKind.GroupVersionKind(), fmt.Errorf("unable to parse apiVersion: %v", err) return schema.EmptyObjectKind.GroupVersionKind(), fmt.Errorf("unable to parse apiVersion: %v", err)
} }
return gv.WithKind(kindStr), nil return gv.WithKind(kindStr), nil
} }

View File

@ -17,13 +17,14 @@ limitations under the License.
package util package util
import ( import (
"encoding/json"
"reflect" "reflect"
"testing" "testing"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme" "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) { func TestMarshalUnmarshalYaml(t *testing.T) {
@ -75,51 +76,42 @@ func TestMarshalUnmarshalYaml(t *testing.T) {
} }
func TestMarshalUnmarshalToYamlForCodecs(t *testing.T) { func TestMarshalUnmarshalToYamlForCodecs(t *testing.T) {
cfg := &kubeadmapiext.MasterConfiguration{ cfg := &kubeadmapiv1alpha2.MasterConfiguration{
API: kubeadmapiext.API{ TypeMeta: metav1.TypeMeta{
Kind: "MasterConfiguration",
APIVersion: kubeadmapiv1alpha2.SchemeGroupVersion.String(),
},
API: kubeadmapiv1alpha2.API{
AdvertiseAddress: "10.100.0.1", AdvertiseAddress: "10.100.0.1",
BindPort: 4332, BindPort: 4332,
}, },
NodeName: "testNode", NodeRegistration: kubeadmapiv1alpha2.NodeRegistrationOptions{
NoTaintMaster: true, Name: "testNode",
Networking: kubeadmapiext.Networking{ CRISocket: "/var/run/cri.sock",
},
Networking: kubeadmapiv1alpha2.Networking{
ServiceSubnet: "10.100.0.0/24", ServiceSubnet: "10.100.0.0/24",
PodSubnet: "10.100.1.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 { if err != nil {
t.Fatalf("unexpected error marshalling MasterConfiguration: %v", err) t.Fatalf("unexpected error marshalling MasterConfiguration: %v", err)
} }
t.Logf("\n%s", bytes) t.Logf("\n%s", bytes)
obj, err := UnmarshalFromYamlForCodecs(bytes, kubeadmapiext.SchemeGroupVersion, scheme.Codecs) obj, err := UnmarshalFromYamlForCodecs(bytes, kubeadmapiv1alpha2.SchemeGroupVersion, scheme.Codecs)
if err != nil { if err != nil {
t.Fatalf("unexpected error unmarshalling MasterConfiguration: %v", err) t.Fatalf("unexpected error unmarshalling MasterConfiguration: %v", err)
} }
cfg2, ok := obj.(*kubeadmapiext.MasterConfiguration) cfg2, ok := obj.(*kubeadmapiv1alpha2.MasterConfiguration)
if !ok { if !ok || cfg2 == nil {
t.Fatal("did not get MasterConfiguration back") t.Fatal("did not get MasterConfiguration back")
} }
if !reflect.DeepEqual(*cfg, *cfg2) {
if cfg2.API.AdvertiseAddress != cfg.API.AdvertiseAddress { t.Errorf("expected %v, got %v", *cfg, *cfg2)
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)
} }
} }