diff --git a/api/restHandler/app/pipeline/configure/BuildPipelineRestHandler.go b/api/restHandler/app/pipeline/configure/BuildPipelineRestHandler.go index 1a1ec93c2b..32448e0abf 100644 --- a/api/restHandler/app/pipeline/configure/BuildPipelineRestHandler.go +++ b/api/restHandler/app/pipeline/configure/BuildPipelineRestHandler.go @@ -565,15 +565,12 @@ func (handler *PipelineConfigRestHandlerImpl) GetCiPipeline(w http.ResponseWrite common.WriteJsonResp(w, fmt.Errorf("unauthorized user"), "Unauthorized User", http.StatusForbidden) return } - ciConf, err := handler.pipelineBuilder.GetCiPipeline(appId) + ciConf, err := handler.pipelineBuilder.GetCiPipelineRespResolved(appId) if err != nil { - handler.Logger.Errorw("service err, GetCiPipeline", "err", err, "appId", appId) + handler.Logger.Errorw("service err, GetCiPipelineRespResolved", "appId", appId, "err", err) common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) return } - if ciConf == nil || ciConf.Id == 0 { - err = &util.ApiError{Code: "404", HttpStatusCode: 200, UserMessage: "no data found"} - } common.WriteJsonResp(w, err, ciConf, http.StatusOK) } @@ -1286,13 +1283,12 @@ func (handler *PipelineConfigRestHandlerImpl) GetCIPipelineById(w http.ResponseW } } - ciPipeline, err := handler.pipelineBuilder.GetCiPipelineById(pipelineId) + ciPipeline, err := handler.pipelineBuilder.GetCiPipelineByIdWithDefaultTag(pipelineId) if err != nil { - handler.Logger.Infow("service error, GetCIPipelineById", "err", err, "appId", appId, "pipelineId", pipelineId) + handler.Logger.Infow("service error, GetCiPipelineByIdWithDefaultTag", "err", err, "appId", appId, "pipelineId", pipelineId) 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/api/restHandler/app/pipeline/configure/DeploymentPipelineRestHandler.go b/api/restHandler/app/pipeline/configure/DeploymentPipelineRestHandler.go index 21793673aa..52728940d5 100644 --- a/api/restHandler/app/pipeline/configure/DeploymentPipelineRestHandler.go +++ b/api/restHandler/app/pipeline/configure/DeploymentPipelineRestHandler.go @@ -41,7 +41,6 @@ import ( "github.com/devtron-labs/devtron/pkg/auth/authorisation/casbin" "github.com/devtron-labs/devtron/pkg/bean" "github.com/devtron-labs/devtron/pkg/generateManifest" - "github.com/devtron-labs/devtron/pkg/pipeline" pipelineBean "github.com/devtron-labs/devtron/pkg/pipeline/bean" resourceGroup2 "github.com/devtron-labs/devtron/pkg/resourceGroup" "github.com/devtron-labs/devtron/pkg/resourceQualifiers" @@ -718,48 +717,22 @@ func (handler *PipelineConfigRestHandlerImpl) EnvConfigOverrideCreate(w http.Res return } - createResp, err := handler.propertiesConfigService.CreateEnvironmentProperties(appId, &envConfigProperties) - if err != nil { - if err.Error() == bean4.NOCHARTEXIST { - ctx, cancel := context.WithCancel(r.Context()) - if cn, ok := w.(http.CloseNotifier); ok { - go func(done <-chan struct{}, closed <-chan bool) { - select { - case <-done: - case <-closed: - cancel() - } - }(ctx.Done(), cn.CloseNotify()) - } - appMetrics := false - if envConfigProperties.AppMetrics != nil { - appMetrics = *envConfigProperties.AppMetrics - } - templateRequest := bean3.TemplateRequest{ - AppId: appId, - ChartRefId: envConfigProperties.ChartRefId, - ValuesOverride: []byte("{}"), - UserId: userId, - IsAppMetricsEnabled: appMetrics, + ctx, cancel := context.WithCancel(r.Context()) + if cn, ok := w.(http.CloseNotifier); ok { + go func(done <-chan struct{}, closed <-chan bool) { + select { + case <-done: + case <-closed: + cancel() } + }(ctx.Done(), cn.CloseNotify()) + } - _, err = handler.chartService.CreateChartFromEnvOverride(templateRequest, ctx) - if err != nil { - handler.Logger.Errorw("service err, EnvConfigOverrideCreate", "err", err, "payload", envConfigProperties) - common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) - return - } - createResp, err = handler.propertiesConfigService.CreateEnvironmentProperties(appId, &envConfigProperties) - if err != nil { - handler.Logger.Errorw("service err, EnvConfigOverrideCreate", "err", err, "payload", envConfigProperties) - common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) - return - } - } else { - handler.Logger.Errorw("service err, EnvConfigOverrideCreate", "err", err, "payload", envConfigProperties) - common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) - return - } + createResp, err := handler.propertiesConfigService.CreateEnvironmentPropertiesAndBaseIfNeeded(ctx, appId, &envConfigProperties) + if err != nil { + handler.Logger.Errorw("service err, CreateEnvironmentPropertiesAndBaseIfNeeded", "payload", envConfigProperties, "err", err) + common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) + return } common.WriteJsonResp(w, err, createResp, http.StatusOK) } @@ -1037,77 +1010,12 @@ func (handler *PipelineConfigRestHandlerImpl) GetDeploymentTemplate(w http.Respo common.WriteJsonResp(w, fmt.Errorf("unauthorized user"), "Unauthorized User", http.StatusForbidden) return } - - appConfigResponse := make(map[string]interface{}) - appConfigResponse["globalConfig"] = nil - - err = handler.chartRefService.CheckChartExists(chartRefId) + appConfigResponse, err := handler.chartService.GetDeploymentTemplateDataByAppIdAndCharRefId(appId, chartRefId) if err != nil { handler.Logger.Errorw("refChartDir Not Found err, JsonSchemaExtractFromFile", err) common.WriteJsonResp(w, err, nil, http.StatusForbidden) return } - - schema, readme, err := handler.chartRefService.GetSchemaAndReadmeForTemplateByChartRefId(chartRefId) - if err != nil { - handler.Logger.Errorw("err in getting schema and readme, GetDeploymentTemplate", "err", err, "appId", appId, "chartRefId", chartRefId) - } - - template, err := handler.chartReadService.FindLatestChartForAppByAppId(appId) - if err != nil && pg.ErrNoRows != err { - handler.Logger.Errorw("service err, GetDeploymentTemplate", "err", err, "appId", appId, "chartRefId", chartRefId) - common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) - return - } - - if pg.ErrNoRows == err { - appOverride, _, err := handler.chartRefService.GetAppOverrideForDefaultTemplate(chartRefId) - if err != nil { - handler.Logger.Errorw("service err, GetDeploymentTemplate", "err", err, "appId", appId, "chartRefId", chartRefId) - common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) - return - } - appOverride["schema"] = json.RawMessage(schema) - appOverride["readme"] = string(readme) - mapB, err := json.Marshal(appOverride) - if err != nil { - handler.Logger.Errorw("marshal err, GetDeploymentTemplate", "err", err, "appId", appId, "chartRefId", chartRefId) - return - } - appConfigResponse["globalConfig"] = json.RawMessage(mapB) - } else { - if template.ChartRefId != chartRefId { - templateRequested, err := handler.chartService.GetByAppIdAndChartRefId(appId, chartRefId) - if err != nil && err != pg.ErrNoRows { - handler.Logger.Errorw("service err, GetDeploymentTemplate", "err", err, "appId", appId, "chartRefId", chartRefId) - common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) - return - } - - if pg.ErrNoRows == err { - template.ChartRefId = chartRefId - template.Id = 0 - template.Latest = false - } else { - template.ChartRefId = templateRequested.ChartRefId - template.Id = templateRequested.Id - template.ChartRepositoryId = templateRequested.ChartRepositoryId - template.RefChartTemplate = templateRequested.RefChartTemplate - template.RefChartTemplateVersion = templateRequested.RefChartTemplateVersion - template.Latest = templateRequested.Latest - } - } - template.Schema = schema - template.Readme = string(readme) - bytes, err := json.Marshal(template) - if err != nil { - handler.Logger.Errorw("marshal err, GetDeploymentTemplate", "err", err, "appId", appId, "chartRefId", chartRefId) - return - } - appOverride := json.RawMessage(bytes) - appConfigResponse["globalConfig"] = appOverride - } - common.WriteJsonResp(w, nil, appConfigResponse, http.StatusOK) } @@ -1992,15 +1900,9 @@ func (handler *PipelineConfigRestHandlerImpl) GetCdPipelineById(w http.ResponseW return } - cdPipeline, err := handler.pipelineBuilder.GetCdPipelineById(pipelineId) - if err != nil { - handler.Logger.Errorw("service err, GetCdPipelineById", "err", err, "appId", appId, "pipelineId", pipelineId) - common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) - return - } - cdResp, err := pipeline.CreatePreAndPostStageResponse(cdPipeline, version) + cdResp, err := handler.pipelineBuilder.GetCdPipelineByIdResolved(pipelineId, version) if err != nil { - handler.Logger.Errorw("service err, CheckForVersionAndCreatePreAndPostStagePayload", "err", err, "appId", appId, "pipelineId", pipelineId) + handler.Logger.Errorw("service err, GetCdPipelineByIdResolved", "appId", appId, "pipelineId", pipelineId, "err", err) common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) return } diff --git a/api/restHandler/app/pipeline/configure/PipelineConfigRestHandler.go b/api/restHandler/app/pipeline/configure/PipelineConfigRestHandler.go index 10f899556c..5e3e81defd 100644 --- a/api/restHandler/app/pipeline/configure/PipelineConfigRestHandler.go +++ b/api/restHandler/app/pipeline/configure/PipelineConfigRestHandler.go @@ -35,6 +35,7 @@ import ( security2 "github.com/devtron-labs/devtron/pkg/policyGovernance/security/imageScanning" "github.com/devtron-labs/devtron/pkg/policyGovernance/security/imageScanning/read" read3 "github.com/devtron-labs/devtron/pkg/team/read" + "github.com/devtron-labs/devtron/util/beHelper" "io" "net/http" "strconv" @@ -60,7 +61,6 @@ import ( "github.com/devtron-labs/devtron/pkg/bean" "github.com/devtron-labs/devtron/pkg/pipeline" "github.com/devtron-labs/devtron/pkg/team" - util2 "github.com/devtron-labs/devtron/util" "github.com/devtron-labs/devtron/util/rbac" "github.com/gorilla/mux" "go.uber.org/zap" @@ -663,7 +663,7 @@ func (handler *PipelineConfigRestHandlerImpl) PipelineNameSuggestion(w http.Resp common.WriteJsonResp(w, err, nil, http.StatusBadRequest) return } - suggestedName := fmt.Sprintf("%s-%d-%s", pType, appId, util2.Generate(4)) + suggestedName := beHelper.GetPipelineNameByPipelineType(pType, appId) resourceName := handler.enforcerUtil.GetAppRBACName(app.AppName) ok := handler.enforcerUtil.CheckAppRbacForAppOrJob(token, resourceName, casbin.ActionGet) if !ok { diff --git a/api/restHandler/app/workflow/AppWorkflowRestHandler.go b/api/restHandler/app/workflow/AppWorkflowRestHandler.go index 79c62e025a..b4251d2630 100644 --- a/api/restHandler/app/workflow/AppWorkflowRestHandler.go +++ b/api/restHandler/app/workflow/AppWorkflowRestHandler.go @@ -188,13 +188,6 @@ func (impl AppWorkflowRestHandlerImpl) FindAppWorkflow(w http.ResponseWriter, r return } token := r.Header.Get("token") - app, err := impl.pipelineBuilder.GetApp(appId) - if err != nil { - impl.Logger.Errorw("bad request", "err", err) - common.WriteJsonResp(w, err, nil, http.StatusBadRequest) - return - } - v := r.URL.Query() envIdsString := v.Get("envIds") envIds := make([]int, 0) @@ -207,16 +200,15 @@ func (impl AppWorkflowRestHandlerImpl) FindAppWorkflow(w http.ResponseWriter, r } // RBAC enforcer applying - object := impl.enforcerUtil.GetAppRBACName(app.AppName) + object := impl.enforcerUtil.GetAppRBACNameByAppId(appId) impl.Logger.Debugw("rbac object for other environment list", "object", object) ok := impl.enforcerUtil.CheckAppRbacForAppOrJob(token, object, casbin.ActionGet) if !ok { common.WriteJsonResp(w, err, "unauthorized user", http.StatusForbidden) return } - // RBAC enforcer Ends - workflows := make(map[string]interface{}) - workflowsList, err := impl.appWorkflowService.FindAppWorkflows(appId) + //RBAC enforcer Ends + workflowsListResp, appType, err := impl.appWorkflowService.FindAppWorkflowsListResolvedResp(appId) if err != nil { impl.Logger.Errorw("error in fetching workflows for app", "err", err) common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) @@ -231,7 +223,7 @@ func (impl AppWorkflowRestHandlerImpl) FindAppWorkflow(w http.ResponseWriter, r return } triggerViewPayload := &bean2.TriggerViewWorkflowConfig{ - Workflows: workflowsList, + Workflows: workflowsListResp.Workflows, CdPipelines: cdPipelineWfData, } queryParam := bean2.NewWorkflowsFilterQuery().WithEnvIds(envIds) @@ -242,12 +234,10 @@ func (impl AppWorkflowRestHandlerImpl) FindAppWorkflow(w http.ResponseWriter, r common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) return } - workflowsList = response.Workflows + workflowsListResp.Workflows = response.Workflows } - workflows["appId"] = app.Id - workflows["appName"] = app.AppName - if len(workflowsList) > 0 && app.AppType == helper.Job { + if len(workflowsListResp.Workflows) > 0 && appType == helper.Job { // RBAC var workflowNames []string @@ -256,7 +246,7 @@ func (impl AppWorkflowRestHandlerImpl) FindAppWorkflow(w http.ResponseWriter, r var rbacObjects []string workNameObjectMap := make(map[string]bean2.AppWorkflowDto) - for _, workflow := range workflowsList { + for _, workflow := range workflowsListResp.Workflows { workflowNames = append(workflowNames, workflow.Name) workflowIds = append(workflowIds, workflow.Id) } @@ -264,7 +254,7 @@ func (impl AppWorkflowRestHandlerImpl) FindAppWorkflow(w http.ResponseWriter, r itr := 0 for _, val := range workflowIdToObjectMap { rbacObjects = append(rbacObjects, val) - workNameObjectMap[val] = workflowsList[itr] + workNameObjectMap[val] = workflowsListResp.Workflows[itr] itr++ } @@ -277,20 +267,8 @@ func (impl AppWorkflowRestHandlerImpl) FindAppWorkflow(w http.ResponseWriter, r if len(updatedWorkflowList) == 0 { updatedWorkflowList = []bean2.AppWorkflowDto{} } - workflows[bean3.Workflows] = updatedWorkflowList - } else if len(workflowsList) > 0 { - workflows[bean3.Workflows] = workflowsList - } else { - workflows[bean3.Workflows] = []bean2.AppWorkflowDto{} - } - isAppLevelGitOpsConfigured, err := impl.chartService.IsGitOpsRepoConfiguredForDevtronApp(appId) - if err != nil && !util.IsErrNoRows(err) { - impl.Logger.Errorw("service err, IsGitOpsRepoConfiguredForDevtronApp", "appId", appId, "envIds", envIds, "err", err) - common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) - return } - workflows["isGitOpsRepoNotConfigured"] = !isAppLevelGitOpsConfigured - common.WriteJsonResp(w, nil, workflows, http.StatusOK) + common.WriteJsonResp(w, nil, workflowsListResp, http.StatusOK) } func (impl AppWorkflowRestHandlerImpl) FindAllWorkflows(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/appWorkflow/AppWorkflowService.go b/pkg/appWorkflow/AppWorkflowService.go index 5121445a79..9e669b5b21 100644 --- a/pkg/appWorkflow/AppWorkflowService.go +++ b/pkg/appWorkflow/AppWorkflowService.go @@ -19,6 +19,7 @@ package appWorkflow import ( "errors" "fmt" + "github.com/devtron-labs/devtron/internal/sql/repository/helper" bean4 "github.com/devtron-labs/devtron/pkg/appWorkflow/bean" "github.com/devtron-labs/devtron/pkg/deployment/common" util2 "github.com/devtron-labs/devtron/util" @@ -46,6 +47,7 @@ import ( type AppWorkflowService interface { CreateAppWorkflow(req bean4.AppWorkflowDto) (bean4.AppWorkflowDto, error) + FindAppWorkflowsListResolvedResp(appId int) (resp bean4.AppWorkflowListRespDto, appType helper.AppType, err error) FindAppWorkflows(appId int) ([]bean4.AppWorkflowDto, error) FindAppWorkflowById(Id int, appId int) (bean4.AppWorkflowDto, error) DeleteAppWorkflow(appWorkflowId int, userId int32) error @@ -79,6 +81,7 @@ type AppWorkflowServiceImpl struct { userAuthService user.UserAuthService chartService chart.ChartService deploymentConfigService common.DeploymentConfigService + pipelineBuilder pipeline.PipelineBuilder } func NewAppWorkflowServiceImpl(logger *zap.SugaredLogger, appWorkflowRepository appWorkflow.AppWorkflowRepository, @@ -86,6 +89,7 @@ func NewAppWorkflowServiceImpl(logger *zap.SugaredLogger, appWorkflowRepository pipelineRepository pipelineConfig.PipelineRepository, enforcerUtil rbac.EnforcerUtil, resourceGroupService resourceGroup2.ResourceGroupService, appRepository appRepository.AppRepository, userAuthService user.UserAuthService, chartService chart.ChartService, deploymentConfigService common.DeploymentConfigService, + pipelineBuilder pipeline.PipelineBuilder, ) *AppWorkflowServiceImpl { return &AppWorkflowServiceImpl{ Logger: logger, @@ -99,6 +103,7 @@ func NewAppWorkflowServiceImpl(logger *zap.SugaredLogger, appWorkflowRepository userAuthService: userAuthService, chartService: chartService, deploymentConfigService: deploymentConfigService, + pipelineBuilder: pipelineBuilder, } } @@ -153,6 +158,34 @@ func (impl AppWorkflowServiceImpl) CreateAppWorkflow(req bean4.AppWorkflowDto) ( return req, nil } +func (impl AppWorkflowServiceImpl) FindAppWorkflowsListResolvedResp(appId int) (resp bean4.AppWorkflowListRespDto, appType helper.AppType, err error) { + app, err := impl.pipelineBuilder.GetApp(appId) + if err != nil { + impl.Logger.Errorw("error, GetApp", "appId", appId, "err", err) + return resp, appType, err + } + workflowsList, err := impl.FindAppWorkflows(appId) + if err != nil { + impl.Logger.Errorw("error in fetching workflows for app", "appId", appId, "err", err) + return resp, appType, err + } + isAppLevelGitOpsConfigured, err := impl.chartService.IsGitOpsRepoConfiguredForDevtronApp(appId) + if err != nil && !util.IsErrNoRows(err) { + impl.Logger.Errorw("service err, IsGitOpsRepoConfiguredForDevtronApp", "appId", appId, "err", err) + return resp, appType, err + } + resp.AppId = app.Id + resp.AppName = app.AppName + resp.Workflows = workflowsList + resp.IsGitOpsRepoNotConfigured = !isAppLevelGitOpsConfigured + if len(workflowsList) > 0 { + resp.Workflows = workflowsList + } else { + resp.Workflows = []bean4.AppWorkflowDto{} + } + return resp, app.AppType, nil +} + func (impl AppWorkflowServiceImpl) FindAppWorkflows(appId int) ([]bean4.AppWorkflowDto, error) { appWorkflows, err := impl.appWorkflowRepository.FindByAppId(appId) if err != nil && err != pg.ErrNoRows { diff --git a/pkg/appWorkflow/bean/bean.go b/pkg/appWorkflow/bean/bean.go index 9660e50ee8..655d01c9a3 100644 --- a/pkg/appWorkflow/bean/bean.go +++ b/pkg/appWorkflow/bean/bean.go @@ -55,6 +55,13 @@ type WorkflowComponents struct { CdPipelineIds []int } +type AppWorkflowListRespDto struct { + Workflows []AppWorkflowDto `json:"workflows"` + AppId int `json:"appId"` + AppName string `json:"appName"` + IsGitOpsRepoNotConfigured bool `json:"isGitOpsRepoNotConfigured"` +} + type AppWorkflowDto struct { Id int `json:"id,omitempty"` Name string `json:"name"` diff --git a/pkg/chart/ChartService.go b/pkg/chart/ChartService.go index 006bba7795..452f85dcfd 100644 --- a/pkg/chart/ChartService.go +++ b/pkg/chart/ChartService.go @@ -67,12 +67,15 @@ type ChartService interface { CheckIfChartRefUserUploadedByAppId(id int) (bool, error) PatchEnvOverrides(values json.RawMessage, oldChartType string, newChartType string) (json.RawMessage, error) + ChartRefAutocompleteGlobalData() (*chartRefBean.ChartRefAutocompleteResponse, error) ChartRefAutocompleteForAppOrEnv(appId int, envId int) (*chartRefBean.ChartRefAutocompleteResponse, error) ConfigureGitOpsRepoUrlForApp(appId int, repoUrl, chartLocation string, isCustomRepo bool, userId int32) (*bean2.DeploymentConfig, error) IsGitOpsRepoConfiguredForDevtronApp(appId int) (bool, error) IsGitOpsRepoAlreadyRegistered(gitOpsRepoUrl string) (bool, error) + + GetDeploymentTemplateDataByAppIdAndCharRefId(appId, chartRefId int) (map[string]interface{}, error) } type ChartServiceImpl struct { @@ -846,22 +849,45 @@ func (impl *ChartServiceImpl) IsReadyToTrigger(appId int, envId int, pipelineId } func (impl *ChartServiceImpl) ChartRefAutocompleteForAppOrEnv(appId int, envId int) (*chartRefBean.ChartRefAutocompleteResponse, error) { - chartRefResponse := &chartRefBean.ChartRefAutocompleteResponse{} - var chartRefs []chartRefBean.ChartRefAutocompleteDto + chartRefResponse, err := impl.ChartRefAutocompleteGlobalData() + if err != nil { + impl.logger.Errorw("error, ChartRefAutocompleteGlobalData", "err", err) + return nil, err + } + chart, err := impl.chartRepository.FindLatestChartForAppByAppId(appId) + if err != nil && err != pg.ErrNoRows { + impl.logger.Errorw("error in fetching latest chart", "err", err) + return chartRefResponse, err + } + chartRefResponse.LatestAppChartRef = chart.ChartRefId + if envId > 0 { + envOverride, err := impl.envConfigOverrideReadService.FindLatestChartForAppByAppIdAndEnvId(appId, envId) + if err != nil && !errors.IsNotFound(err) { + impl.logger.Errorw("error in fetching latest chart", "err", err) + return chartRefResponse, err + } + if envOverride != nil && envOverride.Chart != nil { + chartRefResponse.LatestEnvChartRef = envOverride.Chart.ChartRefId + } else { + chartRefResponse.LatestEnvChartRef = chart.ChartRefId + } + } + return chartRefResponse, nil +} +func (impl *ChartServiceImpl) ChartRefAutocompleteGlobalData() (*chartRefBean.ChartRefAutocompleteResponse, error) { results, err := impl.chartRefService.GetAll() if err != nil { impl.logger.Errorw("error in fetching chart ref", "err", err) - return chartRefResponse, err + return nil, err } - resultsMetadataMap, err := impl.chartRefService.GetAllChartMetadata() if err != nil { impl.logger.Errorw("error in fetching chart metadata", "err", err) - return chartRefResponse, err + return nil, err } - chartRefResponse.ChartsMetadata = resultsMetadataMap - var LatestAppChartRef int + var latestChartRef int + chartRefs := make([]chartRefBean.ChartRefAutocompleteDto, 0, len(results)) for _, result := range results { chartRefs = append(chartRefs, chartRefBean.ChartRefAutocompleteDto{ Id: result.Id, @@ -872,32 +898,14 @@ func (impl *ChartServiceImpl) ChartRefAutocompleteForAppOrEnv(appId int, envId i IsAppMetricsSupported: result.IsAppMetricsSupported, }) if result.Default == true { - LatestAppChartRef = result.Id - } - } - - chart, err := impl.chartRepository.FindLatestChartForAppByAppId(appId) - if err != nil && err != pg.ErrNoRows { - impl.logger.Errorw("error in fetching latest chart", "err", err) - return chartRefResponse, err - } - - if envId > 0 { - envOverride, err := impl.envConfigOverrideReadService.FindLatestChartForAppByAppIdAndEnvId(appId, envId) - if err != nil && !errors.IsNotFound(err) { - impl.logger.Errorw("error in fetching latest chart", "err", err) - return chartRefResponse, err - } - if envOverride != nil && envOverride.Chart != nil { - chartRefResponse.LatestEnvChartRef = envOverride.Chart.ChartRefId - } else { - chartRefResponse.LatestEnvChartRef = chart.ChartRefId + latestChartRef = result.Id } } - chartRefResponse.LatestAppChartRef = chart.ChartRefId - chartRefResponse.ChartRefs = chartRefs - chartRefResponse.LatestChartRef = LatestAppChartRef - return chartRefResponse, nil + return &chartRefBean.ChartRefAutocompleteResponse{ + ChartsMetadata: resultsMetadataMap, + ChartRefs: chartRefs, + LatestChartRef: latestChartRef, + }, nil } func (impl *ChartServiceImpl) FindPreviousChartByAppId(appId int) (chartTemplate *bean3.TemplateRequest, err error) { @@ -1117,3 +1125,72 @@ func (impl *ChartServiceImpl) IsGitOpsRepoAlreadyRegistered(gitOpsRepoUrl string impl.logger.Errorw("repository is already in use for devtron app", "repoUrl", gitOpsRepoUrl, "appId", chartModel.AppId) return true, nil } + +func (impl *ChartServiceImpl) GetDeploymentTemplateDataByAppIdAndCharRefId(appId, chartRefId int) (map[string]interface{}, error) { + appConfigResponse := make(map[string]interface{}) + appConfigResponse["globalConfig"] = nil + + err := impl.chartRefService.CheckChartExists(chartRefId) + if err != nil { + impl.logger.Errorw("refChartDir Not Found err, JsonSchemaExtractFromFile", err) + return nil, err + } + + schema, readme, err := impl.chartRefService.GetSchemaAndReadmeForTemplateByChartRefId(chartRefId) + if err != nil { + impl.logger.Errorw("err in getting schema and readme, GetDeploymentTemplate", "err", err, "appId", appId, "chartRefId", chartRefId) + } + + template, err := impl.chartReadService.FindLatestChartForAppByAppId(appId) + if err != nil && pg.ErrNoRows != err { + impl.logger.Errorw("service err, GetDeploymentTemplate", "err", err, "appId", appId, "chartRefId", chartRefId) + return nil, err + } + + if pg.ErrNoRows == err { + appOverride, _, err := impl.chartRefService.GetAppOverrideForDefaultTemplate(chartRefId) + if err != nil { + impl.logger.Errorw("service err, GetDeploymentTemplate", "err", err, "appId", appId, "chartRefId", chartRefId) + return nil, err + } + appOverride["schema"] = json.RawMessage(schema) + appOverride["readme"] = string(readme) + mapB, err := json.Marshal(appOverride) + if err != nil { + impl.logger.Errorw("marshal err, GetDeploymentTemplate", "err", err, "appId", appId, "chartRefId", chartRefId) + return nil, err + } + appConfigResponse["globalConfig"] = json.RawMessage(mapB) + } else { + if template.ChartRefId != chartRefId { + templateRequested, err := impl.GetByAppIdAndChartRefId(appId, chartRefId) + if err != nil && err != pg.ErrNoRows { + impl.logger.Errorw("service err, GetDeploymentTemplate", "err", err, "appId", appId, "chartRefId", chartRefId) + return nil, err + } + + if pg.ErrNoRows == err { + template.ChartRefId = chartRefId + template.Id = 0 + template.Latest = false + } else { + template.ChartRefId = templateRequested.ChartRefId + template.Id = templateRequested.Id + template.ChartRepositoryId = templateRequested.ChartRepositoryId + template.RefChartTemplate = templateRequested.RefChartTemplate + template.RefChartTemplateVersion = templateRequested.RefChartTemplateVersion + template.Latest = templateRequested.Latest + } + } + template.Schema = schema + template.Readme = string(readme) + bytes, err := json.Marshal(template) + if err != nil { + impl.logger.Errorw("marshal err, GetDeploymentTemplate", "err", err, "appId", appId, "chartRefId", chartRefId) + return nil, err + } + appOverride := json.RawMessage(bytes) + appConfigResponse["globalConfig"] = appOverride + } + return appConfigResponse, nil +} diff --git a/pkg/config/configDiff/DeploymentConfigurationService.go b/pkg/config/configDiff/DeploymentConfigurationService.go index b8168bfc60..ebc63c47f3 100644 --- a/pkg/config/configDiff/DeploymentConfigurationService.go +++ b/pkg/config/configDiff/DeploymentConfigurationService.go @@ -149,6 +149,12 @@ func (impl *DeploymentConfigurationServiceImpl) ConfigAutoComplete(appId int, en impl.logger.Errorw("error in fetching CM and CS names at app or env level", "appId", appId, "envId", envId, "err", err) return nil, err } + combinedProperties := GetConfigAutoCompleteCombinedPropertiesDataWithoutDraft(cMCSNamesAppLevel, cMCSNamesEnvLevel, envId) + configDataResp := bean2.NewConfigDataResponse().WithResourceConfig(combinedProperties) + return configDataResp, nil +} + +func GetConfigAutoCompleteCombinedPropertiesDataWithoutDraft(cMCSNamesAppLevel, cMCSNamesEnvLevel []bean.ConfigNameAndType, envId int) (combinedProperties []*bean2.ConfigProperty) { cmcsKeyPropertyAppLevelMap, cmcsKeyPropertyEnvLevelMap := adaptor.GetCmCsAppAndEnvLevelMap(cMCSNamesAppLevel, cMCSNamesEnvLevel) for key, configProperty := range cmcsKeyPropertyAppLevelMap { if _, ok := cmcsKeyPropertyEnvLevelMap[key]; !ok { @@ -166,11 +172,9 @@ func (impl *DeploymentConfigurationServiceImpl) ConfigAutoComplete(appId int, en configProperty.ConfigStage = bean2.Env } } - combinedProperties := helper.GetCombinedPropertiesMap(cmcsKeyPropertyAppLevelMap, cmcsKeyPropertyEnvLevelMap) + combinedProperties = helper.GetCombinedPropertiesMap(cmcsKeyPropertyAppLevelMap, cmcsKeyPropertyEnvLevelMap) combinedProperties = append(combinedProperties, adaptor.GetConfigProperty(0, "", bean.DeploymentTemplate, bean2.PublishedConfigState)) - - configDataResp := bean2.NewConfigDataResponse().WithResourceConfig(combinedProperties) - return configDataResp, nil + return combinedProperties } func (impl *DeploymentConfigurationServiceImpl) GetAllConfigData(ctx context.Context, configDataQueryParams *bean2.ConfigDataQueryParams, userHasAdminAccess bool) (*bean2.DeploymentAndCmCsConfigDto, error) { diff --git a/pkg/pipeline/BuildPipelineConfigService.go b/pkg/pipeline/BuildPipelineConfigService.go index 2c56acfc47..fca115326e 100644 --- a/pkg/pipeline/BuildPipelineConfigService.go +++ b/pkg/pipeline/BuildPipelineConfigService.go @@ -55,12 +55,16 @@ import ( ) type CiPipelineConfigService interface { + // GetCiPipelineRespResolved : gets the ci pipeline get response after resolving empty data response as expected by FE + GetCiPipelineRespResolved(appId int) (*bean.CiConfigRequest, error) //GetCiPipeline : retrieves CI pipeline configuration (CiConfigRequest) for a specific application (appId). // It fetches CI pipeline data, including pipeline materials, scripts, and associated configurations. // It returns a detailed CiConfigRequest. //If any errors occur during the retrieval process CI pipeline configuration remains nil. //If you want less detail of ciPipeline ,Please refer GetCiPipelineMin GetCiPipeline(appId int) (ciConfig *bean.CiConfigRequest, err error) + // GetCiPipelineByIdWithDefaultTag : Retrieve ciPipeline for given ciPipelineId with defaultTagData + GetCiPipelineByIdWithDefaultTag(pipelineId int) (ciPipeline *bean.CiPipeline, err error) //GetCiPipelineById : Retrieve ciPipeline for given ciPipelineId GetCiPipelineById(pipelineId int) (ciPipeline *bean.CiPipeline, err error) //GetTriggerViewCiPipeline : retrieves a detailed view of the CI pipelines configured for a specific application (appId). @@ -511,6 +515,17 @@ func (impl *CiPipelineConfigServiceImpl) getCiTemplateVariables(appId int) (ciCo return ciConfig, err } +func (impl *CiPipelineConfigServiceImpl) GetCiPipelineRespResolved(appId int) (*bean.CiConfigRequest, error) { + ciConf, err := impl.GetCiPipeline(appId) + if err != nil { + return nil, err + } + if ciConf == nil || ciConf.Id == 0 { + err = &util.ApiError{Code: "404", HttpStatusCode: 200, UserMessage: "no data found"} + } + return ciConf, err +} + func (impl *CiPipelineConfigServiceImpl) GetCiPipeline(appId int) (ciConfig *bean.CiConfigRequest, err error) { ciConfig, err = impl.getCiTemplateVariables(appId) if err != nil { @@ -675,6 +690,16 @@ func (impl *CiPipelineConfigServiceImpl) GetCiPipeline(appId int) (ciConfig *bea return ciConfig, err } +func (impl *CiPipelineConfigServiceImpl) GetCiPipelineByIdWithDefaultTag(pipelineId int) (ciPipeline *bean.CiPipeline, err error) { + ciPipeline, err = impl.GetCiPipelineById(pipelineId) + if err != nil { + impl.logger.Infow("service error, GetCIPipelineById", "pipelineId", pipelineId, "err", err) + return nil, err + } + ciPipeline.DefaultTag = []string{"{git_hash}", "{ci_pipeline_id}", "{global_counter}"} + return ciPipeline, nil +} + func (impl *CiPipelineConfigServiceImpl) GetCiPipelineById(pipelineId int) (ciPipeline *bean.CiPipeline, err error) { pipeline, err := impl.ciPipelineRepository.FindById(pipelineId) if err != nil && !util.IsErrNoRows(err) { diff --git a/pkg/pipeline/CiCdPipelineOrchestrator.go b/pkg/pipeline/CiCdPipelineOrchestrator.go index d5ce8ec8fc..6be1e14ab0 100644 --- a/pkg/pipeline/CiCdPipelineOrchestrator.go +++ b/pkg/pipeline/CiCdPipelineOrchestrator.go @@ -40,6 +40,7 @@ import ( constants3 "github.com/devtron-labs/devtron/pkg/pipeline/constants" util4 "github.com/devtron-labs/devtron/pkg/pipeline/util" "github.com/devtron-labs/devtron/pkg/plugin" + "github.com/devtron-labs/devtron/util/beHelper" "github.com/devtron-labs/devtron/util/sliceUtil" "golang.org/x/exp/slices" "net/http" @@ -2288,7 +2289,7 @@ func (impl CiCdPipelineOrchestratorImpl) AddPipelineToTemplate(createRequest *be if createRequest.AppWorkflowId == 0 { // create workflow wf := &appWorkflow.AppWorkflow{ - Name: fmt.Sprintf("wf-%d-%s", createRequest.AppId, util2.Generate(4)), + Name: beHelper.GetAppWorkflowName(createRequest.AppId), AppId: createRequest.AppId, Active: true, AuditLog: sql.AuditLog{ diff --git a/pkg/pipeline/DeploymentPipelineConfigService.go b/pkg/pipeline/DeploymentPipelineConfigService.go index 3de2179b22..82347de4fc 100644 --- a/pkg/pipeline/DeploymentPipelineConfigService.go +++ b/pkg/pipeline/DeploymentPipelineConfigService.go @@ -81,6 +81,7 @@ import ( "github.com/devtron-labs/devtron/pkg/variables" repository3 "github.com/devtron-labs/devtron/pkg/variables/repository" globalUtil "github.com/devtron-labs/devtron/util" + "github.com/devtron-labs/devtron/util/beHelper" "github.com/devtron-labs/devtron/util/rbac" "github.com/go-pg/pg" errors2 "github.com/juju/errors" @@ -97,6 +98,8 @@ import ( ) type CdPipelineConfigService interface { + // GetCdPipelineByIdResolved : Retrieve cdPipeline for given cdPipelineId and update response as per version(change of pre/post stage data) + GetCdPipelineByIdResolved(pipelineId int, version string) (cdPipeline *bean.CDPipelineConfigObject, err error) // GetCdPipelineById : Retrieve cdPipeline for given cdPipelineId. // getting cdPipeline,environment and strategies ,preDeployStage, postDeployStage,appWorkflowMapping from respective repository and service layer // converting above data in proper bean object and then assigning to CDPipelineConfigObject @@ -274,6 +277,20 @@ func NewCdPipelineConfigServiceImpl(logger *zap.SugaredLogger, pipelineRepositor } } +func (impl *CdPipelineConfigServiceImpl) GetCdPipelineByIdResolved(pipelineId int, version string) (cdPipeline *bean.CDPipelineConfigObject, err error) { + cdPipeline, err = impl.GetCdPipelineById(pipelineId) + if err != nil { + impl.logger.Errorw("service err, GetCdPipelineById", "pipelineId", pipelineId, "err", err) + return + } + cdResp, err := CreatePreAndPostStageResponse(cdPipeline, version) + if err != nil { + impl.logger.Errorw("service err, CheckForVersionAndCreatePreAndPostStagePayload", "pipelineId", pipelineId, "err", err) + return + } + return cdResp, nil +} + func (impl *CdPipelineConfigServiceImpl) GetCdPipelineById(pipelineId int) (cdPipeline *bean.CDPipelineConfigObject, err error) { dbPipeline, err := impl.pipelineRepository.FindById(pipelineId) if err != nil && errors.IsNotFound(err) { @@ -2228,7 +2245,7 @@ func (impl *CdPipelineConfigServiceImpl) createCdPipeline(ctx context.Context, a if (pipeline.AppWorkflowId == 0 || pipeline.IsSwitchCiPipelineRequest()) && pipeline.ParentPipelineType == "WEBHOOK" { if pipeline.AppWorkflowId == 0 { wf := &appWorkflow.AppWorkflow{ - Name: fmt.Sprintf("wf-%d-%s", app.Id, globalUtil.Generate(4)), + Name: beHelper.GetAppWorkflowName(app.Id), AppId: app.Id, Active: true, AuditLog: sql.AuditLog{CreatedBy: userId, CreatedOn: time.Now(), UpdatedOn: time.Now(), UpdatedBy: userId}, diff --git a/pkg/pipeline/PropertiesConfig.go b/pkg/pipeline/PropertiesConfig.go index 4d37024830..ec3dc93945 100644 --- a/pkg/pipeline/PropertiesConfig.go +++ b/pkg/pipeline/PropertiesConfig.go @@ -22,6 +22,9 @@ import ( errors2 "errors" "fmt" "github.com/devtron-labs/devtron/internal/util" + bean5 "github.com/devtron-labs/devtron/pkg/auth/user/bean" + chartService "github.com/devtron-labs/devtron/pkg/chart" + bean3 "github.com/devtron-labs/devtron/pkg/chart/bean" "github.com/devtron-labs/devtron/pkg/cluster/environment/repository" "github.com/devtron-labs/devtron/pkg/deployment/common" "github.com/devtron-labs/devtron/pkg/deployment/manifest/deployedAppMetrics" @@ -47,6 +50,7 @@ import ( ) type PropertiesConfigService interface { + CreateEnvironmentPropertiesAndBaseIfNeeded(ctx context.Context, appId int, environmentProperties *bean.EnvironmentProperties) (*bean.EnvironmentProperties, error) CreateEnvironmentProperties(appId int, propertiesRequest *bean.EnvironmentProperties) (*bean.EnvironmentProperties, error) UpdateEnvironmentProperties(appId int, propertiesRequest *bean.EnvironmentProperties, userId int32) (*bean.EnvironmentProperties, error) //create environment entry for each new environment @@ -71,6 +75,7 @@ type PropertiesConfigServiceImpl struct { deployedAppMetricsService deployedAppMetrics.DeployedAppMetricsService envConfigOverrideReadService read.EnvConfigOverrideService deploymentConfigService common.DeploymentConfigService + chartService chartService.ChartService } func NewPropertiesConfigServiceImpl(logger *zap.SugaredLogger, @@ -81,7 +86,8 @@ func NewPropertiesConfigServiceImpl(logger *zap.SugaredLogger, scopedVariableManager variables.ScopedVariableManager, deployedAppMetricsService deployedAppMetrics.DeployedAppMetricsService, envConfigOverrideReadService read.EnvConfigOverrideService, - deploymentConfigService common.DeploymentConfigService) *PropertiesConfigServiceImpl { + deploymentConfigService common.DeploymentConfigService, + chartService chartService.ChartService) *PropertiesConfigServiceImpl { return &PropertiesConfigServiceImpl{ logger: logger, envConfigRepo: envConfigRepo, @@ -92,6 +98,7 @@ func NewPropertiesConfigServiceImpl(logger *zap.SugaredLogger, deployedAppMetricsService: deployedAppMetricsService, envConfigOverrideReadService: envConfigOverrideReadService, deploymentConfigService: deploymentConfigService, + chartService: chartService, } } @@ -214,6 +221,39 @@ func (impl PropertiesConfigServiceImpl) FetchEnvProperties(appId, envId, chartRe return impl.envConfigOverrideReadService.GetByAppIdEnvIdAndChartRefId(appId, envId, chartRefId) } +func (impl PropertiesConfigServiceImpl) CreateEnvironmentPropertiesAndBaseIfNeeded(ctx context.Context, appId int, environmentProperties *bean.EnvironmentProperties) (*bean.EnvironmentProperties, error) { + createResp, err := impl.CreateEnvironmentProperties(appId, environmentProperties) + if err != nil { + if err.Error() == bean5.NOCHARTEXIST { + appMetrics := false + if environmentProperties.AppMetrics != nil { + appMetrics = *environmentProperties.AppMetrics + } + templateRequest := bean3.TemplateRequest{ + AppId: appId, + ChartRefId: environmentProperties.ChartRefId, + ValuesOverride: []byte("{}"), + UserId: environmentProperties.UserId, + IsAppMetricsEnabled: appMetrics, + } + _, err = impl.chartService.CreateChartFromEnvOverride(templateRequest, ctx) + if err != nil { + impl.logger.Errorw("service err, EnvConfigOverrideCreate", "err", err, "payload", environmentProperties) + return nil, err + } + createResp, err = impl.CreateEnvironmentProperties(appId, environmentProperties) + if err != nil { + impl.logger.Errorw("service err, EnvConfigOverrideCreate", "err", err, "payload", environmentProperties) + return nil, err + } + } else { + impl.logger.Errorw("service err, EnvConfigOverrideCreate", "err", err, "payload", environmentProperties) + return nil, err + } + } + return createResp, nil +} + func (impl PropertiesConfigServiceImpl) CreateEnvironmentProperties(appId int, environmentProperties *bean.EnvironmentProperties) (*bean.EnvironmentProperties, error) { chart, err := impl.chartRepo.FindChartByAppIdAndRefId(appId, environmentProperties.ChartRefId) if err != nil && !errors2.Is(err, pg.ErrNoRows) { diff --git a/util/beHelper/nameSuggestionUtil.go b/util/beHelper/nameSuggestionUtil.go new file mode 100644 index 0000000000..b4758f5fb1 --- /dev/null +++ b/util/beHelper/nameSuggestionUtil.go @@ -0,0 +1,23 @@ +package beHelper + +import ( + "fmt" + "github.com/devtron-labs/common-lib/git-manager/util" + util2 "github.com/devtron-labs/devtron/util" +) + +func GetCIPipelineName(appId int) string { + return fmt.Sprintf("ci-%d-%s", appId, util2.Generate(4)) +} + +func GetCDPipelineName(appId int) string { + return fmt.Sprintf("cd-%d-%s", appId, util2.Generate(4)) +} + +func GetAppWorkflowName(appId int) string { + return fmt.Sprintf("wf-%d-%s", appId, util2.Generate(4)) +} + +func GetPipelineNameByPipelineType(pipelineType string, appId int) string { + return fmt.Sprintf("%s-%d-%s", pipelineType, appId, util.Generate(4)) +}