Skip to content

Commit 5c20f35

Browse files
fix: switch ci fixes (#5358)
* fix: switch ci fixes * chore: review refactoring * chore: refactoring * fix: add missing validations in switching to external ci * fix: ci pipeline id is not being updated in cd pipeline if switching from external ci to other ci types * fix: update ci pipeline id in all the cd workflows that exists in the given workflow * fix: checkIfNsExistsForEnvIds , empty envIds check fix
1 parent 45fe19f commit 5c20f35

File tree

5 files changed

+111
-37
lines changed

5 files changed

+111
-37
lines changed

internal/sql/repository/appWorkflow/AppWorkflowRepository.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ type AppWorkflowRepository interface {
4444
FindWFAllMappingByWorkflowId(workflowId int) ([]*AppWorkflowMapping, error)
4545
FindWFCIMappingByCIPipelineId(ciPipelineId int) ([]*AppWorkflowMapping, error)
4646
FindWFCDMappingByCIPipelineId(ciPipelineId int) ([]*AppWorkflowMapping, error)
47+
FindWFCDMappingsByWorkflowId(appWorkflowId int) ([]*AppWorkflowMapping, error)
4748
FindWFCDMappingByCDPipelineId(cdPipelineId int) (*AppWorkflowMapping, error)
4849
GetParentDetailsByPipelineId(pipelineId int) (*AppWorkflowMapping, error)
4950
DeleteAppWorkflowMapping(appWorkflow *AppWorkflowMapping, tx *pg.Tx) error
@@ -273,6 +274,17 @@ func (impl AppWorkflowRepositoryImpl) FindWFCIMappingByCIPipelineId(ciPipelineId
273274
return appWorkflowsMapping, err
274275
}
275276

277+
func (impl AppWorkflowRepositoryImpl) FindWFCDMappingsByWorkflowId(appWorkflowId int) ([]*AppWorkflowMapping, error) {
278+
var appWorkflowsMapping []*AppWorkflowMapping
279+
280+
err := impl.dbConnection.Model(&appWorkflowsMapping).
281+
Where("app_workflow_id = ?", appWorkflowId).
282+
Where("type = ?", CDPIPELINE).
283+
Where("active = ?", true).
284+
Select()
285+
return appWorkflowsMapping, err
286+
}
287+
276288
func (impl AppWorkflowRepositoryImpl) FindWFCDMappingByCIPipelineId(ciPipelineId int) ([]*AppWorkflowMapping, error) {
277289
var appWorkflowsMapping []*AppWorkflowMapping
278290

internal/sql/repository/pipelineConfig/PipelineRepository.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ type PipelineRepository interface {
122122
FindAppAndEnvironmentAndProjectByPipelineIds(pipelineIds []int) (pipelines []*Pipeline, err error)
123123
FilterDeploymentDeleteRequestedPipelineIds(cdPipelineIds []int) (map[int]bool, error)
124124
FindDeploymentTypeByPipelineIds(cdPipelineIds []int) (map[int]DeploymentObject, error)
125+
UpdateCiPipelineId(tx *pg.Tx, pipelineIds []int, ciPipelineId int) error
125126
UpdateOldCiPipelineIdToNewCiPipelineId(tx *pg.Tx, oldCiPipelineId, newCiPipelineId int) error
126127
// FindWithEnvironmentByCiIds Possibility of duplicate environment names when filtered by unique pipeline ids
127128
FindWithEnvironmentByCiIds(ctx context.Context, cIPipelineIds []int) ([]*Pipeline, error)
@@ -770,6 +771,17 @@ func (impl PipelineRepositoryImpl) UpdateOldCiPipelineIdToNewCiPipelineId(tx *pg
770771
Where("deleted = ?", false).Update()
771772
return err
772773
}
774+
775+
func (impl PipelineRepositoryImpl) UpdateCiPipelineId(tx *pg.Tx, pipelineIds []int, ciPipelineId int) error {
776+
if len(pipelineIds) == 0 {
777+
return nil
778+
}
779+
_, err := tx.Model((*Pipeline)(nil)).Set("ci_pipeline_id = ?", ciPipelineId).
780+
Where("id IN (?) ", pg.In(pipelineIds)).
781+
Where("deleted = ?", false).Update()
782+
return err
783+
}
784+
773785
func (impl PipelineRepositoryImpl) FindWithEnvironmentByCiIds(ctx context.Context, cIPipelineIds []int) ([]*Pipeline, error) {
774786
_, span := otel.Tracer("orchestrator").Start(ctx, "FindWithEnvironmentByCiIds")
775787
defer span.End()

pkg/bean/app.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,10 @@ type CiPipeline struct {
141141
EnableCustomTag bool `json:"enableCustomTag"`
142142
}
143143

144+
func (ciPipeline *CiPipeline) IsLinkedCi() bool {
145+
return ciPipeline.IsExternal
146+
}
147+
144148
type DockerConfigOverride struct {
145149
DockerRegistry string `json:"dockerRegistry,omitempty"`
146150
DockerRepository string `json:"dockerRepository,omitempty"`

pkg/pipeline/BuildPipelineSwitchService.go

Lines changed: 63 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package pipeline
1919
import (
2020
"github.com/devtron-labs/devtron/internal/sql/repository/appWorkflow"
2121
"github.com/devtron-labs/devtron/internal/sql/repository/pipelineConfig"
22+
"github.com/devtron-labs/devtron/internal/util"
2223
"github.com/devtron-labs/devtron/pkg/bean"
2324
"github.com/devtron-labs/devtron/pkg/pipeline/adapter"
2425
pipelineConfigBean "github.com/devtron-labs/devtron/pkg/pipeline/bean/CiPipeline"
@@ -28,6 +29,7 @@ import (
2829
"github.com/go-pg/pg"
2930
"github.com/juju/errors"
3031
"go.uber.org/zap"
32+
"net/http"
3133
"time"
3234
)
3335

@@ -81,7 +83,12 @@ func NewBuildPipelineSwitchServiceImpl(logger *zap.SugaredLogger,
8183

8284
func (impl *BuildPipelineSwitchServiceImpl) SwitchToExternalCi(tx *pg.Tx, appWorkflowMapping *appWorkflow.AppWorkflowMapping, switchFromCiPipelineId int, userId int32) error {
8385

84-
err := impl.deleteCiAndItsWorkflowMappings(tx, switchFromCiPipelineId, userId)
86+
err := impl.validateSwitchPreConditions(switchFromCiPipelineId)
87+
if err != nil {
88+
return err
89+
}
90+
91+
err = impl.deleteCiAndItsWorkflowMappings(tx, switchFromCiPipelineId, userId)
8592
if err != nil {
8693
impl.logger.Errorw("error in deleting old ci-pipeline and getting the appWorkflow mapping of that", "err", err, "userId", userId)
8794
return err
@@ -97,7 +104,7 @@ func (impl *BuildPipelineSwitchServiceImpl) SwitchToExternalCi(tx *pg.Tx, appWor
97104
return err
98105
}
99106

100-
//setting new ci_pipeline_id to 0 because we dont store ci_pipeline_id if the ci_pipeline is external/webhook type.
107+
// setting new ci_pipeline_id to 0 because we dont store ci_pipeline_id if the ci_pipeline is external/webhook type.
101108
err = impl.pipelineRepository.UpdateOldCiPipelineIdToNewCiPipelineId(tx, switchFromCiPipelineId, 0)
102109
if err != nil {
103110
impl.logger.Errorw("error in updating pipelines ci_pipeline_ids with new ci_pipelineId", "oldCiPipelineId", switchFromCiPipelineId)
@@ -130,11 +137,18 @@ func (impl *BuildPipelineSwitchServiceImpl) SwitchToCiPipelineExceptExternal(req
130137
return nil, err
131138
}
132139

133-
//delete old pipeline and it's appworkflow mapping
140+
// delete old pipeline and it's appworkflow mapping
134141
return impl.createNewPipelineAndReplaceOldPipelineLinks(request.CiPipeline, ciConfig, switchFromPipelineId, switchFromType, request.UserId)
135142
}
136143

137144
func (impl *BuildPipelineSwitchServiceImpl) createNewPipelineAndReplaceOldPipelineLinks(ciPipelineReq *bean.CiPipeline, ciConfig *bean.CiConfigRequest, switchFromPipelineId int, switchFromType pipelineConfigBean.PipelineType, userId int32) (*bean.CiConfigRequest, error) {
145+
146+
isSelfLinkedCiPipeline := switchFromType != pipelineConfigBean.EXTERNAL && ciPipelineReq.IsLinkedCi() && ciPipelineReq.ParentCiPipeline == switchFromPipelineId
147+
if isSelfLinkedCiPipeline {
148+
errMsg := "cannot create linked ci pipeline from the same source"
149+
return nil, util.NewApiError().WithInternalMessage(errMsg).WithUserMessage(errMsg).WithHttpStatusCode(http.StatusBadRequest)
150+
}
151+
138152
tx, err := impl.ciPipelineRepository.StartTx()
139153
if err != nil {
140154
impl.logger.Errorw("error in starting transaction", "switchFromPipelineId", switchFromPipelineId, "switchFromType", switchFromType, "userId", userId, "err", err)
@@ -161,12 +175,21 @@ func (impl *BuildPipelineSwitchServiceImpl) createNewPipelineAndReplaceOldPipeli
161175
return nil, err
162176
}
163177

164-
//we don't store ci-pipeline-id in pipeline table for external ci's
165-
if switchFromPipelineId > 0 && switchFromType != pipelineConfigBean.EXTERNAL {
166-
// ciPipeline id is being set in res object in the addpipelineToTemplate method.
167-
err = impl.pipelineRepository.UpdateOldCiPipelineIdToNewCiPipelineId(tx, switchFromPipelineId, res.CiPipelines[0].Id)
178+
if switchFromPipelineId > 0 {
179+
// get all the cd workflow mappings whose parent component is our old pipeline
180+
cdwfmappings, err := impl.appWorkflowRepository.FindWFCDMappingsByWorkflowId(oldAppWorkflowMapping.AppWorkflowId)
181+
if err != nil {
182+
impl.logger.Errorw("error in finding parent cd workflowMappings using parent component details", "parentComponentType", oldAppWorkflowMapping.Type, "parentComponentId", oldAppWorkflowMapping.ComponentId, "err", err)
183+
return nil, err
184+
}
185+
pipelineIds := make([]int, 0, len(cdwfmappings))
186+
for _, cdwfMapping := range cdwfmappings {
187+
pipelineIds = append(pipelineIds, cdwfMapping.ComponentId)
188+
}
189+
190+
err = impl.pipelineRepository.UpdateCiPipelineId(tx, pipelineIds, res.CiPipelines[0].Id)
168191
if err != nil {
169-
impl.logger.Errorw("error in updating pipelines ci_pipeline_ids with new ci_pipelineId", "oldCiPipelineId", switchFromPipelineId, "newCiPipelineId", res.CiPipelines[0].Id)
192+
impl.logger.Errorw("error in updating pipelines ci_pipeline_ids with new ci_pipelineId", "oldCiPipelineId", switchFromPipelineId, "newCiPipelineId", res.CiPipelines[0].Id, "err", err)
170193
return nil, err
171194
}
172195
}
@@ -200,30 +223,10 @@ func (impl *BuildPipelineSwitchServiceImpl) validateCiPipelineSwitch(switchFromC
200223
// we should not check the below logic for external_ci type as builds are not built in devtron and
201224
// linked pipelines won't be there as per current external-ci-pipeline architecture
202225
if switchFromCiPipelineId > 0 && switchFromType != pipelineConfigBean.EXTERNAL {
203-
// old ci_pipeline should not contain any linked ci_pipelines.
204-
linkedCiPipelines, err := impl.ciPipelineRepository.FindLinkedCiCount(switchFromCiPipelineId)
226+
err := impl.validateSwitchPreConditions(switchFromCiPipelineId)
205227
if err != nil {
206-
return nil
207-
}
208-
if linkedCiPipelines > 0 {
209-
return errors.New(string(cannotConvertIfLinkedCiFound))
210-
}
211-
212-
// note: ideally we should have found any builds running on old ci_pipeline, if yes block this conversion with proper message.
213-
// but checking only latest wf for now.
214-
ciWorkflow, err := impl.ciWorkflowRepository.FindLastTriggeredWorkflow(switchFromCiPipelineId)
215-
// no build is triggered case
216-
if err == pg.ErrNoRows {
217-
return nil
218-
}
219-
if err != nil {
220-
impl.logger.Errorw("error in finding latest ciwokflow by ciPipelineId", "ciPipelineId", switchFromCiPipelineId)
221228
return err
222229
}
223-
224-
if ciWorkflow.InProgress() {
225-
return errors.New(string(cannotConvertIfLatestWorkflowIsInNonTerminalState))
226-
}
227230
}
228231

229232
return nil
@@ -348,3 +351,34 @@ func (impl *BuildPipelineSwitchServiceImpl) saveHistoryOfOverriddenTemplate(ciPi
348351
func (impl *BuildPipelineSwitchServiceImpl) updateLinkedAppWorkflowMappings(tx *pg.Tx, oldAppWorkflowMapping *appWorkflow.AppWorkflowMapping, newAppWorkflowMapping *appWorkflow.AppWorkflowMapping) error {
349352
return impl.appWorkflowRepository.UpdateParentComponentDetails(tx, oldAppWorkflowMapping.ComponentId, oldAppWorkflowMapping.Type, newAppWorkflowMapping.ComponentId, newAppWorkflowMapping.Type, nil)
350353
}
354+
355+
func (impl *BuildPipelineSwitchServiceImpl) validateSwitchPreConditions(switchFromCiPipelineId int) error {
356+
357+
// old ci_pipeline should not contain any linked ci_pipelines.
358+
linkedCiPipelines, err := impl.ciPipelineRepository.FindLinkedCiCount(switchFromCiPipelineId)
359+
if err != nil {
360+
impl.logger.Errorw("error in finding the linkedCi count for the pipeline", "ciPipelineId", switchFromCiPipelineId, "err", err)
361+
return err
362+
}
363+
if linkedCiPipelines > 0 {
364+
return errors.New(string(cannotConvertIfLinkedCiFound))
365+
}
366+
367+
// note: ideally we should have found any builds running on old ci_pipeline, if yes block this conversion with proper message.
368+
// but checking only latest wf for now.
369+
ciWorkflow, err := impl.ciWorkflowRepository.FindLastTriggeredWorkflow(switchFromCiPipelineId)
370+
// no build is triggered case
371+
if err == pg.ErrNoRows {
372+
return nil
373+
}
374+
if err != nil {
375+
impl.logger.Errorw("error in finding latest ciwokflow by ciPipelineId", "ciPipelineId", switchFromCiPipelineId)
376+
return err
377+
}
378+
379+
if ciWorkflow.InProgress() {
380+
return errors.New(string(cannotConvertIfLatestWorkflowIsInNonTerminalState))
381+
}
382+
383+
return nil
384+
}

pkg/pipeline/DeploymentPipelineConfigService.go

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -385,18 +385,24 @@ func (impl *CdPipelineConfigServiceImpl) CreateCdPipelines(pipelineCreateRequest
385385
}
386386
pipeline.DeploymentAppType = overrideDeploymentType
387387
}
388+
388389
err = impl.checkIfNsExistsForEnvIds(envIds)
389390
if err != nil {
391+
impl.logger.Errorw("error in checking existence of namespace for env's", "envIds", envIds, "err", err)
390392
return nil, err
391393
}
394+
392395
isGitOpsRequiredForCD := impl.IsGitOpsRequiredForCD(pipelineCreateRequest)
393396
app, err := impl.appRepo.FindById(pipelineCreateRequest.AppId)
397+
394398
if err != nil {
395399
impl.logger.Errorw("app not found", "err", err, "appId", pipelineCreateRequest.AppId)
396400
return nil, err
397401
}
402+
398403
_, err = impl.validateCDPipelineRequest(pipelineCreateRequest)
399404
if err != nil {
405+
impl.logger.Errorw("error in validating cd pipeline create request", "pipelineCreateRequest", pipelineCreateRequest, "err", err)
400406
return nil, err
401407
}
402408

@@ -1765,18 +1771,20 @@ func (impl *CdPipelineConfigServiceImpl) createCdPipeline(ctx context.Context, a
17651771
}
17661772

17671773
}
1768-
}
1769-
// save custom tag data
1770-
err = impl.CDPipelineCustomTagDBOperations(pipeline)
1771-
if err != nil {
1772-
return pipelineId, err
1773-
}
17741774

1775-
if pipeline.IsDigestEnforcedForPipeline {
1776-
_, err = impl.imageDigestPolicyService.CreatePolicyForPipeline(tx, pipelineId, pipeline.Name, userId)
1775+
// save custom tag data
1776+
err = impl.CDPipelineCustomTagDBOperations(pipeline)
17771777
if err != nil {
17781778
return pipelineId, err
17791779
}
1780+
1781+
if pipeline.IsDigestEnforcedForPipeline {
1782+
_, err = impl.imageDigestPolicyService.CreatePolicyForPipeline(tx, pipelineId, pipeline.Name, userId)
1783+
if err != nil {
1784+
return pipelineId, err
1785+
}
1786+
}
1787+
17801788
}
17811789

17821790
err = tx.Commit()
@@ -2072,6 +2080,10 @@ func (impl *CdPipelineConfigServiceImpl) BulkDeleteCdPipelines(impactedPipelines
20722080

20732081
}
20742082
func (impl *CdPipelineConfigServiceImpl) checkIfNsExistsForEnvIds(envIds []*int) error {
2083+
2084+
if len(envIds) == 0 {
2085+
return nil
2086+
}
20752087
//fetching environments for the given environment Ids
20762088
environmentList, err := impl.environmentRepository.FindByIds(envIds)
20772089
if err != nil {

0 commit comments

Comments
 (0)