Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 44 additions & 4 deletions controllers/jenkins/pipelinerun/pipelinerun_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"fmt"
"net/http"
"reflect"
"strconv"
"strings"
"time"

Expand All @@ -43,6 +44,7 @@ import (
cmstore "github.com/kubesphere/ks-devops/pkg/store/configmap"
storeInter "github.com/kubesphere/ks-devops/pkg/store/store"
"github.com/kubesphere/ks-devops/pkg/utils/k8sutil"
"github.com/kubesphere/ks-devops/pkg/utils/sliceutil"
)

// BuildNotExistMsg indicates the build with pipelinerun-id not exist in jenkins
Expand Down Expand Up @@ -86,16 +88,54 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu

// DeletionTimestamp.IsZero() means copyPipeline has not been deleted.
if !pipelineRunCopied.ObjectMeta.DeletionTimestamp.IsZero() {
if err = jHandler.deleteJenkinsJobHistory(pipelineRunCopied); err != nil {
// if the annotation value is true, we should keep the record in Jenkins
if keep, err := strconv.ParseBool(pipelineRunCopied.Annotations[v1alpha3.PipelineRunKeepJenkinsRecordAnnoKey]); err == nil && keep {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pipelineRunCopied.Annotations may be nil map.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not perform write operations on the map here. Reading a nil map will return its default empty value, and no panic will occur.

klog.V(4).Infof("try to delete PipelineRun: %s/%s, but need to keep Jenkins record",
pipelineRunCopied.Namespace, pipelineRunCopied.Name)
} else if err = jHandler.deleteJenkinsJobHistory(pipelineRunCopied); err != nil {
klog.V(4).Infof("failed to delete Jenkins job history from PipelineRun: %s/%s, error: %v",
pipelineRunCopied.Namespace, pipelineRunCopied.Name, err)
} else {
k8sutil.RemoveFinalizer(&pipelineRunCopied.ObjectMeta, v1alpha3.PipelineRunFinalizerName)
err = r.Update(context.TODO(), pipelineRunCopied)
}

err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
// Get the latest version of PipelineRun
if err := r.Get(ctx, req.NamespacedName, pipelineRunCopied); err != nil {
// ignore not found error
return client.IgnoreNotFound(err)
}

// Remove the finalizer
pipelineRunCopied.ObjectMeta.Finalizers = sliceutil.RemoveString(pipelineRunCopied.ObjectMeta.Finalizers, func(item string) bool {
return item == v1alpha3.PipelineRunFinalizerName
})

// Try to update
return r.Update(ctx, pipelineRunCopied)
})

return ctrl.Result{}, err
}

// add finalizer if it does not exist
if !sliceutil.HasString(pipelineRunCopied.ObjectMeta.Finalizers, v1alpha3.PipelineRunFinalizerName) {
err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
// Get the latest version of PipelineRun
if err := r.Get(ctx, req.NamespacedName, pipelineRunCopied); err != nil {
return err
}
// Add the finalizer if it's still not there
if !sliceutil.HasString(pipelineRunCopied.ObjectMeta.Finalizers, v1alpha3.PipelineRunFinalizerName) {
pipelineRunCopied.ObjectMeta.Finalizers = append(pipelineRunCopied.ObjectMeta.Finalizers, v1alpha3.PipelineRunFinalizerName)
// Try to update
return r.Update(ctx, pipelineRunCopied)
}
return nil // Finalizer was added by another process, no need to update
})
if err != nil {
return ctrl.Result{}, err
}
}

// the PipelineRun cannot allow building
if !pipelineRunCopied.Buildable() {
return ctrl.Result{}, nil
Expand Down
9 changes: 7 additions & 2 deletions pkg/api/devops/v1alpha3/pipelinerun_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,13 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// PipelineRunFinalizerName is the name of PipelineRun finalizer
const PipelineRunFinalizerName = "pipelinerun.finalizers.kubesphere.io"
const (
// PipelineRunFinalizerName is the name of PipelineRun finalizer
PipelineRunFinalizerName = "pipelinerun.finalizers.kubesphere.io"

// PipelineRunKeepJenkinsRecordAnnoKey is the annotation key of keeping Jenkins record
PipelineRunKeepJenkinsRecordAnnoKey = "devops.kubesphere.io/keep-jenkins-record"
)

// PipelineRunSpec defines the desired state of PipelineRun
type PipelineRunSpec struct {
Expand Down
46 changes: 46 additions & 0 deletions pkg/kapis/devops/v1alpha3/pipelinerun/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strconv"

Expand Down Expand Up @@ -286,3 +287,48 @@ func (h *apiHandler) downloadArtifact(request *restful.Request, response *restfu
return
}
}

// deletePipelineRun deletes a specific PipelineRun of a Pipeline.
func (h *apiHandler) deletePipelineRun(request *restful.Request, response *restful.Response) {
nsName := request.PathParameter("namespace")
runName := request.PathParameter("pipelinerun")
keepJenkinsRecord := request.QueryParameter("keepJenkinsRecord")

ctx := request.Request.Context()
// get the PipelineRun
var pr v1alpha3.PipelineRun
if err := h.client.Get(ctx, client.ObjectKey{Namespace: nsName, Name: runName}, &pr); err != nil {
kapis.HandleError(request, response, err)
return
}

// check if the PipelineRun is in a final state
switch pr.Status.Phase {
case v1alpha3.Succeeded, v1alpha3.Failed, v1alpha3.Cancelled:
// allow to delete
default:
// if the PipelineRun is still running, we should not delete it
err := fmt.Errorf("cannot delete a running PipelineRun, please stop it first, current status is %s", pr.Status.Phase)
kapis.HandleBadRequest(response, request, err)
return
}

// check if we need to keep the record in Jenkins
if keep, err := strconv.ParseBool(keepJenkinsRecord); err == nil && keep {
if pr.Annotations == nil {
pr.Annotations = make(map[string]string)
}
pr.Annotations[v1alpha3.PipelineRunKeepJenkinsRecordAnnoKey] = "true"
if err := h.client.Update(ctx, &pr); err != nil {
kapis.HandleError(request, response, err)
return
}
}

// delete the CR
if err := h.client.Delete(ctx, &pr); err != nil {
kapis.HandleError(request, response, err)
return
}
response.WriteHeader(http.StatusOK)
}
9 changes: 9 additions & 0 deletions pkg/kapis/devops/v1alpha3/pipelinerun/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,13 @@ func RegisterRoutes(ws *restful.WebService, devopsClient dclient.Interface, c cl
To(handler.downloadArtifact).
Returns(http.StatusOK, api.StatusOK, nil).
Metadata(restfulspec.KeyOpenAPITags, constants.DevOpsPipelineTags))

// delete a pipelinerun of a pipeline
ws.Route(ws.DELETE("/namespaces/{namespace}/pipelineruns/{pipelinerun}").
To(handler.deletePipelineRun).
Doc("Delete a pipelinerun of a pipeline.").
Param(ws.PathParameter("namespace", "The namespace of the pipeline.")).
Param(ws.PathParameter("pipelinerun", "The name of the pipelinerun.")).
Param(ws.QueryParameter("keepJenkinsRecord", "Whether to keep the record in Jenkins. Default is false.")).
Returns(http.StatusOK, "OK", nil))
}