mirror of https://github.com/harness/drone.git
158 lines
4.3 KiB
Go
158 lines
4.3 KiB
Go
// Copyright 2023 Harness, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package canceler
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/harness/gitness/internal/pipeline/scheduler"
|
|
"github.com/harness/gitness/internal/sse"
|
|
"github.com/harness/gitness/internal/store"
|
|
"github.com/harness/gitness/types"
|
|
"github.com/harness/gitness/types/enum"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
type service struct {
|
|
executionStore store.ExecutionStore
|
|
sseStreamer sse.Streamer
|
|
repoStore store.RepoStore
|
|
scheduler scheduler.Scheduler
|
|
stageStore store.StageStore
|
|
stepStore store.StepStore
|
|
}
|
|
|
|
// Canceler cancels a build.
|
|
type Canceler interface {
|
|
// Cancel cancels the provided execution.
|
|
Cancel(ctx context.Context, repo *types.Repository, execution *types.Execution) error
|
|
}
|
|
|
|
// New returns a cancellation service that encapsulates
|
|
// all cancellation operations.
|
|
func New(
|
|
executionStore store.ExecutionStore,
|
|
sseStreamer sse.Streamer,
|
|
repoStore store.RepoStore,
|
|
scheduler scheduler.Scheduler,
|
|
stageStore store.StageStore,
|
|
stepStore store.StepStore,
|
|
) Canceler {
|
|
return &service{
|
|
executionStore: executionStore,
|
|
sseStreamer: sseStreamer,
|
|
repoStore: repoStore,
|
|
scheduler: scheduler,
|
|
stageStore: stageStore,
|
|
stepStore: stepStore,
|
|
}
|
|
}
|
|
|
|
//nolint:gocognit // refactor if needed.
|
|
func (s *service) Cancel(ctx context.Context, repo *types.Repository, execution *types.Execution) error {
|
|
log := log.With().
|
|
Int64("execution.id", execution.ID).
|
|
Str("execution.status", string(execution.Status)).
|
|
Str("execution.Ref", execution.Ref).
|
|
Logger()
|
|
|
|
// do not cancel the build if the build status is
|
|
// complete. only cancel the build if the status is
|
|
// running or pending.
|
|
if execution.Status != enum.CIStatusPending &&
|
|
execution.Status != enum.CIStatusRunning {
|
|
return nil
|
|
}
|
|
|
|
// update the build status to killed. if the update fails
|
|
// due to an optimistic lock error it means the build has
|
|
// already started, and should now be ignored.
|
|
now := time.Now().UnixMilli()
|
|
execution.Status = enum.CIStatusKilled
|
|
execution.Finished = now
|
|
if execution.Started == 0 {
|
|
execution.Started = now
|
|
}
|
|
|
|
err := s.executionStore.Update(ctx, execution)
|
|
if err != nil {
|
|
return fmt.Errorf("could not update execution status to canceled: %w", err)
|
|
}
|
|
|
|
stages, err := s.stageStore.ListWithSteps(ctx, execution.ID)
|
|
if err != nil {
|
|
return fmt.Errorf("could not list stages with steps: %w", err)
|
|
}
|
|
|
|
// update the status of all steps to indicate they
|
|
// were killed or skipped.
|
|
for _, stage := range stages {
|
|
if stage.Status.IsDone() {
|
|
continue
|
|
}
|
|
if stage.Started != 0 {
|
|
stage.Status = enum.CIStatusKilled
|
|
} else {
|
|
stage.Status = enum.CIStatusSkipped
|
|
stage.Started = now
|
|
}
|
|
stage.Stopped = now
|
|
err := s.stageStore.Update(ctx, stage)
|
|
if err != nil {
|
|
log.Debug().Err(err).
|
|
Int64("stage.number", stage.Number).
|
|
Msg("canceler: cannot update stage status")
|
|
}
|
|
|
|
// update the status of all steps to indicate they
|
|
// were killed or skipped.
|
|
for _, step := range stage.Steps {
|
|
if step.Status.IsDone() {
|
|
continue
|
|
}
|
|
if step.Started != 0 {
|
|
step.Status = enum.CIStatusKilled
|
|
} else {
|
|
step.Status = enum.CIStatusSkipped
|
|
step.Started = now
|
|
}
|
|
step.Stopped = now
|
|
step.ExitCode = 130
|
|
err := s.stepStore.Update(ctx, step)
|
|
if err != nil {
|
|
log.Debug().Err(err).
|
|
Int64("stage.number", stage.Number).
|
|
Int64("step.number", step.Number).
|
|
Msg("canceler: cannot update step status")
|
|
}
|
|
}
|
|
}
|
|
|
|
execution.Stages = stages
|
|
log.Info().Msg("canceler: successfully cancelled build")
|
|
|
|
// trigger a SSE to notify subscribers that
|
|
// the execution was cancelled.
|
|
err = s.sseStreamer.Publish(ctx, repo.ParentID, enum.SSETypeExecutionCanceled, execution)
|
|
if err != nil {
|
|
log.Debug().Err(err).Msg("canceler: failed to publish server-sent event")
|
|
}
|
|
|
|
return nil
|
|
}
|