diff --git a/api/appStore/InstalledAppRestHandler.go b/api/appStore/InstalledAppRestHandler.go index eec150392f..a5cad4921c 100644 --- a/api/appStore/InstalledAppRestHandler.go +++ b/api/appStore/InstalledAppRestHandler.go @@ -26,6 +26,7 @@ import ( "github.com/devtron-labs/devtron/pkg/appStore/installedApp/service/FullMode" "github.com/devtron-labs/devtron/pkg/appStore/installedApp/service/FullMode/deploymentTypeChange" "github.com/devtron-labs/devtron/pkg/appStore/installedApp/service/FullMode/resource" + util3 "github.com/devtron-labs/devtron/pkg/appStore/util" "github.com/devtron-labs/devtron/pkg/bean" "net/http" "strconv" @@ -603,6 +604,10 @@ func (handler *InstalledAppRestHandlerImpl) FetchAppDetailsForInstalledApp(w htt common.WriteJsonResp(w, err, "App not found in database", http.StatusBadRequest) return } + if util3.IsExternalChartStoreApp(installedApp.App.DisplayName) { + //this is external app case where app_name is a unique identifier, and we want to fetch resource based on display_name + handler.installedAppService.ChangeAppNameToDisplayNameForInstalledApp(installedApp) + } appDetail, err := handler.installedAppService.FindAppDetailsForAppstoreApplication(installedAppId, envId) if err != nil { @@ -724,6 +729,10 @@ func (handler *InstalledAppRestHandlerImpl) FetchResourceTree(w http.ResponseWri common.WriteJsonResp(w, err, "App not found in database", http.StatusBadRequest) return } + if util3.IsExternalChartStoreApp(installedApp.App.DisplayName) { + //this is external app case where app_name is a unique identifier, and we want to fetch resource based on display_name + handler.installedAppService.ChangeAppNameToDisplayNameForInstalledApp(installedApp) + } if installedApp.Environment.IsVirtualEnvironment { common.WriteJsonResp(w, nil, nil, http.StatusOK) return diff --git a/api/appStore/deployment/CommonDeploymentRestHandler.go b/api/appStore/deployment/CommonDeploymentRestHandler.go index 1963952319..726d2ba227 100644 --- a/api/appStore/deployment/CommonDeploymentRestHandler.go +++ b/api/appStore/deployment/CommonDeploymentRestHandler.go @@ -93,7 +93,8 @@ func (handler *CommonDeploymentRestHandlerImpl) getAppOfferingMode(installedAppI err = &util.ApiError{HttpStatusCode: http.StatusBadRequest, UserMessage: "invalid app id"} return appOfferingMode, installedAppDto, err } - installedAppDto, err = handler.installedAppService.GetInstalledAppByClusterNamespaceAndName(appIdentifier.ClusterId, appIdentifier.Namespace, appIdentifier.ReleaseName) + uniqueAppName := appIdentifier.GetUniqueAppNameIdentifier() + installedAppDto, err = handler.installedAppService.GetInstalledAppByClusterNamespaceAndName(appIdentifier.ClusterId, appIdentifier.Namespace, uniqueAppName) if err != nil { err = &util.ApiError{HttpStatusCode: http.StatusBadRequest, UserMessage: "unable to find app in database"} return appOfferingMode, installedAppDto, err diff --git a/api/helm-app/HelmAppRestHandler.go b/api/helm-app/HelmAppRestHandler.go index 6990ad279e..15ae30c452 100644 --- a/api/helm-app/HelmAppRestHandler.go +++ b/api/helm-app/HelmAppRestHandler.go @@ -224,7 +224,7 @@ func (handler *HelmAppRestHandlerImpl) GetReleaseInfo(w http.ResponseWriter, r * common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) return } - installedApp, err := handler.installedAppService.GetInstalledAppByClusterNamespaceAndName(appIdentifier.ClusterId, appIdentifier.Namespace, appIdentifier.ReleaseName) + installedAppVersionDto, err := handler.installedAppService.GetReleaseInfo(appIdentifier) if err != nil { common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) return @@ -232,7 +232,7 @@ func (handler *HelmAppRestHandlerImpl) GetReleaseInfo(w http.ResponseWriter, r * res := &bean.ReleaseAndInstalledAppInfo{ ReleaseInfo: releaseInfo, - InstalledAppInfo: bean.ConvertToInstalledAppInfo(installedApp), + InstalledAppInfo: bean.ConvertToInstalledAppInfo(installedAppVersionDto), } common.WriteJsonResp(w, err, res, http.StatusOK) diff --git a/api/helm-app/HelmAppRouter.go b/api/helm-app/HelmAppRouter.go index 6036b36b9c..132708fb46 100644 --- a/api/helm-app/HelmAppRouter.go +++ b/api/helm-app/HelmAppRouter.go @@ -29,6 +29,7 @@ func (impl *HelmAppRouterImpl) InitAppListRouter(helmRouter *mux.Router) { helmRouter.Path("/hibernate").HandlerFunc(impl.helmAppRestHandler.Hibernate).Methods("POST") helmRouter.Path("/unhibernate").HandlerFunc(impl.helmAppRestHandler.UnHibernate).Methods("POST") + // GetReleaseInfo used only for external apps helmRouter.Path("/release-info").Queries("appId", "{appId}"). HandlerFunc(impl.helmAppRestHandler.GetReleaseInfo).Methods("GET") diff --git a/api/helm-app/service/HelmAppService.go b/api/helm-app/service/HelmAppService.go index 887736f98e..f16ef581e0 100644 --- a/api/helm-app/service/HelmAppService.go +++ b/api/helm-app/service/HelmAppService.go @@ -14,7 +14,6 @@ import ( "net/http" "reflect" "strconv" - "strings" "time" "github.com/caarlos0/env/v6" @@ -56,7 +55,7 @@ type HelmAppService interface { GetValuesYaml(ctx context.Context, app *AppIdentifier) (*gRPC.ReleaseInfo, error) GetDesiredManifest(ctx context.Context, app *AppIdentifier, resource *openapi.ResourceIdentifier) (*openapi.DesiredManifestResponse, error) DeleteApplication(ctx context.Context, app *AppIdentifier) (*openapi.UninstallReleaseResponse, error) - DeleteDBLinkedHelmApplication(ctx context.Context, app *AppIdentifier, useId int32) (*openapi.UninstallReleaseResponse, error) + DeleteDBLinkedHelmApplication(ctx context.Context, appIdentifier *AppIdentifier, useId int32) (*openapi.UninstallReleaseResponse, error) // UpdateApplication is a wrapper over helmAppClient.UpdateApplication, sends update request to kubelink for external chart store apps UpdateApplication(ctx context.Context, app *AppIdentifier, request *bean.UpdateApplicationRequestDto) (*openapi.UpdateReleaseResponse, error) GetDeploymentDetail(ctx context.Context, app *AppIdentifier, version int32) (*openapi.HelmAppDeploymentManifestDetail, error) @@ -448,7 +447,56 @@ func (impl *HelmAppServiceImpl) GetDesiredManifest(ctx context.Context, app *App return response, nil } -func (impl *HelmAppServiceImpl) DeleteDBLinkedHelmApplication(ctx context.Context, app *AppIdentifier, userId int32) (*openapi.UninstallReleaseResponse, error) { +// getInstalledAppForAppIdentifier return installed_apps for app unique identifier or releaseName/displayName whichever exists else return pg.ErrNoRows +func (impl *HelmAppServiceImpl) getInstalledAppForAppIdentifier(appIdentifier *AppIdentifier) (*repository.InstalledApps, error) { + model := &repository.InstalledApps{} + var err error + //for ext apps search app from unique identifier + appUniqueIdentifier := appIdentifier.GetUniqueAppNameIdentifier() + model, err = impl.installedAppRepository.GetInstalledAppByAppName(appUniqueIdentifier) + if err != nil { + if util.IsErrNoRows(err) { + //if error is pg no rows, then find installed app via app.DisplayName because this can also happen that + //an ext-app is already linked to devtron, and it's entry in app_name col in app table will not be a unique + //identifier but the display name. + displayName := appIdentifier.ReleaseName + model, err = impl.installedAppRepository.GetInstalledAppByAppName(displayName) + if err != nil { + impl.logger.Errorw("error in fetching installed app from display name", "appDisplayName", displayName, "err", err) + return model, err + } + } else { + impl.logger.Errorw("error in fetching installed app by app unique identifier", "appUniqueIdentifier", appUniqueIdentifier, "err", err) + return model, err + } + } + return model, nil +} + +func (impl *HelmAppServiceImpl) getAppForAppIdentifier(appIdentifier *AppIdentifier) (*app.App, error) { + //for ext apps search app from unique identifier + appUniqueIdentifier := appIdentifier.GetUniqueAppNameIdentifier() + model, err := impl.appRepository.FindActiveByName(appUniqueIdentifier) + if err != nil { + if util.IsErrNoRows(err) { + //if error is pg no rows, then find app via release name because this can also happen that a project is + //already assigned a project, and it's entry in app_name col in app table will not be a unique + //identifier but the display name i.e. release name. + displayName := appIdentifier.ReleaseName + model, err = impl.appRepository.FindActiveByName(displayName) + if err != nil { + impl.logger.Errorw("error in fetching app from display name", "appDisplayName", displayName, "err", err) + return nil, err + } + } else { + impl.logger.Errorw("error in fetching app by app unique identifier", "appUniqueIdentifier", appUniqueIdentifier, "err", err) + return nil, err + } + } + return model, nil +} + +func (impl *HelmAppServiceImpl) DeleteDBLinkedHelmApplication(ctx context.Context, appIdentifier *AppIdentifier, userId int32) (*openapi.UninstallReleaseResponse, error) { dbConnection := impl.appRepository.GetConnection() tx, err := dbConnection.Begin() if err != nil { @@ -457,67 +505,94 @@ func (impl *HelmAppServiceImpl) DeleteDBLinkedHelmApplication(ctx context.Contex } // Rollback tx on error. defer tx.Rollback() + var isAppLinkedToChartStore bool // if true, entry present in both app and installed_app table - // For Helm deployments ReleaseName is App.Name - model, err := impl.installedAppRepository.GetInstalledAppByAppName(app.ReleaseName) - if err != nil { - impl.logger.Errorw("error in fetching installed app", "appName", app.ReleaseName, "err", err) + installedAppModel, err := impl.getInstalledAppForAppIdentifier(appIdentifier) + if err != nil && !util.IsErrNoRows(err) { + impl.logger.Errorw("DeleteDBLinkedHelmApplication, error in fetching installed app for app identifier", "appIdentifier", appIdentifier, "err", err) return nil, err } - - // If there are two releases with same name but in different namespace (eg: test -n demo-1 {Hyperion App}, test -n demo-2 {Externally Installed}); - // And if the request is received for the externally installed app, the below condition will handle - if model.Environment.Namespace != app.Namespace { - return nil, pg.ErrNoRows + if installedAppModel.Id > 0 { + isAppLinkedToChartStore = true } - // App Delete --> Start - //soft delete app - appModel := &model.App - appModel.Active = false - appModel.UpdatedBy = userId - appModel.UpdatedOn = time.Now() - err = impl.appRepository.UpdateWithTxn(appModel, tx) - if err != nil { - impl.logger.Errorw("error in deleting appModel", "app", appModel) - return nil, err - } - // App Delete --> End + if isAppLinkedToChartStore { + // If there are two releases with same name but in different namespace (eg: test -n demo-1 {Hyperion App}, test -n demo-2 {Externally Installed}); + // And if the request is received for the externally installed app, the below condition will handle + if installedAppModel.Environment.Namespace != appIdentifier.Namespace { + return nil, pg.ErrNoRows + } - // InstalledApp Delete --> Start - // soft delete install app - model.Active = false - model.UpdatedBy = userId - model.UpdatedOn = time.Now() - _, err = impl.installedAppRepository.UpdateInstalledApp(model, tx) - if err != nil { - impl.logger.Errorw("error while deleting install app", "error", err) - return nil, err - } - // InstalledApp Delete --> End + // App Delete --> Start + //soft delete app + appModel := &installedAppModel.App + appModel.Active = false + appModel.UpdatedBy = userId + appModel.UpdatedOn = time.Now() + err = impl.appRepository.UpdateWithTxn(appModel, tx) + if err != nil { + impl.logger.Errorw("error in deleting appModel", "app", appModel) + return nil, err + } + // App Delete --> End + + // InstalledApp Delete --> Start + // soft delete install app + installedAppModel.Active = false + installedAppModel.UpdatedBy = userId + installedAppModel.UpdatedOn = time.Now() + _, err = impl.installedAppRepository.UpdateInstalledApp(installedAppModel, tx) + if err != nil { + impl.logger.Errorw("error while deleting install app", "error", err) + return nil, err + } + // InstalledApp Delete --> End - // InstalledAppVersions Delete --> Start - models, err := impl.installedAppRepository.GetInstalledAppVersionByInstalledAppId(model.Id) - if err != nil { - impl.logger.Errorw("error while fetching install app versions", "error", err) - return nil, err - } + // InstalledAppVersions Delete --> Start + models, err := impl.installedAppRepository.GetInstalledAppVersionByInstalledAppId(installedAppModel.Id) + if err != nil { + impl.logger.Errorw("error while fetching install app versions", "error", err) + return nil, err + } - // soft delete install app versions - for _, item := range models { - item.Active = false - item.UpdatedBy = userId - item.UpdatedOn = time.Now() - _, err = impl.installedAppRepository.UpdateInstalledAppVersion(item, tx) + // soft delete install app versions + for _, item := range models { + item.Active = false + item.UpdatedBy = userId + item.UpdatedOn = time.Now() + _, err = impl.installedAppRepository.UpdateInstalledAppVersion(item, tx) + if err != nil { + impl.logger.Errorw("error while fetching from db", "error", err) + return nil, err + } + } + // InstalledAppVersions Delete --> End + } else { + //this means app not found in installed_apps, but a scenario where an external app is only + //assigned project and not linked to devtron, in that case only entry in app will be found. + appModel, err := impl.getAppForAppIdentifier(appIdentifier) if err != nil { - impl.logger.Errorw("error while fetching from db", "error", err) + impl.logger.Errorw("DeleteDBLinkedHelmApplication, error in fetching app from appIdentifier", "appIdentifier", appIdentifier, "err", err) return nil, err } + if appModel != nil && appModel.Id > 0 { + // App Delete --> Start + //soft delete app + appModel.Active = false + appModel.UpdatedBy = userId + appModel.UpdatedOn = time.Now() + err = impl.appRepository.UpdateWithTxn(appModel, tx) + if err != nil { + impl.logger.Errorw("error in deleting appModel", "app", appModel) + return nil, err + } + // App Delete --> End + } } - // InstalledAppVersions Delete --> End - res, err := impl.DeleteApplication(ctx, app) + + res, err := impl.DeleteApplication(ctx, appIdentifier) if err != nil { - impl.logger.Errorw("error in deleting helm application", "error", err, "appIdentifier", app) + impl.logger.Errorw("error in deleting helm application", "error", err, "appIdentifier", appIdentifier) return nil, err } @@ -1005,29 +1080,32 @@ type AppIdentifier struct { ReleaseName string `json:"releaseName"` } +// GetUniqueAppNameIdentifier returns unique app name identifier, we store all helm releases in kubelink cache with key +// as what is returned from this func, this is the case where an app across diff namespace or cluster can have same name, +// so to identify then uniquely below implementation would serve as good unique identifier for an external app. +func (r *AppIdentifier) GetUniqueAppNameIdentifier() string { + return fmt.Sprintf("%s-%s-%s", r.ReleaseName, r.Namespace, strconv.Itoa(r.ClusterId)) +} + +func (r *AppIdentifier) GetUniqueAppIdentifierForGivenNamespaceAndCluster(namespace, clusterId string) string { + return fmt.Sprintf("%s-%s-%s", r.ReleaseName, namespace, clusterId) +} + func (impl *HelmAppServiceImpl) DecodeAppId(appId string) (*AppIdentifier, error) { - component := strings.Split(appId, "|") - if len(component) != 3 { - return nil, fmt.Errorf("malformed app id %s", appId) - } - clusterId, err := strconv.Atoi(component[0]) - if err != nil { - return nil, err - } - if clusterId <= 0 { - return nil, fmt.Errorf("target cluster is not provided") - } - return &AppIdentifier{ - ClusterId: clusterId, - Namespace: component[1], - ReleaseName: component[2], - }, nil + return DecodeExternalAppAppId(appId) } func (impl *HelmAppServiceImpl) EncodeAppId(appIdentifier *AppIdentifier) string { return fmt.Sprintf("%d|%s|%s", appIdentifier.ClusterId, appIdentifier.Namespace, appIdentifier.ReleaseName) } +func isSameAppName(deployedAppName string, appDto app.App) bool { + if len(appDto.DisplayName) > 0 { + return deployedAppName == appDto.DisplayName + } + return deployedAppName == appDto.AppName +} + func (impl *HelmAppServiceImpl) appListRespProtoTransformer(deployedApps *gRPC.DeployedAppList, token string, helmAuth func(token string, object string) bool, helmCdPipelines []*pipelineConfig.Pipeline, installedHelmApps []*repository.InstalledApps) openapi.AppList { applicationType := "HELM-APP" appList := openapi.AppList{ClusterIds: &[]int32{deployedApps.ClusterId}, ApplicationType: &applicationType} @@ -1055,7 +1133,7 @@ func (impl *HelmAppServiceImpl) appListRespProtoTransformer(deployedApps *gRPC.D // do not add helm apps in the list which are created using app_store for _, installedHelmApp := range installedHelmApps { - if deployedapp.AppName == installedHelmApp.App.AppName && int(deployedapp.EnvironmentDetail.ClusterId) == installedHelmApp.Environment.ClusterId && deployedapp.EnvironmentDetail.Namespace == installedHelmApp.Environment.Namespace { + if isSameAppName(deployedapp.AppName, installedHelmApp.App) && int(deployedapp.EnvironmentDetail.ClusterId) == installedHelmApp.Environment.ClusterId && deployedapp.EnvironmentDetail.Namespace == installedHelmApp.Environment.Namespace { toExcludeFromList = true break } diff --git a/api/helm-app/service/helper.go b/api/helm-app/service/helper.go new file mode 100644 index 0000000000..dc835ea9a3 --- /dev/null +++ b/api/helm-app/service/helper.go @@ -0,0 +1,26 @@ +package service + +import ( + "fmt" + "strconv" + "strings" +) + +func DecodeExternalAppAppId(appId string) (*AppIdentifier, error) { + component := strings.Split(appId, "|") + if len(component) != 3 { + return nil, fmt.Errorf("malformed app id %s", appId) + } + clusterId, err := strconv.Atoi(component[0]) + if err != nil { + return nil, err + } + if clusterId <= 0 { + return nil, fmt.Errorf("target cluster is not provided") + } + return &AppIdentifier{ + ClusterId: clusterId, + Namespace: component[1], + ReleaseName: component[2], + }, nil +} diff --git a/api/restHandler/app/appList/AppListingRestHandler.go b/api/restHandler/app/appList/AppListingRestHandler.go index 22e908c3b2..341f91dfe7 100644 --- a/api/restHandler/app/appList/AppListingRestHandler.go +++ b/api/restHandler/app/appList/AppListingRestHandler.go @@ -28,6 +28,7 @@ import ( argoApplication "github.com/devtron-labs/devtron/client/argocdServer/bean" "github.com/devtron-labs/devtron/pkg/appStore/installedApp/service/FullMode" "github.com/devtron-labs/devtron/pkg/appStore/installedApp/service/FullMode/resource" + util4 "github.com/devtron-labs/devtron/pkg/appStore/util" bean2 "github.com/devtron-labs/devtron/pkg/cluster/repository/bean" "net/http" "strconv" @@ -909,6 +910,10 @@ func (handler AppListingRestHandlerImpl) GetHostUrlsByBatch(w http.ResponseWrite common.WriteJsonResp(w, err, "App not found in database", http.StatusBadRequest) return } + if util4.IsExternalChartStoreApp(installedApp.App.DisplayName) { + //this is external app case where app_name is a unique identifier, and we want to fetch resource based on display_name + handler.installedAppService.ChangeAppNameToDisplayNameForInstalledApp(installedApp) + } resourceTreeAndNotesContainer := bean.AppDetailsContainer{} resourceTreeAndNotesContainer, err = handler.fetchResourceTreeFromInstallAppService(w, r, resourceTreeAndNotesContainer, *installedApp) if err != nil { diff --git a/cmd/external-app/wire_gen.go b/cmd/external-app/wire_gen.go index 6acb6b09c5..d4e7974bb9 100644 --- a/cmd/external-app/wire_gen.go +++ b/cmd/external-app/wire_gen.go @@ -261,7 +261,7 @@ func InitializeApp() (*App, error) { deploymentTypeOverrideServiceImpl := providerConfig.NewDeploymentTypeOverrideServiceImpl(sugaredLogger, environmentVariables, attributesServiceImpl) eaModeDeploymentServiceImpl := EAMode.NewEAModeDeploymentServiceImpl(sugaredLogger, helmAppServiceImpl, appStoreApplicationVersionRepositoryImpl, helmAppClientImpl, installedAppRepositoryImpl, ociRegistryConfigRepositoryImpl) appStoreValidatorImpl := service2.NewAppAppStoreValidatorImpl(sugaredLogger) - appStoreDeploymentDBServiceImpl := service2.NewAppStoreDeploymentDBServiceImpl(sugaredLogger, installedAppRepositoryImpl, appStoreApplicationVersionRepositoryImpl, appRepositoryImpl, environmentServiceImpl, clusterServiceImpl, installedAppVersionHistoryRepositoryImpl, environmentVariables, gitOpsConfigReadServiceImpl, deploymentTypeOverrideServiceImpl, eaModeDeploymentServiceImpl, appStoreValidatorImpl) + appStoreDeploymentDBServiceImpl := service2.NewAppStoreDeploymentDBServiceImpl(sugaredLogger, installedAppRepositoryImpl, appStoreApplicationVersionRepositoryImpl, appRepositoryImpl, environmentServiceImpl, clusterServiceImpl, installedAppVersionHistoryRepositoryImpl, environmentVariables, gitOpsConfigReadServiceImpl, deploymentTypeOverrideServiceImpl, eaModeDeploymentServiceImpl, appStoreValidatorImpl, installedAppDBServiceImpl) chartGroupDeploymentRepositoryImpl := repository7.NewChartGroupDeploymentRepositoryImpl(db, sugaredLogger) acdConfig, err := argocdServer.GetACDDeploymentConfig() if err != nil { @@ -392,7 +392,7 @@ func InitializeApp() (*App, error) { attributesRouterImpl := router.NewAttributesRouterImpl(attributesRestHandlerImpl) appLabelRepositoryImpl := pipelineConfig.NewAppLabelRepositoryImpl(db) materialRepositoryImpl := pipelineConfig.NewMaterialRepositoryImpl(db) - appCrudOperationServiceImpl := app2.NewAppCrudOperationServiceImpl(appLabelRepositoryImpl, sugaredLogger, appRepositoryImpl, userRepositoryImpl, installedAppRepositoryImpl, genericNoteServiceImpl, materialRepositoryImpl) + appCrudOperationServiceImpl := app2.NewAppCrudOperationServiceImpl(appLabelRepositoryImpl, sugaredLogger, appRepositoryImpl, userRepositoryImpl, installedAppRepositoryImpl, genericNoteServiceImpl, materialRepositoryImpl, installedAppDBServiceImpl) 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 4988783ec6..73ca2a541b 100644 --- a/internal/sql/repository/app/AppRepository.go +++ b/internal/sql/repository/app/AppRepository.go @@ -41,6 +41,10 @@ type App struct { sql.AuditLog } +func (r *App) IsAppJobOrExternalType() bool { + return len(r.DisplayName) > 0 +} + type AppRepository interface { SaveWithTxn(pipelineGroup *App, tx *pg.Tx) error Update(app *App) error diff --git a/internal/sql/repository/helper/AppListingRepositoryQueryBuilder.go b/internal/sql/repository/helper/AppListingRepositoryQueryBuilder.go index 655bf2b314..4a9b9acf26 100644 --- a/internal/sql/repository/helper/AppListingRepositoryQueryBuilder.go +++ b/internal/sql/repository/helper/AppListingRepositoryQueryBuilder.go @@ -31,6 +31,8 @@ const ( CustomApp AppType = 0 // cicd app ChartStoreApp AppType = 1 // helm app Job AppType = 2 // jobs + // ExternalChartStoreApp app-type is not stored in db + ExternalChartStoreApp AppType = 3 // external helm app ) type AppListingRepositoryQueryBuilder struct { diff --git a/pkg/app/AppCrudOperationService.go b/pkg/app/AppCrudOperationService.go index 57de8600bc..813f736458 100644 --- a/pkg/app/AppCrudOperationService.go +++ b/pkg/app/AppCrudOperationService.go @@ -20,6 +20,11 @@ package app import ( "encoding/json" "fmt" + client "github.com/devtron-labs/devtron/api/helm-app/service" + "github.com/devtron-labs/devtron/internal/util" + "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" bean3 "github.com/devtron-labs/devtron/pkg/deployment/manifest/bean" "regexp" "strconv" @@ -28,7 +33,6 @@ import ( "github.com/devtron-labs/common-lib/utils/k8s" appRepository "github.com/devtron-labs/devtron/internal/sql/repository/app" - "github.com/devtron-labs/devtron/internal/sql/repository/helper" "github.com/devtron-labs/devtron/internal/sql/repository/pipelineConfig" repository2 "github.com/devtron-labs/devtron/pkg/appStore/installedApp/repository" "github.com/devtron-labs/devtron/pkg/auth/user/repository" @@ -65,13 +69,15 @@ type AppCrudOperationServiceImpl struct { installedAppRepository repository2.InstalledAppRepository genericNoteService genericNotes.GenericNoteService gitMaterialRepository pipelineConfig.MaterialRepository + installedAppDbService EAMode.InstalledAppDBService } func NewAppCrudOperationServiceImpl(appLabelRepository pipelineConfig.AppLabelRepository, logger *zap.SugaredLogger, appRepository appRepository.AppRepository, userRepository repository.UserRepository, installedAppRepository repository2.InstalledAppRepository, genericNoteService genericNotes.GenericNoteService, - gitMaterialRepository pipelineConfig.MaterialRepository) *AppCrudOperationServiceImpl { + gitMaterialRepository pipelineConfig.MaterialRepository, + installedAppDbService EAMode.InstalledAppDBService) *AppCrudOperationServiceImpl { return &AppCrudOperationServiceImpl{ appLabelRepository: appLabelRepository, logger: logger, @@ -80,6 +86,7 @@ func NewAppCrudOperationServiceImpl(appLabelRepository pipelineConfig.AppLabelRe installedAppRepository: installedAppRepository, genericNoteService: genericNoteService, gitMaterialRepository: gitMaterialRepository, + installedAppDbService: installedAppDbService, } } @@ -343,7 +350,7 @@ func (impl AppCrudOperationServiceImpl) GetAppMetaInfo(appId int, installedAppId } } appName := app.AppName - if app.AppType == helper.Job { + if app.IsAppJobOrExternalType() { appName = app.DisplayName } noteResp, err := impl.genericNoteService.GetGenericNotesForAppIds([]int{app.Id}) @@ -425,6 +432,60 @@ func convertUrlToHttpsIfSshType(url string) string { return httpsURL } +// getAppAndProjectForAppIdentifier, returns app db model for an app unique identifier or from display_name if both exists else it throws pg.ErrNoRows +func (impl AppCrudOperationServiceImpl) getAppAndProjectForAppIdentifier(appIdentifier *client.AppIdentifier) (*appRepository.App, error) { + app := &appRepository.App{} + var err error + appNameUniqueIdentifier := appIdentifier.GetUniqueAppNameIdentifier() + app, err = impl.appRepository.FindAppAndProjectByAppName(appNameUniqueIdentifier) + if err != nil && err != pg.ErrNoRows { + impl.logger.Errorw("error in fetching app meta data by unique app identifier", "appNameUniqueIdentifier", appNameUniqueIdentifier, "err", err) + return app, 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 != nil { + impl.logger.Errorw("error in fetching app meta data by display name", "displayName", appIdentifier.ReleaseName, "err", err) + return app, err + } + } + return app, nil +} + +// updateAppNameToUniqueAppIdentifierInApp, migrates values of app_name col. in app table to unique identifier and also updates display_name with releaseName +// returns is requested external app is migrated or other app (linked to chart store) with same name is migrated(which is tracked via namespace). +func (impl AppCrudOperationServiceImpl) updateAppNameToUniqueAppIdentifierInApp(app *appRepository.App, appIdentifier *client.AppIdentifier) error { + appNameUniqueIdentifier := appIdentifier.GetUniqueAppNameIdentifier() + 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 then installed_app found for this app then this app name is already linked to an installed app then + //create new appEntry for all those installedApps and link installedApp.AppId to the newly created app. + if isLinked { + // 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 + } + } + // migrating the requested ext-app + app.AppName = appNameUniqueIdentifier + app.DisplayName = appIdentifier.ReleaseName + app.UpdatedBy = bean2.SystemUserId + app.UpdatedOn = time.Now() + err = impl.appRepository.Update(app) + if err != nil { + impl.logger.Errorw("error in migrating displayName and appName to unique identifier", "appNameUniqueIdentifier", appNameUniqueIdentifier, "err", err) + return err + } + return nil +} + func (impl AppCrudOperationServiceImpl) GetHelmAppMetaInfo(appId string) (*bean.AppMetaInfoDto, error) { // adding separate function for helm apps because for CLI helm apps, apps can be of form "1|clusterName|releaseName" @@ -432,17 +493,34 @@ func (impl AppCrudOperationServiceImpl) GetHelmAppMetaInfo(appId string) (*bean. appIdSplitted := strings.Split(appId, "|") app := &appRepository.App{} var err error + var displayName string impl.logger.Info("request payload, appId", appId) if len(appIdSplitted) > 1 { - appName := appIdSplitted[2] - app, err = impl.appRepository.FindAppAndProjectByAppName(appName) - if err != nil && err != pg.ErrNoRows { - impl.logger.Errorw("error in fetching app meta data", "err", err) + appIdDecoded, err := client.DecodeExternalAppAppId(appId) + if err != nil { + impl.logger.Errorw("error in decoding app id for external app", "appId", appId, "err", err) return nil, err } + app, err = impl.getAppAndProjectForAppIdentifier(appIdDecoded) + if err != nil && !util.IsErrNoRows(err) { + impl.logger.Errorw("GetHelmAppMetaInfo, error in getAppAndProjectForAppIdentifier for external apps", "appIdentifier", appIdDecoded, "err", err) + return nil, err + } + // 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) + 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 + } + } if app.Id == 0 { - app.AppName = appName + app.AppName = appIdDecoded.ReleaseName + } + if util2.IsExternalChartStoreApp(app.DisplayName) { + displayName = app.DisplayName } + } else { installedAppIdInt, err := strconv.Atoi(appId) if err != nil { @@ -460,10 +538,9 @@ func (impl AppCrudOperationServiceImpl) GetHelmAppMetaInfo(appId string) (*bean. app.Team.Name = InstalledApp.App.Team.Name app.CreatedBy = InstalledApp.App.CreatedBy app.Active = InstalledApp.App.Active - - if err != nil { - impl.logger.Errorw("error in fetching App Meta Info", "error", err) - return nil, err + if util2.IsExternalChartStoreApp(InstalledApp.App.DisplayName) { + // in case of external apps, we will send display name as appName will be a unique identifier + displayName = InstalledApp.App.DisplayName } } @@ -489,6 +566,10 @@ func (impl AppCrudOperationServiceImpl) GetHelmAppMetaInfo(appId string) (*bean. CreatedOn: app.CreatedOn, Active: app.Active, } + if util2.IsExternalChartStoreApp(displayName) { + //special handling for ext-helm apps where name visible on UI is display name + info.AppName = displayName + } return info, nil } @@ -611,12 +692,16 @@ func (impl AppCrudOperationServiceImpl) GetAppListByTeamIds(teamIds []int, appTy return nil, err } for _, app := range apps { + appName := app.AppName + if util2.IsExternalChartStoreApp(app.DisplayName) { + appName = app.DisplayName + } if _, ok := teamMap[app.TeamId]; ok { - teamMap[app.TeamId].AppList = append(teamMap[app.TeamId].AppList, &AppBean{Id: app.Id, Name: app.AppName}) + teamMap[app.TeamId].AppList = append(teamMap[app.TeamId].AppList, &AppBean{Id: app.Id, Name: appName}) } else { teamMap[app.TeamId] = &TeamAppBean{ProjectId: app.Team.Id, ProjectName: app.Team.Name} - teamMap[app.TeamId].AppList = append(teamMap[app.TeamId].AppList, &AppBean{Id: app.Id, Name: app.AppName}) + teamMap[app.TeamId].AppList = append(teamMap[app.TeamId].AppList, &AppBean{Id: app.Id, Name: appName}) } } diff --git a/pkg/appStore/adapter/Adapter.go b/pkg/appStore/adapter/Adapter.go index f48dad09e7..464d1124b3 100644 --- a/pkg/appStore/adapter/Adapter.go +++ b/pkg/appStore/adapter/Adapter.go @@ -8,6 +8,7 @@ import ( appStoreBean "github.com/devtron-labs/devtron/pkg/appStore/bean" appStoreDiscoverRepository "github.com/devtron-labs/devtron/pkg/appStore/discover/repository" "github.com/devtron-labs/devtron/pkg/appStore/installedApp/repository" + util4 "github.com/devtron-labs/devtron/pkg/appStore/util" "github.com/devtron-labs/devtron/pkg/bean" "github.com/devtron-labs/devtron/pkg/cluster/adapter" clutserBean "github.com/devtron-labs/devtron/pkg/cluster/repository/bean" @@ -140,9 +141,10 @@ func GenerateInstallAppVersionDTO(installedApp *repository.InstalledApps, instal } // GenerateInstallAppVersionMinDTO converts repository.InstalledApps db object to appStoreBean.InstallAppVersionDTO bean; -// Note: It only generates a minimal DTO and doesn't include repository.InstalledAppVersions data +// Note: It only generates a minimal DTO and doesn't include repository.InstalledAppVersions data, also it's safe not to +// use this bean for creating db model again func GenerateInstallAppVersionMinDTO(installedApp *repository.InstalledApps) *appStoreBean.InstallAppVersionDTO { - return &appStoreBean.InstallAppVersionDTO{ + installAppVersionDto := &appStoreBean.InstallAppVersionDTO{ EnvironmentId: installedApp.EnvironmentId, InstalledAppId: installedApp.Id, AppId: installedApp.AppId, @@ -156,6 +158,10 @@ func GenerateInstallAppVersionMinDTO(installedApp *repository.InstalledApps) *ap DeploymentAppType: installedApp.DeploymentAppType, IsVirtualEnvironment: installedApp.Environment.IsVirtualEnvironment, } + if util4.IsExternalChartStoreApp(installedApp.App.DisplayName) { + installAppVersionDto.AppName = installedApp.App.DisplayName + } + return installAppVersionDto } func GetGeneratedHelmPackageName(appName, envName string, updatedOn time.Time) string { @@ -216,6 +222,10 @@ func UpdateAppDetails(request *appStoreBean.InstallAppVersionDTO, app *app.App) request.AppName = app.AppName request.TeamId = app.TeamId request.AppOfferingMode = app.AppOfferingMode + // for external apps, AppName is unique identifier(appName-ns-clusterId), hence DisplayName should be used in that case + if util4.IsExternalChartStoreApp(app.DisplayName) { + request.AppName = app.DisplayName + } } // UpdateInstallAppDetails update repository.InstalledApps data into the same InstallAppVersionDTO diff --git a/pkg/appStore/bean/bean.go b/pkg/appStore/bean/bean.go index ce81af93b2..c3163323f2 100644 --- a/pkg/appStore/bean/bean.go +++ b/pkg/appStore/bean/bean.go @@ -76,7 +76,7 @@ const ( type InstallAppVersionDTO struct { Id int `json:"id,omitempty"` // TODO: redundant data; refers to InstalledAppVersionId AppId int `json:"appId,omitempty"` - AppName string `json:"appName,omitempty"` + AppName string `json:"appName,omitempty"` // AppName can be display_name in case of external-apps (which is not unique in that case) TeamId int `json:"teamId,omitempty"` TeamName string `json:"teamName,omitempty"` EnvironmentId int `json:"environmentId,omitempty"` @@ -116,6 +116,7 @@ type InstallAppVersionDTO struct { EnvironmentName string `json:"-"` InstallAppVersionChartDTO *InstallAppVersionChartDTO `json:"-"` AppStoreApplicationVersionId int + DisplayName string `json:"-"` // used only for external apps } // UpdateDeploymentAppType updates deploymentAppType to InstallAppVersionDTO diff --git a/pkg/appStore/installedApp/repository/InstalledAppModels.go b/pkg/appStore/installedApp/repository/InstalledAppModels.go index 320f0fbe48..18669d4423 100644 --- a/pkg/appStore/installedApp/repository/InstalledAppModels.go +++ b/pkg/appStore/installedApp/repository/InstalledAppModels.go @@ -20,6 +20,7 @@ type InstalledAppsWithChartDetails struct { ChartRepoName string `json:"chart_repo_name"` DockerArtifactStoreId string `json:"docker_artifact_store_id"` AppName string `json:"app_name"` + DisplayName string `json:"display_name"` EnvironmentName string `json:"environment_name"` InstalledAppVersionId int `json:"installed_app_version_id"` AppStoreApplicationVersionId int `json:"app_store_application_version_id"` @@ -46,6 +47,7 @@ type InstalledAppAndEnvDetails struct { EnvironmentName string `json:"environment_name"` EnvironmentId int `json:"environment_id"` AppName string `json:"app_name"` + DisplayName string `json:"display_name"` AppOfferingMode string `json:"appOfferingMode"` UpdatedOn time.Time `json:"updated_on"` EmailId string `json:"email_id"` diff --git a/pkg/appStore/installedApp/repository/InstalledAppRepository.go b/pkg/appStore/installedApp/repository/InstalledAppRepository.go index 7e7b7c2958..f201eb0951 100644 --- a/pkg/appStore/installedApp/repository/InstalledAppRepository.go +++ b/pkg/appStore/installedApp/repository/InstalledAppRepository.go @@ -18,12 +18,14 @@ package repository import ( + "fmt" "github.com/devtron-labs/common-lib/utils/k8s/health" "github.com/devtron-labs/devtron/internal/sql/repository/app" "github.com/devtron-labs/devtron/internal/sql/repository/pipelineConfig" util2 "github.com/devtron-labs/devtron/internal/util" appStoreBean "github.com/devtron-labs/devtron/pkg/appStore/bean" appStoreDiscoverRepository "github.com/devtron-labs/devtron/pkg/appStore/discover/repository" + util3 "github.com/devtron-labs/devtron/pkg/appStore/util" "github.com/devtron-labs/devtron/pkg/cluster/repository" "github.com/devtron-labs/devtron/pkg/sql" "github.com/devtron-labs/devtron/util" @@ -75,6 +77,18 @@ func (model *InstalledApps) UpdateGitOpsRepository(gitOpsRepoUrl string, isCusto model.GitOpsRepoName = gitUtil.GetGitRepoNameFromGitRepoUrl(gitOpsRepoUrl) // Handled for backward compatibility } +func (model *InstalledApps) ChangeAppNameToDisplayName() { + model.App.AppName = model.App.DisplayName +} + +func (model *InstalledApps) GetUniqueAppNameIdentifier() string { + if util3.IsExternalChartStoreApp(model.App.DisplayName) { + //if display name is set then that installedApp's app is already migrated + return model.App.AppName + } + return fmt.Sprintf("%s-%s-%s", model.App.AppName, model.Environment.Namespace, strconv.Itoa(model.Environment.ClusterId)) +} + type InstalledAppVersions struct { TableName struct{} `sql:"installed_app_versions" pg:",discard_unknown_columns"` Id int `sql:"id,pk"` @@ -143,6 +157,8 @@ type InstalledAppRepository interface { GetActiveInstalledAppByEnvIdAndDeploymentType(envId int, deploymentType string, excludeAppIds []string, includeAppIds []string) ([]*InstalledApps, error) UpdateDeploymentAppTypeInInstalledApp(deploymentAppType string, installedAppIdIncludes []int, userId int32, deployStatus int) error FindInstalledAppByIds(ids []int) ([]*InstalledApps, error) + // FindInstalledAppsByAppId returns multiple installed apps for an appId, this only happens for external-apps with same name installed in diff namespaces + FindInstalledAppsByAppId(appId int) ([]*InstalledApps, error) } type InstalledAppRepositoryImpl struct { @@ -371,7 +387,7 @@ func (impl InstalledAppRepositoryImpl) GetAllInstalledApps(filter *appStoreBean. var installedAppsWithChartDetails []InstalledAppsWithChartDetails var query string query = "select iav.updated_on, iav.id as installed_app_version_id, ch.name as chart_repo_name, das.id as docker_artifact_store_id," - query = query + " env.environment_name, env.id as environment_id, env.is_virtual_environment, a.app_name, a.app_offering_mode, asav.icon, asav.name as app_store_application_name," + query = query + " env.environment_name, env.id as environment_id, env.is_virtual_environment, a.app_name, a.display_name, a.app_offering_mode, asav.icon, asav.name as app_store_application_name," query = query + " env.namespace, cluster.cluster_name, a.team_id, cluster.id as cluster_id, " query = query + " asav.id as app_store_application_version_id, ia.id , asav.deprecated , app_status.status as app_status, ia.deployment_app_delete_request" query = query + " from installed_app_versions iav" @@ -422,7 +438,7 @@ func (impl InstalledAppRepositoryImpl) GetAllInstalledApps(filter *appStoreBean. func (impl InstalledAppRepositoryImpl) GetAllInstalledAppsByAppStoreId(appStoreId int) ([]InstalledAppAndEnvDetails, error) { var installedAppAndEnvDetails []InstalledAppAndEnvDetails - var queryTemp = "select env.environment_name, env.id as environment_id, a.app_name, a.app_offering_mode, ia.updated_on, u.email_id," + + var queryTemp = "select env.environment_name, env.id as environment_id, a.app_name, a.display_name, a.app_offering_mode, ia.updated_on, u.email_id," + " asav.id as app_store_application_version_id, iav.id as installed_app_version_id, ia.id as installed_app_id, ia.app_id, ia.deployment_app_type, app_status.status as app_status" + " from installed_app_versions iav inner join installed_apps ia on iav.installed_app_id = ia.id" + " inner join app a on a.id = ia.app_id " + @@ -674,7 +690,7 @@ func (impl InstalledAppRepositoryImpl) GetAppAndEnvDetailsForDeploymentAppTypeIn var installedApps []*InstalledApps err := impl.dbConnection. Model(&installedApps). - Column("installed_apps.id", "App.app_name", "Environment.cluster_id", "Environment.namespace"). + Column("installed_apps.id", "App.app_name", "App.display_name", "Environment.cluster_id", "Environment.namespace"). Where("environment.cluster_id in (?)", pg.In(clusterIds)). Where("installed_apps.deployment_app_type = ?", deploymentAppType). Where("app.active = ?", true). @@ -891,3 +907,18 @@ func (impl InstalledAppRepositoryImpl) FindInstalledAppByIds(ids []int) ([]*Inst } return installedApps, err } + +func (impl InstalledAppRepositoryImpl) FindInstalledAppsByAppId(appId int) ([]*InstalledApps, error) { + var installedApps []*InstalledApps + err := impl.dbConnection.Model(&installedApps). + Column("installed_apps.*", "App", "Environment"). + Join("inner join app a on installed_apps.app_id = a.id"). + Join("inner join environment e on installed_apps.environment_id = e.id"). + Where("installed_apps.app_id = ?", appId). + Where("installed_apps.active = true"). + Select() + if err != nil { + impl.Logger.Errorw("error on fetching installed apps by appId", "appId", appId) + } + return installedApps, err +} diff --git a/pkg/appStore/installedApp/service/AppStoreDeploymentDBService.go b/pkg/appStore/installedApp/service/AppStoreDeploymentDBService.go index 4218667f99..9ed53bb5dc 100644 --- a/pkg/appStore/installedApp/service/AppStoreDeploymentDBService.go +++ b/pkg/appStore/installedApp/service/AppStoreDeploymentDBService.go @@ -13,7 +13,9 @@ import ( appStoreBean "github.com/devtron-labs/devtron/pkg/appStore/bean" discoverRepository "github.com/devtron-labs/devtron/pkg/appStore/discover/repository" "github.com/devtron-labs/devtron/pkg/appStore/installedApp/repository" + "github.com/devtron-labs/devtron/pkg/appStore/installedApp/service/EAMode" "github.com/devtron-labs/devtron/pkg/appStore/installedApp/service/FullMode/deployment" + util4 "github.com/devtron-labs/devtron/pkg/appStore/util" "github.com/devtron-labs/devtron/pkg/bean" clusterService "github.com/devtron-labs/devtron/pkg/cluster" clutserBean "github.com/devtron-labs/devtron/pkg/cluster/repository/bean" @@ -42,7 +44,7 @@ type AppStoreDeploymentDBService interface { // UpdateInstalledAppVersionHistoryWithGitHash updates GitHash in the repository.InstalledAppVersionHistory UpdateInstalledAppVersionHistoryWithGitHash(versionHistoryId int, gitHash string, userId int32) error // UpdateProjectForHelmApp updates TeamId in the app.App - UpdateProjectForHelmApp(appName string, teamId int, userId int32) error + UpdateProjectForHelmApp(appName, displayName string, teamId int, userId int32) error // InstallAppPostDbOperation is used to perform Post-Install DB operations in App Store deployments InstallAppPostDbOperation(installAppVersionRequest *appStoreBean.InstallAppVersionDTO) error // MarkInstalledAppVersionsInactiveByInstalledAppId will mark the repository.InstalledAppVersions inactive for the given InstalledAppId @@ -53,6 +55,8 @@ type AppStoreDeploymentDBService interface { MarkHelmInstalledAppDeploymentSucceeded(versionHistoryId int) error // UpdateInstalledAppVersionHistoryStatus will update the Status in the repository.InstalledAppVersionHistory UpdateInstalledAppVersionHistoryStatus(versionHistoryId int, status string) error + // GetActiveAppForAppIdentifierOrReleaseName returns app db model for an app unique identifier or from display_name if either exists else it throws pg.ErrNoRows + GetActiveAppForAppIdentifierOrReleaseName(appNameUniqueIdentifier, releaseName string) (*app.App, error) } type AppStoreDeploymentDBServiceImpl struct { @@ -68,6 +72,7 @@ type AppStoreDeploymentDBServiceImpl struct { deploymentTypeOverrideService providerConfig.DeploymentTypeOverrideService fullModeDeploymentService deployment.FullModeDeploymentService appStoreValidator AppStoreValidator + installedAppDbService EAMode.InstalledAppDBService } func NewAppStoreDeploymentDBServiceImpl(logger *zap.SugaredLogger, @@ -80,7 +85,8 @@ func NewAppStoreDeploymentDBServiceImpl(logger *zap.SugaredLogger, envVariables *globalUtil.EnvironmentVariables, gitOpsConfigReadService config.GitOpsConfigReadService, deploymentTypeOverrideService providerConfig.DeploymentTypeOverrideService, - fullModeDeploymentService deployment.FullModeDeploymentService, appStoreValidator AppStoreValidator) *AppStoreDeploymentDBServiceImpl { + fullModeDeploymentService deployment.FullModeDeploymentService, appStoreValidator AppStoreValidator, + installedAppDbService EAMode.InstalledAppDBService) *AppStoreDeploymentDBServiceImpl { return &AppStoreDeploymentDBServiceImpl{ logger: logger, installedAppRepository: installedAppRepository, @@ -94,6 +100,7 @@ func NewAppStoreDeploymentDBServiceImpl(logger *zap.SugaredLogger, deploymentTypeOverrideService: deploymentTypeOverrideService, fullModeDeploymentService: fullModeDeploymentService, appStoreValidator: appStoreValidator, + installedAppDbService: installedAppDbService, } } @@ -115,6 +122,11 @@ func (impl *AppStoreDeploymentDBServiceImpl) AppStoreDeployOperationDB(installRe TeamId: installRequest.TeamId, UserId: installRequest.UserId, } + if util4.IsExternalChartStoreApp(installRequest.DisplayName) { + //this is the case of linking external helm app to devtron chart store + appCreateRequest.AppType = helper.ExternalChartStoreApp + appCreateRequest.DisplayName = installRequest.DisplayName + } appCreateRequest, err = impl.createAppForAppStore(appCreateRequest, tx, getAppInstallationMode(installRequest.AppOfferingMode)) if err != nil { impl.logger.Errorw("error while creating app", "error", err) @@ -299,7 +311,9 @@ func (impl *AppStoreDeploymentDBServiceImpl) GetAllInstalledAppsByAppStoreId(app installedAppRes.ClusterId = environment.ClusterId installedAppRes.Namespace = environment.Namespace } - + if util4.IsExternalChartStoreApp(a.DisplayName) { + installedAppRes.AppName = a.DisplayName + } installedAppsEnvResponse = append(installedAppsEnvResponse, installedAppRes) } return installedAppsEnvResponse, nil @@ -317,13 +331,50 @@ func (impl *AppStoreDeploymentDBServiceImpl) UpdateInstalledAppVersionHistoryWit return nil } -func (impl *AppStoreDeploymentDBServiceImpl) UpdateProjectForHelmApp(appName string, teamId int, userId int32) error { - appModel, err := impl.appRepository.FindActiveByName(appName) +func (impl *AppStoreDeploymentDBServiceImpl) GetActiveAppForAppIdentifierOrReleaseName(appNameUniqueIdentifier, releaseName string) (*app.App, error) { + app, err := impl.appRepository.FindActiveByName(appNameUniqueIdentifier) + if err != nil && err != pg.ErrNoRows { + impl.logger.Errorw("error in fetching app meta data by unique app identifier", "appNameUniqueIdentifier", appNameUniqueIdentifier, "err", err) + return nil, err + } else if util.IsErrNoRows(err) { + //find app by displayName/releaseName if not found by unique identifier + app, err = impl.appRepository.FindActiveByName(releaseName) + if err != nil { + impl.logger.Errorw("error in fetching app meta data by display name", "displayName", releaseName, "err", err) + return nil, err + } + } + return app, nil +} + +func (impl *AppStoreDeploymentDBServiceImpl) UpdateProjectForHelmApp(appName, displayName string, teamId int, userId int32) error { + appModel, err := impl.GetActiveAppForAppIdentifierOrReleaseName(appName, displayName) if err != nil && !util.IsErrNoRows(err) { - impl.logger.Errorw("error in fetching appModel", "err", err) + impl.logger.Errorw("error in fetching appModel by appName", "appName", appName, "err", err) return err } + // only external app will have a display name, so checking the following case only for external apps + if appModel != nil && appModel.Id > 0 && len(displayName) > 0 { + /* + 1. now we will check if for that appModel, installed_app entries are present or not i.e. linked to devtron or not, + 2. if not, then let the normal flow continue as we can change the app_name with app unique identifier. + 3. if exists then we will create new app entries with uniqueAppNameIdentifier for all installed apps. + */ + isLinkedToDevtron, installedApps, err := impl.installedAppDbService.IsExternalAppLinkedToChartStore(appModel.Id) + if err != nil { + impl.logger.Errorw("UpdateProjectForHelmApp, error in checking IsExternalAppLinkedToChartStore", "appId", appModel.Id, "err", err) + return err + } + if isLinkedToDevtron { + err := impl.installedAppDbService.CreateNewAppEntryForAllInstalledApps(installedApps) + if err != nil { + impl.logger.Errorw("UpdateProjectForHelmApp, error in CreateNewAppEntryForAllInstalledApps", "appName", displayName, "err", err) + //not returning from here, project update req is yet to be processed for requested ext-app + } + } + } + var appInstallationMode string dbConnection := impl.appRepository.GetConnection() tx, err := dbConnection.Begin() @@ -344,14 +395,25 @@ func (impl *AppStoreDeploymentDBServiceImpl) UpdateProjectForHelmApp(appName str UserId: userId, TeamId: teamId, } + if util4.IsExternalChartStoreApp(displayName) { + createAppRequest.AppType = helper.ExternalChartStoreApp + createAppRequest.DisplayName = displayName + } _, err = impl.createAppForAppStore(&createAppRequest, tx, appInstallationMode) if err != nil { impl.logger.Errorw("error while creating appModel", "error", err) return err } } else { + if util4.IsExternalChartStoreApp(displayName) { + //handling the case when ext-helm app is already assigned to a project and an entry already exist in app table but + //not yet migrated, then this will override app_name with unique identifier app name and update display_name also + appModel.AppName = appName + appModel.DisplayName = displayName + } // update team id if appModel exist appModel.TeamId = teamId + appModel.AppOfferingMode = globalUtil.SERVER_MODE_FULL appModel.UpdateAuditLog(userId) err = impl.appRepository.UpdateWithTxn(appModel, tx) if err != nil { @@ -497,6 +559,12 @@ func (impl *AppStoreDeploymentDBServiceImpl) createAppForAppStore(createRequest AppType: helper.ChartStoreApp, AppOfferingMode: appInstallationMode, } + if createRequest.AppType == helper.ExternalChartStoreApp { + //when linking ext helm app to chart store, there can be a case that two (or more) external apps can have same name, in diff namespaces or diff + //clusters, so now we are storing display_name also to get rid of multiple installed apps pointing to the same app, which caused unwarranted + //behaviours. appName in this case will be displayName-namespace-clusterId + appModel.DisplayName = createRequest.DisplayName + } appModel.CreateAuditLog(createRequest.UserId) err = impl.appRepository.SaveWithTxn(appModel, tx) if err != nil { diff --git a/pkg/appStore/installedApp/service/AppStoreDeploymentService.go b/pkg/appStore/installedApp/service/AppStoreDeploymentService.go index dbc5d0f23d..c83e47a650 100644 --- a/pkg/appStore/installedApp/service/AppStoreDeploymentService.go +++ b/pkg/appStore/installedApp/service/AppStoreDeploymentService.go @@ -372,7 +372,7 @@ func (impl *AppStoreDeploymentServiceImpl) LinkHelmApplicationToChartStore(ctx c // Initialise bean installAppVersionRequestDto := &appStoreBean.InstallAppVersionDTO{ - AppName: appIdentifier.ReleaseName, + AppName: appIdentifier.GetUniqueAppNameIdentifier(), UserId: userId, AppOfferingMode: util2.SERVER_MODE_HYPERION, ClusterId: appIdentifier.ClusterId, @@ -382,6 +382,7 @@ func (impl *AppStoreDeploymentServiceImpl) LinkHelmApplicationToChartStore(ctx c ReferenceValueId: int(request.GetReferenceValueId()), ReferenceValueKind: request.GetReferenceValueKind(), DeploymentAppType: util.PIPELINE_DEPLOYMENT_TYPE_HELM, + DisplayName: appIdentifier.ReleaseName, } // STEP-2 InstallApp with only DB operations @@ -396,21 +397,35 @@ func (impl *AppStoreDeploymentServiceImpl) LinkHelmApplicationToChartStore(ctx c return res, isChartRepoActive, nil } -func (impl *AppStoreDeploymentServiceImpl) UpdateProjectHelmApp(updateAppRequest *appStoreBean.UpdateProjectHelmAppDTO) error { - - appIdSplitted := strings.Split(updateAppRequest.AppId, "|") - - appName := updateAppRequest.AppName +func isExternalHelmApp(appId string) bool { + // for external helm apps, updateAppRequest.AppId is of the form clusterId|namespace|displayAppName + return len(strings.Split(appId, "|")) > 1 +} - if len(appIdSplitted) > 1 { - // app id is zero for CLI apps - appIdentifier, _ := impl.helmAppService.DecodeAppId(updateAppRequest.AppId) - appName = appIdentifier.ReleaseName +func (impl *AppStoreDeploymentServiceImpl) UpdateProjectHelmApp(updateAppRequest *appStoreBean.UpdateProjectHelmAppDTO) error { + var appName string + var displayName string + appName = updateAppRequest.AppName + if isExternalHelmApp(updateAppRequest.AppId) { + appIdentifier, err := impl.helmAppService.DecodeAppId(updateAppRequest.AppId) + if err != nil { + impl.logger.Errorw("error in decoding app id for external helm apps", "err", err) + return err + } + appName = appIdentifier.GetUniqueAppNameIdentifier() + displayName = updateAppRequest.AppName + } else { + //in case the external app is linked, then it's unique identifier is set in app_name col. hence retrieving appName + //for this case, although this will also handle the case for non-external apps + appNameUniqueIdentifier := impl.getAppNameForInstalledApp(updateAppRequest.InstalledAppId) + if len(appNameUniqueIdentifier) > 0 { + appName = appNameUniqueIdentifier + } } impl.logger.Infow("update helm project request", updateAppRequest) - err := impl.appStoreDeploymentDBService.UpdateProjectForHelmApp(appName, updateAppRequest.TeamId, updateAppRequest.UserId) + err := impl.appStoreDeploymentDBService.UpdateProjectForHelmApp(appName, displayName, updateAppRequest.TeamId, updateAppRequest.UserId) if err != nil { - impl.logger.Errorw("error in linking project to helm app", "appName", appName, "err", err) + impl.logger.Errorw("error in linking project to helm app", "appName", updateAppRequest.AppName, "err", err) return err } return nil @@ -870,9 +885,6 @@ func (impl *AppStoreDeploymentServiceImpl) linkHelmApplicationToChartStore(insta // Rollback tx on error. defer tx.Rollback() - // skipAppCreation flag is set for CLI apps because for CLI Helm apps if project is created first before linking to chart store then app is created during project update time. - // skipAppCreation - This flag will skip app creation if app already exists. - //step 1 db operation initiated appModel, err := impl.appRepository.FindActiveByName(installAppVersionRequest.AppName) if err != nil && !util.IsErrNoRows(err) { @@ -906,7 +918,7 @@ func (impl *AppStoreDeploymentServiceImpl) linkHelmApplicationToChartStore(insta ChartVersion: appStoreAppVersion.Version, ReleaseIdentifier: &bean4.ReleaseIdentifier{ ReleaseNamespace: installAppVersionRequest.Namespace, - ReleaseName: installAppVersionRequest.AppName, + ReleaseName: installAppVersionRequest.DisplayName, }, }, SourceAppType: bean3.SOURCE_HELM_APP, @@ -1008,3 +1020,16 @@ func (impl *AppStoreDeploymentServiceImpl) handleGitOpsRepoUrlMigration(tx *pg.T } return err } + +// getAppNameForInstalledApp will fetch and returns AppName from app table +func (impl *AppStoreDeploymentServiceImpl) getAppNameForInstalledApp(installedAppId int) string { + installedApp, err := impl.installedAppRepository.GetInstalledApp(installedAppId) + if err != nil { + impl.logger.Errorw("UpdateProjectHelmApp, error in finding app by installedAppId", "installedAppId", installedAppId, "err", err) + return "" + } + if installedApp != nil { + return installedApp.App.AppName + } + return "" +} diff --git a/pkg/appStore/installedApp/service/EAMode/EAModeDeploymentService.go b/pkg/appStore/installedApp/service/EAMode/EAModeDeploymentService.go index 932f267e78..3e7f15012c 100644 --- a/pkg/appStore/installedApp/service/EAMode/EAModeDeploymentService.go +++ b/pkg/appStore/installedApp/service/EAMode/EAModeDeploymentService.go @@ -8,6 +8,7 @@ import ( client "github.com/devtron-labs/devtron/api/helm-app/service" repository2 "github.com/devtron-labs/devtron/internal/sql/repository/dockerRegistry" "github.com/devtron-labs/devtron/pkg/appStore/installedApp/service/bean" + util2 "github.com/devtron-labs/devtron/pkg/appStore/util" commonBean "github.com/devtron-labs/devtron/pkg/deployment/gitOps/common/bean" validationBean "github.com/devtron-labs/devtron/pkg/deployment/gitOps/validation/bean" "net/http" @@ -252,6 +253,10 @@ func (impl *EAModeDeploymentServiceImpl) updateApplicationWithChartInfo(ctx cont impl.Logger.Errorw("error in getting in installedApp", "installedAppId", installedAppId, "err", err) return err } + appName := installedApp.App.AppName + if util2.IsExternalChartStoreApp(installedApp.App.DisplayName) { + appName = installedApp.App.DisplayName + } appStoreApplicationVersion, err := impl.appStoreApplicationVersionRepository.FindById(appStoreApplicationVersionId) if err != nil { impl.Logger.Errorw("error in getting in appStoreApplicationVersion", "appStoreApplicationVersionId", appStoreApplicationVersionId, "err", err) @@ -300,7 +305,7 @@ func (impl *EAModeDeploymentServiceImpl) updateApplicationWithChartInfo(ctx cont ValuesYaml: valuesOverrideYaml, ReleaseIdentifier: &gRPC.ReleaseIdentifier{ ReleaseNamespace: installedApp.Environment.Namespace, - ReleaseName: installedApp.App.AppName, + ReleaseName: appName, }, ChartName: appStoreApplicationVersion.Name, ChartVersion: appStoreApplicationVersion.Version, diff --git a/pkg/appStore/installedApp/service/EAMode/InstalledAppDBService.go b/pkg/appStore/installedApp/service/EAMode/InstalledAppDBService.go index 967c3a5e27..d1746dbd06 100644 --- a/pkg/appStore/installedApp/service/EAMode/InstalledAppDBService.go +++ b/pkg/appStore/installedApp/service/EAMode/InstalledAppDBService.go @@ -18,6 +18,10 @@ package EAMode import ( + "github.com/devtron-labs/devtron/api/helm-app/service" + "github.com/devtron-labs/devtron/internal/sql/repository/helper" + util4 "github.com/devtron-labs/devtron/pkg/appStore/util" + bean3 "github.com/devtron-labs/devtron/pkg/auth/user/bean" "github.com/devtron-labs/devtron/pkg/cluster" "net/http" "strconv" @@ -49,6 +53,11 @@ type InstalledAppDBService interface { GetInstalledAppVersion(id int, userId int32) (*appStoreBean.InstallAppVersionDTO, error) CreateInstalledAppVersion(installAppVersionRequest *appStoreBean.InstallAppVersionDTO, tx *pg.Tx) (*appStoreRepo.InstalledAppVersions, error) UpdateInstalledAppVersion(installedAppVersion *appStoreRepo.InstalledAppVersions, installAppVersionRequest *appStoreBean.InstallAppVersionDTO, tx *pg.Tx) (*appStoreRepo.InstalledAppVersions, error) + + ChangeAppNameToDisplayNameForInstalledApp(installedApp *appStoreRepo.InstalledApps) + GetReleaseInfo(appIdentifier *service.AppIdentifier) (*appStoreBean.InstallAppVersionDTO, error) + IsExternalAppLinkedToChartStore(appId int) (bool, []*appStoreRepo.InstalledApps, error) + CreateNewAppEntryForAllInstalledApps(installedApps []*appStoreRepo.InstalledApps) error } type InstalledAppDBServiceImpl struct { @@ -122,6 +131,10 @@ func (impl *InstalledAppDBServiceImpl) GetAll(filter *appStoreBean.AppStoreFilte LastDeployedAt: &appLocal.UpdatedOn, AppStatus: &appLocal.AppStatus, } + if util4.IsExternalChartStoreApp(appLocal.DisplayName) { + //case of external app where display name is stored in app table + helmAppResp.AppName = &appLocal.DisplayName + } helmAppsResponse = append(helmAppsResponse, helmAppResp) } installedAppsResponse.HelmApps = &helmAppsResponse @@ -193,11 +206,11 @@ func (impl *InstalledAppDBServiceImpl) FindAppDetailsForAppstoreApplication(inst IsVirtualEnvironment: installedAppVerison.InstalledApp.Environment.IsVirtualEnvironment, HelmReleaseInstallStatus: helmReleaseInstallStatus, Status: status, - HelmPackageName: adapter.GetGeneratedHelmPackageName( - installedAppVerison.InstalledApp.App.AppName, - installedAppVerison.InstalledApp.Environment.Name, - installedAppVerison.InstalledApp.UpdatedOn), } + if util4.IsExternalChartStoreApp(installedAppVerison.InstalledApp.App.DisplayName) { + deploymentContainer.AppName = installedAppVerison.InstalledApp.App.DisplayName + } + deploymentContainer.HelmPackageName = adapter.GetGeneratedHelmPackageName(deploymentContainer.AppName, deploymentContainer.EnvironmentName, installedAppVerison.InstalledApp.UpdatedOn) userInfo, err := impl.UserService.GetByIdIncludeDeleted(installedAppVerison.AuditLog.UpdatedBy) if err != nil { impl.Logger.Errorw("error fetching user info", "err", err) @@ -308,3 +321,82 @@ func (impl *InstalledAppDBServiceImpl) UpdateInstalledAppVersion(installedAppVer } return installedAppVersion, nil } + +func (impl *InstalledAppDBServiceImpl) ChangeAppNameToDisplayNameForInstalledApp(installedApp *appStoreRepo.InstalledApps) { + installedApp.ChangeAppNameToDisplayName() +} + +func (impl *InstalledAppDBServiceImpl) GetReleaseInfo(appIdentifier *service.AppIdentifier) (*appStoreBean.InstallAppVersionDTO, error) { + //for external-apps appName would be uniqueIdentifier + appName := appIdentifier.GetUniqueAppNameIdentifier() + installedAppVersionDto, err := impl.GetInstalledAppByClusterNamespaceAndName(appIdentifier.ClusterId, appIdentifier.Namespace, appName) + if err != nil && !util.IsErrNoRows(err) { + impl.Logger.Errorw("GetReleaseInfo, error in getting installed app by clusterId, namespace and appUniqueIdentifierName", "appIdentifier", appIdentifier, "appUniqueIdentifierName", appName, "error", err) + return nil, err + } else if util.IsErrNoRows(err) { + // when app_name is not yet migrated to unique identifier + installedAppVersionDto, err = impl.GetInstalledAppByClusterNamespaceAndName(appIdentifier.ClusterId, appIdentifier.Namespace, appIdentifier.ReleaseName) + if err != nil { + impl.Logger.Errorw("GetReleaseInfo, error in getting installed app by clusterId, namespace and releaseName", "appIdentifier", appIdentifier, "error", err) + return nil, err + } + //if dto found, check if release-info request is for the same namespace app as stored in installed_app because two ext-apps can have same + //release name but in different namespaces, if they differ then release info request is for another ext-app with same name but in diff namespace + if installedAppVersionDto != nil && installedAppVersionDto.Id > 0 && installedAppVersionDto.Namespace != appIdentifier.Namespace { + installedAppVersionDto = nil + } + } + return installedAppVersionDto, nil +} + +// IsExternalAppLinkedToChartStore checks for an appId, if that app is linked to any chart-store app or not, +// if it's linked then it returns true along with all the installedApps linked to that appId +func (impl *InstalledAppDBServiceImpl) IsExternalAppLinkedToChartStore(appId int) (bool, []*appStoreRepo.InstalledApps, error) { + installedApps, err := impl.InstalledAppRepository.FindInstalledAppsByAppId(appId) + if err != nil && err != pg.ErrNoRows { + impl.Logger.Errorw("IsExternalAppLinkedToChartStore, error in fetching installed apps by app id for external apps", "appId", appId, "err", err) + return false, nil, err + } + if installedApps != nil && len(installedApps) > 0 { + return true, installedApps, nil + } + return false, nil, nil +} + +func (impl *InstalledAppDBServiceImpl) CreateNewAppEntryForAllInstalledApps(installedApps []*appStoreRepo.InstalledApps) error { + // db operations + dbConnection := impl.InstalledAppRepository.GetConnection() + tx, err := dbConnection.Begin() + if err != nil { + return err + } + // Rollback tx on error. + defer tx.Rollback() + for _, installedApp := range installedApps { + appModel := &app.App{ + Active: true, + AppName: installedApp.GetUniqueAppNameIdentifier(), + TeamId: installedApp.App.TeamId, + AppType: helper.ChartStoreApp, + AppOfferingMode: installedApp.App.AppOfferingMode, + DisplayName: installedApp.App.AppName, + } + appModel.CreateAuditLog(bean3.SystemUserId) + err := impl.AppRepository.SaveWithTxn(appModel, tx) + if err != nil { + impl.Logger.Errorw("error saving appModel", "err", err) + return err + } + //updating the installedApp.AppId with new app entry + installedApp.AppId = appModel.Id + installedApp.UpdateAuditLog(bean3.SystemUserId) + _, err = impl.InstalledAppRepository.UpdateInstalledApp(installedApp, tx) + if err != nil { + impl.Logger.Errorw("error saving updating installed app with new appId", "installedAppId", installedApp.Id, "err", err) + return err + } + } + + tx.Commit() + return nil +} diff --git a/pkg/appStore/installedApp/service/FullMode/deploymentTypeChange/InstalledAppDeploymentTypeChangeService.go b/pkg/appStore/installedApp/service/FullMode/deploymentTypeChange/InstalledAppDeploymentTypeChangeService.go index 7ca63390ac..4e5937faa5 100644 --- a/pkg/appStore/installedApp/service/FullMode/deploymentTypeChange/InstalledAppDeploymentTypeChangeService.go +++ b/pkg/appStore/installedApp/service/FullMode/deploymentTypeChange/InstalledAppDeploymentTypeChangeService.go @@ -141,8 +141,13 @@ func (impl *InstalledAppDeploymentTypeChangeServiceImpl) MigrateDeploymentType(c return response, err } var installedAppIds []int - for _, item := range installedApps { - installedAppIds = append(installedAppIds, item.Id) + for _, installedApp := range installedApps { + if util2.IsExternalChartStoreApp(installedApp.App.DisplayName) { + //for ext-apps, appName is a unique identifier pertaining to devtron environment hence changing appName to ReleaseName, as going + //further interactions with helm/argo-cd will happen via release name only so refrain from doing any db updates using this installed apps + installedApp.App.AppName = installedApp.App.DisplayName + } + installedAppIds = append(installedAppIds, installedApp.Id) } if len(installedAppIds) == 0 { @@ -400,8 +405,13 @@ func (impl *InstalledAppDeploymentTypeChangeServiceImpl) TriggerAfterMigration(c } var installedAppIds []int - for _, item := range installedApps { - installedAppIds = append(installedAppIds, item.Id) + for _, installedApp := range installedApps { + if util2.IsExternalChartStoreApp(installedApp.App.DisplayName) { + //for ext-apps, appName is a unique identifier pertaining to devtron environment hence changing appName to ReleaseName, as going + //further interactions with helm/argo-cd will happen via release name only so refrain from doing any db updates using this installed apps + installedApp.App.AppName = installedApp.App.DisplayName + } + installedAppIds = append(installedAppIds, installedApp.Id) } if len(installedAppIds) == 0 { diff --git a/pkg/appStore/installedApp/service/FullMode/resource/NotesService.go b/pkg/appStore/installedApp/service/FullMode/resource/NotesService.go index df03d62471..743c53d10a 100644 --- a/pkg/appStore/installedApp/service/FullMode/resource/NotesService.go +++ b/pkg/appStore/installedApp/service/FullMode/resource/NotesService.go @@ -44,7 +44,7 @@ func (impl *InstalledAppResourceServiceImpl) FetchChartNotes(installedAppId int, } //if notes is not present in db then below call will happen if installedApp.Notes == "" { - notes, _, err := impl.findNotesForArgoApplication(installedAppId, envId) + notes, err := impl.findNotesForArgoApplication(installedAppId, envId) if err != nil { impl.logger.Errorw("error fetching notes", "err", err) return "", err @@ -58,25 +58,24 @@ func (impl *InstalledAppResourceServiceImpl) FetchChartNotes(installedAppId int, return installedApp.Notes, nil } -func (impl *InstalledAppResourceServiceImpl) findNotesForArgoApplication(installedAppId, envId int) (string, string, error) { +func (impl *InstalledAppResourceServiceImpl) findNotesForArgoApplication(installedAppId, envId int) (string, error) { installedAppVerison, err := impl.installedAppRepository.GetInstalledAppVersionByInstalledAppIdAndEnvId(installedAppId, envId) if err != nil { impl.logger.Errorw("error fetching installed app version in installed app service", "err", err) - return "", "", err + return "", err } var notes string - appName := installedAppVerison.InstalledApp.App.AppName if util.IsAcdApp(installedAppVerison.InstalledApp.DeploymentAppType) { appStoreAppVersion, err := impl.appStoreApplicationVersionRepository.FindById(installedAppVerison.AppStoreApplicationVersion.Id) if err != nil { impl.logger.Errorw("error fetching app store app version in installed app service", "err", err) - return notes, appName, err + return notes, err } k8sServerVersion, err := impl.K8sUtil.GetKubeVersion() if err != nil { impl.logger.Errorw("exception caught in getting k8sServerVersion", "err", err) - return notes, appName, err + return notes, err } installReleaseRequest := &gRPC.InstallReleaseRequest{ @@ -100,23 +99,23 @@ func (impl *InstalledAppResourceServiceImpl) findNotesForArgoApplication(install config, err := impl.helmAppService.GetClusterConf(clusterId) if err != nil { impl.logger.Errorw("error in fetching cluster detail", "clusterId", clusterId, "err", err) - return "", appName, err + return "", err } installReleaseRequest.ReleaseIdentifier.ClusterConfig = config notes, err = impl.helmAppService.GetNotes(context.Background(), installReleaseRequest) if err != nil { impl.logger.Errorw("error in fetching notes", "err", err) - return notes, appName, err + return notes, err } _, err = impl.updateNotesForInstalledApp(installedAppId, notes) if err != nil { impl.logger.Errorw("error in updating notes in db ", "err", err) - return notes, appName, err + return notes, err } } - return notes, appName, nil + return notes, nil } // updateNotesForInstalledApp will update the notes in repository.InstalledApps table diff --git a/pkg/appStore/util/util.go b/pkg/appStore/util/util.go index c0599c9ed6..6cd28b75ed 100644 --- a/pkg/appStore/util/util.go +++ b/pkg/appStore/util/util.go @@ -47,3 +47,7 @@ func CheckAppReleaseNotExist(err error) bool { func CheckPermissionErrorForArgoCd(err error) bool { return strings.Contains(err.Error(), PermissionDenied) } + +func IsExternalChartStoreApp(displayName string) bool { + return len(displayName) > 0 +} diff --git a/pkg/appStore/values/service/AppStoreValuesService.go b/pkg/appStore/values/service/AppStoreValuesService.go index 94d5e51195..b38ba6bd74 100644 --- a/pkg/appStore/values/service/AppStoreValuesService.go +++ b/pkg/appStore/values/service/AppStoreValuesService.go @@ -19,6 +19,7 @@ package service import ( "fmt" + util2 "github.com/devtron-labs/devtron/pkg/appStore/util" "time" "github.com/devtron-labs/devtron/internal/util" @@ -240,6 +241,9 @@ func (impl AppStoreValuesServiceImpl) FindValuesByAppStoreId(appStoreId int, ins ChartVersion: installedAppVersion.AppStoreApplicationVersion.Version, EnvironmentName: installedAppVersion.InstalledApp.Environment.Name, } + if util2.IsExternalChartStoreApp(installedAppVersion.InstalledApp.App.DisplayName) { + appStoreVersion.Name = installedAppVersion.InstalledApp.App.DisplayName + } installedVal.Values = append(installedVal.Values, appStoreVersion) } @@ -260,6 +264,9 @@ func (impl AppStoreValuesServiceImpl) FindValuesByAppStoreId(appStoreId int, ins ChartVersion: installedAppVersion.AppStoreApplicationVersion.Version, EnvironmentName: installedAppVersion.InstalledApp.Environment.Name, } + if util2.IsExternalChartStoreApp(installedAppVersion.InstalledApp.App.DisplayName) { + appStoreVersion.Name = installedAppVersion.InstalledApp.App.DisplayName + } existingVal.Values = append(existingVal.Values, appStoreVersion) } diff --git a/pkg/bean/app.go b/pkg/bean/app.go index 0beac8612e..a940facbd7 100644 --- a/pkg/bean/app.go +++ b/pkg/bean/app.go @@ -58,6 +58,7 @@ type CreateAppDTO struct { AppLabels []*Label `json:"labels,omitempty" validate:"dive"` GenericNote *bean2.GenericNoteResponseBean `json:"genericNote,omitempty"` AppType helper.AppType `json:"appType" validate:"gt=-1,lt=3"` //TODO: Change Validation if new AppType is introduced + DisplayName string `json:"-"` //not exposed to UI } type CreateMaterialDTO struct { diff --git a/wire_gen.go b/wire_gen.go index 6c226a42a6..6712526b9e 100644 --- a/wire_gen.go +++ b/wire_gen.go @@ -496,7 +496,8 @@ func InitializeApp() (*App, error) { ciTemplateServiceImpl := pipeline.NewCiTemplateServiceImpl(sugaredLogger, ciBuildConfigServiceImpl, ciTemplateRepositoryImpl, ciTemplateOverrideRepositoryImpl) appLabelRepositoryImpl := pipelineConfig.NewAppLabelRepositoryImpl(db) materialRepositoryImpl := pipelineConfig.NewMaterialRepositoryImpl(db) - appCrudOperationServiceImpl := app2.NewAppCrudOperationServiceImpl(appLabelRepositoryImpl, sugaredLogger, appRepositoryImpl, userRepositoryImpl, installedAppRepositoryImpl, genericNoteServiceImpl, materialRepositoryImpl) + installedAppDBServiceImpl := EAMode.NewInstalledAppDBServiceImpl(sugaredLogger, installedAppRepositoryImpl, appRepositoryImpl, userServiceImpl, environmentServiceImpl, installedAppVersionHistoryRepositoryImpl) + appCrudOperationServiceImpl := app2.NewAppCrudOperationServiceImpl(appLabelRepositoryImpl, sugaredLogger, appRepositoryImpl, userRepositoryImpl, installedAppRepositoryImpl, genericNoteServiceImpl, materialRepositoryImpl, installedAppDBServiceImpl) imageTagRepositoryImpl := repository2.NewImageTagRepository(db, sugaredLogger) customTagServiceImpl := pipeline.NewCustomTagService(sugaredLogger, imageTagRepositoryImpl) pluginInputVariableParserImpl := pipeline.NewPluginInputVariableParserImpl(sugaredLogger, dockerRegistryConfigImpl, customTagServiceImpl) @@ -582,7 +583,6 @@ func InitializeApp() (*App, error) { devtronAppConfigServiceImpl := pipeline.NewDevtronAppConfigServiceImpl(sugaredLogger, ciCdPipelineOrchestratorImpl, appRepositoryImpl, pipelineRepositoryImpl, resourceGroupServiceImpl, enforcerUtilImpl, ciMaterialConfigServiceImpl) pipelineBuilderImpl := pipeline.NewPipelineBuilderImpl(sugaredLogger, materialRepositoryImpl, chartRepositoryImpl, ciPipelineConfigServiceImpl, ciMaterialConfigServiceImpl, appArtifactManagerImpl, devtronAppCMCSServiceImpl, devtronAppStrategyServiceImpl, appDeploymentTypeChangeManagerImpl, cdPipelineConfigServiceImpl, devtronAppConfigServiceImpl) deploymentTemplateValidationServiceImpl := deploymentTemplate.NewDeploymentTemplateValidationServiceImpl(sugaredLogger, chartRefServiceImpl, scopedVariableManagerImpl) - installedAppDBServiceImpl := EAMode.NewInstalledAppDBServiceImpl(sugaredLogger, installedAppRepositoryImpl, appRepositoryImpl, userServiceImpl, environmentServiceImpl, installedAppVersionHistoryRepositoryImpl) installedAppDBExtendedServiceImpl := FullMode.NewInstalledAppDBExtendedServiceImpl(installedAppDBServiceImpl, appStatusServiceImpl, gitOpsConfigReadServiceImpl) gitOpsValidationServiceImpl := validation.NewGitOpsValidationServiceImpl(sugaredLogger, gitFactory, gitOperationServiceImpl, gitOpsConfigReadServiceImpl, chartTemplateServiceImpl, chartServiceImpl, installedAppDBExtendedServiceImpl) devtronAppGitOpConfigServiceImpl := gitOpsConfig.NewDevtronAppGitOpConfigServiceImpl(sugaredLogger, chartRepositoryImpl, chartServiceImpl, gitOpsConfigReadServiceImpl, gitOpsValidationServiceImpl, argoClientWrapperServiceImpl) @@ -679,7 +679,7 @@ func InitializeApp() (*App, error) { appStoreDeploymentCommonServiceImpl := appStoreDeploymentCommon.NewAppStoreDeploymentCommonServiceImpl(sugaredLogger, appStoreApplicationVersionRepositoryImpl, chartTemplateServiceImpl) fullModeDeploymentServiceImpl := deployment.NewFullModeDeploymentServiceImpl(sugaredLogger, applicationServiceClientImpl, argoK8sClientImpl, acdAuthConfig, chartGroupDeploymentRepositoryImpl, installedAppRepositoryImpl, installedAppVersionHistoryRepositoryImpl, argoUserServiceImpl, appStoreDeploymentCommonServiceImpl, helmAppServiceImpl, appStatusServiceImpl, pipelineStatusTimelineServiceImpl, userServiceImpl, pipelineStatusTimelineRepositoryImpl, appStoreApplicationVersionRepositoryImpl, argoClientWrapperServiceImpl, acdConfig, gitOperationServiceImpl, gitOpsConfigReadServiceImpl, gitOpsValidationServiceImpl, environmentRepositoryImpl) appStoreValidatorImpl := service3.NewAppAppStoreValidatorImpl(sugaredLogger) - appStoreDeploymentDBServiceImpl := service3.NewAppStoreDeploymentDBServiceImpl(sugaredLogger, installedAppRepositoryImpl, appStoreApplicationVersionRepositoryImpl, appRepositoryImpl, environmentServiceImpl, clusterServiceImplExtended, installedAppVersionHistoryRepositoryImpl, environmentVariables, gitOpsConfigReadServiceImpl, deploymentTypeOverrideServiceImpl, fullModeDeploymentServiceImpl, appStoreValidatorImpl) + appStoreDeploymentDBServiceImpl := service3.NewAppStoreDeploymentDBServiceImpl(sugaredLogger, installedAppRepositoryImpl, appStoreApplicationVersionRepositoryImpl, appRepositoryImpl, environmentServiceImpl, clusterServiceImplExtended, installedAppVersionHistoryRepositoryImpl, environmentVariables, gitOpsConfigReadServiceImpl, deploymentTypeOverrideServiceImpl, fullModeDeploymentServiceImpl, appStoreValidatorImpl, installedAppDBServiceImpl) eaModeDeploymentServiceImpl := EAMode.NewEAModeDeploymentServiceImpl(sugaredLogger, helmAppServiceImpl, appStoreApplicationVersionRepositoryImpl, helmAppClientImpl, installedAppRepositoryImpl, ociRegistryConfigRepositoryImpl) deletePostProcessorImpl := service3.NewDeletePostProcessorImpl(sugaredLogger) appStoreDeploymentServiceImpl := service3.NewAppStoreDeploymentServiceImpl(sugaredLogger, installedAppRepositoryImpl, installedAppDBServiceImpl, appStoreDeploymentDBServiceImpl, chartGroupDeploymentRepositoryImpl, appStoreApplicationVersionRepositoryImpl, appRepositoryImpl, eaModeDeploymentServiceImpl, fullModeDeploymentServiceImpl, environmentServiceImpl, helmAppServiceImpl, installedAppVersionHistoryRepositoryImpl, environmentVariables, acdConfig, gitOpsConfigReadServiceImpl, deletePostProcessorImpl, appStoreValidatorImpl)