mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-10 12:32:03 +00:00
Merge pull request #54539 from jamiehannaford/add-ha-feature-gate
Automatic merge from submit-queue (batch tested with PRs 54593, 54607, 54539, 54105). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Add HA feature gate and minVersion validation **What this PR does / why we need it**: As we add more feature gates, there might be occasions where a feature is only available on newer releases of K8s. If a user makes a mistake, we should notify them as soon as possible in the init procedure and not them go down the path of hard-to-debug component issues. Specifically with HA, we ideally need the new `TaintNodesByCondition` (added in v1.8.0 but working in v1.9.0). **Which issue this PR fixes:** kubernetes/kubeadm#261 kubernetes/kubeadm#277 **Release note**: ```release-note Feature gates now check minimum versions ``` /cc @kubernetes/sig-cluster-lifecycle-pr-reviews @luxas @timothysc
This commit is contained in:
commit
633ca56494
@ -231,6 +231,10 @@ func NewInit(cfgPath string, cfg *kubeadmapi.MasterConfiguration, skipPreFlight,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := features.ValidateVersion(features.InitFeatureGates, cfg.FeatureGates, cfg.KubernetesVersion); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Printf("[init] Using Kubernetes version: %s\n", cfg.KubernetesVersion)
|
fmt.Printf("[init] Using Kubernetes version: %s\n", cfg.KubernetesVersion)
|
||||||
fmt.Printf("[init] Using Authorization modes: %v\n", cfg.AuthorizationModes)
|
fmt.Printf("[init] Using Authorization modes: %v\n", cfg.AuthorizationModes)
|
||||||
|
|
||||||
|
@ -10,7 +10,10 @@ go_library(
|
|||||||
name = "go_default_library",
|
name = "go_default_library",
|
||||||
srcs = ["features.go"],
|
srcs = ["features.go"],
|
||||||
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/features",
|
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/features",
|
||||||
deps = ["//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library"],
|
deps = [
|
||||||
|
"//pkg/util/version:go_default_library",
|
||||||
|
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
filegroup(
|
filegroup(
|
||||||
|
@ -23,21 +23,61 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
|
"k8s.io/kubernetes/pkg/util/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// HighAvailability is alpha in v1.9
|
||||||
|
HighAvailability = "HighAvailability"
|
||||||
|
|
||||||
// SelfHosting is beta in v1.8
|
// SelfHosting is beta in v1.8
|
||||||
SelfHosting utilfeature.Feature = "SelfHosting"
|
SelfHosting = "SelfHosting"
|
||||||
|
|
||||||
// StoreCertsInSecrets is alpha in v1.8
|
// StoreCertsInSecrets is alpha in v1.8
|
||||||
StoreCertsInSecrets utilfeature.Feature = "StoreCertsInSecrets"
|
StoreCertsInSecrets = "StoreCertsInSecrets"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var v190 = version.MustParseSemantic("v1.9.0")
|
||||||
|
|
||||||
|
// InitFeatureGates are the default feature gates for the init command
|
||||||
|
var InitFeatureGates = FeatureList{
|
||||||
|
SelfHosting: {FeatureSpec: utilfeature.FeatureSpec{Default: false, PreRelease: utilfeature.Beta}},
|
||||||
|
StoreCertsInSecrets: {FeatureSpec: utilfeature.FeatureSpec{Default: false, PreRelease: utilfeature.Alpha}},
|
||||||
|
HighAvailability: {FeatureSpec: utilfeature.FeatureSpec{Default: false, PreRelease: utilfeature.Alpha}, MinimumVersion: v190},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Feature represents a feature being gated
|
||||||
|
type Feature struct {
|
||||||
|
utilfeature.FeatureSpec
|
||||||
|
MinimumVersion *version.Version
|
||||||
|
}
|
||||||
|
|
||||||
// FeatureList represents a list of feature gates
|
// FeatureList represents a list of feature gates
|
||||||
type FeatureList map[utilfeature.Feature]utilfeature.FeatureSpec
|
type FeatureList map[string]Feature
|
||||||
|
|
||||||
|
// ValidateVersion ensures that a feature gate list is compatible with the chosen kubernetes version
|
||||||
|
func ValidateVersion(allFeatures FeatureList, requestedFeatures map[string]bool, requestedVersion string) error {
|
||||||
|
if requestedVersion == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
parsedExpVersion, err := version.ParseSemantic(requestedVersion)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error parsing version %s: %v", requestedVersion, err)
|
||||||
|
}
|
||||||
|
for k := range requestedFeatures {
|
||||||
|
if minVersion := allFeatures[k].MinimumVersion; minVersion != nil {
|
||||||
|
if !parsedExpVersion.AtLeast(minVersion) {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"the requested kubernetes version (%s) is incompatible with the %s feature gate, which needs %s as a minimum",
|
||||||
|
requestedVersion, k, minVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Enabled indicates whether a feature name has been enabled
|
// Enabled indicates whether a feature name has been enabled
|
||||||
func Enabled(featureList map[string]bool, featureName utilfeature.Feature) bool {
|
func Enabled(featureList map[string]bool, featureName string) bool {
|
||||||
return featureList[string(featureName)]
|
return featureList[string(featureName)]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,12 +101,6 @@ func Keys(featureList FeatureList) []string {
|
|||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitFeatureGates are the default feature gates for the init command
|
|
||||||
var InitFeatureGates = FeatureList{
|
|
||||||
SelfHosting: {Default: false, PreRelease: utilfeature.Alpha},
|
|
||||||
StoreCertsInSecrets: {Default: false, PreRelease: utilfeature.Alpha},
|
|
||||||
}
|
|
||||||
|
|
||||||
// KnownFeatures returns a slice of strings describing the FeatureList features.
|
// KnownFeatures returns a slice of strings describing the FeatureList features.
|
||||||
func KnownFeatures(f *FeatureList) []string {
|
func KnownFeatures(f *FeatureList) []string {
|
||||||
var known []string
|
var known []string
|
||||||
|
@ -25,9 +25,9 @@ import (
|
|||||||
|
|
||||||
func TestKnownFeatures(t *testing.T) {
|
func TestKnownFeatures(t *testing.T) {
|
||||||
var someFeatures = FeatureList{
|
var someFeatures = FeatureList{
|
||||||
"feature2": {Default: true, PreRelease: utilfeature.Alpha},
|
"feature2": {FeatureSpec: utilfeature.FeatureSpec{Default: true, PreRelease: utilfeature.Alpha}},
|
||||||
"feature1": {Default: false, PreRelease: utilfeature.Beta},
|
"feature1": {FeatureSpec: utilfeature.FeatureSpec{Default: false, PreRelease: utilfeature.Beta}},
|
||||||
"feature3": {Default: false, PreRelease: utilfeature.GA},
|
"feature3": {FeatureSpec: utilfeature.FeatureSpec{Default: false, PreRelease: utilfeature.GA}},
|
||||||
}
|
}
|
||||||
|
|
||||||
r := KnownFeatures(&someFeatures)
|
r := KnownFeatures(&someFeatures)
|
||||||
@ -55,8 +55,8 @@ func TestKnownFeatures(t *testing.T) {
|
|||||||
|
|
||||||
func TestNewFeatureGate(t *testing.T) {
|
func TestNewFeatureGate(t *testing.T) {
|
||||||
var someFeatures = FeatureList{
|
var someFeatures = FeatureList{
|
||||||
"feature1": {Default: false, PreRelease: utilfeature.Beta},
|
"feature1": {FeatureSpec: utilfeature.FeatureSpec{Default: false, PreRelease: utilfeature.Beta}},
|
||||||
"feature2": {Default: true, PreRelease: utilfeature.Alpha},
|
"feature2": {FeatureSpec: utilfeature.FeatureSpec{Default: true, PreRelease: utilfeature.Alpha}},
|
||||||
}
|
}
|
||||||
|
|
||||||
var tests = []struct {
|
var tests = []struct {
|
||||||
@ -117,3 +117,42 @@ func TestNewFeatureGate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidateVersion(t *testing.T) {
|
||||||
|
var someFeatures = FeatureList{
|
||||||
|
"feature1": {FeatureSpec: utilfeature.FeatureSpec{Default: false, PreRelease: utilfeature.Beta}},
|
||||||
|
"feature2": {FeatureSpec: utilfeature.FeatureSpec{Default: true, PreRelease: utilfeature.Alpha}, MinimumVersion: v190},
|
||||||
|
}
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
requestedVersion string
|
||||||
|
requestedFeatures map[string]bool
|
||||||
|
expectedError bool
|
||||||
|
}{
|
||||||
|
{ //no min version
|
||||||
|
requestedFeatures: map[string]bool{"feature1": true},
|
||||||
|
expectedError: false,
|
||||||
|
},
|
||||||
|
{ //min version but correct value given
|
||||||
|
requestedFeatures: map[string]bool{"feature2": true},
|
||||||
|
requestedVersion: "v1.9.0",
|
||||||
|
expectedError: false,
|
||||||
|
},
|
||||||
|
{ //min version and incorrect value given
|
||||||
|
requestedFeatures: map[string]bool{"feature2": true},
|
||||||
|
requestedVersion: "v1.8.2",
|
||||||
|
expectedError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
err := ValidateVersion(someFeatures, test.requestedFeatures, test.requestedVersion)
|
||||||
|
if !test.expectedError && err != nil {
|
||||||
|
t.Errorf("ValidateVersion failed when not expected: %v", err)
|
||||||
|
continue
|
||||||
|
} else if test.expectedError && err == nil {
|
||||||
|
t.Error("ValidateVersion didn't failed when expected")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user