@@ -14,7 +14,6 @@ import (
14
14
"net/http"
15
15
"reflect"
16
16
"strconv"
17
- "strings"
18
17
"time"
19
18
20
19
"github.com/caarlos0/env/v6"
@@ -56,7 +55,7 @@ type HelmAppService interface {
56
55
GetValuesYaml (ctx context.Context , app * AppIdentifier ) (* gRPC.ReleaseInfo , error )
57
56
GetDesiredManifest (ctx context.Context , app * AppIdentifier , resource * openapi.ResourceIdentifier ) (* openapi.DesiredManifestResponse , error )
58
57
DeleteApplication (ctx context.Context , app * AppIdentifier ) (* openapi.UninstallReleaseResponse , error )
59
- DeleteDBLinkedHelmApplication (ctx context.Context , app * AppIdentifier , useId int32 ) (* openapi.UninstallReleaseResponse , error )
58
+ DeleteDBLinkedHelmApplication (ctx context.Context , appIdentifier * AppIdentifier , useId int32 ) (* openapi.UninstallReleaseResponse , error )
60
59
// UpdateApplication is a wrapper over helmAppClient.UpdateApplication, sends update request to kubelink for external chart store apps
61
60
UpdateApplication (ctx context.Context , app * AppIdentifier , request * bean.UpdateApplicationRequestDto ) (* openapi.UpdateReleaseResponse , error )
62
61
GetDeploymentDetail (ctx context.Context , app * AppIdentifier , version int32 ) (* openapi.HelmAppDeploymentManifestDetail , error )
@@ -448,7 +447,56 @@ func (impl *HelmAppServiceImpl) GetDesiredManifest(ctx context.Context, app *App
448
447
return response , nil
449
448
}
450
449
451
- func (impl * HelmAppServiceImpl ) DeleteDBLinkedHelmApplication (ctx context.Context , app * AppIdentifier , userId int32 ) (* openapi.UninstallReleaseResponse , error ) {
450
+ // getInstalledAppForAppIdentifier return installed_apps for app unique identifier or releaseName/displayName whichever exists else return pg.ErrNoRows
451
+ func (impl * HelmAppServiceImpl ) getInstalledAppForAppIdentifier (appIdentifier * AppIdentifier ) (* repository.InstalledApps , error ) {
452
+ model := & repository.InstalledApps {}
453
+ var err error
454
+ //for ext apps search app from unique identifier
455
+ appUniqueIdentifier := appIdentifier .GetUniqueAppNameIdentifier ()
456
+ model , err = impl .installedAppRepository .GetInstalledAppByAppName (appUniqueIdentifier )
457
+ if err != nil {
458
+ if util .IsErrNoRows (err ) {
459
+ //if error is pg no rows, then find installed app via app.DisplayName because this can also happen that
460
+ //an ext-app is already linked to devtron, and it's entry in app_name col in app table will not be a unique
461
+ //identifier but the display name.
462
+ displayName := appIdentifier .ReleaseName
463
+ model , err = impl .installedAppRepository .GetInstalledAppByAppName (displayName )
464
+ if err != nil {
465
+ impl .logger .Errorw ("error in fetching installed app from display name" , "appDisplayName" , displayName , "err" , err )
466
+ return model , err
467
+ }
468
+ } else {
469
+ impl .logger .Errorw ("error in fetching installed app by app unique identifier" , "appUniqueIdentifier" , appUniqueIdentifier , "err" , err )
470
+ return model , err
471
+ }
472
+ }
473
+ return model , nil
474
+ }
475
+
476
+ func (impl * HelmAppServiceImpl ) getAppForAppIdentifier (appIdentifier * AppIdentifier ) (* app.App , error ) {
477
+ //for ext apps search app from unique identifier
478
+ appUniqueIdentifier := appIdentifier .GetUniqueAppNameIdentifier ()
479
+ model , err := impl .appRepository .FindActiveByName (appUniqueIdentifier )
480
+ if err != nil {
481
+ if util .IsErrNoRows (err ) {
482
+ //if error is pg no rows, then find app via release name because this can also happen that a project is
483
+ //already assigned a project, and it's entry in app_name col in app table will not be a unique
484
+ //identifier but the display name i.e. release name.
485
+ displayName := appIdentifier .ReleaseName
486
+ model , err = impl .appRepository .FindActiveByName (displayName )
487
+ if err != nil {
488
+ impl .logger .Errorw ("error in fetching app from display name" , "appDisplayName" , displayName , "err" , err )
489
+ return nil , err
490
+ }
491
+ } else {
492
+ impl .logger .Errorw ("error in fetching app by app unique identifier" , "appUniqueIdentifier" , appUniqueIdentifier , "err" , err )
493
+ return nil , err
494
+ }
495
+ }
496
+ return model , nil
497
+ }
498
+
499
+ func (impl * HelmAppServiceImpl ) DeleteDBLinkedHelmApplication (ctx context.Context , appIdentifier * AppIdentifier , userId int32 ) (* openapi.UninstallReleaseResponse , error ) {
452
500
dbConnection := impl .appRepository .GetConnection ()
453
501
tx , err := dbConnection .Begin ()
454
502
if err != nil {
@@ -457,67 +505,94 @@ func (impl *HelmAppServiceImpl) DeleteDBLinkedHelmApplication(ctx context.Contex
457
505
}
458
506
// Rollback tx on error.
459
507
defer tx .Rollback ()
508
+ var isAppLinkedToChartStore bool // if true, entry present in both app and installed_app table
460
509
461
- // For Helm deployments ReleaseName is App.Name
462
- model , err := impl .installedAppRepository .GetInstalledAppByAppName (app .ReleaseName )
463
- if err != nil {
464
- impl .logger .Errorw ("error in fetching installed app" , "appName" , app .ReleaseName , "err" , err )
510
+ installedAppModel , err := impl .getInstalledAppForAppIdentifier (appIdentifier )
511
+ if err != nil && ! util .IsErrNoRows (err ) {
512
+ impl .logger .Errorw ("DeleteDBLinkedHelmApplication, error in fetching installed app for app identifier" , "appIdentifier" , appIdentifier , "err" , err )
465
513
return nil , err
466
514
}
467
-
468
- // 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});
469
- // And if the request is received for the externally installed app, the below condition will handle
470
- if model .Environment .Namespace != app .Namespace {
471
- return nil , pg .ErrNoRows
515
+ if installedAppModel .Id > 0 {
516
+ isAppLinkedToChartStore = true
472
517
}
473
518
474
- // App Delete --> Start
475
- //soft delete app
476
- appModel := & model .App
477
- appModel .Active = false
478
- appModel .UpdatedBy = userId
479
- appModel .UpdatedOn = time .Now ()
480
- err = impl .appRepository .UpdateWithTxn (appModel , tx )
481
- if err != nil {
482
- impl .logger .Errorw ("error in deleting appModel" , "app" , appModel )
483
- return nil , err
484
- }
485
- // App Delete --> End
519
+ if isAppLinkedToChartStore {
520
+ // 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});
521
+ // And if the request is received for the externally installed app, the below condition will handle
522
+ if installedAppModel .Environment .Namespace != appIdentifier .Namespace {
523
+ return nil , pg .ErrNoRows
524
+ }
486
525
487
- // InstalledApp Delete --> Start
488
- // soft delete install app
489
- model .Active = false
490
- model .UpdatedBy = userId
491
- model .UpdatedOn = time .Now ()
492
- _ , err = impl .installedAppRepository .UpdateInstalledApp (model , tx )
493
- if err != nil {
494
- impl .logger .Errorw ("error while deleting install app" , "error" , err )
495
- return nil , err
496
- }
497
- // InstalledApp Delete --> End
526
+ // App Delete --> Start
527
+ //soft delete app
528
+ appModel := & installedAppModel .App
529
+ appModel .Active = false
530
+ appModel .UpdatedBy = userId
531
+ appModel .UpdatedOn = time .Now ()
532
+ err = impl .appRepository .UpdateWithTxn (appModel , tx )
533
+ if err != nil {
534
+ impl .logger .Errorw ("error in deleting appModel" , "app" , appModel )
535
+ return nil , err
536
+ }
537
+ // App Delete --> End
538
+
539
+ // InstalledApp Delete --> Start
540
+ // soft delete install app
541
+ installedAppModel .Active = false
542
+ installedAppModel .UpdatedBy = userId
543
+ installedAppModel .UpdatedOn = time .Now ()
544
+ _ , err = impl .installedAppRepository .UpdateInstalledApp (installedAppModel , tx )
545
+ if err != nil {
546
+ impl .logger .Errorw ("error while deleting install app" , "error" , err )
547
+ return nil , err
548
+ }
549
+ // InstalledApp Delete --> End
498
550
499
- // InstalledAppVersions Delete --> Start
500
- models , err := impl .installedAppRepository .GetInstalledAppVersionByInstalledAppId (model .Id )
501
- if err != nil {
502
- impl .logger .Errorw ("error while fetching install app versions" , "error" , err )
503
- return nil , err
504
- }
551
+ // InstalledAppVersions Delete --> Start
552
+ models , err := impl .installedAppRepository .GetInstalledAppVersionByInstalledAppId (installedAppModel .Id )
553
+ if err != nil {
554
+ impl .logger .Errorw ("error while fetching install app versions" , "error" , err )
555
+ return nil , err
556
+ }
505
557
506
- // soft delete install app versions
507
- for _ , item := range models {
508
- item .Active = false
509
- item .UpdatedBy = userId
510
- item .UpdatedOn = time .Now ()
511
- _ , err = impl .installedAppRepository .UpdateInstalledAppVersion (item , tx )
558
+ // soft delete install app versions
559
+ for _ , item := range models {
560
+ item .Active = false
561
+ item .UpdatedBy = userId
562
+ item .UpdatedOn = time .Now ()
563
+ _ , err = impl .installedAppRepository .UpdateInstalledAppVersion (item , tx )
564
+ if err != nil {
565
+ impl .logger .Errorw ("error while fetching from db" , "error" , err )
566
+ return nil , err
567
+ }
568
+ }
569
+ // InstalledAppVersions Delete --> End
570
+ } else {
571
+ //this means app not found in installed_apps, but a scenario where an external app is only
572
+ //assigned project and not linked to devtron, in that case only entry in app will be found.
573
+ appModel , err := impl .getAppForAppIdentifier (appIdentifier )
512
574
if err != nil {
513
- impl .logger .Errorw ("error while fetching from db " , "error " , err )
575
+ impl .logger .Errorw ("DeleteDBLinkedHelmApplication, error in fetching app from appIdentifier " , "appIdentifier" , appIdentifier , "err " , err )
514
576
return nil , err
515
577
}
578
+ if appModel != nil && appModel .Id > 0 {
579
+ // App Delete --> Start
580
+ //soft delete app
581
+ appModel .Active = false
582
+ appModel .UpdatedBy = userId
583
+ appModel .UpdatedOn = time .Now ()
584
+ err = impl .appRepository .UpdateWithTxn (appModel , tx )
585
+ if err != nil {
586
+ impl .logger .Errorw ("error in deleting appModel" , "app" , appModel )
587
+ return nil , err
588
+ }
589
+ // App Delete --> End
590
+ }
516
591
}
517
- // InstalledAppVersions Delete --> End
518
- res , err := impl .DeleteApplication (ctx , app )
592
+
593
+ res , err := impl .DeleteApplication (ctx , appIdentifier )
519
594
if err != nil {
520
- impl .logger .Errorw ("error in deleting helm application" , "error" , err , "appIdentifier" , app )
595
+ impl .logger .Errorw ("error in deleting helm application" , "error" , err , "appIdentifier" , appIdentifier )
521
596
return nil , err
522
597
}
523
598
@@ -1005,29 +1080,32 @@ type AppIdentifier struct {
1005
1080
ReleaseName string `json:"releaseName"`
1006
1081
}
1007
1082
1083
+ // GetUniqueAppNameIdentifier returns unique app name identifier, we store all helm releases in kubelink cache with key
1084
+ // as what is returned from this func, this is the case where an app across diff namespace or cluster can have same name,
1085
+ // so to identify then uniquely below implementation would serve as good unique identifier for an external app.
1086
+ func (r * AppIdentifier ) GetUniqueAppNameIdentifier () string {
1087
+ return fmt .Sprintf ("%s-%s-%s" , r .ReleaseName , r .Namespace , strconv .Itoa (r .ClusterId ))
1088
+ }
1089
+
1090
+ func (r * AppIdentifier ) GetUniqueAppIdentifierForGivenNamespaceAndCluster (namespace , clusterId string ) string {
1091
+ return fmt .Sprintf ("%s-%s-%s" , r .ReleaseName , namespace , clusterId )
1092
+ }
1093
+
1008
1094
func (impl * HelmAppServiceImpl ) DecodeAppId (appId string ) (* AppIdentifier , error ) {
1009
- component := strings .Split (appId , "|" )
1010
- if len (component ) != 3 {
1011
- return nil , fmt .Errorf ("malformed app id %s" , appId )
1012
- }
1013
- clusterId , err := strconv .Atoi (component [0 ])
1014
- if err != nil {
1015
- return nil , err
1016
- }
1017
- if clusterId <= 0 {
1018
- return nil , fmt .Errorf ("target cluster is not provided" )
1019
- }
1020
- return & AppIdentifier {
1021
- ClusterId : clusterId ,
1022
- Namespace : component [1 ],
1023
- ReleaseName : component [2 ],
1024
- }, nil
1095
+ return DecodeExternalAppAppId (appId )
1025
1096
}
1026
1097
1027
1098
func (impl * HelmAppServiceImpl ) EncodeAppId (appIdentifier * AppIdentifier ) string {
1028
1099
return fmt .Sprintf ("%d|%s|%s" , appIdentifier .ClusterId , appIdentifier .Namespace , appIdentifier .ReleaseName )
1029
1100
}
1030
1101
1102
+ func isSameAppName (deployedAppName string , appDto app.App ) bool {
1103
+ if len (appDto .DisplayName ) > 0 {
1104
+ return deployedAppName == appDto .DisplayName
1105
+ }
1106
+ return deployedAppName == appDto .AppName
1107
+ }
1108
+
1031
1109
func (impl * HelmAppServiceImpl ) appListRespProtoTransformer (deployedApps * gRPC.DeployedAppList , token string , helmAuth func (token string , object string ) bool , helmCdPipelines []* pipelineConfig.Pipeline , installedHelmApps []* repository.InstalledApps ) openapi.AppList {
1032
1110
applicationType := "HELM-APP"
1033
1111
appList := openapi.AppList {ClusterIds : & []int32 {deployedApps .ClusterId }, ApplicationType : & applicationType }
@@ -1055,7 +1133,7 @@ func (impl *HelmAppServiceImpl) appListRespProtoTransformer(deployedApps *gRPC.D
1055
1133
1056
1134
// do not add helm apps in the list which are created using app_store
1057
1135
for _ , installedHelmApp := range installedHelmApps {
1058
- if deployedapp .AppName == installedHelmApp .App . AppName && int (deployedapp .EnvironmentDetail .ClusterId ) == installedHelmApp .Environment .ClusterId && deployedapp .EnvironmentDetail .Namespace == installedHelmApp .Environment .Namespace {
1136
+ if isSameAppName ( deployedapp .AppName , installedHelmApp .App ) && int (deployedapp .EnvironmentDetail .ClusterId ) == installedHelmApp .Environment .ClusterId && deployedapp .EnvironmentDetail .Namespace == installedHelmApp .Environment .Namespace {
1059
1137
toExcludeFromList = true
1060
1138
break
1061
1139
}
0 commit comments