diff --git a/pkg/apis/kubeone/validation/validation.go b/pkg/apis/kubeone/validation/validation.go index 52ed015b3..1e3b50d80 100644 --- a/pkg/apis/kubeone/validation/validation.go +++ b/pkg/apis/kubeone/validation/validation.go @@ -30,6 +30,18 @@ import ( "k8s.io/apimachinery/pkg/util/validation/field" ) +const ( + // lowerVersionConstraint defines a semver constraint that validates Kubernetes versions against a lower bound + lowerVersionConstraint = ">= 1.19" + // upperVersionConstraint defines a semver constraint that validates Kubernetes versions against an upper bound + upperVersionConstraint = "<= 1.23" +) + +var ( + lowerConstraint = mustParseConstraint(lowerVersionConstraint) + upperConstraint = mustParseConstraint(upperVersionConstraint) +) + // ValidateKubeOneCluster validates the KubeOneCluster object func ValidateKubeOneCluster(c kubeone.KubeOneCluster) field.ErrorList { allErrs := field.ErrorList{} @@ -183,13 +195,23 @@ func ValidateVersionConfig(version kubeone.VersionConfig, fldPath *field.Path) f allErrs = append(allErrs, field.Invalid(fldPath.Child("kubernetes"), version, ".versions.kubernetes is not a semver string")) return allErrs } - if v.Major() != 1 || v.Minor() < 19 { - allErrs = append(allErrs, field.Invalid(fldPath.Child("kubernetes"), version, "kubernetes versions lower than 1.19 are not supported. You need to use an older KubeOne version to upgrade your cluster to v1.19. Please refer to the Compatibility section of docs for more details.")) - } + if strings.HasPrefix(version.Kubernetes, "v") { allErrs = append(allErrs, field.Invalid(fldPath.Child("kubernetes"), version, ".versions.kubernetes can't start with a leading 'v'")) } + if valid, errs := lowerConstraint.Validate(v); !valid { + for _, err := range errs { + allErrs = append(allErrs, field.Invalid(fldPath.Child("kubernetes"), version, fmt.Sprintf("kubernetes version does not satisfy version constraint '%s': %s. You need to use an older KubeOne version to upgrade your cluster to a supported version. Please refer to the Compatibility section of docs for more details.", lowerVersionConstraint, err.Error()))) + } + } + + if valid, errs := upperConstraint.Validate(v); !valid { + for _, err := range errs { + allErrs = append(allErrs, field.Invalid(fldPath.Child("kubernetes"), version, fmt.Sprintf("kubernetes version does not satisfy version constraint '%s': %s. This version is not yet supported. Please refer to the Compatibility section of docs for more details.", upperVersionConstraint, err.Error()))) + } + } + return allErrs } @@ -516,3 +538,12 @@ func ValidateAssetConfiguration(a *kubeone.AssetConfiguration, fldPath *field.Pa return allErrs } + +func mustParseConstraint(constraint string) *semver.Constraints { + result, err := semver.NewConstraint(constraint) + if err != nil { + panic(err) + } + + return result +} diff --git a/pkg/apis/kubeone/validation/validation_test.go b/pkg/apis/kubeone/validation/validation_test.go index 2cf8af29d..ddd183df0 100644 --- a/pkg/apis/kubeone/validation/validation_test.go +++ b/pkg/apis/kubeone/validation/validation_test.go @@ -629,6 +629,13 @@ func TestValidateVersionConfig(t *testing.T) { versionConfig kubeone.VersionConfig expectedError bool }{ + { + name: "valid version config (1.23.1)", + versionConfig: kubeoneapi.VersionConfig{ + Kubernetes: "1.23.1", + }, + expectedError: false, + }, { name: "valid version config (1.22.1)", versionConfig: kubeone.VersionConfig{ @@ -678,6 +685,13 @@ func TestValidateVersionConfig(t *testing.T) { }, expectedError: false, }, + { + name: "not supported kubernetes version (1.24.0)", + versionConfig: kubeoneapi.VersionConfig{ + Kubernetes: "1.24.0", + }, + expectedError: true, + }, { name: "not supported kubernetes version (1.18.19)", versionConfig: kubeone.VersionConfig{