2022-06-15 19:33:29 +00:00
|
|
|
// Copyright 2022 Woodpecker Authors
|
|
|
|
//
|
|
|
|
// 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 pipeline
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2022-08-15 12:37:46 +00:00
|
|
|
"errors"
|
2022-06-15 19:33:29 +00:00
|
|
|
"fmt"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
|
2022-08-15 12:37:46 +00:00
|
|
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml"
|
2022-06-15 19:33:29 +00:00
|
|
|
"github.com/woodpecker-ci/woodpecker/server"
|
2022-11-06 11:44:04 +00:00
|
|
|
forge_types "github.com/woodpecker-ci/woodpecker/server/forge/types"
|
2022-06-15 19:33:29 +00:00
|
|
|
"github.com/woodpecker-ci/woodpecker/server/model"
|
|
|
|
"github.com/woodpecker-ci/woodpecker/server/store"
|
|
|
|
)
|
|
|
|
|
2022-10-18 01:24:12 +00:00
|
|
|
// Restart a pipeline by creating a new one out of the old and start it
|
2023-08-21 11:22:33 +00:00
|
|
|
func Restart(ctx context.Context, store store.Store, lastPipeline *model.Pipeline, user *model.User, repo *model.Repo, envs map[string]string, netrc *model.Netrc) (*model.Pipeline, error) {
|
2023-05-14 12:18:43 +00:00
|
|
|
switch lastPipeline.Status {
|
2022-06-15 19:33:29 +00:00
|
|
|
case model.StatusDeclined,
|
|
|
|
model.StatusBlocked:
|
2023-05-14 12:18:43 +00:00
|
|
|
return nil, &ErrBadRequest{Msg: fmt.Sprintf("cannot restart a pipeline with status %s", lastPipeline.Status)}
|
2022-06-15 19:33:29 +00:00
|
|
|
}
|
|
|
|
|
2022-11-06 11:44:04 +00:00
|
|
|
var pipelineFiles []*forge_types.FileMeta
|
2022-06-15 19:33:29 +00:00
|
|
|
|
2023-10-08 12:05:06 +00:00
|
|
|
// fetch the old pipeline config from the database
|
2023-05-14 12:18:43 +00:00
|
|
|
configs, err := store.ConfigsForPipeline(lastPipeline.ID)
|
2022-06-15 19:33:29 +00:00
|
|
|
if err != nil {
|
2022-10-18 01:24:12 +00:00
|
|
|
msg := fmt.Sprintf("failure to get pipeline config for %s. %s", repo.FullName, err)
|
2022-06-15 19:33:29 +00:00
|
|
|
log.Error().Msgf(msg)
|
2023-02-01 23:08:02 +00:00
|
|
|
return nil, &ErrNotFound{Msg: msg}
|
2022-06-15 19:33:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, y := range configs {
|
2022-11-06 11:44:04 +00:00
|
|
|
pipelineFiles = append(pipelineFiles, &forge_types.FileMeta{Data: y.Data, Name: y.Name})
|
2022-06-15 19:33:29 +00:00
|
|
|
}
|
|
|
|
|
2023-10-07 14:41:25 +00:00
|
|
|
// If the config extension is active we should refetch the config in case something changed
|
|
|
|
if server.Config.Services.ConfigService != nil {
|
2022-11-06 11:44:04 +00:00
|
|
|
currentFileMeta := make([]*forge_types.FileMeta, len(configs))
|
2022-06-15 19:33:29 +00:00
|
|
|
for i, cfg := range configs {
|
2022-11-06 11:44:04 +00:00
|
|
|
currentFileMeta[i] = &forge_types.FileMeta{Name: cfg.Name, Data: cfg.Data}
|
2022-06-15 19:33:29 +00:00
|
|
|
}
|
|
|
|
|
2023-08-21 11:22:33 +00:00
|
|
|
newConfig, useOld, err := server.Config.Services.ConfigService.FetchConfig(ctx, repo, lastPipeline, currentFileMeta, netrc)
|
2022-06-15 19:33:29 +00:00
|
|
|
if err != nil {
|
2023-02-01 23:08:02 +00:00
|
|
|
return nil, &ErrBadRequest{
|
2022-10-18 01:24:12 +00:00
|
|
|
Msg: fmt.Sprintf("On fetching external pipeline config: %s", err),
|
2022-06-15 19:33:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if !useOld {
|
|
|
|
pipelineFiles = newConfig
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-14 12:18:43 +00:00
|
|
|
newPipeline := createNewOutOfOld(lastPipeline)
|
|
|
|
newPipeline.Parent = lastPipeline.ID
|
2022-06-15 19:33:29 +00:00
|
|
|
|
2023-05-14 12:18:43 +00:00
|
|
|
err = store.CreatePipeline(newPipeline)
|
2022-06-15 19:33:29 +00:00
|
|
|
if err != nil {
|
2022-10-18 01:24:12 +00:00
|
|
|
msg := fmt.Sprintf("failure to save pipeline for %s", repo.FullName)
|
2022-06-15 19:33:29 +00:00
|
|
|
log.Error().Err(err).Msg(msg)
|
|
|
|
return nil, fmt.Errorf(msg)
|
|
|
|
}
|
|
|
|
|
2022-08-15 12:37:46 +00:00
|
|
|
if len(configs) == 0 {
|
2023-05-14 12:18:43 +00:00
|
|
|
newPipeline, uerr := UpdateToStatusError(store, *newPipeline, errors.New("pipeline definition not found"))
|
2022-08-15 12:37:46 +00:00
|
|
|
if uerr != nil {
|
|
|
|
log.Debug().Err(uerr).Msg("failure to update pipeline status")
|
2023-05-14 12:18:43 +00:00
|
|
|
} else {
|
|
|
|
updatePipelineStatus(ctx, newPipeline, repo, user)
|
2022-08-15 12:37:46 +00:00
|
|
|
}
|
2023-05-14 12:18:43 +00:00
|
|
|
return newPipeline, nil
|
2022-08-15 12:37:46 +00:00
|
|
|
}
|
2023-10-08 12:05:06 +00:00
|
|
|
if err := linkPipelineConfigs(store, configs, newPipeline.ID); err != nil {
|
2022-10-18 01:24:12 +00:00
|
|
|
msg := fmt.Sprintf("failure to persist pipeline config for %s.", repo.FullName)
|
2022-06-15 19:33:29 +00:00
|
|
|
log.Error().Err(err).Msg(msg)
|
|
|
|
return nil, fmt.Errorf(msg)
|
|
|
|
}
|
|
|
|
|
2023-05-14 12:18:43 +00:00
|
|
|
newPipeline, pipelineItems, err := createPipelineItems(ctx, store, newPipeline, user, repo, pipelineFiles, envs)
|
2022-06-15 19:33:29 +00:00
|
|
|
if err != nil {
|
2022-08-15 12:37:46 +00:00
|
|
|
if errors.Is(err, &yaml.PipelineParseError{}) {
|
2023-05-14 12:18:43 +00:00
|
|
|
return newPipeline, nil
|
2022-08-15 12:37:46 +00:00
|
|
|
}
|
2022-06-15 19:33:29 +00:00
|
|
|
msg := fmt.Sprintf("failure to createBuildItems for %s", repo.FullName)
|
|
|
|
log.Error().Err(err).Msg(msg)
|
|
|
|
return nil, fmt.Errorf(msg)
|
|
|
|
}
|
|
|
|
|
2023-05-14 12:18:43 +00:00
|
|
|
newPipeline, err = start(ctx, store, newPipeline, user, repo, pipelineItems)
|
2022-06-15 19:33:29 +00:00
|
|
|
if err != nil {
|
2022-10-18 01:24:12 +00:00
|
|
|
msg := fmt.Sprintf("failure to start pipeline for %s", repo.FullName)
|
2022-06-15 19:33:29 +00:00
|
|
|
log.Error().Err(err).Msg(msg)
|
|
|
|
return nil, fmt.Errorf(msg)
|
|
|
|
}
|
|
|
|
|
2023-05-14 12:18:43 +00:00
|
|
|
return newPipeline, nil
|
2022-06-15 19:33:29 +00:00
|
|
|
}
|
|
|
|
|
2023-10-08 12:05:06 +00:00
|
|
|
func linkPipelineConfigs(store store.Store, configs []*model.Config, pipelineID int64) error {
|
2022-06-15 19:33:29 +00:00
|
|
|
for _, conf := range configs {
|
2022-10-18 01:24:12 +00:00
|
|
|
pipelineConfig := &model.PipelineConfig{
|
|
|
|
ConfigID: conf.ID,
|
|
|
|
PipelineID: pipelineID,
|
2022-06-15 19:33:29 +00:00
|
|
|
}
|
2022-10-18 01:24:12 +00:00
|
|
|
err := store.PipelineConfigCreate(pipelineConfig)
|
2022-06-15 19:33:29 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-10-18 01:24:12 +00:00
|
|
|
func createNewOutOfOld(old *model.Pipeline) *model.Pipeline {
|
2023-03-18 19:35:27 +00:00
|
|
|
newPipeline := *old
|
|
|
|
newPipeline.ID = 0
|
|
|
|
newPipeline.Number = 0
|
|
|
|
newPipeline.Status = model.StatusPending
|
|
|
|
newPipeline.Started = 0
|
|
|
|
newPipeline.Finished = 0
|
|
|
|
newPipeline.Enqueued = time.Now().UTC().Unix()
|
|
|
|
newPipeline.Error = ""
|
|
|
|
return &newPipeline
|
2022-06-15 19:33:29 +00:00
|
|
|
}
|