Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Helm apps entries in Ea mode #5652

Merged
merged 19 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
a738b35
added the ea apps entry app table
RajeevRanjan27 Aug 8, 2024
15d0985
resolved the ea mode multiple rows error during configuration of app
RajeevRanjan27 Aug 8, 2024
e7cfab6
modified the ea dockerfile in ca-certificates cmd
RajeevRanjan27 Aug 8, 2024
a1fa72f
uncommented the code and left the ea helm app making way untouched
RajeevRanjan27 Aug 8, 2024
9f73940
remodified the dockerfile as previous state
RajeevRanjan27 Aug 8, 2024
0660784
modified the docker file ea mode
RajeevRanjan27 Aug 8, 2024
531721a
dockerfile exit code 100 due to ap install alternative in ea mode doc…
RajeevRanjan27 Aug 8, 2024
ff841d1
Merge branch 'main' into fix-helm-apps-entries
RajeevRanjan27 Aug 9, 2024
2aecb3c
execute make after main merge
RajeevRanjan27 Aug 9, 2024
f64bb35
modified changes in dockerfile ea mode
RajeevRanjan27 Aug 9, 2024
4b94e16
Merge branch 'main' into fix-helm-apps-entries
RajeevRanjan27 Aug 9, 2024
36190e2
Merge branch 'main' into fix-helm-apps-entries
RajeevRanjan27 Aug 12, 2024
cbfd7e7
resolved comments after first level review
RajeevRanjan27 Aug 12, 2024
59628be
Merge branch 'main' into fix-helm-apps-entries
RajeevRanjan27 Aug 12, 2024
27c1036
Merge branch 'develop' into fix-helm-apps-entries
RajeevRanjan27 Aug 20, 2024
071e767
executed make after merging with develop branch
RajeevRanjan27 Aug 20, 2024
3de0199
Merge branch 'develop' into fix-helm-apps-entries
RajeevRanjan27 Aug 21, 2024
2c0acfd
Merge branch 'develop' into fix-helm-apps-entries
RajeevRanjan27 Aug 21, 2024
1462aa8
Merge branch 'develop' into fix-helm-apps-entries
RajeevRanjan27 Aug 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 73 additions & 5 deletions pkg/app/AppCrudOperationService.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package app
import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/caarlos0/env"
client "github.com/devtron-labs/devtron/api/helm-app/service"
Expand Down Expand Up @@ -457,13 +458,67 @@ func convertUrlToHttpsIfSshType(url string) string {
return httpsURL
}

// handleDuplicateAppEntries identifies and resolves duplicate app entries based on creation time.
// It marks the most recent duplicate entry as inactive and updates the corresponding installed app.
func (impl AppCrudOperationServiceImpl) handleDuplicateAppEntries(appNameUniqueIdentifier string) (*appRepository.App, error) {
// Fetch app IDs by name
appIds, err := impl.getAppIdsByName(appNameUniqueIdentifier)
if err != nil {
impl.logger.Errorw("error in fetching app Ids by appIdentifier", "appNameUniqueIdentifier", appNameUniqueIdentifier, "err", err)
return nil, err
}

// Fetch apps by IDs from App table for duplicated entries
apps, err := impl.appRepository.FindByIds(appIds)
if err != nil || errors.Is(err, pg.ErrNoRows) {
impl.logger.Errorw("error in fetching app List by appIds", "appIds", appIds, "err", err)
return nil, err
}

// Identify the earliest and duplicated app entries
earliestApp, duplicatedApp := identifyDuplicateApps(apps)

// Fetch the installed app associated with the duplicated app
installedApp, err := impl.installedAppRepository.GetInstalledAppsByAppId(duplicatedApp.Id)
if err != nil {
impl.logger.Errorw("error in fetching installed app by appId", "appId", duplicatedApp.Id, "err", err)
return nil, err
}
// Update duplicated app entries
err = impl.installedAppDbService.UpdateDuplicatedEntriesInAppAndInstalledApps(earliestApp, duplicatedApp, &installedApp)
if err != nil {
impl.logger.Errorw("error in updating duplicated entries", "earliestApp", earliestApp, "duplicatedApp", duplicatedApp, "err", err)
return nil, err
}

impl.logger.Debug("Successfully resolved duplicate app entries", "earliestApp", earliestApp, "duplicatedApp", duplicatedApp)
return earliestApp, nil

}

// getAppIdsByName fetches app IDs by the app name unique identifier [for duplicated active app]
func (impl AppCrudOperationServiceImpl) getAppIdsByName(appNameUniqueIdentifier string) ([]*int, error) {
slice := []string{appNameUniqueIdentifier}
appIds, err := impl.appRepository.FindIdsByNames(slice)
if err != nil {
return nil, err
}

// Convert each element to a pointer and store in a slice of pointers
ids := make([]*int, len(appIds))
for i := range appIds {
ids[i] = &appIds[i]
}
return ids, nil
}

// 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 *helmBean.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 {
if err != nil && !errors.Is(err, pg.ErrNoRows) && !errors.Is(err, pg.ErrMultiRows) {
impl.logger.Errorw("error in fetching app meta data by unique app identifier", "appNameUniqueIdentifier", appNameUniqueIdentifier, "err", err)
return app, err
}
Expand All @@ -475,6 +530,14 @@ func (impl AppCrudOperationServiceImpl) getAppAndProjectForAppIdentifier(appIden
return app, err
}
}
if errors.Is(err, pg.ErrMultiRows) {

app, err = impl.handleDuplicateAppEntries(appNameUniqueIdentifier)
if err != nil {
impl.logger.Errorw("error in handling Duplicate entries in the app", "appNameUniqueIdentifier", appNameUniqueIdentifier, "err", err)
return app, err
}
}
return app, nil
}

Expand Down Expand Up @@ -532,17 +595,17 @@ func (impl AppCrudOperationServiceImpl) GetHelmAppMetaInfo(appId string) (*bean.
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 {
if app != nil && 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 {
if app != nil && app.Id == 0 {
app.AppName = appIdDecoded.ReleaseName
}
if util2.IsExternalChartStoreApp(app.DisplayName) {
if app != nil && util2.IsExternalChartStoreApp(app.DisplayName) {
displayName = app.DisplayName
}

Expand All @@ -568,9 +631,14 @@ func (impl AppCrudOperationServiceImpl) GetHelmAppMetaInfo(appId string) (*bean.
displayName = InstalledApp.App.DisplayName
}
}
// Safeguard against nil app cases
if app == nil {
impl.logger.Errorw("no rows found for the requested app", "appId", appId, "error", err)
return nil, fmt.Errorf("no rows found for the requested app, %q", pg.ErrNoRows)
}

user, err := impl.userRepository.GetByIdIncludeDeleted(app.CreatedBy)
if err != nil && err != pg.ErrNoRows {
if err != nil && !errors.Is(err, pg.ErrNoRows) {
impl.logger.Errorw("error in fetching user for app meta info", "error", err)
return nil, err
}
Expand Down
23 changes: 22 additions & 1 deletion pkg/app/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@

package app

import "strings"
import (
appRepository "github.com/devtron-labs/devtron/internal/sql/repository/app"
"strings"
)

// LabelMatchingRegex is the official k8s label matching regex, pls refer https://github.com/kubernetes/apimachinery/blob/bfd2aff97e594f6aad77acbe2cbbe190acc93cbc/pkg/util/validation/validation.go#L167
const LabelMatchingRegex = "^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$"
Expand All @@ -43,3 +46,21 @@ func sanitizeLabels(extraAppLabels map[string]string) map[string]string {
}
return extraAppLabels
}

// identifyDuplicateApps identifies the earliest created app and the most recent duplicate app.
func identifyDuplicateApps(apps []*appRepository.App) (earliestApp *appRepository.App, duplicatedApp *appRepository.App) {
if len(apps) == 0 {
return nil, nil
}
earliestApp = apps[0]
duplicatedApp = apps[0]
for _, app := range apps[1:] {
if app.AuditLog.CreatedOn.Before(earliestApp.AuditLog.CreatedOn) {
earliestApp = app
}
if app.AuditLog.CreatedOn.After(duplicatedApp.AuditLog.CreatedOn) {
duplicatedApp = app
}
}
return earliestApp, duplicatedApp
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,6 @@ func (impl *AppStoreDeploymentDBServiceImpl) AppStoreDeployOperationDB(installRe
}
// setting additional env data required in appStoreBean.InstallAppVersionDTO
adapter.UpdateAdditionalEnvDetails(installRequest, environment)

impl.appStoreValidator.Validate(installRequest, environment)

// Stage 1: Create App in tx (Only if AppId is not set already)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ type InstalledAppDBService interface {
GetReleaseInfo(appIdentifier *helmBean.AppIdentifier) (*appStoreBean.InstallAppVersionDTO, error)
IsExternalAppLinkedToChartStore(appId int) (bool, []*appStoreRepo.InstalledApps, error)
CreateNewAppEntryForAllInstalledApps(installedApps []*appStoreRepo.InstalledApps) error
UpdateDuplicatedEntriesInAppAndInstalledApps(earlyApp *app.App, duplicatedApp *app.App, installedApp *appStoreRepo.InstalledApps) error
}

type InstalledAppDBServiceImpl struct {
Expand Down Expand Up @@ -399,6 +400,17 @@ func (impl *InstalledAppDBServiceImpl) CreateNewAppEntryForAllInstalledApps(inst
// Rollback tx on error.
defer tx.Rollback()
for _, installedApp := range installedApps {

//check if there is any app from its appName is exits and active ...if yes then we will not insert any extra entry in the db
appMetadataByAppName, err := impl.AppRepository.FindActiveByName(installedApp.App.AppName)
if err != nil && !util.IsErrNoRows(err) {
impl.Logger.Errorw("error in fetching app by unique app identifier", "appNameUniqueIdentifier", installedApp.GetUniqueAppNameIdentifier(), "err", err)
return err
}
if appMetadataByAppName != nil && appMetadataByAppName.Id > 0 {
//app already exists for this unique identifier hence not creating new app entry for this as it will get modified after this function
continue
}
//check if for this unique identifier name an app already exists, if yes then continue
appMetadata, err := impl.AppRepository.FindActiveByName(installedApp.GetUniqueAppNameIdentifier())
if err != nil && !util.IsErrNoRows(err) {
Expand Down Expand Up @@ -437,3 +449,45 @@ func (impl *InstalledAppDBServiceImpl) CreateNewAppEntryForAllInstalledApps(inst
tx.Commit()
return nil
}

// UpdateDuplicatedEntriesInAppAndInstalledApps performs the updation in app table and installedApps table for the cases when multiple active app found [typically two due to migration], here we are updating the db with its previous value in the installedApps table and early created app id
func (impl *InstalledAppDBServiceImpl) UpdateDuplicatedEntriesInAppAndInstalledApps(earlyApp *app.App, duplicatedApp *app.App, installedApp *appStoreRepo.InstalledApps) error {
// db operations
dbConnection := impl.InstalledAppRepository.GetConnection()
tx, err := dbConnection.Begin()
if err != nil {
return err
}
// Rollback tx on error.
defer func(tx *pg.Tx) {
err := tx.Rollback()
if err != nil {
impl.Logger.Errorw("Rollback error", "err", err)
}
}(tx)

//updated the app table with active column as false for the duplicated app
duplicatedApp.Active = false
duplicatedApp.CreateAuditLog(bean3.SystemUserId)
err = impl.AppRepository.UpdateWithTxn(duplicatedApp, tx)
if err != nil {
impl.Logger.Errorw("error saving appModel", "err", err)
return err
}

// updating the installedApps table with its appId column with the previous app
installedApp.AppId = earlyApp.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
}

err = tx.Commit()
if err != nil {
impl.Logger.Errorw("error saving appModel", "err", err)
return err
}
return nil
}
2 changes: 1 addition & 1 deletion wire_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.