diff --git a/.changelog/6987.txt b/.changelog/6987.txt
new file mode 100644
index 0000000000..647aad3540
--- /dev/null
+++ b/.changelog/6987.txt
@@ -0,0 +1,3 @@
+```release-note:enhancement
+added `template.0.containers0.liveness_probe.grpc`, `template.0.containers0.startup_probe.grpc` fields to `google_cloud_run_v2_service` resource
+```
diff --git a/google-beta/config.go b/google-beta/config.go
index aa3ddb7ed8..f912c559c6 100644
--- a/google-beta/config.go
+++ b/google-beta/config.go
@@ -42,6 +42,7 @@ import (
iamcredentials "google.golang.org/api/iamcredentials/v1"
cloudlogging "google.golang.org/api/logging/v2"
"google.golang.org/api/pubsub/v1"
+ runadminv2 "google.golang.org/api/run/v2"
runtimeconfig "google.golang.org/api/runtimeconfig/v1beta1"
"google.golang.org/api/servicemanagement/v1"
"google.golang.org/api/servicenetworking/v1"
@@ -1200,6 +1201,20 @@ func (c *Config) NewBigTableProjectsInstancesTablesClient(userAgent string) *big
return clientBigtableProjectsInstancesTables
}
+func (c *Config) NewCloudRunV2Client(userAgent string) *runadminv2.Service {
+ runAdminV2ClientBasePath := removeBasePathVersion(removeBasePathVersion(c.CloudRunV2BasePath))
+ log.Printf("[INFO] Instantiating Google Cloud Run Admin v2 client for path %s", runAdminV2ClientBasePath)
+ clientRunAdminV2, err := runadminv2.NewService(c.context, option.WithHTTPClient(c.client))
+ if err != nil {
+ log.Printf("[WARN] Error creating client run admin: %s", err)
+ return nil
+ }
+ clientRunAdminV2.UserAgent = userAgent
+ clientRunAdminV2.BasePath = runAdminV2ClientBasePath
+
+ return clientRunAdminV2
+}
+
// staticTokenSource is used to be able to identify static token sources without reflection.
type staticTokenSource struct {
oauth2.TokenSource
diff --git a/google-beta/resource_cloud_run_v2_service.go b/google-beta/resource_cloud_run_v2_service.go
index ab774c77fe..466b2fb625 100644
--- a/google-beta/resource_cloud_run_v2_service.go
+++ b/google-beta/resource_cloud_run_v2_service.go
@@ -139,6 +139,7 @@ func ResourceCloudRunV2Service() *schema.Resource {
},
"liveness_probe": {
Type: schema.TypeList,
+ Computed: true,
Optional: true,
Description: `Periodic probe of container liveness. Container will be restarted if the probe fails. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes`,
MaxItems: 1,
@@ -150,6 +151,29 @@ func ResourceCloudRunV2Service() *schema.Resource {
Description: `Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.`,
Default: 3,
},
+ "grpc": {
+ Type: schema.TypeList,
+ Optional: true,
+ Description: `GRPC specifies an action involving a GRPC port.`,
+ MaxItems: 1,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "port": {
+ Type: schema.TypeInt,
+ Computed: true,
+ Optional: true,
+ Description: `Port number to access on the container. Number must be in the range 1 to 65535. If not specified, defaults to the same value as container.ports[0].containerPort.`,
+ },
+ "service": {
+ Type: schema.TypeString,
+ Optional: true,
+ Description: `The name of the service to place in the gRPC HealthCheckRequest
+(see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).
+If this is not specified, the default behavior is defined by gRPC.`,
+ },
+ },
+ },
+ },
"http_get": {
Type: schema.TypeList,
Optional: true,
@@ -288,6 +312,29 @@ If omitted, a port number will be chosen and passed to the container through the
Description: `Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.`,
Default: 3,
},
+ "grpc": {
+ Type: schema.TypeList,
+ Optional: true,
+ Description: `GRPC specifies an action involving a GRPC port.`,
+ MaxItems: 1,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "port": {
+ Type: schema.TypeInt,
+ Computed: true,
+ Optional: true,
+ Description: `Port number to access on the container. Number must be in the range 1 to 65535. If not specified, defaults to the same value as container.ports[0].containerPort.`,
+ },
+ "service": {
+ Type: schema.TypeString,
+ Optional: true,
+ Description: `The name of the service to place in the gRPC HealthCheckRequest
+(see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).
+If this is not specified, the default behavior is defined by gRPC.`,
+ },
+ },
+ },
+ },
"http_get": {
Type: schema.TypeList,
Optional: true,
@@ -1649,6 +1696,8 @@ func flattenCloudRunV2ServiceTemplateContainersLivenessProbe(v interface{}, d *s
flattenCloudRunV2ServiceTemplateContainersLivenessProbeHttpGet(original["httpGet"], d, config)
transformed["tcp_socket"] =
flattenCloudRunV2ServiceTemplateContainersLivenessProbeTcpSocket(original["tcpSocket"], d, config)
+ transformed["grpc"] =
+ flattenCloudRunV2ServiceTemplateContainersLivenessProbeGrpc(original["grpc"], d, config)
return []interface{}{transformed}
}
func flattenCloudRunV2ServiceTemplateContainersLivenessProbeInitialDelaySeconds(v interface{}, d *schema.ResourceData, config *Config) interface{} {
@@ -1789,6 +1838,39 @@ func flattenCloudRunV2ServiceTemplateContainersLivenessProbeTcpSocketPort(v inte
return v // let terraform core handle it otherwise
}
+func flattenCloudRunV2ServiceTemplateContainersLivenessProbeGrpc(v interface{}, d *schema.ResourceData, config *Config) interface{} {
+ if v == nil {
+ return nil
+ }
+ original := v.(map[string]interface{})
+ transformed := make(map[string]interface{})
+ transformed["port"] =
+ flattenCloudRunV2ServiceTemplateContainersLivenessProbeGrpcPort(original["port"], d, config)
+ transformed["service"] =
+ flattenCloudRunV2ServiceTemplateContainersLivenessProbeGrpcService(original["service"], d, config)
+ return []interface{}{transformed}
+}
+func flattenCloudRunV2ServiceTemplateContainersLivenessProbeGrpcPort(v interface{}, d *schema.ResourceData, config *Config) interface{} {
+ // Handles the string fixed64 format
+ if strVal, ok := v.(string); ok {
+ if intVal, err := stringToFixed64(strVal); err == nil {
+ return intVal
+ }
+ }
+
+ // number values are represented as float64
+ if floatVal, ok := v.(float64); ok {
+ intVal := int(floatVal)
+ return intVal
+ }
+
+ return v // let terraform core handle it otherwise
+}
+
+func flattenCloudRunV2ServiceTemplateContainersLivenessProbeGrpcService(v interface{}, d *schema.ResourceData, config *Config) interface{} {
+ return v
+}
+
func flattenCloudRunV2ServiceTemplateContainersStartupProbe(v interface{}, d *schema.ResourceData, config *Config) interface{} {
if v == nil {
return nil
@@ -1810,6 +1892,8 @@ func flattenCloudRunV2ServiceTemplateContainersStartupProbe(v interface{}, d *sc
flattenCloudRunV2ServiceTemplateContainersStartupProbeHttpGet(original["httpGet"], d, config)
transformed["tcp_socket"] =
flattenCloudRunV2ServiceTemplateContainersStartupProbeTcpSocket(original["tcpSocket"], d, config)
+ transformed["grpc"] =
+ flattenCloudRunV2ServiceTemplateContainersStartupProbeGrpc(original["grpc"], d, config)
return []interface{}{transformed}
}
func flattenCloudRunV2ServiceTemplateContainersStartupProbeInitialDelaySeconds(v interface{}, d *schema.ResourceData, config *Config) interface{} {
@@ -1950,6 +2034,39 @@ func flattenCloudRunV2ServiceTemplateContainersStartupProbeTcpSocketPort(v inter
return v // let terraform core handle it otherwise
}
+func flattenCloudRunV2ServiceTemplateContainersStartupProbeGrpc(v interface{}, d *schema.ResourceData, config *Config) interface{} {
+ if v == nil {
+ return nil
+ }
+ original := v.(map[string]interface{})
+ transformed := make(map[string]interface{})
+ transformed["port"] =
+ flattenCloudRunV2ServiceTemplateContainersStartupProbeGrpcPort(original["port"], d, config)
+ transformed["service"] =
+ flattenCloudRunV2ServiceTemplateContainersStartupProbeGrpcService(original["service"], d, config)
+ return []interface{}{transformed}
+}
+func flattenCloudRunV2ServiceTemplateContainersStartupProbeGrpcPort(v interface{}, d *schema.ResourceData, config *Config) interface{} {
+ // Handles the string fixed64 format
+ if strVal, ok := v.(string); ok {
+ if intVal, err := stringToFixed64(strVal); err == nil {
+ return intVal
+ }
+ }
+
+ // number values are represented as float64
+ if floatVal, ok := v.(float64); ok {
+ intVal := int(floatVal)
+ return intVal
+ }
+
+ return v // let terraform core handle it otherwise
+}
+
+func flattenCloudRunV2ServiceTemplateContainersStartupProbeGrpcService(v interface{}, d *schema.ResourceData, config *Config) interface{} {
+ return v
+}
+
func flattenCloudRunV2ServiceTemplateVolumes(v interface{}, d *schema.ResourceData, config *Config) interface{} {
if v == nil {
return v
@@ -2993,6 +3110,13 @@ func expandCloudRunV2ServiceTemplateContainersLivenessProbe(v interface{}, d Ter
transformed["tcpSocket"] = transformedTcpSocket
}
+ transformedGrpc, err := expandCloudRunV2ServiceTemplateContainersLivenessProbeGrpc(original["grpc"], d, config)
+ if err != nil {
+ return nil, err
+ } else {
+ transformed["grpc"] = transformedGrpc
+ }
+
return transformed, nil
}
@@ -3112,6 +3236,45 @@ func expandCloudRunV2ServiceTemplateContainersLivenessProbeTcpSocketPort(v inter
return v, nil
}
+func expandCloudRunV2ServiceTemplateContainersLivenessProbeGrpc(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
+ l := v.([]interface{})
+ if len(l) == 0 {
+ return nil, nil
+ }
+
+ if l[0] == nil {
+ transformed := make(map[string]interface{})
+ return transformed, nil
+ }
+ raw := l[0]
+ original := raw.(map[string]interface{})
+ transformed := make(map[string]interface{})
+
+ transformedPort, err := expandCloudRunV2ServiceTemplateContainersLivenessProbeGrpcPort(original["port"], d, config)
+ if err != nil {
+ return nil, err
+ } else if val := reflect.ValueOf(transformedPort); val.IsValid() && !isEmptyValue(val) {
+ transformed["port"] = transformedPort
+ }
+
+ transformedService, err := expandCloudRunV2ServiceTemplateContainersLivenessProbeGrpcService(original["service"], d, config)
+ if err != nil {
+ return nil, err
+ } else if val := reflect.ValueOf(transformedService); val.IsValid() && !isEmptyValue(val) {
+ transformed["service"] = transformedService
+ }
+
+ return transformed, nil
+}
+
+func expandCloudRunV2ServiceTemplateContainersLivenessProbeGrpcPort(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
+ return v, nil
+}
+
+func expandCloudRunV2ServiceTemplateContainersLivenessProbeGrpcService(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
+ return v, nil
+}
+
func expandCloudRunV2ServiceTemplateContainersStartupProbe(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
l := v.([]interface{})
if len(l) == 0 || l[0] == nil {
@@ -3163,6 +3326,13 @@ func expandCloudRunV2ServiceTemplateContainersStartupProbe(v interface{}, d Terr
transformed["tcpSocket"] = transformedTcpSocket
}
+ transformedGrpc, err := expandCloudRunV2ServiceTemplateContainersStartupProbeGrpc(original["grpc"], d, config)
+ if err != nil {
+ return nil, err
+ } else {
+ transformed["grpc"] = transformedGrpc
+ }
+
return transformed, nil
}
@@ -3282,6 +3452,45 @@ func expandCloudRunV2ServiceTemplateContainersStartupProbeTcpSocketPort(v interf
return v, nil
}
+func expandCloudRunV2ServiceTemplateContainersStartupProbeGrpc(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
+ l := v.([]interface{})
+ if len(l) == 0 {
+ return nil, nil
+ }
+
+ if l[0] == nil {
+ transformed := make(map[string]interface{})
+ return transformed, nil
+ }
+ raw := l[0]
+ original := raw.(map[string]interface{})
+ transformed := make(map[string]interface{})
+
+ transformedPort, err := expandCloudRunV2ServiceTemplateContainersStartupProbeGrpcPort(original["port"], d, config)
+ if err != nil {
+ return nil, err
+ } else if val := reflect.ValueOf(transformedPort); val.IsValid() && !isEmptyValue(val) {
+ transformed["port"] = transformedPort
+ }
+
+ transformedService, err := expandCloudRunV2ServiceTemplateContainersStartupProbeGrpcService(original["service"], d, config)
+ if err != nil {
+ return nil, err
+ } else if val := reflect.ValueOf(transformedService); val.IsValid() && !isEmptyValue(val) {
+ transformed["service"] = transformedService
+ }
+
+ return transformed, nil
+}
+
+func expandCloudRunV2ServiceTemplateContainersStartupProbeGrpcPort(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
+ return v, nil
+}
+
+func expandCloudRunV2ServiceTemplateContainersStartupProbeGrpcService(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
+ return v, nil
+}
+
func expandCloudRunV2ServiceTemplateVolumes(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
l := v.([]interface{})
req := make([]interface{}, 0, len(l))
diff --git a/google-beta/resource_cloud_run_v2_service_test.go b/google-beta/resource_cloud_run_v2_service_test.go
index 24633dadd4..4241fcf27f 100644
--- a/google-beta/resource_cloud_run_v2_service_test.go
+++ b/google-beta/resource_cloud_run_v2_service_test.go
@@ -1,7 +1,10 @@
package google
import (
+ "fmt"
+ "regexp"
"testing"
+ "time"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)
@@ -199,7 +202,7 @@ resource "google_compute_network" "custom_test" {
`, context)
}
-func TestAccCloudRunV2Service_cloudrunv2ServiceProbesUpdate(t *testing.T) {
+func TestAccCloudRunV2Service_cloudrunv2ServiceTCPProbesUpdate(t *testing.T) {
t.Parallel()
context := map[string]interface{}{
@@ -229,6 +232,22 @@ func TestAccCloudRunV2Service_cloudrunv2ServiceProbesUpdate(t *testing.T) {
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"name", "location"},
},
+ },
+ })
+}
+
+func TestAccCloudRunV2Service_cloudrunv2ServiceHTTPProbesUpdate(t *testing.T) {
+ t.Parallel()
+
+ context := map[string]interface{}{
+ "random_suffix": randString(t, 10),
+ }
+
+ vcrTest(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ CheckDestroy: testAccCheckCloudRunV2ServiceDestroyProducer(t),
+ Steps: []resource.TestStep{
{
Config: testAccCloudRunV2Service_cloudrunv2ServiceUpdateWithEmptyHTTPStartupProbe(context),
},
@@ -251,12 +270,89 @@ func TestAccCloudRunV2Service_cloudrunv2ServiceProbesUpdate(t *testing.T) {
})
}
+func TestAccCloudRunV2Service_cloudrunv2ServiceGRPCProbesUpdate(t *testing.T) {
+ t.Parallel()
+
+ serviceName := fmt.Sprintf("tf-test-cloudrun-service%s", randString(t, 10))
+ context := map[string]interface{}{
+ "service_name": serviceName,
+ }
+
+ vcrTest(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ CheckDestroy: testAccCheckCloudRunV2ServiceDestroyProducer(t),
+ Steps: []resource.TestStep{
+ {
+ Config: testAccCloudRunV2Service_cloudRunServiceUpdateWithEmptyGRPCLivenessProbe(context),
+ },
+ {
+ ResourceName: "google_cloud_run_v2_service.default",
+ ImportState: true,
+ ImportStateVerify: true,
+ ImportStateVerifyIgnore: []string{"name", "location"},
+ },
+ {
+ Config: testAccCloudRunV2Service_cloudRunServiceUpdateWithGRPCLivenessProbe(context),
+ },
+ {
+ ResourceName: "google_cloud_run_v2_service.default",
+ ImportState: true,
+ ImportStateVerify: true,
+ ImportStateVerifyIgnore: []string{"name", "location"},
+ },
+ // The following test steps of gRPC startup probe are expected to fail with startup probe check failures.
+ // This is because, due to the unavailability of ready-to-use container images of a gRPC service that
+ // implements the standard gRPC health check protocol, we compromise and use a container image of an
+ // ordinary HTTP service to deploy the gRPC service, which never passes startup probes.
+ // So we only check that the `startup.grpc {}` block and its properties are accepted by the APIs.
+ {
+ Config: testAccCloudRunV2Service_cloudRunServiceUpdateWithEmptyGRPCStartupProbe(context),
+ ExpectError: regexp.MustCompile(fmt.Sprintf(`Revision '%s-.*' is not ready and cannot serve traffic\. The user-provided container failed the configured startup probe checks\.`, serviceName)),
+ },
+ {
+ PreConfig: testAccCheckCloudRunV2ServiceDestroyByNameProducer(t, serviceName),
+ Config: testAccCloudRunV2Service_cloudRunServiceUpdateWithGRPCStartupProbe(context),
+ ExpectError: regexp.MustCompile(fmt.Sprintf(`Revision '%s-.*' is not ready and cannot serve traffic\. The user-provided container failed the configured startup probe checks\.`, serviceName)),
+ },
+ {
+ PreConfig: testAccCheckCloudRunV2ServiceDestroyByNameProducer(t, serviceName),
+ Config: testAccCloudRunV2Service_cloudRunServiceUpdateWithGRPCLivenessAndStartupProbes(context),
+ ExpectError: regexp.MustCompile(fmt.Sprintf(`Revision '%s-.*' is not ready and cannot serve traffic\. The user-provided container failed the configured startup probe checks\.`, serviceName)),
+ },
+ {
+ PreConfig: testAccCheckCloudRunV2ServiceDestroyByNameProducer(t, serviceName),
+ Config: testAccCloudRunV2Service_cloudRunServiceUpdateWithGRPCLivenessAndStartupProbes(context),
+ PlanOnly: true,
+ ExpectNonEmptyPlan: true,
+ },
+ },
+ })
+}
+
+func testAccCheckCloudRunV2ServiceDestroyByNameProducer(t *testing.T, serviceName string) func() {
+ return func() {
+ config := googleProviderConfig(t)
+ service := config.NewCloudRunV2Client(config.userAgent).Projects.Locations.Services
+ qualifiedServiceName := fmt.Sprintf("projects/%s/locations/%s/services/%s", config.Project, config.Region, serviceName)
+ op, err := service.Delete(qualifiedServiceName).Do()
+ if err != nil {
+ t.Errorf("Error while deleting the Cloud Run service: %s", err)
+ return
+ }
+ err = runAdminV2OperationWaitTime(config, op, config.Project, "Waiting for Cloud Run service to be deleted", config.userAgent, 5*time.Minute)
+ if err != nil {
+ t.Errorf("Error while waiting for Cloud Run service delete operation to complete: %s", err.Error())
+ }
+ }
+}
+
func testAccCloudRunV2Service_cloudrunv2ServiceWithEmptyTCPStartupProbeAndHTTPLivenessProbe(context map[string]interface{}) string {
return Nprintf(`
resource "google_cloud_run_v2_service" "default" {
name = "tf-test-cloudrun-service%{random_suffix}"
location = "us-central1"
-
+
template {
containers {
image = "us-docker.pkg.dev/cloudrun/container/hello"
@@ -280,7 +376,7 @@ func testAccCloudRunV2Service_cloudrunv2ServiceUpdateWithTCPStartupProbeAndHTTPL
resource "google_cloud_run_v2_service" "default" {
name = "tf-test-cloudrun-service%{random_suffix}"
location = "us-central1"
-
+
template {
containers {
image = "us-docker.pkg.dev/cloudrun/container/hello"
@@ -323,7 +419,7 @@ func testAccCloudRunV2Service_cloudrunv2ServiceUpdateWithEmptyHTTPStartupProbe(c
resource "google_cloud_run_v2_service" "default" {
name = "tf-test-cloudrun-service%{random_suffix}"
location = "us-central1"
-
+
template {
containers {
image = "us-docker.pkg.dev/cloudrun/container/hello"
@@ -341,7 +437,7 @@ func testAccCloudRunV2Service_cloudrunv2ServiceUpdateWithHTTPStartupProbe(contex
resource "google_cloud_run_v2_service" "default" {
name = "tf-test-cloudrun-service%{random_suffix}"
location = "us-central1"
-
+
template {
containers {
image = "us-docker.pkg.dev/cloudrun/container/hello"
@@ -366,3 +462,123 @@ resource "google_cloud_run_v2_service" "default" {
}
`, context)
}
+
+func testAccCloudRunV2Service_cloudRunServiceUpdateWithEmptyGRPCLivenessProbe(context map[string]interface{}) string {
+ return Nprintf(`
+resource "google_cloud_run_v2_service" "default" {
+ name ="%{service_name}"
+ location = "us-central1"
+
+ template {
+ containers {
+ image = "us-docker.pkg.dev/cloudrun/container/hello"
+ ports {
+ container_port = 8080
+ }
+ liveness_probe {
+ grpc {}
+ }
+ }
+ }
+}
+`, context)
+}
+
+func testAccCloudRunV2Service_cloudRunServiceUpdateWithGRPCLivenessProbe(context map[string]interface{}) string {
+ return Nprintf(`
+resource "google_cloud_run_v2_service" "default" {
+ name = "%{service_name}"
+ location = "us-central1"
+
+ template {
+ containers {
+ image = "us-docker.pkg.dev/cloudrun/container/hello"
+ ports {
+ container_port = 8080
+ }
+ liveness_probe {
+ grpc {
+ port = 8080
+ service = "grpc.health.v1.Health"
+ }
+ }
+ }
+ }
+}
+`, context)
+}
+
+func testAccCloudRunV2Service_cloudRunServiceUpdateWithEmptyGRPCStartupProbe(context map[string]interface{}) string {
+ return Nprintf(`
+resource "google_cloud_run_v2_service" "default" {
+ name = "%{service_name}"
+ location = "us-central1"
+
+ template {
+ containers {
+ image = "us-docker.pkg.dev/cloudrun/container/hello"
+ ports {
+ container_port = 8080
+ }
+ startup_probe {
+ grpc {}
+ }
+ }
+ }
+}
+`, context)
+}
+
+func testAccCloudRunV2Service_cloudRunServiceUpdateWithGRPCStartupProbe(context map[string]interface{}) string {
+ return Nprintf(`
+resource "google_cloud_run_v2_service" "default" {
+ name = "%{service_name}"
+ location = "us-central1"
+
+ template {
+ containers {
+ image = "us-docker.pkg.dev/cloudrun/container/hello"
+ ports {
+ container_port = 8080
+ }
+ startup_probe {
+ grpc {
+ port = 8080
+ service = "grpc.health.v1.Health"
+ }
+ }
+ }
+ }
+}
+`, context)
+}
+
+func testAccCloudRunV2Service_cloudRunServiceUpdateWithGRPCLivenessAndStartupProbes(context map[string]interface{}) string {
+ return Nprintf(`
+resource "google_cloud_run_v2_service" "default" {
+ name = "%{service_name}"
+ location = "us-central1"
+
+ template {
+ containers {
+ image = "us-docker.pkg.dev/cloudrun/container/hello"
+ ports {
+ container_port = 8080
+ }
+ liveness_probe {
+ grpc {
+ port = 8080
+ service = "grpc.health.v1.Health"
+ }
+ }
+ startup_probe {
+ grpc {
+ port = 8080
+ service = "grpc.health.v1.Health"
+ }
+ }
+ }
+ }
+}
+`, context)
+}
diff --git a/google-beta/runadminv3_operation.go b/google-beta/runadminv3_operation.go
new file mode 100644
index 0000000000..bf540c11c8
--- /dev/null
+++ b/google-beta/runadminv3_operation.go
@@ -0,0 +1,59 @@
+package google
+
+import (
+ "encoding/json"
+ "fmt"
+ "time"
+
+ "google.golang.org/api/run/v2"
+)
+
+type RunAdminV2OperationWaiter struct {
+ Config *Config
+ UserAgent string
+ Project string
+ CommonOperationWaiter
+}
+
+func (w *RunAdminV2OperationWaiter) QueryOp() (interface{}, error) {
+ if w == nil {
+ return nil, fmt.Errorf("Cannot query operation, it's unset or nil.")
+ }
+ url := fmt.Sprintf("%s%s", w.Config.CloudRunV2BasePath, w.CommonOperationWaiter.Op.Name)
+
+ return sendRequest(w.Config, "GET", w.Project, url, w.UserAgent, nil)
+}
+
+func createRunAdminV2Waiter(config *Config, op *run.GoogleLongrunningOperation, project, activity, userAgent string) (*RunAdminV2OperationWaiter, error) {
+ w := &RunAdminV2OperationWaiter{
+ Config: config,
+ UserAgent: userAgent,
+ Project: project,
+ }
+ if err := w.CommonOperationWaiter.SetOp(op); err != nil {
+ return nil, err
+ }
+ return w, nil
+}
+
+func runAdminV2OperationWaitTimeWithResponse(config *Config, op *run.GoogleLongrunningOperation, response *map[string]interface{}, project, activity, userAgent string, timeout time.Duration) error {
+ w, err := createRunAdminV2Waiter(config, op, project, activity, userAgent)
+ if err != nil {
+ return err
+ }
+ if err := OperationWait(w, activity, timeout, config.PollInterval); err != nil {
+ return err
+ }
+ return json.Unmarshal([]byte(w.CommonOperationWaiter.Op.Response), response)
+}
+
+func runAdminV2OperationWaitTime(config *Config, op *run.GoogleLongrunningOperation, project, activity, userAgent string, timeout time.Duration) error {
+ if op.Done {
+ return nil
+ }
+ w, err := createRunAdminV2Waiter(config, op, project, activity, userAgent)
+ if err != nil {
+ return err
+ }
+ return OperationWait(w, activity, timeout, config.PollInterval)
+}
diff --git a/website/docs/r/cloud_run_v2_service.html.markdown b/website/docs/r/cloud_run_v2_service.html.markdown
index 3bc5597bc8..c6a2fa4ce4 100644
--- a/website/docs/r/cloud_run_v2_service.html.markdown
+++ b/website/docs/r/cloud_run_v2_service.html.markdown
@@ -517,6 +517,11 @@ The following arguments are supported:
TCPSocket specifies an action involving a TCP port. This field is not supported in liveness probe currently.
Structure is [documented below](#nested_tcp_socket).
+* `grpc` -
+ (Optional)
+ GRPC specifies an action involving a GRPC port.
+ Structure is [documented below](#nested_grpc).
+
The `http_get` block supports:
@@ -546,6 +551,18 @@ The following arguments are supported:
(Optional)
Port number to access on the container. Must be in the range 1 to 65535. If not specified, defaults to 8080.
+The `grpc` block supports:
+
+* `port` -
+ (Optional)
+ Port number to access on the container. Number must be in the range 1 to 65535. If not specified, defaults to the same value as container.ports[0].containerPort.
+
+* `service` -
+ (Optional)
+ The name of the service to place in the gRPC HealthCheckRequest
+ (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).
+ If this is not specified, the default behavior is defined by gRPC.
+
The `startup_probe` block supports:
* `initial_delay_seconds` -
@@ -574,6 +591,11 @@ The following arguments are supported:
TCPSocket specifies an action involving a TCP port. Exactly one of HTTPGet or TCPSocket must be specified.
Structure is [documented below](#nested_tcp_socket).
+* `grpc` -
+ (Optional)
+ GRPC specifies an action involving a GRPC port.
+ Structure is [documented below](#nested_grpc).
+
The `http_get` block supports:
@@ -603,6 +625,18 @@ The following arguments are supported:
(Optional)
Port number to access on the container. Must be in the range 1 to 65535. If not specified, defaults to 8080.
+The `grpc` block supports:
+
+* `port` -
+ (Optional)
+ Port number to access on the container. Number must be in the range 1 to 65535. If not specified, defaults to the same value as container.ports[0].containerPort.
+
+* `service` -
+ (Optional)
+ The name of the service to place in the gRPC HealthCheckRequest
+ (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).
+ If this is not specified, the default behavior is defined by gRPC.
+
The `volumes` block supports:
* `name` -