mirror of https://github.com/k3s-io/k3s
Merge pull request #73474 from rosti/IsSupportedVersion
kubeadm: Introduce ValidateSupportedVersionpull/564/head
commit
0c2613c71a
|
@ -27,6 +27,7 @@ import (
|
||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
netutil "k8s.io/apimachinery/pkg/util/net"
|
netutil "k8s.io/apimachinery/pkg/util/net"
|
||||||
"k8s.io/apimachinery/pkg/util/version"
|
"k8s.io/apimachinery/pkg/util/version"
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
|
@ -48,15 +49,9 @@ func MarshalKubeadmConfigObject(obj runtime.Object) ([]byte, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
// ValidateSupportedVersion checks if a supplied GroupVersion is not on the list of unsupported GVs. If it is, an error is returned.
|
||||||
func DetectUnsupportedVersion(b []byte) error {
|
func ValidateSupportedVersion(gv schema.GroupVersion) error {
|
||||||
gvks, err := kubeadmutil.GroupVersionKindsFromBytes(b)
|
// The support matrix will look something like this now and in the future:
|
||||||
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.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. Warns if the user tries to use v1alpha1
|
||||||
|
@ -65,27 +60,9 @@ func DetectUnsupportedVersion(b []byte) error {
|
||||||
"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 we find an old API version in this gvk list, error out and tell the user why this doesn't work
|
if useKubeadmVersion := oldKnownAPIVersions[gv.String()]; useKubeadmVersion != "" {
|
||||||
knownKinds := map[string]bool{}
|
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)
|
||||||
for _, gvk := range gvks {
|
|
||||||
if useKubeadmVersion := oldKnownAPIVersions[gvk.GroupVersion().String()]; len(useKubeadmVersion) != 0 {
|
|
||||||
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.", gvk.GroupVersion().String(), useKubeadmVersion)
|
|
||||||
}
|
}
|
||||||
knownKinds[gvk.Kind] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitConfiguration and JoinConfiguration may not apply together, warn if more than one is specified
|
|
||||||
mutuallyExclusive := []string{constants.InitConfigurationKind, constants.JoinConfigurationKind}
|
|
||||||
mutuallyExclusiveCount := 0
|
|
||||||
for _, kind := range mutuallyExclusive {
|
|
||||||
if knownKinds[kind] {
|
|
||||||
mutuallyExclusiveCount++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if mutuallyExclusiveCount > 1 {
|
|
||||||
klog.Warningf("WARNING: Detected resource kinds that may not apply: %v", mutuallyExclusive)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,187 +17,66 @@ limitations under the License.
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/lithammer/dedent"
|
"github.com/lithammer/dedent"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1"
|
kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
var files = map[string][]byte{
|
func TestValidateSupportedVersion(t *testing.T) {
|
||||||
"Master_v1alpha1": []byte(`
|
const KubeadmGroupName = "kubeadm.k8s.io"
|
||||||
apiVersion: kubeadm.k8s.io/v1alpha1
|
|
||||||
kind: InitConfiguration
|
|
||||||
`),
|
|
||||||
"Node_v1alpha1": []byte(`
|
|
||||||
apiVersion: kubeadm.k8s.io/v1alpha1
|
|
||||||
kind: NodeConfiguration
|
|
||||||
`),
|
|
||||||
"Master_v1alpha2": []byte(`
|
|
||||||
apiVersion: kubeadm.k8s.io/v1alpha2
|
|
||||||
kind: MasterConfiguration
|
|
||||||
`),
|
|
||||||
"Node_v1alpha2": []byte(`
|
|
||||||
apiVersion: kubeadm.k8s.io/v1alpha2
|
|
||||||
kind: NodeConfiguration
|
|
||||||
`),
|
|
||||||
"Init_v1alpha3": []byte(`
|
|
||||||
apiVersion: kubeadm.k8s.io/v1alpha3
|
|
||||||
kind: InitConfiguration
|
|
||||||
`),
|
|
||||||
"Join_v1alpha3": []byte(`
|
|
||||||
apiVersion: kubeadm.k8s.io/v1alpha3
|
|
||||||
kind: JoinConfiguration
|
|
||||||
`),
|
|
||||||
"Init_v1beta1": []byte(`
|
|
||||||
apiVersion: kubeadm.k8s.io/v1beta1
|
|
||||||
kind: InitConfiguration
|
|
||||||
`),
|
|
||||||
"Join_v1beta1": []byte(`
|
|
||||||
apiVersion: kubeadm.k8s.io/v1beta1
|
|
||||||
kind: JoinConfiguration
|
|
||||||
`),
|
|
||||||
"NoKind": []byte(`
|
|
||||||
apiVersion: baz.k8s.io/v1
|
|
||||||
foo: foo
|
|
||||||
bar: bar
|
|
||||||
`),
|
|
||||||
"NoAPIVersion": []byte(`
|
|
||||||
kind: Bar
|
|
||||||
foo: foo
|
|
||||||
bar: bar
|
|
||||||
`),
|
|
||||||
"Foo": []byte(`
|
|
||||||
apiVersion: foo.k8s.io/v1
|
|
||||||
kind: Foo
|
|
||||||
`),
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDetectUnsupportedVersion(t *testing.T) {
|
tests := []struct {
|
||||||
var tests = []struct {
|
gv schema.GroupVersion
|
||||||
name string
|
|
||||||
fileContents []byte
|
|
||||||
expectedErr bool
|
expectedErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Master_v1alpha1",
|
gv: schema.GroupVersion{
|
||||||
fileContents: files["Master_v1alpha1"],
|
Group: KubeadmGroupName,
|
||||||
|
Version: "v1alpha1",
|
||||||
|
},
|
||||||
expectedErr: true,
|
expectedErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Node_v1alpha1",
|
gv: schema.GroupVersion{
|
||||||
fileContents: files["Node_v1alpha1"],
|
Group: KubeadmGroupName,
|
||||||
|
Version: "v1alpha2",
|
||||||
|
},
|
||||||
expectedErr: true,
|
expectedErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Master_v1alpha2",
|
gv: schema.GroupVersion{
|
||||||
fileContents: files["Master_v1alpha2"],
|
Group: KubeadmGroupName,
|
||||||
expectedErr: true,
|
Version: "v1alpha3",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Node_v1alpha2",
|
gv: schema.GroupVersion{
|
||||||
fileContents: files["Node_v1alpha2"],
|
Group: KubeadmGroupName,
|
||||||
expectedErr: true,
|
Version: "v1beta1",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Init_v1alpha3",
|
gv: schema.GroupVersion{
|
||||||
fileContents: files["Init_v1alpha3"],
|
Group: "foo.k8s.io",
|
||||||
|
Version: "v1",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "Join_v1alpha3",
|
|
||||||
fileContents: files["Join_v1alpha3"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Init_v1beta1",
|
|
||||||
fileContents: files["Init_v1beta1"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Join_v1beta1",
|
|
||||||
fileContents: files["Join_v1beta1"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "DuplicateInit v1alpha3",
|
|
||||||
fileContents: bytes.Join([][]byte{files["Init_v1alpha3"], files["Init_v1alpha3"]}, []byte(constants.YAMLDocumentSeparator)),
|
|
||||||
expectedErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "DuplicateInit v1beta1",
|
|
||||||
fileContents: bytes.Join([][]byte{files["Init_v1beta1"], files["Init_v1beta1"]}, []byte(constants.YAMLDocumentSeparator)),
|
|
||||||
expectedErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "DuplicateInit v1beta1 and v1alpha3",
|
|
||||||
fileContents: bytes.Join([][]byte{files["Init_v1beta1"], files["Init_v1alpha3"]}, []byte(constants.YAMLDocumentSeparator)),
|
|
||||||
expectedErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "DuplicateJoin v1alpha3",
|
|
||||||
fileContents: bytes.Join([][]byte{files["Join_v1alpha3"], files["Join_v1alpha3"]}, []byte(constants.YAMLDocumentSeparator)),
|
|
||||||
expectedErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "DuplicateJoin v1beta1",
|
|
||||||
fileContents: bytes.Join([][]byte{files["Join_v1beta1"], files["Join_v1beta1"]}, []byte(constants.YAMLDocumentSeparator)),
|
|
||||||
expectedErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "DuplicateJoin v1beta1 and v1alpha3",
|
|
||||||
fileContents: bytes.Join([][]byte{files["Join_v1beta1"], files["Join_v1alpha3"]}, []byte(constants.YAMLDocumentSeparator)),
|
|
||||||
expectedErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "NoKind",
|
|
||||||
fileContents: files["NoKind"],
|
|
||||||
expectedErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "NoAPIVersion",
|
|
||||||
fileContents: files["NoAPIVersion"],
|
|
||||||
expectedErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Ignore other Kind",
|
|
||||||
fileContents: bytes.Join([][]byte{files["Foo"], files["Master_v1alpha3"]}, []byte(constants.YAMLDocumentSeparator)),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Ignore other Kind",
|
|
||||||
fileContents: bytes.Join([][]byte{files["Foo"], files["Master_v1beta1"]}, []byte(constants.YAMLDocumentSeparator)),
|
|
||||||
},
|
|
||||||
// CanMixInitJoin cases used to be MustNotMixInitJoin, however due to UX issues DetectUnsupportedVersion had to tolerate that.
|
|
||||||
// So the following tests actually verify, that Init and Join can be mixed together with no error.
|
|
||||||
{
|
|
||||||
name: "CanMixInitJoin v1alpha3",
|
|
||||||
fileContents: bytes.Join([][]byte{files["Init_v1alpha3"], files["Join_v1alpha3"]}, []byte(constants.YAMLDocumentSeparator)),
|
|
||||||
expectedErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "CanMixInitJoin v1alpha3 - v1beta1",
|
|
||||||
fileContents: bytes.Join([][]byte{files["Init_v1alpha3"], files["Join_v1beta1"]}, []byte(constants.YAMLDocumentSeparator)),
|
|
||||||
expectedErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "CanMixInitJoin v1beta1 - v1alpha3",
|
|
||||||
fileContents: bytes.Join([][]byte{files["Init_v1beta1"], files["Join_v1alpha3"]}, []byte(constants.YAMLDocumentSeparator)),
|
|
||||||
expectedErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "CanMixInitJoin v1beta1",
|
|
||||||
fileContents: bytes.Join([][]byte{files["Init_v1beta1"], files["Join_v1beta1"]}, []byte(constants.YAMLDocumentSeparator)),
|
|
||||||
expectedErr: false,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, rt := range tests {
|
for _, rt := range tests {
|
||||||
t.Run(rt.name, func(t2 *testing.T) {
|
t.Run(rt.gv.String(), func(t *testing.T) {
|
||||||
|
err := ValidateSupportedVersion(rt.gv)
|
||||||
err := DetectUnsupportedVersion(rt.fileContents)
|
if rt.expectedErr && err == nil {
|
||||||
if (err != nil) != rt.expectedErr {
|
t.Error("unexpected success")
|
||||||
t2.Errorf("expected error: %t, actual: %t", rt.expectedErr, err != nil)
|
} else if !rt.expectedErr && err != nil {
|
||||||
|
t.Errorf("unexpected failure: %v", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -208,16 +208,17 @@ func BytesToInternalConfig(b []byte) (*kubeadmapi.InitConfiguration, error) {
|
||||||
var clustercfg *kubeadmapi.ClusterConfiguration
|
var clustercfg *kubeadmapi.ClusterConfiguration
|
||||||
decodedComponentConfigObjects := map[componentconfigs.RegistrationKind]runtime.Object{}
|
decodedComponentConfigObjects := map[componentconfigs.RegistrationKind]runtime.Object{}
|
||||||
|
|
||||||
if err := DetectUnsupportedVersion(b); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
gvkmap, err := kubeadmutil.SplitYAMLDocuments(b)
|
gvkmap, err := kubeadmutil.SplitYAMLDocuments(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for gvk, fileContent := range gvkmap {
|
for gvk, fileContent := range gvkmap {
|
||||||
|
// first, check if this GVK is supported one
|
||||||
|
if err := ValidateSupportedVersion(gvk.GroupVersion()); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// verify the validity of the YAML
|
// verify the validity of the YAML
|
||||||
strict.VerifyUnmarshalStrict(fileContent, gvk)
|
strict.VerifyUnmarshalStrict(fileContent, gvk)
|
||||||
|
|
||||||
|
|
|
@ -72,10 +72,6 @@ func JoinConfigFileAndDefaultsToInternalConfig(cfgPath string, defaultversionedc
|
||||||
return nil, errors.Wrapf(err, "unable to read config from %q ", cfgPath)
|
return nil, errors.Wrapf(err, "unable to read config from %q ", cfgPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := DetectUnsupportedVersion(b); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
gvkmap, err := kubeadmutil.SplitYAMLDocuments(b)
|
gvkmap, err := kubeadmutil.SplitYAMLDocuments(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -83,11 +79,20 @@ func JoinConfigFileAndDefaultsToInternalConfig(cfgPath string, defaultversionedc
|
||||||
|
|
||||||
joinBytes := []byte{}
|
joinBytes := []byte{}
|
||||||
for gvk, bytes := range gvkmap {
|
for gvk, bytes := range gvkmap {
|
||||||
if gvk.Kind == constants.JoinConfigurationKind {
|
// not interested in anything other than JoinConfiguration
|
||||||
joinBytes = bytes
|
if gvk.Kind != constants.JoinConfigurationKind {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if this version is supported one
|
||||||
|
if err := ValidateSupportedVersion(gvk.GroupVersion()); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// verify the validity of the YAML
|
// verify the validity of the YAML
|
||||||
strict.VerifyUnmarshalStrict(bytes, gvk)
|
strict.VerifyUnmarshalStrict(bytes, gvk)
|
||||||
}
|
|
||||||
|
joinBytes = bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(joinBytes) == 0 {
|
if len(joinBytes) == 0 {
|
||||||
|
|
Loading…
Reference in New Issue