diff --git a/Wire.go b/Wire.go index 7d2cd72d88e..9a442dd9c92 100644 --- a/Wire.go +++ b/Wire.go @@ -105,6 +105,7 @@ import ( security2 "github.com/devtron-labs/devtron/internal/sql/repository/security" "github.com/devtron-labs/devtron/internal/util" "github.com/devtron-labs/devtron/pkg/app" + "github.com/devtron-labs/devtron/pkg/app/dbMigration" "github.com/devtron-labs/devtron/pkg/app/status" "github.com/devtron-labs/devtron/pkg/appClone" "github.com/devtron-labs/devtron/pkg/appClone/batch" @@ -1005,6 +1006,9 @@ func InitializeApp() (*App, error) { repocreds.NewServiceClientImpl, wire.Bind(new(repocreds.ServiceClient), new(*repocreds.ServiceClientImpl)), + + dbMigration.NewDbMigrationServiceImpl, + wire.Bind(new(dbMigration.DbMigration), new(*dbMigration.DbMigrationServiceImpl)), ) return &App{}, nil } diff --git a/cmd/external-app/wire.go b/cmd/external-app/wire.go index 6f23ffdf64d..80d52db6966 100644 --- a/cmd/external-app/wire.go +++ b/cmd/external-app/wire.go @@ -64,6 +64,7 @@ import ( security2 "github.com/devtron-labs/devtron/internal/sql/repository/security" "github.com/devtron-labs/devtron/internal/util" "github.com/devtron-labs/devtron/pkg/app" + "github.com/devtron-labs/devtron/pkg/app/dbMigration" repository4 "github.com/devtron-labs/devtron/pkg/appStore/chartGroup/repository" "github.com/devtron-labs/devtron/pkg/appStore/installedApp/service/EAMode" "github.com/devtron-labs/devtron/pkg/appStore/installedApp/service/FullMode/deployment" @@ -253,6 +254,9 @@ func InitializeApp() (*App, error) { argoRepositoryCreds.NewRepositorySecret, wire.Bind(new(argoRepositoryCreds.RepositorySecret), new(*argoRepositoryCreds.RepositorySecretImpl)), + + dbMigration.NewDbMigrationServiceImpl, + wire.Bind(new(dbMigration.DbMigration), new(*dbMigration.DbMigrationServiceImpl)), ) return &App{}, nil } diff --git a/cmd/external-app/wire_gen.go b/cmd/external-app/wire_gen.go index bab4ef2b470..27ea1f7f1bb 100644 --- a/cmd/external-app/wire_gen.go +++ b/cmd/external-app/wire_gen.go @@ -57,6 +57,7 @@ import ( "github.com/devtron-labs/devtron/internal/util" "github.com/devtron-labs/devtron/pkg/apiToken" app2 "github.com/devtron-labs/devtron/pkg/app" + "github.com/devtron-labs/devtron/pkg/app/dbMigration" repository7 "github.com/devtron-labs/devtron/pkg/appStore/chartGroup/repository" "github.com/devtron-labs/devtron/pkg/appStore/chartProvider" "github.com/devtron-labs/devtron/pkg/appStore/discover/repository" @@ -234,7 +235,8 @@ func InitializeApp() (*App, error) { helmAppClientImpl := gRPC.NewHelmAppClientImpl(sugaredLogger, helmClientConfig, configuration) pumpImpl := connector.NewPumpImpl(sugaredLogger) appRepositoryImpl := app.NewAppRepositoryImpl(db, sugaredLogger) - enforcerUtilHelmImpl := rbac.NewEnforcerUtilHelmImpl(sugaredLogger, clusterRepositoryImpl, teamRepositoryImpl, appRepositoryImpl, installedAppRepositoryImpl) + dbMigrationServiceImpl := dbMigration.NewDbMigrationServiceImpl(sugaredLogger, appRepositoryImpl, installedAppRepositoryImpl) + enforcerUtilHelmImpl := rbac.NewEnforcerUtilHelmImpl(sugaredLogger, clusterRepositoryImpl, teamRepositoryImpl, appRepositoryImpl, installedAppRepositoryImpl, dbMigrationServiceImpl) serverDataStoreServerDataStore := serverDataStore.InitServerDataStore() appStoreApplicationVersionRepositoryImpl := appStoreDiscoverRepository.NewAppStoreApplicationVersionRepositoryImpl(sugaredLogger, db) pipelineRepositoryImpl := pipelineConfig.NewPipelineRepositoryImpl(db, sugaredLogger) @@ -421,7 +423,7 @@ func InitializeApp() (*App, error) { if err != nil { return nil, err } - appCrudOperationServiceImpl := app2.NewAppCrudOperationServiceImpl(appLabelRepositoryImpl, sugaredLogger, appRepositoryImpl, userRepositoryImpl, installedAppRepositoryImpl, genericNoteServiceImpl, materialRepositoryImpl, installedAppDBServiceImpl, crudOperationServiceConfig) + appCrudOperationServiceImpl := app2.NewAppCrudOperationServiceImpl(appLabelRepositoryImpl, sugaredLogger, appRepositoryImpl, userRepositoryImpl, installedAppRepositoryImpl, genericNoteServiceImpl, materialRepositoryImpl, installedAppDBServiceImpl, crudOperationServiceConfig, dbMigrationServiceImpl) appInfoRestHandlerImpl := appInfo.NewAppInfoRestHandlerImpl(sugaredLogger, appCrudOperationServiceImpl, userServiceImpl, validate, enforcerUtilImpl, enforcerImpl, helmAppServiceImpl, enforcerUtilHelmImpl, genericNoteServiceImpl) appInfoRouterImpl := appInfo2.NewAppInfoRouterImpl(sugaredLogger, appInfoRestHandlerImpl) appFilteringRestHandlerImpl := appList.NewAppFilteringRestHandlerImpl(sugaredLogger, teamServiceImpl, enforcerImpl, userServiceImpl, clusterServiceImpl, environmentServiceImpl) diff --git a/internal/sql/repository/app/AppRepository.go b/internal/sql/repository/app/AppRepository.go index 02e10b95021..fa61abf25cc 100644 --- a/internal/sql/repository/app/AppRepository.go +++ b/internal/sql/repository/app/AppRepository.go @@ -54,6 +54,7 @@ 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) + FindAllActiveByName(appName string) ([]*App, error) FindAppIdByName(appName string) (int, error) FindJobByDisplayName(appName string) (pipelineGroup *App, err error) @@ -133,35 +134,24 @@ func (repo AppRepositoryImpl) SetDescription(id int, description string, userId } func (repo AppRepositoryImpl) FindActiveByName(appName string) (*App, error) { + pipelineGroup := &App{} + err := repo.dbConnection. + Model(pipelineGroup). + Where("app_name = ?", appName). + Where("active = ?", true). + Order("id DESC").Limit(1). + Select() + return pipelineGroup, err +} + +func (repo AppRepositoryImpl) FindAllActiveByName(appName string) ([]*App, error) { var apps []*App err := repo.dbConnection. Model(&apps). Where("app_name = ?", appName). Where("active = ?", true). - Order("id DESC"). Select() - if len(apps) == 1 { - return apps[0], nil - } else if len(apps) > 1 { - isHelmApp := true - for _, app := range apps { - if app.AppType != helper.ChartStoreApp && app.AppType != helper.ExternalChartStoreApp { - isHelmApp = false - break - } - } - if isHelmApp { - err := repo.fixMultipleHelmAppsWithSameName(appName) - if err != nil { - repo.logger.Errorw("error in fixing duplicate helm apps with same name") - return nil, err - } - } - return apps[0], nil - } else { - err = pg.ErrNoRows - } - return nil, err + return apps, err } func (repo AppRepositoryImpl) FindAppIdByName(appName string) (int, error) { @@ -349,52 +339,9 @@ func (repo AppRepositoryImpl) FindAppAndProjectByAppName(appName string) (*App, Where("app.app_name = ?", appName). Where("app.active=?", true). Select() - - if err == pg.ErrMultiRows && (app.AppType == helper.ChartStoreApp || app.AppType == helper.ExternalChartStoreApp) { - // this case can arise in helms apps only - - err := repo.fixMultipleHelmAppsWithSameName(appName) - if err != nil { - repo.logger.Errorw("error in fixing duplicate helm apps with same name") - return nil, err - } - - err = repo.dbConnection.Model(app).Column("Team"). - Where("app.app_name = ?", appName). - Where("app.active=?", true). - Select() - if err != nil { - repo.logger.Errorw("error in fetching apps by name", "appName", appName, "err", err) - return nil, err - } - } return app, err } -func (repo AppRepositoryImpl) fixMultipleHelmAppsWithSameName(appName string) error { - // updating installed apps setting app_id = max app_id - installAppUpdateQuery := `update installed_apps set - app_id=(select max(id) as id from app where app_name = ?) - where app_id in (select id from app where app_name= ? )` - - _, err := repo.dbConnection.Exec(installAppUpdateQuery, appName, appName) - if err != nil { - repo.logger.Errorw("error in updating maxAppId in installedApps", "appName", appName, "err", err) - return err - } - - maxAppIdQuery := repo.dbConnection.Model((*App)(nil)).ColumnExpr("max(id)"). - Where("app_name = ? ", appName). - Where("active = ? ", true) - - // deleting all apps other than app with max id - _, err = repo.dbConnection.Model((*App)(nil)). - Set("active = ?", false).Set("updated_by = ?", SYSTEM_USER_ID).Set("updated_on = ?", time.Now()). - Where("id not in (?) ", maxAppIdQuery).Update() - - return nil -} - func (repo AppRepositoryImpl) FindAllMatchesByAppName(appName string, appType helper.AppType) ([]*App, error) { var apps []*App var err error diff --git a/pkg/app/AppCrudOperationService.go b/pkg/app/AppCrudOperationService.go index 845d1f03ae9..939552346f4 100644 --- a/pkg/app/AppCrudOperationService.go +++ b/pkg/app/AppCrudOperationService.go @@ -24,6 +24,7 @@ import ( client "github.com/devtron-labs/devtron/api/helm-app/service" helmBean "github.com/devtron-labs/devtron/api/helm-app/service/bean" "github.com/devtron-labs/devtron/internal/util" + "github.com/devtron-labs/devtron/pkg/app/dbMigration" "github.com/devtron-labs/devtron/pkg/appStore/installedApp/service/EAMode" util2 "github.com/devtron-labs/devtron/pkg/appStore/util" bean2 "github.com/devtron-labs/devtron/pkg/auth/user/bean" @@ -88,6 +89,7 @@ type AppCrudOperationServiceImpl struct { gitMaterialRepository pipelineConfig.MaterialRepository installedAppDbService EAMode.InstalledAppDBService crudOperationServiceConfig *CrudOperationServiceConfig + dbMigration dbMigration.DbMigration } func NewAppCrudOperationServiceImpl(appLabelRepository pipelineConfig.AppLabelRepository, @@ -96,7 +98,8 @@ func NewAppCrudOperationServiceImpl(appLabelRepository pipelineConfig.AppLabelRe genericNoteService genericNotes.GenericNoteService, gitMaterialRepository pipelineConfig.MaterialRepository, installedAppDbService EAMode.InstalledAppDBService, - crudOperationServiceConfig *CrudOperationServiceConfig) *AppCrudOperationServiceImpl { + crudOperationServiceConfig *CrudOperationServiceConfig, + dbMigration dbMigration.DbMigration) *AppCrudOperationServiceImpl { impl := &AppCrudOperationServiceImpl{ appLabelRepository: appLabelRepository, logger: logger, @@ -106,6 +109,7 @@ func NewAppCrudOperationServiceImpl(appLabelRepository pipelineConfig.AppLabelRe genericNoteService: genericNoteService, gitMaterialRepository: gitMaterialRepository, installedAppDbService: installedAppDbService, + dbMigration: dbMigration, } crudOperationServiceConfig, err := GetCrudOperationServiceConfig() if err != nil { @@ -463,13 +467,29 @@ func (impl AppCrudOperationServiceImpl) getAppAndProjectForAppIdentifier(appIden var err error appNameUniqueIdentifier := appIdentifier.GetUniqueAppNameIdentifier() app, err = impl.appRepository.FindAppAndProjectByAppName(appNameUniqueIdentifier) - if err != nil && err != pg.ErrNoRows { + if err != nil && err != pg.ErrNoRows && err != pg.ErrMultiRows { impl.logger.Errorw("error in fetching app meta data by unique app identifier", "appNameUniqueIdentifier", appNameUniqueIdentifier, "err", err) return app, err } + if err == pg.ErrMultiRows { + validApp, err := impl.dbMigration.FixMultipleAppsForInstalledApp(appNameUniqueIdentifier) + if err != nil { + impl.logger.Errorw("error in fixing multiple installed app entries", "appName", appNameUniqueIdentifier, "err", err) + return app, err + } + return validApp, err + } if util.IsErrNoRows(err) { //find app by display name if not found by unique identifier app, err = impl.appRepository.FindAppAndProjectByAppName(appIdentifier.ReleaseName) + if err == pg.ErrMultiRows { + validApp, err := impl.dbMigration.FixMultipleAppsForInstalledApp(appNameUniqueIdentifier) + if err != nil { + impl.logger.Errorw("error in fixing multiple installed app entries", "appName", appNameUniqueIdentifier, "err", err) + return app, err + } + return validApp, err + } if err != nil { impl.logger.Errorw("error in fetching app meta data by display name", "displayName", appIdentifier.ReleaseName, "err", err) return app, err diff --git a/pkg/app/dbMigration/migration.go b/pkg/app/dbMigration/migration.go new file mode 100644 index 00000000000..c26384724de --- /dev/null +++ b/pkg/app/dbMigration/migration.go @@ -0,0 +1,61 @@ +package dbMigration + +import ( + appRepository "github.com/devtron-labs/devtron/internal/sql/repository/app" + repository2 "github.com/devtron-labs/devtron/pkg/appStore/installedApp/repository" + "go.uber.org/zap" + "time" +) + +type DbMigration interface { + FixMultipleAppsForInstalledApp(appNameUniqueIdentifier string) (*appRepository.App, error) +} + +type DbMigrationServiceImpl struct { + logger *zap.SugaredLogger + appRepository appRepository.AppRepository + installedAppRepository repository2.InstalledAppRepository +} + +func NewDbMigrationServiceImpl( + logger *zap.SugaredLogger, appRepository appRepository.AppRepository, + installedAppRepository repository2.InstalledAppRepository, +) *DbMigrationServiceImpl { + impl := &DbMigrationServiceImpl{ + logger: logger, + appRepository: appRepository, + installedAppRepository: installedAppRepository, + } + return impl +} + +func (impl DbMigrationServiceImpl) FixMultipleAppsForInstalledApp(appNameUniqueIdentifier string) (*appRepository.App, error) { + installedApp, err := impl.installedAppRepository.GetInstalledAppByAppName(appNameUniqueIdentifier) + if err != nil { + impl.logger.Errorw("error in fetching installed app by unique identifier", "appNameUniqueIdentifier", appNameUniqueIdentifier, "err", err) + return nil, err + } + validAppId := installedApp.AppId + allActiveApps, err := impl.appRepository.FindAllActiveByName(appNameUniqueIdentifier) + if err != nil { + impl.logger.Errorw("error in fetching all active apps by name", "appName", appNameUniqueIdentifier, "err", err) + return nil, err + } + var validApp *appRepository.App + for _, activeApp := range allActiveApps { + if activeApp.Id != validAppId { + impl.logger.Info("duplicate entries found for app, marking app inactive ", "appName", appNameUniqueIdentifier) + activeApp.Active = false + activeApp.UpdatedOn = time.Now() + activeApp.UpdatedBy = 1 + err := impl.appRepository.Update(activeApp) + if err != nil { + impl.logger.Errorw("error in marking app inactive", "name", activeApp.AppName, "err", err) + return nil, err + } + } else { + validApp = activeApp + } + } + return validApp, nil +} diff --git a/util/rbac/EnforcerUtil.go b/util/rbac/EnforcerUtil.go index 77f3b952130..a2abf61acd8 100644 --- a/util/rbac/EnforcerUtil.go +++ b/util/rbac/EnforcerUtil.go @@ -18,6 +18,7 @@ package rbac import ( "fmt" + "github.com/devtron-labs/devtron/pkg/app/dbMigration" "golang.org/x/exp/maps" "strings" @@ -89,12 +90,14 @@ type EnforcerUtilImpl struct { ciPipelineRepository pipelineConfig.CiPipelineRepository clusterRepository repository.ClusterRepository enforcer casbin.Enforcer + dbMigration dbMigration.DbMigration } func NewEnforcerUtilImpl(logger *zap.SugaredLogger, teamRepository team.TeamRepository, appRepo app.AppRepository, environmentRepository repository.EnvironmentRepository, pipelineRepository pipelineConfig.PipelineRepository, ciPipelineRepository pipelineConfig.CiPipelineRepository, - clusterRepository repository.ClusterRepository, enforcer casbin.Enforcer) *EnforcerUtilImpl { + clusterRepository repository.ClusterRepository, enforcer casbin.Enforcer, + dbMigration dbMigration.DbMigration) *EnforcerUtilImpl { return &EnforcerUtilImpl{ logger: logger, teamRepository: teamRepository, @@ -104,6 +107,7 @@ func NewEnforcerUtilImpl(logger *zap.SugaredLogger, teamRepository team.TeamRepo ciPipelineRepository: ciPipelineRepository, clusterRepository: clusterRepository, enforcer: enforcer, + dbMigration: dbMigration, } } @@ -401,6 +405,13 @@ func (impl EnforcerUtilImpl) GetHelmObject(appId int, envId int) (string, string func (impl EnforcerUtilImpl) GetHelmObjectByAppNameAndEnvId(appName string, envId int) (string, string) { application, err := impl.appRepo.FindAppAndProjectByAppName(appName) + if err == pg.ErrMultiRows { + application, err = impl.dbMigration.FixMultipleAppsForInstalledApp(appName) + if err != nil { + impl.logger.Errorw("error on fetching data for rbac object", "appName", appName, "err", err) + return fmt.Sprintf("%s/%s/%s", "", "", ""), "" + } + } if err != nil { impl.logger.Errorw("error on fetching data for rbac object", "err", err) return fmt.Sprintf("%s/%s/%s", "", "", ""), "" diff --git a/util/rbac/EnforcerUtilHelm.go b/util/rbac/EnforcerUtilHelm.go index c72ce626614..e3d95ca6fa5 100644 --- a/util/rbac/EnforcerUtilHelm.go +++ b/util/rbac/EnforcerUtilHelm.go @@ -20,6 +20,7 @@ import ( "fmt" "github.com/devtron-labs/devtron/api/helm-app/service/bean" "github.com/devtron-labs/devtron/internal/sql/repository/app" + "github.com/devtron-labs/devtron/pkg/app/dbMigration" repository2 "github.com/devtron-labs/devtron/pkg/appStore/installedApp/repository" "github.com/devtron-labs/devtron/pkg/cluster/repository" "github.com/devtron-labs/devtron/pkg/team" @@ -40,6 +41,7 @@ type EnforcerUtilHelmImpl struct { teamRepository team.TeamRepository appRepository app.AppRepository InstalledAppRepository repository2.InstalledAppRepository + dbMigration dbMigration.DbMigration } func NewEnforcerUtilHelmImpl(logger *zap.SugaredLogger, @@ -47,6 +49,7 @@ func NewEnforcerUtilHelmImpl(logger *zap.SugaredLogger, teamRepository team.TeamRepository, appRepository app.AppRepository, installedAppRepository repository2.InstalledAppRepository, + migration dbMigration.DbMigration, ) *EnforcerUtilHelmImpl { return &EnforcerUtilHelmImpl{ logger: logger, @@ -54,6 +57,7 @@ func NewEnforcerUtilHelmImpl(logger *zap.SugaredLogger, teamRepository: teamRepository, appRepository: appRepository, InstalledAppRepository: installedAppRepository, + dbMigration: migration, } } @@ -98,7 +102,6 @@ func (impl EnforcerUtilHelmImpl) GetHelmObjectByClusterIdNamespaceAndAppName(clu impl.logger.Errorw("error in fetching app details", "err", err) return "", "" } - if app.TeamId == 0 { // case if project is not assigned to cli app return fmt.Sprintf("%s/%s__%s/%s", team.UNASSIGNED_PROJECT, cluster.ClusterName, namespace, appName), fmt.Sprintf("%s/%s/%s", team.UNASSIGNED_PROJECT, namespace, appName) @@ -138,9 +141,15 @@ func (impl EnforcerUtilHelmImpl) getAppObject(clusterId int, namespace string, a } appNameIdentifier := appIdentifier.GetUniqueAppNameIdentifier() appObj, err := impl.appRepository.FindAppAndProjectByAppName(appNameIdentifier) + if err == pg.ErrMultiRows { + appObj, err = impl.dbMigration.FixMultipleAppsForInstalledApp(appName) + } if appObj == nil || err == pg.ErrNoRows { impl.logger.Warnw("appObj not found, going to find app using display name ", "appIdentifier", appNameIdentifier, "appName", appName) appObj, err = impl.appRepository.FindAppAndProjectByAppName(appName) + if err == pg.ErrMultiRows { + appObj, err = impl.dbMigration.FixMultipleAppsForInstalledApp(appName) + } } return appObj, err } diff --git a/wire_gen.go b/wire_gen.go index 9ad1f1575a8..939e8fc67c7 100644 --- a/wire_gen.go +++ b/wire_gen.go @@ -98,6 +98,7 @@ import ( "github.com/devtron-labs/devtron/internal/util" "github.com/devtron-labs/devtron/pkg/apiToken" app2 "github.com/devtron-labs/devtron/pkg/app" + "github.com/devtron-labs/devtron/pkg/app/dbMigration" "github.com/devtron-labs/devtron/pkg/app/status" "github.com/devtron-labs/devtron/pkg/appClone" "github.com/devtron-labs/devtron/pkg/appClone/batch" @@ -359,7 +360,8 @@ func InitializeApp() (*App, error) { } helmAppClientImpl := gRPC.NewHelmAppClientImpl(sugaredLogger, helmClientConfig, configuration) pumpImpl := connector.NewPumpImpl(sugaredLogger) - enforcerUtilHelmImpl := rbac.NewEnforcerUtilHelmImpl(sugaredLogger, clusterRepositoryImpl, teamRepositoryImpl, appRepositoryImpl, installedAppRepositoryImpl) + dbMigrationServiceImpl := dbMigration.NewDbMigrationServiceImpl(sugaredLogger, appRepositoryImpl, installedAppRepositoryImpl) + enforcerUtilHelmImpl := rbac.NewEnforcerUtilHelmImpl(sugaredLogger, clusterRepositoryImpl, teamRepositoryImpl, appRepositoryImpl, installedAppRepositoryImpl, dbMigrationServiceImpl) serverDataStoreServerDataStore := serverDataStore.InitServerDataStore() appStoreApplicationVersionRepositoryImpl := appStoreDiscoverRepository.NewAppStoreApplicationVersionRepositoryImpl(sugaredLogger, db) helmReleaseConfig, err := service.GetHelmReleaseConfig() @@ -457,7 +459,7 @@ func InitializeApp() (*App, error) { if err != nil { return nil, err } - enforcerUtilImpl := rbac.NewEnforcerUtilImpl(sugaredLogger, teamRepositoryImpl, appRepositoryImpl, environmentRepositoryImpl, pipelineRepositoryImpl, ciPipelineRepositoryImpl, clusterRepositoryImpl, enforcerImpl) + enforcerUtilImpl := rbac.NewEnforcerUtilImpl(sugaredLogger, teamRepositoryImpl, appRepositoryImpl, environmentRepositoryImpl, pipelineRepositoryImpl, ciPipelineRepositoryImpl, clusterRepositoryImpl, enforcerImpl, dbMigrationServiceImpl) appStatusServiceImpl := appStatus2.NewAppStatusServiceImpl(appStatusRepositoryImpl, sugaredLogger, enforcerImpl, enforcerUtilImpl) scopedVariableRepositoryImpl := repository7.NewScopedVariableRepository(db, sugaredLogger, transactionUtilImpl) devtronResourceSearchableKeyRepositoryImpl := repository8.NewDevtronResourceSearchableKeyRepositoryImpl(sugaredLogger, db) @@ -550,7 +552,7 @@ func InitializeApp() (*App, error) { if err != nil { return nil, err } - appCrudOperationServiceImpl := app2.NewAppCrudOperationServiceImpl(appLabelRepositoryImpl, sugaredLogger, appRepositoryImpl, userRepositoryImpl, installedAppRepositoryImpl, genericNoteServiceImpl, materialRepositoryImpl, installedAppDBServiceImpl, crudOperationServiceConfig) + appCrudOperationServiceImpl := app2.NewAppCrudOperationServiceImpl(appLabelRepositoryImpl, sugaredLogger, appRepositoryImpl, userRepositoryImpl, installedAppRepositoryImpl, genericNoteServiceImpl, materialRepositoryImpl, installedAppDBServiceImpl, crudOperationServiceConfig, dbMigrationServiceImpl) imageTagRepositoryImpl := repository2.NewImageTagRepository(db, sugaredLogger) customTagServiceImpl := pipeline.NewCustomTagService(sugaredLogger, imageTagRepositoryImpl) pluginInputVariableParserImpl := pipeline.NewPluginInputVariableParserImpl(sugaredLogger, dockerRegistryConfigImpl, customTagServiceImpl)