diff --git a/pkg/app/piped/executor/waitapproval/waitapproval.go b/pkg/app/piped/executor/waitapproval/waitapproval.go index 3ec1c0f976..f2f4e0b908 100644 --- a/pkg/app/piped/executor/waitapproval/waitapproval.go +++ b/pkg/app/piped/executor/waitapproval/waitapproval.go @@ -55,6 +55,8 @@ func (e *Executor) Execute(sig executor.StopSignal) model.StageStatus { ) defer ticker.Stop() + timer := time.NewTimer(time.Duration(e.StageConfig.WaitApprovalStageOptions.Timeout) * time.Second) + e.LogPersister.Info("Waiting for an approval...") for { select { @@ -73,6 +75,9 @@ func (e *Executor) Execute(sig executor.StopSignal) model.StageStatus { default: return model.StageStatus_STAGE_FAILURE } + case <-timer.C: + e.LogPersister.Errorf("Timed out %v", e.StageConfig.WaitApprovalStageOptions.Timeout) + return model.StageStatus_STAGE_FAILURE } } } diff --git a/pkg/config/deployment.go b/pkg/config/deployment.go index 813007d02b..b281c27147 100644 --- a/pkg/config/deployment.go +++ b/pkg/config/deployment.go @@ -17,10 +17,13 @@ package config import ( "encoding/json" "fmt" + "time" "github.com/pipe-cd/pipe/pkg/model" ) +var defaultWaitApprovalTimeout = Duration(6 * time.Hour) + type GenericDeploymentSpec struct { // Forcibly use QuickSync or Pipeline when commit message matched the specified pattern. CommitMatcher DeploymentCommitMatcher `json:"commitMatcher"` @@ -66,13 +69,13 @@ type DeploymentCommitMatcher struct { // DeploymentPipeline represents the way to deploy the application. // The pipeline is triggered by changes in any of the following objects: -// - Target PodSpec (Target can be Deployment, DaemonSet, StatefullSet) +// - Target PodSpec (Target can be Deployment, DaemonSet, StatefulSet) // - ConfigMaps, Secrets that are mounted as volumes or envs in the deployment. type DeploymentPipeline struct { Stages []PipelineStage `json:"stages"` } -// PiplineStage represents a single stage of a pipeline. +// PipelineStage represents a single stage of a pipeline. // This is used as a generic struct for all stage type. type PipelineStage struct { Id string @@ -133,6 +136,9 @@ func (s *PipelineStage) UnmarshalJSON(data []byte) error { if len(gs.With) > 0 { err = json.Unmarshal(gs.With, s.WaitApprovalStageOptions) } + if s.WaitApprovalStageOptions.Timeout <= 0 { + s.WaitApprovalStageOptions.Timeout = defaultWaitApprovalTimeout + } case model.StageAnalysis: s.AnalysisStageOptions = &AnalysisStageOptions{} if len(gs.With) > 0 { @@ -220,6 +226,8 @@ type WaitStageOptions struct { // WaitStageOptions contains all configurable values for a WAIT_APPROVAL stage. type WaitApprovalStageOptions struct { + // The maximum length of time to wait before giving up. + Timeout Duration `json:"timeout"` Approvers []string `json:"approvers"` } diff --git a/pkg/config/deployment_terraform_test.go b/pkg/config/deployment_terraform_test.go index 2b63cb7a72..24a4c715dd 100644 --- a/pkg/config/deployment_terraform_test.go +++ b/pkg/config/deployment_terraform_test.go @@ -90,6 +90,8 @@ func TestTerraformDeploymentConfig(t *testing.T) { Name: model.StageWaitApproval, WaitApprovalStageOptions: &WaitApprovalStageOptions{ Approvers: []string{"foo", "bar"}, + // Use defaultWaitApprovalTimeout on unset timeout value for WaitApprovalStage. + Timeout: defaultWaitApprovalTimeout, }, }, {