diff --git a/Wire.go b/Wire.go index 0d279f67c22..7d2cd72d88e 100644 --- a/Wire.go +++ b/Wire.go @@ -125,6 +125,7 @@ import ( "github.com/devtron-labs/devtron/pkg/chart/gitOpsConfig" chartRepoRepository "github.com/devtron-labs/devtron/pkg/chartRepo/repository" "github.com/devtron-labs/devtron/pkg/commonService" + "github.com/devtron-labs/devtron/pkg/configDiff" delete2 "github.com/devtron-labs/devtron/pkg/delete" deployment2 "github.com/devtron-labs/devtron/pkg/deployment" "github.com/devtron-labs/devtron/pkg/deployment/common" @@ -711,6 +712,13 @@ func InitializeApp() (*App, error) { scopedVariable.NewScopedVariableRestHandlerImpl, wire.Bind(new(scopedVariable.ScopedVariableRestHandler), new(*scopedVariable.ScopedVariableRestHandlerImpl)), + router.NewDeploymentConfigurationRouter, + wire.Bind(new(router.DeploymentConfigurationRouter), new(*router.DeploymentConfigurationRouterImpl)), + restHandler.NewDeploymentConfigurationRestHandlerImpl, + wire.Bind(new(restHandler.DeploymentConfigurationRestHandler), new(*restHandler.DeploymentConfigurationRestHandlerImpl)), + configDiff.NewDeploymentConfigurationServiceImpl, + wire.Bind(new(configDiff.DeploymentConfigurationService), new(*configDiff.DeploymentConfigurationServiceImpl)), + router.NewTelemetryRouterImpl, wire.Bind(new(router.TelemetryRouter), new(*router.TelemetryRouterImpl)), restHandler.NewTelemetryRestHandlerImpl, diff --git a/api/restHandler/DeploymentConfigurationRestHandler.go b/api/restHandler/DeploymentConfigurationRestHandler.go new file mode 100644 index 00000000000..a29776a6b65 --- /dev/null +++ b/api/restHandler/DeploymentConfigurationRestHandler.go @@ -0,0 +1,135 @@ +package restHandler + +import ( + "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" + "github.com/devtron-labs/devtron/util/rbac" + "github.com/gorilla/schema" + "go.uber.org/zap" + "gopkg.in/go-playground/validator.v9" + "net/http" +) + +type DeploymentConfigurationRestHandler interface { + ConfigAutoComplete(w http.ResponseWriter, r *http.Request) + GetConfigData(w http.ResponseWriter, r *http.Request) +} +type DeploymentConfigurationRestHandlerImpl struct { + logger *zap.SugaredLogger + userAuthService user.UserService + validator *validator.Validate + enforcerUtil rbac.EnforcerUtil + deploymentConfigurationService configDiff.DeploymentConfigurationService + enforcer casbin.Enforcer +} + +func NewDeploymentConfigurationRestHandlerImpl(logger *zap.SugaredLogger, + userAuthService user.UserService, + enforcerUtil rbac.EnforcerUtil, + deploymentConfigurationService configDiff.DeploymentConfigurationService, + enforcer casbin.Enforcer, +) *DeploymentConfigurationRestHandlerImpl { + return &DeploymentConfigurationRestHandlerImpl{ + logger: logger, + userAuthService: userAuthService, + enforcerUtil: enforcerUtil, + deploymentConfigurationService: deploymentConfigurationService, + enforcer: enforcer, + } +} + +func (handler *DeploymentConfigurationRestHandlerImpl) ConfigAutoComplete(w http.ResponseWriter, r *http.Request) { + userId, err := handler.userAuthService.GetLoggedInUser(r) + if userId == 0 || err != nil { + common.WriteJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized) + return + } + appId, err := common.ExtractIntQueryParam(w, r, "appId", 0) + if err != nil { + return + } + envId, err := common.ExtractIntQueryParam(w, r, "envId", 0) + if err != nil { + return + } + + //RBAC START + token := r.Header.Get(common.TokenHeaderKey) + object := handler.enforcerUtil.GetAppRBACNameByAppId(appId) + ok := handler.enforcerUtil.CheckAppRbacForAppOrJob(token, object, casbin.ActionGet) + if !ok { + common.WriteJsonResp(w, fmt.Errorf("unauthorized user"), nil, http.StatusForbidden) + return + } + //RBAC END + + res, err := handler.deploymentConfigurationService.ConfigAutoComplete(appId, envId) + if err != nil { + handler.logger.Errorw("service err, ConfigAutoComplete ", "appId", appId, "envId", envId, "err", err) + common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) + return + } + common.WriteJsonResp(w, err, res, http.StatusOK) +} + +func (handler *DeploymentConfigurationRestHandlerImpl) GetConfigData(w http.ResponseWriter, r *http.Request) { + userId, err := handler.userAuthService.GetLoggedInUser(r) + if userId == 0 || err != nil { + common.WriteJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized) + return + } + configDataQueryParams, err := getConfigDataQueryParams(r) + if err != nil { + common.WriteJsonResp(w, err, nil, http.StatusBadRequest) + return + } + + //RBAC START + token := r.Header.Get(common.TokenHeaderKey) + object := handler.enforcerUtil.GetAppRBACName(configDataQueryParams.AppName) + ok := handler.enforcerUtil.CheckAppRbacForAppOrJob(token, object, casbin.ActionGet) + if !ok { + common.WriteJsonResp(w, fmt.Errorf("unauthorized user"), nil, http.StatusForbidden) + return + } + //RBAC END + + res, err := handler.deploymentConfigurationService.GetAllConfigData(r.Context(), configDataQueryParams) + if err != nil { + handler.logger.Errorw("service err, GetAllConfigData ", "err", err) + common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) + return + } + res.IsAppAdmin = handler.enforceForAppAndEnv(configDataQueryParams.AppName, configDataQueryParams.EnvName, token, casbin.ActionUpdate) + + common.WriteJsonResp(w, nil, res, http.StatusOK) +} + +func (handler *DeploymentConfigurationRestHandlerImpl) enforceForAppAndEnv(appName, envName string, token string, action string) bool { + object := handler.enforcerUtil.GetAppRBACNameByAppName(appName) + if ok := handler.enforcer.Enforce(token, casbin.ResourceApplications, action, object); !ok { + return false + } + + object = handler.enforcerUtil.GetEnvRBACNameByAppAndEnvName(appName, envName) + if ok := handler.enforcer.Enforce(token, casbin.ResourceEnvironment, action, object); !ok { + return false + } + return true +} +func getConfigDataQueryParams(r *http.Request) (*bean.ConfigDataQueryParams, error) { + v := r.URL.Query() + var decoder = schema.NewDecoder() + decoder.IgnoreUnknownKeys(true) + queryParams := bean.ConfigDataQueryParams{} + err := decoder.Decode(&queryParams, v) + if err != nil { + return nil, err + } + + return &queryParams, nil +} diff --git a/api/router/DeploymentConfigRouter.go b/api/router/DeploymentConfigRouter.go new file mode 100644 index 00000000000..a8a568d6046 --- /dev/null +++ b/api/router/DeploymentConfigRouter.go @@ -0,0 +1,31 @@ +package router + +import ( + "github.com/devtron-labs/devtron/api/restHandler" + "github.com/gorilla/mux" +) + +type DeploymentConfigurationRouter interface { + initDeploymentConfigurationRouter(configRouter *mux.Router) +} + +type DeploymentConfigurationRouterImpl struct { + deploymentGroupRestHandler restHandler.DeploymentConfigurationRestHandler +} + +func NewDeploymentConfigurationRouter(deploymentGroupRestHandler restHandler.DeploymentConfigurationRestHandler) *DeploymentConfigurationRouterImpl { + router := &DeploymentConfigurationRouterImpl{ + deploymentGroupRestHandler: deploymentGroupRestHandler, + } + return router +} + +func (router DeploymentConfigurationRouterImpl) initDeploymentConfigurationRouter(configRouter *mux.Router) { + configRouter.Path("/autocomplete"). + HandlerFunc(router.deploymentGroupRestHandler.ConfigAutoComplete). + Methods("GET") + configRouter.Path("/data"). + HandlerFunc(router.deploymentGroupRestHandler.GetConfigData). + Methods("GET") + +} diff --git a/api/router/router.go b/api/router/router.go index 8902c53cafd..cbee85a928a 100644 --- a/api/router/router.go +++ b/api/router/router.go @@ -114,6 +114,7 @@ type MuxRouter struct { rbacRoleRouter user.RbacRoleRouter scopedVariableRouter ScopedVariableRouter ciTriggerCron cron.CiTriggerCron + deploymentConfigurationRouter DeploymentConfigurationRouter infraConfigRouter infraConfig.InfraConfigRouter argoApplicationRouter argoApplication.ArgoApplicationRouter fluxApplicationRouter fluxApplication2.FluxApplicationRouter @@ -146,6 +147,7 @@ func NewMuxRouter(logger *zap.SugaredLogger, scopedVariableRouter ScopedVariableRouter, ciTriggerCron cron.CiTriggerCron, proxyRouter proxy.ProxyRouter, + deploymentConfigurationRouter DeploymentConfigurationRouter, infraConfigRouter infraConfig.InfraConfigRouter, argoApplicationRouter argoApplication.ArgoApplicationRouter, devtronResourceRouter devtronResource.DevtronResourceRouter, @@ -210,6 +212,7 @@ func NewMuxRouter(logger *zap.SugaredLogger, rbacRoleRouter: rbacRoleRouter, scopedVariableRouter: scopedVariableRouter, ciTriggerCron: ciTriggerCron, + deploymentConfigurationRouter: deploymentConfigurationRouter, infraConfigRouter: infraConfigRouter, argoApplicationRouter: argoApplicationRouter, devtronResourceRouter: devtronResourceRouter, @@ -293,8 +296,9 @@ func (r MuxRouter) Init() { chartRefRouter := r.Router.PathPrefix("/orchestrator/chartref").Subrouter() r.ChartRefRouter.initChartRefRouter(chartRefRouter) - configMapRouter := r.Router.PathPrefix("/orchestrator/config").Subrouter() - r.ConfigMapRouter.initConfigMapRouter(configMapRouter) + configRouter := r.Router.PathPrefix("/orchestrator/config").Subrouter() + r.ConfigMapRouter.initConfigMapRouter(configRouter) + r.deploymentConfigurationRouter.initDeploymentConfigurationRouter(configRouter) appStoreRouter := r.Router.PathPrefix("/orchestrator/app-store").Subrouter() r.AppStoreRouter.Init(appStoreRouter) diff --git a/cmd/external-app/wire_gen.go b/cmd/external-app/wire_gen.go index c3a4512ff85..23dbbb83014 100644 --- a/cmd/external-app/wire_gen.go +++ b/cmd/external-app/wire_gen.go @@ -1,6 +1,6 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run github.com/google/wire/cmd/wire //go:build !wireinject // +build !wireinject @@ -199,8 +199,8 @@ func InitializeApp() (*App, error) { userAuthServiceImpl := user.NewUserAuthServiceImpl(userAuthRepositoryImpl, sessionManager, loginService, sugaredLogger, userRepositoryImpl, roleGroupRepositoryImpl, userServiceImpl) teamServiceImpl := team.NewTeamServiceImpl(sugaredLogger, teamRepositoryImpl, userAuthServiceImpl) clusterRepositoryImpl := repository2.NewClusterRepositoryImpl(db, sugaredLogger) - v := informer.NewGlobalMapClusterNamespace() - k8sInformerFactoryImpl := informer.NewK8sInformerFactoryImpl(sugaredLogger, v, k8sServiceImpl) + syncMap := informer.NewGlobalMapClusterNamespace() + k8sInformerFactoryImpl := informer.NewK8sInformerFactoryImpl(sugaredLogger, syncMap, k8sServiceImpl) clusterServiceImpl := cluster.NewClusterServiceImpl(clusterRepositoryImpl, sugaredLogger, k8sServiceImpl, k8sInformerFactoryImpl, userAuthRepositoryImpl, userRepositoryImpl, roleGroupRepositoryImpl) appStatusRepositoryImpl := appStatus.NewAppStatusRepositoryImpl(db, sugaredLogger) environmentRepositoryImpl := repository2.NewEnvironmentRepositoryImpl(db, sugaredLogger, appStatusRepositoryImpl) diff --git a/internal/sql/repository/app/AppRepository.go b/internal/sql/repository/app/AppRepository.go index fef6630bfd7..76d526f1744 100644 --- a/internal/sql/repository/app/AppRepository.go +++ b/internal/sql/repository/app/AppRepository.go @@ -50,6 +50,8 @@ type AppRepository interface { UpdateWithTxn(app *App, tx *pg.Tx) error SetDescription(id int, description string, userId int32) error FindActiveByName(appName string) (pipelineGroup *App, err error) + FindAppIdByName(appName string) (int, error) + FindJobByDisplayName(appName string) (pipelineGroup *App, err error) FindActiveListByName(appName string) ([]*App, error) FindById(id int) (pipelineGroup *App, err error) @@ -137,6 +139,19 @@ func (repo AppRepositoryImpl) FindActiveByName(appName string) (*App, error) { // there is only single active app will be present in db with a same name. return pipelineGroup, err } +func (repo AppRepositoryImpl) FindAppIdByName(appName string) (int, error) { + app := &App{} + err := repo.dbConnection. + Model(app). + Column("app.id"). + Where("app_name = ?", appName). + Where("active = ?", true). + Order("id DESC").Limit(1). + Select() + // there is only single active app will be present in db with a same name. + return app.Id, err +} + func (repo AppRepositoryImpl) FindJobByDisplayName(appName string) (*App, error) { pipelineGroup := &App{} err := repo.dbConnection. diff --git a/internal/sql/repository/chartConfig/ConfigMapRepository.go b/internal/sql/repository/chartConfig/ConfigMapRepository.go index 5cc12311991..764378765c3 100644 --- a/internal/sql/repository/chartConfig/ConfigMapRepository.go +++ b/internal/sql/repository/chartConfig/ConfigMapRepository.go @@ -17,6 +17,7 @@ package chartConfig import ( + "github.com/devtron-labs/devtron/pkg/pipeline/bean" "github.com/devtron-labs/devtron/pkg/sql" "github.com/go-pg/pg" "github.com/go-pg/pg/orm" @@ -38,6 +39,7 @@ type ConfigMapRepository interface { GetByAppIdAppLevel(appId int) (*ConfigMapAppModel, error) GetByAppIdAndEnvIdEnvLevel(appId int, envId int) (*ConfigMapEnvModel, error) GetEnvLevelByAppId(appId int) ([]*ConfigMapEnvModel, error) + GetConfigNamesForAppAndEnvLevel(appId int, envId int) ([]bean.ConfigNameAndType, error) } type ConfigMapRepositoryImpl struct { @@ -49,6 +51,11 @@ func NewConfigMapRepositoryImpl(Logger *zap.SugaredLogger, dbConnection *pg.DB) return &ConfigMapRepositoryImpl{dbConnection: dbConnection, Logger: Logger} } +const ( + ConfigMapAppLevel string = "config_map_app_level" + ConfigMapEnvLevel string = "config_map_env_level" +) + type ConfigMapAppModel struct { TableName struct{} `sql:"config_map_app_level" pg:",discard_unknown_columns"` Id int `sql:"id,pk"` @@ -57,6 +64,55 @@ type ConfigMapAppModel struct { SecretData string `sql:"secret_data"` sql.AuditLog } +type cMCSNames struct { + Id int `json:"id"` + CMName string `json:"cm_name"` + CSName string `json:"cs_name"` +} + +func (impl ConfigMapRepositoryImpl) GetConfigNamesForAppAndEnvLevel(appId int, envId int) ([]bean.ConfigNameAndType, error) { + var cMCSNames []cMCSNames + tableName := ConfigMapEnvLevel + if envId == -1 { + tableName = ConfigMapAppLevel + } + //below query iterates over the cm, cs stored as json element, and fetches cmName and csName, id for a particular appId or envId if provided + query := impl.dbConnection. + Model(). + Table(tableName). + Column("id"). + ColumnExpr("json_array_elements(CASE WHEN (config_map_data::json->'maps')::TEXT != 'null' THEN (config_map_data::json->'maps') ELSE '[]' END )->>'name' AS cm_name"). + ColumnExpr("json_array_elements(CASE WHEN (secret_data::json->'secrets')::TEXT != 'null' THEN (secret_data::json->'secrets') ELSE '[]' END )->>'name' AS cs_name"). + Where("app_id = ?", appId) + + if envId > 0 { + query = query.Where("environment_id=?", envId) + } + if err := query.Select(&cMCSNames); err != nil { + if err != pg.ErrNoRows { + impl.Logger.Errorw("error occurred while fetching CM/CS names", "appId", appId, "err", err) + return nil, err + } + } + var configNames []bean.ConfigNameAndType + for _, name := range cMCSNames { + if name.CMName != "" { + configNames = append(configNames, bean.ConfigNameAndType{ + Id: name.Id, + Name: name.CMName, + Type: bean.CM, + }) + } + if name.CSName != "" { + configNames = append(configNames, bean.ConfigNameAndType{ + Id: name.Id, + Name: name.CSName, + Type: bean.CS, + }) + } + } + return configNames, nil +} func (impl ConfigMapRepositoryImpl) CreateAppLevel(model *ConfigMapAppModel) (*ConfigMapAppModel, error) { err := impl.dbConnection.Insert(model) diff --git a/internal/sql/repository/chartConfig/PipelineOverrideRepository.go b/internal/sql/repository/chartConfig/PipelineOverrideRepository.go index aba3d7554c6..db8476d9132 100644 --- a/internal/sql/repository/chartConfig/PipelineOverrideRepository.go +++ b/internal/sql/repository/chartConfig/PipelineOverrideRepository.go @@ -58,6 +58,7 @@ type PipelineConfigOverrideMetadata struct { type PipelineOverrideRepository interface { Save(*PipelineOverride) error + Update(pipelineOverride *PipelineOverride) error UpdateStatusByRequestIdentifier(requestId string, newStatus models.ChartStatus) (int, error) GetLatestConfigByRequestIdentifier(requestIdentifier string) (pipelineOverride *PipelineOverride, err error) GetLatestConfigByEnvironmentConfigOverrideId(envConfigOverrideId int) (pipelineOverride *PipelineOverride, err error) @@ -85,6 +86,10 @@ func (impl PipelineOverrideRepositoryImpl) Save(pipelineOverride *PipelineOverri return impl.dbConnection.Insert(pipelineOverride) } +func (impl PipelineOverrideRepositoryImpl) Update(pipelineOverride *PipelineOverride) error { + return impl.dbConnection.Update(pipelineOverride) +} + func (impl PipelineOverrideRepositoryImpl) UpdatePipelineMergedValues(ctx context.Context, tx *pg.Tx, id int, pipelineMergedValues string, userId int32) error { _, span := otel.Tracer("orchestrator").Start(ctx, "PipelineOverrideRepositoryImpl.UpdatePipelineMergedValues") defer span.End() diff --git a/pkg/cluster/repository/EnvironmentRepository.go b/pkg/cluster/repository/EnvironmentRepository.go index c1864ca2864..048aa83ae76 100644 --- a/pkg/cluster/repository/EnvironmentRepository.go +++ b/pkg/cluster/repository/EnvironmentRepository.go @@ -61,6 +61,7 @@ type EnvironmentRepository interface { FindById(id int) (*Environment, error) Update(mappings *Environment) error FindByName(name string) (*Environment, error) + FindIdByName(name string) (int, error) FindByIdentifier(identifier string) (*Environment, error) FindByNameOrIdentifier(name string, identifier string) (*Environment, error) FindByEnvNameOrIdentifierOrNamespace(clusterId int, envName string, identifier string, namespace string) (*Environment, error) @@ -159,6 +160,18 @@ func (repositoryImpl EnvironmentRepositoryImpl) FindByName(name string) (*Enviro return environment, err } +func (repositoryImpl EnvironmentRepositoryImpl) FindIdByName(name string) (int, error) { + environment := &Environment{} + err := repositoryImpl.dbConnection. + Model(environment). + Column("environment.id"). + Where("environment_name = ?", name). + Where("active = ?", true). + Limit(1). + Select() + return environment.Id, err +} + func (repositoryImpl EnvironmentRepositoryImpl) FindByIdentifier(identifier string) (*Environment, error) { environment := &Environment{} err := repositoryImpl.dbConnection. diff --git a/pkg/configDiff/DeploymentConfigurationService.go b/pkg/configDiff/DeploymentConfigurationService.go new file mode 100644 index 00000000000..360de7f8b35 --- /dev/null +++ b/pkg/configDiff/DeploymentConfigurationService.go @@ -0,0 +1,281 @@ +package configDiff + +import ( + "context" + "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/util" + 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/generateManifest" + "github.com/devtron-labs/devtron/pkg/pipeline" + "github.com/devtron-labs/devtron/pkg/pipeline/bean" + "go.uber.org/zap" + "net/http" + "strconv" +) + +type DeploymentConfigurationService interface { + ConfigAutoComplete(appId int, envId int) (*bean2.ConfigDataResponse, error) + GetAllConfigData(ctx context.Context, configDataQueryParams *bean2.ConfigDataQueryParams) (*bean2.DeploymentAndCmCsConfigDto, error) +} + +type DeploymentConfigurationServiceImpl struct { + logger *zap.SugaredLogger + configMapService pipeline.ConfigMapService + appRepository appRepository.AppRepository + environmentRepository repository.EnvironmentRepository + chartService chartService.ChartService + deploymentTemplateService generateManifest.DeploymentTemplateService +} + +func NewDeploymentConfigurationServiceImpl(logger *zap.SugaredLogger, + configMapService pipeline.ConfigMapService, + appRepository appRepository.AppRepository, + environmentRepository repository.EnvironmentRepository, + chartService chartService.ChartService, + deploymentTemplateService generateManifest.DeploymentTemplateService, +) (*DeploymentConfigurationServiceImpl, error) { + deploymentConfigurationService := &DeploymentConfigurationServiceImpl{ + logger: logger, + configMapService: configMapService, + appRepository: appRepository, + environmentRepository: environmentRepository, + chartService: chartService, + deploymentTemplateService: deploymentTemplateService, + } + + return deploymentConfigurationService, nil +} +func (impl *DeploymentConfigurationServiceImpl) ConfigAutoComplete(appId int, envId int) (*bean2.ConfigDataResponse, error) { + cMCSNamesAppLevel, cMCSNamesEnvLevel, err := impl.configMapService.FetchCmCsNamesAppAndEnvLevel(appId, envId) + if err != nil { + impl.logger.Errorw("error in fetching CM and CS names at app or env level", "appId", appId, "envId", envId, "err", err) + return nil, err + } + cmcsKeyPropertyAppLevelMap, cmcsKeyPropertyEnvLevelMap := adaptor.GetCmCsAppAndEnvLevelMap(cMCSNamesAppLevel, cMCSNamesEnvLevel) + for key, configProperty := range cmcsKeyPropertyAppLevelMap { + if _, ok := cmcsKeyPropertyEnvLevelMap[key]; !ok { + if envId > 0 { + configProperty.ConfigStage = bean2.Inheriting + } + + } + } + for key, configProperty := range cmcsKeyPropertyEnvLevelMap { + if _, ok := cmcsKeyPropertyAppLevelMap[key]; ok { + configProperty.ConfigStage = bean2.Overridden + } else { + configProperty.ConfigStage = bean2.Env + } + } + combinedProperties := helper.GetCombinedPropertiesMap(cmcsKeyPropertyAppLevelMap, cmcsKeyPropertyEnvLevelMap) + combinedProperties = append(combinedProperties, adaptor.GetConfigProperty(0, "", bean.DeploymentTemplate, bean2.PublishedConfigState)) + + configDataResp := bean2.NewConfigDataResponse().WithResourceConfig(combinedProperties) + 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} + } + var err error + var envId int + var appId int + if configDataQueryParams.IsEnvNameProvided() { + envId, err = impl.environmentRepository.FindIdByName(configDataQueryParams.EnvName) + if err != nil { + impl.logger.Errorw("GetAllConfigData, error in getting environment model by envName", "envName", configDataQueryParams.EnvName, "err", err) + return nil, err + } + } + appId, err = impl.appRepository.FindAppIdByName(configDataQueryParams.AppName) + if err != nil { + impl.logger.Errorw("GetAllConfigData, error in getting app model by appName", "appName", configDataQueryParams.AppName, "err", err) + return nil, err + } + + configDataDto := &bean2.DeploymentAndCmCsConfigDto{} + switch configDataQueryParams.ConfigType { + default: // keeping default as PublishedOnly + configDataDto, err = impl.getPublishedConfigData(ctx, configDataQueryParams, appId, envId) + if err != nil { + impl.logger.Errorw("GetAllConfigData, error in config data for PublishedOnly", "configDataQueryParams", configDataQueryParams, "err", err) + return nil, err + } + } + return configDataDto, nil +} + +func (impl *DeploymentConfigurationServiceImpl) getCmCsEditDataForPublishedOnly(configDataQueryParams *bean2.ConfigDataQueryParams, envId, appId int) (*bean2.DeploymentAndCmCsConfigDto, error) { + configDataDto := &bean2.DeploymentAndCmCsConfigDto{} + + var resourceType bean.ResourceType + var fetchConfigFunc func(string, int, int, int) (*bean.ConfigDataRequest, error) + + if configDataQueryParams.IsResourceTypeSecret() { + //handles for single resource when resource type is secret and for a given resource name + resourceType = bean.CS + fetchConfigFunc = impl.getSecretConfigResponse + } else if configDataQueryParams.IsResourceTypeConfigMap() { + //handles for single resource when resource type is configMap and for a given resource name + resourceType = bean.CM + fetchConfigFunc = impl.getConfigMapResponse + } + cmcsConfigData, err := fetchConfigFunc(configDataQueryParams.ResourceName, configDataQueryParams.ResourceId, envId, appId) + if err != nil { + impl.logger.Errorw("getCmCsEditDataForPublishedOnly, error in getting config response", "resourceName", configDataQueryParams.ResourceName, "envName", configDataQueryParams.EnvName, "err", err) + return nil, err + } + + respJson, err := utils.ConvertToJsonRawMessage(cmcsConfigData) + if err != nil { + impl.logger.Errorw("getCmCsEditDataForPublishedOnly, error in converting to json raw message", "configDataQueryParams", configDataQueryParams, "err", err) + return nil, err + } + + cmCsConfig := bean2.NewDeploymentAndCmCsConfig().WithConfigData(respJson).WithResourceType(resourceType) + if resourceType == bean.CS { + configDataDto.WithSecretData(cmCsConfig) + } else if resourceType == bean.CM { + configDataDto.WithConfigMapData(cmCsConfig) + } + return configDataDto, nil +} + +func (impl *DeploymentConfigurationServiceImpl) getCmCsPublishedConfigResponse(envId, appId int) (*bean2.DeploymentAndCmCsConfigDto, error) { + + configDataDto := &bean2.DeploymentAndCmCsConfigDto{} + secretData, err := impl.getSecretConfigResponse("", 0, envId, appId) + if err != nil { + impl.logger.Errorw("getCmCsPublishedConfigResponse, error in getting secret config response by appId and envId", "appId", appId, "envId", envId, "err", err) + return nil, err + } + + //iterate on secret configData and then and set draft data from draftResourcesMap if same resourceName found do the same for configMap below + cmData, err := impl.getConfigMapResponse("", 0, envId, appId) + if err != nil { + impl.logger.Errorw("getCmCsPublishedConfigResponse, error in getting config map by appId and envId", "appId", appId, "envId", envId, "err", err) + return nil, err + } + + secretRespJson, err := utils.ConvertToJsonRawMessage(secretData) + if err != nil { + impl.logger.Errorw("getCmCsPublishedConfigResponse, error in converting secret data to json raw message", "appId", appId, "envId", envId, "err", err) + return nil, err + } + + cmRespJson, err := utils.ConvertToJsonRawMessage(cmData) + if err != nil { + impl.logger.Errorw("getCmCsPublishedConfigResponse, error in converting config map data to json raw message", "appId", appId, "envId", envId, "err", err) + return nil, err + } + + cmConfigData := bean2.NewDeploymentAndCmCsConfig().WithConfigData(cmRespJson).WithResourceType(bean.CM) + secretConfigData := bean2.NewDeploymentAndCmCsConfig().WithConfigData(secretRespJson).WithResourceType(bean.CS) + + configDataDto.WithConfigMapData(cmConfigData).WithSecretData(secretConfigData) + return configDataDto, nil + +} + +func (impl *DeploymentConfigurationServiceImpl) getPublishedDeploymentConfig(ctx context.Context, appId, envId int) (json.RawMessage, error) { + if envId > 0 { + return impl.getDeploymentTemplateForEnvLevel(ctx, appId, envId) + } + return impl.getBaseDeploymentTemplate(appId) +} + +func (impl *DeploymentConfigurationServiceImpl) getPublishedConfigData(ctx context.Context, configDataQueryParams *bean2.ConfigDataQueryParams, + appId, envId int) (*bean2.DeploymentAndCmCsConfigDto, error) { + + if configDataQueryParams.IsRequestMadeForOneResource() { + return impl.getCmCsEditDataForPublishedOnly(configDataQueryParams, envId, appId) + } + //ConfigMapsData and SecretsData are populated here + configData, err := impl.getCmCsPublishedConfigResponse(envId, appId) + 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) + 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(deploymentConfig) + return configData, nil +} + +func (impl *DeploymentConfigurationServiceImpl) getBaseDeploymentTemplate(appId int) (json.RawMessage, 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 +} + +func (impl *DeploymentConfigurationServiceImpl) getDeploymentTemplateForEnvLevel(ctx context.Context, appId, envId int) (json.RawMessage, error) { + deploymentTemplateRequest := generateManifest.DeploymentTemplateRequest{ + AppId: appId, + EnvId: envId, + RequestDataMode: generateManifest.Values, + Type: repository2.PublishedOnEnvironments, + } + 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 impl.getBaseDeploymentTemplate(appId) +} + +func (impl *DeploymentConfigurationServiceImpl) getSecretConfigResponse(resourceName string, resourceId, envId, appId int) (*bean.ConfigDataRequest, error) { + if len(resourceName) > 0 { + if envId > 0 { + return impl.configMapService.CSEnvironmentFetchForEdit(resourceName, resourceId, appId, envId) + } + return impl.configMapService.ConfigGlobalFetchEditUsingAppId(resourceName, appId, bean.CS) + } + + if envId > 0 { + return impl.configMapService.CSEnvironmentFetch(appId, envId) + } + return impl.configMapService.CSGlobalFetch(appId) +} + +func (impl *DeploymentConfigurationServiceImpl) getConfigMapResponse(resourceName string, resourceId, envId, appId int) (*bean.ConfigDataRequest, error) { + if len(resourceName) > 0 { + if envId > 0 { + return impl.configMapService.CMEnvironmentFetchForEdit(resourceName, resourceId, appId, envId) + } + return impl.configMapService.ConfigGlobalFetchEditUsingAppId(resourceName, appId, bean.CM) + } + + if envId > 0 { + return impl.configMapService.CMEnvironmentFetch(appId, envId) + } + return impl.configMapService.CMGlobalFetch(appId) +} diff --git a/pkg/configDiff/adaptor/adaptor.go b/pkg/configDiff/adaptor/adaptor.go new file mode 100644 index 00000000000..4ab81eb2d11 --- /dev/null +++ b/pkg/configDiff/adaptor/adaptor.go @@ -0,0 +1,29 @@ +package adaptor + +import ( + bean2 "github.com/devtron-labs/devtron/pkg/configDiff/bean" + "github.com/devtron-labs/devtron/pkg/pipeline/bean" +) + +func GetConfigProperty(id int, name string, configType bean.ResourceType, State bean2.ConfigState) *bean2.ConfigProperty { + return &bean2.ConfigProperty{ + Id: id, + Name: name, + Type: configType, + ConfigState: State, + } +} + +func GetCmCsAppAndEnvLevelMap(cMCSNamesAppLevel, cMCSNamesEnvLevel []bean.ConfigNameAndType) (map[string]*bean2.ConfigProperty, map[string]*bean2.ConfigProperty) { + cMCSNamesAppLevelMap, cMCSNamesEnvLevelMap := make(map[string]*bean2.ConfigProperty, len(cMCSNamesAppLevel)), make(map[string]*bean2.ConfigProperty, len(cMCSNamesEnvLevel)) + + for _, cmcs := range cMCSNamesAppLevel { + property := GetConfigProperty(cmcs.Id, cmcs.Name, cmcs.Type, bean2.PublishedConfigState) + cMCSNamesAppLevelMap[property.GetKey()] = property + } + for _, cmcs := range cMCSNamesEnvLevel { + property := GetConfigProperty(cmcs.Id, cmcs.Name, cmcs.Type, bean2.PublishedConfigState) + cMCSNamesEnvLevelMap[property.GetKey()] = property + } + return cMCSNamesAppLevelMap, cMCSNamesEnvLevelMap +} diff --git a/pkg/configDiff/bean/bean.go b/pkg/configDiff/bean/bean.go new file mode 100644 index 00000000000..2113ea81a65 --- /dev/null +++ b/pkg/configDiff/bean/bean.go @@ -0,0 +1,152 @@ +package bean + +import "C" +import ( + "encoding/json" + "fmt" + "github.com/devtron-labs/devtron/pkg/pipeline/bean" +) + +type ConfigState string + +const ( + PublishedConfigState ConfigState = "PublishedOnly" +) + +func (r ConfigState) ToString() string { + return string(r) +} + +type ConfigStage string + +const ( + Env ConfigStage = "Env" + Inheriting ConfigStage = "Inheriting" + Overridden ConfigStage = "Overridden" +) + +type ConfigProperty struct { + Id int `json:"id"` + Name string `json:"name"` + ConfigState ConfigState `json:"configState"` + Type bean.ResourceType `json:"type"` + ConfigStage ConfigStage `json:"configStage"` +} + +func NewConfigProperty() *ConfigProperty { + return &ConfigProperty{} +} + +func (r *ConfigProperty) IsConfigPropertyGlobal() bool { + return r.ConfigStage == Inheriting +} + +type ConfigDataResponse struct { + ResourceConfig []*ConfigProperty `json:"resourceConfig"` +} + +func NewConfigDataResponse() *ConfigDataResponse { + return &ConfigDataResponse{} +} + +func (r *ConfigDataResponse) WithResourceConfig(resourceConfig []*ConfigProperty) *ConfigDataResponse { + r.ResourceConfig = resourceConfig + return r +} + +func (r *ConfigProperty) GetKey() string { + return fmt.Sprintf("%s-%s", string(r.Type), r.Name) +} + +type ConfigPropertyIdentifier struct { + Name string `json:"name"` + Type bean.ResourceType `json:"type"` +} + +func (r *ConfigProperty) GetIdentifier() ConfigPropertyIdentifier { + return ConfigPropertyIdentifier{ + Name: r.Name, + Type: r.Type, + } +} + +type DeploymentAndCmCsConfig struct { + ResourceType bean.ResourceType `json:"resourceType"` + Data json.RawMessage `json:"data"` +} + +func NewDeploymentAndCmCsConfig() *DeploymentAndCmCsConfig { + return &DeploymentAndCmCsConfig{} +} + +func (r *DeploymentAndCmCsConfig) WithResourceType(resourceType bean.ResourceType) *DeploymentAndCmCsConfig { + r.ResourceType = resourceType + return r +} + +func (r *DeploymentAndCmCsConfig) WithConfigData(data json.RawMessage) *DeploymentAndCmCsConfig { + r.Data = data + return r +} + +type DeploymentAndCmCsConfigDto struct { + DeploymentTemplate *DeploymentAndCmCsConfig `json:"deploymentTemplate"` + ConfigMapsData *DeploymentAndCmCsConfig `json:"configMapData"` + SecretsData *DeploymentAndCmCsConfig `json:"secretsData"` + IsAppAdmin bool `json:"isAppAdmin"` +} + +func NewDeploymentAndCmCsConfigDto() *DeploymentAndCmCsConfigDto { + return &DeploymentAndCmCsConfigDto{} +} + +func (r *DeploymentAndCmCsConfigDto) WithDeploymentTemplateData(data *DeploymentAndCmCsConfig) *DeploymentAndCmCsConfigDto { + r.DeploymentTemplate = data + return r +} +func (r *DeploymentAndCmCsConfigDto) WithConfigMapData(data *DeploymentAndCmCsConfig) *DeploymentAndCmCsConfigDto { + r.ConfigMapsData = data + return r +} +func (r *DeploymentAndCmCsConfigDto) WithSecretData(data *DeploymentAndCmCsConfig) *DeploymentAndCmCsConfigDto { + r.SecretsData = 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"` + UserId int32 `schema:"-"` +} + +// FilterCriteria []string `schema:"filterCriteria"` +// OffSet int `schema:"offSet"` +// Limit int `schema:"limit"` +func (r *ConfigDataQueryParams) IsResourceTypeSecret() bool { + return r.ResourceType == bean.CS.ToString() +} + +func (r *ConfigDataQueryParams) IsResourceTypeConfigMap() bool { + return r.ResourceType == bean.CM.ToString() +} + +func (r *ConfigDataQueryParams) IsEnvNameProvided() bool { + return len(r.EnvName) > 0 +} +func (r *ConfigDataQueryParams) IsValidConfigType() bool { + return r.ConfigType == PublishedConfigState.ToString() +} + +func (r *ConfigDataQueryParams) IsRequestMadeForOneResource() bool { + return len(r.ResourceName) > 0 && len(r.ResourceType) > 0 +} + +const ( + InvalidConfigTypeErr = "invalid config type provided, please send a valid config type" +) diff --git a/pkg/configDiff/helper/helper.go b/pkg/configDiff/helper/helper.go new file mode 100644 index 00000000000..70082a7bea6 --- /dev/null +++ b/pkg/configDiff/helper/helper.go @@ -0,0 +1,20 @@ +package helper + +import ( + bean2 "github.com/devtron-labs/devtron/pkg/configDiff/bean" +) + +func GetCombinedPropertiesMap(cmcsKeyPropertyAppLevelMap, cmcsKeyPropertyEnvLevelMap map[string]*bean2.ConfigProperty) []*bean2.ConfigProperty { + combinedPropertiesMap := make(map[string]*bean2.ConfigProperty, len(cmcsKeyPropertyAppLevelMap)+len(cmcsKeyPropertyEnvLevelMap)) + for key, property := range cmcsKeyPropertyAppLevelMap { + combinedPropertiesMap[key] = property + } + for key, property := range cmcsKeyPropertyEnvLevelMap { + combinedPropertiesMap[key] = property + } + combinedProperties := make([]*bean2.ConfigProperty, 0, len(cmcsKeyPropertyAppLevelMap)+len(cmcsKeyPropertyEnvLevelMap)) + for _, property := range combinedPropertiesMap { + combinedProperties = append(combinedProperties, property) + } + return combinedProperties +} diff --git a/pkg/configDiff/utils/utils.go b/pkg/configDiff/utils/utils.go new file mode 100644 index 00000000000..8185993775f --- /dev/null +++ b/pkg/configDiff/utils/utils.go @@ -0,0 +1,16 @@ +package utils + +import "encoding/json" + +func ConvertToJsonRawMessage(request interface{}) (json.RawMessage, error) { + var r json.RawMessage + configMapByte, err := json.Marshal(request) + if err != nil { + return nil, err + } + err = r.UnmarshalJSON(configMapByte) + if err != nil { + return nil, err + } + return r, nil +} diff --git a/pkg/deployment/gitOps/git/GitOpsHelper.go b/pkg/deployment/gitOps/git/GitOpsHelper.go index 2d204ef8a0d..bc7add2ff92 100644 --- a/pkg/deployment/gitOps/git/GitOpsHelper.go +++ b/pkg/deployment/gitOps/git/GitOpsHelper.go @@ -89,6 +89,7 @@ func (impl *GitOpsHelper) Clone(url, targetDir string) (clonedDir string, err er } } if errMsg != "" { + impl.logger.Errorw("error in git fetch command", "errMsg", errMsg, "err", err) return "", fmt.Errorf(errMsg) } return clonedDir, nil diff --git a/pkg/deployment/gitOps/git/GitServiceGithub.go b/pkg/deployment/gitOps/git/GitServiceGithub.go index 589d79c2ac7..b48d8b5ab43 100644 --- a/pkg/deployment/gitOps/git/GitServiceGithub.go +++ b/pkg/deployment/gitOps/git/GitServiceGithub.go @@ -259,6 +259,7 @@ func (impl GitHubClient) GetRepoUrl(config *bean2.GitOpsConfigDto) (repoUrl stri ctx := context.Background() repo, _, err := impl.client.Repositories.Get(ctx, impl.org, config.GitRepoName) if err != nil { + impl.logger.Errorw("error in getting repo url by repo name", "org", impl.org, "gitRepoName", config.GitRepoName, "err", err) return "", err } return *repo.CloneURL, nil diff --git a/pkg/deployment/gitOps/git/commandManager/GitCliManager.go b/pkg/deployment/gitOps/git/commandManager/GitCliManager.go index 0501f67cb34..b5b3a3a146d 100644 --- a/pkg/deployment/gitOps/git/commandManager/GitCliManager.go +++ b/pkg/deployment/gitOps/git/commandManager/GitCliManager.go @@ -77,6 +77,9 @@ func (impl *GitCliManagerImpl) Pull(ctx GitContext, repoRoot string) (err error) return err } response, errMsg, err := impl.PullCli(ctx, repoRoot, "origin/master") + if err != nil { + impl.logger.Errorw("error in git pull from cli", "errMsg", errMsg, "err", err) + } if strings.Contains(response, "already up-to-date") || strings.Contains(errMsg, "already up-to-date") { err = nil diff --git a/pkg/deployment/gitOps/git/commandManager/GoGitSdkManager.go b/pkg/deployment/gitOps/git/commandManager/GoGitSdkManager.go index 950a0bf4516..3d70a73c311 100644 --- a/pkg/deployment/gitOps/git/commandManager/GoGitSdkManager.go +++ b/pkg/deployment/gitOps/git/commandManager/GoGitSdkManager.go @@ -56,6 +56,9 @@ func (impl GoGitSDKManagerImpl) Pull(ctx GitContext, repoRoot string) (err error } err = workTree.PullContext(ctx, pullOptions) + if err != nil { + impl.logger.Errorw("error in git pull from go-git", "err", err) + } if err != nil && err.Error() == "already up-to-date" { err = nil return nil diff --git a/pkg/deployment/manifest/ManifestCreationService.go b/pkg/deployment/manifest/ManifestCreationService.go index 86641dfeac9..9b2cc199274 100644 --- a/pkg/deployment/manifest/ManifestCreationService.go +++ b/pkg/deployment/manifest/ManifestCreationService.go @@ -275,10 +275,12 @@ func (impl *ManifestCreationServiceImpl) GetValuesOverrideForTrigger(overrideReq // error is not returned as it's not blocking for deployment process // blocking deployments based on this use case can vary for user to user } - mergedValues, err = impl.autoscalingCheckBeforeTrigger(newCtx, appName, envOverride.Namespace, mergedValues, overrideRequest) - if err != nil { - impl.logger.Errorw("error in autoscaling check before trigger", "pipelineId", overrideRequest.PipelineId, "err", err) - return valuesOverrideResponse, err + if !envOverride.Environment.IsVirtualEnvironment { + mergedValues, err = impl.autoscalingCheckBeforeTrigger(newCtx, appName, envOverride.Namespace, mergedValues, overrideRequest) + if err != nil { + impl.logger.Errorw("error in autoscaling check before trigger", "pipelineId", overrideRequest.PipelineId, "err", err) + return valuesOverrideResponse, err + } } // handle image pull secret if access given mergedValues, err = impl.dockerRegistryIpsConfigService.HandleImagePullSecretOnApplicationDeployment(newCtx, envOverride.Environment, artifact, pipeline.CiPipelineId, mergedValues) @@ -806,7 +808,7 @@ func (impl *ManifestCreationServiceImpl) checkAndFixDuplicateReleaseNo(override return err } override.PipelineReleaseCounter = currentReleaseNo + 1 - err = impl.pipelineOverrideRepository.Save(override) + err = impl.pipelineOverrideRepository.Update(override) if err != nil { return err } diff --git a/pkg/pipeline/ConfigMapService.go b/pkg/pipeline/ConfigMapService.go index 77e996dcce5..72cd8026da2 100644 --- a/pkg/pipeline/ConfigMapService.go +++ b/pkg/pipeline/ConfigMapService.go @@ -34,7 +34,9 @@ import ( util2 "github.com/devtron-labs/devtron/util" "github.com/go-pg/pg" "go.uber.org/zap" + "net/http" "regexp" + "strconv" "time" ) @@ -56,6 +58,7 @@ type ConfigMapService interface { CMEnvironmentFetch(appId int, envId int) (*bean.ConfigDataRequest, error) CMGlobalFetchForEdit(name string, id int) (*bean.ConfigDataRequest, error) CMEnvironmentFetchForEdit(name string, id int, appId int, envId int) (*bean.ConfigDataRequest, error) + ConfigGlobalFetchEditUsingAppId(name string, appId int, resourceType bean.ResourceType) (*bean.ConfigDataRequest, error) CSGlobalAddUpdate(configMapRequest *bean.ConfigDataRequest) (*bean.ConfigDataRequest, error) CSGlobalFetch(appId int) (*bean.ConfigDataRequest, error) @@ -81,6 +84,8 @@ type ConfigMapService interface { ConfigSecretEnvironmentDelete(createJobEnvOverrideRequest *bean.CreateJobEnvOverridePayload) (*bean.CreateJobEnvOverridePayload, error) ConfigSecretEnvironmentGet(appId int) ([]bean.JobEnvOverrideResponse, error) ConfigSecretEnvironmentClone(appId int, cloneAppId int, userId int32) ([]chartConfig.ConfigMapEnvModel, error) + + FetchCmCsNamesAppAndEnvLevel(appId int, envId int) ([]bean.ConfigNameAndType, []bean.ConfigNameAndType, error) } type ConfigMapServiceImpl struct { @@ -485,8 +490,6 @@ func (impl ConfigMapServiceImpl) CMEnvironmentFetch(appId int, envId int) (*bean if configDataRequest.ConfigData == nil { list := []*bean.ConfigData{} configDataRequest.ConfigData = list - } else { - //configDataRequest.ConfigData = configMapGlobalList.ConfigData } return configDataRequest, nil @@ -504,12 +507,19 @@ func (impl ConfigMapServiceImpl) CSGlobalAddUpdate(configMapRequest *bean.Config return nil, fmt.Errorf("invalid request multiple config found for add or update") } configData := configMapRequest.ConfigData[0] + // validating config/secret data at service layer since this func is consumed in multiple flows, hence preventing code duplication valid, err := impl.validateConfigData(configData) if err != nil && !valid { impl.logger.Errorw("error in validating", "error", err) return configMapRequest, err } + valid, err = impl.validateConfigDataForSecretsOnly(configData) + if err != nil && !valid { + impl.logger.Errorw("error in validating secrets only data", "error", err) + return configMapRequest, err + } + valid, err = impl.validateExternalSecretChartCompatibility(configMapRequest.AppId, configMapRequest.EnvironmentId, configData) if err != nil && !valid { impl.logger.Errorw("error in validating", "error", err) @@ -631,68 +641,15 @@ func (impl ConfigMapServiceImpl) CSGlobalFetch(appId int) (*bean.ConfigDataReque configDataRequest := &bean.ConfigDataRequest{} configDataRequest.Id = configMapGlobal.Id configDataRequest.AppId = appId - //configDataRequest.ConfigData = configMapGlobalList.ConfigData for _, item := range configMapGlobalList.ConfigData { item.Global = true configDataRequest.ConfigData = append(configDataRequest.ConfigData, item) } - //removing actual values - var configs []*bean.ConfigData - for _, item := range configDataRequest.ConfigData { - resultMap := make(map[string]string) - resultMapFinal := make(map[string]string) - - if item.Data != nil { - err = json.Unmarshal(item.Data, &resultMap) - if err != nil { - impl.logger.Warnw("unmarshal failed: ", "error", err) - configs = append(configs, item) - continue - } - for k := range resultMap { - resultMapFinal[k] = "" - } - resultByte, err := json.Marshal(resultMapFinal) - if err != nil { - impl.logger.Errorw("error while marshaling request ", "err", err) - return nil, err - } - item.Data = resultByte - } - - var externalSecret []bean.ExternalSecret - if item.ExternalSecret != nil && len(item.ExternalSecret) > 0 { - for _, es := range item.ExternalSecret { - externalSecret = append(externalSecret, bean.ExternalSecret{Key: es.Key, Name: es.Name, Property: es.Property, IsBinary: es.IsBinary}) - } - } - item.ExternalSecret = externalSecret - - var esoData []bean.ESOData - if len(item.ESOSecretData.EsoData) > 0 { - for _, data := range item.ESOSecretData.EsoData { - esoData = append(esoData, bean.ESOData{Key: data.Key, SecretKey: data.SecretKey, Property: data.Property}) - } - } - - esoSecretData := bean.ESOSecretData{ - SecretStore: item.ESOSecretData.SecretStore, - SecretStoreRef: item.ESOSecretData.SecretStoreRef, - EsoData: esoData, - RefreshInterval: item.ESOSecretData.RefreshInterval, - } - item.ESOSecretData = esoSecretData - configs = append(configs, item) - } - configDataRequest.ConfigData = configs - if configDataRequest.ConfigData == nil { list := []*bean.ConfigData{} configDataRequest.ConfigData = list - } else { - //configDataRequest.ConfigData = configMapGlobalList.ConfigData } return configDataRequest, nil @@ -704,11 +661,17 @@ func (impl ConfigMapServiceImpl) CSEnvironmentAddUpdate(configMapRequest *bean.C } configData := configMapRequest.ConfigData[0] + // validating config/secret data at service layer since this func is consumed in multiple flows, hence preventing code duplication valid, err := impl.validateConfigData(configData) if err != nil && !valid { impl.logger.Errorw("error in validating", "error", err) return configMapRequest, err } + valid, err = impl.validateConfigDataForSecretsOnly(configData) + if err != nil && !valid { + impl.logger.Errorw("error in validating secrets only data", "error", err) + return configMapRequest, err + } valid, err = impl.validateExternalSecretChartCompatibility(configMapRequest.AppId, configMapRequest.EnvironmentId, configData) if err != nil && !valid { @@ -795,13 +758,6 @@ func (impl ConfigMapServiceImpl) CSEnvironmentAddUpdate(configMapRequest *bean.C } configMapRequest.Id = configMap.Id } - //VARIABLE_MAPPING_UPDATE - //sl := bean.SecretsList{} - //data, err := sl.GetTransformedDataForSecretList(model.SecretData, util2.DecodeSecret) - //if err != nil { - // return nil, err - //} - //err = impl.extractAndMapVariables(data, model.Id, repository5.EntityTypeSecretEnvLevel, configMapRequest.UserId) err = impl.scopedVariableManager.CreateVariableMappingsForSecretEnv(model) if err != nil { return nil, err @@ -919,93 +875,6 @@ func (impl ConfigMapServiceImpl) CSEnvironmentFetch(appId int, envId int) (*bean } } - //removing actual values - var configs []*bean.ConfigData - for _, item := range configDataRequest.ConfigData { - - if item.Data != nil { - resultMap := make(map[string]string) - resultMapFinal := make(map[string]string) - err = json.Unmarshal(item.Data, &resultMap) - if err != nil { - impl.logger.Warnw("unmarshal failed: ", "error", err) - //item.Data = []byte("[]") - configs = append(configs, item) - continue - //return nil, err - } - for k := range resultMap { - resultMapFinal[k] = "" - } - var resultByte []byte - if resultMapFinal != nil && len(resultMapFinal) > 0 { - resultByte, err = json.Marshal(resultMapFinal) - if err != nil { - impl.logger.Errorw("error while marshaling request ", "err", err) - return nil, err - } - } - item.Data = resultByte - } - - var externalSecret []bean.ExternalSecret - if item.ExternalSecret != nil && len(item.ExternalSecret) > 0 { - for _, es := range item.ExternalSecret { - externalSecret = append(externalSecret, bean.ExternalSecret{Key: es.Key, Name: es.Name, Property: es.Property, IsBinary: es.IsBinary}) - } - } - item.ExternalSecret = externalSecret - - var esoData []bean.ESOData - if len(item.ESOSecretData.EsoData) > 0 { - for _, data := range item.ESOSecretData.EsoData { - esoData = append(esoData, bean.ESOData{Key: data.Key, SecretKey: data.SecretKey, Property: data.Property}) - } - } - - esoSecretData := bean.ESOSecretData{ - SecretStore: item.ESOSecretData.SecretStore, - SecretStoreRef: item.ESOSecretData.SecretStoreRef, - EsoData: esoData, - RefreshInterval: item.ESOSecretData.RefreshInterval, - } - item.ESOSecretData = esoSecretData - - if item.DefaultData != nil { - resultMap := make(map[string]string) - resultMapFinal := make(map[string]string) - err = json.Unmarshal(item.DefaultData, &resultMap) - if err != nil { - impl.logger.Warnw("unmarshal failed: ", "error", err) - //item.Data = []byte("[]") - configs = append(configs, item) - continue - //return nil, err - } - for k := range resultMap { - resultMapFinal[k] = "" - } - resultByte, err := json.Marshal(resultMapFinal) - if err != nil { - impl.logger.Errorw("error while marshaling request ", "err", err) - return nil, err - } - item.DefaultData = resultByte - } - - if item.DefaultExternalSecret != nil { - var externalSecret []bean.ExternalSecret - if item.DefaultExternalSecret != nil && len(item.DefaultExternalSecret) > 0 { - for _, es := range item.DefaultExternalSecret { - externalSecret = append(externalSecret, bean.ExternalSecret{Key: es.Key, Name: es.Name, Property: es.Property, IsBinary: es.IsBinary}) - } - } - item.DefaultExternalSecret = externalSecret - } - configs = append(configs, item) - } - configDataRequest.ConfigData = configs - if configDataRequest.ConfigData == nil { list := []*bean.ConfigData{} configDataRequest.ConfigData = list @@ -1545,6 +1414,26 @@ func (impl ConfigMapServiceImpl) validateConfigData(configData *bean.ConfigData) return true, nil } +func (impl ConfigMapServiceImpl) validateConfigDataForSecretsOnly(configData *bean.ConfigData) (bool, error) { + + // check encoding in base64 for secret data + if len(configData.Data) > 0 { + dataMap := make(map[string]string) + err := json.Unmarshal(configData.Data, &dataMap) + if err != nil { + impl.logger.Errorw("error while unmarshalling secret data ", "error", err) + return false, err + } + err = util2.ValidateEncodedDataByDecoding(dataMap) + if err != nil { + impl.logger.Errorw("error in decoding secret data", "error", err) + return false, util.NewApiError().WithHttpStatusCode(http.StatusUnprocessableEntity).WithCode(strconv.Itoa(http.StatusUnprocessableEntity)). + WithUserMessage("error in decoding data, make sure the secret data is encoded properly") + } + } + return true, nil +} + func (impl ConfigMapServiceImpl) updateConfigData(configData *bean.ConfigData, syncRequest *bean.BulkPatchRequest) (*bean.ConfigData, error) { dataMap := make(map[string]string) var updatedData json.RawMessage @@ -1991,3 +1880,42 @@ func (impl ConfigMapServiceImpl) ConfigSecretEnvironmentClone(appId int, cloneAp return jobEnvOverrideResponse, nil } +func (impl ConfigMapServiceImpl) FetchCmCsNamesAppAndEnvLevel(appId int, envId int) ([]bean.ConfigNameAndType, []bean.ConfigNameAndType, error) { + var cMCSNamesEnvLevel []bean.ConfigNameAndType + + cMCSNamesAppLevel, err := impl.configMapRepository.GetConfigNamesForAppAndEnvLevel(appId, -1) + if err != nil { + impl.logger.Errorw("error in fetching CM/CS names at app level ", "appId", appId, "err", err) + return nil, nil, err + } + if envId > 0 { + cMCSNamesEnvLevel, err = impl.configMapRepository.GetConfigNamesForAppAndEnvLevel(appId, envId) + if err != nil { + impl.logger.Errorw("error in fetching CM/CS names at env level ", "appId", appId, "envId", envId, "err", err) + return nil, nil, err + } + } + return cMCSNamesAppLevel, cMCSNamesEnvLevel, nil +} + +func (impl ConfigMapServiceImpl) ConfigGlobalFetchEditUsingAppId(name string, appId int, resourceType bean.ResourceType) (*bean.ConfigDataRequest, error) { + var fetchGlobalConfigFunc func(int) (*bean.ConfigDataRequest, error) + if resourceType == bean.CS { + fetchGlobalConfigFunc = impl.CSGlobalFetch + } else if resourceType == bean.CM { + fetchGlobalConfigFunc = impl.CMGlobalFetch + } + configDataRequest, err := fetchGlobalConfigFunc(appId) + if err != nil { + impl.logger.Errorw("error in fetching global cm using app id ", "cmName", name, "appId", appId, "err", err) + return nil, err + } + configs := make([]*bean.ConfigData, 0, len(configDataRequest.ConfigData)) + for _, configData := range configDataRequest.ConfigData { + if configData.Name == name { + configs = append(configs, configData) + } + } + configDataRequest.ConfigData = configs + return configDataRequest, nil +} diff --git a/pkg/pipeline/bean/ConfigMapBean.go b/pkg/pipeline/bean/ConfigMapBean.go index cae2f33f4a8..2f572bd6058 100644 --- a/pkg/pipeline/bean/ConfigMapBean.go +++ b/pkg/pipeline/bean/ConfigMapBean.go @@ -107,3 +107,21 @@ type CreateJobEnvOverridePayload struct { type SecretsList struct { ConfigData []*ConfigData `json:"secrets"` } + +type ConfigNameAndType struct { + Id int + Name string + Type ResourceType +} + +type ResourceType string + +const ( + CM ResourceType = "ConfigMap" + CS ResourceType = "Secret" + DeploymentTemplate ResourceType = "Deployment Template" +) + +func (r ResourceType) ToString() string { + return string(r) +} diff --git a/pkg/plugin/bean/bean.go b/pkg/plugin/bean/bean.go index 9e152272e37..55424f3caac 100644 --- a/pkg/plugin/bean/bean.go +++ b/pkg/plugin/bean/bean.go @@ -171,6 +171,7 @@ func (r *PluginsVersionDetail) SetMinimalPluginsVersionDetail(pluginVersionMetad r.Description = pluginVersionMetadata.Description r.Version = pluginVersionMetadata.PluginVersion r.IsLatest = pluginVersionMetadata.IsLatest + r.DocLink = pluginVersionMetadata.DocLink return r } diff --git a/scripts/sql/274_improved_image_scan_plugin.down.sql b/scripts/sql/270_improved_image_scan_plugin.down.sql similarity index 100% rename from scripts/sql/274_improved_image_scan_plugin.down.sql rename to scripts/sql/270_improved_image_scan_plugin.down.sql diff --git a/scripts/sql/274_improved_image_scan_plugin.up.sql b/scripts/sql/270_improved_image_scan_plugin.up.sql similarity index 100% rename from scripts/sql/274_improved_image_scan_plugin.up.sql rename to scripts/sql/270_improved_image_scan_plugin.up.sql diff --git a/scripts/sql/270_plugin_parent_metadata.down.sql b/scripts/sql/271_plugin_parent_metadata.down.sql similarity index 100% rename from scripts/sql/270_plugin_parent_metadata.down.sql rename to scripts/sql/271_plugin_parent_metadata.down.sql diff --git a/scripts/sql/270_plugin_parent_metadata.up.sql b/scripts/sql/271_plugin_parent_metadata.up.sql similarity index 100% rename from scripts/sql/270_plugin_parent_metadata.up.sql rename to scripts/sql/271_plugin_parent_metadata.up.sql diff --git a/scripts/sql/271_alter_plugin_metadata.down.sql b/scripts/sql/272_alter_plugin_metadata.down.sql similarity index 100% rename from scripts/sql/271_alter_plugin_metadata.down.sql rename to scripts/sql/272_alter_plugin_metadata.down.sql diff --git a/scripts/sql/271_alter_plugin_metadata.up.sql b/scripts/sql/272_alter_plugin_metadata.up.sql similarity index 100% rename from scripts/sql/271_alter_plugin_metadata.up.sql rename to scripts/sql/272_alter_plugin_metadata.up.sql diff --git a/scripts/sql/272_tls_support_in_git.down.sql b/scripts/sql/273_tls_support_in_git.down.sql similarity index 100% rename from scripts/sql/272_tls_support_in_git.down.sql rename to scripts/sql/273_tls_support_in_git.down.sql diff --git a/scripts/sql/272_tls_support_in_git.up.sql b/scripts/sql/273_tls_support_in_git.up.sql similarity index 100% rename from scripts/sql/272_tls_support_in_git.up.sql rename to scripts/sql/273_tls_support_in_git.up.sql diff --git a/scripts/sql/273_system_controller.down.sql b/scripts/sql/274_system_controller.down.sql similarity index 100% rename from scripts/sql/273_system_controller.down.sql rename to scripts/sql/274_system_controller.down.sql diff --git a/scripts/sql/273_system_controller.up.sql b/scripts/sql/274_system_controller.up.sql similarity index 100% rename from scripts/sql/273_system_controller.up.sql rename to scripts/sql/274_system_controller.up.sql diff --git a/scripts/sql/276_alter_pipeline_stage_step_variable.down.sql b/scripts/sql/276_alter_pipeline_stage_step_variable.down.sql new file mode 100644 index 00000000000..bad0b4c4928 --- /dev/null +++ b/scripts/sql/276_alter_pipeline_stage_step_variable.down.sql @@ -0,0 +1,3 @@ +ALTER TABLE pipeline_stage_step_variable ALTER COLUMN default_value TYPE VARCHAR(255); +ALTER TABLE pipeline_stage_step_variable ALTER COLUMN value TYPE VARCHAR(255); + diff --git a/scripts/sql/276_alter_pipeline_stage_step_variable.up.sql b/scripts/sql/276_alter_pipeline_stage_step_variable.up.sql new file mode 100644 index 00000000000..cbcf6515c90 --- /dev/null +++ b/scripts/sql/276_alter_pipeline_stage_step_variable.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE pipeline_stage_step_variable ALTER COLUMN value TYPE text; +ALTER TABLE pipeline_stage_step_variable ALTER COLUMN default_value TYPE text; diff --git a/scripts/sql/276_scan_policies.down.sql b/scripts/sql/277_scan_policies.down.sql similarity index 100% rename from scripts/sql/276_scan_policies.down.sql rename to scripts/sql/277_scan_policies.down.sql diff --git a/scripts/sql/276_scan_policies.up.sql b/scripts/sql/277_scan_policies.up.sql similarity index 100% rename from scripts/sql/276_scan_policies.up.sql rename to scripts/sql/277_scan_policies.up.sql diff --git a/scripts/sql/277_rbac_role_audit.down.sql b/scripts/sql/278_rbac_role_audit.down.sql similarity index 100% rename from scripts/sql/277_rbac_role_audit.down.sql rename to scripts/sql/278_rbac_role_audit.down.sql diff --git a/scripts/sql/277_rbac_role_audit.up.sql b/scripts/sql/278_rbac_role_audit.up.sql similarity index 100% rename from scripts/sql/277_rbac_role_audit.up.sql rename to scripts/sql/278_rbac_role_audit.up.sql diff --git a/specs/configDiffView.yaml b/specs/configDiffView.yaml new file mode 100644 index 00000000000..8a24d50989c --- /dev/null +++ b/specs/configDiffView.yaml @@ -0,0 +1,73 @@ +openapi: 3.0.0 +info: + title: Orchestrator Config Autocomplete API + version: 1.0.0 +paths: + /orchestrator/config/autocomplete: + get: + summary: Retrieve autocomplete data for configuration based on the provided appId and envId. The response includes configuration definitions with names, draft states, and types. + parameters: + - name: appId + in: query + description: The application ID. + required: true + schema: + type: string + - name: envId + in: query + description: The environment ID. + required: true + schema: + type: string + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ConfigProperty" + + '500': + description: will get this response if any failure occurs at server side. + '400': + description: will get this response if invalid payload is sent in the request. + '403': + description: will get this response if user doesn't view access permission for the app or env + '404': + description: will get this when BaseDeployment Template is not configured + +components: + schemas: + ConfigDataResponse: + type: object + properties: + resourceConfig: + type: array + items: + $ref: '#/components/schemas/ConfigProperty' + + ConfigProperty: + type: object + properties: + name: + type: string + description: Name of the config + example: cm-1 + nullable: true + configState: + $ref: '#/components/schemas/ConfigStateEnum' + type: + $ref: '#/components/schemas/ResourceTypeEnum' + + ConfigStateEnum: + type: integer + enum: [ 1, 2, 3 ] + description: State of config (1 represents draft state , 2 represents approval pending state,3 represents published state) + + ResourceTypeEnum: + type: string + enum: [ "ConfigMap", "Secret", "Deployment Template" ] + description: Describe the config type (possible values are ConfigMap, Secret, Deployment Template) + diff --git a/util/encoding-utils.go b/util/encoding-utils.go index 88064a26bd5..82837c229cb 100644 --- a/util/encoding-utils.go +++ b/util/encoding-utils.go @@ -53,3 +53,13 @@ func GetDecodedAndEncodedData(data json.RawMessage, transformer SecretTransformM } return marshal, nil } + +func ValidateEncodedDataByDecoding(dataMap map[string]string) error { + for _, value := range dataMap { + _, err := base64.StdEncoding.DecodeString(value) + if err != nil { + return err + } + } + return nil +} diff --git a/util/rbac/EnforcerUtil.go b/util/rbac/EnforcerUtil.go index bb646e70355..77f3b952130 100644 --- a/util/rbac/EnforcerUtil.go +++ b/util/rbac/EnforcerUtil.go @@ -76,6 +76,8 @@ type EnforcerUtil interface { CheckAppRbacForAppOrJob(token, resourceName, action string) bool CheckAppRbacForAppOrJobInBulk(token, action string, rbacObjects []string, appType helper.AppType) map[string]bool GetRbacObjectsByEnvIdsAndAppIdBatch(appIdToEnvIds map[int][]int) map[int]map[int]string + GetEnvRBACNameByAppAndEnvName(appName, envName string) string + GetAppRBACNameByAppName(appName string) string } type EnforcerUtilImpl struct { @@ -764,3 +766,19 @@ func (impl EnforcerUtilImpl) GetRbacObjectsByEnvIdsAndAppIdBatch(appIdToEnvIds m } return objects } + +func (impl EnforcerUtilImpl) GetAppRBACNameByAppName(appName string) string { + application, err := impl.appRepo.FindAppAndProjectByAppName(appName) + if err != nil { + return fmt.Sprintf("%s/%s", "", "") + } + return fmt.Sprintf("%s/%s", application.Team.Name, application.AppName) +} + +func (impl EnforcerUtilImpl) GetEnvRBACNameByAppAndEnvName(appName, envName string) string { + env, err := impl.environmentRepository.FindByName(envName) + if err != nil { + return fmt.Sprintf("%s/%s", "", appName) + } + return fmt.Sprintf("%s/%s", env.EnvironmentIdentifier, appName) +} diff --git a/wire_gen.go b/wire_gen.go index b1ef4f1028b..0bc013a9cf0 100644 --- a/wire_gen.go +++ b/wire_gen.go @@ -137,6 +137,7 @@ import ( "github.com/devtron-labs/devtron/pkg/cluster/repository" "github.com/devtron-labs/devtron/pkg/clusterTerminalAccess" "github.com/devtron-labs/devtron/pkg/commonService" + "github.com/devtron-labs/devtron/pkg/configDiff" delete2 "github.com/devtron-labs/devtron/pkg/delete" "github.com/devtron-labs/devtron/pkg/deployment/common" "github.com/devtron-labs/devtron/pkg/deployment/deployedApp" @@ -714,7 +715,7 @@ func InitializeApp() (*App, error) { if err != nil { return nil, err } - installedAppResourceServiceImpl := resource.NewInstalledAppResourceServiceImpl(sugaredLogger, installedAppRepositoryImpl, appStoreApplicationVersionRepositoryImpl, applicationServiceClientImpl, acdAuthConfig, installedAppVersionHistoryRepositoryImpl, argoUserServiceImpl, helmAppClientImpl, helmAppServiceImpl, appStatusServiceImpl, k8sCommonServiceImpl, k8sApplicationServiceImpl, k8sServiceImpl, deploymentConfigServiceImpl) + installedAppResourceServiceImpl := resource.NewInstalledAppResourceServiceImpl(sugaredLogger, installedAppRepositoryImpl, appStoreApplicationVersionRepositoryImpl, applicationServiceClientImpl, acdAuthConfig, installedAppVersionHistoryRepositoryImpl, argoUserServiceImpl, helmAppClientImpl, helmAppServiceImpl, appStatusServiceImpl, k8sCommonServiceImpl, k8sApplicationServiceImpl, k8sServiceImpl, deploymentConfigServiceImpl, ociRegistryConfigRepositoryImpl) chartGroupEntriesRepositoryImpl := repository17.NewChartGroupEntriesRepositoryImpl(db, sugaredLogger) chartGroupReposotoryImpl := repository17.NewChartGroupReposotoryImpl(db, sugaredLogger) chartGroupDeploymentRepositoryImpl := repository17.NewChartGroupDeploymentRepositoryImpl(db, sugaredLogger) @@ -942,6 +943,12 @@ func InitializeApp() (*App, error) { if err != nil { return nil, err } + deploymentConfigurationServiceImpl, err := configDiff.NewDeploymentConfigurationServiceImpl(sugaredLogger, configMapServiceImpl, appRepositoryImpl, environmentRepositoryImpl, chartServiceImpl, generateManifestDeploymentTemplateServiceImpl) + if err != nil { + return nil, err + } + deploymentConfigurationRestHandlerImpl := restHandler.NewDeploymentConfigurationRestHandlerImpl(sugaredLogger, userServiceImpl, enforcerUtilImpl, deploymentConfigurationServiceImpl, enforcerImpl) + deploymentConfigurationRouterImpl := router.NewDeploymentConfigurationRouter(deploymentConfigurationRestHandlerImpl) infraConfigRestHandlerImpl := infraConfig2.NewInfraConfigRestHandlerImpl(sugaredLogger, infraConfigServiceImpl, userServiceImpl, enforcerImpl, enforcerUtilImpl, validate) infraConfigRouterImpl := infraConfig2.NewInfraProfileRouterImpl(infraConfigRestHandlerImpl) argoApplicationRestHandlerImpl := argoApplication2.NewArgoApplicationRestHandlerImpl(argoApplicationServiceImpl, sugaredLogger, enforcerImpl) @@ -953,7 +960,7 @@ func InitializeApp() (*App, error) { devtronResourceRouterImpl := devtronResource2.NewDevtronResourceRouterImpl(historyRouterImpl) fluxApplicationRestHandlerImpl := fluxApplication2.NewFluxApplicationRestHandlerImpl(fluxApplicationServiceImpl, sugaredLogger, enforcerImpl) fluxApplicationRouterImpl := fluxApplication2.NewFluxApplicationRouterImpl(fluxApplicationRestHandlerImpl) - muxRouter := router.NewMuxRouter(sugaredLogger, environmentRouterImpl, clusterRouterImpl, webhookRouterImpl, userAuthRouterImpl, gitProviderRouterImpl, gitHostRouterImpl, dockerRegRouterImpl, notificationRouterImpl, teamRouterImpl, userRouterImpl, chartRefRouterImpl, configMapRouterImpl, appStoreRouterImpl, chartRepositoryRouterImpl, releaseMetricsRouterImpl, deploymentGroupRouterImpl, batchOperationRouterImpl, chartGroupRouterImpl, imageScanRouterImpl, policyRouterImpl, gitOpsConfigRouterImpl, dashboardRouterImpl, attributesRouterImpl, userAttributesRouterImpl, commonRouterImpl, grafanaRouterImpl, ssoLoginRouterImpl, telemetryRouterImpl, telemetryEventClientImplExtended, bulkUpdateRouterImpl, webhookListenerRouterImpl, appRouterImpl, coreAppRouterImpl, helmAppRouterImpl, k8sApplicationRouterImpl, pProfRouterImpl, deploymentConfigRouterImpl, dashboardTelemetryRouterImpl, commonDeploymentRouterImpl, externalLinkRouterImpl, globalPluginRouterImpl, moduleRouterImpl, serverRouterImpl, apiTokenRouterImpl, cdApplicationStatusUpdateHandlerImpl, k8sCapacityRouterImpl, webhookHelmRouterImpl, globalCMCSRouterImpl, userTerminalAccessRouterImpl, jobRouterImpl, ciStatusUpdateCronImpl, resourceGroupingRouterImpl, rbacRoleRouterImpl, scopedVariableRouterImpl, ciTriggerCronImpl, proxyRouterImpl, infraConfigRouterImpl, argoApplicationRouterImpl, devtronResourceRouterImpl, fluxApplicationRouterImpl) + muxRouter := router.NewMuxRouter(sugaredLogger, environmentRouterImpl, clusterRouterImpl, webhookRouterImpl, userAuthRouterImpl, gitProviderRouterImpl, gitHostRouterImpl, dockerRegRouterImpl, notificationRouterImpl, teamRouterImpl, userRouterImpl, chartRefRouterImpl, configMapRouterImpl, appStoreRouterImpl, chartRepositoryRouterImpl, releaseMetricsRouterImpl, deploymentGroupRouterImpl, batchOperationRouterImpl, chartGroupRouterImpl, imageScanRouterImpl, policyRouterImpl, gitOpsConfigRouterImpl, dashboardRouterImpl, attributesRouterImpl, userAttributesRouterImpl, commonRouterImpl, grafanaRouterImpl, ssoLoginRouterImpl, telemetryRouterImpl, telemetryEventClientImplExtended, bulkUpdateRouterImpl, webhookListenerRouterImpl, appRouterImpl, coreAppRouterImpl, helmAppRouterImpl, k8sApplicationRouterImpl, pProfRouterImpl, deploymentConfigRouterImpl, dashboardTelemetryRouterImpl, commonDeploymentRouterImpl, externalLinkRouterImpl, globalPluginRouterImpl, moduleRouterImpl, serverRouterImpl, apiTokenRouterImpl, cdApplicationStatusUpdateHandlerImpl, k8sCapacityRouterImpl, webhookHelmRouterImpl, globalCMCSRouterImpl, userTerminalAccessRouterImpl, jobRouterImpl, ciStatusUpdateCronImpl, resourceGroupingRouterImpl, rbacRoleRouterImpl, scopedVariableRouterImpl, ciTriggerCronImpl, proxyRouterImpl, deploymentConfigurationRouterImpl, infraConfigRouterImpl, argoApplicationRouterImpl, devtronResourceRouterImpl, fluxApplicationRouterImpl) loggingMiddlewareImpl := util4.NewLoggingMiddlewareImpl(userServiceImpl) cdWorkflowServiceImpl := cd.NewCdWorkflowServiceImpl(sugaredLogger, cdWorkflowRepositoryImpl) cdWorkflowRunnerServiceImpl := cd.NewCdWorkflowRunnerServiceImpl(sugaredLogger, cdWorkflowRepositoryImpl)