Skip to content

Commit e50008b

Browse files
Eneman DonatienDonatien26
authored andcommitted
[BUG] 🐛 fix invalid delete of associate secret of s3user
1 parent d79ce95 commit e50008b

File tree

3 files changed

+112
-163
lines changed

3 files changed

+112
-163
lines changed

internal/controller/user/finalizer.go

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,11 @@ func (r *S3UserReconciler) handleDeletion(
7878
)
7979
}
8080

81-
err := r.deleteOldLinkedSecret(ctx, userResource)
81+
userOwnedlinkedSecrets, err := r.getUserLinkedSecrets(ctx, userResource)
8282
if err != nil {
8383
logger.Error(
8484
err,
85-
"An error occurred when trying to clean old secret linked to user",
85+
"An error occurred when trying to list secret linked to user",
8686
"userResourceName",
8787
userResource.Name,
8888
"NamespacedName",
@@ -93,29 +93,32 @@ func (r *S3UserReconciler) handleDeletion(
9393
req,
9494
userResource,
9595
s3v1alpha1.DeletionFailure,
96-
"Deletion of old secret associated to user have failed",
96+
"An error occurred when trying to list secret linked to user",
9797
err,
9898
)
9999
}
100+
for _, linkedSecret := range userOwnedlinkedSecrets {
101+
if err := r.deleteSecret(ctx, &linkedSecret); err != nil {
102+
logger.Error(
103+
err,
104+
"An error occurred when trying to list secret linked to user",
105+
"userResourceName",
106+
userResource.Name,
107+
"NamespacedName",
108+
req.NamespacedName.String(),
109+
"secretName",
110+
linkedSecret.Name,
111+
)
112+
return r.SetReconciledCondition(
113+
ctx,
114+
req,
115+
userResource,
116+
s3v1alpha1.DeletionFailure,
117+
"Deletion of secret associated to user have failed",
118+
err,
119+
)
120+
}
100121

101-
userOwnedSecret, _ := r.getUserSecret(ctx, userResource)
102-
if err := r.deleteSecret(ctx, &userOwnedSecret); err != nil {
103-
logger.Error(
104-
err,
105-
"An error occurred when trying to clean secret linked to user",
106-
"userResourceName",
107-
userResource.Name,
108-
"NamespacedName",
109-
req.NamespacedName.String(),
110-
)
111-
return r.SetReconciledCondition(
112-
ctx,
113-
req,
114-
userResource,
115-
s3v1alpha1.DeletionFailure,
116-
"Deletion of secret associated to user have failed",
117-
err,
118-
)
119122
}
120123

121124
//Remove userFinalizer. Once all finalizers have been removed, the object will be deleted.

internal/controller/user/reconcile.go

Lines changed: 59 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package user_controller
1818

1919
import (
20+
"cmp"
2021
"context"
2122
"fmt"
2223
"slices"
@@ -247,11 +248,11 @@ func (r *S3UserReconciler) handleUpdate(
247248
)
248249
}
249250

250-
err = r.deleteOldLinkedSecret(ctx, userResource)
251+
userOwnedlinkedSecrets, err := r.getUserLinkedSecrets(ctx, userResource)
251252
if err != nil {
252253
logger.Error(
253254
err,
254-
"An error occurred when trying to clean old secret linked to user",
255+
"An error occurred while listing the user's secret",
255256
"userResourceName",
256257
userResource.Name,
257258
"NamespacedName",
@@ -262,45 +263,70 @@ func (r *S3UserReconciler) handleUpdate(
262263
req,
263264
userResource,
264265
s3v1alpha1.Unreachable,
265-
"Deletion of old secret associated to user have failed",
266+
"Impossible to list the user's secret",
266267
err,
267268
)
268269
}
270+
currentUserSecret := corev1.Secret{}
271+
if len(userOwnedlinkedSecrets) == 0 {
272+
logger.Info(
273+
"No Secret associated to user found, user will be deleted from the S3 backend, then recreated with a secret",
274+
"userResourceName",
275+
userResource.Name,
276+
"NamespacedName",
277+
req.NamespacedName.String(),
278+
)
269279

270-
userOwnedSecret, err := r.getUserSecret(ctx, userResource)
271-
if err != nil {
272-
if err.Error() == "SecretListingFailed" {
273-
logger.Error(
280+
err = s3Client.DeleteUser(userResource.Spec.AccessKey)
281+
if err != nil {
282+
logger.Error(err, "Could not delete user on S3 server", "userResource",
283+
userResource.Name,
284+
"userResourceName",
285+
userResource.Name,
286+
"NamespacedName",
287+
req.NamespacedName.String())
288+
return r.SetReconciledCondition(
289+
ctx,
290+
req,
291+
userResource,
292+
s3v1alpha1.Unreachable,
293+
fmt.Sprintf(
294+
"Deletion of S3user %s on S3 server has failed",
295+
userResource.Name,
296+
),
274297
err,
275-
"An error occurred when trying to obtain the user's secret",
298+
)
299+
}
300+
return r.handleCreate(ctx, req, userResource)
301+
} else {
302+
foundSecret := false
303+
for _, linkedsecret := range userOwnedlinkedSecrets {
304+
if linkedsecret.Name != cmp.Or(userResource.Spec.SecretName, userResource.Name) {
305+
if err := r.deleteSecret(ctx, &linkedsecret); err != nil {
306+
logger.Info("Failed to delete unused secret", "secretName", linkedsecret.Name)
307+
return r.SetReconciledCondition(
308+
ctx,
309+
req,
310+
userResource,
311+
s3v1alpha1.Unreachable,
312+
"Failed to delete old linkedSecret",
313+
err,
314+
)
315+
}
316+
} else {
317+
foundSecret = true
318+
currentUserSecret = linkedsecret
319+
}
320+
}
321+
if !foundSecret {
322+
logger.Info(
323+
"No Secret associated to user found, user will be deleted from the S3 backend, then recreated with a secret",
276324
"userResourceName",
277325
userResource.Name,
278326
"NamespacedName",
279327
req.NamespacedName.String(),
280328
)
281329

282-
err = r.deleteSecret(ctx, &userOwnedSecret)
283-
if err != nil {
284-
logger.Error(
285-
err,
286-
"Deletion of secret associated to user have failed",
287-
"userResource",
288-
userResource.Name,
289-
"userResourceName",
290-
userResource.Name,
291-
"NamespacedName",
292-
req.NamespacedName.String(),
293-
)
294-
return r.SetReconciledCondition(
295-
ctx,
296-
req,
297-
userResource,
298-
s3v1alpha1.Unreachable,
299-
"Deletion of secret associated to user have failed",
300-
err,
301-
)
302-
303-
}
304330
err = s3Client.DeleteUser(userResource.Spec.AccessKey)
305331
if err != nil {
306332
logger.Error(err, "Could not delete user on S3 server", "userResource",
@@ -320,63 +346,9 @@ func (r *S3UserReconciler) handleUpdate(
320346
),
321347
err,
322348
)
323-
324349
}
325350
return r.handleCreate(ctx, req, userResource)
326-
} else if err.Error() == "S3UserSecretNameMismatch" {
327-
logger.Info("A secret with owner reference to the user was found, but its name doesn't match the spec. This is probably due to the S3User's spec changing (specifically spec.secretName being added, changed or removed). The \"old\" secret will be deleted.", "userResource",
328-
userResource.Name,
329-
"NamespacedName",
330-
req.NamespacedName.String())
331-
err = r.deleteSecret(ctx, &userOwnedSecret)
332-
if err != nil {
333-
logger.Error(err, "Deletion of secret associated to user have failed", "userResourceName",
334-
userResource.Name,
335-
"NamespacedName",
336-
req.NamespacedName.String())
337-
return r.SetReconciledCondition(
338-
ctx,
339-
req,
340-
userResource,
341-
s3v1alpha1.Unreachable,
342-
"Deletion of secret associated to user have failed",
343-
err,
344-
)
345-
346-
}
347-
}
348-
}
349-
350-
if userOwnedSecret.Name == "" {
351-
logger.Info(
352-
"Secret associated to user not found, user will be deleted from the S3 backend, then recreated with a secret",
353-
"userResourceName",
354-
userResource.Name,
355-
"NamespacedName",
356-
req.NamespacedName.String(),
357-
)
358-
359-
err = s3Client.DeleteUser(userResource.Spec.AccessKey)
360-
if err != nil {
361-
logger.Error(err, "Could not delete user on S3 server", "userResource",
362-
userResource.Name,
363-
"userResourceName",
364-
userResource.Name,
365-
"NamespacedName",
366-
req.NamespacedName.String())
367-
return r.SetReconciledCondition(
368-
ctx,
369-
req,
370-
userResource,
371-
s3v1alpha1.Unreachable,
372-
fmt.Sprintf(
373-
"Deletion of S3user %s on S3 server has failed",
374-
userResource.Name,
375-
),
376-
err,
377-
)
378351
}
379-
return r.handleCreate(ctx, req, userResource)
380352
}
381353

382354
logger.Info("Checking user policies", "userResource",
@@ -477,8 +449,8 @@ func (r *S3UserReconciler) handleUpdate(
477449

478450
credentialsValid, err := s3Client.CheckUserCredentialsValid(
479451
userResource.Name,
480-
string(userOwnedSecret.Data[userResource.Spec.SecretFieldNameAccessKey]),
481-
string(userOwnedSecret.Data[userResource.Spec.SecretFieldNameSecretKey]),
452+
string(currentUserSecret.Data[userResource.Spec.SecretFieldNameAccessKey]),
453+
string(currentUserSecret.Data[userResource.Spec.SecretFieldNameSecretKey]),
482454
)
483455

484456
if err != nil {
@@ -508,7 +480,7 @@ func (r *S3UserReconciler) handleUpdate(
508480
"NamespacedName",
509481
req.NamespacedName.String(),
510482
)
511-
err = r.deleteSecret(ctx, &userOwnedSecret)
483+
err = r.deleteSecret(ctx, &currentUserSecret)
512484
if err != nil {
513485
logger.Error(err, "Deletion of secret associated to user have failed", "userResource",
514486
userResource.Name,

internal/controller/user/utils.go

Lines changed: 29 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,12 @@ import (
2121
"context"
2222
"fmt"
2323

24+
s3v1alpha1 "github.com/InseeFrLab/s3-operator/api/v1alpha1"
2425
corev1 "k8s.io/api/core/v1"
25-
k8sapierrors "k8s.io/apimachinery/pkg/api/errors"
2626
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27-
"k8s.io/apimachinery/pkg/types"
2827
ctrl "sigs.k8s.io/controller-runtime"
2928
"sigs.k8s.io/controller-runtime/pkg/client"
3029
"sigs.k8s.io/controller-runtime/pkg/log"
31-
32-
s3v1alpha1 "github.com/InseeFrLab/s3-operator/api/v1alpha1"
3330
)
3431

3532
func (r *S3UserReconciler) addPoliciesToUser(
@@ -65,69 +62,46 @@ func (r *S3UserReconciler) addPoliciesToUser(
6562
return nil
6663
}
6764

68-
func (r *S3UserReconciler) deleteOldLinkedSecret(ctx context.Context, userResource *s3v1alpha1.S3User) error {
65+
func (r *S3UserReconciler) getUserLinkedSecrets(
66+
ctx context.Context,
67+
userResource *s3v1alpha1.S3User,
68+
) ([]corev1.Secret, error) {
6969
logger := log.FromContext(ctx)
70+
71+
// Listing every secrets in the S3User's namespace, as a first step
72+
// to get the actual secret matching the S3User proper.
73+
// TODO : proper label matching ?
7074
secretsList := &corev1.SecretList{}
7175

72-
// Define options with label selector and namespace
73-
listOpts := []client.ListOption{
74-
client.InNamespace(userResource.Namespace), // Filter by namespace
75-
client.MatchingLabels{"app.kubernetes.io/created-by": "s3-operator"}, // Filter by label
76-
}
76+
userSecretList := []corev1.Secret{}
7777

78-
// List Secrets with the specified label in the given namespace
79-
if err := r.List(ctx, secretsList, listOpts...); err != nil {
80-
return fmt.Errorf("failed to list secrets in namespace %s: %w", userResource.Namespace, err)
78+
err := r.List(ctx, secretsList, client.InNamespace(userResource.Namespace))
79+
if err != nil {
80+
logger.Error(err, "An error occurred while listing the secrets in user's namespace")
81+
return userSecretList, fmt.Errorf("SecretListingFailed")
8182
}
8283

84+
if len(secretsList.Items) == 0 {
85+
logger.Info("The user's namespace doesn't appear to contain any secret")
86+
return userSecretList, nil
87+
}
88+
// In all the secrets inside the S3User's namespace, one should have an owner reference
89+
// pointing to the S3User. For that specific secret, we check if its name matches the one from
90+
// the S3User, whether explicit (userResource.Spec.SecretName) or implicit (userResource.Name)
91+
// In case of mismatch, that secret is deleted (and will be recreated) ; if there is a match,
92+
// it will be used for state comparison.
93+
uid := userResource.GetUID()
94+
95+
// cmp.Or takes the first non "zero" value, see https://pkg.go.dev/cmp#Or
8396
for _, secret := range secretsList.Items {
8497
for _, ref := range secret.OwnerReferences {
85-
if ref.UID == userResource.GetUID() {
86-
if (userResource.Spec.SecretName != "" && secret.Name != userResource.Spec.SecretName) || (userResource.Spec.SecretName == "" && secret.Name != userResource.Name) {
87-
if err := r.deleteSecret(ctx, &secret); err != nil {
88-
logger.Info("Failed to delete unused secret", "secret", secret.Name)
89-
return fmt.Errorf("failed to delete unused secret %s, err %w", secret.Name, err)
90-
}
91-
}
98+
if ref.UID == uid {
99+
userSecretList = append(userSecretList, secret)
92100
}
93101
}
94102
}
95103

96-
return nil
97-
}
98-
99-
func (r *S3UserReconciler) getUserSecret(
100-
ctx context.Context,
101-
userResource *s3v1alpha1.S3User,
102-
) (corev1.Secret, error) {
103-
userSecret := &corev1.Secret{}
104-
secretName := userResource.Spec.SecretName
105-
if secretName == "" {
106-
secretName = userResource.Name
107-
}
108-
err := r.Get(
109-
ctx,
110-
types.NamespacedName{Namespace: userResource.Namespace, Name: secretName},
111-
userSecret,
112-
)
113-
if err != nil {
114-
if k8sapierrors.IsNotFound(err) {
115-
return *userSecret, fmt.Errorf(
116-
"secret %s not found in namespace %s",
117-
secretName,
118-
userResource.Namespace,
119-
)
120-
}
121-
return *userSecret, err
122-
}
123-
124-
for _, ref := range userSecret.OwnerReferences {
125-
if ref.UID == userResource.GetUID() {
126-
return *userSecret, nil
127-
}
128-
}
129-
130-
return *userSecret, err
104+
return userSecretList, nil
131105
}
132106

133107
func (r *S3UserReconciler) deleteSecret(ctx context.Context, secret *corev1.Secret) error {

0 commit comments

Comments
 (0)