diff --git a/api/helm-app/service/HelmAppService.go b/api/helm-app/service/HelmAppService.go index 01e1e90d94c..a803e0e358d 100644 --- a/api/helm-app/service/HelmAppService.go +++ b/api/helm-app/service/HelmAppService.go @@ -1159,7 +1159,12 @@ func (impl *HelmAppServiceImpl) appListRespProtoTransformer(deployedApps *gRPC.D } // end lastDeployed := deployedapp.LastDeployed.AsTime() - appDetails, appFetchErr := impl.appRepository.FindActiveByName(deployedapp.AppName) + appDetails, appFetchErr := impl.getAppForAppIdentifier( + &helmBean.AppIdentifier{ + ClusterId: int(deployedapp.EnvironmentDetail.ClusterId), + Namespace: deployedapp.EnvironmentDetail.Namespace, + ReleaseName: deployedapp.AppName, + }) projectId := int32(0) if appFetchErr == nil { projectId = int32(appDetails.TeamId) diff --git a/internal/sql/repository/app/AppRepository.go b/internal/sql/repository/app/AppRepository.go index 76d526f1744..02e10b95021 100644 --- a/internal/sql/repository/app/AppRepository.go +++ b/internal/sql/repository/app/AppRepository.go @@ -40,6 +40,10 @@ type App struct { sql.AuditLog } +const ( + SYSTEM_USER_ID = 1 +) + func (r *App) IsAppJobOrExternalType() bool { return len(r.DisplayName) > 0 } @@ -129,16 +133,37 @@ func (repo AppRepositoryImpl) SetDescription(id int, description string, userId } func (repo AppRepositoryImpl) FindActiveByName(appName string) (*App, error) { - pipelineGroup := &App{} + var apps []*App err := repo.dbConnection. - Model(pipelineGroup). + Model(&apps). Where("app_name = ?", appName). Where("active = ?", true). - Order("id DESC").Limit(1). + Order("id DESC"). Select() - // there is only single active app will be present in db with a same name. - return pipelineGroup, err + 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 } + func (repo AppRepositoryImpl) FindAppIdByName(appName string) (int, error) { app := &App{} err := repo.dbConnection. @@ -324,9 +349,52 @@ 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 a7035d7603f..845d1f03ae9 100644 --- a/pkg/app/AppCrudOperationService.go +++ b/pkg/app/AppCrudOperationService.go @@ -533,10 +533,15 @@ func (impl AppCrudOperationServiceImpl) GetHelmAppMetaInfo(appId string) (*bean. } // if app.DisplayName is empty then that app_name is not yet migrated to app name unique identifier if app.Id > 0 && len(app.DisplayName) == 0 { - err = impl.updateAppNameToUniqueAppIdentifierInApp(app, appIdDecoded) + appNameUniqueIdentifier := appIdDecoded.GetUniqueAppNameIdentifier() + app.AppName = appNameUniqueIdentifier + app.DisplayName = appIdDecoded.ReleaseName + app.UpdatedBy = bean2.SystemUserId + app.UpdatedOn = time.Now() + err = impl.appRepository.Update(app) if err != nil { - impl.logger.Errorw("GetHelmAppMetaInfo, error in migrating displayName and appName to unique identifier for external apps", "appIdentifier", appIdDecoded, "err", err) - //not returning from here as we need to show helm app metadata even if migration of app_name fails, then migration can happen on project update + impl.logger.Errorw("error in migrating displayName and appName to unique identifier", "appNameUniqueIdentifier", appNameUniqueIdentifier, "err", err) + return nil, err } } if app.Id == 0 { @@ -569,6 +574,12 @@ func (impl AppCrudOperationServiceImpl) GetHelmAppMetaInfo(appId string) (*bean. } } + err = impl.fixMultipleInstalledAppForSingleApp(app) + if err != nil { + impl.logger.Errorw("GetHelmAppMetaInfo, error in fixing multiple installed apps linked to same app", "appId", appId, "err", err) + return nil, err + } + user, err := impl.userRepository.GetByIdIncludeDeleted(app.CreatedBy) if err != nil && err != pg.ErrNoRows { impl.logger.Errorw("error in fetching user for app meta info", "error", err) @@ -598,6 +609,26 @@ func (impl AppCrudOperationServiceImpl) GetHelmAppMetaInfo(appId string) (*bean. return info, nil } +// fixMultipleInstalledAppForSingleApp fixes multiple entries of installed app for single app +func (impl AppCrudOperationServiceImpl) fixMultipleInstalledAppForSingleApp(app *appRepository.App) error { + isLinked, installedApps, err := impl.installedAppDbService.IsExternalAppLinkedToChartStore(app.Id) + if err != nil { + impl.logger.Errorw("error in checking IsExternalAppLinkedToChartStore", "appId", app.Id, "err", err) + return err + } + //if isLinked is true and more than one installed app is found for that app, we will create new app for each installed app + if isLinked && len(installedApps) > 1 { + // if installed_apps are already present for that display_name then migrate the app_name to unique identifier with installedApp's ns and clusterId. + // creating new entry for app all installedApps with uniqueAppNameIdentifier and display name + err := impl.installedAppDbService.CreateNewAppEntryForAllInstalledApps(installedApps) + if err != nil { + impl.logger.Errorw("error in CreateNewAppEntryForAllInstalledApps", "appName", app.AppName, "err", err) + //not returning from here as we have to migrate the app for requested ext-app and return the response for meta info + } + } + return nil +} + func (impl AppCrudOperationServiceImpl) getLabelsByAppIdForDeployment(appId int) (map[string]string, error) { labelsDto := make(map[string]string) labels, err := impl.appLabelRepository.FindAllByAppId(appId) diff --git a/pkg/appStore/installedApp/service/EAMode/InstalledAppDBService.go b/pkg/appStore/installedApp/service/EAMode/InstalledAppDBService.go index 6eb4156e245..21adc8dbd6b 100644 --- a/pkg/appStore/installedApp/service/EAMode/InstalledAppDBService.go +++ b/pkg/appStore/installedApp/service/EAMode/InstalledAppDBService.go @@ -396,6 +396,17 @@ func (impl *InstalledAppDBServiceImpl) CreateNewAppEntryForAllInstalledApps(inst if err != nil { return err } + currApp, err := impl.AppRepository.FindById(installedApps[0].AppId) + if err != nil { + impl.Logger.Errorw("error in fetching app by id", "appId", currApp.Id, "err", err) + return err + } + currApp.Active = false + err = impl.AppRepository.UpdateWithTxn(currApp, tx) + if err != nil { + impl.Logger.Errorw("error in marking current app inactive while creating new apps", "currentAppId", currApp.Id, "err", err) + return err + } // Rollback tx on error. defer tx.Rollback() for _, installedApp := range installedApps {