forked from gardener/gardener
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathadd.go
More file actions
180 lines (162 loc) · 6.47 KB
/
add.go
File metadata and controls
180 lines (162 loc) · 6.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
// SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors
//
// SPDX-License-Identifier: Apache-2.0
package managedseed
import (
"context"
"strings"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/util/workqueue"
"k8s.io/utils/clock"
"k8s.io/utils/ptr"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/cluster"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/source"
gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1"
v1beta1constants "github.com/gardener/gardener/pkg/apis/core/v1beta1/constants"
seedmanagementv1alpha1 "github.com/gardener/gardener/pkg/apis/seedmanagement/v1alpha1"
"github.com/gardener/gardener/pkg/utils"
)
// ControllerName is the name of this controller.
const ControllerName = "managedseed"
// AddToManager adds Reconciler to the given manager.
func (r *Reconciler) AddToManager(
mgr manager.Manager,
gardenCluster cluster.Cluster,
seedCluster cluster.Cluster,
) error {
if r.GardenAPIReader == nil {
r.GardenAPIReader = gardenCluster.GetAPIReader()
}
if r.GardenClient == nil {
r.GardenClient = gardenCluster.GetClient()
}
if r.SeedClient == nil {
r.SeedClient = seedCluster.GetClient()
}
if r.Clock == nil {
r.Clock = clock.RealClock{}
}
if r.Recorder == nil {
r.Recorder = gardenCluster.GetEventRecorderFor(ControllerName + "-controller")
}
if r.GardenNamespaceGarden == "" {
r.GardenNamespaceGarden = v1beta1constants.GardenNamespace
}
if r.GardenNamespaceSeed == "" {
r.GardenNamespaceSeed = v1beta1constants.GardenNamespace
}
if r.GardenNamespaceShoot == "" {
r.GardenNamespaceShoot = v1beta1constants.GardenNamespace
}
return builder.
ControllerManagedBy(mgr).
Named(ControllerName).
WithOptions(controller.Options{
MaxConcurrentReconciles: ptr.Deref(r.Config.Controllers.ManagedSeed.ConcurrentSyncs, 0),
}).
WatchesRawSource(source.Kind[client.Object](
gardenCluster.GetCache(),
&seedmanagementv1alpha1.ManagedSeed{},
r.EnqueueWithJitterDelay(),
&predicate.GenerationChangedPredicate{},
)).
WatchesRawSource(source.Kind[client.Object](
gardenCluster.GetCache(),
&gardencorev1beta1.Seed{},
handler.EnqueueRequestsFromMapFunc(r.MapSeedToManagedSeed),
r.SeedPredicate(),
)).
Complete(r)
}
// SeedPredicate returns true when the Seed is a ManagedSeed controlled by this gardenlet. ManagedSeeds always have two
// `name.seed.gardener.cloud/` labels, and since the cache for Seeds is already limited on manager.Manager level to only
// contain Seeds relevant for this gardenlet, we can make this simple check here.
func (r *Reconciler) SeedPredicate() predicate.Predicate {
return predicate.NewPredicateFuncs(func(object client.Object) bool {
count := 0
for key := range object.GetLabels() {
if strings.HasPrefix(key, v1beta1constants.LabelPrefixSeedName) {
count++
}
}
return count > 1
})
}
// MapSeedToManagedSeed is a handler.MapFunc for mapping a Seed to the owning ManagedSeed.
func (r *Reconciler) MapSeedToManagedSeed(_ context.Context, obj client.Object) []reconcile.Request {
return []reconcile.Request{{NamespacedName: types.NamespacedName{Namespace: r.GardenNamespaceGarden, Name: obj.GetName()}}}
}
func reconcileRequest(obj client.Object) reconcile.Request {
return reconcile.Request{NamespacedName: types.NamespacedName{
Name: obj.GetName(),
Namespace: obj.GetNamespace(),
}}
}
// RandomDurationWithMetaDuration is an alias for `utils.RandomDurationWithMetaDuration`. Exposed for unit tests.
var RandomDurationWithMetaDuration = utils.RandomDurationWithMetaDuration
// EnqueueWithJitterDelay returns handler.Funcs which enqueues the object with a random Jitter duration when the JitterUpdate
// is enabled in ManagedSeed controller configuration.
// All other events are normally enqueued.
func (r *Reconciler) EnqueueWithJitterDelay() handler.EventHandler {
return &handler.Funcs{
CreateFunc: func(_ context.Context, evt event.CreateEvent, q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
managedSeed, ok := evt.Object.(*seedmanagementv1alpha1.ManagedSeed)
if !ok {
return
}
generationChanged := managedSeed.Generation != managedSeed.Status.ObservedGeneration
// Managed seed with deletion timestamp and newly created managed seed will be enqueued immediately.
// Generation is 1 for newly created objects.
if managedSeed.DeletionTimestamp != nil || managedSeed.Generation == 1 {
q.Add(reconcileRequest(evt.Object))
return
}
if generationChanged {
if *r.Config.Controllers.ManagedSeed.JitterUpdates {
q.AddAfter(reconcileRequest(evt.Object), RandomDurationWithMetaDuration(r.Config.Controllers.ManagedSeed.SyncJitterPeriod))
} else {
q.Add(reconcileRequest(evt.Object))
}
return
}
// Spread reconciliation of managed seeds (including gardenlet updates/rollouts) across the configured sync jitter
// period to avoid overloading the gardener-apiserver if all gardenlets in all managed seeds are (re)starting
// roughly at the same time.
q.AddAfter(reconcileRequest(evt.Object), RandomDurationWithMetaDuration(r.Config.Controllers.ManagedSeed.SyncJitterPeriod))
},
UpdateFunc: func(_ context.Context, evt event.UpdateEvent, q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
managedSeed, ok := evt.ObjectNew.(*seedmanagementv1alpha1.ManagedSeed)
if !ok {
return
}
if managedSeed.Generation == managedSeed.Status.ObservedGeneration {
return
}
// Managed seed with deletion timestamp and newly created managed seed will be enqueued immediately.
// Generation is 1 for newly created objects.
if managedSeed.DeletionTimestamp != nil || managedSeed.Generation == 1 {
q.Add(reconcileRequest(evt.ObjectNew))
return
}
if ptr.Deref(r.Config.Controllers.ManagedSeed.JitterUpdates, false) {
q.AddAfter(reconcileRequest(evt.ObjectNew), RandomDurationWithMetaDuration(r.Config.Controllers.ManagedSeed.SyncJitterPeriod))
} else {
q.Add(reconcileRequest(evt.ObjectNew))
}
},
DeleteFunc: func(_ context.Context, evt event.DeleteEvent, q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
if evt.Object == nil {
return
}
q.Add(reconcileRequest(evt.Object))
},
}
}