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/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
netutil "k8s.io/apimachinery/pkg/util/net"
|
||||
"k8s.io/apimachinery/pkg/util/version"
|
||||
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
|
||||
func DetectUnsupportedVersion(b []byte) error {
|
||||
gvks, err := kubeadmutil.GroupVersionKindsFromBytes(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:
|
||||
// ValidateSupportedVersion checks if a supplied GroupVersion is not on the list of unsupported GVs. If it is, an error is returned.
|
||||
func ValidateSupportedVersion(gv schema.GroupVersion) error {
|
||||
// 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 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/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
|
||||
knownKinds := map[string]bool{}
|
||||
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)
|
||||
if useKubeadmVersion := oldKnownAPIVersions[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)
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -17,187 +17,66 @@ limitations under the License.
|
|||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/lithammer/dedent"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
)
|
||||
|
||||
var files = map[string][]byte{
|
||||
"Master_v1alpha1": []byte(`
|
||||
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 TestValidateSupportedVersion(t *testing.T) {
|
||||
const KubeadmGroupName = "kubeadm.k8s.io"
|
||||
|
||||
func TestDetectUnsupportedVersion(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
fileContents []byte
|
||||
tests := []struct {
|
||||
gv schema.GroupVersion
|
||||
expectedErr bool
|
||||
}{
|
||||
{
|
||||
name: "Master_v1alpha1",
|
||||
fileContents: files["Master_v1alpha1"],
|
||||
gv: schema.GroupVersion{
|
||||
Group: KubeadmGroupName,
|
||||
Version: "v1alpha1",
|
||||
},
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "Node_v1alpha1",
|
||||
fileContents: files["Node_v1alpha1"],
|
||||
gv: schema.GroupVersion{
|
||||
Group: KubeadmGroupName,
|
||||
Version: "v1alpha2",
|
||||
},
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "Master_v1alpha2",
|
||||
fileContents: files["Master_v1alpha2"],
|
||||
expectedErr: true,
|
||||
gv: schema.GroupVersion{
|
||||
Group: KubeadmGroupName,
|
||||
Version: "v1alpha3",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Node_v1alpha2",
|
||||
fileContents: files["Node_v1alpha2"],
|
||||
expectedErr: true,
|
||||
gv: schema.GroupVersion{
|
||||
Group: KubeadmGroupName,
|
||||
Version: "v1beta1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Init_v1alpha3",
|
||||
fileContents: files["Init_v1alpha3"],
|
||||
gv: schema.GroupVersion{
|
||||
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 {
|
||||
t.Run(rt.name, func(t2 *testing.T) {
|
||||
|
||||
err := DetectUnsupportedVersion(rt.fileContents)
|
||||
if (err != nil) != rt.expectedErr {
|
||||
t2.Errorf("expected error: %t, actual: %t", rt.expectedErr, err != nil)
|
||||
t.Run(rt.gv.String(), func(t *testing.T) {
|
||||
err := ValidateSupportedVersion(rt.gv)
|
||||
if rt.expectedErr && err == nil {
|
||||
t.Error("unexpected success")
|
||||
} 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
|
||||
decodedComponentConfigObjects := map[componentconfigs.RegistrationKind]runtime.Object{}
|
||||
|
||||
if err := DetectUnsupportedVersion(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gvkmap, err := kubeadmutil.SplitYAMLDocuments(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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
|
||||
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)
|
||||
}
|
||||
|
||||
if err := DetectUnsupportedVersion(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gvkmap, err := kubeadmutil.SplitYAMLDocuments(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -83,11 +79,20 @@ func JoinConfigFileAndDefaultsToInternalConfig(cfgPath string, defaultversionedc
|
|||
|
||||
joinBytes := []byte{}
|
||||
for gvk, bytes := range gvkmap {
|
||||
if gvk.Kind == constants.JoinConfigurationKind {
|
||||
joinBytes = bytes
|
||||
// not interested in anything other than JoinConfiguration
|
||||
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
|
||||
strict.VerifyUnmarshalStrict(bytes, gvk)
|
||||
}
|
||||
|
||||
joinBytes = bytes
|
||||
}
|
||||
|
||||
if len(joinBytes) == 0 {
|
||||
|
|
Loading…
Reference in New Issue