...

Source file src/k8s.io/kubernetes/cmd/kubeadm/app/features/features.go

Documentation: k8s.io/kubernetes/cmd/kubeadm/app/features

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package features
    18  
    19  import (
    20  	"fmt"
    21  	"sort"
    22  	"strconv"
    23  	"strings"
    24  
    25  	"github.com/pkg/errors"
    26  
    27  	"k8s.io/apimachinery/pkg/util/version"
    28  	"k8s.io/component-base/featuregate"
    29  	"k8s.io/klog/v2"
    30  )
    31  
    32  const (
    33  	// PublicKeysECDSA is expected to be alpha in v1.19
    34  	PublicKeysECDSA = "PublicKeysECDSA"
    35  	// RootlessControlPlane is expected to be in alpha in v1.22
    36  	RootlessControlPlane = "RootlessControlPlane"
    37  	// EtcdLearnerMode is expected to be in alpha in v1.27, beta in v1.29
    38  	EtcdLearnerMode = "EtcdLearnerMode"
    39  	// UpgradeAddonsBeforeControlPlane is expected to be in deprecated in v1.28 and will be removed in future release
    40  	UpgradeAddonsBeforeControlPlane = "UpgradeAddonsBeforeControlPlane"
    41  	// WaitForAllControlPlaneComponents is expected to be alpha in v1.30
    42  	WaitForAllControlPlaneComponents = "WaitForAllControlPlaneComponents"
    43  )
    44  
    45  // InitFeatureGates are the default feature gates for the init command
    46  var InitFeatureGates = FeatureList{
    47  	PublicKeysECDSA: {
    48  		FeatureSpec: featuregate.FeatureSpec{Default: false, PreRelease: featuregate.Deprecated},
    49  		DeprecationMessage: "The PublicKeysECDSA feature gate is deprecated and will be removed when v1beta3 is removed." +
    50  			" v1beta4 supports a new option 'ClusterConfiguration.EncryptionAlgorithm'.",
    51  	},
    52  	RootlessControlPlane: {FeatureSpec: featuregate.FeatureSpec{Default: false, PreRelease: featuregate.Alpha}},
    53  	EtcdLearnerMode:      {FeatureSpec: featuregate.FeatureSpec{Default: true, PreRelease: featuregate.Beta}},
    54  	UpgradeAddonsBeforeControlPlane: {
    55  		FeatureSpec:        featuregate.FeatureSpec{Default: false, PreRelease: featuregate.Deprecated},
    56  		DeprecationMessage: "The UpgradeAddonsBeforeControlPlane feature gate is deprecated and will be removed in a future release.",
    57  	},
    58  	WaitForAllControlPlaneComponents: {FeatureSpec: featuregate.FeatureSpec{Default: false, PreRelease: featuregate.Alpha}},
    59  }
    60  
    61  // Feature represents a feature being gated
    62  type Feature struct {
    63  	featuregate.FeatureSpec
    64  	MinimumVersion     *version.Version
    65  	HiddenInHelpText   bool
    66  	DeprecationMessage string
    67  }
    68  
    69  // FeatureList represents a list of feature gates
    70  type FeatureList map[string]Feature
    71  
    72  // ValidateVersion ensures that a feature gate list is compatible with the chosen Kubernetes version
    73  func ValidateVersion(allFeatures FeatureList, requestedFeatures map[string]bool, requestedVersion string) error {
    74  	if requestedVersion == "" {
    75  		return nil
    76  	}
    77  	parsedExpVersion, err := version.ParseSemantic(requestedVersion)
    78  	if err != nil {
    79  		return errors.Wrapf(err, "error parsing version %s", requestedVersion)
    80  	}
    81  	for k := range requestedFeatures {
    82  		if minVersion := allFeatures[k].MinimumVersion; minVersion != nil {
    83  			if !parsedExpVersion.AtLeast(minVersion) {
    84  				return errors.Errorf(
    85  					"the requested Kubernetes version (%s) is incompatible with the %s feature gate, which needs %s as a minimum",
    86  					requestedVersion, k, minVersion)
    87  			}
    88  		}
    89  	}
    90  	return nil
    91  }
    92  
    93  // Enabled indicates whether a feature name has been enabled
    94  func Enabled(featureList map[string]bool, featureName string) bool {
    95  	if enabled, ok := featureList[featureName]; ok {
    96  		return enabled
    97  	}
    98  	return InitFeatureGates[featureName].Default
    99  }
   100  
   101  // Supports indicates whether a feature name is supported on the given
   102  // feature set
   103  func Supports(featureList FeatureList, featureName string) bool {
   104  	for k := range featureList {
   105  		if featureName == k {
   106  			return true
   107  		}
   108  	}
   109  	return false
   110  }
   111  
   112  // KnownFeatures returns a slice of strings describing the FeatureList features.
   113  func KnownFeatures(f *FeatureList) []string {
   114  	var known []string
   115  	for k, v := range *f {
   116  		if v.HiddenInHelpText {
   117  			continue
   118  		}
   119  
   120  		pre := ""
   121  		if v.PreRelease != featuregate.GA {
   122  			pre = fmt.Sprintf("%s - ", v.PreRelease)
   123  		}
   124  		known = append(known, fmt.Sprintf("%s=true|false (%sdefault=%t)", k, pre, v.Default))
   125  	}
   126  	sort.Strings(known)
   127  	return known
   128  }
   129  
   130  // NewFeatureGate parses a string of the form "key1=value1,key2=value2,..." into a
   131  // map[string]bool of known keys or returns an error.
   132  func NewFeatureGate(f *FeatureList, value string) (map[string]bool, error) {
   133  	featureGate := map[string]bool{}
   134  	for _, s := range strings.Split(value, ",") {
   135  		if len(s) == 0 {
   136  			continue
   137  		}
   138  
   139  		arr := strings.SplitN(s, "=", 2)
   140  		if len(arr) != 2 {
   141  			return nil, errors.Errorf("missing bool value for feature-gate key:%s", s)
   142  		}
   143  
   144  		k := strings.TrimSpace(arr[0])
   145  		v := strings.TrimSpace(arr[1])
   146  
   147  		featureSpec, ok := (*f)[k]
   148  		if !ok {
   149  			return nil, errors.Errorf("unrecognized feature-gate key: %s", k)
   150  		}
   151  
   152  		if featureSpec.PreRelease == featuregate.Deprecated {
   153  			klog.Warningf("Setting deprecated feature gate %s=%s. It will be removed in a future release.", k, v)
   154  		}
   155  
   156  		boolValue, err := strconv.ParseBool(v)
   157  		if err != nil {
   158  			return nil, errors.Errorf("invalid value %v for feature-gate key: %s, use true|false instead", v, k)
   159  		}
   160  		featureGate[k] = boolValue
   161  	}
   162  
   163  	return featureGate, nil
   164  }
   165  
   166  // CheckDeprecatedFlags takes a list of existing feature gate flags and validates against the current feature flag set.
   167  // It used during upgrades for ensuring consistency of feature gates used in an existing cluster, that might
   168  // be created with a previous version of kubeadm, with the set of features currently supported by kubeadm
   169  func CheckDeprecatedFlags(f *FeatureList, features map[string]bool) map[string]string {
   170  	deprecatedMsg := map[string]string{}
   171  	for k := range features {
   172  		featureSpec, ok := (*f)[k]
   173  		if !ok {
   174  			// This case should never happen, it is implemented only as a sentinel
   175  			// for removal of flags executed when flags are still in use (always before deprecate, then after one cycle remove)
   176  			deprecatedMsg[k] = fmt.Sprintf("Unknown feature gate flag: %s", k)
   177  		}
   178  
   179  		if featureSpec.PreRelease == featuregate.Deprecated {
   180  			if _, ok := deprecatedMsg[k]; !ok {
   181  				deprecatedMsg[k] = featureSpec.DeprecationMessage
   182  			}
   183  		}
   184  	}
   185  
   186  	return deprecatedMsg
   187  }
   188  

View as plain text