Skip to content

Commit

Permalink
fix: terminate sync if in progress (#4946)
Browse files Browse the repository at this point in the history
* terminate operation before sync

* using method for checking manual sync

* terminating only in case of error

* handling termination error

* updating sync function
  • Loading branch information
iamayushm authored Apr 25, 2024
1 parent 3f8affc commit f6adb1a
Show file tree
Hide file tree
Showing 10 changed files with 79 additions and 23 deletions.
58 changes: 49 additions & 9 deletions client/argocdServer/ArgoClientWrapperService.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/devtron-labs/devtron/client/argocdServer/application"
"github.com/devtron-labs/devtron/client/argocdServer/bean"
"github.com/devtron-labs/devtron/client/argocdServer/repository"
"github.com/devtron-labs/devtron/internal/util"
"github.com/devtron-labs/devtron/pkg/deployment/gitOps/config"
"github.com/devtron-labs/devtron/pkg/deployment/gitOps/git"
"github.com/devtron-labs/devtron/util/retryFunc"
Expand All @@ -25,6 +26,14 @@ type ACDConfig struct {
ArgoCDAutoSyncEnabled bool `env:"ARGO_AUTO_SYNC_ENABLED" envDefault:"true"` // will gradually switch this flag to false in enterprise
}

func (config *ACDConfig) IsManualSyncEnabled() bool {
return config.ArgoCDAutoSyncEnabled == false
}

func (config *ACDConfig) IsAutoSyncEnabled() bool {
return config.ArgoCDAutoSyncEnabled == true
}

func GetACDDeploymentConfig() (*ACDConfig, error) {
cfg := &ACDConfig{}
err := env.Parse(cfg)
Expand All @@ -34,6 +43,10 @@ func GetACDDeploymentConfig() (*ACDConfig, error) {
return cfg, err
}

const (
ErrorOperationAlreadyInProgress = "another operation is already in progress" // this string is returned from argocd
)

type ArgoClientWrapperService interface {

// GetArgoAppWithNormalRefresh - refresh app at argocd side
Expand Down Expand Up @@ -96,17 +109,45 @@ func (impl *ArgoClientWrapperServiceImpl) GetArgoAppWithNormalRefresh(context co
}

func (impl *ArgoClientWrapperServiceImpl) SyncArgoCDApplicationIfNeededAndRefresh(context context.Context, argoAppName string) error {

impl.logger.Info("argocd manual sync for app started", "argoAppName", argoAppName)
if !impl.ACDConfig.ArgoCDAutoSyncEnabled {
if impl.ACDConfig.IsManualSyncEnabled() {

impl.logger.Debugw("syncing argocd app as manual sync is enabled", "argoAppName", argoAppName)
revision := "master"
pruneResources := true
_, syncErr := impl.acdClient.Sync(context, &application2.ApplicationSyncRequest{Name: &argoAppName, Revision: &revision, Prune: &pruneResources})
_, syncErr := impl.acdClient.Sync(context, &application2.ApplicationSyncRequest{Name: &argoAppName,
Revision: &revision,
Prune: &pruneResources,
})
if syncErr != nil {
impl.logger.Errorw("cannot get application with refresh", "app", argoAppName)
return syncErr
impl.logger.Errorw("error in syncing argoCD app", "app", argoAppName, "err", syncErr)
statusCode, msg := util.GetClientDetailedError(syncErr)
if statusCode.IsFailedPreconditionCode() && msg == ErrorOperationAlreadyInProgress {
impl.logger.Info("terminating ongoing sync operation and retrying manual sync", "argoAppName", argoAppName)
_, terminationErr := impl.acdClient.TerminateOperation(context, &application2.OperationTerminateRequest{
Name: &argoAppName,
})
if terminationErr != nil {
impl.logger.Errorw("error in terminating sync operation")
return fmt.Errorf("error in terminating existing sync, err: %w", terminationErr)
}
_, syncErr = impl.acdClient.Sync(context, &application2.ApplicationSyncRequest{Name: &argoAppName,
Revision: &revision,
Prune: &pruneResources,
RetryStrategy: &v1alpha1.RetryStrategy{
Limit: 1,
},
})
if syncErr != nil {
impl.logger.Errorw("error in syncing argoCD app", "app", argoAppName, "err", syncErr)
return syncErr
}
} else {
return syncErr
}
}
impl.logger.Debugw("argocd sync completed", "argoAppName", argoAppName)
impl.logger.Infow("argocd sync completed", "argoAppName", argoAppName)
}
refreshErr := impl.GetArgoAppWithNormalRefresh(context, argoAppName)
if refreshErr != nil {
Expand All @@ -129,10 +170,9 @@ func (impl *ArgoClientWrapperServiceImpl) UpdateArgoCDSyncModeIfNeeded(ctx conte
}

func (impl *ArgoClientWrapperServiceImpl) isArgoAppSyncModeMigrationNeeded(argoApplication *v1alpha1.Application) bool {
if !impl.ACDConfig.ArgoCDAutoSyncEnabled && argoApplication.Spec.SyncPolicy.Automated != nil {
if impl.ACDConfig.IsManualSyncEnabled() && argoApplication.Spec.SyncPolicy.Automated != nil {
return true
}
if impl.ACDConfig.ArgoCDAutoSyncEnabled && argoApplication.Spec.SyncPolicy.Automated == nil {
} else if impl.ACDConfig.IsAutoSyncEnabled() && argoApplication.Spec.SyncPolicy.Automated == nil {
return true
}
return false
Expand All @@ -141,7 +181,7 @@ func (impl *ArgoClientWrapperServiceImpl) isArgoAppSyncModeMigrationNeeded(argoA
func (impl *ArgoClientWrapperServiceImpl) CreateRequestForArgoCDSyncModeUpdateRequest(argoApplication *v1alpha1.Application) *v1alpha1.Application {
// set automated field in update request
var automated *v1alpha1.SyncPolicyAutomated
if impl.ACDConfig.ArgoCDAutoSyncEnabled {
if impl.ACDConfig.IsAutoSyncEnabled() {
automated = &v1alpha1.SyncPolicyAutomated{
Prune: true,
}
Expand Down
16 changes: 16 additions & 0 deletions client/argocdServer/application/Application.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ type ServiceClient interface {

// Delete deletes an application
Delete(ctx context.Context, query *application.ApplicationDeleteRequest) (*application.ApplicationResponse, error)

TerminateOperation(ctx context.Context, query *application.OperationTerminateRequest) (*application.OperationTerminateResponse, error)
}

type ServiceClientImpl struct {
Expand Down Expand Up @@ -349,3 +351,17 @@ func (c ServiceClientImpl) buildPodMetadata(resp *v1alpha1.ApplicationTree, resp
}
return
}

func (c ServiceClientImpl) TerminateOperation(ctxt context.Context, query *application.OperationTerminateRequest) (*application.OperationTerminateResponse, error) {
ctx, cancel := context.WithTimeout(ctxt, argoApplication.TimeoutFast)
defer cancel()
token, ok := ctxt.Value("token").(string)
if !ok {
return nil, argoApplication.NewErrUnauthorized("Unauthorized")
}
conn := c.argoCDConnectionManager.GetConnection(token)
defer util.Close(conn, c.logger)
asc := application.NewApplicationServiceClient(conn)
resp, err := asc.TerminateOperation(ctx, query)
return resp, err
}
4 changes: 2 additions & 2 deletions pkg/app/AppService.go
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ func (impl *AppServiceImpl) CheckIfPipelineUpdateEventIsValidForAppStore(gitOpsA
// drop event
return isValid, installedAppVersionHistory, appId, envId, nil
}
if !impl.acdConfig.ArgoCDAutoSyncEnabled {
if impl.acdConfig.IsManualSyncEnabled() {
isArgoAppSynced := impl.pipelineStatusTimelineService.GetArgoAppSyncStatusForAppStore(installedAppVersionHistory.Id)
if !isArgoAppSynced {
return isValid, installedAppVersionHistory, appId, envId, nil
Expand Down Expand Up @@ -491,7 +491,7 @@ func (impl *AppServiceImpl) CheckIfPipelineUpdateEventIsValid(argoAppName, gitHa
// drop event
return isValid, pipeline, cdWfr, pipelineOverride, nil
}
if !impl.acdConfig.ArgoCDAutoSyncEnabled {
if impl.acdConfig.IsManualSyncEnabled() {
// if manual sync, proceed only if ARGOCD_SYNC_COMPLETED timeline is created
isArgoAppSynced := impl.pipelineStatusTimelineService.GetArgoAppSyncStatus(cdWfr.Id)
if !isArgoAppSynced {
Expand Down
2 changes: 1 addition & 1 deletion pkg/app/ManifestPushService.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ func (impl *GitOpsManifestPushServiceImpl) PushChart(manifestPushTemplate *bean.
gitCommitTimeline := impl.pipelineStatusTimelineService.GetTimelineDbObjectByTimelineStatusAndTimelineDescription(manifestPushTemplate.WorkflowRunnerId, 0, pipelineConfig.TIMELINE_STATUS_GIT_COMMIT, "Git commit done successfully.", manifestPushTemplate.UserId, time.Now())

timelines := []*pipelineConfig.PipelineStatusTimeline{gitCommitTimeline}
if !impl.acdConfig.ArgoCDAutoSyncEnabled {
if impl.acdConfig.IsManualSyncEnabled() {
// if manual sync is enabled, add ARGOCD_SYNC_INITIATED_TIMELINE
argoCDSyncInitiatedTimeline := impl.pipelineStatusTimelineService.GetTimelineDbObjectByTimelineStatusAndTimelineDescription(manifestPushTemplate.WorkflowRunnerId, 0, pipelineConfig.TIMELINE_STATUS_ARGOCD_SYNC_INITIATED, "argocd sync initiated.", manifestPushTemplate.UserId, time.Now())
timelines = append(timelines, argoCDSyncInitiatedTimeline)
Expand Down
2 changes: 1 addition & 1 deletion pkg/appStore/chartGroup/ChartGroupService.go
Original file line number Diff line number Diff line change
Expand Up @@ -993,7 +993,7 @@ func (impl *ChartGroupServiceImpl) performDeployStageOnAcd(installedAppVersion *
GetTimelineDbObjectByTimelineStatusAndTimelineDescription(0, installedAppVersion.InstalledAppVersionHistoryId, pipelineConfig.TIMELINE_STATUS_GIT_COMMIT, "Git commit done successfully.", installedAppVersion.UserId, time.Now())

timelines := []*pipelineConfig.PipelineStatusTimeline{GitCommitSuccessTimeline}
if !impl.acdConfig.ArgoCDAutoSyncEnabled {
if impl.acdConfig.IsManualSyncEnabled() {
ArgocdSyncInitiatedTimeline := impl.pipelineStatusTimelineService.
GetTimelineDbObjectByTimelineStatusAndTimelineDescription(0, installedAppVersion.InstalledAppVersionHistoryId, pipelineConfig.TIMELINE_STATUS_ARGOCD_SYNC_INITIATED, "ArgoCD sync initiated.", installedAppVersion.UserId, time.Now())

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ func (impl *AppStoreDeploymentServiceImpl) InstallApp(installAppVersionRequest *
}
if util.IsAcdApp(installAppVersionRequest.DeploymentAppType) {
_ = impl.fullModeDeploymentService.SaveTimelineForHelmApps(installAppVersionRequest, pipelineConfig.TIMELINE_STATUS_GIT_COMMIT, "Git commit done successfully.", time.Now(), tx)
if !impl.aCDConfig.ArgoCDAutoSyncEnabled {
if impl.aCDConfig.IsManualSyncEnabled() {
_ = impl.fullModeDeploymentService.SaveTimelineForHelmApps(installAppVersionRequest, pipelineConfig.TIMELINE_STATUS_ARGOCD_SYNC_INITIATED, "argocd sync initiated.", time.Now(), tx)
}
}
Expand Down Expand Up @@ -695,7 +695,7 @@ func (impl *AppStoreDeploymentServiceImpl) UpdateInstalledApp(ctx context.Contex

upgradeAppRequest.GitHash = gitOpsResponse.GitHash
_ = impl.fullModeDeploymentService.SaveTimelineForHelmApps(upgradeAppRequest, pipelineConfig.TIMELINE_STATUS_GIT_COMMIT, "Git commit done successfully.", time.Now(), tx)
if !impl.aCDConfig.ArgoCDAutoSyncEnabled {
if impl.aCDConfig.IsManualSyncEnabled() {
_ = impl.fullModeDeploymentService.SaveTimelineForHelmApps(upgradeAppRequest, pipelineConfig.TIMELINE_STATUS_ARGOCD_SYNC_INITIATED, "Argocd sync initiated", time.Now(), tx)
}
installedAppVersionHistory.GitHash = gitOpsResponse.GitHash
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ func (impl *FullModeDeploymentServiceImpl) InstallApp(installAppVersionRequest *
impl.Logger.Errorw("error in getting the argo application with normal refresh", "err", err)
return nil, err
}
if !impl.acdConfig.ArgoCDAutoSyncEnabled {
if impl.acdConfig.IsManualSyncEnabled() {
timeline := &pipelineConfig.PipelineStatusTimeline{
InstalledAppVersionHistoryId: installAppVersionRequest.InstalledAppVersionHistoryId,
Status: pipelineConfig.TIMELINE_STATUS_ARGOCD_SYNC_COMPLETED,
Expand Down Expand Up @@ -304,7 +304,7 @@ func (impl *FullModeDeploymentServiceImpl) RollbackRelease(ctx context.Context,
return installedApp, false, err
}

isManualSync := !impl.acdConfig.ArgoCDAutoSyncEnabled
isManualSync := impl.acdConfig.IsManualSyncEnabled()

GitCommitSuccessTimeline := impl.pipelineStatusTimelineService.
GetTimelineDbObjectByTimelineStatusAndTimelineDescription(0, installedApp.InstalledAppVersionHistoryId, pipelineConfig.TIMELINE_STATUS_GIT_COMMIT, "Git commit done successfully.", installedApp.UserId, time.Now())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func (impl *FullModeDeploymentServiceImpl) UpdateAndSyncACDApps(installAppVersio
}
return err
}
if !impl.acdConfig.ArgoCDAutoSyncEnabled {
if impl.acdConfig.IsManualSyncEnabled() {
err = impl.SaveTimelineForHelmApps(installAppVersionRequest, pipelineConfig.TIMELINE_STATUS_ARGOCD_SYNC_COMPLETED, "argocd sync completed", syncTime, tx)
if err != nil {
impl.Logger.Errorw("error in saving timeline for acd helm apps", "err", err)
Expand Down
2 changes: 1 addition & 1 deletion pkg/deployment/trigger/devtronApps/TriggerService.go
Original file line number Diff line number Diff line change
Expand Up @@ -1041,7 +1041,7 @@ func (impl *TriggerServiceImpl) deployArgocdApp(overrideRequest *bean3.ValuesOve
impl.logger.Errorw("error in getting argo application with normal refresh", "argoAppName", valuesOverrideResponse.Pipeline.DeploymentAppName)
return fmt.Errorf("%s. err: %s", bean.ARGOCD_SYNC_ERROR, err.Error())
}
if !impl.ACDConfig.ArgoCDAutoSyncEnabled {
if impl.ACDConfig.IsManualSyncEnabled() {
timeline := &pipelineConfig.PipelineStatusTimeline{
CdWorkflowRunnerId: overrideRequest.WfrId,
StatusTime: syncTime,
Expand Down
8 changes: 4 additions & 4 deletions pkg/workflow/status/WorkflowStatusService.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ func (impl *WorkflowStatusServiceImpl) UpdatePipelineTimelineAndStatusByLiveAppl
return nil, isTimelineUpdated
}

if !impl.acdConfig.ArgoCDAutoSyncEnabled {
if impl.acdConfig.IsManualSyncEnabled() {
// if manual sync check for application sync status
isArgoAppSynced := impl.pipelineStatusTimelineService.GetArgoAppSyncStatus(cdWfr.Id)
if !isArgoAppSynced {
Expand Down Expand Up @@ -277,7 +277,7 @@ func (impl *WorkflowStatusServiceImpl) UpdatePipelineTimelineAndStatusByLiveAppl
// drop event
return nil, isTimelineUpdated
}
if !impl.acdConfig.ArgoCDAutoSyncEnabled {
if impl.acdConfig.IsManualSyncEnabled() {
isArgoAppSynced := impl.pipelineStatusTimelineService.GetArgoAppSyncStatusForAppStore(installedAppVersionHistory.Id)
if !isArgoAppSynced {
return nil, isTimelineUpdated
Expand Down Expand Up @@ -469,7 +469,7 @@ func (impl *WorkflowStatusServiceImpl) CheckArgoPipelineTimelineStatusPeriodical
}

func (impl *WorkflowStatusServiceImpl) syncACDDevtronApps(deployedBeforeMinutes int, pipelineId int) error {
if impl.acdConfig.ArgoCDAutoSyncEnabled {
if impl.acdConfig.IsAutoSyncEnabled() {
// don't check for apps if auto sync is enabled
return nil
}
Expand Down Expand Up @@ -528,7 +528,7 @@ func (impl *WorkflowStatusServiceImpl) syncACDDevtronApps(deployedBeforeMinutes
}

func (impl *WorkflowStatusServiceImpl) syncACDHelmApps(deployedBeforeMinutes int, installedAppVersionId int) error {
if impl.acdConfig.ArgoCDAutoSyncEnabled {
if impl.acdConfig.IsAutoSyncEnabled() {
// don't check for apps if auto sync is enabled
return nil
}
Expand Down

0 comments on commit f6adb1a

Please sign in to comment.