Merge pull request #120371 from atiratree/encapsulate-kcm-controllers

Encapsulate KCM controllers with their metadata
This commit is contained in:
Kubernetes Prow Robot 2023-10-27 21:18:17 +02:00 committed by GitHub
commit ee474e6ec5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 763 additions and 294 deletions

View File

@ -27,13 +27,21 @@ import (
"k8s.io/client-go/util/flowcontrol"
"k8s.io/controller-manager/controller"
"k8s.io/klog/v2"
"k8s.io/kubernetes/cmd/kube-controller-manager/names"
"k8s.io/kubernetes/pkg/controller/daemon"
"k8s.io/kubernetes/pkg/controller/deployment"
"k8s.io/kubernetes/pkg/controller/replicaset"
"k8s.io/kubernetes/pkg/controller/statefulset"
)
func startDaemonSetController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newDaemonSetControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.DaemonSetController,
aliases: []string{"daemonset"},
initFunc: startDaemonSetController,
}
}
func startDaemonSetController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
dsc, err := daemon.NewDaemonSetsController(
ctx,
controllerContext.InformerFactory.Apps().V1().DaemonSets(),
@ -50,7 +58,14 @@ func startDaemonSetController(ctx context.Context, controllerContext ControllerC
return nil, true, nil
}
func startStatefulSetController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newStatefulSetControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.StatefulSetController,
aliases: []string{"statefulset"},
initFunc: startStatefulSetController,
}
}
func startStatefulSetController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
go statefulset.NewStatefulSetController(
ctx,
controllerContext.InformerFactory.Core().V1().Pods(),
@ -62,7 +77,15 @@ func startStatefulSetController(ctx context.Context, controllerContext Controlle
return nil, true, nil
}
func startReplicaSetController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newReplicaSetControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.ReplicaSetController,
aliases: []string{"replicaset"},
initFunc: startReplicaSetController,
}
}
func startReplicaSetController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
go replicaset.NewReplicaSetController(
klog.FromContext(ctx),
controllerContext.InformerFactory.Apps().V1().ReplicaSets(),
@ -73,7 +96,15 @@ func startReplicaSetController(ctx context.Context, controllerContext Controller
return nil, true, nil
}
func startDeploymentController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newDeploymentControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.DeploymentController,
aliases: []string{"deployment"},
initFunc: startDeploymentController,
}
}
func startDeploymentController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
dc, err := deployment.NewDeploymentController(
ctx,
controllerContext.InformerFactory.Apps().V1().Deployments(),

View File

@ -26,6 +26,7 @@ import (
"k8s.io/client-go/dynamic"
"k8s.io/client-go/scale"
"k8s.io/controller-manager/controller"
"k8s.io/kubernetes/cmd/kube-controller-manager/names"
"k8s.io/kubernetes/pkg/controller/podautoscaler"
"k8s.io/kubernetes/pkg/controller/podautoscaler/metrics"
"k8s.io/kubernetes/pkg/features"
@ -35,11 +36,15 @@ import (
"k8s.io/metrics/pkg/client/external_metrics"
)
func startHPAController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
return startHPAControllerWithRESTClient(ctx, controllerContext)
func newHorizontalPodAutoscalerControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.HorizontalPodAutoscalerController,
aliases: []string{"horizontalpodautoscaling"},
initFunc: startHorizontalPodAutoscalerControllerWithRESTClient,
}
}
func startHPAControllerWithRESTClient(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func startHorizontalPodAutoscalerControllerWithRESTClient(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
clientConfig := controllerContext.ClientBuilder.ConfigOrDie("horizontal-pod-autoscaler")
hpaClient := controllerContext.ClientBuilder.ClientOrDie("horizontal-pod-autoscaler")

View File

@ -24,11 +24,20 @@ import (
"fmt"
"k8s.io/controller-manager/controller"
"k8s.io/kubernetes/cmd/kube-controller-manager/names"
"k8s.io/kubernetes/pkg/controller/cronjob"
"k8s.io/kubernetes/pkg/controller/job"
)
func startJobController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newJobControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.JobController,
aliases: []string{"job"},
initFunc: startJobController,
}
}
func startJobController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
jobController, err := job.NewController(
ctx,
controllerContext.InformerFactory.Core().V1().Pods(),
@ -42,7 +51,15 @@ func startJobController(ctx context.Context, controllerContext ControllerContext
return nil, true, nil
}
func startCronJobController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newCronJobControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.CronJobController,
aliases: []string{"cronjob"},
initFunc: startCronJobController,
}
}
func startCronJobController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
cj2c, err := cronjob.NewControllerV2(ctx, controllerContext.InformerFactory.Batch().V1().Jobs(),
controllerContext.InformerFactory.Batch().V1().CronJobs(),
controllerContext.ClientBuilder.ClientOrDie("cronjob-controller"),

View File

@ -21,10 +21,19 @@ import (
"fmt"
"k8s.io/controller-manager/controller"
"k8s.io/kubernetes/cmd/kube-controller-manager/names"
"k8s.io/kubernetes/pkg/controller/bootstrap"
)
func startBootstrapSignerController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newBootstrapSignerControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.BootstrapSignerController,
aliases: []string{"bootstrapsigner"},
initFunc: startBootstrapSignerController,
isDisabledByDefault: true,
}
}
func startBootstrapSignerController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
bsc, err := bootstrap.NewSigner(
controllerContext.ClientBuilder.ClientOrDie("bootstrap-signer"),
controllerContext.InformerFactory.Core().V1().Secrets(),
@ -38,7 +47,15 @@ func startBootstrapSignerController(ctx context.Context, controllerContext Contr
return nil, true, nil
}
func startTokenCleanerController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newTokenCleanerControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.TokenCleanerController,
aliases: []string{"tokencleaner"},
initFunc: startTokenCleanerController,
isDisabledByDefault: true,
}
}
func startTokenCleanerController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
tcc, err := bootstrap.NewTokenCleaner(
controllerContext.ClientBuilder.ClientOrDie("token-cleaner"),
controllerContext.InformerFactory.Core().V1().Secrets(),

View File

@ -25,6 +25,7 @@ import (
"k8s.io/controller-manager/controller"
"k8s.io/klog/v2"
"k8s.io/kubernetes/cmd/kube-controller-manager/names"
"k8s.io/kubernetes/pkg/controller/certificates/approver"
"k8s.io/kubernetes/pkg/controller/certificates/cleaner"
"k8s.io/kubernetes/pkg/controller/certificates/rootcacertpublisher"
@ -32,7 +33,15 @@ import (
csrsigningconfig "k8s.io/kubernetes/pkg/controller/certificates/signer/config"
)
func startCSRSigningController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newCertificateSigningRequestSigningControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.CertificateSigningRequestSigningController,
aliases: []string{"csrsigning"},
initFunc: startCertificateSigningRequestSigningController,
}
}
func startCertificateSigningRequestSigningController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
logger := klog.FromContext(ctx)
missingSingleSigningFile := controllerContext.ComponentConfig.CSRSigningController.ClusterSigningCertFile == "" || controllerContext.ComponentConfig.CSRSigningController.ClusterSigningKeyFile == ""
if missingSingleSigningFile && !anySpecificFilesSet(controllerContext.ComponentConfig.CSRSigningController) {
@ -148,7 +157,14 @@ func getLegacyUnknownSignerFiles(config csrsigningconfig.CSRSigningControllerCon
return config.ClusterSigningCertFile, config.ClusterSigningKeyFile
}
func startCSRApprovingController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newCertificateSigningRequestApprovingControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.CertificateSigningRequestApprovingController,
aliases: []string{"csrapproving"},
initFunc: startCertificateSigningRequestApprovingController,
}
}
func startCertificateSigningRequestApprovingController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
approver := approver.NewCSRApprovingController(
ctx,
controllerContext.ClientBuilder.ClientOrDie("certificate-controller"),
@ -159,7 +175,14 @@ func startCSRApprovingController(ctx context.Context, controllerContext Controll
return nil, true, nil
}
func startCSRCleanerController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newCertificateSigningRequestCleanerControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.CertificateSigningRequestCleanerController,
aliases: []string{"csrcleaner"},
initFunc: startCertificateSigningRequestCleanerController,
}
}
func startCertificateSigningRequestCleanerController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
cleaner := cleaner.NewCSRCleanerController(
controllerContext.ClientBuilder.ClientOrDie("certificate-controller").CertificatesV1().CertificateSigningRequests(),
controllerContext.InformerFactory.Certificates().V1().CertificateSigningRequests(),
@ -168,7 +191,15 @@ func startCSRCleanerController(ctx context.Context, controllerContext Controller
return nil, true, nil
}
func startRootCACertPublisher(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newRootCACertificatePublisherControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.RootCACertificatePublisherController,
aliases: []string{"root-ca-cert-publisher"},
initFunc: startRootCACertificatePublisherController,
}
}
func startRootCACertificatePublisherController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
var (
rootCA []byte
err error

View File

@ -25,6 +25,7 @@ import (
"math/rand"
"net/http"
"os"
"sort"
"time"
"github.com/spf13/cobra"
@ -35,7 +36,6 @@ import (
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/uuid"
"k8s.io/apimachinery/pkg/util/wait"
genericfeatures "k8s.io/apiserver/pkg/features"
"k8s.io/apiserver/pkg/server/healthz"
"k8s.io/apiserver/pkg/server/mux"
utilfeature "k8s.io/apiserver/pkg/util/feature"
@ -51,10 +51,10 @@ import (
certutil "k8s.io/client-go/util/cert"
"k8s.io/client-go/util/keyutil"
cloudprovider "k8s.io/cloud-provider"
cpnames "k8s.io/cloud-provider/names"
cliflag "k8s.io/component-base/cli/flag"
"k8s.io/component-base/cli/globalflag"
"k8s.io/component-base/configz"
"k8s.io/component-base/featuregate"
"k8s.io/component-base/logs"
logsapi "k8s.io/component-base/logs/api/v1"
"k8s.io/component-base/metrics/features"
@ -70,8 +70,6 @@ import (
"k8s.io/controller-manager/pkg/informerfactory"
"k8s.io/controller-manager/pkg/leadermigration"
"k8s.io/klog/v2"
kubefeatures "k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/cmd/kube-controller-manager/app/config"
"k8s.io/kubernetes/cmd/kube-controller-manager/app/options"
"k8s.io/kubernetes/cmd/kube-controller-manager/names"
@ -137,7 +135,7 @@ controller, and serviceaccounts controller.`,
}
cliflag.PrintFlags(cmd.Flags())
c, err := s.Config(KnownControllers(), ControllersDisabledByDefault.List(), names.KCMControllerAliases())
c, err := s.Config(KnownControllers(), ControllersDisabledByDefault(), ControllerAliases())
if err != nil {
return err
}
@ -156,7 +154,7 @@ controller, and serviceaccounts controller.`,
}
fs := cmd.Flags()
namedFlagSets := s.Flags(KnownControllers(), ControllersDisabledByDefault.List(), names.KCMControllerAliases())
namedFlagSets := s.Flags(KnownControllers(), ControllersDisabledByDefault(), ControllerAliases())
verflag.AddFlags(namedFlagSets.FlagSet("global"))
globalflag.AddGlobalFlags(namedFlagSets.FlagSet("global"), cmd.Name(), logs.SkipLoggingConfigurationFlags())
registerLegacyGlobalFlags(namedFlagSets)
@ -226,16 +224,16 @@ func Run(ctx context.Context, c *config.CompletedConfig) error {
clientBuilder, rootClientBuilder := createClientBuilders(logger, c)
saTokenControllerInitFunc := serviceAccountTokenControllerStarter{rootClientBuilder: rootClientBuilder}.startServiceAccountTokenController
saTokenControllerDescriptor := newServiceAccountTokenControllerDescriptor(rootClientBuilder)
run := func(ctx context.Context, startSATokenController InitFunc, initializersFunc ControllerInitializersFunc) {
run := func(ctx context.Context, controllerDescriptors map[string]*ControllerDescriptor) {
controllerContext, err := CreateControllerContext(logger, c, rootClientBuilder, clientBuilder, ctx.Done())
if err != nil {
logger.Error(err, "Error building controller context")
klog.FlushAndExit(klog.ExitFlushTimeout, 1)
}
controllerInitializers := initializersFunc(controllerContext.LoopMode)
if err := StartControllers(ctx, controllerContext, startSATokenController, controllerInitializers, unsecuredMux, healthzHandler); err != nil {
if err := StartControllers(ctx, controllerContext, controllerDescriptors, unsecuredMux, healthzHandler); err != nil {
logger.Error(err, "Error starting controllers")
klog.FlushAndExit(klog.ExitFlushTimeout, 1)
}
@ -249,7 +247,9 @@ func Run(ctx context.Context, c *config.CompletedConfig) error {
// No leader election, run directly
if !c.ComponentConfig.Generic.LeaderElection.LeaderElect {
run(ctx, saTokenControllerInitFunc, NewControllerInitializers)
controllerDescriptors := NewControllerDescriptors()
controllerDescriptors[names.ServiceAccountTokenController] = saTokenControllerDescriptor
run(ctx, controllerDescriptors)
return nil
}
@ -264,9 +264,6 @@ func Run(ctx context.Context, c *config.CompletedConfig) error {
// leaderMigrator will be non-nil if and only if Leader Migration is enabled.
var leaderMigrator *leadermigration.LeaderMigrator = nil
// startSATokenController will be original saTokenControllerInitFunc if leader migration is not enabled.
startSATokenController := saTokenControllerInitFunc
// If leader migration is enabled, create the LeaderMigrator and prepare for migration
if leadermigration.Enabled(&c.ComponentConfig.Generic) {
logger.Info("starting leader migration")
@ -274,11 +271,14 @@ func Run(ctx context.Context, c *config.CompletedConfig) error {
leaderMigrator = leadermigration.NewLeaderMigrator(&c.ComponentConfig.Generic.LeaderMigration,
"kube-controller-manager")
// Wrap saTokenControllerInitFunc to signal readiness for migration after starting
// startSATokenControllerInit is the original InitFunc.
startSATokenControllerInit := saTokenControllerDescriptor.GetInitFunc()
// Wrap saTokenControllerDescriptor to signal readiness for migration after starting
// the controller.
startSATokenController = func(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
saTokenControllerDescriptor.initFunc = func(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
defer close(leaderMigrator.MigrationReady)
return saTokenControllerInitFunc(ctx, controllerContext)
return startSATokenControllerInit(ctx, controllerContext, controllerName)
}
}
@ -288,14 +288,15 @@ func Run(ctx context.Context, c *config.CompletedConfig) error {
c.ComponentConfig.Generic.LeaderElection.ResourceName,
leaderelection.LeaderCallbacks{
OnStartedLeading: func(ctx context.Context) {
initializersFunc := NewControllerInitializers
controllerDescriptors := NewControllerDescriptors()
if leaderMigrator != nil {
// If leader migration is enabled, we should start only non-migrated controllers
// for the main lock.
initializersFunc = createInitializersFunc(leaderMigrator.FilterFunc, leadermigration.ControllerNonMigrated)
controllerDescriptors = filteredControllerDescriptors(controllerDescriptors, leaderMigrator.FilterFunc, leadermigration.ControllerNonMigrated)
logger.Info("leader migration: starting main controllers.")
}
run(ctx, startSATokenController, initializersFunc)
controllerDescriptors[names.ServiceAccountTokenController] = saTokenControllerDescriptor
run(ctx, controllerDescriptors)
},
OnStoppedLeading: func() {
logger.Error(nil, "leaderelection lost")
@ -318,8 +319,11 @@ func Run(ctx context.Context, c *config.CompletedConfig) error {
leaderelection.LeaderCallbacks{
OnStartedLeading: func(ctx context.Context) {
logger.Info("leader migration: starting migrated controllers.")
controllerDescriptors := NewControllerDescriptors()
controllerDescriptors = filteredControllerDescriptors(controllerDescriptors, leaderMigrator.FilterFunc, leadermigration.ControllerMigrated)
// DO NOT start saTokenController under migration lock
run(ctx, nil, createInitializersFunc(leaderMigrator.FilterFunc, leadermigration.ControllerMigrated))
delete(controllerDescriptors, names.ServiceAccountTokenController)
run(ctx, controllerDescriptors)
},
OnStoppedLeading: func() {
logger.Error(nil, "migration leaderelection lost")
@ -377,8 +381,12 @@ type ControllerContext struct {
}
// IsControllerEnabled checks if the context's controllers enabled or not
func (c ControllerContext) IsControllerEnabled(name string) bool {
return genericcontrollermanager.IsControllerEnabled(name, ControllersDisabledByDefault, c.ComponentConfig.Generic.Controllers)
func (c ControllerContext) IsControllerEnabled(controllerDescriptor *ControllerDescriptor) bool {
controllersDisabledByDefault := sets.NewString()
if controllerDescriptor.IsDisabledByDefault() {
controllersDisabledByDefault.Insert(controllerDescriptor.Name())
}
return genericcontrollermanager.IsControllerEnabled(controllerDescriptor.Name(), controllersDisabledByDefault, c.ComponentConfig.Generic.Controllers)
}
// InitFunc is used to launch a particular controller. It returns a controller
@ -388,100 +396,171 @@ func (c ControllerContext) IsControllerEnabled(name string) bool {
// that requests no additional features from the controller manager.
// Any error returned will cause the controller process to `Fatal`
// The bool indicates whether the controller was enabled.
type InitFunc func(ctx context.Context, controllerCtx ControllerContext) (controller controller.Interface, enabled bool, err error)
type InitFunc func(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller controller.Interface, enabled bool, err error)
// ControllerInitializersFunc is used to create a collection of initializers
// given the loopMode.
type ControllerInitializersFunc func(loopMode ControllerLoopMode) (initializers map[string]InitFunc)
type ControllerDescriptor struct {
name string
initFunc InitFunc
requiredFeatureGates []featuregate.Feature
aliases []string
isDisabledByDefault bool
isCloudProviderController bool
requiresSpecialHandling bool
}
var _ ControllerInitializersFunc = NewControllerInitializers
func (r *ControllerDescriptor) Name() string {
return r.name
}
func (r *ControllerDescriptor) GetInitFunc() InitFunc {
return r.initFunc
}
func (r *ControllerDescriptor) GetRequiredFeatureGates() []featuregate.Feature {
return append([]featuregate.Feature(nil), r.requiredFeatureGates...)
}
// GetAliases returns aliases to ensure backwards compatibility and should never be removed!
// Only addition of new aliases is allowed, and only when a canonical name is changed (please see CHANGE POLICY of controller names)
func (r *ControllerDescriptor) GetAliases() []string {
return append([]string(nil), r.aliases...)
}
func (r *ControllerDescriptor) IsDisabledByDefault() bool {
return r.isDisabledByDefault
}
func (r *ControllerDescriptor) IsCloudProviderController() bool {
return r.isCloudProviderController
}
// RequiresSpecialHandling should return true only in a special non-generic controllers like ServiceAccountTokenController
func (r *ControllerDescriptor) RequiresSpecialHandling() bool {
return r.requiresSpecialHandling
}
// KnownControllers returns all known controllers's name
func KnownControllers() []string {
ret := sets.StringKeySet(NewControllerInitializers(IncludeCloudLoops))
// add "special" controllers that aren't initialized normally. These controllers cannot be initialized
// using a normal function. The only known special case is the SA token controller which *must* be started
// first to ensure that the SA tokens for future controllers will exist. Think very carefully before adding
// to this list.
ret.Insert(
names.ServiceAccountTokenController,
)
return ret.List()
return sets.StringKeySet(NewControllerDescriptors()).List()
}
// ControllersDisabledByDefault is the set of controllers which is disabled by default
var ControllersDisabledByDefault = sets.NewString(
names.BootstrapSignerController,
names.TokenCleanerController,
)
// ControllerAliases returns a mapping of aliases to canonical controller names
func ControllerAliases() map[string]string {
aliases := map[string]string{}
for name, c := range NewControllerDescriptors() {
for _, alias := range c.GetAliases() {
aliases[alias] = name
}
}
return aliases
}
// NewControllerInitializers is a public map of named controller groups (you can start more than one in an init func)
// paired to their InitFunc. This allows for structured downstream composition and subdivision.
func NewControllerInitializers(loopMode ControllerLoopMode) map[string]InitFunc {
controllers := map[string]InitFunc{}
func ControllersDisabledByDefault() []string {
var controllersDisabledByDefault []string
// All of the controllers must have unique names, or else we will explode.
register := func(name string, fn InitFunc) {
for name, c := range NewControllerDescriptors() {
if c.IsDisabledByDefault() {
controllersDisabledByDefault = append(controllersDisabledByDefault, name)
}
}
sort.Strings(controllersDisabledByDefault)
return controllersDisabledByDefault
}
// NewControllerDescriptors is a public map of named controller groups (you can start more than one in an init func)
// paired to their ControllerDescriptor wrapper object that includes InitFunc.
// This allows for structured downstream composition and subdivision.
func NewControllerDescriptors() map[string]*ControllerDescriptor {
controllers := map[string]*ControllerDescriptor{}
aliases := sets.NewString()
// All the controllers must fulfil common constraints, or else we will explode.
register := func(controllerDesc *ControllerDescriptor) {
if controllerDesc == nil {
panic("received nil controller for a registration")
}
name := controllerDesc.Name()
if len(name) == 0 {
panic("received controller without a name for a registration")
}
if _, found := controllers[name]; found {
panic(fmt.Sprintf("controller name %q was registered twice", name))
}
controllers[name] = fn
if controllerDesc.GetInitFunc() == nil {
panic(fmt.Sprintf("controller %q does not have an init function", name))
}
for _, alias := range controllerDesc.GetAliases() {
if aliases.Has(alias) {
panic(fmt.Sprintf("controller %q has a duplicate alias %q", name, alias))
}
aliases.Insert(alias)
}
controllers[name] = controllerDesc
}
register(names.EndpointsController, startEndpointController)
register(names.EndpointSliceController, startEndpointSliceController)
register(names.EndpointSliceMirroringController, startEndpointSliceMirroringController)
register(names.ReplicationControllerController, startReplicationController)
register(names.PodGarbageCollectorController, startPodGCController)
register(names.ResourceQuotaController, startResourceQuotaController)
register(names.NamespaceController, startNamespaceController)
register(names.ServiceAccountController, startServiceAccountController)
register(names.GarbageCollectorController, startGarbageCollectorController)
register(names.DaemonSetController, startDaemonSetController)
register(names.JobController, startJobController)
register(names.DeploymentController, startDeploymentController)
register(names.ReplicaSetController, startReplicaSetController)
register(names.HorizontalPodAutoscalerController, startHPAController)
register(names.DisruptionController, startDisruptionController)
register(names.StatefulSetController, startStatefulSetController)
register(names.CronJobController, startCronJobController)
register(names.CertificateSigningRequestSigningController, startCSRSigningController)
register(names.CertificateSigningRequestApprovingController, startCSRApprovingController)
register(names.CertificateSigningRequestCleanerController, startCSRCleanerController)
register(names.TTLController, startTTLController)
register(names.BootstrapSignerController, startBootstrapSignerController)
register(names.TokenCleanerController, startTokenCleanerController)
register(names.NodeIpamController, startNodeIpamController)
register(names.NodeLifecycleController, startNodeLifecycleController)
if loopMode == IncludeCloudLoops {
register(cpnames.ServiceLBController, startServiceController)
register(cpnames.NodeRouteController, startRouteController)
register(cpnames.CloudNodeLifecycleController, startCloudNodeLifecycleController)
// TODO: persistent volume controllers into the IncludeCloudLoops only set.
}
register(names.PersistentVolumeBinderController, startPersistentVolumeBinderController)
register(names.PersistentVolumeAttachDetachController, startAttachDetachController)
register(names.PersistentVolumeExpanderController, startVolumeExpandController)
register(names.ClusterRoleAggregationController, startClusterRoleAggregrationController)
register(names.PersistentVolumeClaimProtectionController, startPVCProtectionController)
register(names.PersistentVolumeProtectionController, startPVProtectionController)
register(names.TTLAfterFinishedController, startTTLAfterFinishedController)
register(names.RootCACertificatePublisherController, startRootCACertPublisher)
register(names.EphemeralVolumeController, startEphemeralVolumeController)
if utilfeature.DefaultFeatureGate.Enabled(genericfeatures.APIServerIdentity) &&
utilfeature.DefaultFeatureGate.Enabled(genericfeatures.StorageVersionAPI) {
register(names.StorageVersionGarbageCollectorController, startStorageVersionGCController)
}
if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.DynamicResourceAllocation) {
register(names.ResourceClaimController, startResourceClaimController)
}
if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.LegacyServiceAccountTokenCleanUp) {
register(names.LegacyServiceAccountTokenCleanerController, startLegacySATokenCleaner)
}
if utilfeature.DefaultFeatureGate.Enabled(genericfeatures.ValidatingAdmissionPolicy) {
register(names.ValidatingAdmissionPolicyStatusController, startValidatingAdmissionPolicyStatusController)
// First add "special" controllers that aren't initialized normally. These controllers cannot be initialized
// in the main controller loop initialization, so we add them here only for the metadata and duplication detection.
// app.ControllerDescriptor#RequiresSpecialHandling should return true for such controllers
// The only known special case is the ServiceAccountTokenController which *must* be started
// first to ensure that the SA tokens for future controllers will exist. Think very carefully before adding new
// special controllers.
register(newServiceAccountTokenControllerDescriptor(nil))
register(newEndpointsControllerDescriptor())
register(newEndpointSliceControllerDescriptor())
register(newEndpointSliceMirroringControllerDescriptor())
register(newReplicationControllerDescriptor())
register(newPodGarbageCollectorControllerDescriptor())
register(newResourceQuotaControllerDescriptor())
register(newNamespaceControllerDescriptor())
register(newServiceAccountControllerDescriptor())
register(newGarbageCollectorControllerDescriptor())
register(newDaemonSetControllerDescriptor())
register(newJobControllerDescriptor())
register(newDeploymentControllerDescriptor())
register(newReplicaSetControllerDescriptor())
register(newHorizontalPodAutoscalerControllerDescriptor())
register(newDisruptionControllerDescriptor())
register(newStatefulSetControllerDescriptor())
register(newCronJobControllerDescriptor())
register(newCertificateSigningRequestSigningControllerDescriptor())
register(newCertificateSigningRequestApprovingControllerDescriptor())
register(newCertificateSigningRequestCleanerControllerDescriptor())
register(newTTLControllerDescriptor())
register(newBootstrapSignerControllerDescriptor())
register(newTokenCleanerControllerDescriptor())
register(newNodeIpamControllerDescriptor())
register(newNodeLifecycleControllerDescriptor())
register(newServiceLBControllerDescriptor()) // cloud provider controller
register(newNodeRouteControllerDescriptor()) // cloud provider controller
register(newCloudNodeLifecycleControllerDescriptor()) // cloud provider controller
// TODO: persistent volume controllers into the IncludeCloudLoops only set as a cloud provider controller.
register(newPersistentVolumeBinderControllerDescriptor())
register(newPersistentVolumeAttachDetachControllerDescriptor())
register(newPersistentVolumeExpanderControllerDescriptor())
register(newClusterRoleAggregrationControllerDescriptor())
register(newPersistentVolumeClaimProtectionControllerDescriptor())
register(newPersistentVolumeProtectionControllerDescriptor())
register(newTTLAfterFinishedControllerDescriptor())
register(newRootCACertificatePublisherControllerDescriptor())
register(newEphemeralVolumeControllerDescriptor())
// feature gated
register(newStorageVersionGarbageCollectorControllerDescriptor())
register(newResourceClaimControllerDescriptor())
register(newLegacyServiceAccountTokenCleanerControllerDescriptor())
register(newValidatingAdmissionPolicyStatusControllerDescriptor())
for _, alias := range aliases.UnsortedList() {
if _, ok := controllers[alias]; ok {
panic(fmt.Sprintf("alias %q conflicts with a controller name", alias))
}
}
return controllers
@ -542,16 +621,21 @@ func CreateControllerContext(logger klog.Logger, s *config.CompletedConfig, root
}
// StartControllers starts a set of controllers with a specified ControllerContext
func StartControllers(ctx context.Context, controllerCtx ControllerContext, startSATokenController InitFunc, controllers map[string]InitFunc,
func StartControllers(ctx context.Context, controllerCtx ControllerContext, controllerDescriptors map[string]*ControllerDescriptor,
unsecuredMux *mux.PathRecorderMux, healthzHandler *controllerhealthz.MutableHealthzHandler) error {
logger := klog.FromContext(ctx)
var controllerChecks []healthz.HealthChecker
// Always start the SA token controller first using a full-power client, since it needs to mint tokens for the rest
// If this fails, just return here and fail since other controllers won't be able to get credentials.
if startSATokenController != nil {
if _, _, err := startSATokenController(ctx, controllerCtx); err != nil {
if serviceAccountTokenControllerDescriptor, ok := controllerDescriptors[names.ServiceAccountTokenController]; ok {
check, err := StartController(ctx, controllerCtx, serviceAccountTokenControllerDescriptor, unsecuredMux)
if err != nil {
return err
}
if check != nil {
// HealthChecker should be present when controller has started
controllerChecks = append(controllerChecks, check)
}
}
// Initialize the cloud provider with a reference to the clientBuilder only after token controller
@ -560,55 +644,28 @@ func StartControllers(ctx context.Context, controllerCtx ControllerContext, star
controllerCtx.Cloud.Initialize(controllerCtx.ClientBuilder, ctx.Done())
}
var controllerChecks []healthz.HealthChecker
// Each controller is passed a context where the logger has the name of
// the controller set through WithName. That name then becomes the prefix of
// of all log messages emitted by that controller.
//
// In this loop, an explicit "controller" key is used instead, for two reasons:
// In StartController, an explicit "controller" key is used instead, for two reasons:
// - while contextual logging is alpha, klog.LoggerWithName is still a no-op,
// so we cannot rely on it yet to add the name
// - it allows distinguishing between log entries emitted by the controller
// and those emitted for it - this is a bit debatable and could be revised.
for controllerName, initFn := range controllers {
if !controllerCtx.IsControllerEnabled(controllerName) {
logger.Info("Warning: controller is disabled", "controller", controllerName)
for _, controllerDesc := range controllerDescriptors {
if controllerDesc.RequiresSpecialHandling() {
continue
}
time.Sleep(wait.Jitter(controllerCtx.ComponentConfig.Generic.ControllerStartInterval.Duration, ControllerStartJitter))
logger.V(1).Info("Starting controller", "controller", controllerName)
ctrl, started, err := initFn(klog.NewContext(ctx, klog.LoggerWithName(logger, controllerName)), controllerCtx)
check, err := StartController(ctx, controllerCtx, controllerDesc, unsecuredMux)
if err != nil {
logger.Error(err, "Error starting controller", "controller", controllerName)
return err
}
if !started {
logger.Info("Warning: skipping controller", "controller", controllerName)
continue
if check != nil {
// HealthChecker should be present when controller has started
controllerChecks = append(controllerChecks, check)
}
check := controllerhealthz.NamedPingChecker(controllerName)
if ctrl != nil {
// check if the controller supports and requests a debugHandler
// and it needs the unsecuredMux to mount the handler onto.
if debuggable, ok := ctrl.(controller.Debuggable); ok && unsecuredMux != nil {
if debugHandler := debuggable.DebuggingHandler(); debugHandler != nil {
basePath := "/debug/controllers/" + controllerName
unsecuredMux.UnlistedHandle(basePath, http.StripPrefix(basePath, debugHandler))
unsecuredMux.UnlistedHandlePrefix(basePath+"/", http.StripPrefix(basePath, debugHandler))
}
}
if healthCheckable, ok := ctrl.(controller.HealthCheckable); ok {
if realCheck := healthCheckable.HealthChecker(); realCheck != nil {
check = controllerhealthz.NamedHealthChecker(controllerName, realCheck)
}
}
}
controllerChecks = append(controllerChecks, check)
logger.Info("Started controller", "controller", controllerName)
}
healthzHandler.AddHealthChecker(controllerChecks...)
@ -616,22 +673,85 @@ func StartControllers(ctx context.Context, controllerCtx ControllerContext, star
return nil
}
// serviceAccountTokenControllerStarter is special because it must run first to set up permissions for other controllers.
// It cannot use the "normal" client builder, so it tracks its own. It must also avoid being included in the "normal"
// init map so that it can always run first.
type serviceAccountTokenControllerStarter struct {
rootClientBuilder clientbuilder.ControllerClientBuilder
}
func (c serviceAccountTokenControllerStarter) startServiceAccountTokenController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
// StartController starts a controller with a specified ControllerContext
// and performs required pre- and post- checks/actions
func StartController(ctx context.Context, controllerCtx ControllerContext, controllerDescriptor *ControllerDescriptor,
unsecuredMux *mux.PathRecorderMux) (healthz.HealthChecker, error) {
logger := klog.FromContext(ctx)
if !controllerContext.IsControllerEnabled(names.ServiceAccountTokenController) {
logger.Info("Warning: controller is disabled", "controller", names.ServiceAccountTokenController)
return nil, false, nil
controllerName := controllerDescriptor.Name()
for _, featureGate := range controllerDescriptor.GetRequiredFeatureGates() {
if !utilfeature.DefaultFeatureGate.Enabled(featureGate) {
logger.Info("Controller is disabled by a feature gate", "controller", controllerName, "requiredFeatureGates", controllerDescriptor.GetRequiredFeatureGates())
return nil, nil
}
}
if controllerDescriptor.IsCloudProviderController() && controllerCtx.LoopMode != IncludeCloudLoops {
logger.Info("Skipping a cloud provider controller", "controller", controllerName, "loopMode", controllerCtx.LoopMode)
return nil, nil
}
if !controllerCtx.IsControllerEnabled(controllerDescriptor) {
logger.Info("Warning: controller is disabled", "controller", controllerName)
return nil, nil
}
time.Sleep(wait.Jitter(controllerCtx.ComponentConfig.Generic.ControllerStartInterval.Duration, ControllerStartJitter))
logger.V(1).Info("Starting controller", "controller", controllerName)
initFunc := controllerDescriptor.GetInitFunc()
ctrl, started, err := initFunc(klog.NewContext(ctx, klog.LoggerWithName(logger, controllerName)), controllerCtx, controllerName)
if err != nil {
logger.Error(err, "Error starting controller", "controller", controllerName)
return nil, err
}
if !started {
logger.Info("Warning: skipping controller", "controller", controllerName)
return nil, nil
}
check := controllerhealthz.NamedPingChecker(controllerName)
if ctrl != nil {
// check if the controller supports and requests a debugHandler
// and it needs the unsecuredMux to mount the handler onto.
if debuggable, ok := ctrl.(controller.Debuggable); ok && unsecuredMux != nil {
if debugHandler := debuggable.DebuggingHandler(); debugHandler != nil {
basePath := "/debug/controllers/" + controllerName
unsecuredMux.UnlistedHandle(basePath, http.StripPrefix(basePath, debugHandler))
unsecuredMux.UnlistedHandlePrefix(basePath+"/", http.StripPrefix(basePath, debugHandler))
}
}
if healthCheckable, ok := ctrl.(controller.HealthCheckable); ok {
if realCheck := healthCheckable.HealthChecker(); realCheck != nil {
check = controllerhealthz.NamedHealthChecker(controllerName, realCheck)
}
}
}
logger.Info("Started controller", "controller", controllerName)
return check, nil
}
// serviceAccountTokenControllerStarter is special because it must run first to set up permissions for other controllers.
// It cannot use the "normal" client builder, so it tracks its own.
func newServiceAccountTokenControllerDescriptor(rootClientBuilder clientbuilder.ControllerClientBuilder) *ControllerDescriptor {
return &ControllerDescriptor{
name: names.ServiceAccountTokenController,
aliases: []string{"serviceaccount-token"},
initFunc: func(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
return startServiceAccountTokenController(ctx, controllerContext, controllerName, rootClientBuilder)
},
// will make sure it runs first before other controllers
requiresSpecialHandling: true,
}
}
func startServiceAccountTokenController(ctx context.Context, controllerContext ControllerContext, controllerName string, rootClientBuilder clientbuilder.ControllerClientBuilder) (controller.Interface, bool, error) {
logger := klog.FromContext(ctx)
if len(controllerContext.ComponentConfig.SAController.ServiceAccountKeyFile) == 0 {
logger.Info("Controller is disabled because there is no private key", "controller", names.ServiceAccountTokenController)
logger.Info("Controller is disabled because there is no private key", "controller", controllerName)
return nil, false, nil
}
privateKey, err := keyutil.PrivateKeyFromFile(controllerContext.ComponentConfig.SAController.ServiceAccountKeyFile)
@ -645,7 +765,7 @@ func (c serviceAccountTokenControllerStarter) startServiceAccountTokenController
return nil, true, fmt.Errorf("error parsing root-ca-file at %s: %v", controllerContext.ComponentConfig.SAController.RootCAFile, err)
}
} else {
rootCA = c.rootClientBuilder.ConfigOrDie("tokens-controller").CAData
rootCA = rootClientBuilder.ConfigOrDie("tokens-controller").CAData
}
tokenGenerator, err := serviceaccount.JWTTokenGenerator(serviceaccount.LegacyIssuer, privateKey)
@ -655,7 +775,7 @@ func (c serviceAccountTokenControllerStarter) startServiceAccountTokenController
tokenController, err := serviceaccountcontroller.NewTokensController(
controllerContext.InformerFactory.Core().V1().ServiceAccounts(),
controllerContext.InformerFactory.Core().V1().Secrets(),
c.rootClientBuilder.ClientOrDie("tokens-controller"),
rootClientBuilder.ClientOrDie("tokens-controller"),
serviceaccountcontroller.TokensControllerOptions{
TokenGenerator: tokenGenerator,
RootCA: rootCA,
@ -737,16 +857,13 @@ func leaderElectAndRun(ctx context.Context, c *config.CompletedConfig, lockIdent
panic("unreachable")
}
// createInitializersFunc creates a initializersFunc that returns all initializer
// with expected as the result after filtering through filterFunc.
func createInitializersFunc(filterFunc leadermigration.FilterFunc, expected leadermigration.FilterResult) ControllerInitializersFunc {
return func(loopMode ControllerLoopMode) map[string]InitFunc {
initializers := make(map[string]InitFunc)
for name, initializer := range NewControllerInitializers(loopMode) {
if filterFunc(name) == expected {
initializers[name] = initializer
}
// filteredControllerDescriptors returns all controllerDescriptors after filtering through filterFunc.
func filteredControllerDescriptors(controllerDescriptors map[string]*ControllerDescriptor, filterFunc leadermigration.FilterFunc, expected leadermigration.FilterResult) map[string]*ControllerDescriptor {
resultControllers := make(map[string]*ControllerDescriptor)
for name, controllerDesc := range controllerDescriptors {
if filterFunc(name) == expected {
resultControllers[name] = controllerDesc
}
return initializers
}
return resultControllers
}

View File

@ -21,8 +21,13 @@ import (
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"k8s.io/apimachinery/pkg/util/sets"
utilfeature "k8s.io/apiserver/pkg/util/feature"
cpnames "k8s.io/cloud-provider/names"
"k8s.io/component-base/featuregate"
featuregatetesting "k8s.io/component-base/featuregate/testing"
"k8s.io/kubernetes/cmd/kube-controller-manager/names"
)
@ -41,7 +46,7 @@ func TestControllerNamesConsistency(t *testing.T) {
}
func TestControllerNamesDeclaration(t *testing.T) {
declaredControllers := sets.New(
declaredControllers := sets.NewString(
names.ServiceAccountTokenController,
names.EndpointsController,
names.EndpointSliceController,
@ -83,6 +88,7 @@ func TestControllerNamesDeclaration(t *testing.T) {
names.StorageVersionGarbageCollectorController,
names.ResourceClaimController,
names.LegacyServiceAccountTokenCleanerController,
names.ValidatingAdmissionPolicyStatusController,
)
for _, name := range KnownControllers() {
@ -91,3 +97,62 @@ func TestControllerNamesDeclaration(t *testing.T) {
}
}
}
func TestNewControllerDescriptorsShouldNotPanic(t *testing.T) {
NewControllerDescriptors()
}
func TestNewControllerDescriptorsAlwaysReturnsDescriptorsForAllControllers(t *testing.T) {
controllersWithoutFeatureGates := KnownControllers()
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, "AllAlpha", true)()
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, "AllBeta", true)()
controllersWithFeatureGates := KnownControllers()
if diff := cmp.Diff(controllersWithoutFeatureGates, controllersWithFeatureGates); diff != "" {
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)
}
}
func TestFeatureGatedControllersShouldNotDefineAliases(t *testing.T) {
featureGateRegex := regexp.MustCompile("^([a-zA-Z0-9]+)")
alphaFeatures := sets.NewString()
for _, featureText := range utilfeature.DefaultFeatureGate.KnownFeatures() {
// we have to parse this from KnownFeatures, because usage of mutable FeatureGate is not allowed in unit tests
feature := featureGateRegex.FindString(featureText)
if strings.Contains(featureText, string(featuregate.Alpha)) && feature != "AllAlpha" {
alphaFeatures.Insert(feature)
}
}
for name, controller := range NewControllerDescriptors() {
if len(controller.GetAliases()) == 0 {
continue
}
requiredFeatureGates := controller.GetRequiredFeatureGates()
if len(requiredFeatureGates) == 0 {
continue
}
// DO NOT ADD any new controllers here. These two controllers are an exception, because they were added before this test was introduced
if name == names.LegacyServiceAccountTokenCleanerController || name == names.ResourceClaimController {
continue
}
areAllRequiredFeaturesAlpha := true
for _, feature := range requiredFeatureGates {
if !alphaFeatures.Has(string(feature)) {
areAllRequiredFeaturesAlpha = false
break
}
}
if areAllRequiredFeaturesAlpha {
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)
}
}
}

View File

@ -31,6 +31,7 @@ import (
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
genericfeatures "k8s.io/apiserver/pkg/features"
"k8s.io/apiserver/pkg/quota/v1/generic"
utilfeature "k8s.io/apiserver/pkg/util/feature"
clientset "k8s.io/client-go/kubernetes"
@ -39,8 +40,11 @@ import (
cloudnodelifecyclecontroller "k8s.io/cloud-provider/controllers/nodelifecycle"
routecontroller "k8s.io/cloud-provider/controllers/route"
servicecontroller "k8s.io/cloud-provider/controllers/service"
cpnames "k8s.io/cloud-provider/names"
"k8s.io/component-base/featuregate"
"k8s.io/controller-manager/controller"
csitrans "k8s.io/csi-translation-lib"
"k8s.io/kubernetes/cmd/kube-controller-manager/names"
pkgcontroller "k8s.io/kubernetes/pkg/controller"
endpointcontroller "k8s.io/kubernetes/pkg/controller/endpoint"
"k8s.io/kubernetes/pkg/controller/garbagecollector"
@ -63,6 +67,7 @@ import (
persistentvolumecontroller "k8s.io/kubernetes/pkg/controller/volume/persistentvolume"
"k8s.io/kubernetes/pkg/controller/volume/pvcprotection"
"k8s.io/kubernetes/pkg/controller/volume/pvprotection"
"k8s.io/kubernetes/pkg/features"
quotainstall "k8s.io/kubernetes/pkg/quota/v1/install"
"k8s.io/kubernetes/pkg/volume/csimigration"
"k8s.io/utils/clock"
@ -76,7 +81,16 @@ const (
defaultNodeMaskCIDRIPv6 = 64
)
func startServiceController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newServiceLBControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: cpnames.ServiceLBController,
aliases: []string{"service"},
initFunc: startServiceLBController,
isCloudProviderController: true,
}
}
func startServiceLBController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
serviceController, err := servicecontroller.New(
controllerContext.Cloud,
controllerContext.ClientBuilder.ClientOrDie("service-controller"),
@ -93,8 +107,15 @@ func startServiceController(ctx context.Context, controllerContext ControllerCon
go serviceController.Run(ctx, int(controllerContext.ComponentConfig.ServiceController.ConcurrentServiceSyncs), controllerContext.ControllerManagerMetrics)
return nil, true, nil
}
func newNodeIpamControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.NodeIpamController,
aliases: []string{"nodeipam"},
initFunc: startNodeIpamController,
}
}
func startNodeIpamController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func startNodeIpamController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
var serviceCIDR *net.IPNet
var secondaryServiceCIDR *net.IPNet
logger := klog.FromContext(ctx)
@ -166,7 +187,15 @@ func startNodeIpamController(ctx context.Context, controllerContext ControllerCo
return nil, true, nil
}
func startNodeLifecycleController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newNodeLifecycleControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.NodeLifecycleController,
aliases: []string{"nodelifecycle"},
initFunc: startNodeLifecycleController,
}
}
func startNodeLifecycleController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
lifecycleController, err := lifecyclecontroller.NewNodeLifecycleController(
ctx,
controllerContext.InformerFactory.Coordination().V1().Leases(),
@ -190,7 +219,16 @@ func startNodeLifecycleController(ctx context.Context, controllerContext Control
return nil, true, nil
}
func startCloudNodeLifecycleController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newCloudNodeLifecycleControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: cpnames.CloudNodeLifecycleController,
aliases: []string{"cloud-node-lifecycle"},
initFunc: startCloudNodeLifecycleController,
isCloudProviderController: true,
}
}
func startCloudNodeLifecycleController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
logger := klog.FromContext(ctx)
cloudNodeLifecycleController, err := cloudnodelifecyclecontroller.NewCloudNodeLifecycleController(
controllerContext.InformerFactory.Core().V1().Nodes(),
@ -210,7 +248,16 @@ func startCloudNodeLifecycleController(ctx context.Context, controllerContext Co
return nil, true, nil
}
func startRouteController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newNodeRouteControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: cpnames.NodeRouteController,
aliases: []string{"route"},
initFunc: startNodeRouteController,
isCloudProviderController: true,
}
}
func startNodeRouteController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
logger := klog.FromContext(ctx)
if !controllerContext.ComponentConfig.KubeCloudShared.AllocateNodeCIDRs || !controllerContext.ComponentConfig.KubeCloudShared.ConfigureCloudRoutes {
logger.Info("Will not configure cloud provider routes for allocate-node-cidrs", "CIDRs", controllerContext.ComponentConfig.KubeCloudShared.AllocateNodeCIDRs, "routes", controllerContext.ComponentConfig.KubeCloudShared.ConfigureCloudRoutes)
@ -240,7 +287,15 @@ func startRouteController(ctx context.Context, controllerContext ControllerConte
return nil, true, nil
}
func startPersistentVolumeBinderController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newPersistentVolumeBinderControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.PersistentVolumeBinderController,
aliases: []string{"persistentvolume-binder"},
initFunc: startPersistentVolumeBinderController,
}
}
func startPersistentVolumeBinderController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
logger := klog.FromContext(ctx)
plugins, err := ProbeControllerVolumePlugins(logger, controllerContext.Cloud, controllerContext.ComponentConfig.PersistentVolumeBinderController.VolumeConfiguration)
if err != nil {
@ -268,7 +323,15 @@ func startPersistentVolumeBinderController(ctx context.Context, controllerContex
return nil, true, nil
}
func startAttachDetachController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newPersistentVolumeAttachDetachControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.PersistentVolumeAttachDetachController,
aliases: []string{"attachdetach"},
initFunc: startPersistentVolumeAttachDetachController,
}
}
func startPersistentVolumeAttachDetachController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
logger := klog.FromContext(ctx)
csiNodeInformer := controllerContext.InformerFactory.Storage().V1().CSINodes()
csiDriverInformer := controllerContext.InformerFactory.Storage().V1().CSIDrivers()
@ -304,7 +367,15 @@ func startAttachDetachController(ctx context.Context, controllerContext Controll
return nil, true, nil
}
func startVolumeExpandController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newPersistentVolumeExpanderControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.PersistentVolumeExpanderController,
aliases: []string{"persistentvolume-expander"},
initFunc: startPersistentVolumeExpanderController,
}
}
func startPersistentVolumeExpanderController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
logger := klog.FromContext(ctx)
plugins, err := ProbeExpandableVolumePlugins(logger, controllerContext.ComponentConfig.PersistentVolumeBinderController.VolumeConfiguration)
if err != nil {
@ -326,10 +397,17 @@ func startVolumeExpandController(ctx context.Context, controllerContext Controll
}
go expandController.Run(ctx)
return nil, true, nil
}
func startEphemeralVolumeController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newEphemeralVolumeControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.EphemeralVolumeController,
aliases: []string{"ephemeral-volume"},
initFunc: startEphemeralVolumeController,
}
}
func startEphemeralVolumeController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
ephemeralController, err := ephemeral.NewController(
controllerContext.ClientBuilder.ClientOrDie("ephemeral-volume-controller"),
controllerContext.InformerFactory.Core().V1().Pods(),
@ -343,7 +421,18 @@ func startEphemeralVolumeController(ctx context.Context, controllerContext Contr
const defaultResourceClaimControllerWorkers = 10
func startResourceClaimController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newResourceClaimControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.ResourceClaimController,
aliases: []string{"resource-claim-controller"},
initFunc: startResourceClaimController,
requiredFeatureGates: []featuregate.Feature{
features.DynamicResourceAllocation, // TODO update app.TestFeatureGatedControllersShouldNotDefineAliases when removing this feature
},
}
}
func startResourceClaimController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
ephemeralController, err := resourceclaim.NewController(
klog.FromContext(ctx),
controllerContext.ClientBuilder.ClientOrDie("resource-claim-controller"),
@ -358,18 +447,34 @@ func startResourceClaimController(ctx context.Context, controllerContext Control
return nil, true, nil
}
func startEndpointController(ctx context.Context, controllerCtx ControllerContext) (controller.Interface, bool, error) {
func newEndpointsControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.EndpointsController,
aliases: []string{"endpoint"},
initFunc: startEndpointsController,
}
}
func startEndpointsController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
go endpointcontroller.NewEndpointController(
controllerCtx.InformerFactory.Core().V1().Pods(),
controllerCtx.InformerFactory.Core().V1().Services(),
controllerCtx.InformerFactory.Core().V1().Endpoints(),
controllerCtx.ClientBuilder.ClientOrDie("endpoint-controller"),
controllerCtx.ComponentConfig.EndpointController.EndpointUpdatesBatchPeriod.Duration,
).Run(ctx, int(controllerCtx.ComponentConfig.EndpointController.ConcurrentEndpointSyncs))
controllerContext.InformerFactory.Core().V1().Pods(),
controllerContext.InformerFactory.Core().V1().Services(),
controllerContext.InformerFactory.Core().V1().Endpoints(),
controllerContext.ClientBuilder.ClientOrDie("endpoint-controller"),
controllerContext.ComponentConfig.EndpointController.EndpointUpdatesBatchPeriod.Duration,
).Run(ctx, int(controllerContext.ComponentConfig.EndpointController.ConcurrentEndpointSyncs))
return nil, true, nil
}
func startReplicationController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newReplicationControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.ReplicationControllerController,
aliases: []string{"replicationcontroller"},
initFunc: startReplicationController,
}
}
func startReplicationController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
go replicationcontroller.NewReplicationManager(
klog.FromContext(ctx),
controllerContext.InformerFactory.Core().V1().Pods(),
@ -380,7 +485,15 @@ func startReplicationController(ctx context.Context, controllerContext Controlle
return nil, true, nil
}
func startPodGCController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newPodGarbageCollectorControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.PodGarbageCollectorController,
aliases: []string{"podgc"},
initFunc: startPodGarbageCollectorController,
}
}
func startPodGarbageCollectorController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
go podgc.NewPodGC(
ctx,
controllerContext.ClientBuilder.ClientOrDie("pod-garbage-collector"),
@ -391,7 +504,15 @@ func startPodGCController(ctx context.Context, controllerContext ControllerConte
return nil, true, nil
}
func startResourceQuotaController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newResourceQuotaControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.ResourceQuotaController,
aliases: []string{"resourcequota"},
initFunc: startResourceQuotaController,
}
}
func startResourceQuotaController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
resourceQuotaControllerClient := controllerContext.ClientBuilder.ClientOrDie("resourcequota-controller")
resourceQuotaControllerDiscoveryClient := controllerContext.ClientBuilder.DiscoveryClientOrDie("resourcequota-controller")
discoveryFunc := resourceQuotaControllerDiscoveryClient.ServerPreferredNamespacedResources
@ -422,7 +543,15 @@ func startResourceQuotaController(ctx context.Context, controllerContext Control
return nil, true, nil
}
func startNamespaceController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newNamespaceControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.NamespaceController,
aliases: []string{"namespace"},
initFunc: startNamespaceController,
}
}
func startNamespaceController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
// the namespace cleanup controller is very chatty. It makes lots of discovery calls and then it makes lots of delete calls
// the ratelimiter negatively affects its speed. Deleting 100 total items in a namespace (that's only a few of each resource
// including events), takes ~10 seconds by default.
@ -456,7 +585,15 @@ func startModifiedNamespaceController(ctx context.Context, controllerContext Con
return nil, true, nil
}
func startServiceAccountController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newServiceAccountControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.ServiceAccountController,
aliases: []string{"serviceaccount"},
initFunc: startServiceAccountController,
}
}
func startServiceAccountController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
sac, err := serviceaccountcontroller.NewServiceAccountsController(
controllerContext.InformerFactory.Core().V1().ServiceAccounts(),
controllerContext.InformerFactory.Core().V1().Namespaces(),
@ -470,7 +607,15 @@ func startServiceAccountController(ctx context.Context, controllerContext Contro
return nil, true, nil
}
func startTTLController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newTTLControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.TTLController,
aliases: []string{"ttl"},
initFunc: startTTLController,
}
}
func startTTLController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
go ttlcontroller.NewTTLController(
ctx,
controllerContext.InformerFactory.Core().V1().Nodes(),
@ -479,7 +624,15 @@ func startTTLController(ctx context.Context, controllerContext ControllerContext
return nil, true, nil
}
func startGarbageCollectorController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newGarbageCollectorControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.GarbageCollectorController,
aliases: []string{"garbagecollector"},
initFunc: startGarbageCollectorController,
}
}
func startGarbageCollectorController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
if !controllerContext.ComponentConfig.GarbageCollectorController.EnableGarbageCollector {
return nil, false, nil
}
@ -523,7 +676,15 @@ func startGarbageCollectorController(ctx context.Context, controllerContext Cont
return garbageCollector, true, nil
}
func startPVCProtectionController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newPersistentVolumeClaimProtectionControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.PersistentVolumeClaimProtectionController,
aliases: []string{"pvc-protection"},
initFunc: startPersistentVolumeClaimProtectionController,
}
}
func startPersistentVolumeClaimProtectionController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
pvcProtectionController, err := pvcprotection.NewPVCProtectionController(
klog.FromContext(ctx),
controllerContext.InformerFactory.Core().V1().PersistentVolumeClaims(),
@ -537,7 +698,15 @@ func startPVCProtectionController(ctx context.Context, controllerContext Control
return nil, true, nil
}
func startPVProtectionController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newPersistentVolumeProtectionControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.PersistentVolumeProtectionController,
aliases: []string{"pv-protection"},
initFunc: startPersistentVolumeProtectionController,
}
}
func startPersistentVolumeProtectionController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
go pvprotection.NewPVProtectionController(
klog.FromContext(ctx),
controllerContext.InformerFactory.Core().V1().PersistentVolumes(),
@ -546,7 +715,15 @@ func startPVProtectionController(ctx context.Context, controllerContext Controll
return nil, true, nil
}
func startTTLAfterFinishedController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newTTLAfterFinishedControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.TTLAfterFinishedController,
aliases: []string{"ttl-after-finished"},
initFunc: startTTLAfterFinishedController,
}
}
func startTTLAfterFinishedController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
go ttlafterfinished.New(
ctx,
controllerContext.InformerFactory.Batch().V1().Jobs(),
@ -555,7 +732,18 @@ func startTTLAfterFinishedController(ctx context.Context, controllerContext Cont
return nil, true, nil
}
func startLegacySATokenCleaner(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newLegacyServiceAccountTokenCleanerControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.LegacyServiceAccountTokenCleanerController,
aliases: []string{"legacy-service-account-token-cleaner"},
initFunc: startLegacyServiceAccountTokenCleanerController,
requiredFeatureGates: []featuregate.Feature{
features.LegacyServiceAccountTokenCleanUp, // TODO update app.TestFeatureGatedControllersShouldNotDefineAliases when removing this feature
},
}
}
func startLegacyServiceAccountTokenCleanerController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
cleanUpPeriod := controllerContext.ComponentConfig.LegacySATokenCleaner.CleanUpPeriod.Duration
legacySATokenCleaner, err := serviceaccountcontroller.NewLegacySATokenCleaner(
controllerContext.InformerFactory.Core().V1().ServiceAccounts(),
@ -690,7 +878,19 @@ func setNodeCIDRMaskSizes(cfg nodeipamconfig.NodeIPAMControllerConfiguration, cl
return sortedSizes(ipv4Mask, ipv6Mask), nil
}
func startStorageVersionGCController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newStorageVersionGarbageCollectorControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.StorageVersionGarbageCollectorController,
aliases: []string{"storage-version-gc"},
initFunc: startStorageVersionGarbageCollectorController,
requiredFeatureGates: []featuregate.Feature{
genericfeatures.APIServerIdentity,
genericfeatures.StorageVersionAPI,
},
}
}
func startStorageVersionGarbageCollectorController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
go storageversiongc.NewStorageVersionGC(
ctx,
controllerContext.ClientBuilder.ClientOrDie("storage-version-garbage-collector"),

View File

@ -28,7 +28,6 @@ import (
clientset "k8s.io/client-go/kubernetes"
fakeclientset "k8s.io/client-go/kubernetes/fake"
restclient "k8s.io/client-go/rest"
"k8s.io/controller-manager/controller"
)
// TestClientBuilder inherits ClientBuilder and can accept a given fake clientset.
@ -105,15 +104,13 @@ func possibleDiscoveryResource() []*metav1.APIResourceList {
}
}
type controllerInitFunc func(context.Context, ControllerContext) (controller.Interface, bool, error)
func TestController_DiscoveryError(t *testing.T) {
controllerInitFuncMap := map[string]controllerInitFunc{
"ResourceQuotaController": startResourceQuotaController,
"GarbageCollectorController": startGarbageCollectorController,
"EndpointSliceController": startEndpointSliceController,
"EndpointSliceMirroringController": startEndpointSliceMirroringController,
"PodDisruptionBudgetController": startDisruptionController,
controllerDescriptorMap := map[string]*ControllerDescriptor{
"ResourceQuotaController": newResourceQuotaControllerDescriptor(),
"GarbageCollectorController": newGarbageCollectorControllerDescriptor(),
"EndpointSliceController": newEndpointSliceControllerDescriptor(),
"EndpointSliceMirroringController": newEndpointSliceMirroringControllerDescriptor(),
"PodDisruptionBudgetController": newDisruptionControllerDescriptor(),
}
tcs := map[string]struct {
@ -143,10 +140,10 @@ func TestController_DiscoveryError(t *testing.T) {
ObjectOrMetadataInformerFactory: testInformerFactory,
InformersStarted: make(chan struct{}),
}
for funcName, controllerInit := range controllerInitFuncMap {
_, _, err := controllerInit(context.TODO(), ctx)
for controllerName, controllerDesc := range controllerDescriptorMap {
_, _, err := controllerDesc.GetInitFunc()(context.TODO(), ctx, controllerName)
if test.expectedErr != (err != nil) {
t.Errorf("%v test failed for use case: %v", funcName, name)
t.Errorf("%v test failed for use case: %v", controllerName, name)
}
}
_, _, err := startModifiedNamespaceController(

View File

@ -23,11 +23,20 @@ import (
"context"
"k8s.io/controller-manager/controller"
"k8s.io/kubernetes/cmd/kube-controller-manager/names"
endpointslicecontroller "k8s.io/kubernetes/pkg/controller/endpointslice"
endpointslicemirroringcontroller "k8s.io/kubernetes/pkg/controller/endpointslicemirroring"
)
func startEndpointSliceController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newEndpointSliceControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.EndpointSliceController,
aliases: []string{"endpointslice"},
initFunc: startEndpointSliceController,
}
}
func startEndpointSliceController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
go endpointslicecontroller.NewController(
ctx,
controllerContext.InformerFactory.Core().V1().Pods(),
@ -41,7 +50,15 @@ func startEndpointSliceController(ctx context.Context, controllerContext Control
return nil, true, nil
}
func startEndpointSliceMirroringController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newEndpointSliceMirroringControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.EndpointSliceMirroringController,
aliases: []string{"endpointslicemirroring"},
initFunc: startEndpointSliceMirroringController,
}
}
func startEndpointSliceMirroringController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
go endpointslicemirroringcontroller.NewController(
ctx,
controllerContext.InformerFactory.Core().V1().Endpoints(),

View File

@ -25,10 +25,19 @@ import (
"k8s.io/client-go/dynamic"
"k8s.io/client-go/scale"
"k8s.io/controller-manager/controller"
"k8s.io/kubernetes/cmd/kube-controller-manager/names"
"k8s.io/kubernetes/pkg/controller/disruption"
)
func startDisruptionController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newDisruptionControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.DisruptionController,
aliases: []string{"disruption"},
initFunc: startDisruptionController,
}
}
func startDisruptionController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
client := controllerContext.ClientBuilder.ClientOrDie("disruption-controller")
config := controllerContext.ClientBuilder.ConfigOrDie("disruption-controller")
scaleKindResolver := scale.NewDiscoveryScaleKindResolver(client.Discovery())

View File

@ -20,10 +20,19 @@ import (
"context"
"k8s.io/controller-manager/controller"
"k8s.io/kubernetes/cmd/kube-controller-manager/names"
"k8s.io/kubernetes/pkg/controller/clusterroleaggregation"
)
func startClusterRoleAggregrationController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newClusterRoleAggregrationControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.ClusterRoleAggregationController,
aliases: []string{"clusterrole-aggregation"},
initFunc: startClusterRoleAggregationController,
}
}
func startClusterRoleAggregationController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
go clusterroleaggregation.NewClusterRoleAggregation(
controllerContext.InformerFactory.Rbac().V1().ClusterRoles(),
controllerContext.ClientBuilder.ClientOrDie("clusterrole-aggregation-controller").RbacV1(),

View File

@ -33,7 +33,6 @@ import (
"k8s.io/kubernetes/cmd/kube-controller-manager/app"
kubecontrollerconfig "k8s.io/kubernetes/cmd/kube-controller-manager/app/config"
"k8s.io/kubernetes/cmd/kube-controller-manager/app/options"
"k8s.io/kubernetes/cmd/kube-controller-manager/names"
)
func init() {
@ -97,7 +96,7 @@ func StartTestServer(ctx context.Context, customFlags []string) (result TestServ
if err != nil {
return TestServer{}, err
}
all, disabled, aliases := app.KnownControllers(), app.ControllersDisabledByDefault.List(), names.KCMControllerAliases()
all, disabled, aliases := app.KnownControllers(), app.ControllersDisabledByDefault(), app.ControllerAliases()
namedFlagSets := s.Flags(all, disabled, aliases)
for _, f := range namedFlagSets.FlagSets {
fs.AddFlagSet(f)

View File

@ -21,14 +21,26 @@ import (
pluginvalidatingadmissionpolicy "k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy"
"k8s.io/apiserver/pkg/cel/openapi/resolver"
genericfeatures "k8s.io/apiserver/pkg/features"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/component-base/featuregate"
"k8s.io/controller-manager/controller"
"k8s.io/kubernetes/cmd/kube-controller-manager/names"
"k8s.io/kubernetes/pkg/controller/validatingadmissionpolicystatus"
"k8s.io/kubernetes/pkg/generated/openapi"
)
func startValidatingAdmissionPolicyStatusController(ctx context.Context, controllerContext ControllerContext) (controller.Interface, bool, error) {
func newValidatingAdmissionPolicyStatusControllerDescriptor() *ControllerDescriptor {
return &ControllerDescriptor{
name: names.ValidatingAdmissionPolicyStatusController,
initFunc: startValidatingAdmissionPolicyStatusController,
requiredFeatureGates: []featuregate.Feature{
genericfeatures.ValidatingAdmissionPolicy,
},
}
}
func startValidatingAdmissionPolicyStatusController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
// KCM won't start the controller without the feature gate set.
typeChecker := &pluginvalidatingadmissionpolicy.TypeChecker{
SchemaResolver: resolver.NewDefinitionsSchemaResolver(scheme.Scheme, openapi.GetOpenAPIDefinitions),

View File

@ -16,8 +16,6 @@ limitations under the License.
package names
import cpnames "k8s.io/cloud-provider/names"
// Canonical controller names
//
// NAMING CONVENTIONS
@ -28,23 +26,19 @@ import cpnames "k8s.io/cloud-provider/names"
// CHANGE POLICY
// The controller names should be treated as IDs.
// They can only be changed if absolutely necessary. For example if an inappropriate name was chosen in the past, or if the scope of the controller changes.
// When a name is changed, the old name should be aliased in KCMControllerAliases, while preserving all old aliases.
// When a name is changed, the old name should be aliased in app.ControllerDescriptor#GetAliases, while preserving all old aliases.
// This is done to achieve backwards compatibility
//
// USE CASES
// The following places should use the controller name constants, when:
// 1. registering a controller in app.NewControllerInitializers or app.KnownControllers:
// 1.1. disabling a controller by default in app.ControllersDisabledByDefault
// 1.2. checking if IsControllerEnabled
// 1.3. defining an alias in KCMControllerAliases (for backwards compatibility only)
// 1. defining a new app.ControllerDescriptor so it can be used in app.NewControllerDescriptors or app.KnownControllers:
// 2. used anywhere inside the controller itself:
// 2.1. [TODO] logger component should be configured with the controller name by calling LoggerWithName
// 2.2. [TODO] logging should use a canonical controller name when referencing a controller (Eg. Starting X, Shutting down X)
// 2.3. [TODO] emitted events should have an EventSource.Component set to the controller name (usually when initializing an EventRecorder)
// 2.4. [TODO] registering ControllerManagerMetrics with ControllerStarted and ControllerStopped
// 2.5. [TODO] calling WaitForNamedCacheSync
// 2.1. [TODO] logging should use a canonical controller name when referencing a controller (Eg. Starting X, Shutting down X)
// 2.2. [TODO] emitted events should have an EventSource.Component set to the controller name (usually when initializing an EventRecorder)
// 2.3. [TODO] registering ControllerManagerMetrics with ControllerStarted and ControllerStopped
// 2.4. [TODO] calling WaitForNamedCacheSync
// 3. defining controller options for "--help" command or generated documentation
// 3.1. controller name should be used to create a pflag.FlagSet when registering controller options (the name is rendered in a controller flag group header)
// 3.1. controller name should be used to create a pflag.FlagSet when registering controller options (the name is rendered in a controller flag group header) in options.KubeControllerManagerOptions
// 3.2. when defined flag's help mentions a controller name
// 4. defining a new service account for a new controller (old controllers may have inconsistent service accounts to stay backwards compatible)
const (
@ -88,54 +82,3 @@ const (
LegacyServiceAccountTokenCleanerController = "legacy-serviceaccount-token-cleaner-controller"
ValidatingAdmissionPolicyStatusController = "validatingadmissionpolicy-status-controller"
)
// KCMControllerAliases returns a mapping of aliases to canonical controller names
//
// These aliases ensure backwards compatibility and should never be removed!
// Only addition of new aliases is allowed, and only when a canonical name is changed (please see CHANGE POLICY of controller names)
func KCMControllerAliases() map[string]string {
// return a new reference to achieve immutability of the mapping
return map[string]string{
"serviceaccount-token": ServiceAccountTokenController,
"endpoint": EndpointsController,
"endpointslice": EndpointSliceController,
"endpointslicemirroring": EndpointSliceMirroringController,
"replicationcontroller": ReplicationControllerController,
"podgc": PodGarbageCollectorController,
"resourcequota": ResourceQuotaController,
"namespace": NamespaceController,
"serviceaccount": ServiceAccountController,
"garbagecollector": GarbageCollectorController,
"daemonset": DaemonSetController,
"job": JobController,
"deployment": DeploymentController,
"replicaset": ReplicaSetController,
"horizontalpodautoscaling": HorizontalPodAutoscalerController,
"disruption": DisruptionController,
"statefulset": StatefulSetController,
"cronjob": CronJobController,
"csrsigning": CertificateSigningRequestSigningController,
"csrapproving": CertificateSigningRequestApprovingController,
"csrcleaner": CertificateSigningRequestCleanerController,
"ttl": TTLController,
"bootstrapsigner": BootstrapSignerController,
"tokencleaner": TokenCleanerController,
"nodeipam": NodeIpamController,
"nodelifecycle": NodeLifecycleController,
"service": cpnames.ServiceLBController,
"route": cpnames.NodeRouteController,
"cloud-node-lifecycle": cpnames.CloudNodeLifecycleController,
"persistentvolume-binder": PersistentVolumeBinderController,
"attachdetach": PersistentVolumeAttachDetachController,
"persistentvolume-expander": PersistentVolumeExpanderController,
"clusterrole-aggregation": ClusterRoleAggregationController,
"pvc-protection": PersistentVolumeClaimProtectionController,
"pv-protection": PersistentVolumeProtectionController,
"ttl-after-finished": TTLAfterFinishedController,
"root-ca-cert-publisher": RootCACertificatePublisherController,
"ephemeral-volume": EphemeralVolumeController,
"storage-version-gc": StorageVersionGarbageCollectorController,
"resource-claim-controller": ResourceClaimController,
"legacy-service-account-token-cleaner": LegacyServiceAccountTokenCleanerController,
}
}