...

Source file src/k8s.io/kubernetes/cmd/kube-controller-manager/app/controllermanager_test.go

Documentation: k8s.io/kubernetes/cmd/kube-controller-manager/app

     1  /*
     2  Copyright 2023 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 app
    18  
    19  import (
    20  	"context"
    21  	"regexp"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/google/go-cmp/cmp"
    26  
    27  	"k8s.io/apimachinery/pkg/util/sets"
    28  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    29  	cpnames "k8s.io/cloud-provider/names"
    30  	"k8s.io/component-base/featuregate"
    31  	featuregatetesting "k8s.io/component-base/featuregate/testing"
    32  	controllermanagercontroller "k8s.io/controller-manager/controller"
    33  	"k8s.io/klog/v2/ktesting"
    34  	"k8s.io/kubernetes/cmd/kube-controller-manager/names"
    35  	"k8s.io/kubernetes/pkg/features"
    36  )
    37  
    38  func TestControllerNamesConsistency(t *testing.T) {
    39  	controllerNameRegexp := regexp.MustCompile("^[a-z]([-a-z]*[a-z])?$")
    40  
    41  	for _, name := range KnownControllers() {
    42  		if !controllerNameRegexp.MatchString(name) {
    43  			t.Errorf("name consistency check failed: controller %q must consist of lower case alphabetic characters or '-', and must start and end with an alphabetic character", name)
    44  		}
    45  		if !strings.HasSuffix(name, "-controller") {
    46  			t.Errorf("name consistency check failed: controller %q must have \"-controller\" suffix", name)
    47  		}
    48  	}
    49  }
    50  
    51  func TestControllerNamesDeclaration(t *testing.T) {
    52  	declaredControllers := sets.NewString(
    53  		names.ServiceAccountTokenController,
    54  		names.EndpointsController,
    55  		names.EndpointSliceController,
    56  		names.EndpointSliceMirroringController,
    57  		names.ReplicationControllerController,
    58  		names.PodGarbageCollectorController,
    59  		names.ResourceQuotaController,
    60  		names.NamespaceController,
    61  		names.ServiceAccountController,
    62  		names.GarbageCollectorController,
    63  		names.DaemonSetController,
    64  		names.JobController,
    65  		names.DeploymentController,
    66  		names.ReplicaSetController,
    67  		names.HorizontalPodAutoscalerController,
    68  		names.DisruptionController,
    69  		names.StatefulSetController,
    70  		names.CronJobController,
    71  		names.CertificateSigningRequestSigningController,
    72  		names.CertificateSigningRequestApprovingController,
    73  		names.CertificateSigningRequestCleanerController,
    74  		names.TTLController,
    75  		names.BootstrapSignerController,
    76  		names.TokenCleanerController,
    77  		names.NodeIpamController,
    78  		names.NodeLifecycleController,
    79  		names.TaintEvictionController,
    80  		cpnames.ServiceLBController,
    81  		cpnames.NodeRouteController,
    82  		cpnames.CloudNodeLifecycleController,
    83  		names.PersistentVolumeBinderController,
    84  		names.PersistentVolumeAttachDetachController,
    85  		names.PersistentVolumeExpanderController,
    86  		names.ClusterRoleAggregationController,
    87  		names.PersistentVolumeClaimProtectionController,
    88  		names.PersistentVolumeProtectionController,
    89  		names.TTLAfterFinishedController,
    90  		names.RootCACertificatePublisherController,
    91  		names.EphemeralVolumeController,
    92  		names.StorageVersionGarbageCollectorController,
    93  		names.ResourceClaimController,
    94  		names.LegacyServiceAccountTokenCleanerController,
    95  		names.ValidatingAdmissionPolicyStatusController,
    96  		names.ServiceCIDRController,
    97  		names.StorageVersionMigratorController,
    98  	)
    99  
   100  	for _, name := range KnownControllers() {
   101  		if !declaredControllers.Has(name) {
   102  			t.Errorf("name declaration check failed: controller name %q should be declared in  \"controller_names.go\" and added to this test", name)
   103  		}
   104  	}
   105  }
   106  
   107  func TestNewControllerDescriptorsShouldNotPanic(t *testing.T) {
   108  	NewControllerDescriptors()
   109  }
   110  
   111  func TestNewControllerDescriptorsAlwaysReturnsDescriptorsForAllControllers(t *testing.T) {
   112  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, "AllAlpha", false)()
   113  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, "AllBeta", false)()
   114  
   115  	controllersWithoutFeatureGates := KnownControllers()
   116  
   117  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, "AllAlpha", true)()
   118  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, "AllBeta", true)()
   119  
   120  	controllersWithFeatureGates := KnownControllers()
   121  
   122  	if diff := cmp.Diff(controllersWithoutFeatureGates, controllersWithFeatureGates); diff != "" {
   123  		t.Errorf("unexpected controllers after enabling feature gates, NewControllerDescriptors should always return all controller descriptors. Controllers should define required feature gates in ControllerDescriptor.requiredFeatureGates. Diff of returned controllers:\n%s", diff)
   124  	}
   125  }
   126  
   127  func TestFeatureGatedControllersShouldNotDefineAliases(t *testing.T) {
   128  	featureGateRegex := regexp.MustCompile("^([a-zA-Z0-9]+)")
   129  
   130  	alphaFeatures := sets.NewString()
   131  	for _, featureText := range utilfeature.DefaultFeatureGate.KnownFeatures() {
   132  		// we have to parse this from KnownFeatures, because usage of mutable FeatureGate is not allowed in unit tests
   133  		feature := featureGateRegex.FindString(featureText)
   134  		if strings.Contains(featureText, string(featuregate.Alpha)) && feature != "AllAlpha" {
   135  			alphaFeatures.Insert(feature)
   136  		}
   137  
   138  	}
   139  
   140  	for name, controller := range NewControllerDescriptors() {
   141  		if len(controller.GetAliases()) == 0 {
   142  			continue
   143  		}
   144  
   145  		requiredFeatureGates := controller.GetRequiredFeatureGates()
   146  		if len(requiredFeatureGates) == 0 {
   147  			continue
   148  		}
   149  
   150  		// DO NOT ADD any new controllers here. one controller is an exception, because it was added before this test was introduced
   151  		if name == names.ResourceClaimController {
   152  			continue
   153  		}
   154  
   155  		areAllRequiredFeaturesAlpha := true
   156  		for _, feature := range requiredFeatureGates {
   157  			if !alphaFeatures.Has(string(feature)) {
   158  				areAllRequiredFeaturesAlpha = false
   159  				break
   160  			}
   161  		}
   162  
   163  		if areAllRequiredFeaturesAlpha {
   164  			t.Errorf("alias check failed: controller name %q should not be aliased as it is still guarded by alpha feature gates (%v) and thus should have only a canonical name", name, requiredFeatureGates)
   165  		}
   166  	}
   167  }
   168  
   169  // TestTaintEvictionControllerGating ensures that it is possible to run taint-manager as a separated controller
   170  // only when the SeparateTaintEvictionController feature is enabled
   171  func TestTaintEvictionControllerGating(t *testing.T) {
   172  	tests := []struct {
   173  		name               string
   174  		enableFeatureGate  bool
   175  		expectInitFuncCall bool
   176  	}{
   177  		{
   178  			name:               "standalone taint-eviction-controller should run when SeparateTaintEvictionController feature gate is enabled",
   179  			enableFeatureGate:  true,
   180  			expectInitFuncCall: true,
   181  		},
   182  		{
   183  			name:               "standalone taint-eviction-controller should not run when SeparateTaintEvictionController feature gate is not enabled",
   184  			enableFeatureGate:  false,
   185  			expectInitFuncCall: false,
   186  		},
   187  	}
   188  
   189  	for _, test := range tests {
   190  		t.Run(test.name, func(t *testing.T) {
   191  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SeparateTaintEvictionController, test.enableFeatureGate)()
   192  			_, ctx := ktesting.NewTestContext(t)
   193  			ctx, cancel := context.WithCancel(ctx)
   194  			defer cancel()
   195  
   196  			controllerCtx := ControllerContext{}
   197  			controllerCtx.ComponentConfig.Generic.Controllers = []string{names.TaintEvictionController}
   198  
   199  			initFuncCalled := false
   200  
   201  			taintEvictionControllerDescriptor := NewControllerDescriptors()[names.TaintEvictionController]
   202  			taintEvictionControllerDescriptor.initFunc = func(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller controllermanagercontroller.Interface, enabled bool, err error) {
   203  				initFuncCalled = true
   204  				return nil, true, nil
   205  			}
   206  
   207  			healthCheck, err := StartController(ctx, controllerCtx, taintEvictionControllerDescriptor, nil)
   208  			if err != nil {
   209  				t.Errorf("starting a TaintEvictionController controller should not return an error")
   210  			}
   211  			if test.expectInitFuncCall != initFuncCalled {
   212  				t.Errorf("TaintEvictionController init call check failed: expected=%v, got=%v", test.expectInitFuncCall, initFuncCalled)
   213  			}
   214  			hasHealthCheck := healthCheck != nil
   215  			expectHealthCheck := test.expectInitFuncCall
   216  			if expectHealthCheck != hasHealthCheck {
   217  				t.Errorf("TaintEvictionController healthCheck check failed: expected=%v, got=%v", expectHealthCheck, hasHealthCheck)
   218  			}
   219  		})
   220  	}
   221  }
   222  

View as plain text