Merge pull request #74025 from rosti/deprecated-configs

kubeadm: Don't load deprecated configs
pull/564/head
Kubernetes Prow Robot 2019-02-19 04:55:32 -08:00 committed by GitHub
commit 8d69dc630b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 84 additions and 67 deletions

View File

@ -1,3 +1,3 @@
apiVersion: kubeadm.k8s.io/v1alpha3 apiVersion: kubeadm.k8s.io/v1beta1
kind: ClusterConfiguration kind: ClusterConfiguration
kubernetesVersion: 1.12.0 kubernetesVersion: 1.13.0

View File

@ -48,20 +48,35 @@ func MarshalKubeadmConfigObject(obj runtime.Object) ([]byte, error) {
} }
} }
// ValidateSupportedVersion checks if a supplied GroupVersion is not on the list of unsupported GVs. If it is, an error is returned. // validateSupportedVersion checks if the supplied GroupVersion is not on the lists of old unsupported or deprecated GVs.
func ValidateSupportedVersion(gv schema.GroupVersion) error { // If it is, an error is returned.
func validateSupportedVersion(gv schema.GroupVersion, allowDeprecated bool) error {
// The support matrix will look something like this now and in the future: // The support matrix will look something like this now and in the future:
// v1.10 and earlier: v1alpha1 // v1.10 and earlier: v1alpha1
// v1.11: v1alpha1 read-only, writes only v1alpha2 config // v1.11: v1alpha1 read-only, writes only v1alpha2 config
// v1.12: v1alpha2 read-only, writes only v1alpha3 config. Warns if the user tries to use v1alpha1 // v1.12: v1alpha2 read-only, writes only v1alpha3 config. Errors if the user tries to use v1alpha1
// v1.13: v1alpha3 read-only, writes only v1beta1 config. Warns if the user tries to use v1alpha1 or v1alpha2 // v1.13: v1alpha3 read-only, writes only v1beta1 config. Errors if the user tries to use v1alpha1 or v1alpha2
// v1.14: v1alpha3 convert only, writes only v1beta1 config. Errors if the user tries to use v1alpha1 or v1alpha2
oldKnownAPIVersions := map[string]string{ oldKnownAPIVersions := map[string]string{
"kubeadm.k8s.io/v1alpha1": "v1.11", "kubeadm.k8s.io/v1alpha1": "v1.11",
"kubeadm.k8s.io/v1alpha2": "v1.12", "kubeadm.k8s.io/v1alpha2": "v1.12",
} }
if useKubeadmVersion := oldKnownAPIVersions[gv.String()]; useKubeadmVersion != "" {
// Deprecated API versions are supported by us, but can only be used for migration.
deprecatedAPIVersions := map[string]struct{}{
"kubeadm.k8s.io/v1alpha3": {}, // Can be migrated with kubeadm 1.13+
}
gvString := gv.String()
if useKubeadmVersion := oldKnownAPIVersions[gvString]; useKubeadmVersion != "" {
return errors.Errorf("your configuration file uses an old API spec: %q. 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.", gv.String(), useKubeadmVersion) return errors.Errorf("your configuration file uses an old API spec: %q. 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.", gv.String(), useKubeadmVersion)
} }
if _, present := deprecatedAPIVersions[gvString]; present && !allowDeprecated {
return errors.Errorf("your configuration file uses a deprecated API spec: %q. Please use 'kubeadm config migrate --old-config old.yaml --new-config new.yaml', which will write the new, similar spec using a newer API version.", gv.String())
}
return nil return nil
} }
@ -154,7 +169,7 @@ func MigrateOldConfig(oldConfig []byte) ([]byte, error) {
// Migrate InitConfiguration and ClusterConfiguration if there are any in the config // Migrate InitConfiguration and ClusterConfiguration if there are any in the config
if kubeadmutil.GroupVersionKindsHasInitConfiguration(gvks...) || kubeadmutil.GroupVersionKindsHasClusterConfiguration(gvks...) { if kubeadmutil.GroupVersionKindsHasInitConfiguration(gvks...) || kubeadmutil.GroupVersionKindsHasClusterConfiguration(gvks...) {
o, err := documentMapToInitConfiguration(gvkmap) o, err := documentMapToInitConfiguration(gvkmap, true)
if err != nil { if err != nil {
return []byte{}, err return []byte{}, err
} }
@ -167,7 +182,7 @@ func MigrateOldConfig(oldConfig []byte) ([]byte, error) {
// Migrate JoinConfiguration if there is any // Migrate JoinConfiguration if there is any
if kubeadmutil.GroupVersionKindsHasJoinConfiguration(gvks...) { if kubeadmutil.GroupVersionKindsHasJoinConfiguration(gvks...) {
o, err := documentMapToJoinConfiguration(gvkmap) o, err := documentMapToJoinConfiguration(gvkmap, true)
if err != nil { if err != nil {
return []byte{}, err return []byte{}, err
} }

View File

@ -17,6 +17,7 @@ limitations under the License.
package config package config
import ( import (
"fmt"
"testing" "testing"
"github.com/lithammer/dedent" "github.com/lithammer/dedent"
@ -27,11 +28,12 @@ import (
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
) )
func TestValidateSupportedVersion(t *testing.T) {
const KubeadmGroupName = "kubeadm.k8s.io" const KubeadmGroupName = "kubeadm.k8s.io"
func TestValidateSupportedVersion(t *testing.T) {
tests := []struct { tests := []struct {
gv schema.GroupVersion gv schema.GroupVersion
allowDeprecated bool
expectedErr bool expectedErr bool
}{ }{
{ {
@ -53,6 +55,14 @@ func TestValidateSupportedVersion(t *testing.T) {
Group: KubeadmGroupName, Group: KubeadmGroupName,
Version: "v1alpha3", Version: "v1alpha3",
}, },
expectedErr: true,
},
{
gv: schema.GroupVersion{
Group: KubeadmGroupName,
Version: "v1alpha3",
},
allowDeprecated: true,
}, },
{ {
gv: schema.GroupVersion{ gv: schema.GroupVersion{
@ -69,8 +79,8 @@ func TestValidateSupportedVersion(t *testing.T) {
} }
for _, rt := range tests { for _, rt := range tests {
t.Run(rt.gv.String(), func(t *testing.T) { t.Run(fmt.Sprintf("%s/allowDeprecated:%t", rt.gv, rt.allowDeprecated), func(t *testing.T) {
err := ValidateSupportedVersion(rt.gv) err := validateSupportedVersion(rt.gv, rt.allowDeprecated)
if rt.expectedErr && err == nil { if rt.expectedErr && err == nil {
t.Error("unexpected success") t.Error("unexpected success")
} else if !rt.expectedErr && err != nil { } else if !rt.expectedErr && err != nil {

View File

@ -190,11 +190,7 @@ func LoadInitConfigurationFromFile(cfgPath string) (*kubeadmapi.InitConfiguratio
return nil, errors.Wrapf(err, "unable to read config from %q ", cfgPath) return nil, errors.Wrapf(err, "unable to read config from %q ", cfgPath)
} }
internalcfg, err := BytesToInitConfiguration(b) return BytesToInitConfiguration(b)
if err != nil {
return nil, err
}
return internalcfg, nil
} }
// LoadOrDefaultInitConfiguration takes a path to a config file and a versioned configuration that can serve as the default config // LoadOrDefaultInitConfiguration takes a path to a config file and a versioned configuration that can serve as the default config
@ -222,18 +218,18 @@ func BytesToInitConfiguration(b []byte) (*kubeadmapi.InitConfiguration, error) {
return nil, err return nil, err
} }
return documentMapToInitConfiguration(gvkmap) return documentMapToInitConfiguration(gvkmap, false)
} }
// documentMapToInitConfiguration converts a map of GVKs and YAML documents to defaulted and validated configuration object. // documentMapToInitConfiguration converts a map of GVKs and YAML documents to defaulted and validated configuration object.
func documentMapToInitConfiguration(gvkmap map[schema.GroupVersionKind][]byte) (*kubeadmapi.InitConfiguration, error) { func documentMapToInitConfiguration(gvkmap map[schema.GroupVersionKind][]byte, allowDeprecated bool) (*kubeadmapi.InitConfiguration, error) {
var initcfg *kubeadmapi.InitConfiguration var initcfg *kubeadmapi.InitConfiguration
var clustercfg *kubeadmapi.ClusterConfiguration var clustercfg *kubeadmapi.ClusterConfiguration
decodedComponentConfigObjects := map[componentconfigs.RegistrationKind]runtime.Object{} decodedComponentConfigObjects := map[componentconfigs.RegistrationKind]runtime.Object{}
for gvk, fileContent := range gvkmap { for gvk, fileContent := range gvkmap {
// first, check if this GVK is supported one // first, check if this GVK is supported and possibly not deprecated
if err := ValidateSupportedVersion(gvk.GroupVersion()); err != nil { if err := validateSupportedVersion(gvk.GroupVersion(), allowDeprecated); err != nil {
return nil, err return nil, err
} }

View File

@ -71,6 +71,7 @@ func TestLoadInitConfigurationFromFile(t *testing.T) {
var tests = []struct { var tests = []struct {
name string name string
fileContents []byte fileContents []byte
expectErr bool
}{ }{
{ {
name: "v1beta1.partial1", name: "v1beta1.partial1",
@ -92,10 +93,12 @@ func TestLoadInitConfigurationFromFile(t *testing.T) {
{ {
name: "v1alpha3.partial1", name: "v1alpha3.partial1",
fileContents: cfgFiles["InitConfiguration_v1alpha3"], fileContents: cfgFiles["InitConfiguration_v1alpha3"],
expectErr: true,
}, },
{ {
name: "v1alpha3.partial2", name: "v1alpha3.partial2",
fileContents: cfgFiles["ClusterConfiguration_v1alpha3"], fileContents: cfgFiles["ClusterConfiguration_v1alpha3"],
expectErr: true,
}, },
{ {
name: "v1alpha3.full", name: "v1alpha3.full",
@ -105,6 +108,7 @@ func TestLoadInitConfigurationFromFile(t *testing.T) {
cfgFiles["Kube-proxy_componentconfig"], cfgFiles["Kube-proxy_componentconfig"],
cfgFiles["Kubelet_componentconfig"], cfgFiles["Kubelet_componentconfig"],
}, []byte(constants.YAMLDocumentSeparator)), }, []byte(constants.YAMLDocumentSeparator)),
expectErr: true,
}, },
} }
@ -118,6 +122,11 @@ func TestLoadInitConfigurationFromFile(t *testing.T) {
} }
obj, err := LoadInitConfigurationFromFile(cfgPath) obj, err := LoadInitConfigurationFromFile(cfgPath)
if rt.expectErr {
if err == nil {
t.Error("Unexpected success")
}
} else {
if err != nil { if err != nil {
t.Errorf("Error reading file: %v", err) t.Errorf("Error reading file: %v", err)
return return
@ -126,6 +135,7 @@ func TestLoadInitConfigurationFromFile(t *testing.T) {
if obj == nil { if obj == nil {
t.Errorf("Unexpected nil return value") t.Errorf("Unexpected nil return value")
} }
}
}) })
} }
} }
@ -150,10 +160,9 @@ func TestInitConfigurationMarshallingFromFile(t *testing.T) {
// These tests are reading one file, loading it using LoadInitConfigurationFromFile that all of kubeadm is using for unmarshal of our API types, // These tests are reading one file, loading it using LoadInitConfigurationFromFile 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
{ // v1alpha3 -> internal { // v1alpha3 -> internal
name: "v1alpha3ToInternal", name: "v1alpha3IsDeprecated",
in: masterV1alpha3YAMLAbstracted, in: masterV1alpha3YAMLAbstracted,
out: masterInternalYAMLAbstracted, expectedErr: true,
groupVersion: kubeadm.SchemeGroupVersion,
}, },
{ // v1beta1 -> internal { // v1beta1 -> internal
name: "v1beta1ToInternal", name: "v1beta1ToInternal",
@ -161,12 +170,6 @@ func TestInitConfigurationMarshallingFromFile(t *testing.T) {
out: masterInternalYAMLAbstracted, out: masterInternalYAMLAbstracted,
groupVersion: kubeadm.SchemeGroupVersion, groupVersion: kubeadm.SchemeGroupVersion,
}, },
{ // v1alpha3 -> internal -> v1beta1
name: "v1alpha3Tov1beta1",
in: masterV1alpha3YAMLAbstracted,
out: masterV1beta1YAMLAbstracted,
groupVersion: kubeadmapiv1beta1.SchemeGroupVersion,
},
{ // v1beta1 -> internal -> v1beta1 { // v1beta1 -> internal -> v1beta1
name: "v1beta1Tov1beta1", name: "v1beta1Tov1beta1",
in: masterV1beta1YAMLAbstracted, in: masterV1beta1YAMLAbstracted,
@ -181,7 +184,7 @@ func TestInitConfigurationMarshallingFromFile(t *testing.T) {
out: masterDefaultedYAMLAbstracted, out: masterDefaultedYAMLAbstracted,
groupVersion: kubeadmapiv1beta1.SchemeGroupVersion, groupVersion: kubeadmapiv1beta1.SchemeGroupVersion,
}, },
{ // v1alpha3 -> validation should fail { // v1beta1 -> validation should fail
name: "invalidYAMLShouldFail", name: "invalidYAMLShouldFail",
in: masterInvalidYAML, in: masterInvalidYAML,
expectedErr: true, expectedErr: true,

View File

@ -84,12 +84,12 @@ func LoadJoinConfigurationFromFile(cfgPath string) (*kubeadmapi.JoinConfiguratio
return nil, err return nil, err
} }
return documentMapToJoinConfiguration(gvkmap) return documentMapToJoinConfiguration(gvkmap, false)
} }
// documentMapToJoinConfiguration takes a map between GVKs and YAML documents (as returned by SplitYAMLDocuments), // documentMapToJoinConfiguration takes a map between GVKs and YAML documents (as returned by SplitYAMLDocuments),
// finds a JoinConfiguration, decodes it, dynamically defaults it and then validates it prior to return. // finds a JoinConfiguration, decodes it, dynamically defaults it and then validates it prior to return.
func documentMapToJoinConfiguration(gvkmap map[schema.GroupVersionKind][]byte) (*kubeadmapi.JoinConfiguration, error) { func documentMapToJoinConfiguration(gvkmap map[schema.GroupVersionKind][]byte, allowDeprecated bool) (*kubeadmapi.JoinConfiguration, error) {
joinBytes := []byte{} joinBytes := []byte{}
for gvk, bytes := range gvkmap { for gvk, bytes := range gvkmap {
// not interested in anything other than JoinConfiguration // not interested in anything other than JoinConfiguration
@ -97,8 +97,8 @@ func documentMapToJoinConfiguration(gvkmap map[schema.GroupVersionKind][]byte) (
continue continue
} }
// check if this version is supported one // check if this version is supported and possibly not deprecated
if err := ValidateSupportedVersion(gvk.GroupVersion()); err != nil { if err := validateSupportedVersion(gvk.GroupVersion(), allowDeprecated); err != nil {
return nil, err return nil, err
} }

View File

@ -46,10 +46,9 @@ func TestLoadJoinConfigurationFromFile(t *testing.T) {
// These tests are reading one file, loading it using LoadJoinConfigurationFromFile that all of kubeadm is using for unmarshal of our API types, // These tests are reading one file, loading it using LoadJoinConfigurationFromFile 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
{ // v1alpha3 -> internal { // v1alpha3 -> internal
name: "v1alpha3ToInternal", name: "v1alpha3IsDeprecated",
in: nodeV1alpha3YAML, in: nodeV1alpha3YAML,
out: nodeInternalYAML, expectedErr: true,
groupVersion: kubeadm.SchemeGroupVersion,
}, },
{ // v1beta1 -> internal { // v1beta1 -> internal
name: "v1beta1ToInternal", name: "v1beta1ToInternal",
@ -57,12 +56,6 @@ func TestLoadJoinConfigurationFromFile(t *testing.T) {
out: nodeInternalYAML, out: nodeInternalYAML,
groupVersion: kubeadm.SchemeGroupVersion, groupVersion: kubeadm.SchemeGroupVersion,
}, },
{ // v1alpha3 -> internal -> v1beta1
name: "v1alpha3Tov1beta1",
in: nodeV1alpha3YAML,
out: nodeV1beta1YAML,
groupVersion: kubeadmapiv1beta1.SchemeGroupVersion,
},
{ // v1beta1 -> internal -> v1beta1 { // v1beta1 -> internal -> v1beta1
name: "v1beta1Tov1beta1", name: "v1beta1Tov1beta1",
in: nodeV1beta1YAML, in: nodeV1beta1YAML,
@ -77,7 +70,7 @@ func TestLoadJoinConfigurationFromFile(t *testing.T) {
out: nodeDefaultedYAML, out: nodeDefaultedYAML,
groupVersion: kubeadmapiv1beta1.SchemeGroupVersion, groupVersion: kubeadmapiv1beta1.SchemeGroupVersion,
}, },
{ // v1alpha3 -> validation should fail { // v1beta1 -> validation should fail
name: "invalidYAMLShouldFail", name: "invalidYAMLShouldFail",
in: nodeInvalidYAML, in: nodeInvalidYAML,
expectedErr: true, expectedErr: true,

View File

@ -1,11 +1,11 @@
apiVersion: kubeadm.k8s.io/v1alpha3 apiVersion: kubeadm.k8s.io/v1beta1
discoveryToken: abcdef.0123456789abcdef discovery:
discoveryTokenAPIServers: bootstrapToken:
- kube-apiserver:6443 apiServerEndpoint: kube-apiserver:6443
discoveryTokenUnsafeSkipCAVerification: true token: abcdef.0123456789abcdef
unsafeSkipCAVerification: true
tlsBootstrapToken: abcdef.0123456789abcdef
kind: JoinConfiguration kind: JoinConfiguration
nodeRegistration: nodeRegistration:
criSocket: /var/run/dockershim.sock criSocket: /var/run/dockershim.sock
name: thegopher name: thegopher
tlsBootstrapToken: abcdef.0123456789abcdef
token: abcdef.0123456789abcdef

View File

@ -146,19 +146,19 @@ func TestCmdInitConfig(t *testing.T) {
expected: false, expected: false,
}, },
{ {
name: "can't load v1alpha1 config", name: "can't load old v1alpha1 config",
args: "--config=testdata/init/v1alpha1.yaml", args: "--config=testdata/init/v1alpha1.yaml",
expected: false, expected: false,
}, },
{ {
name: "can't load v1alpha2 config", name: "can't load old v1alpha2 config",
args: "--config=testdata/init/v1alpha2.yaml", args: "--config=testdata/init/v1alpha2.yaml",
expected: false, expected: false,
}, },
{ {
name: "can load v1alpha3 config", name: "can't load deprecated v1alpha3 config",
args: "--config=testdata/init/v1alpha3.yaml", args: "--config=testdata/init/v1alpha3.yaml",
expected: true, expected: false,
}, },
{ {
name: "can load v1beta1 config", name: "can load v1beta1 config",