-
Notifications
You must be signed in to change notification settings - Fork 775
feat: multi SCP composition #8917
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 5 commits
6bba692
400b02d
dd18a2d
b5eaad2
994a33e
4004fc7
5e03e79
0b83045
5c216cc
48f510a
cfd5cb1
6c4e575
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,7 +6,9 @@ package reconciler | |
|
|
||
| import ( | ||
| "context" | ||
| "encoding/json" | ||
| "reflect" | ||
| "strings" | ||
|
|
||
| corev1 "k8s.io/api/core/v1" | ||
| apierrors "k8s.io/apimachinery/pkg/api/errors" | ||
|
|
@@ -28,6 +30,7 @@ const ( | |
| SoftOwnerNamespaceLabel = "eck.k8s.elastic.co/owner-namespace" | ||
| SoftOwnerNameLabel = "eck.k8s.elastic.co/owner-name" | ||
| SoftOwnerKindLabel = "eck.k8s.elastic.co/owner-kind" | ||
| SoftOwnerRefsAnnotation = "eck.k8s.elastic.co/owner-refs" | ||
| ) | ||
|
|
||
| func WithPostUpdate(f func()) func(p *Params) { | ||
|
|
@@ -92,6 +95,49 @@ func SoftOwnerRefFromLabels(labels map[string]string) (SoftOwnerRef, bool) { | |
| return SoftOwnerRef{Namespace: namespace, Name: name, Kind: kind}, true | ||
| } | ||
|
|
||
| // SoftOwnerRefs returns the soft owner references of the given object. | ||
| func SoftOwnerRefs(obj metav1.Object) ([]SoftOwnerRef, error) { | ||
| // Check if this Secret has a soft-owner kind label set | ||
| ownerKind, exists := obj.GetLabels()[SoftOwnerKindLabel] | ||
| if !exists { | ||
| // Not a soft-owned secret | ||
| return nil, nil | ||
| } | ||
|
|
||
| // Check for multi-policy ownership (annotation-based) | ||
| if ownerRefsBytes, exists := obj.GetAnnotations()[SoftOwnerRefsAnnotation]; exists { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Its a bit awkward that some of the multi-ref functionality is here and some is in ownership.go? Maybe move all the code here and make it generic? // In pkg/controller/common/reconciler/secret.go
// SetMultipleSoftOwners adds multiple soft owner references to an object
func SetMultipleSoftOwners(obj metav1.Object, ownerKind string, owners []types.NamespacedName) error
// AddSoftOwner adds a soft owner to an object (handles single->multi transition)
func AddSoftOwner(obj metav1.Object, owner SoftOwnerRef) error
// RemoveSoftOwner removes a soft owner from an object
func RemoveSoftOwner(obj metav1.Object, owner types.NamespacedName) (remainingCount int, err error)
// IsSoftOwnedBy checks if an object is soft-owned by the given owner
func IsSoftOwnedBy(obj metav1.Object, ownerKind string, owner types.NamespacedName) (bool, error)
// SoftOwnerRefs already exists and reads them
func SoftOwnerRefs(obj metav1.Object) ([]SoftOwnerRef, error)and then call it from the stack config controller, maybe with small wrappers to make the transformation from SCP to func setMultipleSoftOwners(secret *corev1.Secret, policies []policyv1alpha1.StackConfigPolicy) error {
owners := make([]types.NamespacedName, len(policies))
for i, p := range policies {
owners[i] = k8s.ExtractNamespacedName(&p)
}
return reconciler.SetMultipleSoftOwners(secret, policyv1alpha1.Kind, owners)
} |
||
| // Multi-policy soft owned secret - parse the JSON map of owners | ||
| var ownerRefs map[string]struct{} | ||
| if err := json.Unmarshal([]byte(ownerRefsBytes), &ownerRefs); err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| // Convert the map keys (namespaced name strings) back to NamespacedName objects | ||
| var ownerRefsNsn []SoftOwnerRef | ||
| for nsnStr := range ownerRefs { | ||
| // Split the string format "namespace/name" into components | ||
| nsnComponents := strings.Split(nsnStr, string(types.Separator)) | ||
| if len(nsnComponents) != 2 { | ||
| // Skip malformed entries | ||
| continue | ||
| } | ||
| ownerRefsNsn = append(ownerRefsNsn, SoftOwnerRef{Namespace: nsnComponents[0], Name: nsnComponents[1], Kind: ownerKind}) | ||
| } | ||
|
|
||
| return ownerRefsNsn, nil | ||
| } | ||
|
|
||
| // Fall back to single-policy ownership (label-based) | ||
| currentOwner, referenced := SoftOwnerRefFromLabels(obj.GetLabels()) | ||
| if !referenced { | ||
| // No soft owner found in labels | ||
| return nil, nil | ||
| } | ||
|
|
||
| // Return the single owner as a slice with one element | ||
| return []SoftOwnerRef{currentOwner}, nil | ||
| } | ||
|
|
||
| // ReconcileSecretNoOwnerRef should be called to reconcile a Secret for which we explicitly don't want | ||
| // an owner reference to be set, and want existing ownerReferences from previous operator versions to be removed, | ||
| // because of this k8s bug: https://github.com/kubernetes/kubernetes/issues/65200 (fixed in k8s 1.20). | ||
|
|
@@ -200,43 +246,54 @@ func GarbageCollectAllSoftOwnedOrphanSecrets(ctx context.Context, c k8s.Client, | |
| var secrets corev1.SecretList | ||
| if err := c.List(ctx, | ||
| &secrets, | ||
| client.HasLabels{SoftOwnerNamespaceLabel, SoftOwnerNameLabel, SoftOwnerKindLabel}, | ||
| client.HasLabels{SoftOwnerKindLabel}, | ||
| ); err != nil { | ||
| return err | ||
| } | ||
| // remove any secret whose owner doesn't exist | ||
| for i := range secrets.Items { | ||
| secret := secrets.Items[i] | ||
| softOwner, referenced := SoftOwnerRefFromLabels(secret.Labels) | ||
| if !referenced { | ||
| continue | ||
| } | ||
| if restrictedToOwnerNamespace(softOwner.Kind) && softOwner.Namespace != secret.Namespace { | ||
| // Secret references an owner in a different namespace: this likely results | ||
| // from a "manual" copy of the secret in another namespace, not handled by the operator. | ||
| // We don't want to touch that secret. | ||
| continue | ||
| softOwners, err := SoftOwnerRefs(&secret) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| owner, managed := ownerKinds[softOwner.Kind] | ||
| if !managed { | ||
| if len(softOwners) == 0 { | ||
| continue | ||
| } | ||
| owner = k8s.DeepCopyObject(owner) | ||
| err := c.Get(ctx, types.NamespacedName{Namespace: softOwner.Namespace, Name: softOwner.Name}, owner) | ||
| if err != nil { | ||
| if apierrors.IsNotFound(err) { | ||
| // owner doesn't exit anymore | ||
| ulog.FromContext(ctx).Info("Deleting secret as part of garbage collection", | ||
| "namespace", secret.Namespace, "secret_name", secret.Name, | ||
| "owner_kind", softOwner.Kind, "owner_namespace", softOwner.Namespace, "owner_name", softOwner.Name, | ||
| ) | ||
| options := client.DeleteOptions{Preconditions: &metav1.Preconditions{UID: &secret.UID}} | ||
| if err := c.Delete(ctx, &secret, &options); err != nil && !apierrors.IsNotFound(err) { | ||
| return err | ||
| } | ||
|
|
||
| missingOwners := make(map[types.NamespacedName]client.Object) | ||
| for _, softOwner := range softOwners { | ||
| if restrictedToOwnerNamespace(softOwner.Kind) && softOwner.Namespace != secret.Namespace { | ||
| // Secret references an owner in a different namespace: this likely results | ||
| // from a "manual" copy of the secret in another namespace, not handled by the operator. | ||
| // We don't want to touch that secret. | ||
| continue | ||
| } | ||
| return err | ||
| owner, managed := ownerKinds[softOwner.Kind] | ||
| if !managed { | ||
| continue | ||
| } | ||
| owner = k8s.DeepCopyObject(owner) | ||
| err := c.Get(ctx, types.NamespacedName{Namespace: softOwner.Namespace, Name: softOwner.Name}, owner) | ||
| if err != nil { | ||
| if apierrors.IsNotFound(err) { | ||
| // owner doesn't exit anymore | ||
| ulog.FromContext(ctx).Info("Deleting secret as part of garbage collection", | ||
| "namespace", secret.Namespace, "secret_name", secret.Name, | ||
| "owner_kind", softOwner.Kind, "owner_namespace", softOwner.Namespace, "owner_name", softOwner.Name, | ||
| ) | ||
| missingOwners[types.NamespacedName{Namespace: softOwner.Namespace, Name: softOwner.Name}] = owner | ||
| continue | ||
| } | ||
| return err | ||
| } | ||
| } | ||
|
|
||
| if len(missingOwners) == len(softOwners) { | ||
| options := client.DeleteOptions{Preconditions: &metav1.Preconditions{UID: &secret.UID}} | ||
| if err := c.Delete(ctx, &secret, &options); err != nil && !apierrors.IsNotFound(err) { | ||
| return err | ||
| } | ||
| } | ||
| // owner still exists, keep the secret | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.