diff --git a/google/resource_cloud_build_trigger.go b/google/resource_cloud_build_trigger.go index e726be29ef7..d07bc3b5f9a 100644 --- a/google/resource_cloud_build_trigger.go +++ b/google/resource_cloud_build_trigger.go @@ -23,6 +23,43 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/schema" ) +func stepTimeoutCustomizeDiff(diff *schema.ResourceDiff, v interface{}) error { + buildList := diff.Get("build").([]interface{}) + if len(buildList) == 0 || buildList[0] == nil { + return nil + } + build := buildList[0].(map[string]interface{}) + buildTimeoutString := build["timeout"].(string) + + buildTimeout, err := time.ParseDuration(buildTimeoutString) + if err != nil { + return fmt.Errorf("Error parsing build timeout : %s", err) + } + + var stepTimeoutSum time.Duration = 0 + steps := build["step"].([]interface{}) + for _, rawstep := range steps { + if rawstep == nil { + continue + } + step := rawstep.(map[string]interface{}) + timeoutString := step["timeout"].(string) + if len(timeoutString) == 0 { + continue + } + + timeout, err := time.ParseDuration(timeoutString) + if err != nil { + return fmt.Errorf("Error parsing build step timeout: %s", err) + } + stepTimeoutSum += timeout + } + if stepTimeoutSum > buildTimeout { + return fmt.Errorf("Step timeout sum (%v) cannot be greater than build timeout (%v)", stepTimeoutSum, buildTimeout) + } + return nil +} + func resourceCloudBuildTrigger() *schema.Resource { return &schema.Resource{ Create: resourceCloudBuildTriggerCreate, @@ -41,6 +78,7 @@ func resourceCloudBuildTrigger() *schema.Resource { }, SchemaVersion: 1, + CustomizeDiff: stepTimeoutCustomizeDiff, Schema: map[string]*schema.Schema{ "build": { @@ -220,6 +258,16 @@ If any of the images fail to be pushed, the build status is marked FAILURE.`, Type: schema.TypeString, }, }, + "timeout": { + Type: schema.TypeString, + Optional: true, + Description: `Amount of time that this build should be allowed to run, to second granularity. +If this amount of time elapses, work on the build will cease and the build status will be TIMEOUT. +This timeout must be equal to or greater than the sum of the timeouts for build steps within the build. +The expected format is the number of seconds followed by s. +Default time is ten minutes (600s).`, + Default: "600s", + }, }, }, ExactlyOneOf: []string{"filename", "build"}, @@ -744,6 +792,8 @@ func flattenCloudBuildTriggerBuild(v interface{}, d *schema.ResourceData) interf flattenCloudBuildTriggerBuildTags(original["tags"], d) transformed["images"] = flattenCloudBuildTriggerBuildImages(original["images"], d) + transformed["timeout"] = + flattenCloudBuildTriggerBuildTimeout(original["timeout"], d) transformed["step"] = flattenCloudBuildTriggerBuildStep(original["steps"], d) return []interface{}{transformed} @@ -756,6 +806,10 @@ func flattenCloudBuildTriggerBuildImages(v interface{}, d *schema.ResourceData) return v } +func flattenCloudBuildTriggerBuildTimeout(v interface{}, d *schema.ResourceData) interface{} { + return v +} + func flattenCloudBuildTriggerBuildStep(v interface{}, d *schema.ResourceData) interface{} { if v == nil { return v @@ -987,6 +1041,13 @@ func expandCloudBuildTriggerBuild(v interface{}, d TerraformResourceData, config transformed["images"] = transformedImages } + transformedTimeout, err := expandCloudBuildTriggerBuildTimeout(original["timeout"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedTimeout); val.IsValid() && !isEmptyValue(val) { + transformed["timeout"] = transformedTimeout + } + transformedStep, err := expandCloudBuildTriggerBuildStep(original["step"], d, config) if err != nil { return nil, err @@ -1005,6 +1066,10 @@ func expandCloudBuildTriggerBuildImages(v interface{}, d TerraformResourceData, return v, nil } +func expandCloudBuildTriggerBuildTimeout(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + func expandCloudBuildTriggerBuildStep(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { l := v.([]interface{}) req := make([]interface{}, 0, len(l)) diff --git a/google/resource_cloudbuild_trigger_test.go b/google/resource_cloudbuild_trigger_test.go index 08882da12c1..533774c9f08 100644 --- a/google/resource_cloudbuild_trigger_test.go +++ b/google/resource_cloudbuild_trigger_test.go @@ -2,6 +2,7 @@ package google import ( "fmt" + "regexp" "testing" "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" @@ -37,6 +38,42 @@ func TestAccCloudBuildTrigger_basic(t *testing.T) { }) } +func TestAccCloudBuildTrigger_customizeDiffTimeoutSum(t *testing.T) { + t.Parallel() + + name := acctest.RandomWithPrefix("tf-test") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudBuildTriggerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudBuildTrigger_customizeDiffTimeoutSum(name), + ExpectError: regexp.MustCompile("cannot be greater than build timeout"), + }, + }, + }) +} + +func TestAccCloudBuildTrigger_customizeDiffTimeoutFormat(t *testing.T) { + t.Parallel() + + name := acctest.RandomWithPrefix("tf-test") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudBuildTriggerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudBuildTrigger_customizeDiffTimeoutFormat(name), + ExpectError: regexp.MustCompile("Error parsing build timeout"), + }, + }, + }) +} + func TestAccCloudBuildTrigger_disable(t *testing.T) { t.Parallel() name := acctest.RandomWithPrefix("tf-test") @@ -98,18 +135,22 @@ resource "google_cloudbuild_trigger" "build_trigger" { build { images = ["gcr.io/$PROJECT_ID/$REPO_NAME:$COMMIT_SHA"] tags = ["team-a", "service-b"] + timeout = "1800s" step { name = "gcr.io/cloud-builders/gsutil" args = ["cp", "gs://mybucket/remotefile.zip", "localfile.zip"] + timeout = "300s" } step { name = "gcr.io/cloud-builders/go" args = ["build", "my_package"] env = ["env1=two"] + timeout = "300s" } step { name = "gcr.io/cloud-builders/docker" args = ["build", "-t", "gcr.io/$PROJECT_ID/$REPO_NAME:$COMMIT_SHA", "-f", "Dockerfile", "."] + timeout = "300s" } } } @@ -185,21 +226,83 @@ resource "google_cloudbuild_trigger" "build_trigger" { build { images = ["gcr.io/$PROJECT_ID/$REPO_NAME:$SHORT_SHA"] tags = ["team-a", "service-b", "updated"] + timeout = "2100s" step { name = "gcr.io/cloud-builders/gsutil" args = ["cp", "gs://mybucket/remotefile.zip", "localfile-updated.zip"] + timeout = "300s" } step { name = "gcr.io/cloud-builders/go" args = ["build", "my_package_updated"] + timeout = "300s" } step { name = "gcr.io/cloud-builders/docker" args = ["build", "-t", "gcr.io/$PROJECT_ID/$REPO_NAME:$SHORT_SHA", "-f", "Dockerfile", "."] + timeout = "300s" } step { name = "gcr.io/$PROJECT_ID/$REPO_NAME:$SHORT_SHA" args = ["test"] + timeout = "300s" + } + } +} + `, name) +} + +func testAccCloudBuildTrigger_customizeDiffTimeoutSum(name string) string { + return fmt.Sprintf(` +resource "google_cloudbuild_trigger" "build_trigger" { + name = "%s" + description = "acceptance test build trigger" + trigger_template { + branch_name = "master" + repo_name = "some-repo" + } + build { + images = ["gcr.io/$PROJECT_ID/$REPO_NAME:$COMMIT_SHA"] + tags = ["team-a", "service-b"] + timeout = "900s" + step { + name = "gcr.io/cloud-builders/gsutil" + args = ["cp", "gs://mybucket/remotefile.zip", "localfile.zip"] + timeout = "500s" + } + step { + name = "gcr.io/cloud-builders/go" + args = ["build", "my_package"] + env = ["env1=two"] + timeout = "500s" + } + step { + name = "gcr.io/cloud-builders/docker" + args = ["build", "-t", "gcr.io/$PROJECT_ID/$REPO_NAME:$COMMIT_SHA", "-f", "Dockerfile", "."] + timeout = "500s" + } + } +} + `, name) +} + +func testAccCloudBuildTrigger_customizeDiffTimeoutFormat(name string) string { + return fmt.Sprintf(` +resource "google_cloudbuild_trigger" "build_trigger" { + name = "%s" + description = "acceptance test build trigger" + trigger_template { + branch_name = "master" + repo_name = "some-repo" + } + build { + images = ["gcr.io/$PROJECT_ID/$REPO_NAME:$COMMIT_SHA"] + tags = ["team-a", "service-b"] + timeout = "1200" + step { + name = "gcr.io/cloud-builders/gsutil" + args = ["cp", "gs://mybucket/remotefile.zip", "localfile.zip"] + timeout = "500s" } } } diff --git a/website/docs/r/cloudbuild_trigger.html.markdown b/website/docs/r/cloudbuild_trigger.html.markdown index 1cbcad92aa8..03d058d6b8b 100644 --- a/website/docs/r/cloudbuild_trigger.html.markdown +++ b/website/docs/r/cloudbuild_trigger.html.markdown @@ -167,6 +167,14 @@ The `build` block supports: The digests of the pushed images will be stored in the Build resource's results field. If any of the images fail to be pushed, the build status is marked FAILURE. +* `timeout` - + (Optional) + Amount of time that this build should be allowed to run, to second granularity. + If this amount of time elapses, work on the build will cease and the build status will be TIMEOUT. + This timeout must be equal to or greater than the sum of the timeouts for build steps within the build. + The expected format is the number of seconds followed by s. + Default time is ten minutes (600s). + * `step` - (Required) The operations to be performed on the workspace. Structure is documented below.