From d1dbdb02528d4320a52a74343ccef31ea9535222 Mon Sep 17 00:00:00 2001 From: The Magician Date: Mon, 1 Apr 2019 18:14:13 -0700 Subject: [PATCH] More Cloudbuild Trigger Step support (#3346) /cc @chrisst --- google/resource_cloud_build_trigger.go | 261 +++++++++++++++++- google/resource_cloudbuild_trigger_test.go | 47 ++++ .../docs/r/cloud_build_trigger.html.markdown | 81 ++++++ 3 files changed, 387 insertions(+), 2 deletions(-) diff --git a/google/resource_cloud_build_trigger.go b/google/resource_cloud_build_trigger.go index 33d01b65..bdb9d9ae 100644 --- a/google/resource_cloud_build_trigger.go +++ b/google/resource_cloud_build_trigger.go @@ -68,10 +68,67 @@ func resourceCloudBuildTrigger() *schema.Resource { Type: schema.TypeString, }, }, + "dir": { + Type: schema.TypeString, + Optional: true, + }, + "entrypoint": { + Type: schema.TypeString, + Optional: true, + }, + "env": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "id": { + Type: schema.TypeString, + Optional: true, + }, "name": { Type: schema.TypeString, Optional: true, }, + "secret_env": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "timeout": { + Type: schema.TypeString, + Optional: true, + }, + "timing": { + Type: schema.TypeString, + Optional: true, + }, + "volumes": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Optional: true, + }, + "path": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "wait_for": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, }, }, }, @@ -538,8 +595,17 @@ func flattenCloudBuildTriggerBuildStep(v interface{}, d *schema.ResourceData) in continue } transformed = append(transformed, map[string]interface{}{ - "name": flattenCloudBuildTriggerBuildStepName(original["name"], d), - "args": flattenCloudBuildTriggerBuildStepArgs(original["args"], d), + "name": flattenCloudBuildTriggerBuildStepName(original["name"], d), + "args": flattenCloudBuildTriggerBuildStepArgs(original["args"], d), + "env": flattenCloudBuildTriggerBuildStepEnv(original["env"], d), + "id": flattenCloudBuildTriggerBuildStepId(original["id"], d), + "entrypoint": flattenCloudBuildTriggerBuildStepEntrypoint(original["entrypoint"], d), + "dir": flattenCloudBuildTriggerBuildStepDir(original["dir"], d), + "secret_env": flattenCloudBuildTriggerBuildStepSecretEnv(original["secretEnv"], d), + "timeout": flattenCloudBuildTriggerBuildStepTimeout(original["timeout"], d), + "timing": flattenCloudBuildTriggerBuildStepTiming(original["timing"], d), + "volumes": flattenCloudBuildTriggerBuildStepVolumes(original["volumes"], d), + "wait_for": flattenCloudBuildTriggerBuildStepWaitFor(original["waitFor"], d), }) } return transformed @@ -552,6 +618,65 @@ func flattenCloudBuildTriggerBuildStepArgs(v interface{}, d *schema.ResourceData return v } +func flattenCloudBuildTriggerBuildStepEnv(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func flattenCloudBuildTriggerBuildStepId(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func flattenCloudBuildTriggerBuildStepEntrypoint(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func flattenCloudBuildTriggerBuildStepDir(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func flattenCloudBuildTriggerBuildStepSecretEnv(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func flattenCloudBuildTriggerBuildStepTimeout(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func flattenCloudBuildTriggerBuildStepTiming(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func flattenCloudBuildTriggerBuildStepVolumes(v interface{}, d *schema.ResourceData) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "name": flattenCloudBuildTriggerBuildStepVolumesName(original["name"], d), + "path": flattenCloudBuildTriggerBuildStepVolumesPath(original["path"], d), + }) + } + return transformed +} +func flattenCloudBuildTriggerBuildStepVolumesName(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func flattenCloudBuildTriggerBuildStepVolumesPath(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func flattenCloudBuildTriggerBuildStepWaitFor(v interface{}, d *schema.ResourceData) interface{} { + return v +} + func expandCloudBuildTriggerDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } @@ -726,6 +851,69 @@ func expandCloudBuildTriggerBuildStep(v interface{}, d TerraformResourceData, co transformed["args"] = transformedArgs } + transformedEnv, err := expandCloudBuildTriggerBuildStepEnv(original["env"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEnv); val.IsValid() && !isEmptyValue(val) { + transformed["env"] = transformedEnv + } + + transformedId, err := expandCloudBuildTriggerBuildStepId(original["id"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedId); val.IsValid() && !isEmptyValue(val) { + transformed["id"] = transformedId + } + + transformedEntrypoint, err := expandCloudBuildTriggerBuildStepEntrypoint(original["entrypoint"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEntrypoint); val.IsValid() && !isEmptyValue(val) { + transformed["entrypoint"] = transformedEntrypoint + } + + transformedDir, err := expandCloudBuildTriggerBuildStepDir(original["dir"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDir); val.IsValid() && !isEmptyValue(val) { + transformed["dir"] = transformedDir + } + + transformedSecretEnv, err := expandCloudBuildTriggerBuildStepSecretEnv(original["secret_env"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSecretEnv); val.IsValid() && !isEmptyValue(val) { + transformed["secretEnv"] = transformedSecretEnv + } + + transformedTimeout, err := expandCloudBuildTriggerBuildStepTimeout(original["timeout"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedTimeout); val.IsValid() && !isEmptyValue(val) { + transformed["timeout"] = transformedTimeout + } + + transformedTiming, err := expandCloudBuildTriggerBuildStepTiming(original["timing"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedTiming); val.IsValid() && !isEmptyValue(val) { + transformed["timing"] = transformedTiming + } + + transformedVolumes, err := expandCloudBuildTriggerBuildStepVolumes(original["volumes"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedVolumes); val.IsValid() && !isEmptyValue(val) { + transformed["volumes"] = transformedVolumes + } + + transformedWaitFor, err := expandCloudBuildTriggerBuildStepWaitFor(original["wait_for"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedWaitFor); val.IsValid() && !isEmptyValue(val) { + transformed["waitFor"] = transformedWaitFor + } + req = append(req, transformed) } return req, nil @@ -738,3 +926,72 @@ func expandCloudBuildTriggerBuildStepName(v interface{}, d TerraformResourceData func expandCloudBuildTriggerBuildStepArgs(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } + +func expandCloudBuildTriggerBuildStepEnv(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudBuildTriggerBuildStepId(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudBuildTriggerBuildStepEntrypoint(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudBuildTriggerBuildStepDir(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudBuildTriggerBuildStepSecretEnv(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudBuildTriggerBuildStepTimeout(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudBuildTriggerBuildStepTiming(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudBuildTriggerBuildStepVolumes(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedName, err := expandCloudBuildTriggerBuildStepVolumesName(original["name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedName); val.IsValid() && !isEmptyValue(val) { + transformed["name"] = transformedName + } + + transformedPath, err := expandCloudBuildTriggerBuildStepVolumesPath(original["path"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPath); val.IsValid() && !isEmptyValue(val) { + transformed["path"] = transformedPath + } + + req = append(req, transformed) + } + return req, nil +} + +func expandCloudBuildTriggerBuildStepVolumesName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudBuildTriggerBuildStepVolumesPath(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudBuildTriggerBuildStepWaitFor(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} diff --git a/google/resource_cloudbuild_trigger_test.go b/google/resource_cloudbuild_trigger_test.go index 02fb0fc1..f476cc7e 100644 --- a/google/resource_cloudbuild_trigger_test.go +++ b/google/resource_cloudbuild_trigger_test.go @@ -35,6 +35,26 @@ func TestAccCloudBuildTrigger_basic(t *testing.T) { }) } +func TestAccCloudBuildTrigger_fullStep(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudBuildTriggerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudBuildTrigger_fullStep(), + }, + { + ResourceName: "google_cloudbuild_trigger.build_trigger", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testGoogleCloudBuildTrigger_basic() string { return fmt.Sprintf(` resource "google_cloudbuild_trigger" "build_trigger" { @@ -53,6 +73,7 @@ resource "google_cloudbuild_trigger" "build_trigger" { step { name = "gcr.io/cloud-builders/go" args = ["build", "my_package"] + env = ["env1=two"] } step { name = "gcr.io/cloud-builders/docker" @@ -63,6 +84,32 @@ resource "google_cloudbuild_trigger" "build_trigger" { `) } +func testAccCloudBuildTrigger_fullStep() string { + return fmt.Sprintf(` +resource "google_cloudbuild_trigger" "build_trigger" { + 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"] + step { + name = "gcr.io/cloud-builders/go" + args = ["build", "my_package"] + env = ["env1=two"] + dir = "directory" + id = "12345" + secret_env = ["fooo"] + timeout = "100s" + wait_for = ["something"] + } + } +} + `) +} + func testGoogleCloudBuildTrigger_updated() string { return fmt.Sprintf(` resource "google_cloudbuild_trigger" "build_trigger" { diff --git a/website/docs/r/cloud_build_trigger.html.markdown b/website/docs/r/cloud_build_trigger.html.markdown index a88c51eb..35f246fa 100644 --- a/website/docs/r/cloud_build_trigger.html.markdown +++ b/website/docs/r/cloud_build_trigger.html.markdown @@ -188,6 +188,87 @@ The `step` block supports: entrypoint, the first element in args is used as the entrypoint, and the remainder will be used as arguments. +* `env` - + (Optional) + A list of environment variable definitions to be used when + running a step. + The elements are of the form "KEY=VALUE" for the environment variable + "KEY" being given the value "VALUE". + +* `id` - + (Optional) + Unique identifier for this build step, used in `wait_for` to + reference this build step as a dependency. + +* `entrypoint` - + (Optional) + Entrypoint to be used instead of the build step image's + default entrypoint. + If unset, the image's default entrypoint is used + +* `dir` - + (Optional) + Working directory to use when running this step's container. + If this value is a relative path, it is relative to the build's working + directory. If this value is absolute, it may be outside the build's working + directory, in which case the contents of the path may not be persisted + across build step executions, unless a `volume` for that path is specified. + If the build specifies a `RepoSource` with `dir` and a step with a + `dir`, + which specifies an absolute path, the `RepoSource` `dir` is ignored + for the step's execution. + +* `secret_env` - + (Optional) + A list of environment variables which are encrypted using + a Cloud Key + Management Service crypto key. These values must be specified in + the build's `Secret`. + +* `timeout` - + (Optional) + Time limit for executing this build step. If not defined, + the step has no + time limit and will be allowed to continue to run until either it + completes or the build itself times out. + +* `timing` - + (Optional) + Output only. Stores timing information for executing this + build step. + +* `volumes` - + (Optional) + List of volumes to mount into the build step. + Each volume is created as an empty volume prior to execution of the + build step. Upon completion of the build, volumes and their contents + are discarded. + Using a named volume in only one step is not valid as it is + indicative of a build request with an incorrect configuration. Structure is documented below. + +* `wait_for` - + (Optional) + The ID(s) of the step(s) that this build step depends on. + This build step will not start until all the build steps in `wait_for` + have completed successfully. If `wait_for` is empty, this build step + will start when all previous build steps in the `Build.Steps` list + have completed successfully. + + +The `volumes` block supports: + +* `name` - + (Optional) + Name of the volume to mount. + Volume names must be unique per build step and must be valid names for + Docker volumes. Each named volume must be used by at least two build steps. + +* `path` - + (Optional) + Path at which to mount the volume. + Paths must be absolute and cannot conflict with other volume paths on + the same build step or with certain reserved volume paths. + ## Attributes Reference In addition to the arguments listed above, the following computed attributes are exported: