Skip to content

Commit aed2f68

Browse files
committed
refactoring around handling image scan after ci complete, pre-cd and post cd completion
1 parent c3424d1 commit aed2f68

File tree

5 files changed

+135
-16
lines changed

5 files changed

+135
-16
lines changed

internal/sql/repository/CiArtifactRepository.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ type CiArtifactRepository interface {
139139
// MigrateToWebHookDataSourceType is used for backward compatibility. It'll migrate the deprecated DataSource type
140140
MigrateToWebHookDataSourceType(id int) error
141141
UpdateLatestTimestamp(artifactIds []int) error
142+
143+
Update(ciArtifact *CiArtifact) (*CiArtifact, error)
142144
}
143145

144146
type CiArtifactRepositoryImpl struct {
@@ -858,3 +860,12 @@ func (impl CiArtifactRepositoryImpl) FindCiArtifactByImagePaths(images []string)
858860
}
859861
return ciArtifacts, nil
860862
}
863+
864+
func (impl CiArtifactRepositoryImpl) Update(ciArtifact *CiArtifact) (*CiArtifact, error) {
865+
err := impl.dbConnection.Update(ciArtifact)
866+
if err != nil {
867+
impl.logger.Errorw("error in updating ciArtifact", "ciArtifact", ciArtifact, "err", err)
868+
return nil, err
869+
}
870+
return ciArtifact, nil
871+
}

pkg/pipeline/repository/PipelineStageRepository.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,22 @@ const (
5353
PIPELINE_STAGE_STEP_VARIABLE_FORMAT_TYPE_DATE PipelineStageStepVariableFormatType = "DATE"
5454
)
5555

56+
func (r PipelineStageType) ToString() string {
57+
return string(r)
58+
}
59+
func (r PipelineStageType) IsStageTypePreCi() bool {
60+
return r == PIPELINE_STAGE_TYPE_PRE_CI
61+
}
62+
func (r PipelineStageType) IsStageTypePreCd() bool {
63+
return r == PIPELINE_STAGE_TYPE_PRE_CD
64+
}
65+
func (r PipelineStageType) IsStageTypePostCi() bool {
66+
return r == PIPELINE_STAGE_TYPE_POST_CI
67+
}
68+
func (r PipelineStageType) IsStageTypePostCd() bool {
69+
return r == PIPELINE_STAGE_TYPE_POST_CD
70+
}
71+
5672
type PipelineStage struct {
5773
tableName struct{} `sql:"pipeline_stage" pg:",discard_unknown_columns"`
5874
Id int `sql:"id,pk"`
@@ -184,6 +200,7 @@ type PipelineStageRepository interface {
184200
MarkStepsDeletedByStageId(stageId int) error
185201
MarkStepsDeletedExcludingActiveStepsInUpdateReq(activeStepIdsPresentInReq []int, stageId int) error
186202
GetActiveStepsByRefPluginId(refPluginId int) ([]*PipelineStageStep, error)
203+
CheckIfPluginExistsInPipelineStage(pipelineId int, stageType PipelineStageType, pluginId int) (bool, error)
187204

188205
CreatePipelineScript(pipelineScript *PluginPipelineScript, tx *pg.Tx) (*PluginPipelineScript, error)
189206
UpdatePipelineScript(pipelineScript *PluginPipelineScript) (*PluginPipelineScript, error)
@@ -873,3 +890,26 @@ func (impl *PipelineStageRepositoryImpl) MarkConditionsDeletedExcludingActiveVar
873890
}
874891
return nil
875892
}
893+
894+
func (impl *PipelineStageRepositoryImpl) CheckIfPluginExistsInPipelineStage(pipelineId int, stageType PipelineStageType, pluginId int) (bool, error) {
895+
var step PipelineStageStep
896+
query := impl.dbConnection.Model(&step).
897+
Column("pipeline_stage_step.*").
898+
Join("INNER JOIN pipeline_stage ps on ps.id = plugin_stage_step.pipeline_stage_id").
899+
Where("plugin_stage_step.ref_plugin_id = ?", pluginId).
900+
Where("ps.type = ?", stageType).
901+
Where("plugin_stage_step.deleted=?", false).
902+
Where("ps.deleted= ?", false)
903+
904+
if stageType.IsStageTypePostCi() || stageType.IsStageTypePostCi() {
905+
query.Where("ps.ci_pipeline_id= ?", pipelineId)
906+
} else if stageType.IsStageTypePostCd() || stageType.IsStageTypePostCd() {
907+
query.Where("ps.cd_pipeline_id= ?", pipelineId)
908+
}
909+
err := query.Select()
910+
if err != nil {
911+
impl.logger.Errorw("error in getting plugin stage step by pipelineId, stageType nad plugin id", "pipelineId", pipelineId, "stageType", stageType.ToString(), "pluginId", pluginId, "err", err)
912+
return false, err
913+
}
914+
return step.Id != 0, nil
915+
}

pkg/workflow/dag/WorkflowDagExecutor.go

Lines changed: 78 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
"github.com/devtron-labs/devtron/internal/sql/repository/pipelineConfig/adapter/cdWorkflow"
3434
"github.com/devtron-labs/devtron/internal/sql/repository/pipelineConfig/bean/workflow"
3535
cdWorkflow2 "github.com/devtron-labs/devtron/internal/sql/repository/pipelineConfig/bean/workflow/cdWorkflow"
36+
"github.com/devtron-labs/devtron/internal/sql/repository/security"
3637
"github.com/devtron-labs/devtron/pkg/app/status"
3738
"github.com/devtron-labs/devtron/pkg/build/artifacts"
3839
bean5 "github.com/devtron-labs/devtron/pkg/build/pipeline/bean"
@@ -44,6 +45,7 @@ import (
4445
"github.com/devtron-labs/devtron/pkg/deployment/trigger/devtronApps/userDeploymentRequest/service"
4546
eventProcessorBean "github.com/devtron-labs/devtron/pkg/eventProcessor/bean"
4647
"github.com/devtron-labs/devtron/pkg/pipeline"
48+
constants2 "github.com/devtron-labs/devtron/pkg/pipeline/constants"
4749
"github.com/devtron-labs/devtron/pkg/pipeline/executors"
4850
repository2 "github.com/devtron-labs/devtron/pkg/plugin/repository"
4951
"github.com/devtron-labs/devtron/pkg/sql"
@@ -125,6 +127,7 @@ type WorkflowDagExecutorImpl struct {
125127
commonArtifactService artifacts.CommonArtifactService
126128
deploymentConfigService common2.DeploymentConfigService
127129
asyncRunnable *async.Runnable
130+
scanHistoryRepository security.ImageScanHistoryRepository
128131
}
129132

130133
func NewWorkflowDagExecutorImpl(Logger *zap.SugaredLogger, pipelineRepository pipelineConfig.PipelineRepository,
@@ -148,7 +151,8 @@ func NewWorkflowDagExecutorImpl(Logger *zap.SugaredLogger, pipelineRepository pi
148151
manifestCreationService manifest.ManifestCreationService,
149152
commonArtifactService artifacts.CommonArtifactService,
150153
deploymentConfigService common2.DeploymentConfigService,
151-
asyncRunnable *async.Runnable) *WorkflowDagExecutorImpl {
154+
asyncRunnable *async.Runnable,
155+
scanHistoryRepository security.ImageScanHistoryRepository) *WorkflowDagExecutorImpl {
152156
wde := &WorkflowDagExecutorImpl{logger: Logger,
153157
pipelineRepository: pipelineRepository,
154158
cdWorkflowRepository: cdWorkflowRepository,
@@ -172,6 +176,7 @@ func NewWorkflowDagExecutorImpl(Logger *zap.SugaredLogger, pipelineRepository pi
172176
commonArtifactService: commonArtifactService,
173177
deploymentConfigService: deploymentConfigService,
174178
asyncRunnable: asyncRunnable,
179+
scanHistoryRepository: scanHistoryRepository,
175180
}
176181
config, err := types.GetCdConfig()
177182
if err != nil {
@@ -559,6 +564,20 @@ func (impl *WorkflowDagExecutorImpl) HandlePreStageSuccessEvent(triggerContext t
559564
if err != nil {
560565
return err
561566
}
567+
scanEnabled, scanned := ciArtifact.ScanEnabled, ciArtifact.Scanned
568+
err = impl.handleScanningEventForArtifact(ciArtifact, cdStageCompleteEvent.CdPipelineId, repository4.PIPELINE_STAGE_TYPE_PRE_CD)
569+
if err != nil {
570+
impl.logger.Errorw("error in handling scanning event for ci artifact", "ciArtifact", ciArtifact, "err", err)
571+
return err
572+
}
573+
// if ciArtifact scanEnabled and scanned state changed from above func then update ciArtifact
574+
if scanEnabled != ciArtifact.ScanEnabled || scanned != ciArtifact.Scanned {
575+
ciArtifact, err = impl.ciArtifactRepository.Update(ciArtifact)
576+
if err != nil {
577+
impl.logger.Errorw("error in updating ci artifact after handling scan event for this artifact", "ciArtifact", ciArtifact, "err", err)
578+
return err
579+
}
580+
}
562581
// Migration of deprecated DataSource Type
563582
if ciArtifact.IsMigrationRequired() {
564583
migrationErr := impl.ciArtifactRepository.MigrateToWebHookDataSourceType(ciArtifact.Id)
@@ -652,6 +671,20 @@ func (impl *WorkflowDagExecutorImpl) HandlePostStageSuccessEvent(triggerContext
652671
impl.logger.Errorw("error in finding artifact by cd workflow id", "err", err, "cdWorkflowId", cdWorkflowId)
653672
return err
654673
}
674+
scanEnabled, scanned := ciArtifact.ScanEnabled, ciArtifact.Scanned
675+
err = impl.handleScanningEventForArtifact(ciArtifact, cdPipelineId, repository4.PIPELINE_STAGE_TYPE_POST_CD)
676+
if err != nil {
677+
impl.logger.Errorw("error in handling scanning event for ci artifact", "ciArtifact", ciArtifact, "err", err)
678+
return err
679+
}
680+
// if ciArtifact scanEnabled and scanned state changed from above func then update ciArtifact
681+
if scanEnabled != ciArtifact.ScanEnabled || scanned != ciArtifact.Scanned {
682+
ciArtifact, err = impl.ciArtifactRepository.Update(ciArtifact)
683+
if err != nil {
684+
impl.logger.Errorw("error in updating ci artifact after handling scan event for this artifact", "ciArtifact", ciArtifact, "err", err)
685+
return err
686+
}
687+
}
655688
if len(pluginRegistryImageDetails) > 0 {
656689
PostCDArtifacts, err := impl.commonArtifactService.SavePluginArtifacts(ciArtifact, pluginRegistryImageDetails, cdPipelineId, repository.POST_CD, triggeredBy)
657690
if err != nil {
@@ -711,22 +744,49 @@ func (impl *WorkflowDagExecutorImpl) UpdateCiWorkflowForCiSuccess(request *bean2
711744
return nil
712745
}
713746

714-
func (impl *WorkflowDagExecutorImpl) isImageScanningPluginConfiguredInCiPipeline(ciPipelineId int) (bool, error) {
715-
var isScanPluginConfigured bool
716-
var err error
717-
plugin, err := impl.globalPluginRepository.GetPluginByName(bean3.VULNERABILITY_SCANNING_PLUGIN)
718-
if err != nil || len(plugin) == 0 {
719-
impl.logger.Errorw("error in getting image scanning plugin", "err", err)
720-
return isScanPluginConfigured, err
747+
func (impl *WorkflowDagExecutorImpl) isScanPluginConfiguredAtPipelineStage(pipelineId int, pipelineStage repository4.PipelineStageType) (bool, error) {
748+
plugin, err := impl.globalPluginRepository.GetPluginByName(bean2.ImageScanningPluginToCheckInPipelineStageStep)
749+
if err != nil {
750+
impl.logger.Errorw("error in getting image scanning plugin, Vulnerability Scanning", "pipelineId", pipelineId, "pipelineStage", pipelineStage, "err", err)
751+
return false, err
721752
}
722-
isScanPluginConfigured, err = impl.pipelineStageRepository.CheckPluginExistsInCiPipeline(ciPipelineId, string(repository4.PIPELINE_STAGE_TYPE_POST_CI), plugin[0].Id)
753+
if len(plugin) == 0 {
754+
return false, nil
755+
}
756+
isScanPluginConfigured, err := impl.pipelineStageRepository.CheckIfPluginExistsInPipelineStage(pipelineId, pipelineStage, plugin[0].Id)
723757
if err != nil {
724-
impl.logger.Errorw("error in getting ci pipelineModal plugin", "err", err, "ciPipelineId", ciPipelineId, "pluginId", plugin[0].Id)
725-
return isScanPluginConfigured, err
758+
impl.logger.Errorw("error in getting ci pipeline plugin", "err", err, "pipelineId", pipelineId, "pluginId", plugin[0].Id)
759+
return false, err
726760
}
727761
return isScanPluginConfigured, nil
728762
}
729763

764+
func (impl *WorkflowDagExecutorImpl) handleScanningEventForArtifact(ciArtifact *repository.CiArtifact, pipelineId int,
765+
pipelineStage repository4.PipelineStageType) error {
766+
767+
isScanPluginConfigured, err := impl.isScanPluginConfiguredAtPipelineStage(pipelineId, pipelineStage)
768+
if err != nil {
769+
impl.logger.Errorw("error in fetching if a scan plugin is configured or not in a pipeline", "pipelineStage", pipelineStage, "ciArtifact", ciArtifact)
770+
return err
771+
}
772+
if isScanPluginConfigured {
773+
ciArtifact.ScanEnabled = true
774+
// if scan history is present for this artifact, then this image has been scanned
775+
// else there was some issue with the scanning plugin completing its job.
776+
_, err := impl.scanHistoryRepository.FindByImageAndDigest(ciArtifact.ImageDigest, ciArtifact.Image)
777+
if err != nil && !util.IsErrNoRows(err) {
778+
impl.logger.Errorw("error while fetching latest image scan execution history for image and image digest", "image", ciArtifact.Image, "imageDigest", ciArtifact.ImageDigest, "err", err)
779+
return err
780+
} else if util.IsErrNoRows(err) {
781+
//scan history not found for image and digest hence marking scanned as false
782+
ciArtifact.Scanned = false
783+
} else {
784+
ciArtifact.Scanned = true
785+
}
786+
}
787+
return nil
788+
}
789+
730790
func (impl *WorkflowDagExecutorImpl) HandleCiSuccessEvent(triggerContext triggerBean.TriggerContext, ciPipelineId int, request *bean2.CiArtifactWebhookRequest, imagePushedAt time.Time) (id int, err error) {
731791
impl.logger.Infow("webhook for artifact save", "req", request)
732792
pipelineModal, err := impl.ciPipelineRepository.FindByCiAndAppDetailsById(ciPipelineId)
@@ -749,13 +809,16 @@ func (impl *WorkflowDagExecutorImpl) HandleCiSuccessEvent(triggerContext trigger
749809
}
750810
buildArtifact := helper.GetBuildArtifact(request, pipelineModal.Id, materialJson, createdOn, updatedOn)
751811

752-
isScanPluginConfigured, err := impl.isImageScanningPluginConfiguredInCiPipeline(pipelineModal.Id)
812+
pipelineStage := repository4.PIPELINE_STAGE_TYPE_POST_CI
813+
if pipelineModal.PipelineType == constants2.CI_JOB.ToString() {
814+
pipelineStage = repository4.PIPELINE_STAGE_TYPE_PRE_CI
815+
}
816+
err = impl.handleScanningEventForArtifact(buildArtifact, pipelineModal.Id, pipelineStage)
753817
if err != nil {
754-
impl.logger.Errorw("error in checking isImageScanningPluginConfiguredInCiPipeline", "ciPipelineId", ciPipelineId, "err", err)
818+
impl.logger.Errorw("error in handling scanning event for this ci artifact", "ciArtifact", buildArtifact, "err", err)
755819
return 0, err
756820
}
757-
758-
if request.IsScanEnabled || isScanPluginConfigured {
821+
if request.IsScanEnabled {
759822
buildArtifact.Scanned = true
760823
buildArtifact.ScanEnabled = true
761824
}

pkg/workflow/dag/bean/bean.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package bean
1919
import (
2020
"encoding/json"
2121
"github.com/devtron-labs/devtron/internal/sql/repository"
22+
bean3 "github.com/devtron-labs/devtron/pkg/pipeline/bean"
2223
)
2324

2425
type CiArtifactWebhookRequest struct {
@@ -35,3 +36,7 @@ type CiArtifactWebhookRequest struct {
3536
PluginArtifactStage string `json:"pluginArtifactStage"` // at which stage of CI artifact was generated by plugin ("pre_ci/post_ci")
3637
IsScanEnabled bool `json:"isScanEnabled"`
3738
}
39+
40+
const (
41+
ImageScanningPluginToCheckInPipelineStageStep = bean3.VULNERABILITY_SCANNING_PLUGIN
42+
)

wire_gen.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)