diff --git a/Wire.go b/Wire.go index 086152ec43..7cfd730487 100644 --- a/Wire.go +++ b/Wire.go @@ -286,6 +286,12 @@ func InitializeApp() (*App, error) { bulkAction.NewBulkUpdateServiceImpl, wire.Bind(new(bulkAction.BulkUpdateService), new(*bulkAction.BulkUpdateServiceImpl)), + repository.NewImageTagRepository, + wire.Bind(new(repository.ImageTagRepository), new(*repository.ImageTagRepositoryImpl)), + + pipeline.NewCustomTagService, + wire.Bind(new(pipeline.CustomTagService), new(*pipeline.CustomTagServiceImpl)), + repository.NewGitProviderRepositoryImpl, wire.Bind(new(repository.GitProviderRepository), new(*repository.GitProviderRepositoryImpl)), pipeline.NewGitRegistryConfigImpl, diff --git a/api/bean/CustomTag.go b/api/bean/CustomTag.go new file mode 100644 index 0000000000..8d398be5be --- /dev/null +++ b/api/bean/CustomTag.go @@ -0,0 +1,16 @@ +package bean + +type CustomTag struct { + EntityKey int `json:"entityKey"` + EntityValue string `json:"entityValue"` + TagPattern string `json:"tagPattern"` + AutoIncreasingNumber int `json:"counterX"` + Metadata string `json:"metadata"` +} + +type CustomTagErrorResponse struct { + ConflictingArtifactPath string `json:"conflictingLink"` + TagPattern string `json:"tagPattern"` + AutoIncreasingNumber int `json:"counterX"` + Message string `json:"message"` +} diff --git a/api/restHandler/app/BuildPipelineRestHandler.go b/api/restHandler/app/BuildPipelineRestHandler.go index e9e32fd223..ea5a2342a7 100644 --- a/api/restHandler/app/BuildPipelineRestHandler.go +++ b/api/restHandler/app/BuildPipelineRestHandler.go @@ -593,6 +593,11 @@ func (handler PipelineConfigRestHandlerImpl) TriggerCiPipeline(w http.ResponseWr //RBAC ENDS response := make(map[string]string) resp, err := handler.ciHandler.HandleCIManual(ciTriggerRequest) + if errors.Is(err, bean1.ErrImagePathInUse) { + handler.Logger.Errorw("service err duplicate image tag, TriggerCiPipeline", "err", err, "payload", ciTriggerRequest) + common.WriteJsonResp(w, err, response, http.StatusConflict) + return + } if err != nil { handler.Logger.Errorw("service err, TriggerCiPipeline", "err", err, "payload", ciTriggerRequest) common.WriteJsonResp(w, err, response, http.StatusInternalServerError) @@ -1108,6 +1113,7 @@ func (handler PipelineConfigRestHandlerImpl) GetCIPipelineById(w http.ResponseWr common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) return } + ciPipeline.DefaultTag = []string{"{git_hash}", "{ci_pipeline_id}", "{global_counter}"} common.WriteJsonResp(w, err, ciPipeline, http.StatusOK) } diff --git a/internal/sql/repository/CustomTagRepository.go b/internal/sql/repository/CustomTagRepository.go new file mode 100644 index 0000000000..b075491db0 --- /dev/null +++ b/internal/sql/repository/CustomTagRepository.go @@ -0,0 +1,107 @@ +package repository + +import ( + "github.com/go-pg/pg" + "go.uber.org/zap" +) + +type CustomTag struct { + tableName struct{} `sql:"custom_tag" pg:",discard_unknown_columns"` + Id int `sql:"id"` + EntityKey int `sql:"entity_key"` + EntityValue string `sql:"entity_value"` + TagPattern string `sql:"tag_pattern"` + AutoIncreasingNumber int `sql:"auto_increasing_number, notnull"` + Active bool `sql:"active"` + Metadata string `sql:"metadata"` +} + +type ImagePathReservation struct { + tableName struct{} `sql:"image_path_reservation" pg:",discard_unknown_columns"` + Id int `sql:"id"` + ImagePath string `sql:"image_path"` + CustomTagId int `sql:"custom_tag_id"` + active bool `sql:"active"` +} + +type ImageTagRepository interface { + GetConnection() *pg.DB + CreateImageTag(customTagData *CustomTag) error + FetchCustomTagData(entityType int, entityValue string) (*CustomTag, error) + IncrementAndFetchByEntityKeyAndValue(tx *pg.Tx, entityKey int, entityValue string) (*CustomTag, error) + FindByImagePath(tx *pg.Tx, path string) ([]*ImagePathReservation, error) + InsertImagePath(tx *pg.Tx, reservation *ImagePathReservation) error + UpdateImageTag(customTag *CustomTag) error + DeleteByEntityKeyAndValue(entityKey int, entityValue string) error + DeactivateImagePathReservation(id int) error + FetchActiveCustomTagData(entityKey int, entityValue string) (*CustomTag, error) +} + +type ImageTagRepositoryImpl struct { + dbConnection *pg.DB + logger *zap.SugaredLogger +} + +func NewImageTagRepository(dbConnection *pg.DB, logger *zap.SugaredLogger) *ImageTagRepositoryImpl { + return &ImageTagRepositoryImpl{dbConnection: dbConnection, logger: logger} +} + +func (impl *ImageTagRepositoryImpl) GetConnection() *pg.DB { + return impl.dbConnection +} + +func (impl *ImageTagRepositoryImpl) CreateImageTag(customTagData *CustomTag) error { + return impl.dbConnection.Insert(customTagData) +} + +func (impl *ImageTagRepositoryImpl) UpdateImageTag(customTag *CustomTag) error { + return impl.dbConnection.Update(customTag) +} + +func (impl *ImageTagRepositoryImpl) DeleteByEntityKeyAndValue(entityKey int, entityValue string) error { + query := `update custom_tag set active = false where entity_key = ? and entity_value = ?` + _, err := impl.dbConnection.Exec(query, entityKey, entityValue) + return err +} + +func (impl *ImageTagRepositoryImpl) DeactivateImagePathReservation(id int) error { + query := `update image_path_reservation set active=? where id=?` + _, err := impl.dbConnection.Exec(query, false, id) + return err +} + +func (impl *ImageTagRepositoryImpl) FetchCustomTagData(entityType int, entityValue string) (*CustomTag, error) { + var customTagData CustomTag + err := impl.dbConnection.Model(&customTagData). + Where("entity_key = ?", entityType). + Where("entity_value = ?", entityValue).Select() + return &customTagData, err +} + +func (impl *ImageTagRepositoryImpl) FetchActiveCustomTagData(entityType int, entityValue string) (*CustomTag, error) { + var customTagData CustomTag + err := impl.dbConnection.Model(&customTagData). + Where("entity_key = ?", entityType). + Where("entity_value = ?", entityValue). + Where("active = ?", true).Select() + return &customTagData, err +} + +func (impl *ImageTagRepositoryImpl) IncrementAndFetchByEntityKeyAndValue(tx *pg.Tx, entityKey int, entityValue string) (*CustomTag, error) { + var customTag CustomTag + query := `update custom_tag set auto_increasing_number=auto_increasing_number+1 where entity_key=? and entity_value=? and active = ? returning id, tag_pattern, auto_increasing_number, entity_key, entity_value` + _, err := tx.Query(&customTag, query, entityKey, entityValue, true) + return &customTag, err +} + +func (impl *ImageTagRepositoryImpl) FindByImagePath(tx *pg.Tx, path string) ([]*ImagePathReservation, error) { + var imagePaths []*ImagePathReservation + err := tx.Model(&imagePaths). + Where("image_path = ?", path). + Where("active = ?", true).Select() + return imagePaths, err +} + +func (impl *ImageTagRepositoryImpl) InsertImagePath(tx *pg.Tx, reservation *ImagePathReservation) error { + return tx.Insert(reservation) +} diff --git a/internal/sql/repository/appWorkflow/AppWorkflowRepository.go b/internal/sql/repository/appWorkflow/AppWorkflowRepository.go index bebb2540e1..ab9420aa9d 100644 --- a/internal/sql/repository/appWorkflow/AppWorkflowRepository.go +++ b/internal/sql/repository/appWorkflow/AppWorkflowRepository.go @@ -263,7 +263,6 @@ func (impl AppWorkflowRepositoryImpl) FindWFAllMappingByWorkflowId(workflowId in func (impl AppWorkflowRepositoryImpl) FindWFCIMappingByCIPipelineId(ciPipelineId int) ([]*AppWorkflowMapping, error) { var appWorkflowsMapping []*AppWorkflowMapping - err := impl.dbConnection.Model(&appWorkflowsMapping). Where("component_id = ?", ciPipelineId). Where("type = ?", CIPIPELINE). diff --git a/internal/sql/repository/pipelineConfig/CdWorfkflowRepository.go b/internal/sql/repository/pipelineConfig/CdWorfkflowRepository.go index 352b426612..9acaaa578d 100644 --- a/internal/sql/repository/pipelineConfig/CdWorfkflowRepository.go +++ b/internal/sql/repository/pipelineConfig/CdWorfkflowRepository.go @@ -234,6 +234,7 @@ type CiWorkflowStatus struct { CiPipelineName string `json:"ciPipelineName,omitempty"` CiStatus string `json:"ciStatus"` StorageConfigured bool `json:"storageConfigured"` + CiWorkflowId int `json:"ciWorkflowId,omitempty"` } type AppDeploymentStatus struct { diff --git a/internal/sql/repository/pipelineConfig/CiWorkflowRepository.go b/internal/sql/repository/pipelineConfig/CiWorkflowRepository.go index d713b5751c..171cd872d4 100644 --- a/internal/sql/repository/pipelineConfig/CiWorkflowRepository.go +++ b/internal/sql/repository/pipelineConfig/CiWorkflowRepository.go @@ -53,52 +53,54 @@ type CiWorkflowRepositoryImpl struct { } type CiWorkflow struct { - tableName struct{} `sql:"ci_workflow" pg:",discard_unknown_columns"` - Id int `sql:"id,pk"` - Name string `sql:"name"` - Status string `sql:"status"` - PodStatus string `sql:"pod_status"` - Message string `sql:"message"` - StartedOn time.Time `sql:"started_on"` - FinishedOn time.Time `sql:"finished_on"` - CiPipelineId int `sql:"ci_pipeline_id"` - Namespace string `sql:"namespace"` - BlobStorageEnabled bool `sql:"blob_storage_enabled,notnull"` - LogLocation string `sql:"log_file_path"` - GitTriggers map[int]GitCommit `sql:"git_triggers"` - TriggeredBy int32 `sql:"triggered_by"` - CiArtifactLocation string `sql:"ci_artifact_location"` - PodName string `sql:"pod_name"` - CiBuildType string `sql:"ci_build_type"` - EnvironmentId int `sql:"environment_id"` + tableName struct{} `sql:"ci_workflow" pg:",discard_unknown_columns"` + Id int `sql:"id,pk"` + Name string `sql:"name"` + Status string `sql:"status"` + PodStatus string `sql:"pod_status"` + Message string `sql:"message"` + StartedOn time.Time `sql:"started_on"` + FinishedOn time.Time `sql:"finished_on"` + CiPipelineId int `sql:"ci_pipeline_id"` + Namespace string `sql:"namespace"` + BlobStorageEnabled bool `sql:"blob_storage_enabled,notnull"` + LogLocation string `sql:"log_file_path"` + GitTriggers map[int]GitCommit `sql:"git_triggers"` + TriggeredBy int32 `sql:"triggered_by"` + CiArtifactLocation string `sql:"ci_artifact_location"` + PodName string `sql:"pod_name"` + CiBuildType string `sql:"ci_build_type"` + EnvironmentId int `sql:"environment_id"` + ImagePathReservationId int `sql:"image_path_reservation_id"` ReferenceCiWorkflowId int `sql:"ref_ci_workflow_id"` ParentCiWorkFlowId int `sql:"parent_ci_workflow_id"` CiPipeline *CiPipeline } type WorkflowWithArtifact struct { - Id int `json:"id"` - Name string `json:"name"` - PodName string `json:"podName"` - Status string `json:"status"` - PodStatus string `json:"pod_status"` - Message string `json:"message"` - StartedOn time.Time `json:"started_on"` - FinishedOn time.Time `json:"finished_on"` - CiPipelineId int `json:"ci_pipeline_id"` - Namespace string `json:"namespace"` - LogFilePath string `json:"log_file_path"` - GitTriggers map[int]GitCommit `json:"git_triggers"` - TriggeredBy int32 `json:"triggered_by"` - EmailId string `json:"email_id"` - Image string `json:"image"` - CiArtifactLocation string `json:"ci_artifact_location"` - CiArtifactId int `json:"ci_artifact_d"` - BlobStorageEnabled bool `json:"blobStorageEnabled"` - CiBuildType string `json:"ci_build_type"` - IsArtifactUploaded bool `json:"is_artifact_uploaded"` - EnvironmentId int `json:"environmentId"` - EnvironmentName string `json:"environmentName"` + Id int `json:"id"` + Name string `json:"name"` + PodName string `json:"podName"` + Status string `json:"status"` + PodStatus string `json:"pod_status"` + Message string `json:"message"` + StartedOn time.Time `json:"started_on"` + FinishedOn time.Time `json:"finished_on"` + CiPipelineId int `json:"ci_pipeline_id"` + Namespace string `json:"namespace"` + LogFilePath string `json:"log_file_path"` + GitTriggers map[int]GitCommit `json:"git_triggers"` + TriggeredBy int32 `json:"triggered_by"` + EmailId string `json:"email_id"` + Image string `json:"image"` + CiArtifactLocation string `json:"ci_artifact_location"` + CiArtifactId int `json:"ci_artifact_d"` + BlobStorageEnabled bool `json:"blobStorageEnabled"` + CiBuildType string `json:"ci_build_type"` + IsArtifactUploaded bool `json:"is_artifact_uploaded"` + EnvironmentId int `json:"environmentId"` + EnvironmentName string `json:"environmentName"` + ImagePathReservationId int `json:"image_path_reservation_id"` RefCiWorkflowId int `json:"referenceCiWorkflowId"` ParentCiWorkflowId int `json:"parent_ci_workflow_id"` } diff --git a/pkg/bean/app.go b/pkg/bean/app.go index 29919a091e..2c4c1dcae3 100644 --- a/pkg/bean/app.go +++ b/pkg/bean/app.go @@ -118,6 +118,8 @@ type CiPipeline struct { DockerConfigOverride DockerConfigOverride `json:"dockerConfigOverride,omitempty"` EnvironmentId int `json:"environmentId,omitempty"` LastTriggeredEnvId int `json:"lastTriggeredEnvId"` + CustomTagObject *CustomTagData `json:"customTag,omitempty"` + DefaultTag []string `json:"defaultTag,omitempty"` } type DockerConfigOverride struct { @@ -237,6 +239,11 @@ type CiMaterialPatchRequest struct { Source *SourceTypeConfig `json:"source" validate:"required"` } +type CustomTagData struct { + TagPattern string `json:"tagPattern"` + CounterX int `json:"counterX"` +} + type CiMaterialValuePatchRequest struct { AppId int `json:"appId" validate:"required"` EnvironmentId int `json:"environmentId" validate:"required"` diff --git a/pkg/pipeline/BuildPipelineConfigService.go b/pkg/pipeline/BuildPipelineConfigService.go index 94e15827ff..4219f677ef 100644 --- a/pkg/pipeline/BuildPipelineConfigService.go +++ b/pkg/pipeline/BuildPipelineConfigService.go @@ -38,6 +38,7 @@ import ( "github.com/juju/errors" "go.opentelemetry.io/otel" "go.uber.org/zap" + "strconv" "strings" "time" ) @@ -124,6 +125,7 @@ type CiPipelineConfigServiceImpl struct { ciPipelineMaterialRepository pipelineConfig.CiPipelineMaterialRepository resourceGroupService resourceGroup2.ResourceGroupService enforcerUtil rbac.EnforcerUtil + customTagService CustomTagService } func NewCiPipelineConfigServiceImpl(logger *zap.SugaredLogger, @@ -144,7 +146,8 @@ func NewCiPipelineConfigServiceImpl(logger *zap.SugaredLogger, CiTemplateHistoryService history.CiTemplateHistoryService, enforcerUtil rbac.EnforcerUtil, ciWorkflowRepository pipelineConfig.CiWorkflowRepository, - resourceGroupService resourceGroup2.ResourceGroupService) *CiPipelineConfigServiceImpl { + resourceGroupService resourceGroup2.ResourceGroupService, + customTagService CustomTagService) *CiPipelineConfigServiceImpl { securityConfig := &SecurityConfig{} err := env.Parse(securityConfig) @@ -172,6 +175,7 @@ func NewCiPipelineConfigServiceImpl(logger *zap.SugaredLogger, ciWorkflowRepository: ciWorkflowRepository, resourceGroupService: resourceGroupService, securityConfig: securityConfig, + customTagService: customTagService, } } @@ -616,6 +620,16 @@ func (impl *CiPipelineConfigServiceImpl) GetCiPipeline(appId int) (ciConfig *bea impl.logger.Errorw("error in fetching ciEnvMapping", "ciPipelineId ", pipeline.Id, "err", err) return nil, err } + customTag, err := impl.customTagService.GetActiveCustomTagByEntityKeyAndValue(bean3.EntityTypeCiPipelineId, strconv.Itoa(pipeline.Id)) + if err != nil && err != pg.ErrNoRows { + return nil, err + } + if customTag.Id != 0 { + ciPipeline.CustomTagObject = &bean.CustomTagData{ + TagPattern: customTag.TagPattern, + CounterX: customTag.AutoIncreasingNumber, + } + } if ciEnvMapping.Id > 0 { ciPipeline.EnvironmentId = ciEnvMapping.EnvironmentId } @@ -743,6 +757,16 @@ func (impl *CiPipelineConfigServiceImpl) GetCiPipelineById(pipelineId int) (ciPi IsDockerConfigOverridden: pipeline.IsDockerConfigOverridden, PipelineType: bean.PipelineType(pipeline.PipelineType), } + customTag, err := impl.customTagService.GetActiveCustomTagByEntityKeyAndValue(bean3.EntityTypeCiPipelineId, strconv.Itoa(pipeline.Id)) + if err != nil && err != pg.ErrNoRows { + return nil, err + } + if customTag.Id != 0 { + ciPipeline.CustomTagObject = &bean.CustomTagData{ + TagPattern: customTag.TagPattern, + CounterX: customTag.AutoIncreasingNumber, + } + } ciEnvMapping, err := impl.ciPipelineRepository.FindCiEnvMappingByCiPipelineId(pipelineId) if err != nil && err != pg.ErrNoRows { impl.logger.Errorw("error in fetching ci env mapping", "pipelineId", pipelineId, "err", err) diff --git a/pkg/pipeline/CiCdPipelineOrchestrator.go b/pkg/pipeline/CiCdPipelineOrchestrator.go index 19c51428e8..aee7eedeee 100644 --- a/pkg/pipeline/CiCdPipelineOrchestrator.go +++ b/pkg/pipeline/CiCdPipelineOrchestrator.go @@ -27,6 +27,7 @@ import ( "errors" "fmt" util3 "github.com/devtron-labs/common-lib/utils/k8s" + bean4 "github.com/devtron-labs/devtron/api/bean" "github.com/devtron-labs/devtron/client/gitSensor" app2 "github.com/devtron-labs/devtron/internal/sql/repository/app" dockerRegistryRepository "github.com/devtron-labs/devtron/internal/sql/repository/dockerRegistry" @@ -113,6 +114,7 @@ type CiCdPipelineOrchestratorImpl struct { dockerArtifactStoreRepository dockerRegistryRepository.DockerArtifactStoreRepository configMapService ConfigMapService genericNoteService genericNotes.GenericNoteService + customTagService CustomTagService } func NewCiCdPipelineOrchestrator( @@ -138,6 +140,7 @@ func NewCiCdPipelineOrchestrator( ciTemplateService CiTemplateService, dockerArtifactStoreRepository dockerRegistryRepository.DockerArtifactStoreRepository, configMapService ConfigMapService, + customTagService CustomTagService, genericNoteService genericNotes.GenericNoteService) *CiCdPipelineOrchestratorImpl { return &CiCdPipelineOrchestratorImpl{ appRepository: pipelineGroupRepository, @@ -164,6 +167,7 @@ func NewCiCdPipelineOrchestrator( dockerArtifactStoreRepository: dockerArtifactStoreRepository, configMapService: configMapService, genericNoteService: genericNoteService, + customTagService: customTagService, } } @@ -326,6 +330,30 @@ func (impl CiCdPipelineOrchestratorImpl) PatchMaterialValue(createRequest *bean. AuditLog: sql.AuditLog{UpdatedBy: userId, UpdatedOn: time.Now()}, } + //If customTagObject has been passed, create or update the resource + //Otherwise deleteIfExists + if createRequest.CustomTagObject != nil { + customTag := bean4.CustomTag{ + EntityKey: bean2.EntityTypeCiPipelineId, + EntityValue: strconv.Itoa(ciPipelineObject.Id), + TagPattern: createRequest.CustomTagObject.TagPattern, + AutoIncreasingNumber: createRequest.CustomTagObject.CounterX, + } + err = impl.customTagService.CreateOrUpdateCustomTag(&customTag) + if err != nil { + return nil, err + } + } else { + customTag := bean4.CustomTag{ + EntityKey: bean2.EntityTypeCiPipelineId, + EntityValue: strconv.Itoa(ciPipelineObject.Id), + } + err := impl.customTagService.DeleteCustomTagIfExists(customTag) + if err != nil { + return nil, err + } + } + createOnTimeMap := make(map[int]time.Time) createByMap := make(map[int]int32) for _, oldMaterial := range oldPipeline.CiPipelineMaterials { @@ -738,6 +766,21 @@ func (impl CiCdPipelineOrchestratorImpl) CreateCiConf(createRequest *bean.CiConf impl.logger.Errorw("error in saving pipeline", "ciPipelineObject", ciPipelineObject, "err", err) return nil, err } + + //If customTagObejct has been passed, save it + if ciPipeline.CustomTagObject != nil { + customTag := &bean4.CustomTag{ + EntityKey: bean2.EntityTypeCiPipelineId, + EntityValue: strconv.Itoa(ciPipeline.Id), + TagPattern: ciPipeline.CustomTagObject.TagPattern, + AutoIncreasingNumber: ciPipeline.CustomTagObject.CounterX, + } + err := impl.customTagService.CreateOrUpdateCustomTag(customTag) + if err != nil { + return nil, err + } + } + if createRequest.IsJob { CiEnvMapping := &pipelineConfig.CiEnvMapping{ CiPipelineId: ciPipeline.Id, diff --git a/pkg/pipeline/CiHandler.go b/pkg/pipeline/CiHandler.go index 5350f37080..385036df79 100644 --- a/pkg/pipeline/CiHandler.go +++ b/pkg/pipeline/CiHandler.go @@ -28,6 +28,7 @@ import ( "github.com/devtron-labs/common-lib/utils/k8s" bean2 "github.com/devtron-labs/devtron/api/bean" "github.com/devtron-labs/devtron/client/gitSensor" + "github.com/devtron-labs/devtron/internal/sql/repository/appWorkflow" repository2 "github.com/devtron-labs/devtron/internal/sql/repository/imageTagging" "github.com/devtron-labs/devtron/pkg/cluster" repository3 "github.com/devtron-labs/devtron/pkg/cluster/repository" @@ -105,11 +106,13 @@ type CiHandlerImpl struct { resourceGroupService resourceGroup.ResourceGroupService envRepository repository3.EnvironmentRepository imageTaggingService ImageTaggingService + customTagService CustomTagService + appWorkflowRepository appWorkflow.AppWorkflowRepository config *CiConfig k8sCommonService k8s2.K8sCommonService } -func NewCiHandlerImpl(Logger *zap.SugaredLogger, ciService CiService, ciPipelineMaterialRepository pipelineConfig.CiPipelineMaterialRepository, gitSensorClient gitSensor.Client, ciWorkflowRepository pipelineConfig.CiWorkflowRepository, workflowService WorkflowService, ciLogService CiLogService, ciArtifactRepository repository.CiArtifactRepository, userService user.UserService, eventClient client.EventClient, eventFactory client.EventFactory, ciPipelineRepository pipelineConfig.CiPipelineRepository, appListingRepository repository.AppListingRepository, K8sUtil *k8s.K8sUtil, cdPipelineRepository pipelineConfig.PipelineRepository, enforcerUtil rbac.EnforcerUtil, resourceGroupService resourceGroup.ResourceGroupService, envRepository repository3.EnvironmentRepository, imageTaggingService ImageTaggingService, k8sCommonService k8s2.K8sCommonService) *CiHandlerImpl { +func NewCiHandlerImpl(Logger *zap.SugaredLogger, ciService CiService, ciPipelineMaterialRepository pipelineConfig.CiPipelineMaterialRepository, gitSensorClient gitSensor.Client, ciWorkflowRepository pipelineConfig.CiWorkflowRepository, workflowService WorkflowService, ciLogService CiLogService, ciArtifactRepository repository.CiArtifactRepository, userService user.UserService, eventClient client.EventClient, eventFactory client.EventFactory, ciPipelineRepository pipelineConfig.CiPipelineRepository, appListingRepository repository.AppListingRepository, K8sUtil *k8s.K8sUtil, cdPipelineRepository pipelineConfig.PipelineRepository, enforcerUtil rbac.EnforcerUtil, resourceGroupService resourceGroup.ResourceGroupService, envRepository repository3.EnvironmentRepository, imageTaggingService ImageTaggingService, appWorkflowRepository appWorkflow.AppWorkflowRepository, customTagService CustomTagService, k8sCommonService k8s2.K8sCommonService) *CiHandlerImpl { cih := &CiHandlerImpl{ Logger: Logger, ciService: ciService, @@ -130,6 +133,8 @@ func NewCiHandlerImpl(Logger *zap.SugaredLogger, ciService CiService, ciPipeline resourceGroupService: resourceGroupService, envRepository: envRepository, imageTaggingService: imageTaggingService, + customTagService: customTagService, + appWorkflowRepository: appWorkflowRepository, k8sCommonService: k8sCommonService, } config, err := GetCiConfig() @@ -167,6 +172,8 @@ type WorkflowResponse struct { EnvironmentName string `json:"environmentName"` ImageReleaseTags []*repository2.ImageTag `json:"imageReleaseTags"` ImageComment *repository2.ImageComment `json:"imageComment"` + AppWorkflowId int `json:"appWorkflowId"` + CustomTag *bean2.CustomTagErrorResponse `json:"customTag,omitempty"` PipelineType string `json:"pipelineType"` ReferenceWorkflowId int `json:"referenceWorkflowId"` } @@ -613,6 +620,23 @@ func (impl *CiHandlerImpl) GetBuildHistory(pipelineId int, appId int, offset int EnvironmentName: w.EnvironmentName, ReferenceWorkflowId: w.RefCiWorkflowId, } + if w.Message == bean3.ImageTagUnavailableMessage { + customTag, err := impl.customTagService.GetCustomTagByEntityKeyAndValue(bean3.EntityTypeCiPipelineId, strconv.Itoa(w.CiPipelineId)) + if err != nil && err != pg.ErrNoRows { + //err == pg.ErrNoRows should never happen + return nil, err + } + appWorkflows, err := impl.appWorkflowRepository.FindWFCIMappingByCIPipelineId(w.CiPipelineId) + if err != nil && err != pg.ErrNoRows { + return nil, err + } + wfResponse.AppWorkflowId = appWorkflows[0].AppWorkflowId //it is guaranteed there will always be 1 entry (in case of ci_pipeline_id) + wfResponse.CustomTag = &bean2.CustomTagErrorResponse{ + TagPattern: customTag.TagPattern, + AutoIncreasingNumber: customTag.AutoIncreasingNumber, + Message: bean3.ImageTagUnavailableMessage, + } + } if imageTagsDataMap[w.CiArtifactId] != nil { wfResponse.ImageReleaseTags = imageTagsDataMap[w.CiArtifactId] //if artifact is not yet created,empty list will be sent } @@ -664,6 +688,12 @@ func (impl *CiHandlerImpl) CancelBuild(workflowId int) (int, error) { impl.Logger.Errorw("cannot update deleted workflow status, but wf deleted", "err", err) return 0, err } + imagePathReservationId := workflow.ImagePathReservationId + err = impl.customTagService.DeactivateImagePathReservation(imagePathReservationId) + if err != nil { + impl.Logger.Errorw("error in marking image tag unreserved", "err", err) + return 0, err + } return workflow.Id, nil } @@ -1663,9 +1693,12 @@ func (impl *CiHandlerImpl) UpdateCiWorkflowStatusFailure(timeoutForFailureCiBuil err := impl.ciWorkflowRepository.UpdateWorkFlow(ciWorkflow) if err != nil { impl.Logger.Errorw("unable to update ci workflow, its eligible to mark failed", "err", err) - continue // skip this and process for next ci workflow } + err = impl.customTagService.DeactivateImagePathReservation(ciWorkflow.ImagePathReservationId) + if err != nil { + impl.Logger.Errorw("unable to update ci workflow, its eligible to mark failed", "err", err) + } } } return nil @@ -1773,6 +1806,7 @@ func (impl *CiHandlerImpl) FetchCiStatusForTriggerViewForEnvironment(request res ciWorkflowStatus.CiPipelineName = ciWorkflow.CiPipeline.Name ciWorkflowStatus.CiStatus = ciWorkflow.Status ciWorkflowStatus.StorageConfigured = ciWorkflow.BlobStorageEnabled + ciWorkflowStatus.CiWorkflowId = ciWorkflow.Id ciWorkflowStatuses = append(ciWorkflowStatuses, ciWorkflowStatus) notTriggeredWorkflows[ciWorkflowStatus.CiPipelineId] = true } diff --git a/pkg/pipeline/CiService.go b/pkg/pipeline/CiService.go index 9ef337900f..c3dacf2cb6 100644 --- a/pkg/pipeline/CiService.go +++ b/pkg/pipeline/CiService.go @@ -60,6 +60,7 @@ type CiServiceImpl struct { workflowService WorkflowService ciPipelineMaterialRepository pipelineConfig.CiPipelineMaterialRepository ciWorkflowRepository pipelineConfig.CiWorkflowRepository + ciConfig *CiConfig eventClient client.EventClient eventFactory client.EventFactory mergeUtil *util.MergeUtil @@ -71,6 +72,7 @@ type CiServiceImpl struct { appCrudOperationService app.AppCrudOperationService envRepository repository1.EnvironmentRepository appRepository appRepository.AppRepository + customTagService CustomTagService variableSnapshotHistoryService variables.VariableSnapshotHistoryService config *CiConfig } @@ -84,6 +86,7 @@ func NewCiServiceImpl(Logger *zap.SugaredLogger, workflowService WorkflowService userService user.UserService, ciTemplateService CiTemplateService, appCrudOperationService app.AppCrudOperationService, envRepository repository1.EnvironmentRepository, appRepository appRepository.AppRepository, variableSnapshotHistoryService variables.VariableSnapshotHistoryService, + customTagService CustomTagService, ) *CiServiceImpl { cis := &CiServiceImpl{ Logger: Logger, @@ -102,6 +105,7 @@ func NewCiServiceImpl(Logger *zap.SugaredLogger, workflowService WorkflowService envRepository: envRepository, appRepository: appRepository, variableSnapshotHistoryService: variableSnapshotHistoryService, + customTagService: customTagService, } config, err := GetCiConfig() if err != nil { @@ -450,7 +454,32 @@ func (impl *CiServiceImpl) buildWfRequestForCiPipeline(pipeline *pipelineConfig. refPluginsData = []*bean2.RefPluginObject{} } - dockerImageTag := impl.buildImageTag(commitHashes, pipeline.Id, savedWf.Id) + var dockerImageTag string + customTag, err := impl.customTagService.GetActiveCustomTagByEntityKeyAndValue(bean2.EntityTypeCiPipelineId, strconv.Itoa(pipeline.Id)) + if err != nil && err != pg.ErrNoRows { + return nil, err + } + if customTag.Id != 0 { + imagePathReservation, err := impl.customTagService.GenerateImagePath(bean2.EntityTypeCiPipelineId, strconv.Itoa(pipeline.Id), pipeline.CiTemplate.DockerRegistry.RegistryURL, pipeline.CiTemplate.DockerRepository) + if err != nil { + if errors.Is(err, bean2.ErrImagePathInUse) { + savedWf.Status = pipelineConfig.WorkflowFailed + savedWf.Message = bean2.ImageTagUnavailableMessage + err1 := impl.ciWorkflowRepository.UpdateWorkFlow(savedWf) + if err1 != nil { + impl.Logger.Errorw("could not save workflow, after failing due to conflicting image tag") + } + return nil, err + } + return nil, err + } + savedWf.ImagePathReservationId = imagePathReservation.Id + //imagePath = docker.io/avd0/dashboard:fd23414b + dockerImageTag = strings.Split(imagePathReservation.ImagePath, ":")[1] + } else { + dockerImageTag = impl.buildImageTag(commitHashes, pipeline.Id, savedWf.Id) + } + if ciWorkflowConfig.CiCacheBucket == "" { ciWorkflowConfig.CiCacheBucket = impl.config.DefaultCacheBucket } @@ -778,5 +807,4 @@ func _getTruncatedImageTag(imageTag string) string { } else { return imageTag[:_truncatedLength] } - } diff --git a/pkg/pipeline/CustomTagService.go b/pkg/pipeline/CustomTagService.go new file mode 100644 index 0000000000..76cfc766ac --- /dev/null +++ b/pkg/pipeline/CustomTagService.go @@ -0,0 +1,177 @@ +package pipeline + +import ( + "fmt" + "github.com/devtron-labs/devtron/api/bean" + "github.com/devtron-labs/devtron/internal/sql/repository" + bean2 "github.com/devtron-labs/devtron/pkg/pipeline/bean" + "github.com/go-pg/pg" + "go.uber.org/zap" + "regexp" + "strconv" + "strings" +) + +type CustomTagService interface { + CreateOrUpdateCustomTag(tag *bean.CustomTag) error + GetCustomTagByEntityKeyAndValue(entityKey int, entityValue string) (*repository.CustomTag, error) + GetActiveCustomTagByEntityKeyAndValue(entityKey int, entityValue string) (*repository.CustomTag, error) + GenerateImagePath(entityKey int, entityValue string, dockerRegistryURL string, dockerRepo string) (*repository.ImagePathReservation, error) + DeleteCustomTagIfExists(tag bean.CustomTag) error + DeactivateImagePathReservation(id int) error +} + +type CustomTagServiceImpl struct { + Logger *zap.SugaredLogger + customTagRepository repository.ImageTagRepository +} + +func NewCustomTagService(logger *zap.SugaredLogger, customTagRepo repository.ImageTagRepository) *CustomTagServiceImpl { + return &CustomTagServiceImpl{ + Logger: logger, + customTagRepository: customTagRepo, + } +} + +func (impl *CustomTagServiceImpl) DeactivateImagePathReservation(id int) error { + return impl.customTagRepository.DeactivateImagePathReservation(id) +} + +func (impl *CustomTagServiceImpl) CreateOrUpdateCustomTag(tag *bean.CustomTag) error { + if err := validateTagPattern(tag.TagPattern); err != nil { + return err + } + customTagData := repository.CustomTag{ + EntityKey: tag.EntityKey, + EntityValue: tag.EntityValue, + TagPattern: strings.ReplaceAll(tag.TagPattern, bean2.IMAGE_TAG_VARIABLE_NAME_X, bean2.IMAGE_TAG_VARIABLE_NAME_x), + AutoIncreasingNumber: tag.AutoIncreasingNumber, + Metadata: tag.Metadata, + Active: true, + } + oldTagObject, err := impl.customTagRepository.FetchCustomTagData(customTagData.EntityKey, customTagData.EntityValue) + if err != nil && err != pg.ErrNoRows { + return err + } + if oldTagObject.Id == 0 { + return impl.customTagRepository.CreateImageTag(&customTagData) + } else { + customTagData.Id = oldTagObject.Id + customTagData.Active = true + return impl.customTagRepository.UpdateImageTag(&customTagData) + } +} + +func (impl *CustomTagServiceImpl) DeleteCustomTagIfExists(tag bean.CustomTag) error { + return impl.customTagRepository.DeleteByEntityKeyAndValue(tag.EntityKey, tag.EntityValue) +} + +func (impl *CustomTagServiceImpl) GetCustomTagByEntityKeyAndValue(entityKey int, entityValue string) (*repository.CustomTag, error) { + return impl.customTagRepository.FetchCustomTagData(entityKey, entityValue) +} + +func (impl *CustomTagServiceImpl) GetActiveCustomTagByEntityKeyAndValue(entityKey int, entityValue string) (*repository.CustomTag, error) { + return impl.customTagRepository.FetchActiveCustomTagData(entityKey, entityValue) +} + +func (impl *CustomTagServiceImpl) GenerateImagePath(entityKey int, entityValue string, dockerRegistryURL string, dockerRepo string) (*repository.ImagePathReservation, error) { + connection := impl.customTagRepository.GetConnection() + tx, err := connection.Begin() + if err != nil { + return nil, nil + } + defer tx.Rollback() + customTagData, err := impl.customTagRepository.IncrementAndFetchByEntityKeyAndValue(tx, entityKey, entityValue) + if err != nil { + return nil, err + } + tag, err := validateAndConstructTag(customTagData) + if err != nil { + return nil, err + } + imagePath := fmt.Sprintf(bean2.ImagePathPattern, dockerRegistryURL, dockerRepo, tag) + imagePathReservations, err := impl.customTagRepository.FindByImagePath(tx, imagePath) + if err != nil && err != pg.ErrNoRows { + return nil, err + } + if len(imagePathReservations) > 0 { + return nil, bean2.ErrImagePathInUse + } + imagePathReservation := &repository.ImagePathReservation{ + ImagePath: imagePath, + CustomTagId: customTagData.Id, + } + err = impl.customTagRepository.InsertImagePath(tx, imagePathReservation) + if err != nil { + return nil, err + } + err = tx.Commit() + if err != nil { + return nil, err + } + return imagePathReservation, nil +} + +func validateAndConstructTag(customTagData *repository.CustomTag) (string, error) { + err := validateTagPattern(customTagData.TagPattern) + if err != nil { + return "", err + } + if customTagData.AutoIncreasingNumber < 0 { + return "", fmt.Errorf("counter {x} can not be negative") + } + dockerImageTag := strings.ReplaceAll(customTagData.TagPattern, bean2.IMAGE_TAG_VARIABLE_NAME_x, strconv.Itoa(customTagData.AutoIncreasingNumber-1)) //-1 because number is already incremented, current value will be used next time + if !isValidDockerImageTag(dockerImageTag) { + return dockerImageTag, fmt.Errorf("invalid docker tag") + } + return dockerImageTag, nil +} + +func validateTagPattern(customTagPattern string) error { + if len(customTagPattern) == 0 { + return fmt.Errorf("tag length can not be zero") + } + + variableCount := 0 + variableCount = variableCount + strings.Count(customTagPattern, bean2.IMAGE_TAG_VARIABLE_NAME_x) + variableCount = variableCount + strings.Count(customTagPattern, bean2.IMAGE_TAG_VARIABLE_NAME_X) + + if variableCount == 0 { + // there can be case when there is only one {x} or {x} + return fmt.Errorf("variable with format {x} or {X} not found") + } else if variableCount > 1 { + return fmt.Errorf("only one variable with format {x} or {X} allowed") + } + + // replacing variable with 1 (dummy value) and checking if resulting string is valid tag + tagWithDummyValue := strings.ReplaceAll(customTagPattern, bean2.IMAGE_TAG_VARIABLE_NAME_x, "1") + tagWithDummyValue = strings.ReplaceAll(tagWithDummyValue, bean2.IMAGE_TAG_VARIABLE_NAME_X, "1") + + if !isValidDockerImageTag(tagWithDummyValue) { + return fmt.Errorf("not a valid image tag") + } + + return nil +} + +func isValidDockerImageTag(tag string) bool { + // Define the regular expression for a valid Docker image tag + re := regexp.MustCompile(bean2.REGEX_PATTERN_FOR_IMAGE_TAG) + return re.MatchString(tag) +} + +func validateTag(imageTag string) error { + if len(imageTag) == 0 || len(imageTag) > 128 { + return fmt.Errorf("image tag should be of len 1-128 only, imageTag: %s", imageTag) + } + allowedSymbols := ".abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ-0987654321" + allowedCharSet := make(map[int32]struct{}) + for _, c := range allowedSymbols { + allowedCharSet[c] = struct{}{} + } + firstChar := imageTag[0:1] + if firstChar == "." || firstChar == "-" { + return fmt.Errorf("image tag can not start with a period or a hyphen, imageTag: %s", imageTag) + } + return nil +} diff --git a/pkg/pipeline/WebhookService.go b/pkg/pipeline/WebhookService.go index 2d780d440a..5e1b6bc18e 100644 --- a/pkg/pipeline/WebhookService.go +++ b/pkg/pipeline/WebhookService.go @@ -70,6 +70,7 @@ type WebhookServiceImpl struct { eventFactory client.EventFactory workflowDagExecutor WorkflowDagExecutor ciHandler CiHandler + customTagService CustomTagService } func NewWebhookServiceImpl( @@ -79,6 +80,7 @@ func NewWebhookServiceImpl( appService app.AppService, eventClient client.EventClient, eventFactory client.EventFactory, ciWorkflowRepository pipelineConfig.CiWorkflowRepository, + customTagService CustomTagService, workflowDagExecutor WorkflowDagExecutor, ciHandler CiHandler) *WebhookServiceImpl { webhookHandler := &WebhookServiceImpl{ ciArtifactRepository: ciArtifactRepository, @@ -90,6 +92,7 @@ func NewWebhookServiceImpl( ciWorkflowRepository: ciWorkflowRepository, workflowDagExecutor: workflowDagExecutor, ciHandler: ciHandler, + customTagService: customTagService, } config, err := GetCiConfig() if err != nil { @@ -141,6 +144,13 @@ func (impl WebhookServiceImpl) HandleCiStepFailedEvent(ciPipelineId int, request return err } + go func() { + err := impl.customTagService.DeactivateImagePathReservation(savedWorkflow.ImagePathReservationId) + if err != nil { + impl.logger.Errorw("unable to deactivate impage_path_reservation ", err) + } + }() + go impl.WriteCIStepFailedEvent(pipeline, request, savedWorkflow) return nil } diff --git a/pkg/pipeline/bean/CustomTagService.go b/pkg/pipeline/bean/CustomTagService.go new file mode 100644 index 0000000000..b823de3aed --- /dev/null +++ b/pkg/pipeline/bean/CustomTagService.go @@ -0,0 +1,25 @@ +package bean + +import "fmt" + +const ( + EntityNull = iota + EntityTypeCiPipelineId +) + +const ( + ImagePathPattern = "%s/%s:%s" // dockerReg/dockerRepo:Tag + ImageTagUnavailableMessage = "Desired image tag already exists" + REGEX_PATTERN_FOR_ENSURING_ONLY_ONE_VARIABLE_BETWEEN_BRACKETS = `\{.{2,}\}` + REGEX_PATTERN_FOR_CHARACTER_OTHER_THEN_X_OR_x = `\{[^xX]|{}\}` + REGEX_PATTERN_FOR_IMAGE_TAG = `^[a-zA-Z0-9]+[a-zA-Z0-9._-]*$` +) + +var ( + ErrImagePathInUse = fmt.Errorf(ImageTagUnavailableMessage) +) + +const ( + IMAGE_TAG_VARIABLE_NAME_X = "{X}" + IMAGE_TAG_VARIABLE_NAME_x = "{x}" +) diff --git a/scripts/sql/182_custom_image_tag.down.sql b/scripts/sql/182_custom_image_tag.down.sql new file mode 100644 index 0000000000..3033e71504 --- /dev/null +++ b/scripts/sql/182_custom_image_tag.down.sql @@ -0,0 +1,15 @@ +DROP TABLE IF EXISTS custom_tag; + +DROP INDEX IF EXISTS entity_key_value; + +ALTER TABLE custom_tag + DROP CONSTRAINT unique_entity_key_entity_value; + +DROP TABLE IF EXISTS image_path_reservation; + +DROP INDEX IF EXISTS image_path_index; + +ALTER TABLE ci_workflow + DROP column IF EXISTS image_path_reservation_id; +ALTER TABLE ci_workflow + DROP CONSTRAINT fk_image_path_reservation_id; \ No newline at end of file diff --git a/scripts/sql/182_custom_image_tag.up.sql b/scripts/sql/182_custom_image_tag.up.sql new file mode 100644 index 0000000000..d6388c6ce3 --- /dev/null +++ b/scripts/sql/182_custom_image_tag.up.sql @@ -0,0 +1,32 @@ +CREATE TABLE "public"."custom_tag" +( + id serial PRIMARY KEY, + custom_tag_format text, + tag_pattern text, + auto_increasing_number int DEFAULT 0, + entity_key int, + entity_value text, + active boolean DEFAULT true, + metadata jsonb +); + +CREATE INDEX IF NOT EXISTS entity_key_value ON custom_tag (entity_key, entity_value); + +ALTER TABLE custom_tag + ADD CONSTRAINT unique_entity_key_entity_value UNIQUE (entity_key, entity_value); + +CREATE TABLE IF not exists "public"."image_path_reservation" +( + id serial PRIMARY KEY, + custom_tag_id int, + image_path text, + active boolean default true, + FOREIGN KEY (custom_tag_id) REFERENCES custom_tag (id) +); + +CREATE INDEX IF NOT EXISTS image_path_index ON image_path_reservation (image_path); + +ALTER TABLE ci_workflow + ADD column IF NOT EXISTS image_path_reservation_id int; +ALTER TABLE ci_workflow + ADD CONSTRAINT fk_image_path_reservation_id FOREIGN KEY (image_path_reservation_id) REFERENCES image_path_reservation (id); \ No newline at end of file diff --git a/wire_gen.go b/wire_gen.go index f6352874d4..a095cb7752 100644 --- a/wire_gen.go +++ b/wire_gen.go @@ -464,7 +464,9 @@ func InitializeApp() (*App, error) { ciBuildConfigServiceImpl := pipeline.NewCiBuildConfigServiceImpl(sugaredLogger, ciBuildConfigRepositoryImpl) ciTemplateServiceImpl := pipeline.NewCiTemplateServiceImpl(sugaredLogger, ciBuildConfigServiceImpl, ciTemplateRepositoryImpl, ciTemplateOverrideRepositoryImpl) configMapServiceImpl := pipeline.NewConfigMapServiceImpl(chartRepositoryImpl, sugaredLogger, chartRepoRepositoryImpl, utilMergeUtil, pipelineConfigRepositoryImpl, configMapRepositoryImpl, envConfigOverrideRepositoryImpl, commonServiceImpl, appRepositoryImpl, configMapHistoryServiceImpl, environmentRepositoryImpl) - ciCdPipelineOrchestratorImpl := pipeline.NewCiCdPipelineOrchestrator(appRepositoryImpl, sugaredLogger, materialRepositoryImpl, pipelineRepositoryImpl, ciPipelineRepositoryImpl, ciPipelineMaterialRepositoryImpl, clientImpl, ciCdConfig, appWorkflowRepositoryImpl, environmentRepositoryImpl, attributesServiceImpl, appListingRepositoryImpl, appCrudOperationServiceImpl, userAuthServiceImpl, prePostCdScriptHistoryServiceImpl, prePostCiScriptHistoryServiceImpl, pipelineStageServiceImpl, ciTemplateOverrideRepositoryImpl, gitMaterialHistoryServiceImpl, ciPipelineHistoryServiceImpl, ciTemplateServiceImpl, dockerArtifactStoreRepositoryImpl, configMapServiceImpl, genericNoteServiceImpl) + imageTagRepositoryImpl := repository.NewImageTagRepository(db, sugaredLogger) + customTagServiceImpl := pipeline.NewCustomTagService(sugaredLogger, imageTagRepositoryImpl) + ciCdPipelineOrchestratorImpl := pipeline.NewCiCdPipelineOrchestrator(appRepositoryImpl, sugaredLogger, materialRepositoryImpl, pipelineRepositoryImpl, ciPipelineRepositoryImpl, ciPipelineMaterialRepositoryImpl, clientImpl, ciCdConfig, appWorkflowRepositoryImpl, environmentRepositoryImpl, attributesServiceImpl, appListingRepositoryImpl, appCrudOperationServiceImpl, userAuthServiceImpl, prePostCdScriptHistoryServiceImpl, prePostCiScriptHistoryServiceImpl, pipelineStageServiceImpl, ciTemplateOverrideRepositoryImpl, gitMaterialHistoryServiceImpl, ciPipelineHistoryServiceImpl, ciTemplateServiceImpl, dockerArtifactStoreRepositoryImpl, configMapServiceImpl, customTagServiceImpl, genericNoteServiceImpl) ecrConfig, err := pipeline.GetEcrConfig() if err != nil { return nil, err @@ -474,7 +476,7 @@ func InitializeApp() (*App, error) { resourceGroupRepositoryImpl := resourceGroup.NewResourceGroupRepositoryImpl(db) resourceGroupMappingRepositoryImpl := resourceGroup.NewResourceGroupMappingRepositoryImpl(db) resourceGroupServiceImpl := resourceGroup2.NewResourceGroupServiceImpl(sugaredLogger, resourceGroupRepositoryImpl, resourceGroupMappingRepositoryImpl, enforcerUtilImpl, devtronResourceSearchableKeyServiceImpl) - ciPipelineConfigServiceImpl := pipeline.NewCiPipelineConfigServiceImpl(sugaredLogger, ciCdPipelineOrchestratorImpl, dockerArtifactStoreRepositoryImpl, materialRepositoryImpl, appRepositoryImpl, pipelineRepositoryImpl, ciPipelineRepositoryImpl, ecrConfig, appWorkflowRepositoryImpl, ciCdConfig, attributesServiceImpl, pipelineStageServiceImpl, ciPipelineMaterialRepositoryImpl, ciTemplateServiceImpl, ciTemplateOverrideRepositoryImpl, ciTemplateHistoryServiceImpl, enforcerUtilImpl, ciWorkflowRepositoryImpl, resourceGroupServiceImpl) + ciPipelineConfigServiceImpl := pipeline.NewCiPipelineConfigServiceImpl(sugaredLogger, ciCdPipelineOrchestratorImpl, dockerArtifactStoreRepositoryImpl, materialRepositoryImpl, appRepositoryImpl, pipelineRepositoryImpl, ciPipelineRepositoryImpl, ecrConfig, appWorkflowRepositoryImpl, ciCdConfig, attributesServiceImpl, pipelineStageServiceImpl, ciPipelineMaterialRepositoryImpl, ciTemplateServiceImpl, ciTemplateOverrideRepositoryImpl, ciTemplateHistoryServiceImpl, enforcerUtilImpl, ciWorkflowRepositoryImpl, resourceGroupServiceImpl, customTagServiceImpl) ciMaterialConfigServiceImpl := pipeline.NewCiMaterialConfigServiceImpl(sugaredLogger, materialRepositoryImpl, ciTemplateServiceImpl, ciCdPipelineOrchestratorImpl, ciPipelineRepositoryImpl, gitMaterialHistoryServiceImpl, pipelineRepositoryImpl, ciPipelineMaterialRepositoryImpl) imageTaggingRepositoryImpl := repository13.NewImageTaggingRepositoryImpl(db) imageTaggingServiceImpl := pipeline.NewImageTaggingServiceImpl(imageTaggingRepositoryImpl, ciPipelineRepositoryImpl, pipelineRepositoryImpl, environmentRepositoryImpl, sugaredLogger) @@ -493,12 +495,12 @@ func InitializeApp() (*App, error) { devtronAppConfigServiceImpl := pipeline.NewDevtronAppConfigServiceImpl(sugaredLogger, ciCdPipelineOrchestratorImpl, appRepositoryImpl, pipelineRepositoryImpl, resourceGroupServiceImpl, enforcerUtilImpl, ciMaterialConfigServiceImpl) pipelineBuilderImpl := pipeline.NewPipelineBuilderImpl(sugaredLogger, materialRepositoryImpl, chartRepositoryImpl, ciPipelineConfigServiceImpl, ciMaterialConfigServiceImpl, appArtifactManagerImpl, devtronAppCMCSServiceImpl, devtronAppStrategyServiceImpl, appDeploymentTypeChangeManagerImpl, cdPipelineConfigServiceImpl, devtronAppConfigServiceImpl) dbMigrationServiceImpl := pipeline.NewDbMogrationService(sugaredLogger, dbMigrationConfigRepositoryImpl) - ciServiceImpl := pipeline.NewCiServiceImpl(sugaredLogger, workflowServiceImpl, ciPipelineMaterialRepositoryImpl, ciWorkflowRepositoryImpl, eventRESTClientImpl, eventSimpleFactoryImpl, mergeUtil, ciPipelineRepositoryImpl, prePostCiScriptHistoryServiceImpl, pipelineStageServiceImpl, userServiceImpl, ciTemplateServiceImpl, appCrudOperationServiceImpl, environmentRepositoryImpl, appRepositoryImpl, variableSnapshotHistoryServiceImpl) + ciServiceImpl := pipeline.NewCiServiceImpl(sugaredLogger, workflowServiceImpl, ciPipelineMaterialRepositoryImpl, ciWorkflowRepositoryImpl, eventRESTClientImpl, eventSimpleFactoryImpl, mergeUtil, ciPipelineRepositoryImpl, prePostCiScriptHistoryServiceImpl, pipelineStageServiceImpl, userServiceImpl, ciTemplateServiceImpl, appCrudOperationServiceImpl, environmentRepositoryImpl, appRepositoryImpl, variableSnapshotHistoryServiceImpl, customTagServiceImpl) ciLogServiceImpl, err := pipeline.NewCiLogServiceImpl(sugaredLogger, ciServiceImpl, k8sUtil) if err != nil { return nil, err } - ciHandlerImpl := pipeline.NewCiHandlerImpl(sugaredLogger, ciServiceImpl, ciPipelineMaterialRepositoryImpl, clientImpl, ciWorkflowRepositoryImpl, workflowServiceImpl, ciLogServiceImpl, ciArtifactRepositoryImpl, userServiceImpl, eventRESTClientImpl, eventSimpleFactoryImpl, ciPipelineRepositoryImpl, appListingRepositoryImpl, k8sUtil, pipelineRepositoryImpl, enforcerUtilImpl, resourceGroupServiceImpl, environmentRepositoryImpl, imageTaggingServiceImpl, k8sCommonServiceImpl) + ciHandlerImpl := pipeline.NewCiHandlerImpl(sugaredLogger, ciServiceImpl, ciPipelineMaterialRepositoryImpl, clientImpl, ciWorkflowRepositoryImpl, workflowServiceImpl, ciLogServiceImpl, ciArtifactRepositoryImpl, userServiceImpl, eventRESTClientImpl, eventSimpleFactoryImpl, ciPipelineRepositoryImpl, appListingRepositoryImpl, k8sUtil, pipelineRepositoryImpl, enforcerUtilImpl, resourceGroupServiceImpl, environmentRepositoryImpl, imageTaggingServiceImpl, appWorkflowRepositoryImpl, customTagServiceImpl, k8sCommonServiceImpl) gitRegistryConfigImpl := pipeline.NewGitRegistryConfigImpl(sugaredLogger, gitProviderRepositoryImpl, clientImpl) dockerRegistryConfigImpl := pipeline.NewDockerRegistryConfigImpl(sugaredLogger, helmAppServiceImpl, dockerArtifactStoreRepositoryImpl, dockerRegistryIpsConfigRepositoryImpl, ociRegistryConfigRepositoryImpl) appListingViewBuilderImpl := app2.NewAppListingViewBuilderImpl(sugaredLogger) @@ -556,7 +558,7 @@ func InitializeApp() (*App, error) { gitWebhookRepositoryImpl := repository.NewGitWebhookRepositoryImpl(db) gitWebhookServiceImpl := git.NewGitWebhookServiceImpl(sugaredLogger, ciHandlerImpl, gitWebhookRepositoryImpl) gitWebhookRestHandlerImpl := restHandler.NewGitWebhookRestHandlerImpl(sugaredLogger, gitWebhookServiceImpl) - webhookServiceImpl := pipeline.NewWebhookServiceImpl(ciArtifactRepositoryImpl, sugaredLogger, ciPipelineRepositoryImpl, appServiceImpl, eventRESTClientImpl, eventSimpleFactoryImpl, ciWorkflowRepositoryImpl, workflowDagExecutorImpl, ciHandlerImpl) + webhookServiceImpl := pipeline.NewWebhookServiceImpl(ciArtifactRepositoryImpl, sugaredLogger, ciPipelineRepositoryImpl, appServiceImpl, eventRESTClientImpl, eventSimpleFactoryImpl, ciWorkflowRepositoryImpl, customTagServiceImpl, workflowDagExecutorImpl, ciHandlerImpl) ciEventConfig, err := pubsub.GetCiEventConfig() if err != nil { return nil, err