diff --git a/README.md b/README.md index 59426ed..335b730 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,7 @@ stages: reliesOn: - "2" manualJudgement: + timeoutHours: 48 failPipeline: true instructions: | If this stage has completed QA, press proceed. diff --git a/cmd/k8s-pipeliner/main.go b/cmd/k8s-pipeliner/main.go index 11b87ef..861b7a2 100644 --- a/cmd/k8s-pipeliner/main.go +++ b/cmd/k8s-pipeliner/main.go @@ -38,6 +38,10 @@ func main() { Name: "v2", Usage: "Create your manifests with the v2 kubernetes provider", }, + cli.IntFlag{ + Name: "timeout", + Usage: "override the default 72 hour timeout (unit: int)", + }, }, }, { @@ -69,7 +73,7 @@ func createAction(ctx *cli.Context) error { return err } - builder := builder.New(p, builder.WithV2Provider(ctx.Bool("v2")), builder.WithLinear(ctx.Bool("linear"))) + builder := builder.New(p, builder.WithV2Provider(ctx.Bool("v2")), builder.WithLinear(ctx.Bool("linear")), builder.WithTimeoutOverride(ctx.Int("timeout"))) return json.NewEncoder(os.Stdout).Encode(builder) } diff --git a/pipeline/builder/builder.go b/pipeline/builder/builder.go index 3993251..239081a 100644 --- a/pipeline/builder/builder.go +++ b/pipeline/builder/builder.go @@ -40,15 +40,18 @@ const ( WebhookTrigger = "webhook" // LoadBalancerFormat creates the label selectors to attach pipeline.yml labels to deployment selectors LoadBalancerFormat = "load-balancer-%s" + // HourInMS provides 1 hour in milliseconds + HourInMS int64 = 3600000 ) // Builder constructs a spinnaker pipeline JSON from a pipeliner config type Builder struct { pipeline *config.Pipeline - isLinear bool - basePath string - v2Provider bool + isLinear bool + basePath string + v2Provider bool + timeoutHours int } // New initializes a new builder for a pipeline config @@ -508,7 +511,7 @@ func (b *Builder) buildDeployStage(index int, s config.Stage) (*types.DeployStag VolumeSources: mg.VolumeSources, // TODO(bobbytables): allow these to be configurable - Events: []interface{}{}, + Events: []interface{}{}, InterestingHealthProviderNames: []string{"KubernetesContainer", "KubernetesPod"}, Provider: "kubernetes", CloudProvider: "kubernetes", @@ -525,10 +528,19 @@ func (b *Builder) buildDeployStage(index int, s config.Stage) (*types.DeployStag func (b *Builder) buildManualJudgementStage(index int, s config.Stage) (*types.ManualJudgementStage, error) { mjs := &types.ManualJudgementStage{ StageMetadata: buildStageMetadata(s, "manualJudgment", index, b.isLinear), - - FailPipeline: s.ManualJudgement.FailPipeline, - Instructions: s.ManualJudgement.Instructions, - Inputs: s.ManualJudgement.Inputs, + FailPipeline: s.ManualJudgement.FailPipeline, + Instructions: s.ManualJudgement.Instructions, + Inputs: s.ManualJudgement.Inputs, + } + // if global timeout override has been set + if b.timeoutHours != 0 { + mjs.OverrideTimeout = true + mjs.StageTimeoutMS = int64(b.timeoutHours) * HourInMS + } + // if the timeout is actually set go + if s.ManualJudgement.Timeout != 0 { + mjs.OverrideTimeout = true + mjs.StageTimeoutMS = int64(s.ManualJudgement.Timeout) * HourInMS } return mjs, nil diff --git a/pipeline/builder/options.go b/pipeline/builder/options.go index df20044..9e842ec 100644 --- a/pipeline/builder/options.go +++ b/pipeline/builder/options.go @@ -25,3 +25,10 @@ func WithV2Provider(v bool) OptFunc { b.v2Provider = v } } + +// WithTimeoutOverride overrides every stage's default 72 hour timeout +func WithTimeoutOverride(hours int) OptFunc { + return func(b *Builder) { + b.timeoutHours = hours + } +} diff --git a/pipeline/builder/types/types.go b/pipeline/builder/types/types.go index e4f97e8..bcd2764 100644 --- a/pipeline/builder/types/types.go +++ b/pipeline/builder/types/types.go @@ -188,9 +188,11 @@ var _ Stage = DeployStage{} type ManualJudgementStage struct { StageMetadata - FailPipeline bool `json:"failPipeline"` - Instructions string `json:"instructions"` - Inputs []string `json:"inputs,omitempty"` + FailPipeline bool `json:"failPipeline"` + Instructions string `json:"instructions"` + Inputs []string `json:"inputs,omitempty"` + OverrideTimeout bool `json:"overrideTimeout,omitempty"` + StageTimeoutMS int64 `json:"stageTimeoutMs,omitempty"` } func (mjs ManualJudgementStage) spinnakerStage() {} diff --git a/pipeline/config/config.go b/pipeline/config/config.go index 6eb9baf..0ec0fc6 100644 --- a/pipeline/config/config.go +++ b/pipeline/config/config.go @@ -172,6 +172,7 @@ type ManualJudgementStage struct { FailPipeline bool `yaml:"failPipeline"` Instructions string `yaml:"instructions"` Inputs []string `yaml:"inputs"` + Timeout int `yaml:"timeoutHours,omitempty"` } // ManifestFile represents a single manifest file diff --git a/pipeline/config/config_test.go b/pipeline/config/config_test.go index ee68040..d4de182 100644 --- a/pipeline/config/config_test.go +++ b/pipeline/config/config_test.go @@ -23,11 +23,17 @@ func TestNewConfig(t *testing.T) { require.Len(t, cfg.Triggers, 1) require.Equal(t, "nginx/job/master", cfg.Triggers[0].Jenkins.Job) - require.Len(t, cfg.Stages, 1) + require.Len(t, cfg.Stages, 3) stage := cfg.Stages[0] require.NotNil(t, stage.DeployEmbeddedManifests) assert.Equal(t, stage.DeployEmbeddedManifests.Files[0].File, "manifests/nginx-deployment.yml") assert.Equal(t, stage.Name, "Deploy nginx") assert.Equal(t, stage.Account, "int-k8s") + stage2 := cfg.Stages[1] + require.NotNil(t, stage2.ManualJudgement) + assert.Equal(t, stage2.ManualJudgement.Timeout, 100) + stage3 := cfg.Stages[2] + require.NotNil(t, stage3.ManualJudgement) + assert.Equal(t, stage3.ManualJudgement.Timeout, 0) } diff --git a/pipeline/config/testdata/pipeline.full.yaml b/pipeline/config/testdata/pipeline.full.yaml index e901d24..d00a90b 100644 --- a/pipeline/config/testdata/pipeline.full.yaml +++ b/pipeline/config/testdata/pipeline.full.yaml @@ -11,3 +11,16 @@ stages: deployEmbeddedManifests: files: - file: manifests/nginx-deployment.yml +- account: int-k8s + name: Deploy to staging-k8s? + manualJudgement: + failPipeline: true + instructions: Should this pipeline continue? + inputs: [] + timeoutHours: 100 +- account: int-k8s + name: Deploy to staging-k8s? + manualJudgement: + failPipeline: true + instructions: Should this pipeline continue? + inputs: []