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
163 lines (141 loc) · 6.48 KB
/
add.go
File metadata and controls
163 lines (141 loc) · 6.48 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
// SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors
//
// SPDX-License-Identifier: Apache-2.0
package backupentry
import (
"context"
"github.com/go-logr/logr"
"k8s.io/apimachinery/pkg/types"
"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/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"
gardencore "github.com/gardener/gardener/pkg/apis/core"
gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1"
v1beta1constants "github.com/gardener/gardener/pkg/apis/core/v1beta1/constants"
extensionsv1alpha1 "github.com/gardener/gardener/pkg/apis/extensions/v1alpha1"
"github.com/gardener/gardener/pkg/controllerutils/mapper"
predicateutils "github.com/gardener/gardener/pkg/controllerutils/predicate"
"github.com/gardener/gardener/pkg/extensions"
gardenerutils "github.com/gardener/gardener/pkg/utils/gardener"
)
// ControllerName is the name of this controller.
const ControllerName = "backupentry"
// AddToManager adds Reconciler to the given manager.
func (r *Reconciler) AddToManager(mgr manager.Manager, gardenCluster, seedCluster cluster.Cluster) error {
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.GardenNamespace == "" {
r.GardenNamespace = v1beta1constants.GardenNamespace
}
return builder.
ControllerManagedBy(mgr).
Named(ControllerName).
WithOptions(controller.Options{
MaxConcurrentReconciles: ptr.Deref(r.Config.ConcurrentSyncs, 0),
RateLimiter: r.RateLimiter,
}).
WatchesRawSource(source.Kind[client.Object](
gardenCluster.GetCache(),
&gardencorev1beta1.BackupEntry{},
&handler.EnqueueRequestForObject{},
&predicate.GenerationChangedPredicate{},
predicate.NewPredicateFuncs(r.BackupEntryPredicate),
)).
WatchesRawSource(source.Kind[client.Object](
gardenCluster.GetCache(),
&gardencorev1beta1.BackupBucket{},
handler.EnqueueRequestsFromMapFunc(r.MapBackupBucketToBackupEntry(mgr.GetLogger().WithValues("controller", ControllerName))),
predicateutils.LastOperationChanged(getBackupBucketLastOperation),
)).
WatchesRawSource(source.Kind[client.Object](
seedCluster.GetCache(),
&extensionsv1alpha1.BackupEntry{},
handler.EnqueueRequestsFromMapFunc(r.MapExtensionBackupEntryToCoreBackupEntry(mgr.GetLogger().WithValues("controller", ControllerName))),
predicateutils.LastOperationChanged(predicateutils.GetExtensionLastOperation),
)).
Complete(r)
}
// MapBackupBucketToBackupEntry is a handler.MapFunc for mapping a core.gardener.cloud/v1beta1.BackupBucket to the
// core.gardener.cloud/v1beta1.BackupEntry that references it.
func (r *Reconciler) MapBackupBucketToBackupEntry(log logr.Logger) handler.MapFunc {
return func(ctx context.Context, obj client.Object) []reconcile.Request {
backupBucket, ok := obj.(*gardencorev1beta1.BackupBucket)
if !ok {
return nil
}
// We cannot skip BackupBuckets with a seedName that differs from this gardenlet's seed.
// During the restore phase of control-plane migration, the source BackupEntry is created using the original BackupBucket
// name, which carries the seed name of the source seed. Consequently, it won't match this gardenlet's seed name.
// Nevertheless, events generated by that BackupBucket must still be mapped to the source BackupEntry
// running in the target seed.
backupEntryList := &gardencorev1beta1.BackupEntryList{}
if err := r.GardenClient.List(ctx, backupEntryList, client.MatchingFields{gardencore.BackupEntryBucketName: backupBucket.Name}); err != nil {
log.Error(err, "Failed to list backupentries referencing this bucket", "backupBucketName", backupBucket.Name)
return nil
}
return mapper.ObjectListToRequests(backupEntryList, r.BackupEntryPredicate)
}
}
// MapExtensionBackupEntryToCoreBackupEntry is a handler.MapFunc for mapping an extensions.gardener.cloud/v1alpha1.BackupEntry
// to the owning core.gardener.cloud/v1beta1.BackupEntry.
func (r *Reconciler) MapExtensionBackupEntryToCoreBackupEntry(log logr.Logger) handler.MapFunc {
return func(ctx context.Context, obj client.Object) []reconcile.Request {
if obj.GetDeletionTimestamp() != nil {
return nil
}
shootTechnicalID, _ := gardenerutils.ExtractShootDetailsFromBackupEntryName(obj.GetName())
if shootTechnicalID == "" {
return nil
}
shoot, err := extensions.GetShoot(ctx, r.SeedClient, shootTechnicalID)
if err != nil {
log.Error(err, "Failed to get shoot from cluster", "shootTechnicalID", shootTechnicalID)
return nil
}
if shoot == nil {
log.Info("Shoot is missing in cluster resource", "cluster", client.ObjectKey{Name: shootTechnicalID})
return nil
}
return []reconcile.Request{{NamespacedName: types.NamespacedName{Name: obj.GetName(), Namespace: shoot.Namespace}}}
}
}
// BackupEntryPredicate is a predicate which returns true if the core.gardener.cloud/v1beta1.BackupEntry has not yet been successfully migrated or has the `gardener.cloud/operation: restore` annotation and this gardenlet's seed is responsible for this backupentry.
func (r *Reconciler) BackupEntryPredicate(obj client.Object) bool {
backupEntry, ok := obj.(*gardencorev1beta1.BackupEntry)
if !ok {
return false
}
if responsibleSeedName := gardenerutils.GetResponsibleSeedName(backupEntry.Spec.SeedName, backupEntry.Status.SeedName); responsibleSeedName != r.SeedName {
return false
}
isMigrateSucceeded := backupEntry.Status.LastOperation != nil &&
backupEntry.Status.LastOperation.State == gardencorev1beta1.LastOperationStateSucceeded &&
backupEntry.Status.LastOperation.Type == gardencorev1beta1.LastOperationTypeMigrate
hasRestoreAnnotation := backupEntry.GetAnnotations()[v1beta1constants.GardenerOperation] == v1beta1constants.GardenerOperationRestore
return !isMigrateSucceeded || hasRestoreAnnotation
}
func getBackupBucketLastOperation(obj client.Object) *gardencorev1beta1.LastOperation {
backupBucket, ok := obj.(*gardencorev1beta1.BackupBucket)
if !ok {
return nil
}
return backupBucket.Status.LastOperation
}