diff --git a/pkg/app/piped/planner/cloudrun/cloudrun.go b/pkg/app/piped/planner/cloudrun/cloudrun.go index c01b6d60a6..49200dba04 100644 --- a/pkg/app/piped/planner/cloudrun/cloudrun.go +++ b/pkg/app/piped/planner/cloudrun/cloudrun.go @@ -64,13 +64,13 @@ func (p *Planner) Plan(ctx context.Context, in planner.Input) (out planner.Outpu autoRollback := *cfg.Input.AutoRollback - // If the deployment was triggered by forcing via web UI, - // we rely on the user's decision. + // In case the strategy has been decided by trigger. + // For example: user triggered the deployment via web console. switch in.Trigger.SyncStrategy { case model.SyncStrategy_QUICK_SYNC: out.SyncStrategy = model.SyncStrategy_QUICK_SYNC out.Stages = buildQuickSyncPipeline(autoRollback, time.Now()) - out.Summary = fmt.Sprintf("Quick sync to deploy image %s and configure all traffic to it (forced via web)", out.Version) + out.Summary = in.Trigger.StrategySummary return case model.SyncStrategy_PIPELINE: if cfg.Pipeline == nil { @@ -79,7 +79,7 @@ func (p *Planner) Plan(ctx context.Context, in planner.Input) (out planner.Outpu } out.SyncStrategy = model.SyncStrategy_PIPELINE out.Stages = buildProgressivePipeline(cfg.Pipeline, autoRollback, time.Now()) - out.Summary = fmt.Sprintf("Sync with pipeline to deploy image %s (forced via web)", out.Version) + out.Summary = in.Trigger.StrategySummary return } diff --git a/pkg/app/piped/planner/ecs/ecs.go b/pkg/app/piped/planner/ecs/ecs.go index 61c647170f..2fa5e8c682 100644 --- a/pkg/app/piped/planner/ecs/ecs.go +++ b/pkg/app/piped/planner/ecs/ecs.go @@ -64,13 +64,13 @@ func (p *Planner) Plan(ctx context.Context, in planner.Input) (out planner.Outpu autoRollback := *cfg.Input.AutoRollback - // If the deployment was triggered by forcing via web UI, - // we rely on the user's decision. + // In case the strategy has been decided by trigger. + // For example: user triggered the deployment via web console. switch in.Trigger.SyncStrategy { case model.SyncStrategy_QUICK_SYNC: out.SyncStrategy = model.SyncStrategy_QUICK_SYNC out.Stages = buildQuickSyncPipeline(autoRollback, time.Now()) - out.Summary = fmt.Sprintf("Quick sync to deploy image %s and configure all traffic to it (forced via web)", out.Version) + out.Summary = in.Trigger.StrategySummary return case model.SyncStrategy_PIPELINE: if cfg.Pipeline == nil { @@ -79,7 +79,7 @@ func (p *Planner) Plan(ctx context.Context, in planner.Input) (out planner.Outpu } out.SyncStrategy = model.SyncStrategy_PIPELINE out.Stages = buildProgressivePipeline(cfg.Pipeline, autoRollback, time.Now()) - out.Summary = fmt.Sprintf("Sync with pipeline to deploy image %s (forced via web)", out.Version) + out.Summary = in.Trigger.StrategySummary return } diff --git a/pkg/app/piped/planner/kubernetes/kubernetes.go b/pkg/app/piped/planner/kubernetes/kubernetes.go index 3fd1068988..747737e04a 100644 --- a/pkg/app/piped/planner/kubernetes/kubernetes.go +++ b/pkg/app/piped/planner/kubernetes/kubernetes.go @@ -99,13 +99,13 @@ func (p *Planner) Plan(ctx context.Context, in planner.Input) (out planner.Outpu autoRollback := *cfg.Input.AutoRollback - // If the deployment was triggered by forcing via web UI, - // we rely on the user's decision. + // In case the strategy has been decided by trigger. + // For example: user triggered the deployment via web console. switch in.Trigger.SyncStrategy { case model.SyncStrategy_QUICK_SYNC: out.SyncStrategy = model.SyncStrategy_QUICK_SYNC out.Stages = buildQuickSyncPipeline(autoRollback, time.Now()) - out.Summary = "Quick sync by applying all manifests (forced via web)" + out.Summary = in.Trigger.StrategySummary return case model.SyncStrategy_PIPELINE: if cfg.Pipeline == nil { @@ -114,7 +114,7 @@ func (p *Planner) Plan(ctx context.Context, in planner.Input) (out planner.Outpu } out.SyncStrategy = model.SyncStrategy_PIPELINE out.Stages = buildProgressivePipeline(cfg.Pipeline, autoRollback, time.Now()) - out.Summary = "Sync with the specified pipeline (forced via web)" + out.Summary = in.Trigger.StrategySummary return } diff --git a/pkg/app/piped/planner/lambda/lambda.go b/pkg/app/piped/planner/lambda/lambda.go index 2bfc2401bb..d975b56483 100644 --- a/pkg/app/piped/planner/lambda/lambda.go +++ b/pkg/app/piped/planner/lambda/lambda.go @@ -64,13 +64,13 @@ func (p *Planner) Plan(ctx context.Context, in planner.Input) (out planner.Outpu autoRollback := *cfg.Input.AutoRollback - // If the deployment was triggered by forcing via web UI, - // we rely on the user's decision. + // In case the strategy has been decided by trigger. + // For example: user triggered the deployment via web console. switch in.Trigger.SyncStrategy { case model.SyncStrategy_QUICK_SYNC: out.SyncStrategy = model.SyncStrategy_QUICK_SYNC out.Stages = buildQuickSyncPipeline(autoRollback, time.Now()) - out.Summary = fmt.Sprintf("Quick sync to deploy version %s and configure all traffic to it (forced via web)", out.Version) + out.Summary = in.Trigger.StrategySummary return case model.SyncStrategy_PIPELINE: if cfg.Pipeline == nil { @@ -79,7 +79,7 @@ func (p *Planner) Plan(ctx context.Context, in planner.Input) (out planner.Outpu } out.SyncStrategy = model.SyncStrategy_PIPELINE out.Stages = buildProgressivePipeline(cfg.Pipeline, autoRollback, time.Now()) - out.Summary = fmt.Sprintf("Sync with pipeline to deploy version %s (forced via web)", out.Version) + out.Summary = in.Trigger.StrategySummary return } diff --git a/pkg/app/piped/planner/terraform/terraform.go b/pkg/app/piped/planner/terraform/terraform.go index 5a80f6c30d..2dd3104088 100644 --- a/pkg/app/piped/planner/terraform/terraform.go +++ b/pkg/app/piped/planner/terraform/terraform.go @@ -51,13 +51,13 @@ func (p *Planner) Plan(ctx context.Context, in planner.Input) (out planner.Outpu return } - // If the deployment was triggered by forcing via web UI, - // we rely on the user's decision. + // In case the strategy has been decided by trigger. + // For example: user triggered the deployment via web console. switch in.Trigger.SyncStrategy { case model.SyncStrategy_QUICK_SYNC: out.SyncStrategy = model.SyncStrategy_QUICK_SYNC out.Stages = buildQuickSyncPipeline(cfg.Input.AutoRollback, time.Now()) - out.Summary = "Quick sync by automatically applying any detected changes because no pipeline was configured (forced via web)" + out.Summary = in.Trigger.StrategySummary return case model.SyncStrategy_PIPELINE: if cfg.Pipeline == nil { @@ -66,7 +66,7 @@ func (p *Planner) Plan(ctx context.Context, in planner.Input) (out planner.Outpu } out.SyncStrategy = model.SyncStrategy_PIPELINE out.Stages = buildProgressivePipeline(cfg.Pipeline, cfg.Input.AutoRollback, time.Now()) - out.Summary = "Sync with the specified progressive pipeline (forced via web)" + out.Summary = in.Trigger.StrategySummary return } diff --git a/pkg/app/piped/trigger/deployment.go b/pkg/app/piped/trigger/deployment.go index 7a5381f3b4..c1e93f1445 100644 --- a/pkg/app/piped/trigger/deployment.go +++ b/pkg/app/piped/trigger/deployment.go @@ -39,6 +39,7 @@ func (t *Trigger) triggerDeployment( commit git.Commit, commander string, syncStrategy model.SyncStrategy, + strategySummary string, ) (*model.Deployment, error) { // Build deployment model to trigger. @@ -48,6 +49,7 @@ func (t *Trigger) triggerDeployment( commit, commander, syncStrategy, + strategySummary, time.Now(), appCfg.DeploymentNotification, ) @@ -80,6 +82,7 @@ func buildDeployment( commit git.Commit, commander string, syncStrategy model.SyncStrategy, + strategySummary string, now time.Time, noti *config.DeploymentNotification, ) (*model.Deployment, error) { @@ -119,9 +122,10 @@ func buildDeployment( Url: commitURL, CreatedAt: int64(commit.CreatedAt), }, - Commander: commander, - Timestamp: now.Unix(), - SyncStrategy: syncStrategy, + Commander: commander, + Timestamp: now.Unix(), + SyncStrategy: syncStrategy, + StrategySummary: strategySummary, }, GitPath: app.GitPath, CloudProvider: app.CloudProvider, diff --git a/pkg/app/piped/trigger/determiner.go b/pkg/app/piped/trigger/determiner.go index 45c5a7fe62..f887b86557 100644 --- a/pkg/app/piped/trigger/determiner.go +++ b/pkg/app/piped/trigger/determiner.go @@ -39,11 +39,11 @@ type determiners struct { onCommit Determiner } -func (ds *determiners) Determiner(ck candidateKind) Determiner { - switch ck { - case commandCandidate: +func (ds *determiners) Determiner(k model.TriggerKind) Determiner { + switch k { + case model.TriggerKind_ON_COMMAND: return ds.onCommand - case outOfSyncCandidate: + case model.TriggerKind_ON_OUT_OF_SYNC: return ds.onOutOfSync default: return ds.onCommit diff --git a/pkg/app/piped/trigger/trigger.go b/pkg/app/piped/trigger/trigger.go index 64d7a0d836..494212ac5b 100644 --- a/pkg/app/piped/trigger/trigger.go +++ b/pkg/app/piped/trigger/trigger.go @@ -68,17 +68,9 @@ type notifier interface { Notify(event model.NotificationEvent) } -type candidateKind int - -const ( - commitCandidate candidateKind = iota - commandCandidate - outOfSyncCandidate -) - type candidate struct { application *model.Application - kind candidateKind + kind model.TriggerKind command model.ReportableCommand } @@ -219,10 +211,16 @@ func (t *Trigger) checkRepoCandidates(ctx context.Context, repoID string, cs []c onOutOfSync: NewOnOutOfSyncDeterminer(t.apiClient), onCommit: NewOnCommitDeterminer(gitRepo, headCommit.Hash, t.commitStore, t.logger), } + triggered := make(map[string]struct{}) for _, c := range cs { app := c.application + // Avoid triggering multiple deployments for the same application in the same iteration. + if _, ok := triggered[app.Id]; ok { + continue + } + appCfg, err := loadDeploymentConfiguration(gitRepo.GetPath(), app) if err != nil { t.logger.Error("failed to load application config file", zap.Error(err)) @@ -248,8 +246,32 @@ func (t *Trigger) checkRepoCandidates(ctx context.Context, repoID string, cs []c continue } + var ( + commander string + strategy model.SyncStrategy + strategySummary string + ) + + switch c.kind { + case model.TriggerKind_ON_COMMAND: + strategy = c.command.GetSyncApplication().SyncStrategy + commander = c.command.Commander + if strategy == model.SyncStrategy_QUICK_SYNC { + strategySummary = "Quick sync because piped received a command from user via web console or pipectl" + } else { + strategySummary = "Sync with the specified pipeline because piped received a command from user via web console or pipectl" + } + + case model.TriggerKind_ON_OUT_OF_SYNC: + strategy = model.SyncStrategy_QUICK_SYNC + strategySummary = "Quick sync to attempt to resolve the detected configuration drift" + + default: + strategy = model.SyncStrategy_AUTO + } + // Build deployment model and send a request to API to create a new deployment. - deployment, err := t.triggerDeployment(ctx, app, appCfg, branch, headCommit, "", model.SyncStrategy_AUTO) + deployment, err := t.triggerDeployment(ctx, app, appCfg, branch, headCommit, commander, strategy, strategySummary) if err != nil { msg := fmt.Sprintf("failed to trigger application %s: %v", app.Id, err) t.notifyDeploymentTriggerFailed(app, msg, headCommit) @@ -257,11 +279,12 @@ func (t *Trigger) checkRepoCandidates(ctx context.Context, repoID string, cs []c continue } + triggered[app.Id] = struct{}{} t.commitStore.Put(app.Id, headCommit.Hash) t.notifyDeploymentTriggered(ctx, appCfg, deployment) // Mask command as handled since the deployment has been triggered successfully. - if c.kind == commandCandidate { + if c.kind == model.TriggerKind_ON_COMMAND { metadata := map[string]string{ triggeredDeploymentIDKey: deployment.Id, } @@ -301,7 +324,7 @@ func (t *Trigger) listCommandCandidates() []candidate { apps = append(apps, candidate{ application: app, - kind: commandCandidate, + kind: model.TriggerKind_ON_COMMAND, command: cmd, }) } @@ -321,7 +344,7 @@ func (t *Trigger) listOutOfSyncCandidates() []candidate { } apps = append(apps, candidate{ application: app, - kind: outOfSyncCandidate, + kind: model.TriggerKind_ON_OUT_OF_SYNC, }) } return apps @@ -338,7 +361,7 @@ func (t *Trigger) listCommitCandidates() []candidate { for _, app := range list { apps = append(apps, candidate{ application: app, - kind: commitCandidate, + kind: model.TriggerKind_ON_COMMIT, }) } return apps diff --git a/pkg/app/web/src/__fixtures__/dummy-trigger.ts b/pkg/app/web/src/__fixtures__/dummy-trigger.ts index 1ab82523e3..96da4282c2 100644 --- a/pkg/app/web/src/__fixtures__/dummy-trigger.ts +++ b/pkg/app/web/src/__fixtures__/dummy-trigger.ts @@ -20,6 +20,7 @@ export const dummyTrigger: DeploymentTrigger.AsObject = { url: "", }, syncStrategy: SyncStrategy.AUTO, + strategySummary: "", }; function createCommitFromObject(o: Commit.AsObject): Commit { diff --git a/pkg/app/web/src/components/deployments-detail-page/pipeline/index.stories.tsx b/pkg/app/web/src/components/deployments-detail-page/pipeline/index.stories.tsx index 9e9c3e4407..e1d66a8d49 100644 --- a/pkg/app/web/src/components/deployments-detail-page/pipeline/index.stories.tsx +++ b/pkg/app/web/src/components/deployments-detail-page/pipeline/index.stories.tsx @@ -41,6 +41,7 @@ const fakeDeployment: Deployment.AsObject = { commander: "cakecatz", timestamp: 1592201366, syncStrategy: SyncStrategy.AUTO, + strategySummary: "", }, runningCommitHash: "3808585b46f1e90196d7ffe8dd04c807a251febc", summary: "This deployment is debug", diff --git a/pkg/model/deployment.proto b/pkg/model/deployment.proto index a842fff6ce..b56078569b 100644 --- a/pkg/model/deployment.proto +++ b/pkg/model/deployment.proto @@ -91,12 +91,19 @@ message Deployment { int64 updated_at = 102 [(validate.rules).int64.gte = 0]; } +enum TriggerKind { + ON_COMMIT = 0; + ON_COMMAND = 1; + ON_OUT_OF_SYNC = 2; +} + message DeploymentTrigger { Commit commit = 1 [(validate.rules).message.required = true]; // Who triggered this deployment via web page. string commander= 2; int64 timestamp = 3 [(validate.rules).int64.gt = 0]; SyncStrategy sync_strategy = 4; + string strategy_summary = 5; } message PipelineStage {