@@ -21,6 +21,7 @@ import (
21
21
"errors"
22
22
"fmt"
23
23
"github.com/devtron-labs/devtron/internal/sql/repository/pipelineConfig/adapter/cdWorkflow"
24
+ bean2 "github.com/devtron-labs/devtron/pkg/bean"
24
25
common2 "github.com/devtron-labs/devtron/pkg/deployment/common"
25
26
util2 "github.com/devtron-labs/devtron/pkg/pipeline/util"
26
27
"os"
@@ -64,7 +65,7 @@ type CdHandler interface {
64
65
FetchCdWorkflowDetails (appId int , environmentId int , pipelineId int , buildId int ) (types.WorkflowResponse , error )
65
66
DownloadCdWorkflowArtifacts (buildId int ) (* os.File , error )
66
67
FetchCdPrePostStageStatus (pipelineId int ) ([]pipelineBean.CdWorkflowWithArtifact , error )
67
- CancelStage (workflowRunnerId int , userId int32 ) (int , error )
68
+ CancelStage (workflowRunnerId int , forceAbort bool , userId int32 ) (int , error )
68
69
FetchAppWorkflowStatusForTriggerView (appId int ) ([]* pipelineConfig.CdWorkflowStatus , error )
69
70
FetchAppWorkflowStatusForTriggerViewForEnvironment (request resourceGroup2.ResourceGroupingRequest , token string ) ([]* pipelineConfig.CdWorkflowStatus , error )
70
71
FetchAppDeploymentStatusForEnvironments (request resourceGroup2.ResourceGroupingRequest , token string ) ([]* pipelineConfig.AppDeploymentStatus , error )
@@ -133,16 +134,12 @@ func NewCdHandlerImpl(Logger *zap.SugaredLogger, userService user.UserService,
133
134
return cdh
134
135
}
135
136
136
- func (impl * CdHandlerImpl ) CancelStage (workflowRunnerId int , userId int32 ) (int , error ) {
137
+ func (impl * CdHandlerImpl ) CancelStage (workflowRunnerId int , forceAbort bool , userId int32 ) (int , error ) {
137
138
workflowRunner , err := impl .cdWorkflowRepository .FindWorkflowRunnerById (workflowRunnerId )
138
139
if err != nil {
139
140
impl .Logger .Errorw ("err" , "err" , err )
140
141
return 0 , err
141
142
}
142
- if ! (string (v1alpha1 .NodePending ) == workflowRunner .Status || string (v1alpha1 .NodeRunning ) == workflowRunner .Status ) {
143
- impl .Logger .Info ("cannot cancel stage, stage not in progress" )
144
- return 0 , errors .New ("cannot cancel stage, stage not in progress" )
145
- }
146
143
pipeline , err := impl .pipelineRepository .FindById (workflowRunner .CdWorkflow .PipelineId )
147
144
if err != nil {
148
145
impl .Logger .Errorw ("error while fetching cd pipeline" , "err" , err )
@@ -184,10 +181,26 @@ func (impl *CdHandlerImpl) CancelStage(workflowRunnerId int, userId int32) (int,
184
181
Environment : nil ,
185
182
}
186
183
err = impl .workflowService .TerminateWorkflow (cancelWfDtoRequest )
187
- if err != nil {
184
+ if err != nil && forceAbort {
185
+ impl .Logger .Errorw ("error in terminating workflow, with force abort flag as true" , "workflowName" , workflowRunner .Name , "err" , err )
186
+ cancelWfDtoRequest .WorkflowGenerateName = fmt .Sprintf ("%d-%s" , workflowRunnerId , workflowRunner .Name )
187
+ err1 := impl .workflowService .TerminateDanglingWorkflows (cancelWfDtoRequest )
188
+ if err1 != nil {
189
+ impl .Logger .Errorw ("error in terminating dangling workflows" , "cancelWfDtoRequest" , cancelWfDtoRequest , "err" , err )
190
+ // ignoring error here in case of force abort, confirmed from product
191
+ }
192
+ } else if err != nil {
188
193
impl .Logger .Error ("cannot terminate wf runner" , "err" , err )
189
194
return 0 , err
190
195
}
196
+ if forceAbort {
197
+ err = impl .handleForceAbortCaseForCdStage (workflowRunner , forceAbort )
198
+ if err != nil {
199
+ impl .Logger .Errorw ("error in handleForceAbortCaseForCdStage" , "forceAbortFlag" , forceAbort , "workflowRunner" , workflowRunner , "err" , err )
200
+ return 0 , err
201
+ }
202
+ return workflowRunner .Id , nil
203
+ }
191
204
if len (workflowRunner .ImagePathReservationIds ) > 0 {
192
205
err := impl .customTagService .DeactivateImagePathReservationByImageIds (workflowRunner .ImagePathReservationIds )
193
206
if err != nil {
@@ -206,6 +219,34 @@ func (impl *CdHandlerImpl) CancelStage(workflowRunnerId int, userId int32) (int,
206
219
return workflowRunner .Id , nil
207
220
}
208
221
222
+ func (impl * CdHandlerImpl ) updateWorkflowRunnerForForceAbort (workflowRunner * pipelineConfig.CdWorkflowRunner ) error {
223
+ workflowRunner .Status = executors .WorkflowCancel
224
+ workflowRunner .PodStatus = string (bean2 .Failed )
225
+ workflowRunner .Message = FORCE_ABORT_MESSAGE_AFTER_STARTING_STAGE
226
+ err := impl .cdWorkflowRepository .UpdateWorkFlowRunner (workflowRunner )
227
+ if err != nil {
228
+ impl .Logger .Errorw ("error in updating workflow status in cd workflow runner in force abort case" , "err" , err )
229
+ return err
230
+ }
231
+ return nil
232
+ }
233
+
234
+ func (impl * CdHandlerImpl ) handleForceAbortCaseForCdStage (workflowRunner * pipelineConfig.CdWorkflowRunner , forceAbort bool ) error {
235
+ isWorkflowInNonTerminalStage := workflowRunner .Status == string (v1alpha1 .NodePending ) || workflowRunner .Status == string (v1alpha1 .NodeRunning )
236
+ if ! isWorkflowInNonTerminalStage {
237
+ if forceAbort {
238
+ return impl .updateWorkflowRunnerForForceAbort (workflowRunner )
239
+ } else {
240
+ return & util.ApiError {Code : "200" , HttpStatusCode : 400 , UserMessage : "cannot cancel stage, stage not in progress" }
241
+ }
242
+ }
243
+ //this arises when someone deletes the workflow in resource browser and wants to force abort a cd stage(pre/post)
244
+ if workflowRunner .Status == string (v1alpha1 .NodeRunning ) && forceAbort {
245
+ return impl .updateWorkflowRunnerForForceAbort (workflowRunner )
246
+ }
247
+ return nil
248
+ }
249
+
209
250
func (impl * CdHandlerImpl ) UpdateWorkflow (workflowStatus v1alpha1.WorkflowStatus ) (int , string , error ) {
210
251
wfStatusRs := impl .extractWorkfowStatus (workflowStatus )
211
252
workflowName , status , podStatus , message , podName := wfStatusRs .WorkflowName , wfStatusRs .Status , wfStatusRs .PodStatus , wfStatusRs .Message , wfStatusRs .PodName
0 commit comments