diff --git a/api/restHandler/DeploymentConfigurationRestHandler.go b/api/restHandler/DeploymentConfigurationRestHandler.go index 144838da01..7a9f467629 100644 --- a/api/restHandler/DeploymentConfigurationRestHandler.go +++ b/api/restHandler/DeploymentConfigurationRestHandler.go @@ -1,17 +1,20 @@ package restHandler import ( + "context" "fmt" "github.com/devtron-labs/devtron/api/restHandler/common" "github.com/devtron-labs/devtron/pkg/auth/authorisation/casbin" "github.com/devtron-labs/devtron/pkg/auth/user" "github.com/devtron-labs/devtron/pkg/configDiff" "github.com/devtron-labs/devtron/pkg/configDiff/bean" + util2 "github.com/devtron-labs/devtron/util" "github.com/devtron-labs/devtron/util/rbac" "github.com/gorilla/schema" "go.uber.org/zap" "gopkg.in/go-playground/validator.v9" "net/http" + "time" ) type DeploymentConfigurationRestHandler interface { @@ -88,6 +91,7 @@ func (handler *DeploymentConfigurationRestHandlerImpl) GetConfigData(w http.Resp return } + configDataQueryParams.UserId = userId //RBAC START token := r.Header.Get(common.TokenHeaderKey) object := handler.enforcerUtil.GetAppRBACName(configDataQueryParams.AppName) @@ -97,8 +101,12 @@ func (handler *DeploymentConfigurationRestHandlerImpl) GetConfigData(w http.Resp return } //RBAC END - - res, err := handler.deploymentConfigurationService.GetAllConfigData(r.Context(), configDataQueryParams) + isSuperAdmin := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionGet, "*") + userHasAdminAccess := handler.enforcer.Enforce(token, casbin.ResourceApplications, casbin.ActionUpdate, object) + ctx, cancel := context.WithTimeout(r.Context(), 60*time.Second) + defer cancel() + ctx = util2.SetSuperAdminInContext(ctx, isSuperAdmin) + res, err := handler.deploymentConfigurationService.GetAllConfigData(ctx, configDataQueryParams, userHasAdminAccess) if err != nil { handler.logger.Errorw("service err, GetAllConfigData ", "err", err) common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) diff --git a/internal/sql/repository/DeploymentTemplateRepository.go b/internal/sql/repository/DeploymentTemplateRepository.go index 00116eb20b..053e7c8395 100644 --- a/internal/sql/repository/DeploymentTemplateRepository.go +++ b/internal/sql/repository/DeploymentTemplateRepository.go @@ -38,6 +38,7 @@ type DeploymentTemplateComparisonMetadata struct { EnvironmentId int `json:"environmentId,omitempty"` EnvironmentName string `json:"environmentName,omitempty"` DeploymentTemplateHistoryId int `json:"deploymentTemplateHistoryId,omitempty"` + WfrId int `json:"wfrId,omitempty"` StartedOn *time.Time `json:"startedOn,omitempty"` FinishedOn *time.Time `json:"finishedOn,omitempty"` Status string `json:"status,omitempty"` @@ -69,7 +70,7 @@ func (impl DeploymentTemplateRepositoryImpl) FetchDeploymentHistoryWithChartRefs limit := 15 query := "select p.id as pipeline_id, dth.id as deployment_template_history_id," + - " wfr.finished_on, wfr.status, c.chart_ref_id, c.chart_version FROM cd_workflow_runner wfr" + + " wfr.id as wfr_id, wfr.finished_on, wfr.status, c.chart_ref_id, c.chart_version FROM cd_workflow_runner wfr" + " JOIN cd_workflow wf ON wf.id = wfr.cd_workflow_id JOIN pipeline p ON p.id = wf.pipeline_id" + " JOIN deployment_template_history dth ON dth.deployed_on = wfr.started_on " + "JOIN pipeline_config_override pco ON pco.cd_workflow_id = wf.id " + diff --git a/pkg/bean/configSecretData.go b/pkg/bean/configSecretData.go index d172859dc2..c945080e6f 100644 --- a/pkg/bean/configSecretData.go +++ b/pkg/bean/configSecretData.go @@ -30,6 +30,8 @@ type SecretList struct { ConfigData []*ConfigData `json:"secrets"` } +// there is an adapter written in pkg/bean folder to convert below ConfigData struct to pipeline/bean's ConfigData + // TODO refactoring: duplicate struct of ConfigData in ConfigMapBean.go type ConfigData struct { Name string `json:"name"` @@ -49,6 +51,7 @@ type ConfigData struct { SubPath bool `json:"subPath"` ESOSubPath []string `json:"esoSubPath"` FilePermission string `json:"filePermission"` + Overridden bool `json:"overridden"` } func (c *ConfigData) IsESOExternalSecretType() bool { diff --git a/pkg/cluster/repository/EnvironmentRepository.go b/pkg/cluster/repository/EnvironmentRepository.go index 048aa83ae7..15e806e2ca 100644 --- a/pkg/cluster/repository/EnvironmentRepository.go +++ b/pkg/cluster/repository/EnvironmentRepository.go @@ -80,6 +80,7 @@ type EnvironmentRepository interface { FindAllActiveWithFilter() ([]*Environment, error) FindEnvClusterInfosByIds([]int) ([]*EnvCluserInfo, error) FindEnvLinkedWithCiPipelines(externalCi bool, ciPipelineIds []int) ([]*Environment, error) + FindEnvByNameWithClusterDetails(envName string) (*Environment, error) } func NewEnvironmentRepositoryImpl(dbConnection *pg.DB, logger *zap.SugaredLogger, appStatusRepository appStatus.AppStatusRepository) *EnvironmentRepositoryImpl { @@ -160,6 +161,17 @@ func (repositoryImpl EnvironmentRepositoryImpl) FindByName(name string) (*Enviro return environment, err } +func (repositoryImpl EnvironmentRepositoryImpl) FindEnvByNameWithClusterDetails(envName string) (*Environment, error) { + environment := &Environment{} + err := repositoryImpl.dbConnection. + Model(environment). + Column("environment.*", "Cluster"). + Where("environment.environment_name = ?", envName). + Where("environment.active = ?", true). + Select() + return environment, err +} + func (repositoryImpl EnvironmentRepositoryImpl) FindIdByName(name string) (int, error) { environment := &Environment{} err := repositoryImpl.dbConnection. diff --git a/pkg/configDiff/DeploymentConfigurationService.go b/pkg/configDiff/DeploymentConfigurationService.go index f64de5cd2f..4e0f6b166f 100644 --- a/pkg/configDiff/DeploymentConfigurationService.go +++ b/pkg/configDiff/DeploymentConfigurationService.go @@ -5,16 +5,30 @@ import ( "encoding/json" repository2 "github.com/devtron-labs/devtron/internal/sql/repository" appRepository "github.com/devtron-labs/devtron/internal/sql/repository/app" + "github.com/devtron-labs/devtron/internal/sql/repository/chartConfig" + "github.com/devtron-labs/devtron/internal/sql/repository/pipelineConfig" "github.com/devtron-labs/devtron/internal/util" + bean3 "github.com/devtron-labs/devtron/pkg/bean" chartService "github.com/devtron-labs/devtron/pkg/chart" "github.com/devtron-labs/devtron/pkg/cluster/repository" "github.com/devtron-labs/devtron/pkg/configDiff/adaptor" bean2 "github.com/devtron-labs/devtron/pkg/configDiff/bean" "github.com/devtron-labs/devtron/pkg/configDiff/helper" "github.com/devtron-labs/devtron/pkg/configDiff/utils" + "github.com/devtron-labs/devtron/pkg/deployment/manifest/deploymentTemplate/chartRef" "github.com/devtron-labs/devtron/pkg/generateManifest" "github.com/devtron-labs/devtron/pkg/pipeline" + "github.com/devtron-labs/devtron/pkg/pipeline/adapter" "github.com/devtron-labs/devtron/pkg/pipeline/bean" + "github.com/devtron-labs/devtron/pkg/pipeline/history" + repository3 "github.com/devtron-labs/devtron/pkg/pipeline/history/repository" + "github.com/devtron-labs/devtron/pkg/resourceQualifiers" + "github.com/devtron-labs/devtron/pkg/variables" + "github.com/devtron-labs/devtron/pkg/variables/parsers" + repository6 "github.com/devtron-labs/devtron/pkg/variables/repository" + util2 "github.com/devtron-labs/devtron/util" + "github.com/go-pg/pg" + "github.com/juju/errors" "go.uber.org/zap" "net/http" "strconv" @@ -22,16 +36,26 @@ import ( type DeploymentConfigurationService interface { ConfigAutoComplete(appId int, envId int) (*bean2.ConfigDataResponse, error) - GetAllConfigData(ctx context.Context, configDataQueryParams *bean2.ConfigDataQueryParams) (*bean2.DeploymentAndCmCsConfigDto, error) + GetAllConfigData(ctx context.Context, configDataQueryParams *bean2.ConfigDataQueryParams, userHasAdminAccess bool) (*bean2.DeploymentAndCmCsConfigDto, error) } type DeploymentConfigurationServiceImpl struct { - logger *zap.SugaredLogger - configMapService pipeline.ConfigMapService - appRepository appRepository.AppRepository - environmentRepository repository.EnvironmentRepository - chartService chartService.ChartService - deploymentTemplateService generateManifest.DeploymentTemplateService + logger *zap.SugaredLogger + configMapService pipeline.ConfigMapService + appRepository appRepository.AppRepository + environmentRepository repository.EnvironmentRepository + chartService chartService.ChartService + deploymentTemplateService generateManifest.DeploymentTemplateService + deploymentTemplateHistoryRepository repository3.DeploymentTemplateHistoryRepository + pipelineStrategyHistoryRepository repository3.PipelineStrategyHistoryRepository + configMapHistoryRepository repository3.ConfigMapHistoryRepository + scopedVariableManager variables.ScopedVariableCMCSManager + configMapRepository chartConfig.ConfigMapRepository + deploymentConfigService pipeline.PipelineDeploymentConfigService + chartRefService chartRef.ChartRefService + pipelineRepository pipelineConfig.PipelineRepository + deploymentTemplateHistoryService history.DeploymentTemplateHistoryService + configMapHistoryService history.ConfigMapHistoryService } func NewDeploymentConfigurationServiceImpl(logger *zap.SugaredLogger, @@ -40,14 +64,34 @@ func NewDeploymentConfigurationServiceImpl(logger *zap.SugaredLogger, environmentRepository repository.EnvironmentRepository, chartService chartService.ChartService, deploymentTemplateService generateManifest.DeploymentTemplateService, + deploymentTemplateHistoryRepository repository3.DeploymentTemplateHistoryRepository, + pipelineStrategyHistoryRepository repository3.PipelineStrategyHistoryRepository, + configMapHistoryRepository repository3.ConfigMapHistoryRepository, + scopedVariableManager variables.ScopedVariableCMCSManager, + configMapRepository chartConfig.ConfigMapRepository, + deploymentConfigService pipeline.PipelineDeploymentConfigService, + chartRefService chartRef.ChartRefService, + pipelineRepository pipelineConfig.PipelineRepository, + deploymentTemplateHistoryService history.DeploymentTemplateHistoryService, + configMapHistoryService history.ConfigMapHistoryService, ) (*DeploymentConfigurationServiceImpl, error) { deploymentConfigurationService := &DeploymentConfigurationServiceImpl{ - logger: logger, - configMapService: configMapService, - appRepository: appRepository, - environmentRepository: environmentRepository, - chartService: chartService, - deploymentTemplateService: deploymentTemplateService, + logger: logger, + configMapService: configMapService, + appRepository: appRepository, + environmentRepository: environmentRepository, + chartService: chartService, + deploymentTemplateService: deploymentTemplateService, + deploymentTemplateHistoryRepository: deploymentTemplateHistoryRepository, + pipelineStrategyHistoryRepository: pipelineStrategyHistoryRepository, + configMapHistoryRepository: configMapHistoryRepository, + scopedVariableManager: scopedVariableManager, + configMapRepository: configMapRepository, + deploymentConfigService: deploymentConfigService, + chartRefService: chartRefService, + pipelineRepository: pipelineRepository, + deploymentTemplateHistoryService: deploymentTemplateHistoryService, + configMapHistoryService: configMapHistoryService, } return deploymentConfigurationService, nil @@ -82,19 +126,23 @@ func (impl *DeploymentConfigurationServiceImpl) ConfigAutoComplete(appId int, en return configDataResp, nil } -func (impl *DeploymentConfigurationServiceImpl) GetAllConfigData(ctx context.Context, configDataQueryParams *bean2.ConfigDataQueryParams) (*bean2.DeploymentAndCmCsConfigDto, error) { - if !configDataQueryParams.IsValidConfigType() { - return nil, &util.ApiError{HttpStatusCode: http.StatusBadRequest, Code: strconv.Itoa(http.StatusBadRequest), InternalMessage: bean2.InvalidConfigTypeErr, UserMessage: bean2.InvalidConfigTypeErr} - } +func (impl *DeploymentConfigurationServiceImpl) GetAllConfigData(ctx context.Context, configDataQueryParams *bean2.ConfigDataQueryParams, userHasAdminAccess bool) (*bean2.DeploymentAndCmCsConfigDto, error) { var err error - var envId int - var appId int + var envId, appId, clusterId int + systemMetadata := &resourceQualifiers.SystemMetadata{ + AppName: configDataQueryParams.AppName, + } if configDataQueryParams.IsEnvNameProvided() { - envId, err = impl.environmentRepository.FindIdByName(configDataQueryParams.EnvName) + env, err := impl.environmentRepository.FindEnvByNameWithClusterDetails(configDataQueryParams.EnvName) if err != nil { impl.logger.Errorw("GetAllConfigData, error in getting environment model by envName", "envName", configDataQueryParams.EnvName, "err", err) return nil, err } + envId = env.Id + clusterId = env.ClusterId + systemMetadata.EnvironmentName = env.Name + systemMetadata.Namespace = env.Namespace + systemMetadata.ClusterName = env.Cluster.ClusterName } appId, err = impl.appRepository.FindAppIdByName(configDataQueryParams.AppName) if err != nil { @@ -102,10 +150,341 @@ func (impl *DeploymentConfigurationServiceImpl) GetAllConfigData(ctx context.Con return nil, err } + switch configDataQueryParams.ConfigArea { + case bean2.CdRollback.ToString(): + return impl.getConfigDataForCdRollback(ctx, configDataQueryParams, userHasAdminAccess) + case bean2.DeploymentHistory.ToString(): + return impl.getConfigDataForDeploymentHistory(ctx, configDataQueryParams, userHasAdminAccess) + } + // this would be the default case + return impl.getConfigDataForAppConfiguration(ctx, configDataQueryParams, appId, envId, clusterId, userHasAdminAccess, systemMetadata) +} + +func (impl *DeploymentConfigurationServiceImpl) getConfigDataForCdRollback(ctx context.Context, configDataQueryParams *bean2.ConfigDataQueryParams, userHasAdminAccess bool) (*bean2.DeploymentAndCmCsConfigDto, error) { + // wfrId is expected in this case to return the expected data + if configDataQueryParams.WfrId == 0 { + return nil, &util.ApiError{HttpStatusCode: http.StatusNotFound, Code: strconv.Itoa(http.StatusNotFound), InternalMessage: bean2.ExpectedWfrIdNotPassedInQueryParamErr, UserMessage: bean2.ExpectedWfrIdNotPassedInQueryParamErr} + } + return impl.getConfigDataForDeploymentHistory(ctx, configDataQueryParams, userHasAdminAccess) +} + +func (impl *DeploymentConfigurationServiceImpl) getDeploymentHistoryConfig(ctx context.Context, configDataQueryParams *bean2.ConfigDataQueryParams) (*bean2.DeploymentAndCmCsConfig, error) { + deploymentJson := json.RawMessage{} + deploymentHistory, err := impl.deploymentTemplateHistoryRepository.GetHistoryByPipelineIdAndWfrId(configDataQueryParams.PipelineId, configDataQueryParams.WfrId) + if err != nil && !util.IsErrNoRows(err) { + impl.logger.Errorw("error in getting deployment template history for pipelineId and wfrId", "pipelineId", configDataQueryParams.PipelineId, "wfrId", configDataQueryParams.WfrId, "err", err) + return nil, err + } else if util.IsErrNoRows(err) { + return nil, util.GetApiError(http.StatusNotFound, bean2.NoDeploymentDoneForSelectedImage, bean2.NoDeploymentDoneForSelectedImage) + } + err = deploymentJson.UnmarshalJSON([]byte(deploymentHistory.Template)) + if err != nil { + impl.logger.Errorw("getDeploymentTemplateForEnvLevel, error in unmarshalling string deploymentTemplateResponse data into json Raw message", "data", deploymentHistory.Template, "err", err) + return nil, err + } + isSuperAdmin, err := util2.GetIsSuperAdminFromContext(ctx) + if err != nil { + return nil, err + } + reference := repository6.HistoryReference{ + HistoryReferenceId: deploymentHistory.Id, + HistoryReferenceType: repository6.HistoryReferenceTypeDeploymentTemplate, + } + variableSnapshotMap, resolvedTemplate, err := impl.scopedVariableManager.GetVariableSnapshotAndResolveTemplate(deploymentHistory.Template, parsers.JsonVariableTemplate, reference, isSuperAdmin, false) + if err != nil { + impl.logger.Errorw("error while resolving template from history", "deploymentHistoryId", deploymentHistory.Id, "pipelineId", configDataQueryParams.PipelineId, "err", err) + } + + deploymentConfig := bean2.NewDeploymentAndCmCsConfig(). + WithConfigData(deploymentJson). + WithResourceType(bean.DeploymentTemplate). + WithVariableSnapshot(map[string]map[string]string{bean.DeploymentTemplate.ToString(): variableSnapshotMap}). + WithResolvedValue(json.RawMessage(resolvedTemplate)). + WithDeploymentConfigMetadata(deploymentHistory.TemplateVersion, deploymentHistory.IsAppMetricsEnabled) + return deploymentConfig, nil +} + +func (impl *DeploymentConfigurationServiceImpl) getPipelineStrategyConfigHistory(ctx context.Context, configDataQueryParams *bean2.ConfigDataQueryParams) (*bean2.DeploymentAndCmCsConfig, error) { + pipelineStrategyJson := json.RawMessage{} + pipelineConfig := bean2.NewDeploymentAndCmCsConfig() + pipelineStrategyHistory, err := impl.pipelineStrategyHistoryRepository.GetHistoryByPipelineIdAndWfrId(ctx, configDataQueryParams.PipelineId, configDataQueryParams.WfrId) + if err != nil && !util.IsErrNoRows(err) { + impl.logger.Errorw("error in checking if history exists for pipelineId and wfrId", "pipelineId", configDataQueryParams.PipelineId, "wfrId", configDataQueryParams.WfrId, "err", err) + return nil, err + } else if util.IsErrNoRows(err) { + return pipelineConfig, nil + } + err = pipelineStrategyJson.UnmarshalJSON([]byte(pipelineStrategyHistory.Config)) + if err != nil { + impl.logger.Errorw("getDeploymentTemplateForEnvLevel, error in unmarshalling string pipelineStrategyHistory data into json Raw message", "pipelineStrategyHistoryConfig", pipelineStrategyHistory.Config, "err", err) + return nil, err + } + pipelineConfig.WithConfigData(pipelineStrategyJson). + WithResourceType(bean.PipelineStrategy). + WithPipelineStrategyMetadata(pipelineStrategyHistory.PipelineTriggerType, string(pipelineStrategyHistory.Strategy)) + return pipelineConfig, nil +} + +func (impl *DeploymentConfigurationServiceImpl) getConfigDataForDeploymentHistory(ctx context.Context, configDataQueryParams *bean2.ConfigDataQueryParams, userHasAdminAccess bool) (*bean2.DeploymentAndCmCsConfigDto, error) { + // we would be expecting wfrId in case of getting data for Deployment history + configDataDto := &bean2.DeploymentAndCmCsConfigDto{} + var err error + //fetching history for deployment config starts + deploymentConfig, err := impl.getDeploymentHistoryConfig(ctx, configDataQueryParams) + if err != nil { + impl.logger.Errorw("getConfigDataForDeploymentHistory, error in getDeploymentHistoryConfig", "configDataQueryParams", configDataQueryParams, "err", err) + return nil, err + } + configDataDto.WithDeploymentTemplateData(deploymentConfig) + // fetching for deployment config ends + + // fetching for pipeline strategy config starts + pipelineConfig, err := impl.getPipelineStrategyConfigHistory(ctx, configDataQueryParams) + if err != nil { + impl.logger.Errorw("getConfigDataForDeploymentHistory, error in getPipelineStrategyConfigHistory", "configDataQueryParams", configDataQueryParams, "err", err) + return nil, err + } + if len(pipelineConfig.Data) > 0 { + configDataDto.WithPipelineConfigData(pipelineConfig) + } + + // fetching for pipeline strategy config ends + + // fetching for cm config starts + cmConfigData, err := impl.getCmCsConfigHistory(ctx, configDataQueryParams, repository3.CONFIGMAP_TYPE, userHasAdminAccess) + if err != nil { + impl.logger.Errorw("getConfigDataForDeploymentHistory, error in getCmConfigHistory", "configDataQueryParams", configDataQueryParams, "err", err) + return nil, err + } + configDataDto.WithConfigMapData(cmConfigData) + // fetching for cm config ends + + // fetching for cs config starts + secretConfigDto, err := impl.getCmCsConfigHistory(ctx, configDataQueryParams, repository3.SECRET_TYPE, userHasAdminAccess) + if err != nil { + impl.logger.Errorw("getConfigDataForDeploymentHistory, error in getSecretConfigHistory", "configDataQueryParams", configDataQueryParams, "err", err) + return nil, err + } + configDataDto.WithSecretData(secretConfigDto) + // fetching for cs config ends + + return configDataDto, nil +} + +func (impl *DeploymentConfigurationServiceImpl) getCmCsConfigHistory(ctx context.Context, configDataQueryParams *bean2.ConfigDataQueryParams, configType repository3.ConfigType, userHasAdminAccess bool) (*bean2.DeploymentAndCmCsConfig, error) { + var resourceType bean.ResourceType + history, err := impl.configMapHistoryRepository.GetHistoryByPipelineIdAndWfrId(configDataQueryParams.PipelineId, configDataQueryParams.WfrId, configType) + if err != nil { + impl.logger.Errorw("error in checking if cm cs history exists for pipelineId and wfrId", "pipelineId", configDataQueryParams.PipelineId, "wfrId", configDataQueryParams.WfrId, "err", err) + return nil, err + } + var configData []*bean.ConfigData + configList := bean.ConfigsList{} + secretList := bean.SecretsList{} + switch configType { + case repository3.CONFIGMAP_TYPE: + if len(history.Data) > 0 { + err = json.Unmarshal([]byte(history.Data), &configList) + if err != nil { + impl.logger.Debugw("error while Unmarshal", "err", err) + return nil, err + } + } + resourceType = bean.CM + configData = configList.ConfigData + case repository3.SECRET_TYPE: + if len(history.Data) > 0 { + err = json.Unmarshal([]byte(history.Data), &secretList) + if err != nil { + impl.logger.Debugw("error while Unmarshal", "err", err) + return nil, err + } + } + resourceType = bean.CS + configData = secretList.ConfigData + + } + + resolvedDataMap, variableSnapshotMap, err := impl.scopedVariableManager.GetResolvedCMCSHistoryDtos(ctx, configType, adaptor.ReverseConfigListConvertor(configList), history, adaptor.ReverseSecretListConvertor(secretList)) + if err != nil { + return nil, err + } + resolvedConfigDataList := make([]*bean.ConfigData, 0, len(resolvedDataMap)) + for _, resolvedConfigData := range resolvedDataMap { + resolvedConfigDataList = append(resolvedConfigDataList, adapter.ConvertConfigDataToPipelineConfigData(&resolvedConfigData)) + } + + if configType == repository3.SECRET_TYPE { + impl.encodeSecretDataFromNonAdminUsers(configData, userHasAdminAccess) + impl.encodeSecretDataFromNonAdminUsers(resolvedConfigDataList, userHasAdminAccess) + + } + + configDataReq := &bean.ConfigDataRequest{ConfigData: configData} + configDataJson, err := utils.ConvertToJsonRawMessage(configDataReq) + if err != nil { + impl.logger.Errorw("getCmCsPublishedConfigResponse, error in converting config data to json raw message", "pipelineId", configDataQueryParams.PipelineId, "wfrId", configDataQueryParams.WfrId, "err", err) + return nil, err + } + resolvedConfigDataReq := &bean.ConfigDataRequest{ConfigData: resolvedConfigDataList} + resolvedConfigDataStringJson, err := utils.ConvertToJsonRawMessage(resolvedConfigDataReq) + if err != nil { + impl.logger.Errorw("getCmCsPublishedConfigResponse, error in ConvertToJsonRawMessage for resolvedConfigDataString", "pipelineId", configDataQueryParams.PipelineId, "wfrId", configDataQueryParams.WfrId, "err", err) + return nil, err + } + cmConfigData := bean2.NewDeploymentAndCmCsConfig(). + WithConfigData(configDataJson). + WithResourceType(resourceType). + WithVariableSnapshot(variableSnapshotMap). + WithResolvedValue(resolvedConfigDataStringJson) + return cmConfigData, nil +} + +func (impl *DeploymentConfigurationServiceImpl) encodeSecretDataFromNonAdminUsers(configDataList []*bean.ConfigData, userHasAdminAccess bool) { + for _, config := range configDataList { + if config.Data != nil { + if !userHasAdminAccess { + //removing keys and sending + resultMap := make(map[string]string) + resultMapFinal := make(map[string]string) + err := json.Unmarshal(config.Data, &resultMap) + if err != nil { + impl.logger.Errorw("unmarshal failed", "error", err) + return + } + for key, _ := range resultMap { + //hard-coding values to show them as hidden to user + resultMapFinal[key] = bean2.SecretMaskedValue + } + config.Data, err = utils.ConvertToJsonRawMessage(resultMapFinal) + if err != nil { + impl.logger.Errorw("error while marshaling request", "err", err) + return + } + } + } + } +} + +func (impl *DeploymentConfigurationServiceImpl) getCmCsDataForPreviousDeployments(ctx context.Context, deploymentTemplateHistoryId, pipelineId int, userHasAdminAccess bool) (*bean2.DeploymentAndCmCsConfigDto, error) { + + configDataDto := &bean2.DeploymentAndCmCsConfigDto{} + + deplTemplateHistory, err := impl.deploymentTemplateHistoryService.GetTemplateHistoryModelForDeployedTemplateById(deploymentTemplateHistoryId, pipelineId) + if err != nil { + impl.logger.Errorw("error in getting deployment template history", "err", err, "deploymentTemplateHistoryId", deploymentTemplateHistoryId, "pipelineId", pipelineId) + return nil, err + } + + secretConfigData, cmConfigData, err := impl.configMapHistoryService.GetConfigmapHistoryDataByDeployedOnAndPipelineId(ctx, pipelineId, deplTemplateHistory.DeployedOn, userHasAdminAccess) + if err != nil { + impl.logger.Errorw("error in getting secretData and cmData", "err", err, "deploymentTemplateHistoryId", deploymentTemplateHistoryId, "pipelineId", pipelineId) + return nil, err + } + configDataDto.WithConfigMapData(cmConfigData).WithSecretData(secretConfigData) + return configDataDto, nil + +} +func (impl *DeploymentConfigurationServiceImpl) getPipelineStrategyForPreviousDeployments(ctx context.Context, deploymentTemplateHistoryId, pipelineId int) (*bean2.DeploymentAndCmCsConfig, error) { + pipelineStrategyJson := json.RawMessage{} + pipelineConfig := bean2.NewDeploymentAndCmCsConfig() + deplTemplateHistory, err := impl.deploymentTemplateHistoryService.GetTemplateHistoryModelForDeployedTemplateById(deploymentTemplateHistoryId, pipelineId) + if err != nil { + impl.logger.Errorw("error in getting deployment template history", "deploymentTemplateHistoryId", deploymentTemplateHistoryId, "pipelineId", pipelineId, "err", err) + return nil, err + } + pipelineStrategyHistory, err := impl.pipelineStrategyHistoryRepository.FindPipelineStrategyForDeployedOnAndPipelineId(pipelineId, deplTemplateHistory.DeployedOn) + if err != nil && !util.IsErrNoRows(err) { + impl.logger.Errorw("error in FindPipelineStrategyForDeployedOnAndPipelineId", "deploymentTemplateHistoryId", deploymentTemplateHistoryId, "deployedOn", deplTemplateHistory.DeployedOn, "pipelineId", pipelineId, "err", err) + return nil, err + } else if util.IsErrNoRows(err) { + return pipelineConfig, nil + } + err = pipelineStrategyJson.UnmarshalJSON([]byte(pipelineStrategyHistory.Config)) + if err != nil { + impl.logger.Errorw("getDeploymentTemplateForEnvLevel, error in unmarshalling string pipelineStrategyHistory data into json Raw message", "err", err) + return nil, err + } + pipelineConfig.WithConfigData(pipelineStrategyJson). + WithResourceType(bean.PipelineStrategy). + WithPipelineStrategyMetadata(pipelineStrategyHistory.PipelineTriggerType, string(pipelineStrategyHistory.Strategy)) + return pipelineConfig, nil +} + +func (impl *DeploymentConfigurationServiceImpl) getDeploymentsConfigForPreviousDeployments(ctx context.Context, configDataQueryParams *bean2.ConfigDataQueryParams, + appId, envId int) (generateManifest.DeploymentTemplateResponse, error) { + deploymentTemplateRequest := generateManifest.DeploymentTemplateRequest{ + PipelineId: configDataQueryParams.PipelineId, + DeploymentTemplateHistoryId: configDataQueryParams.IdentifierId, + RequestDataMode: generateManifest.Values, + Type: repository2.DeployedOnSelfEnvironment, + } + var deploymentTemplateResponse generateManifest.DeploymentTemplateResponse + deploymentTemplateResponse, err := impl.deploymentTemplateService.GetDeploymentTemplate(ctx, deploymentTemplateRequest) + if err != nil { + impl.logger.Errorw("getDeploymentTemplateForEnvLevel, error in getting deployment template for ", "deploymentTemplateRequest", deploymentTemplateRequest, "err", err) + return deploymentTemplateResponse, err + } + + return deploymentTemplateResponse, nil +} + +func (impl *DeploymentConfigurationServiceImpl) getDeploymentAndCmCsConfigDataForPreviousDeployments(ctx context.Context, configDataQueryParams *bean2.ConfigDataQueryParams, + appId, envId int, userHasAdminAccess bool) (*bean2.DeploymentAndCmCsConfigDto, error) { + + // getting DeploymentAndCmCsConfigDto obj with cm and cs data populated + configDataDto, err := impl.getCmCsDataForPreviousDeployments(ctx, configDataQueryParams.IdentifierId, configDataQueryParams.PipelineId, userHasAdminAccess) + if err != nil { + impl.logger.Errorw("error in getting cm cs for PreviousDeployments state", "deploymentTemplateHistoryId", configDataQueryParams.IdentifierId, "pipelineId", configDataQueryParams.PipelineId, "err", err) + return nil, err + } + pipelineStrategy, err := impl.getPipelineStrategyForPreviousDeployments(ctx, configDataQueryParams.IdentifierId, configDataQueryParams.PipelineId) + if err != nil { + impl.logger.Errorw(" error in getting cm cs for PreviousDeployments state", "deploymentTemplateHistoryId", configDataQueryParams.IdentifierId, "pipelineId", configDataQueryParams.PipelineId, "err", err) + return nil, err + } + if len(pipelineStrategy.Data) > 0 { + configDataDto.WithPipelineConfigData(pipelineStrategy) + } + + deploymentTemplateData, err := impl.getDeploymentsConfigForPreviousDeployments(ctx, configDataQueryParams, appId, envId) + if err != nil { + impl.logger.Errorw("error in getting deployment config", "appName", configDataQueryParams.AppName, "envName", configDataQueryParams.EnvName, "err", err) + return nil, err + } + deploymentJson := json.RawMessage{} + err = deploymentJson.UnmarshalJSON([]byte(deploymentTemplateData.Data)) + if err != nil { + impl.logger.Errorw("error in unmarshalling string deploymentTemplateResponse data into json Raw message", "appName", configDataQueryParams.AppName, "envName", configDataQueryParams.EnvName, "err", err) + return nil, err + } + variableSnapShotMap := map[string]map[string]string{bean.DeploymentTemplate.ToString(): deploymentTemplateData.VariableSnapshot} + + deploymentConfig := bean2.NewDeploymentAndCmCsConfig(). + WithDeploymentConfigMetadata(deploymentTemplateData.TemplateVersion, deploymentTemplateData.IsAppMetricsEnabled). + WithConfigData(deploymentJson). + WithResourceType(bean.DeploymentTemplate). + WithResolvedValue(json.RawMessage(deploymentTemplateData.ResolvedData)). + WithVariableSnapshot(variableSnapShotMap) + + configDataDto.WithDeploymentTemplateData(deploymentConfig) + + return configDataDto, nil +} + +func (impl *DeploymentConfigurationServiceImpl) getConfigDataForAppConfiguration(ctx context.Context, configDataQueryParams *bean2.ConfigDataQueryParams, + appId, envId, clusterId int, userHasAdminAccess bool, systemMetadata *resourceQualifiers.SystemMetadata) (*bean2.DeploymentAndCmCsConfigDto, error) { configDataDto := &bean2.DeploymentAndCmCsConfigDto{} + var err error switch configDataQueryParams.ConfigType { + case bean2.PreviousDeployments.ToString(): + configDataDto, err = impl.getDeploymentAndCmCsConfigDataForPreviousDeployments(ctx, configDataQueryParams, appId, envId, userHasAdminAccess) + if err != nil { + impl.logger.Errorw("GetAllConfigData, error in config data for Previous Deployments", "configDataQueryParams", configDataQueryParams, "err", err) + return nil, err + } default: // keeping default as PublishedOnly - configDataDto, err = impl.getPublishedConfigData(ctx, configDataQueryParams, appId, envId) + configDataDto, err = impl.getPublishedConfigData(ctx, configDataQueryParams, appId, envId, clusterId, userHasAdminAccess, systemMetadata) if err != nil { impl.logger.Errorw("GetAllConfigData, error in config data for PublishedOnly", "configDataQueryParams", configDataQueryParams, "err", err) return nil, err @@ -114,7 +493,8 @@ func (impl *DeploymentConfigurationServiceImpl) GetAllConfigData(ctx context.Con return configDataDto, nil } -func (impl *DeploymentConfigurationServiceImpl) getCmCsEditDataForPublishedOnly(configDataQueryParams *bean2.ConfigDataQueryParams, envId, appId int) (*bean2.DeploymentAndCmCsConfigDto, error) { +func (impl *DeploymentConfigurationServiceImpl) getCmCsEditDataForPublishedOnly(ctx context.Context, configDataQueryParams *bean2.ConfigDataQueryParams, envId, + appId int, clusterId int, userHasAdminAccess bool, systemMetadata *resourceQualifiers.SystemMetadata) (*bean2.DeploymentAndCmCsConfigDto, error) { configDataDto := &bean2.DeploymentAndCmCsConfigDto{} var resourceType bean.ResourceType @@ -140,17 +520,35 @@ func (impl *DeploymentConfigurationServiceImpl) getCmCsEditDataForPublishedOnly( impl.logger.Errorw("getCmCsEditDataForPublishedOnly, error in converting to json raw message", "configDataQueryParams", configDataQueryParams, "err", err) return nil, err } + resolvedCmCsMetadataDto, err := impl.ResolveCmCs(ctx, envId, appId, clusterId, userHasAdminAccess, configDataQueryParams.ResourceName, resourceType, systemMetadata) + if err != nil { + impl.logger.Errorw("error in resolving cm and cs for published only config only response", "appId", appId, "envId", envId, "err", err) + return nil, err + } cmCsConfig := bean2.NewDeploymentAndCmCsConfig().WithConfigData(respJson).WithResourceType(resourceType) + if resourceType == bean.CS { + resolvedConfigDataStringJson, err := utils.ConvertToJsonRawMessage(resolvedCmCsMetadataDto.ResolvedSecretData) + if err != nil { + impl.logger.Errorw("getCmCsPublishedConfigResponse, error in ConvertToJsonRawMessage ", "err", err) + return nil, err + } + cmCsConfig.WithResolvedValue(resolvedConfigDataStringJson).WithVariableSnapshot(resolvedCmCsMetadataDto.VariableMapCS) configDataDto.WithSecretData(cmCsConfig) } else if resourceType == bean.CM { + resolvedConfigDataStringJson, err := utils.ConvertToJsonRawMessage(resolvedCmCsMetadataDto.ResolvedConfigMapData) + if err != nil { + impl.logger.Errorw("getCmCsPublishedConfigResponse, error in ConvertToJsonRawMessage for resolvedJson", "ResolvedConfigMapData", resolvedCmCsMetadataDto.ResolvedConfigMapData, "err", err) + return nil, err + } + cmCsConfig.WithResolvedValue(resolvedConfigDataStringJson).WithVariableSnapshot(resolvedCmCsMetadataDto.VariableMapCM) configDataDto.WithConfigMapData(cmCsConfig) } return configDataDto, nil } -func (impl *DeploymentConfigurationServiceImpl) getCmCsPublishedConfigResponse(envId, appId int) (*bean2.DeploymentAndCmCsConfigDto, error) { +func (impl *DeploymentConfigurationServiceImpl) getCmCsPublishedConfigResponse(ctx context.Context, envId, appId, clusterId int, userHasAdminAccess bool, systemMetadata *resourceQualifiers.SystemMetadata) (*bean2.DeploymentAndCmCsConfigDto, error) { configDataDto := &bean2.DeploymentAndCmCsConfigDto{} secretData, err := impl.getSecretConfigResponse("", 0, envId, appId) @@ -178,79 +576,277 @@ func (impl *DeploymentConfigurationServiceImpl) getCmCsPublishedConfigResponse(e return nil, err } - cmConfigData := bean2.NewDeploymentAndCmCsConfig().WithConfigData(cmRespJson).WithResourceType(bean.CM) - secretConfigData := bean2.NewDeploymentAndCmCsConfig().WithConfigData(secretRespJson).WithResourceType(bean.CS) + resolvedCmCsMetadataDto, err := impl.ResolveCmCs(ctx, envId, appId, clusterId, userHasAdminAccess, "", "", systemMetadata) + if err != nil { + impl.logger.Errorw("error in resolving cm and cs for published only config only response", "appId", appId, "envId", envId, "err", err) + return nil, err + } + resolvedConfigMapDataStringJson, err := utils.ConvertToJsonRawMessage(resolvedCmCsMetadataDto.ResolvedConfigMapData) + if err != nil { + impl.logger.Errorw("error in ConvertToJsonRawMessage for resolvedConfigMapDataStringJson", "resolvedCmData", resolvedCmCsMetadataDto.ResolvedConfigMapData, "err", err) + return nil, err + } + resolvedSecretDataStringJson, err := utils.ConvertToJsonRawMessage(resolvedCmCsMetadataDto.ResolvedSecretData) + if err != nil { + impl.logger.Errorw(" error in ConvertToJsonRawMessage for resolvedConfigDataString", "err", err) + return nil, err + } + + cmConfigData := bean2.NewDeploymentAndCmCsConfig().WithConfigData(cmRespJson).WithResourceType(bean.CM). + WithResolvedValue(resolvedConfigMapDataStringJson).WithVariableSnapshot(resolvedCmCsMetadataDto.VariableMapCM) + + secretConfigData := bean2.NewDeploymentAndCmCsConfig().WithConfigData(secretRespJson).WithResourceType(bean.CS). + WithResolvedValue(resolvedSecretDataStringJson).WithVariableSnapshot(resolvedCmCsMetadataDto.VariableMapCS) configDataDto.WithConfigMapData(cmConfigData).WithSecretData(secretConfigData) return configDataDto, nil } -func (impl *DeploymentConfigurationServiceImpl) getPublishedDeploymentConfig(ctx context.Context, appId, envId int) (json.RawMessage, error) { +func (impl *DeploymentConfigurationServiceImpl) getMergedCmCs(envId, appId int) (*bean2.CmCsMetadataDto, error) { + configAppLevel, err := impl.configMapRepository.GetByAppIdAppLevel(appId) + if err != nil && pg.ErrNoRows != err { + impl.logger.Errorw("error in getting CM/CS app level data", "appId", appId, "err", err) + return nil, err + } + var configMapAppLevel string + var secretAppLevel string + if configAppLevel != nil && configAppLevel.Id > 0 { + configMapAppLevel = configAppLevel.ConfigMapData + secretAppLevel = configAppLevel.SecretData + } + configEnvLevel, err := impl.configMapRepository.GetByAppIdAndEnvIdEnvLevel(appId, envId) + if err != nil && pg.ErrNoRows != err { + impl.logger.Errorw("error in getting CM/CS env level data", "appId", appId, "envId", envId, "err", err) + return nil, err + } + var configMapEnvLevel string + var secretEnvLevel string + if configEnvLevel != nil && configEnvLevel.Id > 0 { + configMapEnvLevel = configEnvLevel.ConfigMapData + secretEnvLevel = configEnvLevel.SecretData + } + mergedConfigMap, err := impl.deploymentConfigService.GetMergedCMCSConfigMap(configMapAppLevel, configMapEnvLevel, repository3.CONFIGMAP_TYPE) + if err != nil { + impl.logger.Errorw("error in merging app level and env level CM configs", "err", err) + return nil, err + } + + mergedSecret, err := impl.deploymentConfigService.GetMergedCMCSConfigMap(secretAppLevel, secretEnvLevel, repository3.SECRET_TYPE) + if err != nil { + impl.logger.Errorw("error in merging app level and env level CM configs", "err", err) + return nil, err + } + return &bean2.CmCsMetadataDto{ + CmMap: mergedConfigMap, + SecretMap: mergedSecret, + ConfigAppLevelId: configAppLevel.Id, + ConfigEnvLevelId: configEnvLevel.Id, + }, nil +} + +func (impl *DeploymentConfigurationServiceImpl) ResolveCmCs(ctx context.Context, envId, appId, clusterId int, userHasAdminAccess bool, + resourceName string, resourceType bean.ResourceType, systemMetadata *resourceQualifiers.SystemMetadata) (*bean2.ResolvedCmCsMetadataDto, error) { + scope := resourceQualifiers.Scope{ + AppId: appId, + EnvId: envId, + ClusterId: clusterId, + SystemMetadata: systemMetadata, + } + cmcsMetadataDto, err := impl.getMergedCmCs(envId, appId) + if err != nil { + impl.logger.Errorw("error in getting merged cm cs", "appId", appId, "envId", envId, "err", err) + return nil, err + } + // if resourceName is provided then, resolve cmcs request is for single resource, then remove other data from merged cmCs + if len(resourceName) > 0 { + helper.FilterOutMergedCmCsForResourceName(cmcsMetadataDto, resourceName, resourceType) + } + resolvedConfigList, resolvedSecretList, variableMapCM, variableMapCS, err := impl.scopedVariableManager.ResolveCMCS(ctx, scope, cmcsMetadataDto.ConfigAppLevelId, cmcsMetadataDto.ConfigEnvLevelId, cmcsMetadataDto.CmMap, cmcsMetadataDto.SecretMap) + if err != nil { + impl.logger.Errorw("error in resolving CM/CS", "scope", scope, "appId", appId, "envId", envId, "err", err) + return nil, err + } + + resolvedConfigString, resolvedSecretString, err := impl.getStringifiedCmCs(resolvedConfigList, resolvedSecretList, userHasAdminAccess) + if err != nil { + impl.logger.Errorw("error in getStringifiedCmCs", "resolvedConfigList", resolvedConfigList, "err", err) + return nil, err + } + resolvedData := &bean2.ResolvedCmCsMetadataDto{ + VariableMapCM: variableMapCM, + VariableMapCS: variableMapCS, + ResolvedSecretData: resolvedSecretString, + ResolvedConfigMapData: resolvedConfigString, + } + + return resolvedData, nil +} + +func (impl *DeploymentConfigurationServiceImpl) getStringifiedCmCs(resolvedCmMap map[string]*bean3.ConfigData, resolvedSecretMap map[string]*bean3.ConfigData, + userHasAdminAccess bool) (string, string, error) { + + resolvedConfigDataList := make([]*bean.ConfigData, 0, len(resolvedCmMap)) + resolvedSecretDataList := make([]*bean.ConfigData, 0, len(resolvedSecretMap)) + + for _, resolvedConfigData := range resolvedCmMap { + resolvedConfigDataList = append(resolvedConfigDataList, adapter.ConvertConfigDataToPipelineConfigData(resolvedConfigData)) + } + + for _, resolvedSecretData := range resolvedSecretMap { + resolvedSecretDataList = append(resolvedSecretDataList, adapter.ConvertConfigDataToPipelineConfigData(resolvedSecretData)) + } + if len(resolvedSecretMap) > 0 { + impl.encodeSecretDataFromNonAdminUsers(resolvedSecretDataList, userHasAdminAccess) + } + resolvedConfigDataReq := &bean.ConfigDataRequest{ConfigData: resolvedConfigDataList} + resolvedConfigDataString, err := utils.ConvertToString(resolvedConfigDataReq) + if err != nil { + impl.logger.Errorw(" error in converting resolved config data to string", "resolvedConfigDataReq", resolvedConfigDataReq, "err", err) + return "", "", err + } + resolvedSecretDataReq := &bean.ConfigDataRequest{ConfigData: resolvedSecretDataList} + resolvedSecretDataString, err := utils.ConvertToString(resolvedSecretDataReq) + if err != nil { + impl.logger.Errorw(" error in converting resolved config data to string", "err", err) + return "", "", err + } + return resolvedConfigDataString, resolvedSecretDataString, nil +} +func (impl *DeploymentConfigurationServiceImpl) getPublishedDeploymentConfig(ctx context.Context, appId, envId int) (*bean2.DeploymentAndCmCsConfig, error) { if envId > 0 { - return impl.getDeploymentTemplateForEnvLevel(ctx, appId, envId) + deplTemplateResp, err := impl.getDeploymentTemplateForEnvLevel(ctx, appId, envId) + if err != nil { + impl.logger.Errorw("error in getting deployment template env level", "err", err) + return nil, err + } + deploymentJson := json.RawMessage{} + err = deploymentJson.UnmarshalJSON([]byte(deplTemplateResp.Data)) + if err != nil { + impl.logger.Errorw("getDeploymentTemplateForEnvLevel, error in unmarshalling string deploymentTemplateResponse data into json Raw message", "appId", appId, "envId", envId, "err", err) + return nil, err + } + + variableSnapShotMap := make(map[string]map[string]string, len(deplTemplateResp.VariableSnapshot)) + variableSnapShotMap[bean.DeploymentTemplate.ToString()] = deplTemplateResp.VariableSnapshot + + return bean2.NewDeploymentAndCmCsConfig().WithConfigData(deploymentJson).WithResourceType(bean.DeploymentTemplate). + WithResolvedValue(json.RawMessage(deplTemplateResp.ResolvedData)).WithVariableSnapshot(variableSnapShotMap). + WithDeploymentConfigMetadata(deplTemplateResp.TemplateVersion, deplTemplateResp.IsAppMetricsEnabled), nil + } + deplMetadata, err := impl.getBaseDeploymentTemplate(appId) + if err != nil { + impl.logger.Errorw("getting base depl. template", "appid", appId, "err", err) + return nil, err + } + deploymentTemplateRequest := generateManifest.DeploymentTemplateRequest{ + AppId: appId, + RequestDataMode: generateManifest.Values, + } + resolvedTemplate, variableSnapshot, err := impl.deploymentTemplateService.ResolveTemplateVariables(ctx, string(deplMetadata.DeploymentTemplateJson), deploymentTemplateRequest) + if err != nil { + impl.logger.Errorw("error in getting resolved data for base deployment template", "appid", appId, "err", err) + return nil, err } - return impl.getBaseDeploymentTemplate(appId) + + variableSnapShotMap := map[string]map[string]string{bean.DeploymentTemplate.ToString(): variableSnapshot} + return bean2.NewDeploymentAndCmCsConfig().WithConfigData(deplMetadata.DeploymentTemplateJson).WithResourceType(bean.DeploymentTemplate). + WithResolvedValue(json.RawMessage(resolvedTemplate)).WithVariableSnapshot(variableSnapShotMap). + WithDeploymentConfigMetadata(deplMetadata.TemplateVersion, deplMetadata.IsAppMetricsEnabled), nil } func (impl *DeploymentConfigurationServiceImpl) getPublishedConfigData(ctx context.Context, configDataQueryParams *bean2.ConfigDataQueryParams, - appId, envId int) (*bean2.DeploymentAndCmCsConfigDto, error) { + appId, envId, clusterId int, userHasAdminAccess bool, systemMetadata *resourceQualifiers.SystemMetadata) (*bean2.DeploymentAndCmCsConfigDto, error) { if configDataQueryParams.IsRequestMadeForOneResource() { - return impl.getCmCsEditDataForPublishedOnly(configDataQueryParams, envId, appId) + return impl.getCmCsEditDataForPublishedOnly(ctx, configDataQueryParams, envId, appId, clusterId, userHasAdminAccess, systemMetadata) } //ConfigMapsData and SecretsData are populated here - configData, err := impl.getCmCsPublishedConfigResponse(envId, appId) + configData, err := impl.getCmCsPublishedConfigResponse(ctx, envId, appId, clusterId, userHasAdminAccess, systemMetadata) if err != nil { impl.logger.Errorw("getPublishedConfigData, error in getting cm cs for PublishedOnly state", "appName", configDataQueryParams.AppName, "envName", configDataQueryParams.EnvName, "err", err) return nil, err } - deploymentTemplateJsonData, err := impl.getPublishedDeploymentConfig(ctx, appId, envId) + deploymentTemplateData, err := impl.getPublishedDeploymentConfig(ctx, appId, envId) if err != nil { impl.logger.Errorw("getPublishedConfigData, error in getting publishedOnly deployment config ", "configDataQueryParams", configDataQueryParams, "err", err) return nil, err } - deploymentConfig := bean2.NewDeploymentAndCmCsConfig().WithConfigData(deploymentTemplateJsonData).WithResourceType(bean.DeploymentTemplate) + configData.WithDeploymentTemplateData(deploymentTemplateData) - configData.WithDeploymentTemplateData(deploymentConfig) + pipelineConfigData, err := impl.getPublishedPipelineStrategyConfig(ctx, appId, envId) + if err != nil { + impl.logger.Errorw("getPublishedConfigData, error in getting publishedOnly pipeline strategy ", "configDataQueryParams", configDataQueryParams, "err", err) + return nil, err + } + if len(pipelineConfigData.Data) > 0 { + configData.WithPipelineConfigData(pipelineConfigData) + } return configData, nil } -func (impl *DeploymentConfigurationServiceImpl) getBaseDeploymentTemplate(appId int) (json.RawMessage, error) { +func (impl *DeploymentConfigurationServiceImpl) getPublishedPipelineStrategyConfig(ctx context.Context, appId int, envId int) (*bean2.DeploymentAndCmCsConfig, error) { + pipelineConfig := bean2.NewDeploymentAndCmCsConfig() + if envId == 0 { + return pipelineConfig, nil + } + pipeline, err := impl.pipelineRepository.FindActiveByAppIdAndEnvId(appId, envId) + if err != nil { + impl.logger.Errorw("error in FindActiveByAppIdAndEnvId", "appId", appId, "envId", envId, "err", err) + return nil, err + } + pipelineStrategy, err := impl.deploymentConfigService.GetLatestPipelineStrategyConfig(pipeline) + if err != nil && !errors.IsNotFound(err) { + impl.logger.Errorw("error in GetLatestPipelineStrategyConfig", "pipelineId", pipeline.Id, "err", err) + return nil, err + } else if errors.IsNotFound(err) { + return pipelineConfig, nil + } + pipelineStrategyJson := json.RawMessage{} + err = pipelineStrategyJson.UnmarshalJSON([]byte(pipelineStrategy.CodeEditorValue.Value)) + if err != nil { + impl.logger.Errorw("getDeploymentTemplateForEnvLevel, error in unmarshalling string pipelineStrategyHistory data into json Raw message", "err", err) + return nil, err + } + pipelineConfig.WithConfigData(pipelineStrategyJson). + WithResourceType(bean.PipelineStrategy). + WithPipelineStrategyMetadata(pipelineStrategy.PipelineTriggerType, string(pipelineStrategy.Strategy)) + return pipelineConfig, nil +} + +func (impl *DeploymentConfigurationServiceImpl) getBaseDeploymentTemplate(appId int) (*bean2.DeploymentTemplateMetadata, error) { deploymentTemplateData, err := impl.chartService.FindLatestChartForAppByAppId(appId) if err != nil { impl.logger.Errorw("error in getting base deployment template for appId", "appId", appId, "err", err) return nil, err } - return deploymentTemplateData.DefaultAppOverride, nil + _, _, version, _, err := impl.chartRefService.GetRefChart(deploymentTemplateData.ChartRefId) + if err != nil { + impl.logger.Errorw("error in getting chart ref by chartRefId ", "chartRefId", deploymentTemplateData.ChartRefId, "err", err) + return nil, err + } + return &bean2.DeploymentTemplateMetadata{ + DeploymentTemplateJson: deploymentTemplateData.DefaultAppOverride, + IsAppMetricsEnabled: deploymentTemplateData.IsAppMetricsEnabled, + TemplateVersion: version, + }, nil } -func (impl *DeploymentConfigurationServiceImpl) getDeploymentTemplateForEnvLevel(ctx context.Context, appId, envId int) (json.RawMessage, error) { +func (impl *DeploymentConfigurationServiceImpl) getDeploymentTemplateForEnvLevel(ctx context.Context, appId, envId int) (generateManifest.DeploymentTemplateResponse, error) { deploymentTemplateRequest := generateManifest.DeploymentTemplateRequest{ AppId: appId, EnvId: envId, RequestDataMode: generateManifest.Values, Type: repository2.PublishedOnEnvironments, } - deploymentTemplateResponse, err := impl.deploymentTemplateService.GetDeploymentTemplate(ctx, deploymentTemplateRequest) + var deploymentTemplateResponse generateManifest.DeploymentTemplateResponse + var err error + deploymentTemplateResponse, err = impl.deploymentTemplateService.GetDeploymentTemplate(ctx, deploymentTemplateRequest) if err != nil { impl.logger.Errorw("getDeploymentTemplateForEnvLevel, error in getting deployment template for ", "deploymentTemplateRequest", deploymentTemplateRequest, "err", err) - return nil, err - } - deploymentJson := json.RawMessage{} - err = deploymentJson.UnmarshalJSON([]byte(deploymentTemplateResponse.Data)) - if err != nil { - impl.logger.Errorw("getDeploymentTemplateForEnvLevel, error in unmarshalling string deploymentTemplateResponse data into json Raw message", "data", deploymentTemplateResponse.Data, "err", err) - return nil, err - } - return deploymentJson, nil -} - -func (impl *DeploymentConfigurationServiceImpl) getDeploymentConfig(ctx context.Context, appId, envId int) (json.RawMessage, error) { - if envId > 0 { - return impl.getDeploymentTemplateForEnvLevel(ctx, appId, envId) + return deploymentTemplateResponse, err } - return impl.getBaseDeploymentTemplate(appId) + return deploymentTemplateResponse, nil } func (impl *DeploymentConfigurationServiceImpl) getSecretConfigResponse(resourceName string, resourceId, envId, appId int) (*bean.ConfigDataRequest, error) { diff --git a/pkg/configDiff/adaptor/adaptor.go b/pkg/configDiff/adaptor/adaptor.go index 4ab81eb2d1..6fd46129fe 100644 --- a/pkg/configDiff/adaptor/adaptor.go +++ b/pkg/configDiff/adaptor/adaptor.go @@ -1,7 +1,9 @@ package adaptor import ( + bean3 "github.com/devtron-labs/devtron/pkg/bean" bean2 "github.com/devtron-labs/devtron/pkg/configDiff/bean" + "github.com/devtron-labs/devtron/pkg/pipeline/adapter" "github.com/devtron-labs/devtron/pkg/pipeline/bean" ) @@ -27,3 +29,35 @@ func GetCmCsAppAndEnvLevelMap(cMCSNamesAppLevel, cMCSNamesEnvLevel []bean.Config } return cMCSNamesAppLevelMap, cMCSNamesEnvLevelMap } + +func ConfigListConvertor(r bean3.ConfigList) bean.ConfigsList { + pipelineConfigData := make([]*bean.ConfigData, 0, len(r.ConfigData)) + for _, item := range r.ConfigData { + pipelineConfigData = append(pipelineConfigData, adapter.ConvertConfigDataToPipelineConfigData(item)) + } + return bean.ConfigsList{ConfigData: pipelineConfigData} +} + +func SecretListConvertor(r bean3.SecretList) bean.SecretsList { + pipelineConfigData := make([]*bean.ConfigData, 0, len(r.ConfigData)) + for _, item := range r.ConfigData { + pipelineConfigData = append(pipelineConfigData, adapter.ConvertConfigDataToPipelineConfigData(item)) + } + return bean.SecretsList{ConfigData: pipelineConfigData} +} + +func ReverseConfigListConvertor(r bean.ConfigsList) bean3.ConfigList { + configData := make([]*bean3.ConfigData, 0, len(r.ConfigData)) + for _, item := range r.ConfigData { + configData = append(configData, adapter.ConvertPipelineConfigDataToConfigData(item)) + } + return bean3.ConfigList{ConfigData: configData} +} + +func ReverseSecretListConvertor(r bean.SecretsList) bean3.SecretList { + configData := make([]*bean3.ConfigData, 0, len(r.ConfigData)) + for _, item := range r.ConfigData { + configData = append(configData, adapter.ConvertPipelineConfigDataToConfigData(item)) + } + return bean3.SecretList{ConfigData: configData} +} diff --git a/pkg/configDiff/bean/bean.go b/pkg/configDiff/bean/bean.go index 2113ea81a6..30e183a229 100644 --- a/pkg/configDiff/bean/bean.go +++ b/pkg/configDiff/bean/bean.go @@ -4,6 +4,8 @@ import "C" import ( "encoding/json" "fmt" + "github.com/devtron-labs/devtron/internal/sql/repository/pipelineConfig" + bean3 "github.com/devtron-labs/devtron/pkg/bean" "github.com/devtron-labs/devtron/pkg/pipeline/bean" ) @@ -11,6 +13,7 @@ type ConfigState string const ( PublishedConfigState ConfigState = "PublishedOnly" + PreviousDeployments ConfigState = "PreviousDeployments" ) func (r ConfigState) ToString() string { @@ -25,6 +28,19 @@ const ( Overridden ConfigStage = "Overridden" ) +type ConfigArea string + +const ( + AppConfiguration ConfigArea = "AppConfiguration" + DeploymentHistory ConfigArea = "DeploymentHistory" + CdRollback ConfigArea = "CdRollback" + ResolveData ConfigArea = "ResolveData" +) + +func (r ConfigArea) ToString() string { + return string(r) +} + type ConfigProperty struct { Id int `json:"id"` Name string `json:"name"` @@ -71,8 +87,16 @@ func (r *ConfigProperty) GetIdentifier() ConfigPropertyIdentifier { } type DeploymentAndCmCsConfig struct { - ResourceType bean.ResourceType `json:"resourceType"` - Data json.RawMessage `json:"data"` + ResourceType bean.ResourceType `json:"resourceType"` + Data json.RawMessage `json:"data"` + VariableSnapshot map[string]map[string]string `json:"variableSnapshot"` // for deployment->{Deployment Template: resolvedValuesMap}, for cm->{cmComponentName: resolvedValuesMap} + ResolvedValue json.RawMessage `json:"resolvedValue"` + // for deployment template + TemplateVersion string `json:"templateVersion,omitempty"` + IsAppMetricsEnabled bool `json:"isAppMetricsEnabled,omitempty"` + //for pipeline strategy + PipelineTriggerType pipelineConfig.TriggerType `json:"pipelineTriggerType,omitempty"` + Strategy string `json:"strategy,omitempty"` } func NewDeploymentAndCmCsConfig() *DeploymentAndCmCsConfig { @@ -89,10 +113,33 @@ func (r *DeploymentAndCmCsConfig) WithConfigData(data json.RawMessage) *Deployme return r } +func (r *DeploymentAndCmCsConfig) WithVariableSnapshot(snapshot map[string]map[string]string) *DeploymentAndCmCsConfig { + r.VariableSnapshot = snapshot + return r +} + +func (r *DeploymentAndCmCsConfig) WithResolvedValue(resolvedValue json.RawMessage) *DeploymentAndCmCsConfig { + r.ResolvedValue = resolvedValue + return r +} + +func (r *DeploymentAndCmCsConfig) WithDeploymentConfigMetadata(templateVersion string, isAppMetricsEnabled bool) *DeploymentAndCmCsConfig { + r.TemplateVersion = templateVersion + r.IsAppMetricsEnabled = isAppMetricsEnabled + return r +} + +func (r *DeploymentAndCmCsConfig) WithPipelineStrategyMetadata(pipelineTriggerType pipelineConfig.TriggerType, strategy string) *DeploymentAndCmCsConfig { + r.PipelineTriggerType = pipelineTriggerType + r.Strategy = strategy + return r +} + type DeploymentAndCmCsConfigDto struct { DeploymentTemplate *DeploymentAndCmCsConfig `json:"deploymentTemplate"` ConfigMapsData *DeploymentAndCmCsConfig `json:"configMapData"` SecretsData *DeploymentAndCmCsConfig `json:"secretsData"` + PipelineConfigData *DeploymentAndCmCsConfig `json:"pipelineConfigData,omitempty"` IsAppAdmin bool `json:"isAppAdmin"` } @@ -112,17 +159,23 @@ func (r *DeploymentAndCmCsConfigDto) WithSecretData(data *DeploymentAndCmCsConfi r.SecretsData = data return r } +func (r *DeploymentAndCmCsConfigDto) WithPipelineConfigData(data *DeploymentAndCmCsConfig) *DeploymentAndCmCsConfigDto { + r.PipelineConfigData = data + return r +} type ConfigDataQueryParams struct { AppName string `schema:"appName"` EnvName string `schema:"envName"` ConfigType string `schema:"configType"` IdentifierId int `schema:"identifierId"` - PipelineId int `schema:"pipelineId"` // req for fetching previous deployments data - ResourceName string `schema:"resourceName"` - ResourceType string `schema:"resourceType"` - ResourceId int `schema:"resourceId"` + PipelineId int `schema:"pipelineId"` // req for fetching previous deployments data + ResourceName string `schema:"resourceName"` // used in case of cm and cs + ResourceType string `schema:"resourceType"` // used in case of cm and cs + ResourceId int `schema:"resourceId"` // used in case of cm and cs UserId int32 `schema:"-"` + WfrId int `schema:"wfrId"` + ConfigArea string `schema:"configArea"` } // FilterCriteria []string `schema:"filterCriteria"` @@ -150,3 +203,33 @@ func (r *ConfigDataQueryParams) IsRequestMadeForOneResource() bool { const ( InvalidConfigTypeErr = "invalid config type provided, please send a valid config type" ) + +type CmCsMetadataDto struct { + CmMap map[string]*bean3.ConfigData + SecretMap map[string]*bean3.ConfigData + ConfigAppLevelId int + ConfigEnvLevelId int +} + +type ResolvedCmCsMetadataDto struct { + ResolvedConfigMapData string + ResolvedSecretData string + VariableMapCM map[string]map[string]string + VariableMapCS map[string]map[string]string +} + +type ValuesDto struct { + Values string `json:"values"` +} + +type DeploymentTemplateMetadata struct { + DeploymentTemplateJson json.RawMessage + TemplateVersion string + IsAppMetricsEnabled bool +} + +const ( + NoDeploymentDoneForSelectedImage = "there were no deployments done for the selected image" + ExpectedWfrIdNotPassedInQueryParamErr = "wfrId is expected in the query param which was not passed" + SecretMaskedValue = "*****" +) diff --git a/pkg/configDiff/helper/helper.go b/pkg/configDiff/helper/helper.go index 70082a7bea..3bf5e5ffaa 100644 --- a/pkg/configDiff/helper/helper.go +++ b/pkg/configDiff/helper/helper.go @@ -1,7 +1,9 @@ package helper import ( + bean3 "github.com/devtron-labs/devtron/pkg/bean" bean2 "github.com/devtron-labs/devtron/pkg/configDiff/bean" + "github.com/devtron-labs/devtron/pkg/pipeline/bean" ) func GetCombinedPropertiesMap(cmcsKeyPropertyAppLevelMap, cmcsKeyPropertyEnvLevelMap map[string]*bean2.ConfigProperty) []*bean2.ConfigProperty { @@ -18,3 +20,39 @@ func GetCombinedPropertiesMap(cmcsKeyPropertyAppLevelMap, cmcsKeyPropertyEnvLeve } return combinedProperties } + +func GetKeysToDelete(cmcsData map[string]*bean3.ConfigData, resourceName string) []string { + keysToDelete := make([]string, 0, len(cmcsData)) + for key, _ := range cmcsData { + if key != resourceName { + keysToDelete = append(keysToDelete, key) + } + } + return keysToDelete +} + +func FilterOutMergedCmCsForResourceName(cmcsMerged *bean2.CmCsMetadataDto, resourceName string, resourceType bean.ResourceType) { + for _, key := range GetKeysToDelete(cmcsMerged.SecretMap, resourceName) { + delete(cmcsMerged.SecretMap, key) + } + for _, key := range GetKeysToDelete(cmcsMerged.CmMap, resourceName) { + delete(cmcsMerged.CmMap, key) + } + + // handle the case when a cm and a cs can have a same name, in that case, check from resource type if correct key is filtered out or not + if resourceType == bean.CS { + if len(cmcsMerged.CmMap) > 0 { + // delete all elements from cmMap as requested resource is of secret type + for key, _ := range cmcsMerged.CmMap { + delete(cmcsMerged.CmMap, key) + } + } + } else if resourceType == bean.CM { + if len(cmcsMerged.SecretMap) > 0 { + // delete all elements from secretMap as requested resource is of secret type + for key, _ := range cmcsMerged.SecretMap { + delete(cmcsMerged.SecretMap, key) + } + } + } +} diff --git a/pkg/configDiff/utils/utils.go b/pkg/configDiff/utils/utils.go index 8185993775..62d1272c31 100644 --- a/pkg/configDiff/utils/utils.go +++ b/pkg/configDiff/utils/utils.go @@ -14,3 +14,11 @@ func ConvertToJsonRawMessage(request interface{}) (json.RawMessage, error) { } return r, nil } + +func ConvertToString(req interface{}) (string, error) { + reqByte, err := json.Marshal(req) + if err != nil { + return "", err + } + return string(reqByte), nil +} diff --git a/pkg/generateManifest/DeploymentTemplateService.go b/pkg/generateManifest/DeploymentTemplateService.go index d4a7a2270d..fadd192726 100644 --- a/pkg/generateManifest/DeploymentTemplateService.go +++ b/pkg/generateManifest/DeploymentTemplateService.go @@ -62,6 +62,8 @@ type DeploymentTemplateService interface { GetDeploymentTemplate(ctx context.Context, request DeploymentTemplateRequest) (DeploymentTemplateResponse, error) GenerateManifest(ctx context.Context, request *DeploymentTemplateRequest, valuesYaml string) (*openapi2.TemplateChartResponse, error) GetRestartWorkloadData(ctx context.Context, appIds []int, envId int) (*RestartPodResponse, error) + GetDeploymentTemplateWithResolvedData(ctx context.Context, request DeploymentTemplateRequest) (DeploymentTemplateResponse, error) + ResolveTemplateVariables(ctx context.Context, values string, request DeploymentTemplateRequest) (string, map[string]string, error) } type DeploymentTemplateServiceImpl struct { Logger *zap.SugaredLogger @@ -201,6 +203,7 @@ func (impl DeploymentTemplateServiceImpl) FetchDeploymentsWithChartRefs(appId in func (impl DeploymentTemplateServiceImpl) GetDeploymentTemplate(ctx context.Context, request DeploymentTemplateRequest) (DeploymentTemplateResponse, error) { var result DeploymentTemplateResponse + var response *DeploymentTemplateResponse var values, resolvedValue string var err error var variableSnapshot map[string]string @@ -217,9 +220,9 @@ func (impl DeploymentTemplateServiceImpl) GetDeploymentTemplate(ctx context.Cont _, values, err = impl.chartRefService.GetAppOverrideForDefaultTemplate(request.ChartRefId) resolvedValue = values case repository.PublishedOnEnvironments: - values, resolvedValue, variableSnapshot, err = impl.fetchResolvedTemplateForPublishedEnvs(ctx, request) + response, err = impl.fetchResolvedTemplateForPublishedEnvs(ctx, request) case repository.DeployedOnSelfEnvironment, repository.DeployedOnOtherEnvironment: - values, resolvedValue, variableSnapshot, err = impl.fetchTemplateForDeployedEnv(ctx, request) + response, err = impl.fetchTemplateForDeployedEnv(ctx, request) } if err != nil { impl.Logger.Errorw("error in getting values", "err", err) @@ -227,6 +230,42 @@ func (impl DeploymentTemplateServiceImpl) GetDeploymentTemplate(ctx context.Cont } } + if request.RequestDataMode == Values { + result.Data = values + result.ResolvedData = resolvedValue + result.VariableSnapshot = variableSnapshot + if response != nil { + result = ConvertPointerDeploymentTemplateResponseToNonPointer(response) + } + return result, nil + } + if variableSnapshot != nil { + result.VariableSnapshot = variableSnapshot + } + request = impl.setRequestMetadata(&request) + manifest, err := impl.GenerateManifest(ctx, &request, resolvedValue) + if err != nil { + return result, err + } + if manifest != nil { + result.Data = *manifest.Manifest + } + return result, nil +} + +func (impl DeploymentTemplateServiceImpl) GetDeploymentTemplateWithResolvedData(ctx context.Context, request DeploymentTemplateRequest) (DeploymentTemplateResponse, error) { + var result DeploymentTemplateResponse + var values, resolvedValue string + var err error + var variableSnapshot map[string]string + + if request.Values != "" { + values = request.Values + resolvedValue, variableSnapshot, err = impl.resolveTemplateVariables(ctx, request.Values, request) + if err != nil { + return result, err + } + } if request.RequestDataMode == Values { result.Data = values result.ResolvedData = resolvedValue @@ -281,7 +320,7 @@ func (impl DeploymentTemplateServiceImpl) setRequestMetadata(request *Deployment return *request } -func (impl DeploymentTemplateServiceImpl) fetchResolvedTemplateForPublishedEnvs(ctx context.Context, request DeploymentTemplateRequest) (string, string, map[string]string, error) { +func (impl DeploymentTemplateServiceImpl) fetchResolvedTemplateForPublishedEnvs(ctx context.Context, request DeploymentTemplateRequest) (*DeploymentTemplateResponse, error) { var values string override, err := impl.propertiesConfigService.GetEnvironmentProperties(request.AppId, request.EnvId, request.ChartRefId) if err == nil && override.GlobalConfig != nil { @@ -292,24 +331,47 @@ func (impl DeploymentTemplateServiceImpl) fetchResolvedTemplateForPublishedEnvs( } } else { impl.Logger.Errorw("error in getting overridden values", "err", err) - return "", "", nil, err + return nil, err + } + // handle here for chart ref id in case + chartRefId := override.EnvironmentConfig.ChartRefId + if chartRefId == 0 { + chartRefId = override.GlobalChartRefId + } + _, _, version, _, err := impl.chartRefService.GetRefChart(chartRefId) + if err != nil { + impl.Logger.Errorw("error in getting chart ref by chartRefId ", "chartRefId", request.ChartRefId, "err", err) + return nil, err } resolvedTemplate, variableSnapshot, err := impl.resolveTemplateVariables(ctx, values, request) if err != nil { - return values, values, variableSnapshot, err + impl.Logger.Errorw("error in resolving template variables for env override ", "deploymentTemplateRequest", request, "err", err) + return nil, err } - return values, resolvedTemplate, variableSnapshot, nil + return &DeploymentTemplateResponse{ + Data: values, + ResolvedData: resolvedTemplate, + VariableSnapshot: variableSnapshot, + TemplateVersion: version, + IsAppMetricsEnabled: *override.AppMetrics, + }, nil } -func (impl DeploymentTemplateServiceImpl) fetchTemplateForDeployedEnv(ctx context.Context, request DeploymentTemplateRequest) (string, string, map[string]string, error) { +func (impl DeploymentTemplateServiceImpl) fetchTemplateForDeployedEnv(ctx context.Context, request DeploymentTemplateRequest) (*DeploymentTemplateResponse, error) { historyObject, err := impl.deploymentTemplateHistoryService.GetHistoryForDeployedTemplateById(ctx, request.DeploymentTemplateHistoryId, request.PipelineId) if err != nil { impl.Logger.Errorw("error in getting deployment template history", "err", err, "id", request.DeploymentTemplateHistoryId, "pipelineId", request.PipelineId) - return "", "", nil, err + return nil, err } //todo Subhashish solve variable leak - return historyObject.CodeEditorValue.Value, historyObject.CodeEditorValue.ResolvedValue, historyObject.CodeEditorValue.VariableSnapshot, nil + return &DeploymentTemplateResponse{ + Data: historyObject.CodeEditorValue.Value, + ResolvedData: historyObject.CodeEditorValue.ResolvedValue, + VariableSnapshot: historyObject.CodeEditorValue.VariableSnapshot, + TemplateVersion: historyObject.TemplateVersion, + IsAppMetricsEnabled: *historyObject.IsAppMetricsEnabled, + }, nil } func (impl DeploymentTemplateServiceImpl) resolveTemplateVariables(ctx context.Context, values string, request DeploymentTemplateRequest) (string, map[string]string, error) { @@ -557,3 +619,21 @@ func (impl DeploymentTemplateServiceImpl) GetRestartWorkloadData(ctx context.Con } return podResp, nil } + +func (impl DeploymentTemplateServiceImpl) ResolveTemplateVariables(ctx context.Context, values string, request DeploymentTemplateRequest) (string, map[string]string, error) { + + isSuperAdmin, err := util2.GetIsSuperAdminFromContext(ctx) + if err != nil { + return values, nil, err + } + scope, err := impl.extractScopeData(request) + if err != nil { + return values, nil, err + } + maskUnknownVariableForHelmGenerate := request.RequestDataMode == Manifest + resolvedTemplate, variableSnapshot, err := impl.scopedVariableManager.ExtractVariablesAndResolveTemplate(scope, values, parsers.JsonVariableTemplate, isSuperAdmin, maskUnknownVariableForHelmGenerate) + if err != nil { + return values, variableSnapshot, err + } + return resolvedTemplate, variableSnapshot, nil +} diff --git a/pkg/generateManifest/adapter.go b/pkg/generateManifest/adapter.go new file mode 100644 index 0000000000..9528ec2d8b --- /dev/null +++ b/pkg/generateManifest/adapter.go @@ -0,0 +1,8 @@ +package generateManifest + +func ConvertPointerDeploymentTemplateResponseToNonPointer(r *DeploymentTemplateResponse) DeploymentTemplateResponse { + if r != nil { + return *r + } + return DeploymentTemplateResponse{} +} diff --git a/pkg/generateManifest/bean.go b/pkg/generateManifest/bean.go index 124a0cf083..26a465dd3f 100644 --- a/pkg/generateManifest/bean.go +++ b/pkg/generateManifest/bean.go @@ -69,9 +69,11 @@ var ReleaseIdentifier = &gRPC.ReleaseIdentifier{ } type DeploymentTemplateResponse struct { - Data string `json:"data"` - ResolvedData string `json:"resolvedData"` - VariableSnapshot map[string]string `json:"variableSnapshot"` + Data string `json:"data"` + ResolvedData string `json:"resolvedData"` + VariableSnapshot map[string]string `json:"variableSnapshot"` + TemplateVersion string `json:"-"` + IsAppMetricsEnabled bool `json:"-"` } type RestartPodResponse struct { diff --git a/pkg/pipeline/ConfigMapService.go b/pkg/pipeline/ConfigMapService.go index d2e0bb883c..bbfb74148b 100644 --- a/pkg/pipeline/ConfigMapService.go +++ b/pkg/pipeline/ConfigMapService.go @@ -47,10 +47,6 @@ const ( HashiCorpVault string = "HashiCorpVault" ) -type ConfigsList struct { - ConfigData []*bean.ConfigData `json:"maps"` -} - type ConfigMapService interface { CMGlobalAddUpdate(configMapRequest *bean.ConfigDataRequest) (*bean.ConfigDataRequest, error) CMGlobalFetch(appId int) (*bean.ConfigDataRequest, error) @@ -165,7 +161,7 @@ func (impl ConfigMapServiceImpl) CMGlobalAddUpdate(configMapRequest *bean.Config impl.logger.Errorw("error while fetching from db", "error", err) return nil, err } - configsList := &ConfigsList{} + configsList := &bean.ConfigsList{} found := false var configs []*bean.ConfigData if len(model.ConfigMapData) > 0 { @@ -208,7 +204,7 @@ func (impl ConfigMapServiceImpl) CMGlobalAddUpdate(configMapRequest *bean.Config } else { //creating config map record for first time - configsList := &ConfigsList{ + configsList := &bean.ConfigsList{ ConfigData: configMapRequest.ConfigData, } configDataByte, err := json.Marshal(configsList) @@ -254,7 +250,7 @@ func (impl ConfigMapServiceImpl) CMGlobalFetch(appId int) (*bean.ConfigDataReque impl.logger.Debugw("no config map data found for this request", "appId", appId) } - configMapGlobalList := &ConfigsList{} + configMapGlobalList := &bean.ConfigsList{} if len(configMapGlobal.ConfigMapData) > 0 { err = json.Unmarshal([]byte(configMapGlobal.ConfigMapData), configMapGlobalList) if err != nil { @@ -301,7 +297,7 @@ func (impl ConfigMapServiceImpl) CMEnvironmentAddUpdate(configMapRequest *bean.C return nil, err } if err == nil && model.Id > 0 { - configsList := &ConfigsList{} + configsList := &bean.ConfigsList{} found := false var configs []*bean.ConfigData if len(model.ConfigMapData) > 0 { @@ -345,7 +341,7 @@ func (impl ConfigMapServiceImpl) CMEnvironmentAddUpdate(configMapRequest *bean.C } else if err == pg.ErrNoRows { //creating config map record for first time - configsList := &ConfigsList{ + configsList := &bean.ConfigsList{ ConfigData: configMapRequest.ConfigData, } configDataByte, err := json.Marshal(configsList) @@ -391,7 +387,7 @@ func (impl ConfigMapServiceImpl) CMGlobalFetchForEdit(name string, id int) (*bea impl.logger.Debugw("no config map data found for this request", "id", id) } - configMapGlobalList := &ConfigsList{} + configMapGlobalList := &bean.ConfigsList{} if len(configMapGlobal.ConfigMapData) > 0 { err = json.Unmarshal([]byte(configMapGlobal.ConfigMapData), configMapGlobalList) if err != nil { @@ -439,7 +435,7 @@ func (impl ConfigMapServiceImpl) CMEnvironmentFetch(appId int, envId int) (*bean if pg.ErrNoRows == err { impl.logger.Debugw("no config map data found for this request", "appId", appId) } - configMapGlobalList := &ConfigsList{} + configMapGlobalList := &bean.ConfigsList{} if len(configMapGlobal.ConfigMapData) > 0 { err = json.Unmarshal([]byte(configMapGlobal.ConfigMapData), configMapGlobalList) if err != nil { @@ -454,7 +450,7 @@ func (impl ConfigMapServiceImpl) CMEnvironmentFetch(appId int, envId int) (*bean if pg.ErrNoRows == err { impl.logger.Debugw("no config map data found for this request", "appId", appId) } - configsListEnvLevel := &ConfigsList{} + configsListEnvLevel := &bean.ConfigsList{} if len(configMapEnvLevel.ConfigMapData) > 0 { err = json.Unmarshal([]byte(configMapEnvLevel.ConfigMapData), configsListEnvLevel) if err != nil { @@ -918,7 +914,7 @@ func (impl ConfigMapServiceImpl) CMGlobalDelete(name string, id int, userId int3 impl.logger.Errorw("error while fetching from db", "error", err) return false, err } - configsList := &ConfigsList{} + configsList := &bean.ConfigsList{} found := false var configs []*bean.ConfigData if len(model.ConfigMapData) > 0 { @@ -974,7 +970,7 @@ func (impl ConfigMapServiceImpl) CMEnvironmentDelete(name string, id int, userId impl.logger.Errorw("error while fetching from db", "error", err) return false, err } - configsList := &ConfigsList{} + configsList := &bean.ConfigsList{} found := false var configs []*bean.ConfigData if len(model.ConfigMapData) > 0 { @@ -1140,7 +1136,7 @@ func (impl ConfigMapServiceImpl) CMGlobalDeleteByAppId(name string, appId int, u impl.logger.Errorw("error while fetching from db", "error", err) return false, err } - configsList := &ConfigsList{} + configsList := &bean.ConfigsList{} found := false var configs []*bean.ConfigData if len(model.ConfigMapData) > 0 { @@ -1190,7 +1186,7 @@ func (impl ConfigMapServiceImpl) CMEnvironmentDeleteByAppIdAndEnvId(name string, impl.logger.Errorw("error while fetching from db", "error", err) return false, err } - configsList := &ConfigsList{} + configsList := &bean.ConfigsList{} found := false var configs []*bean.ConfigData if len(model.ConfigMapData) > 0 { @@ -1540,7 +1536,7 @@ func (impl ConfigMapServiceImpl) ConfigSecretGlobalBulkPatch(bulkPatchRequest *b continue } if bulkPatchRequest.Type == "CM" { - configsList := &ConfigsList{} + configsList := &bean.ConfigsList{} var configs []*bean.ConfigData if len(model.ConfigMapData) > 0 { err = json.Unmarshal([]byte(model.ConfigMapData), configsList) @@ -1645,7 +1641,7 @@ func (impl ConfigMapServiceImpl) ConfigSecretEnvironmentBulkPatch(bulkPatchReque continue } if bulkPatchRequest.Type == "CM" { - configsList := &ConfigsList{} + configsList := &bean.ConfigsList{} var configs []*bean.ConfigData if len(model.ConfigMapData) > 0 { err = json.Unmarshal([]byte(model.ConfigMapData), configsList) diff --git a/pkg/pipeline/DeploymentConfigService.go b/pkg/pipeline/DeploymentConfigService.go index 798f7da2c4..bb35e4fc41 100644 --- a/pkg/pipeline/DeploymentConfigService.go +++ b/pkg/pipeline/DeploymentConfigService.go @@ -38,6 +38,8 @@ import ( type PipelineDeploymentConfigService interface { GetLatestDeploymentConfigurationByPipelineId(ctx context.Context, pipelineId int, userHasAdminAccess bool) (*history.AllDeploymentConfigurationDetail, error) + GetMergedCMCSConfigMap(appLevelConfig, envLevelConfig string, configType repository2.ConfigType) (map[string]*bean.ConfigData, error) + GetLatestPipelineStrategyConfig(pipeline *pipelineConfig.Pipeline) (*history.HistoryDetailDto, error) } type PipelineDeploymentConfigServiceImpl struct { diff --git a/pkg/pipeline/PropertiesConfig.go b/pkg/pipeline/PropertiesConfig.go index 26c387897e..ad4e2806d6 100644 --- a/pkg/pipeline/PropertiesConfig.go +++ b/pkg/pipeline/PropertiesConfig.go @@ -129,6 +129,9 @@ func (impl PropertiesConfigServiceImpl) GetEnvironmentProperties(appId, environm IsBasicViewLocked: envOverride.IsBasicViewLocked, CurrentViewEditor: envOverride.CurrentViewEditor, } + if chartRefId == 0 && envOverride.Chart != nil { + environmentProperties.ChartRefId = envOverride.Chart.ChartRefId + } if environmentPropertiesResponse.Namespace == "" { environmentPropertiesResponse.Namespace = envOverride.Namespace @@ -140,8 +143,10 @@ func (impl PropertiesConfigServiceImpl) GetEnvironmentProperties(appId, environm } if errors.IsNotFound(err) { environmentProperties.Id = 0 - environmentProperties.ChartRefId = chartRefId environmentProperties.IsOverride = false + if chartRefId > 0 { + environmentProperties.ChartRefId = chartRefId + } } else { environmentProperties.Id = ecOverride.Id environmentProperties.Latest = ecOverride.Latest @@ -153,6 +158,9 @@ func (impl PropertiesConfigServiceImpl) GetEnvironmentProperties(appId, environm environmentProperties.Active = ecOverride.Active environmentProperties.IsBasicViewLocked = ecOverride.IsBasicViewLocked environmentProperties.CurrentViewEditor = ecOverride.CurrentViewEditor + if chartRefId == 0 && ecOverride.Chart != nil { + environmentProperties.ChartRefId = ecOverride.Chart.ChartRefId + } } environmentPropertiesResponse.ChartRefId = chartRefId environmentPropertiesResponse.EnvironmentConfig = *environmentProperties diff --git a/pkg/pipeline/adapter/adapter.go b/pkg/pipeline/adapter/adapter.go index ed179e1458..e587a119bc 100644 --- a/pkg/pipeline/adapter/adapter.go +++ b/pkg/pipeline/adapter/adapter.go @@ -21,6 +21,7 @@ import ( dockerRegistryRepository "github.com/devtron-labs/devtron/internal/sql/repository/dockerRegistry" "github.com/devtron-labs/devtron/internal/sql/repository/pipelineConfig" "github.com/devtron-labs/devtron/internal/sql/repository/pipelineConfig/bean/ciPipeline" + "github.com/devtron-labs/devtron/pkg/bean" pipelineConfigBean "github.com/devtron-labs/devtron/pkg/pipeline/bean" "github.com/devtron-labs/devtron/pkg/pipeline/bean/CiPipeline" "github.com/devtron-labs/devtron/pkg/pipeline/types" @@ -225,3 +226,130 @@ func GetSourceCiDownStreamResponse(linkedCIDetails []ciPipeline.LinkedCIDetails, } return response } + +func ConvertConfigDataToPipelineConfigData(r *bean.ConfigData) *pipelineConfigBean.ConfigData { + if r != nil { + return &pipelineConfigBean.ConfigData{ + Name: r.Name, + Type: r.Type, + External: r.External, + MountPath: r.MountPath, + Data: r.Data, + DefaultData: r.DefaultData, + DefaultMountPath: r.DefaultMountPath, + Global: r.Global, + ExternalSecretType: r.ExternalSecretType, + ESOSecretData: ConvertESOSecretDataToPipelineESOSecretData(r.ESOSecretData), + DefaultESOSecretData: ConvertESOSecretDataToPipelineESOSecretData(r.DefaultESOSecretData), + ExternalSecret: ConvertExternalSecretToPipelineExternalSecret(r.ExternalSecret), + DefaultExternalSecret: ConvertExternalSecretToPipelineExternalSecret(r.DefaultExternalSecret), + RoleARN: r.RoleARN, + SubPath: r.SubPath, + ESOSubPath: r.ESOSubPath, + FilePermission: r.FilePermission, + Overridden: r.Overridden, + } + } + return &pipelineConfigBean.ConfigData{} +} + +func ConvertESOSecretDataToPipelineESOSecretData(r bean.ESOSecretData) pipelineConfigBean.ESOSecretData { + return pipelineConfigBean.ESOSecretData{ + SecretStore: r.SecretStore, + SecretStoreRef: r.SecretStoreRef, + ESOData: ConvertEsoDataToPipelineEsoData(r.ESOData), + RefreshInterval: r.RefreshInterval, + } +} + +func ConvertExternalSecretToPipelineExternalSecret(r []bean.ExternalSecret) []pipelineConfigBean.ExternalSecret { + extSec := make([]pipelineConfigBean.ExternalSecret, 0, len(r)) + for _, item := range r { + newItem := pipelineConfigBean.ExternalSecret{ + Key: item.Key, + Name: item.Name, + Property: item.Property, + IsBinary: item.IsBinary, + } + extSec = append(extSec, newItem) + } + return extSec +} + +func ConvertEsoDataToPipelineEsoData(r []bean.ESOData) []pipelineConfigBean.ESOData { + newEsoData := make([]pipelineConfigBean.ESOData, 0, len(r)) + for _, item := range r { + newItem := pipelineConfigBean.ESOData{ + SecretKey: item.SecretKey, + Key: item.Key, + Property: item.Property, + } + newEsoData = append(newEsoData, newItem) + } + return newEsoData +} + +// reverse adapter for the above adapters + +func ConvertPipelineConfigDataToConfigData(r *pipelineConfigBean.ConfigData) *bean.ConfigData { + if r != nil { + return &bean.ConfigData{ + Name: r.Name, + Type: r.Type, + External: r.External, + MountPath: r.MountPath, + Data: r.Data, + DefaultData: r.DefaultData, + DefaultMountPath: r.DefaultMountPath, + Global: r.Global, + ExternalSecretType: r.ExternalSecretType, + ESOSecretData: ConvertPipelineESOSecretDataToESOSecretData(r.ESOSecretData), + DefaultESOSecretData: ConvertPipelineESOSecretDataToESOSecretData(r.DefaultESOSecretData), + ExternalSecret: ConvertPipelineExternalSecretToExternalSecret(r.ExternalSecret), + DefaultExternalSecret: ConvertPipelineExternalSecretToExternalSecret(r.DefaultExternalSecret), + RoleARN: r.RoleARN, + SubPath: r.SubPath, + ESOSubPath: r.ESOSubPath, + FilePermission: r.FilePermission, + Overridden: r.Overridden, + } + } + return &bean.ConfigData{} + +} + +func ConvertPipelineESOSecretDataToESOSecretData(r pipelineConfigBean.ESOSecretData) bean.ESOSecretData { + return bean.ESOSecretData{ + SecretStore: r.SecretStore, + SecretStoreRef: r.SecretStoreRef, + ESOData: ConvertPipelineEsoDataToEsoData(r.ESOData), + RefreshInterval: r.RefreshInterval, + } +} + +func ConvertPipelineExternalSecretToExternalSecret(r []pipelineConfigBean.ExternalSecret) []bean.ExternalSecret { + extSec := make([]bean.ExternalSecret, 0, len(r)) + for _, item := range r { + newItem := bean.ExternalSecret{ + Key: item.Key, + Name: item.Name, + Property: item.Property, + IsBinary: item.IsBinary, + } + extSec = append(extSec, newItem) + } + return extSec +} + +func ConvertPipelineEsoDataToEsoData(r []pipelineConfigBean.ESOData) []bean.ESOData { + newEsoData := make([]bean.ESOData, 0, len(r)) + for _, item := range r { + newItem := bean.ESOData{ + SecretKey: item.SecretKey, + Key: item.Key, + Property: item.Property, + } + newEsoData = append(newEsoData, newItem) + } + return newEsoData +} diff --git a/pkg/pipeline/bean/ConfigMapBean.go b/pkg/pipeline/bean/ConfigMapBean.go index 8a9e78a5ee..65cb8af637 100644 --- a/pkg/pipeline/bean/ConfigMapBean.go +++ b/pkg/pipeline/bean/ConfigMapBean.go @@ -44,6 +44,8 @@ type ESOData struct { Property string `json:"property,omitempty"` } +// there is an adapter written in pkg/bean folder to convert below ConfigData struct to pkg/bean's ConfigData + type ConfigData struct { Name string `json:"name"` Type string `json:"type"` @@ -117,6 +119,10 @@ type SecretsList struct { ConfigData []*ConfigData `json:"secrets"` } +type ConfigsList struct { + ConfigData []*ConfigData `json:"maps"` +} + type ConfigNameAndType struct { Id int Name string @@ -129,6 +135,7 @@ const ( CM ResourceType = "ConfigMap" CS ResourceType = "Secret" DeploymentTemplate ResourceType = "Deployment Template" + PipelineStrategy ResourceType = "Pipeline Strategy" ) func (r ResourceType) ToString() string { diff --git a/pkg/pipeline/history/ConfigMapHistoryService.go b/pkg/pipeline/history/ConfigMapHistoryService.go index 4f56673bd1..07c375db8d 100644 --- a/pkg/pipeline/history/ConfigMapHistoryService.go +++ b/pkg/pipeline/history/ConfigMapHistoryService.go @@ -20,6 +20,11 @@ import ( "context" "encoding/json" "errors" + "github.com/devtron-labs/devtron/pkg/configDiff/adaptor" + bean2 "github.com/devtron-labs/devtron/pkg/configDiff/bean" + "github.com/devtron-labs/devtron/pkg/configDiff/utils" + "github.com/devtron-labs/devtron/pkg/pipeline/adapter" + bean3 "github.com/devtron-labs/devtron/pkg/pipeline/bean" globalUtil "github.com/devtron-labs/devtron/util" "time" @@ -48,6 +53,8 @@ type ConfigMapHistoryService interface { CheckIfTriggerHistoryExistsForPipelineIdOnTime(pipelineId int, deployedOn time.Time) (cmId int, csId int, exists bool, err error) GetDeployedHistoryDetailForCMCSByPipelineIdAndWfrId(ctx context.Context, pipelineId, wfrId int, configType repository.ConfigType, userHasAdminAccess bool) ([]*ComponentLevelHistoryDetailDto, error) ConvertConfigDataToComponentLevelDto(config *bean.ConfigData, configType repository.ConfigType, userHasAdminAccess bool) (*ComponentLevelHistoryDetailDto, error) + + GetConfigmapHistoryDataByDeployedOnAndPipelineId(ctx context.Context, pipelineId int, deployedOn time.Time, userHasAdminAccess bool) (*bean2.DeploymentAndCmCsConfig, *bean2.DeploymentAndCmCsConfig, error) } type ConfigMapHistoryServiceImpl struct { @@ -691,3 +698,124 @@ func (impl ConfigMapHistoryServiceImpl) CheckIfTriggerHistoryExistsForPipelineId } return cmId, csId, exists, nil } + +func (impl ConfigMapHistoryServiceImpl) GetConfigmapHistoryDataByDeployedOnAndPipelineId(ctx context.Context, pipelineId int, deployedOn time.Time, userHasAdminAccess bool) (*bean2.DeploymentAndCmCsConfig, *bean2.DeploymentAndCmCsConfig, error) { + secretConfigData, err := impl.getResolvedConfigData(ctx, pipelineId, deployedOn, repository.SECRET_TYPE, userHasAdminAccess) + if err != nil { + impl.logger.Errorw("error in getting resolved secret config data in case of previous deployments ", "pipelineId", pipelineId, "deployedOn", deployedOn, "err", err) + return nil, nil, err + } + cmConfigData, err := impl.getResolvedConfigData(ctx, pipelineId, deployedOn, repository.CONFIGMAP_TYPE, userHasAdminAccess) + if err != nil { + impl.logger.Errorw("error in getting resolved cm config data in case of previous deployments ", "pipelineId", pipelineId, "deployedOn", deployedOn, "err", err) + return nil, nil, err + } + + return secretConfigData, cmConfigData, nil +} + +func (impl *ConfigMapHistoryServiceImpl) getResolvedConfigData(ctx context.Context, pipelineId int, deployedOn time.Time, configType repository.ConfigType, userHasAdminAccess bool) (*bean2.DeploymentAndCmCsConfig, error) { + configsList := &bean3.ConfigsList{} + secretsList := &bean3.SecretsList{} + var err error + history, err := impl.configMapHistoryRepository.GetDeployedHistoryByPipelineIdAndDeployedOn(pipelineId, deployedOn, configType) + if err != nil { + impl.logger.Errorw("error in getting deployed history by pipeline id and deployed on", "pipelineId", pipelineId, "deployedOn", deployedOn, "err", err) + return nil, err + } + if configType == repository.SECRET_TYPE { + _, secretsList, err = impl.getConfigDataRequestForHistory(history) + if err != nil { + impl.logger.Errorw("error in getting config data request for history", "err", err) + return nil, err + } + } else if configType == repository.CONFIGMAP_TYPE { + configsList, _, err = impl.getConfigDataRequestForHistory(history) + if err != nil { + impl.logger.Errorw("error in getting config data request for history", "cmCsHistory", history, "err", err) + return nil, err + } + } + + resolvedDataMap, variableSnapshotMap, err := impl.scopedVariableManager.GetResolvedCMCSHistoryDtos(ctx, configType, adaptor.ReverseConfigListConvertor(*configsList), history, adaptor.ReverseSecretListConvertor(*secretsList)) + if err != nil { + return nil, err + } + resolvedConfigDataList := make([]*bean3.ConfigData, 0, len(resolvedDataMap)) + for _, resolvedConfigData := range resolvedDataMap { + resolvedConfigDataList = append(resolvedConfigDataList, adapter.ConvertConfigDataToPipelineConfigData(&resolvedConfigData)) + } + configDataReq := &bean3.ConfigDataRequest{} + var resourceType bean3.ResourceType + if configType == repository.SECRET_TYPE { + impl.encodeSecretDataFromNonAdminUsers(secretsList.ConfigData, userHasAdminAccess) + impl.encodeSecretDataFromNonAdminUsers(resolvedConfigDataList, userHasAdminAccess) + configDataReq.ConfigData = secretsList.ConfigData + resourceType = bean3.CS + } else if configType == repository.CONFIGMAP_TYPE { + configDataReq.ConfigData = configsList.ConfigData + resourceType = bean3.CM + } + + configDataJson, err := utils.ConvertToJsonRawMessage(configDataReq) + if err != nil { + impl.logger.Errorw("getCmCsPublishedConfigResponse, error in converting config data to json raw message", "pipelineId", pipelineId, "deployedOn", deployedOn, "err", err) + return nil, err + } + resolvedConfigDataReq := &bean3.ConfigDataRequest{ConfigData: resolvedConfigDataList} + resolvedConfigDataStringJson, err := utils.ConvertToJsonRawMessage(resolvedConfigDataReq) + if err != nil { + impl.logger.Errorw("getCmCsPublishedConfigResponse, error in ConvertToJsonRawMessage for resolvedJson", "resolvedJson", resolvedConfigDataStringJson, "err", err) + return nil, err + } + return bean2.NewDeploymentAndCmCsConfig().WithConfigData(configDataJson).WithResourceType(resourceType). + WithVariableSnapshot(variableSnapshotMap).WithResolvedValue(resolvedConfigDataStringJson), nil +} + +func (impl *ConfigMapHistoryServiceImpl) encodeSecretDataFromNonAdminUsers(configDataList []*bean3.ConfigData, userHasAdminAccess bool) { + for _, config := range configDataList { + if config.Data != nil { + if !userHasAdminAccess { + //removing keys and sending + resultMap := make(map[string]string) + resultMapFinal := make(map[string]string) + err := json.Unmarshal(config.Data, &resultMap) + if err != nil { + impl.logger.Errorw("unmarshal failed", "error", err) + return + } + for key, _ := range resultMap { + //hard-coding values to show them as hidden to user + resultMapFinal[key] = "*****" + } + config.Data, err = utils.ConvertToJsonRawMessage(resultMapFinal) + if err != nil { + impl.logger.Errorw("error while marshaling request", "err", err) + return + } + } + } + } +} + +func (impl ConfigMapHistoryServiceImpl) getConfigDataRequestForHistory(history *repository.ConfigmapAndSecretHistory) (*bean3.ConfigsList, *bean3.SecretsList, error) { + + configsList := &bean3.ConfigsList{} + secretsList := &bean3.SecretsList{} + if history.IsConfigmapHistorySecretType() { + err := json.Unmarshal([]byte(history.Data), secretsList) + if err != nil { + impl.logger.Errorw("error while Unmarshal in secret history data", "error", err) + return configsList, secretsList, err + } + return configsList, secretsList, nil + } else if history.IsConfigmapHistoryConfigMapType() { + err := json.Unmarshal([]byte(history.Data), configsList) + if err != nil { + impl.logger.Errorw("error while Unmarshal in config history data", "historyData", history.Data, "error", err) + return configsList, secretsList, err + } + return configsList, secretsList, nil + } + return configsList, secretsList, nil +} diff --git a/pkg/pipeline/history/DeployedConfigurationHistoryService.go b/pkg/pipeline/history/DeployedConfigurationHistoryService.go index 26124a6df7..241574a2f1 100644 --- a/pkg/pipeline/history/DeployedConfigurationHistoryService.go +++ b/pkg/pipeline/history/DeployedConfigurationHistoryService.go @@ -152,6 +152,7 @@ func (impl *DeployedConfigurationHistoryServiceImpl) GetDeployedConfigurationByW impl.logger.Errorw("error in checking if history exists for deployment template", "err", err, "pipelineId", pipelineId, "wfrId", wfrId) return nil, err } + deploymentTemplateConfiguration := &DeploymentConfigurationDto{ Name: DEPLOYMENT_TEMPLATE_TYPE_HISTORY_COMPONENT, } @@ -161,6 +162,7 @@ func (impl *DeployedConfigurationHistoryServiceImpl) GetDeployedConfigurationByW deployedConfigurations = append(deployedConfigurations, deploymentTemplateConfiguration) //checking if pipeline strategy configuration for this pipelineId and wfrId exists or not + strategyHistoryId, exists, err := impl.strategyHistoryService.CheckIfHistoryExistsForPipelineIdAndWfrId(newCtx, pipelineId, wfrId) if err != nil { impl.logger.Errorw("error in checking if history exists for pipeline strategy", "err", err, "pipelineId", pipelineId, "wfrId", wfrId) diff --git a/pkg/pipeline/history/DeploymentTemplateHistoryService.go b/pkg/pipeline/history/DeploymentTemplateHistoryService.go index 276a090500..d2eac61f80 100644 --- a/pkg/pipeline/history/DeploymentTemplateHistoryService.go +++ b/pkg/pipeline/history/DeploymentTemplateHistoryService.go @@ -50,6 +50,8 @@ type DeploymentTemplateHistoryService interface { // used for rollback GetDeployedHistoryByPipelineIdAndWfrId(ctx context.Context, pipelineId, wfrId int) (*HistoryDetailDto, error) + + GetTemplateHistoryModelForDeployedTemplateById(deploymentTemplateHistoryId, pipelineId int) (*repository.DeploymentTemplateHistory, error) } type DeploymentTemplateHistoryServiceImpl struct { @@ -407,3 +409,12 @@ func (impl DeploymentTemplateHistoryServiceImpl) CheckIfTriggerHistoryExistsForP exists = true return deploymentTemplateHistoryId, exists, err } + +func (impl DeploymentTemplateHistoryServiceImpl) GetTemplateHistoryModelForDeployedTemplateById(deploymentTemplateHistoryId, pipelineId int) (*repository.DeploymentTemplateHistory, error) { + history, err := impl.deploymentTemplateHistoryRepository.GetHistoryForDeployedTemplateById(deploymentTemplateHistoryId, pipelineId) + if err != nil { + impl.logger.Errorw("error in getting deployment template history", "err", err, "deploymentTemplateHistoryId", deploymentTemplateHistoryId, "pipelineId", pipelineId) + return nil, err + } + return history, nil +} diff --git a/pkg/pipeline/history/repository/ConfigMapHistoryRepository.go b/pkg/pipeline/history/repository/ConfigMapHistoryRepository.go index e3a6918ee6..ebf45afe84 100644 --- a/pkg/pipeline/history/repository/ConfigMapHistoryRepository.go +++ b/pkg/pipeline/history/repository/ConfigMapHistoryRepository.go @@ -39,6 +39,7 @@ type ConfigMapHistoryRepository interface { GetHistoryByPipelineIdAndWfrId(pipelineId, wfrId int, configType ConfigType) (*ConfigmapAndSecretHistory, error) GetDeployedHistoryForPipelineIdOnTime(pipelineId int, deployedOn time.Time, configType ConfigType) (*ConfigmapAndSecretHistory, error) GetDeployedHistoryList(pipelineId, baseConfigId int, configType ConfigType, componentName string) ([]*ConfigmapAndSecretHistory, error) + GetDeployedHistoryByPipelineIdAndDeployedOn(pipelineId int, deployedOn time.Time, configType ConfigType) (*ConfigmapAndSecretHistory, error) } type ConfigMapHistoryRepositoryImpl struct { @@ -71,6 +72,13 @@ type ConfigmapAndSecretHistory struct { DeployedByEmailId string `sql:"-"` } +func (r *ConfigmapAndSecretHistory) IsConfigmapHistorySecretType() bool { + return r.DataType == SECRET_TYPE +} + +func (r *ConfigmapAndSecretHistory) IsConfigmapHistoryConfigMapType() bool { + return r.DataType == CONFIGMAP_TYPE +} func (impl ConfigMapHistoryRepositoryImpl) CreateHistory(tx *pg.Tx, model *ConfigmapAndSecretHistory) (*ConfigmapAndSecretHistory, error) { var err error if tx != nil { @@ -149,3 +157,14 @@ func (impl ConfigMapHistoryRepositoryImpl) GetDeployedHistoryForPipelineIdOnTime Select() return &history, err } + +func (impl ConfigMapHistoryRepositoryImpl) GetDeployedHistoryByPipelineIdAndDeployedOn(pipelineId int, deployedOn time.Time, configType ConfigType) (*ConfigmapAndSecretHistory, error) { + var history ConfigmapAndSecretHistory + err := impl.dbConnection.Model(&history). + Where("pipeline_id = ?", pipelineId). + Where("data_type = ?", configType). + Where("deployed_on = ?", deployedOn). + Where("deployed = ?", true). + Select() + return &history, err +} diff --git a/pkg/pipeline/history/repository/PipelineStrategyHistoryRepository.go b/pkg/pipeline/history/repository/PipelineStrategyHistoryRepository.go index 092a81239e..755d38686c 100644 --- a/pkg/pipeline/history/repository/PipelineStrategyHistoryRepository.go +++ b/pkg/pipeline/history/repository/PipelineStrategyHistoryRepository.go @@ -35,6 +35,7 @@ type PipelineStrategyHistoryRepository interface { GetHistoryByPipelineIdAndWfrId(ctx context.Context, pipelineId, wfrId int) (*PipelineStrategyHistory, error) CheckIfTriggerHistoryExistsForPipelineIdOnTime(pipelineId int, deployedOn time.Time) (bool, error) GetDeployedHistoryList(pipelineId, baseConfigId int) ([]*PipelineStrategyHistory, error) + FindPipelineStrategyForDeployedOnAndPipelineId(pipelineId int, deployedOn time.Time) (PipelineStrategyHistory, error) } type PipelineStrategyHistoryRepositoryImpl struct { @@ -145,3 +146,11 @@ func (impl PipelineStrategyHistoryRepositoryImpl) CheckIfTriggerHistoryExistsFor Exists() return exists, err } + +func (impl PipelineStrategyHistoryRepositoryImpl) FindPipelineStrategyForDeployedOnAndPipelineId(pipelineId int, deployedOn time.Time) (PipelineStrategyHistory, error) { + var history PipelineStrategyHistory + err := impl.dbConnection.Model(&history). + Where("pipeline_strategy_history.deployed_on = ?", deployedOn). + Where("pipeline_strategy_history.pipeline_id = ?", pipelineId).Select() + return history, err +} diff --git a/wire_gen.go b/wire_gen.go index f3f6bfe343..a53fca7b97 100644 --- a/wire_gen.go +++ b/wire_gen.go @@ -439,7 +439,7 @@ func InitializeApp() (*App, error) { ciWorkflowRepositoryImpl := pipelineConfig.NewCiWorkflowRepositoryImpl(db, sugaredLogger) ciPipelineMaterialRepositoryImpl := pipelineConfig.NewCiPipelineMaterialRepositoryImpl(db, sugaredLogger) ciArtifactRepositoryImpl := repository2.NewCiArtifactRepositoryImpl(db, sugaredLogger) - eventSimpleFactoryImpl := client2.NewEventSimpleFactoryImpl(sugaredLogger, cdWorkflowRepositoryImpl, pipelineOverrideRepositoryImpl, ciWorkflowRepositoryImpl, ciPipelineMaterialRepositoryImpl, ciPipelineRepositoryImpl, pipelineRepositoryImpl, userRepositoryImpl, ciArtifactRepositoryImpl) + eventSimpleFactoryImpl := client2.NewEventSimpleFactoryImpl(sugaredLogger, cdWorkflowRepositoryImpl, pipelineOverrideRepositoryImpl, ciWorkflowRepositoryImpl, ciPipelineMaterialRepositoryImpl, ciPipelineRepositoryImpl, pipelineRepositoryImpl, userRepositoryImpl, environmentRepositoryImpl, ciArtifactRepositoryImpl) applicationServiceClientImpl := application.NewApplicationClientImpl(sugaredLogger, argoCDConnectionManagerImpl) configMapRepositoryImpl := chartConfig.NewConfigMapRepositoryImpl(sugaredLogger, db) chartRepositoryImpl := chartRepoRepository.NewChartRepository(db, transactionUtilImpl) @@ -722,7 +722,7 @@ func InitializeApp() (*App, error) { if err != nil { return nil, err } - argoApplicationServiceExtendedImpl := argoApplication.NewArgoApplicationServiceExtendedServiceImpl(sugaredLogger, clusterRepositoryImpl, k8sServiceImpl, argoUserServiceImpl, helmAppClientImpl, helmAppServiceImpl, k8sApplicationServiceImpl, argoApplicationReadServiceImpl) + argoApplicationServiceExtendedImpl := argoApplication.NewArgoApplicationServiceExtendedServiceImpl(sugaredLogger, clusterRepositoryImpl, k8sServiceImpl, argoUserServiceImpl, helmAppClientImpl, helmAppServiceImpl, k8sApplicationServiceImpl, argoApplicationReadServiceImpl, applicationServiceClientImpl) installedAppResourceServiceImpl := resource.NewInstalledAppResourceServiceImpl(sugaredLogger, installedAppRepositoryImpl, appStoreApplicationVersionRepositoryImpl, applicationServiceClientImpl, acdAuthConfig, installedAppVersionHistoryRepositoryImpl, argoUserServiceImpl, helmAppClientImpl, helmAppServiceImpl, appStatusServiceImpl, k8sCommonServiceImpl, k8sApplicationServiceImpl, k8sServiceImpl, deploymentConfigServiceImpl, ociRegistryConfigRepositoryImpl, argoApplicationServiceExtendedImpl) chartGroupEntriesRepositoryImpl := repository17.NewChartGroupEntriesRepositoryImpl(db, sugaredLogger) chartGroupReposotoryImpl := repository17.NewChartGroupReposotoryImpl(db, sugaredLogger) @@ -951,7 +951,7 @@ func InitializeApp() (*App, error) { if err != nil { return nil, err } - deploymentConfigurationServiceImpl, err := configDiff.NewDeploymentConfigurationServiceImpl(sugaredLogger, configMapServiceImpl, appRepositoryImpl, environmentRepositoryImpl, chartServiceImpl, generateManifestDeploymentTemplateServiceImpl) + deploymentConfigurationServiceImpl, err := configDiff.NewDeploymentConfigurationServiceImpl(sugaredLogger, configMapServiceImpl, appRepositoryImpl, environmentRepositoryImpl, chartServiceImpl, generateManifestDeploymentTemplateServiceImpl, deploymentTemplateHistoryRepositoryImpl, pipelineStrategyHistoryRepositoryImpl, configMapHistoryRepositoryImpl, scopedVariableCMCSManagerImpl, configMapRepositoryImpl, pipelineDeploymentConfigServiceImpl, chartRefServiceImpl, pipelineRepositoryImpl, deploymentTemplateHistoryServiceImpl, configMapHistoryServiceImpl) if err != nil { return nil, err }