diff --git a/pkg/app/piped/controller/scheduler.go b/pkg/app/piped/controller/scheduler.go index 6c5b888803..79ec256399 100644 --- a/pkg/app/piped/controller/scheduler.go +++ b/pkg/app/piped/controller/scheduler.go @@ -373,17 +373,40 @@ func (s *scheduler) Run(ctx context.Context) error { <-doneCh case cmd := <-s.cancelledCh: + // A nil command means the channel was closed after delivering + // a cancel command, so treat it as a cancellation as well. if cmd != nil { cancelCommand = cmd cancelCommander = cmd.Commander - handler.Cancel() - <-doneCh } + handler.Cancel() + <-doneCh case <-doneCh: break } + // The select may have picked doneCh while a cancel command was already + // waiting, so check it before deciding to move on. + if cancelCommand == nil { + select { + case cmd := <-s.cancelledCh: + if cmd != nil { + cancelCommand = cmd + cancelCommander = cmd.Commander + } + default: + } + } + + // A received cancel command wins even if the stage finished successfully, + // otherwise the loop would continue to the next stage after the user cancelled. + if cancelCommand != nil { + deploymentStatus = model.DeploymentStatus_DEPLOYMENT_CANCELLED + statusReason = fmt.Sprintf("Cancelled by %s while executing stage %s", cancelCommander, ps.Id) + break + } + // If all operations of the stage were completed successfully or skipped by a web user // handle the next stage. if result == model.StageStatus_STAGE_SUCCESS || result == model.StageStatus_STAGE_SKIPPED { diff --git a/pkg/app/pipedv1/controller/scheduler.go b/pkg/app/pipedv1/controller/scheduler.go index 72a0fec59d..8a5e97199e 100644 --- a/pkg/app/pipedv1/controller/scheduler.go +++ b/pkg/app/pipedv1/controller/scheduler.go @@ -370,12 +370,14 @@ func (s *scheduler) Run(ctx context.Context) error { <-doneCh case cmd := <-s.cancelledCh: + // A nil command means the channel was closed after delivering + // a cancel command, so treat it as a cancellation as well. if cmd != nil { cancelCommand = cmd cancelCommander = cmd.Commander - handler.Cancel() - <-doneCh } + handler.Cancel() + <-doneCh case <-timeout: handler.Timeout() @@ -385,6 +387,27 @@ func (s *scheduler) Run(ctx context.Context) error { break } + // The select may have picked doneCh while a cancel command was already + // waiting, so check it before deciding to move on. + if cancelCommand == nil { + select { + case cmd := <-s.cancelledCh: + if cmd != nil { + cancelCommand = cmd + cancelCommander = cmd.Commander + } + default: + } + } + + // A received cancel command wins even if the stage finished successfully, + // otherwise the loop would continue to the next stage after the user cancelled. + if cancelCommand != nil { + deploymentStatus = model.DeploymentStatus_DEPLOYMENT_CANCELLED + statusReason = fmt.Sprintf("Cancelled by %s while executing stage %s", cancelCommander, ps.Id) + break + } + // If all operations of the stage were completed successfully or skipped by a web user // handle the next stage. if result == model.StageStatus_STAGE_SUCCESS || result == model.StageStatus_STAGE_SKIPPED {