diff --git a/mmv1/products/cloudrun/api.yaml b/mmv1/products/cloudrun/api.yaml index 8119a67ed6d8..28bfb97bd183 100644 --- a/mmv1/products/cloudrun/api.yaml +++ b/mmv1/products/cloudrun/api.yaml @@ -18,6 +18,9 @@ versions: - !ruby/object:Api::Product::Version name: ga base_url: https://{{location}}-run.googleapis.com/ + - !ruby/object:Api::Product::Version + name: beta + base_url: https://{{location}}-run.googleapis.com/ scopes: - https://www.googleapis.com/auth/cloud-platform objects: @@ -515,6 +518,37 @@ objects: references will never be expanded, regardless of whether the variable exists or not. Defaults to "". + - !ruby/object:Api::Type::NestedObject + name: valueFrom + min_version: beta + description: |- + Source for the environment variable's value. Only supports secret_key_ref. + properties: + - !ruby/object:Api::Type::NestedObject + name: secretKeyRef + min_version: beta + description: |- + Selects a key (version) of a secret in Secret Manager. + properties: + - !ruby/object:Api::Type::String + name: key + min_version: beta + description: |- + A Cloud Secret Manager secret version. Must be 'latest' for the latest + version or an integer for a specific version. + - !ruby/object:Api::Type::String + name: name + min_version: beta + description: |- + The name of the secret in Cloud Secret Manager. By default, the secret + is assumed to be in the same project. + If the secret is in another project, you must define an alias. + An alias definition has the form: + :projects//secrets/. + If multiple alias definitions are needed, they must be separated by + commas. + The alias definitions must be set on the run.googleapis.com/secrets + annotation. - !ruby/object:Api::Type::Array name: ports description: |- @@ -554,6 +588,27 @@ objects: explicitly specified, otherwise to an implementation-defined value. The values of the map is string form of the 'quantity' k8s type: https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity.go + - !ruby/object:Api::Type::Array + name: volumeMounts + description: |- + Volume to mount into the container's filesystem. + Only supports SecretVolumeSources. + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::String + name: mountPath + description: |- + Path within the container at which the volume should be mounted. Must + not contain ':'. + - !ruby/object:Api::Type::String + name: name + description: |- + This must match the Name of a Volume. + - !ruby/object:Api::Type::Boolean + name: readOnly + description: |- + Only true is accepted. + Defaults to true. - !ruby/object:Api::Type::Integer name: containerConcurrency @@ -575,6 +630,57 @@ objects: service. The service account represents the identity of the running revision, and determines what permissions the revision has. If not provided, the revision will use the project's default service account. + - !ruby/object:Api::Type::Array + name: volumes + description: |- + Volume represents a named volume in a container. + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::String + name: name + description: |- + Volume's name. + - !ruby/object:Api::Type::NestedObject + name: secret + description: |- + The secret's value will be presented as the content of a file whose + name is defined in the item path. If no items are defined, the name of + the file is the secret_name. + properties: + - !ruby/object:Api::Type::String + name: secretName + description: |- + The name of the secret in Cloud Secret Manager. By default, the secret + is assumed to be in the same project. + If the secret is in another project, you must define an alias. + An alias definition has the form: + :projects//secrets/. + If multiple alias definitions are needed, they must be separated by + commas. + The alias definitions must be set on the run.googleapis.com/secrets + annotation. + - !ruby/object:Api::Type::Array + name: items + description: |- + If unspecified, the volume will expose a file whose name is the + secret_name. + If specified, the key will be used as the version to fetch from Cloud + Secret Manager and the path will be the name of the file exposed in the + volume. When items are defined, they must specify a key and a path. + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::String + name: key + description: |- + The Cloud Secret Manager secret version. + Can be 'latest' for the latest value or an integer for a specific version. + - !ruby/object:Api::Type::String + name: path + description: |- + The relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. - !ruby/object:Api::Type::Enum name: servingState deprecation_message: "Not supported by Cloud Run fully managed" diff --git a/mmv1/products/cloudrun/terraform.yaml b/mmv1/products/cloudrun/terraform.yaml index 1a87cd3a8a8e..bebeac67bdb3 100644 --- a/mmv1/products/cloudrun/terraform.yaml +++ b/mmv1/products/cloudrun/terraform.yaml @@ -121,6 +121,30 @@ overrides: !ruby/object:Overrides::ResourceOverrides cloud_run_service_name: "cloudrun-srv" test_env_vars: project: :PROJECT_NAME + - !ruby/object:Provider::Terraform::Examples + name: "cloud_run_service_secret_environment_variables" + min_version: beta + primary_resource_id: "default" + primary_resource_name: "fmt.Sprintf(\"tf-test-cloudrun-srv%s\", context[\"random_suffix\"])" + vars: + cloud_run_service_name: "cloudrun-srv" + secret_id: "secret" + test_env_vars: + project: :PROJECT_NAME + ignore_read_extra: + - "autogenerate_revision_name" + - !ruby/object:Provider::Terraform::Examples + name: "cloud_run_service_secret_volumes" + min_version: beta + primary_resource_id: "default" + primary_resource_name: "fmt.Sprintf(\"tf-test-cloudrun-srv%s\", context[\"random_suffix\"])" + vars: + cloud_run_service_name: "cloudrun-srv" + secret_id: "secret" + test_env_vars: + project: :PROJECT_NAME + ignore_read_extra: + - "autogenerate_revision_name" virtual_fields: - !ruby/object:Api::Type::Boolean name: 'autogenerate_revision_name' diff --git a/mmv1/templates/terraform/examples/cloud_run_service_secret_environment_variables.tf.erb b/mmv1/templates/terraform/examples/cloud_run_service_secret_environment_variables.tf.erb new file mode 100644 index 000000000000..bcdb05a0b4d9 --- /dev/null +++ b/mmv1/templates/terraform/examples/cloud_run_service_secret_environment_variables.tf.erb @@ -0,0 +1,81 @@ +data "google_project" "project" { + provider = google-beta +} + +resource "google_project_service" "secret-manager" { + provider = google-beta + + service = "secretmanager.googleapis.com" +} + +resource "google_secret_manager_secret" "secret" { + provider = google-beta + + secret_id = "<%= ctx[:vars]['secret_id'] %>" + replication { + automatic = true + } + + depends_on = [google_project_service.secret-manager] +} + +resource "google_secret_manager_secret_version" "secret-version-data" { + provider = google-beta + + secret = google_secret_manager_secret.secret.name + secret_data = "secret-data" +} + +resource "google_secret_manager_secret_iam_member" "secret-access" { + provider = google-beta + + secret_id = google_secret_manager_secret.secret.id + role = "roles/secretmanager.secretAccessor" + member = "serviceAccount:${data.google_project.project.number}-compute@developer.gserviceaccount.com" + depends_on = [google_secret_manager_secret.secret] +} + +resource "google_cloud_run_service" "<%= ctx[:primary_resource_id] %>" { + provider = google-beta + + name = "<%= ctx[:vars]['cloud_run_service_name'] %>" + location = "us-central1" + + template { + spec { + containers { + image = "gcr.io/cloudrun/hello" + env { + name = "SECRET_ENV_VAR" + value_from { + secret_key_ref { + name = google_secret_manager_secret.secret.secret_id + key = "1" + } + } + } + } + } + } + + metadata { + annotations = { + generated-by = "magic-modules" + "run.googleapis.com/launch-stage" = "ALPHA" + } + } + + traffic { + percent = 100 + latest_revision = true + } + autogenerate_revision_name = true + + lifecycle { + ignore_changes = [ + metadata.0.annotations, + ] + } + + depends_on = [google_secret_manager_secret_version.secret-version-data] +} diff --git a/mmv1/templates/terraform/examples/cloud_run_service_secret_volumes.tf.erb b/mmv1/templates/terraform/examples/cloud_run_service_secret_volumes.tf.erb new file mode 100644 index 000000000000..6a499cd4c4d8 --- /dev/null +++ b/mmv1/templates/terraform/examples/cloud_run_service_secret_volumes.tf.erb @@ -0,0 +1,86 @@ +data "google_project" "project" { + provider = google-beta +} + +resource "google_project_service" "secret-manager" { + provider = google-beta + + service = "secretmanager.googleapis.com" +} + +resource "google_secret_manager_secret" "secret" { + provider = google-beta + + secret_id = "<%= ctx[:vars]['secret_id'] %>" + replication { + automatic = true + } + + depends_on = [google_project_service.secret-manager] +} + +resource "google_secret_manager_secret_version" "secret-version-data" { + provider = google-beta + + secret = google_secret_manager_secret.secret.name + secret_data = "secret-data" +} + +resource "google_secret_manager_secret_iam_member" "secret-access" { + provider = google-beta + + secret_id = google_secret_manager_secret.secret.id + role = "roles/secretmanager.secretAccessor" + member = "serviceAccount:${data.google_project.project.number}-compute@developer.gserviceaccount.com" + depends_on = [google_secret_manager_secret.secret] +} + +resource "google_cloud_run_service" "<%= ctx[:primary_resource_id] %>" { + provider = google-beta + + name = "<%= ctx[:vars]['cloud_run_service_name'] %>" + location = "us-central1" + + template { + spec { + containers { + image = "gcr.io/cloudrun/hello" + volume_mounts { + name = "a-volume" + mount_path = "/secrets" + } + } + volumes { + name = "a-volume" + secret { + secret_name = google_secret_manager_secret.secret.secret_id + items { + key = "1" + path = "my-secret" + } + } + } + } + } + + metadata { + annotations = { + generated-by = "magic-modules" + "run.googleapis.com/launch-stage" = "ALPHA" + } + } + + traffic { + percent = 100 + latest_revision = true + } + autogenerate_revision_name = true + + lifecycle { + ignore_changes = [ + metadata.0.annotations, + ] + } + + depends_on = [google_secret_manager_secret_version.secret-version-data] +} diff --git a/mmv1/third_party/terraform/tests/resource_cloud_run_service_secret_environment_variable_test.go.erb b/mmv1/third_party/terraform/tests/resource_cloud_run_service_secret_environment_variable_test.go.erb new file mode 100644 index 000000000000..3abf7d7e3340 --- /dev/null +++ b/mmv1/third_party/terraform/tests/resource_cloud_run_service_secret_environment_variable_test.go.erb @@ -0,0 +1,118 @@ +<% autogen_exception -%> +package google + +<% unless version == 'ga' %> +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccCloudRunService_secretEnvironmentVariable(t *testing.T) { + t.Parallel() + + project := getTestProjectFromEnv() + name := "tftest-cloudrun-" + randString(t, 6) + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCloudRunService_cloudRunServiceUpdateWithSecretEnvVar(name, project, "secret-"+randString(t, 5)), + }, + { + ResourceName: "google_cloud_run_service.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"metadata.0.resource_version", "status.0.conditions"}, + }, + { + Config: testAccCloudRunService_cloudRunServiceUpdateWithSecretEnvVar(name, project, "secret-"+randString(t, 10)), + }, + { + ResourceName: "google_cloud_run_service.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"metadata.0.resource_version", "status.0.conditions"}, + }, + }, + }) +} + +func testAccCloudRunService_cloudRunServiceUpdateWithSecretEnvVar(name, project, secretName string) string { + return fmt.Sprintf(` +data "google_project" "project" { +} + +resource "google_project_service" "secret-manager" { + service = "secretmanager.googleapis.com" +} + +resource "google_secret_manager_secret" "secret" { + secret_id = "%s" + replication { + automatic = true + } + + depends_on = [google_project_service.secret-manager] +} + +resource "google_secret_manager_secret_version" "secret-version-data" { + secret = google_secret_manager_secret.secret.name + secret_data = "secret-data" +} + +resource "google_secret_manager_secret_iam_member" "secret-access" { + secret_id = google_secret_manager_secret.secret.id + role = "roles/secretmanager.secretAccessor" + member = "serviceAccount:${data.google_project.project.number}-compute@developer.gserviceaccount.com" + depends_on = [google_secret_manager_secret.secret] +} + +resource "google_cloud_run_service" "default" { + name = "%s" + location = "us-central1" + + template { + spec { + containers { + image = "gcr.io/cloudrun/hello" + env { + name = "SECRET_ENV_VAR" + value_from { + secret_key_ref { + name = google_secret_manager_secret.secret.secret_id + key = "1" + } + } + } + } + } + } + + metadata { + namespace = "%s" + annotations = { + generated-by = "magic-modules" + "run.googleapis.com/launch-stage" = "ALPHA" + } + } + + traffic { + percent = 100 + latest_revision = true + } + + lifecycle { + ignore_changes = [ + metadata.0.annotations, + ] + } + + depends_on = [google_secret_manager_secret_version.secret-version-data] +} +`, secretName, name, project) +} +<% end -%> diff --git a/mmv1/third_party/terraform/tests/resource_cloud_run_service_secret_volume_test.go.erb b/mmv1/third_party/terraform/tests/resource_cloud_run_service_secret_volume_test.go.erb new file mode 100644 index 000000000000..22e4314939f5 --- /dev/null +++ b/mmv1/third_party/terraform/tests/resource_cloud_run_service_secret_volume_test.go.erb @@ -0,0 +1,122 @@ +<% autogen_exception -%> +package google + +<% unless version == 'ga' %> +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccCloudRunService_secretVolume(t *testing.T) { + t.Parallel() + + project := getTestProjectFromEnv() + name := "tftest-cloudrun-" + randString(t, 6) + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCloudRunService_cloudRunServiceUpdateWithSecretVolume(name, project, "secret-"+randString(t, 5)), + }, + { + ResourceName: "google_cloud_run_service.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"metadata.0.resource_version", "status.0.conditions"}, + }, + { + Config: testAccCloudRunService_cloudRunServiceUpdateWithSecretVolume(name, project, "secret-"+randString(t, 10)), + }, + { + ResourceName: "google_cloud_run_service.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"metadata.0.resource_version", "status.0.conditions"}, + }, + }, + }) +} + +func testAccCloudRunService_cloudRunServiceUpdateWithSecretVolume(name, project, secretName string) string { + return fmt.Sprintf(` +data "google_project" "project" { +} + +resource "google_project_service" "secret-manager" { + service = "secretmanager.googleapis.com" +} + +resource "google_secret_manager_secret" "secret" { + secret_id = "%s" + replication { + automatic = true + } + depends_on = [google_project_service.secret-manager] +} + +resource "google_secret_manager_secret_version" "secret-version-data" { + secret = google_secret_manager_secret.secret.name + secret_data = "secret-data" +} + +resource "google_secret_manager_secret_iam_member" "secret-access" { + secret_id = google_secret_manager_secret.secret.id + role = "roles/secretmanager.secretAccessor" + member = "serviceAccount:${data.google_project.project.number}-compute@developer.gserviceaccount.com" + depends_on = [google_secret_manager_secret.secret] +} + +resource "google_cloud_run_service" "default" { + name = "%s" + location = "us-central1" + + template { + spec { + containers { + image = "gcr.io/cloudrun/hello" + volume_mounts { + name = "a-volume" + mount_path = "/secrets" + } + } + volumes { + name = "a-volume" + secret { + secret_name = google_secret_manager_secret.secret.secret_id + items { + key = "1" + path = "my-secret" + } + } + } + } + } + + metadata { + namespace = "%s" + annotations = { + generated-by = "magic-modules" + "run.googleapis.com/launch-stage" = "ALPHA" + } + } + + traffic { + percent = 100 + latest_revision = true + } + + lifecycle { + ignore_changes = [ + metadata.0.annotations, + ] + } + + depends_on = [google_secret_manager_secret_version.secret-version-data] +} +`, secretName, name, project) +} +<% end -%>