diff --git a/.changelog/4977.txt b/.changelog/4977.txt new file mode 100644 index 00000000000..b0d41fa6e7c --- /dev/null +++ b/.changelog/4977.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +cloudbuild: added support for available_secrets to google_cloudbuild_trigger +``` diff --git a/google/resource_cloudbuild_trigger.go b/google/resource_cloudbuild_trigger.go index 29c15bece48..2c3fe4c3fca 100644 --- a/google/resource_cloudbuild_trigger.go +++ b/google/resource_cloudbuild_trigger.go @@ -325,6 +325,37 @@ nine fractional digits. Examples: "2014-10-02T15:01:23Z" and "2014-10-02T15:01:2 }, }, }, + "available_secrets": { + Type: schema.TypeList, + Optional: true, + Description: `Secrets and secret environment variables.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "secret_manager": { + Type: schema.TypeList, + Required: true, + Description: `Pairs a secret environment variable with a SecretVersion in Secret Manager.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "env": { + Type: schema.TypeString, + Required: true, + Description: `Environment variable name to associate with the secret. Secret environment +variables must be unique across all of a build's secrets, and must be used +by at least one build step.`, + }, + "version_name": { + Type: schema.TypeString, + Required: true, + Description: `Resource name of the SecretVersion. In format: projects/*/secrets/*/versions/*`, + }, + }, + }, + }, + }, + }, + }, "images": { Type: schema.TypeList, Optional: true, @@ -1615,6 +1646,8 @@ func flattenCloudBuildTriggerBuild(v interface{}, d *schema.ResourceData, config flattenCloudBuildTriggerBuildTimeout(original["timeout"], d, config) transformed["secret"] = flattenCloudBuildTriggerBuildSecret(original["secrets"], d, config) + transformed["available_secrets"] = + flattenCloudBuildTriggerBuildAvailableSecrets(original["availableSecrets"], d, config) transformed["step"] = flattenCloudBuildTriggerBuildStep(original["steps"], d, config) transformed["artifacts"] = @@ -1777,6 +1810,46 @@ func flattenCloudBuildTriggerBuildSecretSecretEnv(v interface{}, d *schema.Resou return v } +func flattenCloudBuildTriggerBuildAvailableSecrets(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["secret_manager"] = + flattenCloudBuildTriggerBuildAvailableSecretsSecretManager(original["secretManager"], d, config) + return []interface{}{transformed} +} +func flattenCloudBuildTriggerBuildAvailableSecretsSecretManager(v interface{}, d *schema.ResourceData, config *Config) 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{}{ + "version_name": flattenCloudBuildTriggerBuildAvailableSecretsSecretManagerVersionName(original["versionName"], d, config), + "env": flattenCloudBuildTriggerBuildAvailableSecretsSecretManagerEnv(original["env"], d, config), + }) + } + return transformed +} +func flattenCloudBuildTriggerBuildAvailableSecretsSecretManagerVersionName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudBuildTriggerBuildAvailableSecretsSecretManagerEnv(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + func flattenCloudBuildTriggerBuildStep(v interface{}, d *schema.ResourceData, config *Config) interface{} { if v == nil { return v @@ -2483,6 +2556,13 @@ func expandCloudBuildTriggerBuild(v interface{}, d TerraformResourceData, config transformed["secrets"] = transformedSecret } + transformedAvailableSecrets, err := expandCloudBuildTriggerBuildAvailableSecrets(original["available_secrets"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAvailableSecrets); val.IsValid() && !isEmptyValue(val) { + transformed["availableSecrets"] = transformedAvailableSecrets + } + transformedStep, err := expandCloudBuildTriggerBuildStep(original["step"], d, config) if err != nil { return nil, err @@ -2760,6 +2840,62 @@ func expandCloudBuildTriggerBuildSecretSecretEnv(v interface{}, d TerraformResou return m, nil } +func expandCloudBuildTriggerBuildAvailableSecrets(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedSecretManager, err := expandCloudBuildTriggerBuildAvailableSecretsSecretManager(original["secret_manager"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSecretManager); val.IsValid() && !isEmptyValue(val) { + transformed["secretManager"] = transformedSecretManager + } + + return transformed, nil +} + +func expandCloudBuildTriggerBuildAvailableSecretsSecretManager(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{}) + + transformedVersionName, err := expandCloudBuildTriggerBuildAvailableSecretsSecretManagerVersionName(original["version_name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedVersionName); val.IsValid() && !isEmptyValue(val) { + transformed["versionName"] = transformedVersionName + } + + transformedEnv, err := expandCloudBuildTriggerBuildAvailableSecretsSecretManagerEnv(original["env"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEnv); val.IsValid() && !isEmptyValue(val) { + transformed["env"] = transformedEnv + } + + req = append(req, transformed) + } + return req, nil +} + +func expandCloudBuildTriggerBuildAvailableSecretsSecretManagerVersionName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudBuildTriggerBuildAvailableSecretsSecretManagerEnv(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_generated_test.go b/google/resource_cloudbuild_trigger_generated_test.go index f381ca0184a..aae47c49e88 100644 --- a/google/resource_cloudbuild_trigger_generated_test.go +++ b/google/resource_cloudbuild_trigger_generated_test.go @@ -96,12 +96,13 @@ resource "google_cloudbuild_trigger" "build-trigger" { branch_name = "master" repo_name = "my-repo" } - + build { step { name = "gcr.io/cloud-builders/gsutil" args = ["cp", "gs://mybucket/remotefile.zip", "localfile.zip"] timeout = "120s" + secret_env = ["MY_SECRET"] } source { @@ -123,6 +124,12 @@ resource "google_cloudbuild_trigger" "build-trigger" { PASSWORD = "ZW5jcnlwdGVkLXBhc3N3b3JkCg==" } } + available_secrets { + secret_manager { + env = "MY_SECRET" + version_name = "projects/myProject/secrets/mySecret/versions/latest" + } + } artifacts { images = ["gcr.io/$PROJECT_ID/$REPO_NAME:$COMMIT_SHA"] objects { @@ -147,7 +154,7 @@ resource "google_cloudbuild_trigger" "build-trigger" { path = "v1" } } - } + } } `, context) } diff --git a/google/resource_cloudbuild_trigger_test.go b/google/resource_cloudbuild_trigger_test.go index 986516e5304..a256a59b106 100644 --- a/google/resource_cloudbuild_trigger_test.go +++ b/google/resource_cloudbuild_trigger_test.go @@ -37,6 +37,35 @@ func TestAccCloudBuildTrigger_basic(t *testing.T) { }) } +func TestAccCloudBuildTrigger_available_secrets_config(t *testing.T) { + t.Parallel() + name := fmt.Sprintf("tf-test-%d", randInt(t)) + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudBuildTriggerDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccCloudBuildTrigger_available_secrets_config(name), + }, + { + ResourceName: "google_cloudbuild_trigger.build_trigger", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccCloudBuildTrigger_available_secrets_config_update(name), + }, + { + ResourceName: "google_cloudbuild_trigger.build_trigger", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccCloudBuildTrigger_pubsub_config(t *testing.T) { t.Parallel() name := fmt.Sprintf("tf-test-%d", randInt(t)) @@ -339,6 +368,56 @@ resource "google_cloudbuild_trigger" "build_trigger" { `, name) } +func testAccCloudBuildTrigger_available_secrets_config(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 { + tags = ["team-a", "service-b"] + timeout = "1800s" + step { + name = "gcr.io/cloud-builders/gsutil" + args = ["cp", "gs://mybucket/remotefile.zip", "localfile.zip"] + timeout = "300s" + } + available_secrets { + secret_manager { + env = "MY_SECRET" + version_name = "projects/myProject/secrets/mySecret/versions/latest" + } + } + } +} +`, name) +} + +func testAccCloudBuildTrigger_available_secrets_config_update(name string) string { + return fmt.Sprintf(` +resource "google_cloudbuild_trigger" "build_trigger" { + name = "%s" + description = "acceptance test build trigger updated" + trigger_template { + branch_name = "master" + repo_name = "some-repo" + } + build { + tags = ["team-a", "service-b"] + timeout = "1800s" + step { + name = "gcr.io/cloud-builders/gsutil" + args = ["cp", "gs://mybucket/remotefile.zip", "localfile.zip"] + timeout = "300s" + } + } +} +`, name) +} + func testAccCloudBuildTrigger_pubsub_config(name string) string { return fmt.Sprintf(` resource "google_pubsub_topic" "build-trigger" { diff --git a/website/docs/r/cloudbuild_trigger.html.markdown b/website/docs/r/cloudbuild_trigger.html.markdown index 7270891ea4c..c555e8bab1e 100644 --- a/website/docs/r/cloudbuild_trigger.html.markdown +++ b/website/docs/r/cloudbuild_trigger.html.markdown @@ -70,12 +70,13 @@ resource "google_cloudbuild_trigger" "build-trigger" { branch_name = "master" repo_name = "my-repo" } - + build { step { name = "gcr.io/cloud-builders/gsutil" args = ["cp", "gs://mybucket/remotefile.zip", "localfile.zip"] timeout = "120s" + secret_env = ["MY_SECRET"] } source { @@ -97,6 +98,12 @@ resource "google_cloudbuild_trigger" "build-trigger" { PASSWORD = "ZW5jcnlwdGVkLXBhc3N3b3JkCg==" } } + available_secrets { + secret_manager { + env = "MY_SECRET" + version_name = "projects/myProject/secrets/mySecret/versions/latest" + } + } artifacts { images = ["gcr.io/$PROJECT_ID/$REPO_NAME:$COMMIT_SHA"] objects { @@ -121,7 +128,7 @@ resource "google_cloudbuild_trigger" "build-trigger" { path = "v1" } } - } + } } ```