@@ -11,6 +11,9 @@ import (
11
11
"time"
12
12
13
13
csiaddonsv1alpha1 "github.com/csi-addons/kubernetes-csi-addons/api/csiaddons/v1alpha1"
14
+ v1 "k8s.io/api/core/v1"
15
+ k8serrors "k8s.io/apimachinery/pkg/api/errors"
16
+
14
17
volrep "github.com/csi-addons/kubernetes-csi-addons/api/replication.storage/v1alpha1"
15
18
snapv1 "github.com/kubernetes-csi/external-snapshotter/client/v8/apis/volumesnapshot/v1"
16
19
groupsnapv1beta1 "github.com/red-hat-storage/external-snapshotter/client/v8/apis/volumegroupsnapshot/v1beta1"
@@ -56,9 +59,10 @@ const (
56
59
// DRClusterConfigReconciler reconciles a DRClusterConfig object
57
60
type DRClusterConfigReconciler struct {
58
61
client.Client
59
- Scheme * runtime.Scheme
60
- Log logr.Logger
61
- RateLimiter * workqueue.TypedRateLimiter [reconcile.Request ]
62
+ Scheme * runtime.Scheme
63
+ Log logr.Logger
64
+ RateLimiter * workqueue.TypedRateLimiter [reconcile.Request ]
65
+ ObjectStoreGetter ObjectStoreGetter
62
66
}
63
67
64
68
//nolint:lll
@@ -148,7 +152,7 @@ func setDRClusterConfigInitialCondition(conditions *[]metav1.Condition, observed
148
152
Message : message ,
149
153
})
150
154
util .SetStatusConditionIfNotFound (conditions , metav1.Condition {
151
- Type : ramen .DRClusterConfigS3Reachable ,
155
+ Type : ramen .DRClusterConfigS3Healthy ,
152
156
Reason : DRClusterConfigConditionReasonInitializing ,
153
157
ObservedGeneration : observedGeneration ,
154
158
Status : metav1 .ConditionUnknown ,
@@ -168,6 +172,18 @@ func setDRClusterConfigConfigurationProcessedCondition(conditions *[]metav1.Cond
168
172
})
169
173
}
170
174
175
+ func setDRClusterConfigS3ReachableCondition (conditions * []metav1.Condition , observedGeneration int64 ,
176
+ message string , conditionStatus metav1.ConditionStatus , reason string ,
177
+ ) {
178
+ util .SetStatusCondition (conditions , metav1.Condition {
179
+ Type : ramen .DRClusterConfigS3Healthy ,
180
+ Reason : reason ,
181
+ ObservedGeneration : observedGeneration ,
182
+ Status : conditionStatus ,
183
+ Message : message ,
184
+ })
185
+ }
186
+
171
187
func (r * DRClusterConfigReconciler ) GetDRClusterConfig (ctx context.Context ) (* ramen.DRClusterConfig , error ) {
172
188
drcConfigs := & ramen.DRClusterConfigList {}
173
189
if err := r .Client .List (ctx , drcConfigs ); err != nil {
@@ -275,9 +291,66 @@ func (r *DRClusterConfigReconciler) processCreateOrUpdate(
275
291
setDRClusterConfigConfigurationProcessedCondition (& drCConfig .Status .Conditions , drCConfig .Generation ,
276
292
"Configuration processed and validated" , metav1 .ConditionTrue , DRClusterConfigConditionConfigurationProcessed )
277
293
294
+ if err := r .validateS3Profiles (ctx , drCConfig ); err != nil {
295
+ log .Info ("Reconcile error" , "error" , err )
296
+
297
+ return ctrl.Result {Requeue : true }, err
298
+ }
299
+
278
300
return ctrl.Result {}, nil
279
301
}
280
302
303
+ func (r * DRClusterConfigReconciler ) validateS3Profiles (ctx context.Context , drCConfig * ramen.DRClusterConfig ) error {
304
+ // Fetch the ramen config resource
305
+ _ , ramenConfig , err := ConfigMapGet (ctx , r .Client )
306
+ if err != nil {
307
+ return fmt .Errorf ("failed to get Ramen configmap: %w" , err )
308
+ }
309
+
310
+ // Iterate all profiles listed in it and check for existing healthy ones
311
+ for profileIdx := range ramenConfig .S3StoreProfiles {
312
+ // for each profile, check that it has an actual secret attached to its secretRef ID
313
+ profile := ramenConfig .S3StoreProfiles [profileIdx ]
314
+ secretRef := profile .S3SecretRef
315
+ secret := & v1.Secret {
316
+ ObjectMeta : metav1.ObjectMeta {Name : secretRef .Name , Namespace : secretRef .Namespace },
317
+ }
318
+
319
+ if err := r .Client .Get (ctx , types.NamespacedName {
320
+ Namespace : secret .Namespace ,
321
+ Name : secret .Name ,
322
+ }, secret ); err != nil {
323
+ if ! k8serrors .IsNotFound (err ) {
324
+ setDRClusterConfigS3ReachableCondition (& drCConfig .Status .Conditions , drCConfig .Generation ,
325
+ fmt .Sprintf ("Found an unhealthy S3 profile %q for which there's no secret" , profile .S3ProfileName ),
326
+ metav1 .ConditionFalse , DRClusterConfigS3Unreachable )
327
+
328
+ return fmt .Errorf ("failed to get secret: %w" , err )
329
+ }
330
+ // If there's no secret attached to the secretRef's namespacedname -- mark profile as unhealthy
331
+ setDRClusterConfigS3ReachableCondition (& drCConfig .Status .Conditions , drCConfig .Generation ,
332
+ fmt .Sprintf ("Found an unhealthy S3 profile %q for which there's no secret" , profile .S3ProfileName ),
333
+ metav1 .ConditionFalse , DRClusterConfigS3Unreachable )
334
+
335
+ return fmt .Errorf ("secrect not found: %w" , err )
336
+ }
337
+ // Profile does have a secret. Check if it has connectivity and record in status accordingly
338
+ if reason , err := S3ProfileValidate (ctx , r .Client , r .ObjectStoreGetter , profile .S3ProfileName , types.NamespacedName {
339
+ Name : drCConfig .Name , Namespace : drCConfig .Namespace ,
340
+ }.String (), r .Log ); err != nil {
341
+ setDRClusterConfigS3ReachableCondition (& drCConfig .Status .Conditions , drCConfig .Generation , err .Error (),
342
+ metav1 .ConditionFalse , reason )
343
+
344
+ return fmt .Errorf ("failed to validate s3 profile: %w" , err )
345
+ }
346
+ }
347
+ // All S3 profiles are healthy -- record to status and exit
348
+ setDRClusterConfigS3ReachableCondition (& drCConfig .Status .Conditions , drCConfig .Generation ,
349
+ fmt .Sprintf ("All S3 profiles are healthy" ), metav1 .ConditionTrue , DRClusterConfigS3Reachable )
350
+
351
+ return nil
352
+ }
353
+
281
354
// UpdateSupportedClasses updates DRClusterConfig status with a list of storage related classes that are marked for DR
282
355
// support. The list is sorted alphabetically to avoid out of order listing and status updates due to the same
283
356
func (r * DRClusterConfigReconciler ) UpdateSupportedClasses (
0 commit comments