k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/cmd/kubeadm/app/features/features.go (about)

     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  	// WaitForAllControlPlaneComponents is expected to be alpha in v1.30
    40  	WaitForAllControlPlaneComponents = "WaitForAllControlPlaneComponents"
    41  )
    42  
    43  // InitFeatureGates are the default feature gates for the init command
    44  var InitFeatureGates = FeatureList{
    45  	PublicKeysECDSA: {
    46  		FeatureSpec: featuregate.FeatureSpec{Default: false, PreRelease: featuregate.Deprecated},
    47  		DeprecationMessage: "The PublicKeysECDSA feature gate is deprecated and will be removed when v1beta3 is removed." +
    48  			" v1beta4 supports a new option 'ClusterConfiguration.EncryptionAlgorithm'.",
    49  	},
    50  	RootlessControlPlane:             {FeatureSpec: featuregate.FeatureSpec{Default: false, PreRelease: featuregate.Alpha}},
    51  	EtcdLearnerMode:                  {FeatureSpec: featuregate.FeatureSpec{Default: true, PreRelease: featuregate.Beta}},
    52  	WaitForAllControlPlaneComponents: {FeatureSpec: featuregate.FeatureSpec{Default: false, PreRelease: featuregate.Alpha}},
    53  }
    54  
    55  // Feature represents a feature being gated
    56  type Feature struct {
    57  	featuregate.FeatureSpec
    58  	MinimumVersion     *version.Version
    59  	HiddenInHelpText   bool
    60  	DeprecationMessage string
    61  }
    62  
    63  // FeatureList represents a list of feature gates
    64  type FeatureList map[string]Feature
    65  
    66  // ValidateVersion ensures that a feature gate list is compatible with the chosen Kubernetes version
    67  func ValidateVersion(allFeatures FeatureList, requestedFeatures map[string]bool, requestedVersion string) error {
    68  	if requestedVersion == "" {
    69  		return nil
    70  	}
    71  	parsedExpVersion, err := version.ParseSemantic(requestedVersion)
    72  	if err != nil {
    73  		return errors.Wrapf(err, "error parsing version %s", requestedVersion)
    74  	}
    75  	for k := range requestedFeatures {
    76  		if minVersion := allFeatures[k].MinimumVersion; minVersion != nil {
    77  			if !parsedExpVersion.AtLeast(minVersion) {
    78  				return errors.Errorf(
    79  					"the requested Kubernetes version (%s) is incompatible with the %s feature gate, which needs %s as a minimum",
    80  					requestedVersion, k, minVersion)
    81  			}
    82  		}
    83  	}
    84  	return nil
    85  }
    86  
    87  // Enabled indicates whether a feature name has been enabled
    88  func Enabled(featureList map[string]bool, featureName string) bool {
    89  	if enabled, ok := featureList[featureName]; ok {
    90  		return enabled
    91  	}
    92  	return InitFeatureGates[featureName].Default
    93  }
    94  
    95  // Supports indicates whether a feature name is supported on the given
    96  // feature set
    97  func Supports(featureList FeatureList, featureName string) bool {
    98  	for k := range featureList {
    99  		if featureName == k {
   100  			return true
   101  		}
   102  	}
   103  	return false
   104  }
   105  
   106  // KnownFeatures returns a slice of strings describing the FeatureList features.
   107  func KnownFeatures(f *FeatureList) []string {
   108  	var known []string
   109  	for k, v := range *f {
   110  		if v.HiddenInHelpText {
   111  			continue
   112  		}
   113  
   114  		pre := ""
   115  		if v.PreRelease != featuregate.GA {
   116  			pre = fmt.Sprintf("%s - ", v.PreRelease)
   117  		}
   118  		known = append(known, fmt.Sprintf("%s=true|false (%sdefault=%t)", k, pre, v.Default))
   119  	}
   120  	sort.Strings(known)
   121  	return known
   122  }
   123  
   124  // NewFeatureGate parses a string of the form "key1=value1,key2=value2,..." into a
   125  // map[string]bool of known keys or returns an error.
   126  func NewFeatureGate(f *FeatureList, value string) (map[string]bool, error) {
   127  	featureGate := map[string]bool{}
   128  	for _, s := range strings.Split(value, ",") {
   129  		if len(s) == 0 {
   130  			continue
   131  		}
   132  
   133  		arr := strings.SplitN(s, "=", 2)
   134  		if len(arr) != 2 {
   135  			return nil, errors.Errorf("missing bool value for feature-gate key:%s", s)
   136  		}
   137  
   138  		k := strings.TrimSpace(arr[0])
   139  		v := strings.TrimSpace(arr[1])
   140  
   141  		featureSpec, ok := (*f)[k]
   142  		if !ok {
   143  			return nil, errors.Errorf("unrecognized feature-gate key: %s", k)
   144  		}
   145  
   146  		if featureSpec.PreRelease == featuregate.Deprecated {
   147  			klog.Warningf("Setting deprecated feature gate %s=%s. It will be removed in a future release.", k, v)
   148  		}
   149  
   150  		boolValue, err := strconv.ParseBool(v)
   151  		if err != nil {
   152  			return nil, errors.Errorf("invalid value %v for feature-gate key: %s, use true|false instead", v, k)
   153  		}
   154  		featureGate[k] = boolValue
   155  	}
   156  
   157  	return featureGate, nil
   158  }
   159  
   160  // CheckDeprecatedFlags takes a list of existing feature gate flags and validates against the current feature flag set.
   161  // It used during upgrades for ensuring consistency of feature gates used in an existing cluster, that might
   162  // be created with a previous version of kubeadm, with the set of features currently supported by kubeadm
   163  func CheckDeprecatedFlags(f *FeatureList, features map[string]bool) map[string]string {
   164  	deprecatedMsg := map[string]string{}
   165  	for k := range features {
   166  		featureSpec, ok := (*f)[k]
   167  		if !ok {
   168  			// This case should never happen, it is implemented only as a sentinel
   169  			// for removal of flags executed when flags are still in use (always before deprecate, then after one cycle remove)
   170  			deprecatedMsg[k] = fmt.Sprintf("Unknown feature gate flag: %s", k)
   171  		}
   172  
   173  		if featureSpec.PreRelease == featuregate.Deprecated {
   174  			if _, ok := deprecatedMsg[k]; !ok {
   175  				deprecatedMsg[k] = featureSpec.DeprecationMessage
   176  			}
   177  		}
   178  	}
   179  
   180  	return deprecatedMsg
   181  }