Skip to content

Commit 5fc5164

Browse files
author
Tim Usner
authored
Handle NoMatchErrors gracefully for extension controllers (gardener#4412)
1 parent f40f191 commit 5fc5164

File tree

3 files changed

+58
-19
lines changed

3 files changed

+58
-19
lines changed

pkg/gardenlet/controller/extensions/artifact.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ type predicateFn func(newObj, oldObj interface{}) bool
4343
// artifact is specified for extension kinds.
4444
// It servers as a helper to setup the corresponding reconciliation function.
4545
type artifact struct {
46+
initialized bool
47+
4648
gvk schema.GroupVersionKind
4749
newObjFunc func() client.Object
4850
newListFunc func() client.ObjectList
@@ -152,6 +154,9 @@ func (c *controllerArtifacts) registerExtensionControllerArtifacts(controllerIns
152154
// initialize obtains the informers for the enclosing artifacts.
153155
func (c *controllerArtifacts) initialize(ctx context.Context, seedClient kubernetes.Interface) error {
154156
initialize := func(a *artifact) error {
157+
if a.initialized {
158+
return nil
159+
}
155160
informer, err := seedClient.Cache().GetInformerForKind(ctx, a.gvk)
156161
if err != nil {
157162
return err
@@ -160,6 +165,7 @@ func (c *controllerArtifacts) initialize(ctx context.Context, seedClient kuberne
160165
c.hasSyncedFuncs = append(c.hasSyncedFuncs, informer.HasSynced)
161166
c.shutDownFuncs = append(c.shutDownFuncs, a.queue.ShutDown)
162167
a.addEventHandlerFn()
168+
a.initialized = true
163169
return nil
164170
}
165171

pkg/gardenlet/controller/extensions/extensions.go

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import (
3939
type Controller struct {
4040
log *logrus.Logger
4141

42+
initialized bool
4243
waitGroup sync.WaitGroup
4344
workerCh chan int
4445
numberOfRunningWorkers int
@@ -49,7 +50,7 @@ type Controller struct {
4950
}
5051

5152
// NewController creates new controller that syncs extensions states to ShootState
52-
func NewController(ctx context.Context, gardenClient, seedClient kubernetes.Interface, seedName string, log *logrus.Logger, recorder record.EventRecorder) (*Controller, error) {
53+
func NewController(gardenClient, seedClient kubernetes.Interface, seedName string, log *logrus.Logger, recorder record.EventRecorder) *Controller {
5354
controller := &Controller{
5455
log: log,
5556
workerCh: make(chan int),
@@ -67,15 +68,27 @@ func NewController(ctx context.Context, gardenClient, seedClient kubernetes.Inte
6768
shootStateControl: NewShootStateControl(gardenClient, seedClient, log, recorder),
6869
}
6970

70-
if err := controller.controllerArtifacts.initialize(ctx, seedClient); err != nil {
71-
return nil, err
72-
}
71+
return controller
72+
}
7373

74-
return controller, nil
74+
// Initialize sets up all necessary dependencies to run this controller.
75+
// This function must be called before Run is executed.
76+
func (s *Controller) Initialize(ctx context.Context, seedClient kubernetes.Interface) error {
77+
if err := s.controllerArtifacts.initialize(ctx, seedClient); err != nil {
78+
return err
79+
}
80+
s.initialized = true
81+
return nil
7582
}
7683

7784
// Run creates workers that reconciles extension resources.
85+
// Initialize must be called before running the controller.
7886
func (s *Controller) Run(ctx context.Context, controllerInstallationWorkers, shootStateWorkers int) {
87+
if !s.initialized {
88+
s.log.Fatal("controller is not initialized")
89+
return
90+
}
91+
7992
timeoutCtx, cancel := context.WithTimeout(ctx, time.Minute*2)
8093
defer cancel()
8194

pkg/gardenlet/controller/factory.go

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,10 @@ import (
4848
gutil "github.com/gardener/gardener/pkg/utils/gardener"
4949
"github.com/gardener/gardener/pkg/utils/imagevector"
5050
kutil "github.com/gardener/gardener/pkg/utils/kubernetes"
51+
"github.com/gardener/gardener/pkg/utils/retry"
5152

5253
corev1 "k8s.io/api/core/v1"
54+
"k8s.io/apimachinery/pkg/api/meta"
5355
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
5456
"k8s.io/apimachinery/pkg/util/runtime"
5557
"k8s.io/client-go/tools/cache"
@@ -136,7 +138,8 @@ func (f *GardenletControllerFactory) Run(ctx context.Context) error {
136138
// Initialize the workqueue metrics collection.
137139
gardenmetrics.RegisterWorkqueMetrics()
138140

139-
seedClient, err := f.clientMap.GetClient(ctx, keys.ForSeedWithName(f.cfg.SeedConfig.Name))
141+
seedName := f.cfg.SeedConfig.Name
142+
seedClient, err := f.clientMap.GetClient(ctx, keys.ForSeedWithName(seedName))
140143
if err != nil {
141144
return fmt.Errorf("failed to get seed client: %w", err)
142145
}
@@ -167,10 +170,7 @@ func (f *GardenletControllerFactory) Run(ctx context.Context) error {
167170
return fmt.Errorf("failed initializing NetworkPolicy controller: %w", err)
168171
}
169172

170-
extensionsController, err := extensionscontroller.NewController(ctx, k8sGardenClient, seedClient, f.cfg.SeedConfig.Name, logger.Logger, f.recorder)
171-
if err != nil {
172-
return fmt.Errorf("failed initializing extensions controller: %w", err)
173-
}
173+
extensionsController := extensionscontroller.NewController(k8sGardenClient, seedClient, f.cfg.SeedConfig.Name, logger.Logger, f.recorder)
174174

175175
managedSeedController, err := managedseedcontroller.NewManagedSeedController(ctx, f.clientMap, f.cfg, imageVector, f.recorder, logger.Logger)
176176
if err != nil {
@@ -192,20 +192,40 @@ func (f *GardenletControllerFactory) Run(ctx context.Context) error {
192192
extensionsController,
193193
)
194194

195-
go networkpolicyController.Run(ctx, *f.cfg.Controllers.SeedAPIServerNetworkPolicy.ConcurrentSyncs)
196-
go extensionsController.Run(ctx, *f.cfg.Controllers.ControllerInstallationRequired.ConcurrentSyncs, *f.cfg.Controllers.ShootStateSync.ConcurrentSyncs)
197-
go backupBucketController.Run(ctx, *f.cfg.Controllers.BackupBucket.ConcurrentSyncs)
198-
go backupEntryController.Run(ctx, *f.cfg.Controllers.BackupEntry.ConcurrentSyncs)
199-
go bastionController.Run(ctx, *f.cfg.Controllers.Bastion.ConcurrentSyncs)
200-
go controllerInstallationController.Run(ctx, *f.cfg.Controllers.ControllerInstallation.ConcurrentSyncs, *f.cfg.Controllers.ControllerInstallationCare.ConcurrentSyncs)
201-
go seedController.Run(ctx, *f.cfg.Controllers.Seed.ConcurrentSyncs)
202-
go shootController.Run(ctx, *f.cfg.Controllers.Shoot.ConcurrentSyncs, *f.cfg.Controllers.ShootCare.ConcurrentSyncs)
203-
go managedSeedController.Run(ctx, *f.cfg.Controllers.ManagedSeed.ConcurrentSyncs)
195+
controllerCtx, cancel := context.WithCancel(ctx)
196+
197+
go networkpolicyController.Run(controllerCtx, *f.cfg.Controllers.SeedAPIServerNetworkPolicy.ConcurrentSyncs)
198+
go backupBucketController.Run(controllerCtx, *f.cfg.Controllers.BackupBucket.ConcurrentSyncs)
199+
go backupEntryController.Run(controllerCtx, *f.cfg.Controllers.BackupEntry.ConcurrentSyncs)
200+
go bastionController.Run(controllerCtx, *f.cfg.Controllers.Bastion.ConcurrentSyncs)
201+
go controllerInstallationController.Run(controllerCtx, *f.cfg.Controllers.ControllerInstallation.ConcurrentSyncs, *f.cfg.Controllers.ControllerInstallationCare.ConcurrentSyncs)
202+
go seedController.Run(controllerCtx, *f.cfg.Controllers.Seed.ConcurrentSyncs)
203+
go shootController.Run(controllerCtx, *f.cfg.Controllers.Shoot.ConcurrentSyncs, *f.cfg.Controllers.ShootCare.ConcurrentSyncs)
204+
go managedSeedController.Run(controllerCtx, *f.cfg.Controllers.ManagedSeed.ConcurrentSyncs)
205+
206+
if err := retry.Until(ctx, 10*time.Second, func(ctx context.Context) (bool, error) {
207+
if err := extensionsController.Initialize(ctx, seedClient); err != nil {
208+
// A NoMatchError most probably indicates that the necessary CRDs haven't been deployed to the affected seed cluster yet.
209+
// This can either be the case if the seed cluster is new or if a new extension CRD was added.
210+
if meta.IsNoMatchError(err) {
211+
logger.Logger.Errorf("An error occurred when initializing extension controllers: %v. Will retry.", err)
212+
return retry.MinorError(err)
213+
}
214+
return retry.SevereError(err)
215+
}
216+
return retry.Ok()
217+
}); err != nil {
218+
cancel()
219+
return err
220+
}
221+
222+
go extensionsController.Run(controllerCtx, *f.cfg.Controllers.ControllerInstallationRequired.ConcurrentSyncs, *f.cfg.Controllers.ShootStateSync.ConcurrentSyncs)
204223

205224
logger.Logger.Infof("Gardenlet (version %s) initialized.", version.Get().GitVersion)
206225

207226
// Shutdown handling
208227
<-ctx.Done()
228+
cancel()
209229

210230
logger.Logger.Infof("I have received a stop signal and will no longer watch resources.")
211231
logger.Logger.Infof("Bye Bye!")

0 commit comments

Comments
 (0)