Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
36 changes: 20 additions & 16 deletions api/bean/AppView.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ type AppEnvironmentContainer struct {
TeamName string `json:"teamName"`
Description string `json:"description" validate:"max=40"`
TotalCount int `json:"-"`
Commits []string `json:"commits"`
}

type DeploymentDetailContainer struct {
Expand Down Expand Up @@ -197,22 +198,25 @@ type Notes struct {
}

type Environment struct {
AppStatus string `json:"appStatus"` //this is not the status of environment , this make sense with a specific app only
EnvironmentId int `json:"environmentId"`
EnvironmentName string `json:"environmentName"`
AppMetrics *bool `json:"appMetrics"`
InfraMetrics *bool `json:"infraMetrics"`
Prod bool `json:"prod"`
ChartRefId int `json:"chartRefId"`
LastDeployed string `json:"lastDeployed"`
LastDeployedBy string `json:"lastDeployedBy"`
LastDeployedImage string `json:"lastDeployedImage"`
DeploymentAppDeleteRequest bool `json:"deploymentAppDeleteRequest"`
Description string `json:"description" validate:"max=40"`
IsVirtualEnvironment bool `json:"isVirtualEnvironment"`
ClusterId int `json:"clusterId"`
PipelineId int `json:"pipelineId"`
LatestCdWorkflowRunnerId int `json:"latestCdWorkflowRunnerId,omitempty"`
AppStatus string `json:"appStatus"` //this is not the status of environment , this make sense with a specific app only
EnvironmentId int `json:"environmentId"`
EnvironmentName string `json:"environmentName"`
AppMetrics *bool `json:"appMetrics"`
InfraMetrics *bool `json:"infraMetrics"`
Prod bool `json:"prod"`
ChartRefId int `json:"chartRefId"`
LastDeployed string `json:"lastDeployed"`
LastDeployedBy string `json:"lastDeployedBy"`
LastDeployedImage string `json:"lastDeployedImage"`
DeploymentAppDeleteRequest bool `json:"deploymentAppDeleteRequest"`
Description string `json:"description" validate:"max=40"`
IsVirtualEnvironment bool `json:"isVirtualEnvironment"`
ClusterId int `json:"clusterId"`
PipelineId int `json:"pipelineId"`
LatestCdWorkflowRunnerId int `json:"latestCdWorkflowRunnerId,omitempty"`
CiArtifactId int `json:"ciArtifactId"`
ParentCiArtifactId int `json:"-"`
Commits []string `json:"commits"`
}

type InstanceDetail struct {
Expand Down
2 changes: 1 addition & 1 deletion api/restHandler/app/appList/AppListingRestHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ func (handler AppListingRestHandlerImpl) FetchOverviewAppsByEnvironment(w http.R
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
return
}
resp, err := handler.appListingService.FetchOverviewAppsByEnvironment(envId, limit, offset)
resp, err := handler.appListingService.FetchOverviewAppsByEnvironment(envId, limit, offset, r.Context())
if err != nil {
handler.logger.Errorw("error in getting apps for app-group overview", "envid", envId, "limit", limit, "offset", offset)
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
Expand Down
10 changes: 6 additions & 4 deletions internal/sql/repository/AppListingRepository.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ type AppListingRepository interface {
DeploymentDetailByArtifactId(ciArtifactId int, envId int) (bean.DeploymentDetailContainer, error)
FindAppCount(isProd bool) (int, error)
FetchAppsByEnvironmentV2(appListingFilter helper.AppListingFilter) ([]*bean.AppEnvironmentContainer, int, error)
FetchOverviewAppsByEnvironment(envId, limit, offset int) ([]*bean.AppEnvironmentContainer, error)
FetchOverviewAppsByEnvironment(envId, limit, offset int, ctx context.Context) ([]*bean.AppEnvironmentContainer, error)
FetchLastDeployedImage(appId, envId int) (*LastDeployed, error)
}

Expand All @@ -76,6 +76,8 @@ type AppNameTypeIdContainerDBResponse struct {
}

type LastDeployed struct {
ParentCiArtifact int `sql:"parent_ci_artifact_id"`
CiArtifactId int `sql:"ci_artifact_id"`
LastDeployedBy string `sql:"last_deployed_by"`
LastDeployedImage string `sql:"last_deployed_image"`
}
Expand Down Expand Up @@ -133,7 +135,7 @@ func (impl AppListingRepositoryImpl) FetchOverviewCiPipelines(jobId int) ([]*bea
return jobContainers, nil
}

func (impl AppListingRepositoryImpl) FetchOverviewAppsByEnvironment(envId, limit, offset int) ([]*bean.AppEnvironmentContainer, error) {
func (impl AppListingRepositoryImpl) FetchOverviewAppsByEnvironment(envId, limit, offset int, ctx context.Context) ([]*bean.AppEnvironmentContainer, error) {
query := " SELECT a.id as app_id,a.app_name,aps.status as app_status, ld.last_deployed_time " +
" FROM app a " +
" INNER JOIN pipeline p ON p.app_id = a.id and p.deleted = false and p.environment_id = ? " +
Expand All @@ -157,7 +159,7 @@ func (impl AppListingRepositoryImpl) FetchOverviewAppsByEnvironment(envId, limit
func (impl AppListingRepositoryImpl) FetchLastDeployedImage(appId, envId int) (*LastDeployed, error) {
var lastDeployed []*LastDeployed
// we are adding a case in the query to concatenate the string "(inactive)" to the users' email id when user is inactive
query := `select ca.image as last_deployed_image,
query := `select ca.id as ci_artifact_id, ca.parent_ci_artifact as parent_ci_artifact_id, ca.image as last_deployed_image,
case
when u.active = false then u.email_id || ' (inactive)'
else u.email_id
Expand Down Expand Up @@ -605,7 +607,7 @@ func (impl AppListingRepositoryImpl) FetchOtherEnvironment(appId int) ([]*bean.E
//TODO: remove infra metrics from query as it is not being used from here
query := `select pcwr.pipeline_id, pcwr.last_deployed, pcwr.latest_cd_workflow_runner_id, pcwr.environment_id, pcwr.deployment_app_delete_request,
e.cluster_id, e.environment_name, e.default as prod, e.description, ca.image as last_deployed_image,
u.email_id as last_deployed_by, elam.app_metrics, elam.infra_metrics, ap.status as app_status
u.email_id as last_deployed_by, elam.app_metrics, elam.infra_metrics, ap.status as app_status, ca.id as ci_artifact_id, ca.parent_ci_artifact as parent_ci_artifact_id
from (select *
from (select p.id as pipeline_id, p.app_id, cwr.started_on as last_deployed, cwr.triggered_by, cwr.id as latest_cd_workflow_runner_id,
cw.ci_artifact_id, p.environment_id, p.deployment_app_delete_request,
Expand Down
28 changes: 28 additions & 0 deletions internal/sql/repository/pipelineConfig/CiWorkflowRepository.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
package pipelineConfig

import (
"context"
"fmt"
"github.com/devtron-labs/devtron/internal/sql/repository/helper"
"github.com/go-pg/pg"
"go.opentelemetry.io/otel"
"go.uber.org/zap"
"time"
)
Expand Down Expand Up @@ -48,6 +50,7 @@ type CiWorkflowRepository interface {
ExistsByStatus(status string) (bool, error)
FindBuildTypeAndStatusDataOfLast1Day() []*BuildTypeCount
FIndCiWorkflowStatusesByAppId(appId int) ([]*CiWorkflowStatus, error)
FindGitTriggersByArtifactIds(ciArtifactIds []int, ctx context.Context) (ciWorkflows []ArtifactAndGitCommitMapping, err error)
}

type CiWorkflowRepositoryImpl struct {
Expand Down Expand Up @@ -120,6 +123,12 @@ type WorkflowWithArtifact struct {
ImagePathReservationIds []int `json:"image_path_reservation_ids" pg:",array"`
}

type ArtifactAndGitCommitMapping struct {
//Id int `sql:"id,pk"`
GitTriggers map[int]GitCommit `sql:"git_triggers"`
ArtifactId int `sql:"artifact_id"`
}

type GitCommit struct {
Commit string //git hash
Author string
Expand Down Expand Up @@ -287,6 +296,25 @@ func (impl *CiWorkflowRepositoryImpl) FindLastTriggeredWorkflowByArtifactId(ciAr
return workflow, err
}

func (impl *CiWorkflowRepositoryImpl) FindGitTriggersByArtifactIds(ciArtifactIds []int, ctx context.Context) (ciWorkflows []ArtifactAndGitCommitMapping, err error) {
_, span := otel.Tracer("envOverrideRepository").Start(ctx, "fetchCiArtifactAndGitTriggersMapping")
defer span.End()
if ciArtifactIds == nil {
// If ciArtifactIds is nil or empty, return an empty slice and nil error
return []ArtifactAndGitCommitMapping{}, err
}
var workflows []ArtifactAndGitCommitMapping
err = impl.dbConnection.Model().
Table("ci_workflow").
Column("ci_workflow.git_triggers").
ColumnExpr("cia.id as artifact_id").
Join("INNER JOIN ci_artifact cia on cia.ci_workflow_id = ci_workflow.id").
Where("cia.id in (?) ", pg.In(ciArtifactIds)).
Select(&workflows)

return workflows, nil
}

func (impl *CiWorkflowRepositoryImpl) FindAllLastTriggeredWorkflowByArtifactId(ciArtifactIds []int) (ciWorkflows []*CiWorkflow, err error) {
err = impl.dbConnection.Model(&ciWorkflows).
Column("ci_workflow.git_triggers", "ci_workflow.ci_pipeline_id", "CiPipeline", "cia.id").
Expand Down
145 changes: 141 additions & 4 deletions pkg/app/AppListingService.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ type AppListingService interface {
GetReleaseCount(appId, envId int) (int, error)

FetchAppsByEnvironmentV2(fetchAppListingRequest FetchAppListingRequest, w http.ResponseWriter, r *http.Request, token string) ([]*bean.AppEnvironmentContainer, int, error)
FetchOverviewAppsByEnvironment(envId, limit, offset int) (*OverviewAppsByEnvironmentBean, error)
FetchOverviewAppsByEnvironment(envId, limit, offset int, ctx context.Context) (*OverviewAppsByEnvironmentBean, error)
}

const (
Expand Down Expand Up @@ -99,6 +99,10 @@ type AppNameTypeIdContainer struct {
Type string `json:"type"`
AppId int `json:"appId"`
}
type CiArtifactWithParentArtifact struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should move at some common place

ParentCiArtifact int
CiArtifactId int
}

func (req FetchAppListingRequest) GetNamespaceClusterMapping() (namespaceClusterPair []*repository2.ClusterNamespacePair, clusterIds []int, err error) {
for _, ns := range req.Namespaces {
Expand Down Expand Up @@ -143,6 +147,7 @@ type AppListingServiceImpl struct {
dockerRegistryIpsConfigService dockerRegistry.DockerRegistryIpsConfigService
userRepository userrepository.UserRepository
deployedAppMetricsService deployedAppMetrics.DeployedAppMetricsService
ciWorkflowRepository pipelineConfig.CiWorkflowRepository
}

func NewAppListingServiceImpl(Logger *zap.SugaredLogger, appListingRepository repository.AppListingRepository,
Expand All @@ -153,7 +158,7 @@ func NewAppListingServiceImpl(Logger *zap.SugaredLogger, appListingRepository re
argoUserService argo.ArgoUserService, envOverrideRepository chartConfig.EnvConfigOverrideRepository,
chartRepository chartRepoRepository.ChartRepository, ciPipelineRepository pipelineConfig.CiPipelineRepository,
dockerRegistryIpsConfigService dockerRegistry.DockerRegistryIpsConfigService, userRepository userrepository.UserRepository,
deployedAppMetricsService deployedAppMetrics.DeployedAppMetricsService) *AppListingServiceImpl {
deployedAppMetricsService deployedAppMetrics.DeployedAppMetricsService, ciWorkflowRepository pipelineConfig.CiWorkflowRepository) *AppListingServiceImpl {
serviceImpl := &AppListingServiceImpl{
Logger: Logger,
appListingRepository: appListingRepository,
Expand All @@ -172,6 +177,7 @@ func NewAppListingServiceImpl(Logger *zap.SugaredLogger, appListingRepository re
dockerRegistryIpsConfigService: dockerRegistryIpsConfigService,
userRepository: userRepository,
deployedAppMetricsService: deployedAppMetricsService,
ciWorkflowRepository: ciWorkflowRepository,
}
return serviceImpl
}
Expand All @@ -198,7 +204,7 @@ const (
NonProduction = "Non-Production"
)

func (impl AppListingServiceImpl) FetchOverviewAppsByEnvironment(envId, limit, offset int) (*OverviewAppsByEnvironmentBean, error) {
func (impl AppListingServiceImpl) FetchOverviewAppsByEnvironment(envId, limit, offset int, ctx context.Context) (*OverviewAppsByEnvironmentBean, error) {
resp := &OverviewAppsByEnvironmentBean{}
env, err := impl.environmentRepository.FindById(envId)
if err != nil {
Expand Down Expand Up @@ -229,20 +235,47 @@ func (impl AppListingServiceImpl) FetchOverviewAppsByEnvironment(envId, limit, o
resp.CreatedBy = fmt.Sprintf("%s (inactive)", createdBy.EmailId)
}
}
envContainers, err := impl.appListingRepository.FetchOverviewAppsByEnvironment(envId, limit, offset)
envContainers, err := impl.appListingRepository.FetchOverviewAppsByEnvironment(envId, limit, offset, ctx)
if err != nil {
impl.Logger.Errorw("failed to fetch environment containers", "err", err, "envId", envId)
return resp, err
}

artifactDetails := make([]*CiArtifactWithParentArtifact, 0)
for _, envContainer := range envContainers {
lastDeployed, err := impl.appListingRepository.FetchLastDeployedImage(envContainer.AppId, envId)
if err != nil {
impl.Logger.Errorw("failed to fetch last deployed image", "err", err, "appId", envContainer.AppId, "envId", envId)
return resp, err
}

if lastDeployed != nil {
envContainer.LastDeployedImage = lastDeployed.LastDeployedImage
envContainer.LastDeployedBy = lastDeployed.LastDeployedBy
artifactDetails = append(artifactDetails, &CiArtifactWithParentArtifact{
ParentCiArtifact: lastDeployed.ParentCiArtifact, CiArtifactId: lastDeployed.CiArtifactId,
})
envContainer.CiArtifactId = lastDeployed.CiArtifactId
}
}

artifactWithGitCommit, err := impl.fetchCiArtifactAndGitTriggersMapping(artifactDetails, ctx)
if err != nil {
impl.Logger.Errorw("failed to fetch Artifacts to git Triggers ", "err", err, "envId", envId)
return resp, err
}
for _, envContainer := range envContainers {
if envContainer.CiArtifactId > 0 {
if commits, ok := artifactWithGitCommit[envContainer.CiArtifactId]; ok && commits != nil {
envContainer.Commits = commits
} else {
envContainer.Commits = []string{}

}

} else {
envContainer.Commits = []string{}

}
}
resp.Apps = envContainers
Expand Down Expand Up @@ -769,6 +802,85 @@ func (impl AppListingServiceImpl) FetchAppStageStatus(appId int, appType int) ([
appStageStatuses, err := impl.appListingRepository.FetchAppStageStatus(appId, appType)
return appStageStatuses, err
}
func (impl AppListingServiceImpl) fetchCiArtifactAndGitTriggersMapping(artifacts []*CiArtifactWithParentArtifact, ctx context.Context) (CiArtifactAndGitCommitsMap map[int][]string, err error) {
newCtx, span := otel.Tracer("envOverrideRepository").Start(ctx, "fetchCiArtifactAndGitTriggersMapping")
defer span.End()
ciArtifactIds, artifactChildParentMap, err := mapCiArtifactsWithChild(artifacts)
if err != nil {
// Log the error along with the artifact IDs that caused it.
impl.Logger.Errorw("error in getting the set of ciArtifactIds", "ArtifactIds", ciArtifactIds, "err", err)
return nil, err // Return nil map and the encountered error.
}

// Retrieve workflows associated with the artifact IDs, handling any potential error.
artifactsWithGitTriggers, err := impl.ciWorkflowRepository.FindGitTriggersByArtifactIds(ciArtifactIds, newCtx)
if err != nil {
// Log the error along with the artifact IDs that caused it.
impl.Logger.Errorw("error retrieving GitTriggers of the CiWorkflows", "ciArtifactIds", ciArtifactIds, "err", err)
return nil, err // Return nil map and the encountered error.
}

gitCommitsWithChildArtifactMap, err := mapArtifactToGitCommits(artifactsWithGitTriggers, artifactChildParentMap)
if err != nil {
// Log the error along with the artifact child IDs .
impl.Logger.Errorw("error retrieving GitCommits of these ids", "CiArtifactIds", ciArtifactIds, "err", err)
return nil, err
}

return gitCommitsWithChildArtifactMap, nil
}

func mapCiArtifactsWithChild(artifacts []*CiArtifactWithParentArtifact) (ciArtifactIds []int, artifactChildParentMap map[int]int, err error) {
artifactChildParentMap = make(map[int]int)

uniqueArtifactsMap := make(map[int]struct{})
for _, artifact := range artifacts {
if artifact.ParentCiArtifact > 0 {
artifactChildParentMap[artifact.CiArtifactId] = artifact.ParentCiArtifact
} else {
artifactChildParentMap[artifact.CiArtifactId] = artifact.CiArtifactId
}

if _, ok := uniqueArtifactsMap[artifactChildParentMap[artifact.CiArtifactId]]; !ok {
ciArtifactIds = append(ciArtifactIds, artifactChildParentMap[artifact.CiArtifactId])
uniqueArtifactsMap[artifactChildParentMap[artifact.CiArtifactId]] = struct{}{}
}
}
return ciArtifactIds, artifactChildParentMap, err

}

// mapArtifactToGitCommits
func mapArtifactToGitCommits(artifactsWithGitTriggers []pipelineConfig.ArtifactAndGitCommitMapping, artifactChildParentMap map[int]int) (ciArtifactAndGitCommitsMap map[int][]string, err error) {

gitTriggers := make(map[int][]string)

for _, artifactWithGitTriggers := range artifactsWithGitTriggers {
if artifactWithGitTriggers.ArtifactId == 0 {
continue
}

// Check if gitTriggers contains the key artifactWithGitTriggers.ArtifactId
if _, ok := gitTriggers[artifactWithGitTriggers.ArtifactId]; !ok {
for _, gitCommit := range artifactWithGitTriggers.GitTriggers {
gitTriggers[artifactWithGitTriggers.ArtifactId] = append(gitTriggers[artifactWithGitTriggers.ArtifactId], gitCommit.Commit)
}
}
}

ciArtifactAndGitCommitsMap = make(map[int][]string)

for child, parent := range artifactChildParentMap {
gitCommits, exists := gitTriggers[parent]
if !exists {
ciArtifactAndGitCommitsMap[child] = []string{}
} else {
ciArtifactAndGitCommitsMap[child] = append(ciArtifactAndGitCommitsMap[child], gitCommits...)
}
}
return ciArtifactAndGitCommitsMap, err

}

func (impl AppListingServiceImpl) FetchOtherEnvironment(ctx context.Context, appId int) ([]*bean.Environment, error) {
newCtx, span := otel.Tracer("appListingRepository").Start(ctx, "FetchOtherEnvironment")
Expand All @@ -793,6 +905,23 @@ func (impl AppListingServiceImpl) FetchOtherEnvironment(ctx context.Context, app
impl.Logger.Errorw("error in fetching latest chart", "err", err)
return envs, err
}
ciArtifactsWithParent := make([]*CiArtifactWithParentArtifact, 0)
for _, env := range envs {

if env.CiArtifactId > 0 {
ciArtifactsWithParent = append(ciArtifactsWithParent, &CiArtifactWithParentArtifact{
ParentCiArtifact: env.ParentCiArtifactId, CiArtifactId: env.CiArtifactId,
})
}

}

gitCommitsWithArtifacts, err := impl.fetchCiArtifactAndGitTriggersMapping(ciArtifactsWithParent, ctx)
if err != nil {
impl.Logger.Errorw("Error in fetching the git commits of the ciArtifacts", "err", err, "ciArtifactsWithParent", ciArtifactsWithParent)
return envs, err
}

for _, env := range envs {
newCtx, span = otel.Tracer("envOverrideRepository").Start(newCtx, "FindLatestChartForAppByAppIdAndEnvId")
envOverride, err := impl.envOverrideRepository.FindLatestChartForAppByAppIdAndEnvId(appId, env.EnvironmentId)
Expand All @@ -806,6 +935,14 @@ func (impl AppListingServiceImpl) FetchOtherEnvironment(ctx context.Context, app
} else {
env.ChartRefId = chart.ChartRefId
}

if gitCommits, exists := gitCommitsWithArtifacts[env.CiArtifactId]; exists {
env.Commits = gitCommits
} else {
gitCommits = make([]string, 0)
env.Commits = gitCommits
}

if env.AppMetrics == nil {
env.AppMetrics = &appLevelAppMetrics
}
Expand Down
Loading