Skip to content

Commit b5babb7

Browse files
ashishdevtronkartik-579ShashwatDadhich
authored
feat: Bulk Hibernate (#4229)
* fetched additional details for application groups ui * rbac changes * added hiberating filter * modified query * modified query * added cluster id in response * added error and auth error in response of bulk hibernate/unhibernate api * updated const * changed response structure * removed redundant query param from /app/list/group api * added hibernating unhibernating app groups in resource group list api * Added app status not found check * fixed error messaging return * updated response for bulk unhibernate * bug fix for clone app * bug fix for hibernate unhibernate * progress added * status updated * hibernate corrected * modified query * handled nil pointer error * handled nil err * fixed error condition * moved user validation at the top * fix * uncommented hibernating and unhibernating app group code snippet --------- Co-authored-by: kartik-579 <[email protected]> Co-authored-by: ShashwatDadhich <[email protected]>
1 parent 97852ce commit b5babb7

File tree

10 files changed

+284
-29
lines changed

10 files changed

+284
-29
lines changed

api/bean/AppView.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ type AppEnvironmentContainer struct {
118118
PreStageStatus *string `json:"preStageStatus"`
119119
PostStageStatus *string `json:"postStageStatus"`
120120
LastDeployedTime string `json:"lastDeployedTime,omitempty"`
121+
LastDeployedImage string `json:"lastDeployedImage,omitempty"`
122+
LastDeployedBy string `json:"lastDeployedBy,omitempty"`
121123
LastSuccessDeploymentDetail DeploymentDetailContainer `json:"-"`
122124
Default bool `json:"default"`
123125
Deleted bool `json:"deleted"`

api/restHandler/BulkUpdateRestHandler.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ func (handler BulkUpdateRestHandlerImpl) BulkHibernate(w http.ResponseWriter, r
281281
}
282282
ctx := context.WithValue(r.Context(), "token", acdToken)
283283
token := r.Header.Get("token")
284-
response, err := handler.bulkUpdateService.BulkHibernate(&request, ctx, w, token, handler.checkAuthForBulkActions)
284+
response, err := handler.bulkUpdateService.BulkHibernate(&request, ctx, w, token, handler.checkAuthForBulkHibernateAndUnhibernate)
285285
if err != nil {
286286
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
287287
return
@@ -318,7 +318,7 @@ func (handler BulkUpdateRestHandlerImpl) BulkUnHibernate(w http.ResponseWriter,
318318
}
319319
ctx := context.WithValue(r.Context(), "token", acdToken)
320320
token := r.Header.Get("token")
321-
response, err := handler.bulkUpdateService.BulkUnHibernate(&request, ctx, w, token, handler.checkAuthForBulkActions)
321+
response, err := handler.bulkUpdateService.BulkUnHibernate(&request, ctx, w, token, handler.checkAuthForBulkHibernateAndUnhibernate)
322322
if err != nil {
323323
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
324324
return
@@ -406,6 +406,16 @@ func (handler BulkUpdateRestHandlerImpl) checkAuthForBulkActions(token string, a
406406
return true
407407
}
408408

409+
func (handler BulkUpdateRestHandlerImpl) checkAuthForBulkHibernateAndUnhibernate(token string, appObject string, envObject string) bool {
410+
if ok := handler.enforcer.Enforce(token, casbin.ResourceApplications, casbin.ActionTrigger, strings.ToLower(appObject)); !ok {
411+
return false
412+
}
413+
if ok := handler.enforcer.Enforce(token, casbin.ResourceEnvironment, casbin.ActionTrigger, strings.ToLower(envObject)); !ok {
414+
return false
415+
}
416+
return true
417+
}
418+
409419
func (handler BulkUpdateRestHandlerImpl) HandleCdPipelineBulkAction(w http.ResponseWriter, r *http.Request) {
410420
decoder := json.NewDecoder(r.Body)
411421
userId, err := handler.userAuthService.GetLoggedInUser(r)

internal/sql/repository/AppListingRepository.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ type AppListingRepository interface {
5656
FindAppCount(isProd bool) (int, error)
5757
FetchAppsByEnvironmentV2(appListingFilter helper.AppListingFilter) ([]*bean.AppEnvironmentContainer, int, error)
5858
FetchOverviewAppsByEnvironment(envId, limit, offset int) ([]*bean.AppEnvironmentContainer, error)
59+
FetchLastDeployedImage(appId, envId int) (*LastDeployed, error)
5960
}
6061

6162
// below table is deprecated, not being used anywhere
@@ -75,7 +76,13 @@ type AppNameTypeIdContainerDBResponse struct {
7576
AppId int `sql:"id"`
7677
}
7778

79+
type LastDeployed struct {
80+
LastDeployedBy string `sql:"last_deployed_by"`
81+
LastDeployedImage string `sql:"last_deployed_image"`
82+
}
83+
7884
const NewDeployment string = "Deployment Initiated"
85+
const Hibernating string = "HIBERNATING"
7986

8087
type AppListingRepositoryImpl struct {
8188
dbConnection *pg.DB
@@ -138,6 +145,21 @@ func (impl AppListingRepositoryImpl) FetchOverviewAppsByEnvironment(envId, limit
138145
return envContainers, err
139146
}
140147

148+
func (impl AppListingRepositoryImpl) FetchLastDeployedImage(appId, envId int) (*LastDeployed, error) {
149+
var lastDeployed []*LastDeployed
150+
query := `select ca.image as last_deployed_image, u.email_id as last_deployed_by from pipeline p
151+
join cd_workflow cw on cw.pipeline_id = p.id
152+
join cd_workflow_runner cwr on cwr.cd_workflow_id = cw.id
153+
join ci_artifact ca on ca.id = cw.ci_artifact_id
154+
join users u on u.id = cwr.triggered_by
155+
where p.app_id = ? and p.environment_id = ? and p.deleted = false order by cwr.created_on desc;`
156+
_, err := impl.dbConnection.Query(&lastDeployed, query, appId, envId)
157+
if len(lastDeployed) > 0 {
158+
return lastDeployed[0], err
159+
}
160+
return nil, err
161+
}
162+
141163
func (impl AppListingRepositoryImpl) FetchJobsLastSucceededOn(CiPipelineIDs []int) ([]*bean.CiPipelineLastSucceededTime, error) {
142164
var lastSucceededTimeArray []*bean.CiPipelineLastSucceededTime
143165
if len(CiPipelineIDs) == 0 {

internal/sql/repository/appStatus/AppStatusRepository.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ type AppStatusRepository interface {
3232
DeleteWithEnvId(tx *pg.Tx, envId int) error
3333
Get(appId, envId int) (AppStatusContainer, error)
3434
GetConnection() *pg.DB
35+
GetByEnvId(envId int) ([]*AppStatusDto, error)
3536

3637
//GetAllDevtronAppStatuses(appIds []int) ([]AppStatusContainer, error)
3738
//GetAllInstalledAppStatuses(installedAppIds []int) ([]AppStatusContainer, error)
@@ -107,3 +108,11 @@ func (repo *AppStatusRepositoryImpl) Get(appId, envId int) (AppStatusContainer,
107108
}
108109
return container, err
109110
}
111+
112+
func (repo *AppStatusRepositoryImpl) GetByEnvId(envId int) ([]*AppStatusDto, error) {
113+
var models []*AppStatusDto
114+
err := repo.dbConnection.Model(&models).
115+
Where("env_id = ?", envId).
116+
Select()
117+
return models, err
118+
}

internal/sql/repository/pipelineConfig/PipelineRepository.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ package pipelineConfig
2020
import (
2121
"github.com/devtron-labs/common-lib/utils/k8s/health"
2222
"github.com/devtron-labs/devtron/api/bean"
23+
"github.com/devtron-labs/devtron/internal/sql/models"
2324
"github.com/devtron-labs/devtron/internal/sql/repository/app"
2425
"github.com/devtron-labs/devtron/internal/sql/repository/appWorkflow"
2526
"github.com/devtron-labs/devtron/internal/util"
@@ -110,6 +111,7 @@ type PipelineRepository interface {
110111
FindActiveByAppIds(appIds []int) (pipelines []*Pipeline, err error)
111112
FindAppAndEnvironmentAndProjectByPipelineIds(pipelineIds []int) (pipelines []*Pipeline, err error)
112113
FilterDeploymentDeleteRequestedPipelineIds(cdPipelineIds []int) (map[int]bool, error)
114+
FindDeploymentTypeByPipelineIds(cdPipelineIds []int) (map[int]DeploymentObject, error)
113115
}
114116

115117
type CiArtifactDTO struct {
@@ -122,6 +124,12 @@ type CiArtifactDTO struct {
122124
WorkflowId *int `json:"workflowId"`
123125
}
124126

127+
type DeploymentObject struct {
128+
DeploymentType models.DeploymentType `sql:"deployment_type"`
129+
PipelineId int `sql:"pipeline_id"`
130+
Status string `sql:"status"`
131+
}
132+
125133
type PipelineRepositoryImpl struct {
126134
dbConnection *pg.DB
127135
logger *zap.SugaredLogger
@@ -670,3 +678,26 @@ func (impl PipelineRepositoryImpl) FilterDeploymentDeleteRequestedPipelineIds(cd
670678
}
671679
return pipelineIdsMap, nil
672680
}
681+
682+
func (impl PipelineRepositoryImpl) FindDeploymentTypeByPipelineIds(cdPipelineIds []int) (map[int]DeploymentObject, error) {
683+
684+
pipelineIdsMap := make(map[int]DeploymentObject)
685+
686+
var deploymentType []DeploymentObject
687+
query := "with pcos as(select max(id) as id from pipeline_config_override where pipeline_id in (?) " +
688+
"group by pipeline_id) select pco.deployment_type,pco.pipeline_id, aps.status from pipeline_config_override " +
689+
"pco inner join pcos on pcos.id=pco.id" +
690+
" inner join pipeline p on p.id=pco.pipeline_id left join app_status aps on aps.app_id=p.app_id " +
691+
"and aps.env_id=p.environment_id;"
692+
693+
_, err := impl.dbConnection.Query(&deploymentType, query, pg.In(cdPipelineIds), true)
694+
if err != nil {
695+
return pipelineIdsMap, err
696+
}
697+
698+
for _, v := range deploymentType {
699+
pipelineIdsMap[v.PipelineId] = v
700+
}
701+
702+
return pipelineIdsMap, nil
703+
}

pkg/app/AppListingService.go

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
chartRepoRepository "github.com/devtron-labs/devtron/pkg/chartRepo/repository"
2828
repository2 "github.com/devtron-labs/devtron/pkg/cluster/repository"
2929
"github.com/devtron-labs/devtron/pkg/dockerRegistry"
30+
userrepository "github.com/devtron-labs/devtron/pkg/user/repository"
3031
"github.com/devtron-labs/devtron/util/argo"
3132
errors2 "github.com/juju/errors"
3233
"go.opentelemetry.io/otel"
@@ -166,6 +167,7 @@ type AppListingServiceImpl struct {
166167
chartRepository chartRepoRepository.ChartRepository
167168
ciPipelineRepository pipelineConfig.CiPipelineRepository
168169
dockerRegistryIpsConfigService dockerRegistry.DockerRegistryIpsConfigService
170+
userRepository userrepository.UserRepository
169171
}
170172

171173
func NewAppListingServiceImpl(Logger *zap.SugaredLogger, appListingRepository repository.AppListingRepository,
@@ -176,7 +178,7 @@ func NewAppListingServiceImpl(Logger *zap.SugaredLogger, appListingRepository re
176178
pipelineOverrideRepository chartConfig.PipelineOverrideRepository, environmentRepository repository2.EnvironmentRepository,
177179
argoUserService argo.ArgoUserService, envOverrideRepository chartConfig.EnvConfigOverrideRepository,
178180
chartRepository chartRepoRepository.ChartRepository, ciPipelineRepository pipelineConfig.CiPipelineRepository,
179-
dockerRegistryIpsConfigService dockerRegistry.DockerRegistryIpsConfigService) *AppListingServiceImpl {
181+
dockerRegistryIpsConfigService dockerRegistry.DockerRegistryIpsConfigService, userRepository userrepository.UserRepository) *AppListingServiceImpl {
180182
serviceImpl := &AppListingServiceImpl{
181183
Logger: Logger,
182184
appListingRepository: appListingRepository,
@@ -195,6 +197,7 @@ func NewAppListingServiceImpl(Logger *zap.SugaredLogger, appListingRepository re
195197
chartRepository: chartRepository,
196198
ciPipelineRepository: ciPipelineRepository,
197199
dockerRegistryIpsConfigService: dockerRegistryIpsConfigService,
200+
userRepository: userRepository,
198201
}
199202
return serviceImpl
200203
}
@@ -207,10 +210,20 @@ type OverviewAppsByEnvironmentBean struct {
207210
EnvironmentName string `json:"environmentName"`
208211
Namespace string `json:"namespace"`
209212
ClusterName string `json:"clusterName"`
213+
ClusterId int `json:"clusterId"`
214+
Type string `json:"environmentType"`
215+
Description string `json:"description"`
210216
AppCount int `json:"appCount"`
211217
Apps []*bean.AppEnvironmentContainer `json:"apps"`
218+
CreatedOn string `json:"createdOn"`
219+
CreatedBy string `json:"createdBy"`
212220
}
213221

222+
const (
223+
Production = "Production"
224+
NonProduction = "Non-Production"
225+
)
226+
214227
func (impl AppListingServiceImpl) FetchOverviewAppsByEnvironment(envId, limit, offset int) (*OverviewAppsByEnvironmentBean, error) {
215228
resp := &OverviewAppsByEnvironmentBean{}
216229
env, err := impl.environmentRepository.FindById(envId)
@@ -220,11 +233,34 @@ func (impl AppListingServiceImpl) FetchOverviewAppsByEnvironment(envId, limit, o
220233
resp.EnvironmentId = envId
221234
resp.EnvironmentName = env.Name
222235
resp.ClusterName = env.Cluster.ClusterName
236+
resp.ClusterId = env.ClusterId
223237
resp.Namespace = env.Namespace
238+
resp.CreatedOn = env.CreatedOn.String()
239+
if env.Default {
240+
resp.Type = Production
241+
} else {
242+
resp.Type = NonProduction
243+
}
244+
resp.Description = env.Description
245+
createdBy, err := impl.userRepository.GetById(env.CreatedBy)
246+
if err != nil {
247+
return resp, err
248+
}
249+
resp.CreatedBy = createdBy.EmailId
224250
envContainers, err := impl.appListingRepository.FetchOverviewAppsByEnvironment(envId, limit, offset)
225251
if err != nil {
226252
return resp, err
227253
}
254+
for _, envContainer := range envContainers {
255+
lastDeployed, err := impl.appListingRepository.FetchLastDeployedImage(envContainer.AppId, envId)
256+
if err != nil {
257+
return resp, err
258+
}
259+
if lastDeployed != nil {
260+
envContainer.LastDeployedImage = lastDeployed.LastDeployedImage
261+
envContainer.LastDeployedBy = lastDeployed.LastDeployedBy
262+
}
263+
}
228264
resp.Apps = envContainers
229265
return resp, err
230266
}

0 commit comments

Comments
 (0)