Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #214 - Added DeployTimeout to allow pipline timeout override #321

Merged
merged 2 commits into from
Aug 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion common/context_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package common

import (
"github.com/stretchr/testify/assert"
"io/ioutil"
"os"
"strings"
"testing"

"github.com/stretchr/testify/assert"
)

func TestNewContext(t *testing.T) {
Expand Down
13 changes: 8 additions & 5 deletions common/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,12 @@ type Pipeline struct {
Branch string `yaml:"branch,omitempty"`
} `yaml:"source,omitempty"`
Build struct {
Disabled bool `yaml:"disabled,omitempty"`
Type string `yaml:"type,omitempty"`
ComputeType string `yaml:"computeType,omitempty"`
Image string `yaml:"image,omitempty"`
Bucket string `yaml:"bucket,omitempty"`
Disabled bool `yaml:"disabled,omitempty"`
Type string `yaml:"type,omitempty"`
ComputeType string `yaml:"computeType,omitempty"`
Image string `yaml:"image,omitempty"`
Bucket string `yaml:"bucket,omitempty"`
BuildTimeout string `yaml:"timeout,omitempty"`
} `yaml:"build,omitempty"`
Acceptance struct {
Disabled bool `yaml:"disabled,omitempty"`
Expand All @@ -175,6 +176,7 @@ type Pipeline struct {
CodeBuild string `yaml:"codeBuild,omitempty"`
Mu string `yaml:"mu,omitempty"`
} `yaml:"roles,omitempty"`
BuildTimeout string `yaml:"timeout,omitempty"`
} `yaml:"acceptance,omitempty"`
Production struct {
Disabled bool `yaml:"disabled,omitempty"`
Expand All @@ -183,6 +185,7 @@ type Pipeline struct {
CodeBuild string `yaml:"codeBuild,omitempty"`
Mu string `yaml:"mu,omitempty"`
} `yaml:"roles,omitempty"`
BuildTimeout string `yaml:"timeout,omitempty"`
} `yaml:"production,omitempty"`
MuBaseurl string `yaml:"muBaseurl,omitempty"`
MuVersion string `yaml:"muVersion,omitempty"`
Expand Down
24 changes: 21 additions & 3 deletions templates/assets/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,24 @@ Parameters:
Type: String
Description: IAM Role for Prod Stage - used for cross account access
Default: ""
PipelineBuildAcceptanceTimeout:
Type: Number
Description: The number of minutes after which AWS CodeBuild stops the build if it's not complete
Default: 30
MinValue: 5
MaxValue: 480
PipelineBuildTimeout:
Type: Number
Description: The number of minutes after which AWS CodeBuild stops the build if it's not complete
Default: 30
MinValue: 5
MaxValue: 480
PipelineBuildProductionTimeout:
Type: Number
Description: The number of minutes after which AWS CodeBuild stops the build if it's not complete
Default: 30
MinValue: 5
MaxValue: 480
Conditions:
IsS3:
"Fn::And":
Expand Down Expand Up @@ -243,7 +261,7 @@ Resources:
artifacts:
files:
- ${MuFile}
TimeoutInMinutes: 30
TimeoutInMinutes: !Ref PipelineBuildTimeout
DeployAcceptance:
Type: AWS::CodeBuild::Project
Condition: IsAcptEnabled
Expand Down Expand Up @@ -305,7 +323,7 @@ Resources:
Image: !Sub ${TestImage}
Source:
Type: CODEPIPELINE
TimeoutInMinutes: 30
TimeoutInMinutes: !Ref PipelineBuildAcceptanceTimeout
DeployProduction:
Type: AWS::CodeBuild::Project
Condition: IsProdEnabled
Expand Down Expand Up @@ -364,7 +382,7 @@ Resources:
Image: !Sub ${TestImage}
Source:
Type: CODEPIPELINE
TimeoutInMinutes: 30
TimeoutInMinutes: !Ref PipelineBuildProductionTimeout
Pipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
Expand Down
173 changes: 100 additions & 73 deletions workflows/pipeline_upsert.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,83 +199,12 @@ func (workflow *pipelineWorkflow) pipelineUpserter(namespace string, stackUpsert
pipelineStackName := common.CreateStackName(namespace, common.StackTypePipeline, workflow.serviceName)

log.Noticef("Upserting Pipeline for service '%s' ...", workflow.serviceName)
pipelineParams := params

pipelineParams["Namespace"] = namespace
pipelineParams["ServiceName"] = workflow.serviceName
pipelineParams["MuFile"] = workflow.muFile
pipelineParams["SourceProvider"] = workflow.pipelineConfig.Source.Provider
pipelineParams["SourceRepo"] = workflow.pipelineConfig.Source.Repo

if workflow.codeBranch != "" {
pipelineParams["SourceBranch"] = workflow.codeBranch
}

if workflow.pipelineConfig.Source.Provider == "S3" {
repoParts := strings.Split(workflow.pipelineConfig.Source.Repo, "/")
pipelineParams["SourceBucket"] = repoParts[0]
pipelineParams["SourceObjectKey"] = strings.Join(repoParts[1:], "/")
}

if workflow.pipelineConfig.Build.Type != "" {
pipelineParams["BuildType"] = workflow.pipelineConfig.Build.Type
}
if workflow.pipelineConfig.Build.ComputeType != "" {
pipelineParams["BuildComputeType"] = workflow.pipelineConfig.Build.ComputeType
}

if workflow.pipelineConfig.Build.Image != "" {
pipelineParams["BuildImage"] = workflow.pipelineConfig.Build.Image
}

if workflow.pipelineConfig.Acceptance.Type != "" {
pipelineParams["TestType"] = workflow.pipelineConfig.Acceptance.Type
}
if workflow.pipelineConfig.Acceptance.ComputeType != "" {
pipelineParams["TestComputeType"] = workflow.pipelineConfig.Acceptance.ComputeType
}

if workflow.pipelineConfig.Acceptance.Image != "" {
pipelineParams["TestImage"] = workflow.pipelineConfig.Acceptance.Image
}

if workflow.pipelineConfig.Acceptance.Environment != "" {
pipelineParams["AcptEnv"] = workflow.pipelineConfig.Acceptance.Environment
}

if workflow.pipelineConfig.Production.Environment != "" {
pipelineParams["ProdEnv"] = workflow.pipelineConfig.Production.Environment
}

if workflow.pipelineConfig.MuBaseurl != "" {
pipelineParams["MuDownloadBaseurl"] = workflow.pipelineConfig.MuBaseurl
}

pipelineParams["EnableBuildStage"] = strconv.FormatBool(!workflow.pipelineConfig.Build.Disabled)
pipelineParams["EnableAcptStage"] = strconv.FormatBool(!workflow.pipelineConfig.Acceptance.Disabled)
pipelineParams["EnableProdStage"] = strconv.FormatBool(!workflow.pipelineConfig.Production.Disabled)

// get default buildspec
buildspec, err := templates.NewTemplate("buildspec.yml", nil)
pipelineParams, err := PipelineParams(workflow, namespace, params)
if err != nil {
return err
}
buildspecBytes := new(bytes.Buffer)
buildspecBytes.ReadFrom(buildspec)
newlineRegexp := regexp.MustCompile(`\r?\n`)
buildspecString := newlineRegexp.ReplaceAllString(buildspecBytes.String(), "\\n")
pipelineParams["DefaultBuildspec"] = buildspecString

version := workflow.pipelineConfig.MuVersion
if version == "" {
version = common.GetVersion()
if version == "0.0.0-local" {
version = ""
}
}
if version != "" {
pipelineParams["MuDownloadVersion"] = version
}

tags := createTagMap(&PipelineTags{
Type: common.StackTypePipeline,
Service: workflow.serviceName,
Expand Down Expand Up @@ -303,6 +232,104 @@ func (workflow *pipelineWorkflow) pipelineUpserter(namespace string, stackUpsert
}
}

// PipelineParams creates a map of params to send to the CFN pipeline template
func PipelineParams(workflow *pipelineWorkflow, namespace string, params map[string]string) (map[string]string, error) {

pipelineParams := params

pipelineParams["Namespace"] = namespace
pipelineParams["ServiceName"] = workflow.serviceName
pipelineParams["MuFile"] = workflow.muFile
pipelineParams["SourceProvider"] = workflow.pipelineConfig.Source.Provider
pipelineParams["SourceRepo"] = workflow.pipelineConfig.Source.Repo

if workflow.codeBranch != "" {
pipelineParams["SourceBranch"] = workflow.codeBranch
}

if workflow.pipelineConfig.Source.Provider == "S3" {
repoParts := strings.Split(workflow.pipelineConfig.Source.Repo, "/")
pipelineParams["SourceBucket"] = repoParts[0]
pipelineParams["SourceObjectKey"] = strings.Join(repoParts[1:], "/")
}

if workflow.pipelineConfig.Build.Type != "" {
pipelineParams["BuildType"] = workflow.pipelineConfig.Build.Type
}
if workflow.pipelineConfig.Build.ComputeType != "" {
pipelineParams["BuildComputeType"] = workflow.pipelineConfig.Build.ComputeType
}

if workflow.pipelineConfig.Build.Image != "" {
pipelineParams["BuildImage"] = workflow.pipelineConfig.Build.Image
}

if workflow.pipelineConfig.Build.BuildTimeout != "" {
pipelineParams["PipelineBuildTimeout"] = workflow.pipelineConfig.Build.BuildTimeout
}

if workflow.pipelineConfig.Acceptance.Type != "" {
pipelineParams["TestType"] = workflow.pipelineConfig.Acceptance.Type
}

if workflow.pipelineConfig.Acceptance.ComputeType != "" {
pipelineParams["TestComputeType"] = workflow.pipelineConfig.Acceptance.ComputeType
}

if workflow.pipelineConfig.Acceptance.Image != "" {
pipelineParams["TestImage"] = workflow.pipelineConfig.Acceptance.Image
}

if workflow.pipelineConfig.Acceptance.Environment != "" {
pipelineParams["AcptEnv"] = workflow.pipelineConfig.Acceptance.Environment
}

if workflow.pipelineConfig.Acceptance.BuildTimeout != "" {
pipelineParams["PipelineBuildAcceptanceTimeout"] = workflow.pipelineConfig.Acceptance.BuildTimeout
}

if workflow.pipelineConfig.Production.Environment != "" {
pipelineParams["ProdEnv"] = workflow.pipelineConfig.Production.Environment
}

if workflow.pipelineConfig.Production.BuildTimeout != "" {
pipelineParams["PipelineBuildProductionTimeout"] = workflow.pipelineConfig.Production.BuildTimeout
}

if workflow.pipelineConfig.MuBaseurl != "" {
pipelineParams["MuDownloadBaseurl"] = workflow.pipelineConfig.MuBaseurl
}

pipelineParams["EnableBuildStage"] = strconv.FormatBool(!workflow.pipelineConfig.Build.Disabled)
pipelineParams["EnableAcptStage"] = strconv.FormatBool(!workflow.pipelineConfig.Acceptance.Disabled)
pipelineParams["EnableProdStage"] = strconv.FormatBool(!workflow.pipelineConfig.Production.Disabled)

// get default buildspec
buildspec, err := templates.NewTemplate("buildspec.yml", nil)
if err != nil {
return nil, err
}
buildspecBytes := new(bytes.Buffer)
buildspecBytes.ReadFrom(buildspec)
newlineRegexp := regexp.MustCompile(`\r?\n`)
buildspecString := newlineRegexp.ReplaceAllString(buildspecBytes.String(), "\\n")

params["DefaultBuildspec"] = buildspecString

version := workflow.pipelineConfig.MuVersion
if version == "" {
version = common.GetVersion()
if version == "0.0.0-local" {
version = ""
}
}
if version != "" {
params["MuDownloadVersion"] = version
}

return pipelineParams, nil
}

func (workflow *pipelineWorkflow) pipelineNotifyUpserter(namespace string, pipeline *common.Pipeline, subManager common.SubscriptionManager) Executor {
return func() error {
if len(workflow.notificationArn) > 0 && len(pipeline.Notify) > 0 {
Expand Down
45 changes: 45 additions & 0 deletions workflows/pipeline_upsert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,48 @@ func TestPipelineUpserter(t *testing.T) {
assert.Equal("", stackParams["Branch"])
assert.Equal("my-token", stackParams["GitHubToken"])
}

func TestPipelineParams(t *testing.T) {

assert := assert.New(t)

yamlConfig :=
`
---
environments:
- name: acceptance
- name: production
service:
port: 80
healthEndpoint: /
pathPatterns:
- /*
pipeline:
source:
provider: GitHub
repo: foo/bar
acceptance:
timeout: 15
build:
timeout: 25
production:
timeout: 480
`

ctx := common.NewContext()
config, err := loadYamlConfig(yamlConfig)

assert.Nil(err)

ctx.Config = *config

workflow := new(pipelineWorkflow)
workflow.serviceName = "my-service"
workflow.pipelineConfig = &ctx.Config.Service.Pipeline

params, err2 := PipelineParams(workflow, "mu", make(map[string]string))
assert.Nil(err2)
assert.Equal(params["PipelineBuildAcceptanceTimeout"], "15")
assert.Equal(params["PipelineBuildTimeout"], "25")
assert.Equal(params["PipelineBuildProductionTimeout"], "480")
}