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

Add support for baseImageUri and buildInfo in Cloud Run V2 Service.template.containers #21236

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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Add support for baseImageUri and buildInfo in Cloud Run V2 Service.te…
…mplate.containers (#12938)

[upstream:89ec7eb90c4024bf80f6fee0a217568e5d76dee1]

Signed-off-by: Modular Magician <magic-modules@google.com>
  • Loading branch information
modular-magician committed Feb 4, 2025
commit ce6e2a55246d0fa0ca9d04e83b1c650ab601ad8e
3 changes: 3 additions & 0 deletions .changelog/12938.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
cloudrunv2: added `base_image_uri` and `build_info` to `google_cloud_run_v2_service`
```
105 changes: 105 additions & 0 deletions google/services/cloudrunv2/resource_cloud_run_v2_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ This field follows Kubernetes annotations' namespacing, limits, and rules.`,
Type: schema.TypeString,
},
},
"base_image_uri": {
Type: schema.TypeString,
Optional: true,
Description: `Base image for this container. If set, it indicates that the service is enrolled into automatic base image update.`,
},
"command": {
Type: schema.TypeList,
Optional: true,
Expand Down Expand Up @@ -448,6 +453,25 @@ If not specified, defaults to the same value as container.ports[0].containerPort
Optional: true,
Description: `Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image.`,
},
"build_info": {
Type: schema.TypeList,
Computed: true,
Description: `The build info of the container image.`,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"function_target": {
Type: schema.TypeString,
Computed: true,
Description: `Entry point of the function when the image is a Cloud Run function.`,
},
"source_location": {
Type: schema.TypeString,
Computed: true,
Description: `Source code location of the image.`,
},
},
},
},
},
},
},
Expand Down Expand Up @@ -2109,6 +2133,8 @@ func flattenCloudRunV2ServiceTemplateContainers(v interface{}, d *schema.Resourc
"liveness_probe": flattenCloudRunV2ServiceTemplateContainersLivenessProbe(original["livenessProbe"], d, config),
"startup_probe": flattenCloudRunV2ServiceTemplateContainersStartupProbe(original["startupProbe"], d, config),
"depends_on": flattenCloudRunV2ServiceTemplateContainersDependsOn(original["dependsOn"], d, config),
"base_image_uri": flattenCloudRunV2ServiceTemplateContainersBaseImageUri(original["baseImageUri"], d, config),
"build_info": flattenCloudRunV2ServiceTemplateContainersBuildInfo(original["buildInfo"], d, config),
})
}
return transformed
Expand Down Expand Up @@ -2730,6 +2756,33 @@ func flattenCloudRunV2ServiceTemplateContainersDependsOn(v interface{}, d *schem
return v
}

func flattenCloudRunV2ServiceTemplateContainersBaseImageUri(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
return v
}

func flattenCloudRunV2ServiceTemplateContainersBuildInfo(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
if v == nil {
return nil
}
original := v.(map[string]interface{})
if len(original) == 0 {
return nil
}
transformed := make(map[string]interface{})
transformed["function_target"] =
flattenCloudRunV2ServiceTemplateContainersBuildInfoFunctionTarget(original["functionTarget"], d, config)
transformed["source_location"] =
flattenCloudRunV2ServiceTemplateContainersBuildInfoSourceLocation(original["source_location"], d, config)
return []interface{}{transformed}
}
func flattenCloudRunV2ServiceTemplateContainersBuildInfoFunctionTarget(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
return v
}

func flattenCloudRunV2ServiceTemplateContainersBuildInfoSourceLocation(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
return v
}

func flattenCloudRunV2ServiceTemplateVolumes(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
if v == nil {
return v
Expand Down Expand Up @@ -3751,6 +3804,20 @@ func expandCloudRunV2ServiceTemplateContainers(v interface{}, d tpgresource.Terr
transformed["dependsOn"] = transformedDependsOn
}

transformedBaseImageUri, err := expandCloudRunV2ServiceTemplateContainersBaseImageUri(original["base_image_uri"], d, config)
if err != nil {
return nil, err
} else if val := reflect.ValueOf(transformedBaseImageUri); val.IsValid() && !tpgresource.IsEmptyValue(val) {
transformed["baseImageUri"] = transformedBaseImageUri
}

transformedBuildInfo, err := expandCloudRunV2ServiceTemplateContainersBuildInfo(original["build_info"], d, config)
if err != nil {
return nil, err
} else if val := reflect.ValueOf(transformedBuildInfo); val.IsValid() && !tpgresource.IsEmptyValue(val) {
transformed["buildInfo"] = transformedBuildInfo
}

req = append(req, transformed)
}
return req, nil
Expand Down Expand Up @@ -4453,6 +4520,44 @@ func expandCloudRunV2ServiceTemplateContainersDependsOn(v interface{}, d tpgreso
return v, nil
}

func expandCloudRunV2ServiceTemplateContainersBaseImageUri(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
return v, nil
}

func expandCloudRunV2ServiceTemplateContainersBuildInfo(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.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{})

transformedFunctionTarget, err := expandCloudRunV2ServiceTemplateContainersBuildInfoFunctionTarget(original["function_target"], d, config)
if err != nil {
return nil, err
} else if val := reflect.ValueOf(transformedFunctionTarget); val.IsValid() && !tpgresource.IsEmptyValue(val) {
transformed["functionTarget"] = transformedFunctionTarget
}

transformedSourceLocation, err := expandCloudRunV2ServiceTemplateContainersBuildInfoSourceLocation(original["source_location"], d, config)
if err != nil {
return nil, err
} else if val := reflect.ValueOf(transformedSourceLocation); val.IsValid() && !tpgresource.IsEmptyValue(val) {
transformed["source_location"] = transformedSourceLocation
}

return transformed, nil
}

func expandCloudRunV2ServiceTemplateContainersBuildInfoFunctionTarget(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
return v, nil
}

func expandCloudRunV2ServiceTemplateContainersBuildInfoSourceLocation(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
return v, nil
}

func expandCloudRunV2ServiceTemplateVolumes(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
l := v.([]interface{})
req := make([]interface{}, 0, len(l))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ fields:
- field: 'scaling.min_instance_count'
- field: 'template.annotations'
- field: 'template.containers.args'
- field: 'template.containers.base_image_uri'
- field: 'template.containers.build_info.function_target'
- field: 'template.containers.build_info.source_location'
- field: 'template.containers.command'
- field: 'template.containers.depends_on'
- field: 'template.containers.env.name'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,7 @@ resource "google_cloud_run_v2_service" "default" {
template {
containers {
image = "us-docker.pkg.dev/cloudrun/container/hello"
base_image_uri = "us-central1-docker.pkg.dev/serverless-runtimes/google-22-full/runtimes/nodejs22"
}
}
build_config {
Expand Down
173 changes: 173 additions & 0 deletions google/services/cloudrunv2/resource_cloud_run_v2_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"time"

"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/plancheck"
"github.com/hashicorp/terraform-provider-google/google/acctest"
"github.com/hashicorp/terraform-provider-google/google/envvar"
"github.com/hashicorp/terraform-provider-google/google/services/cloudrunv2"
Expand Down Expand Up @@ -1069,3 +1070,175 @@ resource "google_cloud_run_v2_service" "default" {

`, context)
}

func TestAccCloudRunV2Service_cloudrunv2ServiceFunctionExample_update(t *testing.T) {
t.Parallel()

context := map[string]interface{}{
"zip_path": "./test-fixtures/function-source.zip",
"random_suffix": acctest.RandString(t, 10),
}

acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
CheckDestroy: testAccCheckCloudRunV2ServiceDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccCloudRunV2Service_cloudrunv2ServiceFunctionExample_full(context),
},
{
ResourceName: "google_cloud_run_v2_service.default",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"annotations", "deletion_protection", "labels", "location", "name", "terraform_labels"},
},
{
Config: testAccCloudRunV2Service_cloudrunv2ServiceFunctionExample_update(context),
ConfigPlanChecks: resource.ConfigPlanChecks{
PreApply: []plancheck.PlanCheck{
plancheck.ExpectResourceAction("google_cloud_run_v2_service.default", plancheck.ResourceActionUpdate),
},
},
},
{
ResourceName: "google_cloud_run_v2_service.default",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"annotations", "deletion_protection", "labels", "location", "name", "terraform_labels"},
},
},
})
}

func testAccCloudRunV2Service_cloudrunv2ServiceFunctionExample_full(context map[string]interface{}) string {
return acctest.Nprintf(`
resource "google_cloud_run_v2_service" "default" {
name = "tf-test-cloudrun-service%{random_suffix}"
location = "us-central1"
deletion_protection = false
ingress = "INGRESS_TRAFFIC_ALL"

template {
containers {
image = "us-docker.pkg.dev/cloudrun/container/hello"
base_image_uri = "us-central1-docker.pkg.dev/serverless-runtimes/google-22-full/runtimes/nodejs22"
}
}
build_config {
source_location = "gs://${google_storage_bucket.bucket.name}/${google_storage_bucket_object.object.name}"
function_target = "helloHttp"
image_uri = "us-docker.pkg.dev/cloudrun/container/hello"
base_image = "us-central1-docker.pkg.dev/serverless-runtimes/google-22-full/runtimes/nodejs22"
enable_automatic_updates = true
worker_pool = "worker-pool"
environment_variables = {
FOO_KEY = "FOO_VALUE"
BAR_KEY = "BAR_VALUE"
}
service_account = google_service_account.cloudbuild_service_account.id
}
depends_on = [
google_project_iam_member.act_as,
google_project_iam_member.logs_writer
]
}

data "google_project" "project" {
}

resource "google_storage_bucket" "bucket" {
name = "${data.google_project.project.project_id}-tf-test-gcf-source%{random_suffix}" # Every bucket name must be globally unique
location = "US"
uniform_bucket_level_access = true
}

resource "google_storage_bucket_object" "object" {
name = "function-source.zip"
bucket = google_storage_bucket.bucket.name
source = "%{zip_path}" # Add path to the zipped function source code
}

resource "google_service_account" "cloudbuild_service_account" {
account_id = "tf-test-build-sa%{random_suffix}"
}

resource "google_project_iam_member" "act_as" {
project = data.google_project.project.project_id
role = "roles/iam.serviceAccountUser"
member = "serviceAccount:${google_service_account.cloudbuild_service_account.email}"
}

resource "google_project_iam_member" "logs_writer" {
project = data.google_project.project.project_id
role = "roles/logging.logWriter"
member = "serviceAccount:${google_service_account.cloudbuild_service_account.email}"
}
`, context)
}

func testAccCloudRunV2Service_cloudrunv2ServiceFunctionExample_update(context map[string]interface{}) string {
return acctest.Nprintf(`
resource "google_cloud_run_v2_service" "default" {
name = "tf-test-cloudrun-service%{random_suffix}"
location = "us-central1"
deletion_protection = false
ingress = "INGRESS_TRAFFIC_ALL"

template {
containers {
image = "us-docker.pkg.dev/cloudrun/container/hello"
base_image_uri = "us-central1-docker.pkg.dev/serverless-runtimes/google-22/runtimes/nodejs22"
}
}
build_config {
source_location = "gs://${google_storage_bucket.bucket.name}/${google_storage_bucket_object.object.name}"
function_target = "helloHttp"
image_uri = "gcr.io/cloudrun/hello:latest"
base_image = "us-central1-docker.pkg.dev/serverless-runtimes/google-22-full/runtimes/nodejs20"
enable_automatic_updates = false
worker_pool = "worker-pool-2"
environment_variables = {
FOO_KEY_FOO = "FOO_VALUE_FOO"
BAR_KEY_BAR = "BAR_VALUE_BAR"
}
service_account = google_service_account.cloudbuild_service_account.id
}
depends_on = [
google_project_iam_member.act_as,
google_project_iam_member.logs_writer
]
}

data "google_project" "project" {
}

resource "google_storage_bucket" "bucket" {
name = "${data.google_project.project.project_id}-tf-test-gcf-source%{random_suffix}" # Every bucket name must be globally unique
location = "US"
uniform_bucket_level_access = true
}

resource "google_storage_bucket_object" "object" {
name = "function-source-updated.zip"
bucket = google_storage_bucket.bucket.name
source = "%{zip_path}" # Add path to the zipped function source code
}

resource "google_service_account" "cloudbuild_service_account" {
account_id = "tf-test-build-sa-2-%{random_suffix}"
}

resource "google_project_iam_member" "act_as" {
project = data.google_project.project.project_id
role = "roles/iam.serviceAccountUser"
member = "serviceAccount:${google_service_account.cloudbuild_service_account.email}"
}

resource "google_project_iam_member" "logs_writer" {
project = data.google_project.project.project_id
role = "roles/logging.logWriter"
member = "serviceAccount:${google_service_account.cloudbuild_service_account.email}"
}
`, context)
}
20 changes: 20 additions & 0 deletions website/docs/r/cloud_run_v2_service.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,7 @@ resource "google_cloud_run_v2_service" "default" {
template {
containers {
image = "us-docker.pkg.dev/cloudrun/container/hello"
base_image_uri = "us-central1-docker.pkg.dev/serverless-runtimes/google-22-full/runtimes/nodejs22"
}
}
build_config {
Expand Down Expand Up @@ -861,6 +862,15 @@ The following arguments are supported:
(Optional)
Containers which should be started before this container. If specified the container will wait to start until all containers with the listed names are healthy.

* `base_image_uri` -
(Optional)
Base image for this container. If set, it indicates that the service is enrolled into automatic base image update.

* `build_info` -
(Output)
The build info of the container image.
Structure is [documented below](#nested_template_containers_containers_build_info).


<a name="nested_template_containers_containers_env"></a>The `env` block supports:

Expand Down Expand Up @@ -1094,6 +1104,16 @@ The following arguments are supported:
(see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).
If this is not specified, the default behavior is defined by gRPC.

<a name="nested_template_containers_containers_build_info"></a>The `build_info` block contains:

* `function_target` -
(Output)
Entry point of the function when the image is a Cloud Run function.

* `source_location` -
(Output)
Source code location of the image.

<a name="nested_template_volumes"></a>The `volumes` block supports:

* `name` -
Expand Down