Skip to content

Commit

Permalink
feat: added deployment failed message in deployment history (#5845)
Browse files Browse the repository at this point in the history
* feat: added updated api specs for orchestrator/application/deployment-history api

* feat: added deployment failed message in deployment history

* updated migration version

* FailedStatusUpdateOption signature updated

* fixed GetEmailById method

* updated migration version

* updated migration version

* updated migration message formatting

* handled rbac for the source changing to external-ci

* change in patch source info of cdpipeline config object

* resolved the comments after 1st review

---------

Co-authored-by: Rajeev <rajeev.ranjan@devtron.ai>
  • Loading branch information
Ash-exp and RajeevRanjan27 authored Sep 18, 2024
1 parent 3fa1069 commit 85710ec
Show file tree
Hide file tree
Showing 56 changed files with 1,533 additions and 1,075 deletions.
2 changes: 1 addition & 1 deletion api/appStore/deployment/CommonDeploymentRestHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ func (handler *CommonDeploymentRestHandlerImpl) GetDeploymentHistory(w http.Resp
}
//rbac block ends here

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
defer cancel()
res, err := handler.appStoreDeploymentService.GetDeploymentHistory(ctx, installedAppDto)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion api/auth/user/UserRestHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -1038,7 +1038,7 @@ func (handler UserRestHandlerImpl) SyncOrchestratorToCasbin(w http.ResponseWrite
common.WriteJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized)
return
}
userEmailId, err := handler.userService.GetEmailById(userId)
userEmailId, err := handler.userService.GetActiveEmailById(userId)
if err != nil {
handler.logger.Errorw("service err, SyncOrchestratorToCasbin", "err", err, "userId", userId)
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
Expand Down
784 changes: 397 additions & 387 deletions api/helm-app/gRPC/applist.pb.go

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions api/helm-app/gRPC/applist.proto
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ message HelmAppDeploymentDetail {
google.protobuf.Timestamp deployedAt = 4;
string deployedBy = 5;
string status = 6;
string message = 7;
}

message HelmAppDeploymentHistory {
Expand Down
126 changes: 49 additions & 77 deletions api/helm-app/gRPC/applist_grpc.pb.go

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions api/restHandler/UserAttributesRestHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func (handler *UserAttributesRestHandlerImpl) AddUserAttributes(w http.ResponseW
// common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
// return
//}
emailId, err := handler.userService.GetEmailById(userId)
emailId, err := handler.userService.GetActiveEmailById(userId)
if err != nil {
handler.logger.Errorw("request err, UpdateUserAttributes", "err", err, "payload", dto)
common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
Expand Down Expand Up @@ -119,7 +119,7 @@ func (handler *UserAttributesRestHandlerImpl) UpdateUserAttributes(w http.Respon
// return
//}

emailId, err := handler.userService.GetEmailById(userId)
emailId, err := handler.userService.GetActiveEmailById(userId)
if err != nil {
handler.logger.Errorw("request err, UpdateUserAttributes", "err", err, "payload", dto)
common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
Expand Down Expand Up @@ -165,7 +165,7 @@ func (handler *UserAttributesRestHandlerImpl) GetUserAttribute(w http.ResponseWr

dto := attributes.UserAttributesDto{}

emailId, err := handler.userService.GetEmailById(userId)
emailId, err := handler.userService.GetActiveEmailById(userId)
if err != nil {
handler.logger.Errorw("request err, UpdateUserAttributes", "err", err, "payload", dto)
common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,14 +237,26 @@ func (handler *PipelineConfigRestHandlerImpl) CreateCdPipeline(w http.ResponseWr
common.WriteJsonResp(w, fmt.Errorf("unauthorized user"), "Unauthorized User", http.StatusForbidden)
return
}
ok := true
for _, deploymentPipeline := range cdPipeline.Pipelines {
if deploymentPipeline.EnvironmentId > 0 {
object := handler.enforcerUtil.GetAppRBACByAppNameAndEnvId(app.AppName, deploymentPipeline.EnvironmentId)
handler.Logger.Debugw("Triggered Request By:", "object", object)
if ok := handler.enforcer.Enforce(token, casbin.ResourceEnvironment, casbin.ActionCreate, object); !ok {
common.WriteJsonResp(w, fmt.Errorf("unauthorized user"), "Unauthorized User", http.StatusForbidden)
//handling case of change of source from CI_PIPELINE to external-ci type (other change of type any -> any has been handled in ci-pipeline/patch api)
if deploymentPipeline.IsSwitchCiPipelineRequest() {
cdPipelines, err := handler.getCdPipelinesForCdPatchRbac(deploymentPipeline)
if err != nil && !errors.Is(err, pg.ErrNoRows) {
handler.Logger.Errorw("error in finding cdPipelines by deploymentPipeline", "deploymentPipeline", deploymentPipeline, "err", err)
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
return
}
ok = handler.checkCiPatchAccess(token, resourceName, cdPipelines)

} else if deploymentPipeline.EnvironmentId > 0 {
object := handler.enforcerUtil.GetAppRBACByAppNameAndEnvId(app.AppName, deploymentPipeline.EnvironmentId)
handler.Logger.Debugw("Triggered Request By:", "object", object)
ok = handler.enforcer.Enforce(token, casbin.ResourceEnvironment, casbin.ActionCreate, object)
}
if !ok {
common.WriteJsonResp(w, fmt.Errorf("unauthorized user"), "Unauthorized User", http.StatusForbidden)
return
}
}
//RBAC
Expand Down Expand Up @@ -2561,3 +2573,28 @@ func (handler *PipelineConfigRestHandlerImpl) GetGitOpsConfiguration(w http.Resp
}
common.WriteJsonResp(w, nil, appGitOpsConfig, http.StatusOK)
}

// this is being used for getting all cdPipelines in the case of changing the source from any [except] -> external-ci
func (handler *PipelineConfigRestHandlerImpl) getCdPipelinesForCdPatchRbac(deploymentPipeline *bean.CDPipelineConfigObject) (cdPipelines []*pipelineConfig.Pipeline, err error) {
componentId, componentType := deploymentPipeline.PatchSourceInfo()
// the appWorkflowId can be taken from patchRequest.AppWorkflowId but doing this can make 2 sources of truth to find the workflow
sourceAppWorkflowMapping, err := handler.appWorkflowService.FindWFMappingByComponent(componentType, componentId)
if err != nil {
handler.Logger.Errorw("error in finding the appWorkflowMapping using componentId and componentType", "componentType", componentType, "componentId", componentId, "err", err)
return nil, err
}
cdPipelineWFMappings, err := handler.appWorkflowService.FindWFCDMappingsByWorkflowId(sourceAppWorkflowMapping.AppWorkflowId)
if err != nil {
handler.Logger.Errorw("error in finding the appWorkflowMappings of cd pipeline for an appWorkflow", "appWorkflowId", sourceAppWorkflowMapping.AppWorkflowId, "err", err)
return cdPipelines, err
}
if len(cdPipelineWFMappings) == 0 {
return
}

cdPipelineIds := make([]int, 0, len(cdPipelineWFMappings))
for _, cdWfMapping := range cdPipelineWFMappings {
cdPipelineIds = append(cdPipelineIds, cdWfMapping.ComponentId)
}
return handler.pipelineRepository.FindByIdsIn(cdPipelineIds)
}
2 changes: 1 addition & 1 deletion cmd/external-app/wire_gen.go

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

114 changes: 33 additions & 81 deletions internal/sql/repository/pipelineConfig/CdWorfkflowRepository.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,11 @@ import (
"context"
"errors"
"fmt"
"github.com/devtron-labs/common-lib/utils/k8s/health"
apiBean "github.com/devtron-labs/devtron/api/bean"
argoApplication "github.com/devtron-labs/devtron/client/argocdServer/bean"
"github.com/devtron-labs/devtron/client/gitSensor"
"github.com/devtron-labs/devtron/internal/sql/repository"
repository2 "github.com/devtron-labs/devtron/internal/sql/repository/imageTagging"
"github.com/devtron-labs/devtron/internal/sql/repository/pipelineConfig/bean/cdWorkflow"
"github.com/devtron-labs/devtron/internal/util"
"github.com/devtron-labs/devtron/pkg/sql"
"github.com/go-pg/pg"
Expand Down Expand Up @@ -84,46 +83,12 @@ type CdWorkflowRepositoryImpl struct {
logger *zap.SugaredLogger
}

type WorkflowStatus int

const (
WF_UNKNOWN WorkflowStatus = iota
REQUEST_ACCEPTED
ENQUEUED
QUE_ERROR
WF_STARTED
DROPPED_STALE
DEQUE_ERROR
TRIGGER_ERROR
)

const (
WorkflowStarting = "Starting"
WorkflowInQueue = "Queued"
WorkflowInitiated = "Initiating"
WorkflowInProgress = "Progressing"
WorkflowAborted = "Aborted"
WorkflowFailed = "Failed"
WorkflowSucceeded = "Succeeded"
WorkflowTimedOut = "TimedOut"
WorkflowUnableToFetchState = "UnableToFetch"
WorkflowTypeDeploy = "DEPLOY"
WorkflowTypePre = "PRE"
WorkflowTypePost = "POST"
)

var WfrTerminalStatusList = []string{WorkflowAborted, WorkflowFailed, WorkflowSucceeded, argoApplication.HIBERNATING, string(health.HealthStatusHealthy), string(health.HealthStatusDegraded)}

func (a WorkflowStatus) String() string {
return [...]string{"WF_UNKNOWN", "REQUEST_ACCEPTED", "ENQUEUED", "QUE_ERROR", "WF_STARTED", "DROPPED_STALE", "DEQUE_ERROR", "TRIGGER_ERROR"}[a]
}

type CdWorkflow struct {
tableName struct{} `sql:"cd_workflow" pg:",discard_unknown_columns"`
Id int `sql:"id,pk"`
CiArtifactId int `sql:"ci_artifact_id"`
PipelineId int `sql:"pipeline_id"`
WorkflowStatus WorkflowStatus `sql:"workflow_status,notnull"`
tableName struct{} `sql:"cd_workflow" pg:",discard_unknown_columns"`
Id int `sql:"id,pk"`
CiArtifactId int `sql:"ci_artifact_id"`
PipelineId int `sql:"pipeline_id"`
WorkflowStatus cdWorkflow.WorkflowStatus `sql:"workflow_status,notnull"`
Pipeline *Pipeline
CiArtifact *repository.CiArtifact
CdWorkflowRunner []CdWorkflowRunner
Expand Down Expand Up @@ -151,53 +116,40 @@ type CdWorkflowConfig struct {
CdArtifactLocationFormat string `sql:"cd_artifact_location_format"`
}

type WorkflowExecutorType string

var ErrorDeploymentSuperseded = errors.New(NEW_DEPLOYMENT_INITIATED)

const (
WORKFLOW_EXECUTOR_TYPE_AWF = "AWF"
WORKFLOW_EXECUTOR_TYPE_SYSTEM = "SYSTEM"
NEW_DEPLOYMENT_INITIATED = "A new deployment was initiated before this deployment completed!"
PIPELINE_DELETED = "The pipeline has been deleted!"
FOUND_VULNERABILITY = "Found vulnerability on image"
GITOPS_REPO_NOT_CONFIGURED = "GitOps repository is not configured for the app"
)

type CdWorkflowRunnerWithExtraFields struct {
CdWorkflowRunner
TotalCount int
}

type CdWorkflowRunner struct {
tableName struct{} `sql:"cd_workflow_runner" pg:",discard_unknown_columns"`
Id int `sql:"id,pk"`
Name string `sql:"name"`
WorkflowType apiBean.WorkflowType `sql:"workflow_type"` // pre,post,deploy
ExecutorType WorkflowExecutorType `sql:"executor_type"` // awf, system
Status string `sql:"status"`
PodStatus string `sql:"pod_status"`
Message string `sql:"message"`
StartedOn time.Time `sql:"started_on"`
FinishedOn time.Time `sql:"finished_on"`
Namespace string `sql:"namespace"`
LogLocation string `sql:"log_file_path"`
TriggeredBy int32 `sql:"triggered_by"`
CdWorkflowId int `sql:"cd_workflow_id"`
PodName string `sql:"pod_name"`
BlobStorageEnabled bool `sql:"blob_storage_enabled,notnull"`
RefCdWorkflowRunnerId int `sql:"ref_cd_workflow_runner_id,notnull"`
ImagePathReservationIds []int `sql:"image_path_reservation_ids" pg:",array,notnull"`
ReferenceId *string `sql:"reference_id"`
tableName struct{} `sql:"cd_workflow_runner" pg:",discard_unknown_columns"`
Id int `sql:"id,pk"`
Name string `sql:"name"`
WorkflowType apiBean.WorkflowType `sql:"workflow_type"` // pre,post,deploy
ExecutorType cdWorkflow.WorkflowExecutorType `sql:"executor_type"` // awf, system
Status string `sql:"status"`
PodStatus string `sql:"pod_status"`
Message string `sql:"message"`
StartedOn time.Time `sql:"started_on"`
FinishedOn time.Time `sql:"finished_on"`
Namespace string `sql:"namespace"`
LogLocation string `sql:"log_file_path"`
TriggeredBy int32 `sql:"triggered_by"`
CdWorkflowId int `sql:"cd_workflow_id"`
PodName string `sql:"pod_name"`
BlobStorageEnabled bool `sql:"blob_storage_enabled,notnull"`
RefCdWorkflowRunnerId int `sql:"ref_cd_workflow_runner_id,notnull"`
ImagePathReservationIds []int `sql:"image_path_reservation_ids" pg:",array,notnull"`
ReferenceId *string `sql:"reference_id"`
CdWorkflow *CdWorkflow
sql.AuditLog
}

func (c *CdWorkflowRunner) IsExternalRun() bool {
var isExtCluster bool
if c.WorkflowType == WorkflowTypePre {
if c.WorkflowType == cdWorkflow.WorkflowTypePre {
isExtCluster = c.CdWorkflow.Pipeline.RunPreStageInEnv
} else if c.WorkflowType == WorkflowTypePost {
} else if c.WorkflowType == cdWorkflow.WorkflowTypePost {
isExtCluster = c.CdWorkflow.Pipeline.RunPostStageInEnv
}
return isExtCluster
Expand Down Expand Up @@ -492,7 +444,7 @@ func (impl *CdWorkflowRepositoryImpl) FindLastUnFailedProcessedRunner(appId int,
Where("p.environment_id = ?", environmentId).
Where("p.app_id = ?", appId).
Where("cd_workflow_runner.workflow_type = ?", apiBean.CD_WORKFLOW_TYPE_DEPLOY).
Where("cd_workflow_runner.status NOT IN (?)", pg.In([]string{WorkflowInitiated, WorkflowInQueue, WorkflowFailed})).
Where("cd_workflow_runner.status NOT IN (?)", pg.In([]string{cdWorkflow.WorkflowInitiated, cdWorkflow.WorkflowInQueue, cdWorkflow.WorkflowFailed})).
Order("cd_workflow_runner.id DESC").
Join("inner join cd_workflow wf on wf.id = cd_workflow_runner.cd_workflow_id").
Join("inner join pipeline p on p.id = wf.pipeline_id").
Expand Down Expand Up @@ -557,7 +509,7 @@ func (impl *CdWorkflowRepositoryImpl) GetPreviousQueuedRunners(cdWfrId, pipeline
Where("workflow_type = ?", apiBean.CD_WORKFLOW_TYPE_DEPLOY).
Where("cd_workflow.pipeline_id = ?", pipelineId).
Where("cd_workflow_runner.id < ?", cdWfrId).
Where("cd_workflow_runner.status = ?", WorkflowInQueue).
Where("cd_workflow_runner.status = ?", cdWorkflow.WorkflowInQueue).
Select()
return cdWfrs, err
}
Expand All @@ -567,7 +519,7 @@ func (impl *CdWorkflowRepositoryImpl) UpdateRunnerStatusToFailedForIds(errMsg st
return nil
}
_, err := impl.dbConnection.Model((*CdWorkflowRunner)(nil)).
Set("status = ?", WorkflowFailed).
Set("status = ?", cdWorkflow.WorkflowFailed).
Set("finished_on = ?", time.Now()).
Set("updated_on = ?", time.Now()).
Set("updated_by = ?", triggeredBy).
Expand Down Expand Up @@ -763,8 +715,8 @@ func (impl *CdWorkflowRepositoryImpl) FetchArtifactsByCdPipelineIdV2(listingFilt

func (impl *CdWorkflowRepositoryImpl) GetLatestTriggersOfHelmPipelinesStuckInNonTerminalStatuses(getPipelineDeployedWithinHours int) ([]*CdWorkflowRunner, error) {
var wfrList []*CdWorkflowRunner
excludedStatusList := WfrTerminalStatusList
excludedStatusList = append(excludedStatusList, WorkflowInitiated, WorkflowInQueue, WorkflowStarting)
excludedStatusList := cdWorkflow.WfrTerminalStatusList
excludedStatusList = append(excludedStatusList, cdWorkflow.WorkflowInitiated, cdWorkflow.WorkflowInQueue, cdWorkflow.WorkflowStarting)
err := impl.dbConnection.
Model(&wfrList).
Column("cd_workflow_runner.*", "CdWorkflow.id", "CdWorkflow.pipeline_id", "CdWorkflow.Pipeline.id", "CdWorkflow.Pipeline.app_id", "CdWorkflow.Pipeline.environment_id", "CdWorkflow.Pipeline.deployment_app_name", "CdWorkflow.Pipeline.deleted", "CdWorkflow.Pipeline.Environment").
Expand All @@ -776,7 +728,7 @@ func (impl *CdWorkflowRepositoryImpl) GetLatestTriggersOfHelmPipelinesStuckInNon
" INNER JOIN cd_workflow_runner on cd_workflow.id = cd_workflow_runner.cd_workflow_id"+
" WHERE cd_workflow_runner.status != ?"+
" GROUP BY cd_workflow.pipeline_id"+
" ORDER BY cd_workflow.pipeline_id desc)", WorkflowInQueue).
" ORDER BY cd_workflow.pipeline_id desc)", cdWorkflow.WorkflowInQueue).
Where("(cd_workflow__pipeline.deployment_app_type=? or dc.deployment_app_type=?)", util.PIPELINE_DEPLOYMENT_TYPE_HELM, util.PIPELINE_DEPLOYMENT_TYPE_HELM).
Where("cd_workflow_runner.started_on > NOW() - INTERVAL '? hours'", getPipelineDeployedWithinHours).
Where("cd_workflow__pipeline.deleted=?", false).
Expand Down
Loading

0 comments on commit 85710ec

Please sign in to comment.