package main import ( "context" "encoding/json" "slices" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/types" ) const recordKey = "delivery.quorate.dev/record" func (c *controller) releaseRecords(ctx context.Context, delivery *projectDelivery) error { known := make(map[string]bool) for _, attempt := range delivery.Status.Points { known[attempt.WorkflowName] = true } if status := delivery.Status.Continuous; status != nil && status.Attempt != nil { known[status.Attempt.WorkflowName] = true if status.Attempt.History != nil { for _, name := range status.Attempt.History.Workflows { known[name] = true } } } if status := delivery.Status.Qualification; status != nil { for _, attempt := range status.Attempts { if attempt.Phase != "Pending" && attempt.Phase != "Reserved" { known[attempt.WorkflowName] = true } } } items, err := c.client.Resource(workflowGVR).Namespace(delivery.Metadata.Namespace).List(ctx, metav1.ListOptions{LabelSelector: recordKey + "=pending"}) if err != nil { return err } for _, item := range items.Items { if known[item.GetName()] && ownedBy(&item, delivery.Metadata.UID) { if err := c.clearRecord(ctx, &item); err != nil { return err } } } return nil } func (c *controller) releaseOrphans(ctx context.Context) error { items, err := c.client.Resource(workflowGVR).Namespace(metav1.NamespaceAll).List(ctx, metav1.ListOptions{LabelSelector: recordKey + "=pending"}) if err != nil { return err } for _, item := range items.Items { for _, owner := range item.GetOwnerReferences() { if owner.Kind != "ProjectDelivery" || owner.APIVersion != "delivery.quorate.dev/v1alpha1" { continue } parent, err := c.client.Resource(projectDeliveryGVR).Namespace(item.GetNamespace()).Get(ctx, owner.Name, metav1.GetOptions{}) if apierrors.IsNotFound(err) || err == nil && (parent.GetUID() != owner.UID || parent.GetDeletionTimestamp() != nil) { if err := c.clearRecord(ctx, &item); err != nil { return err } } else if err != nil { return err } } } return nil } func ownedBy(workflow *unstructured.Unstructured, uid string) bool { return slices.ContainsFunc(workflow.GetOwnerReferences(), func(owner metav1.OwnerReference) bool { return owner.Kind == "ProjectDelivery" && owner.APIVersion == "delivery.quorate.dev/v1alpha1" && string(owner.UID) == uid }) } func (c *controller) clearRecord(ctx context.Context, workflow *unstructured.Unstructured) error { finalizers := slices.DeleteFunc(slices.Clone(workflow.GetFinalizers()), func(name string) bool { return name == recordKey }) if finalizers == nil { finalizers = []string{} } patch, err := json.Marshal(map[string]any{"metadata": map[string]any{ "resourceVersion": workflow.GetResourceVersion(), "finalizers": finalizers, "labels": map[string]any{recordKey: nil}, }}) if err != nil { return err } _, err = c.client.Resource(workflowGVR).Namespace(workflow.GetNamespace()).Patch(ctx, workflow.GetName(), types.MergePatchType, patch, metav1.PatchOptions{}) if apierrors.IsNotFound(err) { return nil } return err }