Skip to content

Commit 21da420

Browse files
Merge pull request #6773 from devtron-labs/linked-ci-pipeline-status-fix
chore: Linked ci pipeline status fix
2 parents f5745c0 + 6ce4281 commit 21da420

File tree

3 files changed

+125
-16
lines changed

3 files changed

+125
-16
lines changed

pkg/pipeline/CiHandler.go

Lines changed: 102 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
eventProcessorBean "github.com/devtron-labs/devtron/pkg/eventProcessor/bean"
3030
"github.com/devtron-labs/devtron/pkg/pipeline/adapter"
3131
"github.com/devtron-labs/devtron/pkg/pipeline/constants"
32+
util2 "github.com/devtron-labs/devtron/pkg/pipeline/util"
3233
"github.com/devtron-labs/devtron/pkg/pipeline/workflowStatus"
3334
"github.com/devtron-labs/devtron/pkg/workflow/workflowStatusLatest"
3435
"regexp"
@@ -660,9 +661,10 @@ func (impl *CiHandlerImpl) FetchCiStatusForTriggerViewV1(appId int) ([]*pipeline
660661
return []*pipelineConfig.CiWorkflowStatus{}, nil
661662
}
662663

663-
latestStatusEntries, err := impl.workflowStatusLatestService.GetCiWorkflowStatusLatestByPipelineIds(allPipelineIds)
664+
// Prepare pipeline status lookup data (handles linked CI pipelines)
665+
pipelines, pipelineIdForStatus, statusLookupPipelineIds, latestStatusEntries, err := impl.preparePipelineStatusLookup(allPipelineIds)
664666
if err != nil {
665-
impl.Logger.Errorw("error in checking latest status table, falling back to old method", "appId", appId, "err", err)
667+
impl.Logger.Errorw("error in preparing pipeline status lookup, falling back to old method", "appId", appId, "err", err)
666668
return impl.ciWorkflowRepository.FIndCiWorkflowStatusesByAppId(appId)
667669
}
668670

@@ -674,19 +676,21 @@ func (impl *CiHandlerImpl) FetchCiStatusForTriggerViewV1(appId int) ([]*pipeline
674676
impl.Logger.Errorw("error in fetching ci workflow status from latest ci workflow entries ", "latestStatusEntries", latestStatusEntries, "err", err)
675677
return nil, err
676678
} else {
677-
allStatuses = append(allStatuses, statusesFromLatestTable...)
679+
mappedStatuses := impl.mapStatusesToLinkedPipelines(statusesFromLatestTable, pipelines, pipelineIdForStatus)
680+
allStatuses = append(allStatuses, mappedStatuses...)
678681
}
679682
}
680683

681-
pipelinesNotInLatestTable := impl.getPipelineIdsNotInLatestTable(allPipelineIds, latestStatusEntries)
684+
pipelinesNotInLatestTable := impl.getPipelineIdsNotInLatestTable(statusLookupPipelineIds, latestStatusEntries)
682685

683686
if len(pipelinesNotInLatestTable) > 0 {
684687
statusesFromOldQuery, err := impl.fetchCiStatusUsingFallbackMethod(pipelinesNotInLatestTable)
685688
if err != nil {
686689
impl.Logger.Errorw("error in fetching using fallback method by pipelineIds", "pipelineIds", pipelinesNotInLatestTable, "err", err)
687690
return nil, err
688691
} else {
689-
allStatuses = append(allStatuses, statusesFromOldQuery...)
692+
mappedStatuses := impl.mapStatusesToLinkedPipelines(statusesFromOldQuery, pipelines, pipelineIdForStatus)
693+
allStatuses = append(allStatuses, mappedStatuses...)
690694
}
691695
}
692696

@@ -781,6 +785,38 @@ func (impl *CiHandlerImpl) fetchLastTriggeredWorkflowsHybrid(pipelineIds []int)
781785
return allWorkflows, nil
782786
}
783787

788+
// preparePipelineStatusLookup prepares pipeline mapping for linked CI pipelines and returns status lookup data
789+
func (impl *CiHandlerImpl) preparePipelineStatusLookup(pipelineIds []int) (pipelines []*pipelineConfig.CiPipeline, pipelineIdForStatus map[int]int, statusLookupPipelineIds []int, latestStatusEntries []*pipelineConfig.CiWorkflowStatusLatest, err error) {
790+
pipelines, err = impl.ciPipelineRepository.FindByIdsIn(pipelineIds)
791+
if err != nil {
792+
impl.Logger.Errorw("error in getting ci pipelines by ids", "pipelineIds", pipelineIds, "err", err)
793+
return nil, nil, nil, nil, err
794+
}
795+
796+
pipelineIdForStatus = make(map[int]int, len(pipelines)) // linkedPipelineId -> parentPipelineId (or self if not linked)
797+
statusLookupPipelineIds = make([]int, 0, len(pipelines))
798+
799+
for _, pipeline := range pipelines {
800+
if pipeline.ParentCiPipeline > 0 {
801+
// linked CI pipeline - use parent pipeline ID for status lookup
802+
pipelineIdForStatus[pipeline.Id] = pipeline.ParentCiPipeline
803+
statusLookupPipelineIds = append(statusLookupPipelineIds, pipeline.ParentCiPipeline)
804+
} else {
805+
// regular CI pipeline - use its own ID
806+
pipelineIdForStatus[pipeline.Id] = pipeline.Id
807+
statusLookupPipelineIds = append(statusLookupPipelineIds, pipeline.Id)
808+
}
809+
}
810+
statusLookupPipelineIds = util2.RemoveDuplicateInts(statusLookupPipelineIds)
811+
latestStatusEntries, err = impl.workflowStatusLatestService.GetCiWorkflowStatusLatestByPipelineIds(statusLookupPipelineIds)
812+
if err != nil {
813+
impl.Logger.Errorw("error in checking latest status table", "statusLookupPipelineIds", statusLookupPipelineIds, "err", err)
814+
return nil, nil, nil, nil, err
815+
}
816+
817+
return pipelines, pipelineIdForStatus, statusLookupPipelineIds, latestStatusEntries, nil
818+
}
819+
784820
// getPipelineIdsNotInLatestTable finds pipeline IDs that are NOT in the latest status table
785821
func (impl *CiHandlerImpl) getPipelineIdsNotInLatestTable(allPipelineIds []int, latestStatusEntries []*pipelineConfig.CiWorkflowStatusLatest) []int {
786822
var pipelinesInLatestTable []int
@@ -801,6 +837,34 @@ func (impl *CiHandlerImpl) getPipelineIdsNotInLatestTable(allPipelineIds []int,
801837
return missingPipelineIds
802838
}
803839

840+
// mapStatusesToLinkedPipelines maps parent pipeline statuses back to linked pipelines
841+
func (impl *CiHandlerImpl) mapStatusesToLinkedPipelines(
842+
statuses []*pipelineConfig.CiWorkflowStatus,
843+
pipelines []*pipelineConfig.CiPipeline,
844+
pipelineIdForStatus map[int]int,
845+
) []*pipelineConfig.CiWorkflowStatus {
846+
statusMap := make(map[int]*pipelineConfig.CiWorkflowStatus)
847+
for _, status := range statuses {
848+
statusMap[status.CiPipelineId] = status
849+
}
850+
851+
var result []*pipelineConfig.CiWorkflowStatus
852+
for _, pipeline := range pipelines {
853+
parentPipelineId := pipelineIdForStatus[pipeline.Id]
854+
if parentStatus, exists := statusMap[parentPipelineId]; exists {
855+
linkedStatus := &pipelineConfig.CiWorkflowStatus{
856+
CiPipelineId: pipeline.Id,
857+
CiPipelineName: pipeline.Name,
858+
CiStatus: parentStatus.CiStatus,
859+
StorageConfigured: parentStatus.StorageConfigured,
860+
CiWorkflowId: parentStatus.CiWorkflowId,
861+
}
862+
result = append(result, linkedStatus)
863+
}
864+
}
865+
return result
866+
}
867+
804868
func (impl *CiHandlerImpl) FetchCiStatusForTriggerView(appId int) ([]*pipelineConfig.CiWorkflowStatus, error) {
805869
var ciWorkflowStatuses []*pipelineConfig.CiWorkflowStatus
806870

@@ -997,6 +1061,10 @@ func (impl *CiHandlerImpl) FetchCiStatusForTriggerViewForEnvironment(request res
9971061
appObjectArr = append(appObjectArr, object)
9981062
}
9991063
appResults, _ := request.CheckAuthBatch(token, appObjectArr, []string{})
1064+
1065+
linkedPipelineDetails := make(map[int]*pipelineConfig.CiPipeline) // linkedPipelineId -> pipeline object
1066+
parentToLinkedMap := make(map[int][]int) // parentPipelineId -> []linkedPipelineId
1067+
10001068
for _, ciPipeline := range ciPipelines {
10011069
appObject := objects[ciPipeline.Id] // here only app permission have to check
10021070
if !appResults[appObject] {
@@ -1005,7 +1073,15 @@ func (impl *CiHandlerImpl) FetchCiStatusForTriggerViewForEnvironment(request res
10051073
}
10061074
ciPipelineId := impl.getPipelineIdForTriggerView(ciPipeline)
10071075
ciPipelineIds = append(ciPipelineIds, ciPipelineId)
1076+
1077+
// Store mapping for linked CI pipelines
1078+
if ciPipeline.ParentCiPipeline > 0 {
1079+
linkedPipelineDetails[ciPipeline.Id] = ciPipeline
1080+
// Add to slice of linked pipelines for this parent
1081+
parentToLinkedMap[ciPipelineId] = append(parentToLinkedMap[ciPipelineId], ciPipeline.Id)
1082+
}
10081083
}
1084+
10091085
if len(ciPipelineIds) == 0 {
10101086
return ciWorkflowStatuses, nil
10111087
}
@@ -1015,24 +1091,34 @@ func (impl *CiHandlerImpl) FetchCiStatusForTriggerViewForEnvironment(request res
10151091
return ciWorkflowStatuses, err
10161092
}
10171093

1018-
notTriggeredWorkflows := make(map[int]bool)
1094+
// create workflow map for quick lookup
1095+
workflowMap := make(map[int]*pipelineConfig.CiWorkflow)
1096+
for _, workflow := range latestCiWorkflows {
1097+
workflowMap[workflow.CiPipelineId] = workflow
1098+
}
1099+
1100+
triggeredWorkflows := make(map[int]bool)
10191101

10201102
for _, ciWorkflow := range latestCiWorkflows {
1021-
ciWorkflowStatus := &pipelineConfig.CiWorkflowStatus{}
1022-
ciWorkflowStatus.CiPipelineId = ciWorkflow.CiPipelineId
1023-
ciWorkflowStatus.CiPipelineName = ciWorkflow.CiPipeline.Name
1024-
ciWorkflowStatus.CiStatus = ciWorkflow.Status
1025-
ciWorkflowStatus.StorageConfigured = ciWorkflow.BlobStorageEnabled
1026-
ciWorkflowStatus.CiWorkflowId = ciWorkflow.Id
1027-
ciWorkflowStatuses = append(ciWorkflowStatuses, ciWorkflowStatus)
1028-
notTriggeredWorkflows[ciWorkflowStatus.CiPipelineId] = true
1103+
// check if this workflow belongs to a parent pipeline that has linked CIs
1104+
if linkedPipelineIds, isParentOfLinked := parentToLinkedMap[ciWorkflow.CiPipelineId]; isParentOfLinked {
1105+
// create workflow status for each linked pipeline
1106+
for _, linkedPipelineId := range linkedPipelineIds {
1107+
ciWorkflowStatus := adapter.GetCiWorkflowStatusForLinkedCiPipeline(linkedPipelineId, linkedPipelineDetails[linkedPipelineId].Name, ciWorkflow)
1108+
ciWorkflowStatuses = append(ciWorkflowStatuses, ciWorkflowStatus)
1109+
}
1110+
} else {
1111+
ciWorkflowStatus := adapter.GetCiWorkflowStatusFromCiWorkflow(ciWorkflow)
1112+
ciWorkflowStatuses = append(ciWorkflowStatuses, ciWorkflowStatus)
1113+
}
1114+
triggeredWorkflows[ciWorkflow.CiPipelineId] = true
10291115
}
10301116

10311117
for _, ciPipelineId := range ciPipelineIds {
1032-
if _, ok := notTriggeredWorkflows[ciPipelineId]; !ok {
1118+
if _, ok := triggeredWorkflows[ciPipelineId]; !ok {
10331119
ciWorkflowStatus := &pipelineConfig.CiWorkflowStatus{}
10341120
ciWorkflowStatus.CiPipelineId = ciPipelineId
1035-
ciWorkflowStatus.CiStatus = "Not Triggered"
1121+
ciWorkflowStatus.CiStatus = pipelineConfigBean.NotTriggered
10361122
ciWorkflowStatuses = append(ciWorkflowStatuses, ciWorkflowStatus)
10371123
}
10381124
}

pkg/pipeline/adapter/adapter.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,3 +416,13 @@ func GetCiWorkflowStatusFromCiWorkflow(ciWorkflow *pipelineConfig.CiWorkflow) *p
416416
CiWorkflowId: ciWorkflow.Id,
417417
}
418418
}
419+
420+
func GetCiWorkflowStatusForLinkedCiPipeline(linkedCiPipelineId int, linkedCiPipelineName string, ciWorkflow *pipelineConfig.CiWorkflow) *pipelineConfig.CiWorkflowStatus {
421+
return &pipelineConfig.CiWorkflowStatus{
422+
CiPipelineId: linkedCiPipelineId,
423+
CiPipelineName: linkedCiPipelineName,
424+
CiStatus: ciWorkflow.Status,
425+
StorageConfigured: ciWorkflow.BlobStorageEnabled,
426+
CiWorkflowId: ciWorkflow.Id,
427+
}
428+
}

pkg/pipeline/util/CiCdUtil.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,16 @@ func GetWorkflowCacheConfigWithBackwardCompatibility(WorkflowCacheConfig common.
4141
}
4242
}
4343
}
44+
45+
// RemoveDuplicateInts helper function to remove duplicate integers from slice
46+
func RemoveDuplicateInts(slice []int) []int {
47+
keys := make(map[int]bool)
48+
var result []int
49+
for _, item := range slice {
50+
if !keys[item] {
51+
keys[item] = true
52+
result = append(result, item)
53+
}
54+
}
55+
return result
56+
}

0 commit comments

Comments
 (0)