From 6db8d88d793f49638affa12cffec0d5f4654a47b Mon Sep 17 00:00:00 2001 From: Priti Desai Date: Sat, 6 May 2023 11:01:10 -0700 Subject: [PATCH] delete persistent volume claim PVCs can be specified using volumeClaimTemplates as part of a pipelineRun. The pipelineRun controller creates a new PVC using the specifications from the template. Once the pipelineRun is complete, the PVC exist to faciliate any analysis on the completed pod. Such PVCs are helpful but requires an extra cleanup if not needed. In the cases where pipelineRuns are kept for a long period of time, such PVC can waste resources. This commit introduces an option for the user such that the users can configure pipelineRun controller to delete such PVCs. There is no API change needed to support this cleanup. PipelineRun/TaskRun controller now reads a label from the volumeClaimTemplate to decide whether to delete PVC or not. Set the label " pvc-protection-finalizer" to "remove" to take advantage of this kind of cleanup. kind: PipelineRun metadata: generateName: pipeline-run- spec: workspaces: - name: source volumeClaimTemplate: metadata: labels: pvc-protection-finalizer: remove Closes #5776 Signed-off-by: pritidesai --- pkg/reconciler/pipelinerun/pipelinerun.go | 4 ++ pkg/reconciler/taskrun/taskrun.go | 4 ++ pkg/reconciler/volumeclaim/pvchandler.go | 45 +++++++++++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/pkg/reconciler/pipelinerun/pipelinerun.go b/pkg/reconciler/pipelinerun/pipelinerun.go index e2d103fe392..84a628a47de 100644 --- a/pkg/reconciler/pipelinerun/pipelinerun.go +++ b/pkg/reconciler/pipelinerun/pipelinerun.go @@ -224,6 +224,10 @@ func (c *Reconciler) ReconcileKind(ctx context.Context, pr *v1beta1.PipelineRun) if pr.IsDone() { pr.SetDefaults(ctx) + + if err := c.pvcHandler.PurgeProtectionFromPersistentVolumeClaimsForWorkspaces(ctx, pr.Spec.Workspaces, *kmeta.NewControllerRef(pr), pr.Namespace); err != nil { + logger.Errorf("Failed to update PersistentVolumeClaim for PipelineRun %s: %v", pr.Name, err) + } err := c.cleanupAffinityAssistants(ctx, pr) if err != nil { logger.Errorf("Failed to delete StatefulSet for PipelineRun %s: %v", pr.Name, err) diff --git a/pkg/reconciler/taskrun/taskrun.go b/pkg/reconciler/taskrun/taskrun.go index bbcf5436553..54ff2ac002a 100644 --- a/pkg/reconciler/taskrun/taskrun.go +++ b/pkg/reconciler/taskrun/taskrun.go @@ -147,6 +147,10 @@ func (c *Reconciler) ReconcileKind(ctx context.Context, tr *v1beta1.TaskRun) pkg return err } + if err := c.pvcHandler.PurgeProtectionFromPersistentVolumeClaimsForWorkspaces(ctx, tr.Spec.Workspaces, *kmeta.NewControllerRef(tr), tr.Namespace); err != nil { + return fmt.Errorf("failed to update PersistentVolumeClaim for TaskRun %s: %v", tr.Name, err) + } + return c.finishReconcileUpdateEmitEvents(ctx, tr, before, nil) } diff --git a/pkg/reconciler/volumeclaim/pvchandler.go b/pkg/reconciler/volumeclaim/pvchandler.go index 384bcb516d1..222955ebbfe 100644 --- a/pkg/reconciler/volumeclaim/pvchandler.go +++ b/pkg/reconciler/volumeclaim/pvchandler.go @@ -19,13 +19,16 @@ package volumeclaim import ( "context" "crypto/sha256" + "encoding/json" "fmt" "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" "go.uber.org/zap" + "gomodules.xyz/jsonpatch/v2" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" errorutils "k8s.io/apimachinery/pkg/util/errors" clientset "k8s.io/client-go/kubernetes" ) @@ -34,11 +37,13 @@ const ( // ReasonCouldntCreateWorkspacePVC indicates that a Pipeline expects a workspace from a // volumeClaimTemplate but couldn't create a claim. ReasonCouldntCreateWorkspacePVC = "CouldntCreateWorkspacePVC" + LabelPVCProtectionFinalizer = "pvc-protection-finalizer" ) // PvcHandler is used to create PVCs for workspaces type PvcHandler interface { CreatePersistentVolumeClaimsForWorkspaces(ctx context.Context, wb []v1beta1.WorkspaceBinding, ownerReference metav1.OwnerReference, namespace string) error + PurgeProtectionFromPersistentVolumeClaimsForWorkspaces(ctx context.Context, wb []v1beta1.WorkspaceBinding, ownerReference metav1.OwnerReference, namespace string) error } type defaultPVCHandler struct { @@ -81,6 +86,46 @@ func (c *defaultPVCHandler) CreatePersistentVolumeClaimsForWorkspaces(ctx contex return errorutils.NewAggregate(errs) } +func (c *defaultPVCHandler) PurgeProtectionFromPersistentVolumeClaimsForWorkspaces(ctx context.Context, wb []v1beta1.WorkspaceBinding, ownerReference metav1.OwnerReference, namespace string) error { + var errs []error + for _, claim := range getPersistentVolumeClaims(wb, ownerReference, namespace) { + p, err := c.clientset.CoreV1().PersistentVolumeClaims(claim.Namespace).Get(ctx, claim.Name, metav1.GetOptions{}) + if err != nil { + errs = append(errs, fmt.Errorf("failed to get the PVC %s: %w", claim.Name, err)) + } + //the label to remove PVC protection is not set to true + if err == nil && p != nil && p.Labels[LabelPVCProtectionFinalizer] == "remove" { + err = c.clientset.CoreV1().PersistentVolumeClaims(claim.Namespace).Delete(ctx, claim.Name, metav1.DeleteOptions{}) + if err != nil { + errs = append(errs, fmt.Errorf("failed to delete the PVC %s: %w", claim.Name, err)) + } + + // get the list of existing finalizers and drop `pvc-protection` if exists + var finalizers []string + for _, f := range p.ObjectMeta.Finalizers { + if f == "kubernetes.io/pvc-protection" { + continue + } + finalizers = append(finalizers, f) + } + + // prepare data to remove pvc-protection from the list of finalizers + removeFinalizerBytes, err := json.Marshal([]jsonpatch.JsonPatchOperation{{ + Path: "/metadata/finalizers", + Operation: "replace", + Value: finalizers, + }}) + + // patch the existing PVC to update the finalizers + _, err = c.clientset.CoreV1().PersistentVolumeClaims(claim.Namespace).Patch(ctx, claim.Name, types.JSONPatchType, removeFinalizerBytes, metav1.PatchOptions{}) + if err != nil { + errs = append(errs, fmt.Errorf("failed to patch the PVC %s: %w", claim.Name, err)) + } + } + } + return errorutils.NewAggregate(errs) +} + func getPersistentVolumeClaims(workspaceBindings []v1beta1.WorkspaceBinding, ownerReference metav1.OwnerReference, namespace string) map[string]*corev1.PersistentVolumeClaim { claims := make(map[string]*corev1.PersistentVolumeClaim) for _, workspaceBinding := range workspaceBindings {