diff --git a/.changelog/3825.txt b/.changelog/3825.txt new file mode 100644 index 00000000000..c818de1364e --- /dev/null +++ b/.changelog/3825.txt @@ -0,0 +1,6 @@ +```release-note:enhancement +compute: added grpc_health_check block to compute_health_check +``` +```release-note:enhancement +compute: added grpc_health_check block to compute_region_health_check +``` diff --git a/google/resource_compute_health_check.go b/google/resource_compute_health_check.go index e5f0ea436b3..1ae337e05fc 100644 --- a/google/resource_compute_health_check.go +++ b/google/resource_compute_health_check.go @@ -154,6 +154,63 @@ seconds.`, Description: `An optional description of this resource. Provide this property when you create the resource.`, }, + "grpc_health_check": { + Type: schema.TypeList, + Optional: true, + DiffSuppressFunc: portDiffSuppress, + Description: `A nested object resource`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "grpc_service_name": { + Type: schema.TypeString, + Optional: true, + Description: `The gRPC service name for the health check. +The value of grpcServiceName has the following meanings by convention: + - Empty serviceName means the overall status of all services at the backend. + - Non-empty serviceName means the health of that gRPC service, as defined by the owner of the service. +The grpcServiceName can only be ASCII.`, + AtLeastOneOf: []string{"grpc_health_check.0.port", "grpc_health_check.0.port_name", "grpc_health_check.0.port_specification", "grpc_health_check.0.grpc_service_name"}, + }, + "port": { + Type: schema.TypeInt, + Optional: true, + Description: `The port number for the health check request. +Must be specified if portName and portSpecification are not set +or if port_specification is USE_FIXED_PORT. Valid values are 1 through 65535.`, + AtLeastOneOf: []string{"grpc_health_check.0.port", "grpc_health_check.0.port_name", "grpc_health_check.0.port_specification", "grpc_health_check.0.grpc_service_name"}, + }, + "port_name": { + Type: schema.TypeString, + Optional: true, + Description: `Port name as defined in InstanceGroup#NamedPort#name. If both port and +port_name are defined, port takes precedence.`, + AtLeastOneOf: []string{"grpc_health_check.0.port", "grpc_health_check.0.port_name", "grpc_health_check.0.port_specification", "grpc_health_check.0.grpc_service_name"}, + }, + "port_specification": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"USE_FIXED_PORT", "USE_NAMED_PORT", "USE_SERVING_PORT", ""}, false), + Description: `Specifies how port is selected for health checking, can be one of the +following values: + + * 'USE_FIXED_PORT': The port number in 'port' is used for health checking. + + * 'USE_NAMED_PORT': The 'portName' is used for health checking. + + * 'USE_SERVING_PORT': For NetworkEndpointGroup, the port specified for each + network endpoint is used for health checking. For other backends, the + port or named port specified in the Backend Service is used for health + checking. + +If not specified, gRPC health check follows behavior specified in 'port' and +'portName' fields. Possible values: ["USE_FIXED_PORT", "USE_NAMED_PORT", "USE_SERVING_PORT"]`, + AtLeastOneOf: []string{"grpc_health_check.0.port", "grpc_health_check.0.port_name", "grpc_health_check.0.port_specification", "grpc_health_check.0.grpc_service_name"}, + }, + }, + }, + ExactlyOneOf: []string{"http_health_check", "https_health_check", "http2_health_check", "tcp_health_check", "ssl_health_check", "grpc_health_check"}, + }, "healthy_threshold": { Type: schema.TypeInt, Optional: true, @@ -238,7 +295,7 @@ can only be ASCII.`, }, }, }, - ExactlyOneOf: []string{"http_health_check", "https_health_check", "http2_health_check", "tcp_health_check", "ssl_health_check"}, + ExactlyOneOf: []string{"http_health_check", "https_health_check", "http2_health_check", "tcp_health_check", "ssl_health_check", "grpc_health_check"}, }, "http_health_check": { Type: schema.TypeList, @@ -317,7 +374,7 @@ can only be ASCII.`, }, }, }, - ExactlyOneOf: []string{"http_health_check", "https_health_check", "http2_health_check", "tcp_health_check", "ssl_health_check"}, + ExactlyOneOf: []string{"http_health_check", "https_health_check", "http2_health_check", "tcp_health_check", "ssl_health_check", "grpc_health_check"}, }, "https_health_check": { Type: schema.TypeList, @@ -396,7 +453,7 @@ can only be ASCII.`, }, }, }, - ExactlyOneOf: []string{"http_health_check", "https_health_check", "http2_health_check", "tcp_health_check", "ssl_health_check"}, + ExactlyOneOf: []string{"http_health_check", "https_health_check", "http2_health_check", "tcp_health_check", "ssl_health_check", "grpc_health_check"}, }, "ssl_health_check": { Type: schema.TypeList, @@ -468,7 +525,7 @@ can only be ASCII.`, }, }, }, - ExactlyOneOf: []string{"http_health_check", "https_health_check", "http2_health_check", "tcp_health_check", "ssl_health_check"}, + ExactlyOneOf: []string{"http_health_check", "https_health_check", "http2_health_check", "tcp_health_check", "ssl_health_check", "grpc_health_check"}, }, "tcp_health_check": { Type: schema.TypeList, @@ -540,7 +597,7 @@ can only be ASCII.`, }, }, }, - ExactlyOneOf: []string{"http_health_check", "https_health_check", "http2_health_check", "tcp_health_check", "ssl_health_check"}, + ExactlyOneOf: []string{"http_health_check", "https_health_check", "http2_health_check", "tcp_health_check", "ssl_health_check", "grpc_health_check"}, }, "timeout_sec": { Type: schema.TypeInt, @@ -651,6 +708,12 @@ func resourceComputeHealthCheckCreate(d *schema.ResourceData, meta interface{}) } else if v, ok := d.GetOkExists("http2_health_check"); !isEmptyValue(reflect.ValueOf(http2HealthCheckProp)) && (ok || !reflect.DeepEqual(v, http2HealthCheckProp)) { obj["http2HealthCheck"] = http2HealthCheckProp } + grpcHealthCheckProp, err := expandComputeHealthCheckGrpcHealthCheck(d.Get("grpc_health_check"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("grpc_health_check"); !isEmptyValue(reflect.ValueOf(grpcHealthCheckProp)) && (ok || !reflect.DeepEqual(v, grpcHealthCheckProp)) { + obj["grpcHealthCheck"] = grpcHealthCheckProp + } obj, err = resourceComputeHealthCheckEncoder(d, meta, obj) if err != nil { @@ -754,6 +817,9 @@ func resourceComputeHealthCheckRead(d *schema.ResourceData, meta interface{}) er if err := d.Set("http2_health_check", flattenComputeHealthCheckHttp2HealthCheck(res["http2HealthCheck"], d, config)); err != nil { return fmt.Errorf("Error reading HealthCheck: %s", err) } + if err := d.Set("grpc_health_check", flattenComputeHealthCheckGrpcHealthCheck(res["grpcHealthCheck"], d, config)); err != nil { + return fmt.Errorf("Error reading HealthCheck: %s", err) + } if err := d.Set("self_link", ConvertSelfLinkToV1(res["selfLink"].(string))); err != nil { return fmt.Errorf("Error reading HealthCheck: %s", err) } @@ -836,6 +902,12 @@ func resourceComputeHealthCheckUpdate(d *schema.ResourceData, meta interface{}) } else if v, ok := d.GetOkExists("http2_health_check"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, http2HealthCheckProp)) { obj["http2HealthCheck"] = http2HealthCheckProp } + grpcHealthCheckProp, err := expandComputeHealthCheckGrpcHealthCheck(d.Get("grpc_health_check"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("grpc_health_check"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, grpcHealthCheckProp)) { + obj["grpcHealthCheck"] = grpcHealthCheckProp + } obj, err = resourceComputeHealthCheckEncoder(d, meta, obj) if err != nil { @@ -1322,6 +1394,54 @@ func flattenComputeHealthCheckHttp2HealthCheckPortSpecification(v interface{}, d return v } +func flattenComputeHealthCheckGrpcHealthCheck(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["port"] = + flattenComputeHealthCheckGrpcHealthCheckPort(original["port"], d, config) + transformed["port_name"] = + flattenComputeHealthCheckGrpcHealthCheckPortName(original["portName"], d, config) + transformed["port_specification"] = + flattenComputeHealthCheckGrpcHealthCheckPortSpecification(original["portSpecification"], d, config) + transformed["grpc_service_name"] = + flattenComputeHealthCheckGrpcHealthCheckGrpcServiceName(original["grpcServiceName"], d, config) + return []interface{}{transformed} +} +func flattenComputeHealthCheckGrpcHealthCheckPort(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); 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 flattenComputeHealthCheckGrpcHealthCheckPortName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeHealthCheckGrpcHealthCheckPortSpecification(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeHealthCheckGrpcHealthCheckGrpcServiceName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + func expandComputeHealthCheckCheckIntervalSec(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } @@ -1769,6 +1889,62 @@ func expandComputeHealthCheckHttp2HealthCheckPortSpecification(v interface{}, d return v, nil } +func expandComputeHealthCheckGrpcHealthCheck(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{}) + + transformedPort, err := expandComputeHealthCheckGrpcHealthCheckPort(original["port"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPort); val.IsValid() && !isEmptyValue(val) { + transformed["port"] = transformedPort + } + + transformedPortName, err := expandComputeHealthCheckGrpcHealthCheckPortName(original["port_name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPortName); val.IsValid() && !isEmptyValue(val) { + transformed["portName"] = transformedPortName + } + + transformedPortSpecification, err := expandComputeHealthCheckGrpcHealthCheckPortSpecification(original["port_specification"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPortSpecification); val.IsValid() && !isEmptyValue(val) { + transformed["portSpecification"] = transformedPortSpecification + } + + transformedGrpcServiceName, err := expandComputeHealthCheckGrpcHealthCheckGrpcServiceName(original["grpc_service_name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedGrpcServiceName); val.IsValid() && !isEmptyValue(val) { + transformed["grpcServiceName"] = transformedGrpcServiceName + } + + return transformed, nil +} + +func expandComputeHealthCheckGrpcHealthCheckPort(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeHealthCheckGrpcHealthCheckPortName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeHealthCheckGrpcHealthCheckPortSpecification(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeHealthCheckGrpcHealthCheckGrpcServiceName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + func resourceComputeHealthCheckEncoder(d *schema.ResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) { if _, ok := d.GetOk("http_health_check"); ok { diff --git a/google/resource_compute_health_check_generated_test.go b/google/resource_compute_health_check_generated_test.go index 7383ba7d0db..07d5680464d 100644 --- a/google/resource_compute_health_check_generated_test.go +++ b/google/resource_compute_health_check_generated_test.go @@ -451,6 +451,86 @@ resource "google_compute_health_check" "http2-health-check" { `, context) } +func TestAccComputeHealthCheck_healthCheckGrpcExample(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: testAccCheckComputeHealthCheckDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeHealthCheck_healthCheckGrpcExample(context), + }, + { + ResourceName: "google_compute_health_check.grpc-health-check", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccComputeHealthCheck_healthCheckGrpcExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_compute_health_check" "grpc-health-check" { + name = "tf-test-grpc-health-check%{random_suffix}" + + timeout_sec = 1 + check_interval_sec = 1 + + grpc_health_check { + port = "443" + } +} +`, context) +} + +func TestAccComputeHealthCheck_healthCheckGrpcFullExample(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: testAccCheckComputeHealthCheckDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeHealthCheck_healthCheckGrpcFullExample(context), + }, + { + ResourceName: "google_compute_health_check.grpc-health-check", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccComputeHealthCheck_healthCheckGrpcFullExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_compute_health_check" "grpc-health-check" { + name = "tf-test-grpc-health-check%{random_suffix}" + + timeout_sec = 1 + check_interval_sec = 1 + + grpc_health_check { + port_name = "health-check-port" + port_specification = "USE_NAMED_PORT" + grpc_service_name = "testservice" + } +} +`, context) +} + func testAccCheckComputeHealthCheckDestroyProducer(t *testing.T) func(s *terraform.State) error { return func(s *terraform.State) error { for name, rs := range s.RootModule().Resources { diff --git a/google/resource_compute_region_health_check.go b/google/resource_compute_region_health_check.go index dbb01a2cf70..66cecffcae0 100644 --- a/google/resource_compute_region_health_check.go +++ b/google/resource_compute_region_health_check.go @@ -70,6 +70,63 @@ seconds.`, Description: `An optional description of this resource. Provide this property when you create the resource.`, }, + "grpc_health_check": { + Type: schema.TypeList, + Optional: true, + DiffSuppressFunc: portDiffSuppress, + Description: `A nested object resource`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "grpc_service_name": { + Type: schema.TypeString, + Optional: true, + Description: `The gRPC service name for the health check. +The value of grpcServiceName has the following meanings by convention: + - Empty serviceName means the overall status of all services at the backend. + - Non-empty serviceName means the health of that gRPC service, as defined by the owner of the service. +The grpcServiceName can only be ASCII.`, + AtLeastOneOf: []string{"grpc_health_check.0.port", "grpc_health_check.0.port_name", "grpc_health_check.0.port_specification", "grpc_health_check.0.grpc_service_name"}, + }, + "port": { + Type: schema.TypeInt, + Optional: true, + Description: `The port number for the health check request. +Must be specified if portName and portSpecification are not set +or if port_specification is USE_FIXED_PORT. Valid values are 1 through 65535.`, + AtLeastOneOf: []string{"grpc_health_check.0.port", "grpc_health_check.0.port_name", "grpc_health_check.0.port_specification", "grpc_health_check.0.grpc_service_name"}, + }, + "port_name": { + Type: schema.TypeString, + Optional: true, + Description: `Port name as defined in InstanceGroup#NamedPort#name. If both port and +port_name are defined, port takes precedence.`, + AtLeastOneOf: []string{"grpc_health_check.0.port", "grpc_health_check.0.port_name", "grpc_health_check.0.port_specification", "grpc_health_check.0.grpc_service_name"}, + }, + "port_specification": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"USE_FIXED_PORT", "USE_NAMED_PORT", "USE_SERVING_PORT", ""}, false), + Description: `Specifies how port is selected for health checking, can be one of the +following values: + + * 'USE_FIXED_PORT': The port number in 'port' is used for health checking. + + * 'USE_NAMED_PORT': The 'portName' is used for health checking. + + * 'USE_SERVING_PORT': For NetworkEndpointGroup, the port specified for each + network endpoint is used for health checking. For other backends, the + port or named port specified in the Backend Service is used for health + checking. + +If not specified, gRPC health check follows behavior specified in 'port' and +'portName' fields. Possible values: ["USE_FIXED_PORT", "USE_NAMED_PORT", "USE_SERVING_PORT"]`, + AtLeastOneOf: []string{"grpc_health_check.0.port", "grpc_health_check.0.port_name", "grpc_health_check.0.port_specification", "grpc_health_check.0.grpc_service_name"}, + }, + }, + }, + ExactlyOneOf: []string{"http_health_check", "https_health_check", "http2_health_check", "tcp_health_check", "ssl_health_check", "grpc_health_check"}, + }, "healthy_threshold": { Type: schema.TypeInt, Optional: true, @@ -154,7 +211,7 @@ can only be ASCII.`, }, }, }, - ExactlyOneOf: []string{"http_health_check", "https_health_check", "http2_health_check", "tcp_health_check", "ssl_health_check"}, + ExactlyOneOf: []string{"http_health_check", "https_health_check", "http2_health_check", "tcp_health_check", "ssl_health_check", "grpc_health_check"}, }, "http_health_check": { Type: schema.TypeList, @@ -233,7 +290,7 @@ can only be ASCII.`, }, }, }, - ExactlyOneOf: []string{"http_health_check", "https_health_check", "http2_health_check", "tcp_health_check", "ssl_health_check"}, + ExactlyOneOf: []string{"http_health_check", "https_health_check", "http2_health_check", "tcp_health_check", "ssl_health_check", "grpc_health_check"}, }, "https_health_check": { Type: schema.TypeList, @@ -312,7 +369,7 @@ can only be ASCII.`, }, }, }, - ExactlyOneOf: []string{"http_health_check", "https_health_check", "http2_health_check", "tcp_health_check", "ssl_health_check"}, + ExactlyOneOf: []string{"http_health_check", "https_health_check", "http2_health_check", "tcp_health_check", "ssl_health_check", "grpc_health_check"}, }, "region": { Type: schema.TypeString, @@ -393,7 +450,7 @@ can only be ASCII.`, }, }, }, - ExactlyOneOf: []string{"http_health_check", "https_health_check", "http2_health_check", "tcp_health_check", "ssl_health_check"}, + ExactlyOneOf: []string{"http_health_check", "https_health_check", "http2_health_check", "tcp_health_check", "ssl_health_check", "grpc_health_check"}, }, "tcp_health_check": { Type: schema.TypeList, @@ -465,7 +522,7 @@ can only be ASCII.`, }, }, }, - ExactlyOneOf: []string{"http_health_check", "https_health_check", "http2_health_check", "tcp_health_check", "ssl_health_check"}, + ExactlyOneOf: []string{"http_health_check", "https_health_check", "http2_health_check", "tcp_health_check", "ssl_health_check", "grpc_health_check"}, }, "timeout_sec": { Type: schema.TypeInt, @@ -576,6 +633,12 @@ func resourceComputeRegionHealthCheckCreate(d *schema.ResourceData, meta interfa } else if v, ok := d.GetOkExists("http2_health_check"); !isEmptyValue(reflect.ValueOf(http2HealthCheckProp)) && (ok || !reflect.DeepEqual(v, http2HealthCheckProp)) { obj["http2HealthCheck"] = http2HealthCheckProp } + grpcHealthCheckProp, err := expandComputeRegionHealthCheckGrpcHealthCheck(d.Get("grpc_health_check"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("grpc_health_check"); !isEmptyValue(reflect.ValueOf(grpcHealthCheckProp)) && (ok || !reflect.DeepEqual(v, grpcHealthCheckProp)) { + obj["grpcHealthCheck"] = grpcHealthCheckProp + } regionProp, err := expandComputeRegionHealthCheckRegion(d.Get("region"), d, config) if err != nil { return err @@ -685,6 +748,9 @@ func resourceComputeRegionHealthCheckRead(d *schema.ResourceData, meta interface if err := d.Set("http2_health_check", flattenComputeRegionHealthCheckHttp2HealthCheck(res["http2HealthCheck"], d, config)); err != nil { return fmt.Errorf("Error reading RegionHealthCheck: %s", err) } + if err := d.Set("grpc_health_check", flattenComputeRegionHealthCheckGrpcHealthCheck(res["grpcHealthCheck"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionHealthCheck: %s", err) + } if err := d.Set("region", flattenComputeRegionHealthCheckRegion(res["region"], d, config)); err != nil { return fmt.Errorf("Error reading RegionHealthCheck: %s", err) } @@ -770,6 +836,12 @@ func resourceComputeRegionHealthCheckUpdate(d *schema.ResourceData, meta interfa } else if v, ok := d.GetOkExists("http2_health_check"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, http2HealthCheckProp)) { obj["http2HealthCheck"] = http2HealthCheckProp } + grpcHealthCheckProp, err := expandComputeRegionHealthCheckGrpcHealthCheck(d.Get("grpc_health_check"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("grpc_health_check"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, grpcHealthCheckProp)) { + obj["grpcHealthCheck"] = grpcHealthCheckProp + } regionProp, err := expandComputeRegionHealthCheckRegion(d.Get("region"), d, config) if err != nil { return err @@ -1263,6 +1335,54 @@ func flattenComputeRegionHealthCheckHttp2HealthCheckPortSpecification(v interfac return v } +func flattenComputeRegionHealthCheckGrpcHealthCheck(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["port"] = + flattenComputeRegionHealthCheckGrpcHealthCheckPort(original["port"], d, config) + transformed["port_name"] = + flattenComputeRegionHealthCheckGrpcHealthCheckPortName(original["portName"], d, config) + transformed["port_specification"] = + flattenComputeRegionHealthCheckGrpcHealthCheckPortSpecification(original["portSpecification"], d, config) + transformed["grpc_service_name"] = + flattenComputeRegionHealthCheckGrpcHealthCheckGrpcServiceName(original["grpcServiceName"], d, config) + return []interface{}{transformed} +} +func flattenComputeRegionHealthCheckGrpcHealthCheckPort(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); 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 flattenComputeRegionHealthCheckGrpcHealthCheckPortName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionHealthCheckGrpcHealthCheckPortSpecification(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenComputeRegionHealthCheckGrpcHealthCheckGrpcServiceName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + func flattenComputeRegionHealthCheckRegion(v interface{}, d *schema.ResourceData, config *Config) interface{} { if v == nil { return v @@ -1717,6 +1837,62 @@ func expandComputeRegionHealthCheckHttp2HealthCheckPortSpecification(v interface return v, nil } +func expandComputeRegionHealthCheckGrpcHealthCheck(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{}) + + transformedPort, err := expandComputeRegionHealthCheckGrpcHealthCheckPort(original["port"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPort); val.IsValid() && !isEmptyValue(val) { + transformed["port"] = transformedPort + } + + transformedPortName, err := expandComputeRegionHealthCheckGrpcHealthCheckPortName(original["port_name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPortName); val.IsValid() && !isEmptyValue(val) { + transformed["portName"] = transformedPortName + } + + transformedPortSpecification, err := expandComputeRegionHealthCheckGrpcHealthCheckPortSpecification(original["port_specification"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPortSpecification); val.IsValid() && !isEmptyValue(val) { + transformed["portSpecification"] = transformedPortSpecification + } + + transformedGrpcServiceName, err := expandComputeRegionHealthCheckGrpcHealthCheckGrpcServiceName(original["grpc_service_name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedGrpcServiceName); val.IsValid() && !isEmptyValue(val) { + transformed["grpcServiceName"] = transformedGrpcServiceName + } + + return transformed, nil +} + +func expandComputeRegionHealthCheckGrpcHealthCheckPort(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionHealthCheckGrpcHealthCheckPortName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionHealthCheckGrpcHealthCheckPortSpecification(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeRegionHealthCheckGrpcHealthCheckGrpcServiceName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + func expandComputeRegionHealthCheckRegion(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { f, err := parseGlobalFieldValue("regions", v.(string), "project", d, config, true) if err != nil { diff --git a/google/resource_compute_region_health_check_generated_test.go b/google/resource_compute_region_health_check_generated_test.go index 7b33d10cb17..1f42a14c407 100644 --- a/google/resource_compute_region_health_check_generated_test.go +++ b/google/resource_compute_region_health_check_generated_test.go @@ -451,6 +451,86 @@ resource "google_compute_region_health_check" "http2-region-health-check" { `, context) } +func TestAccComputeRegionHealthCheck_regionHealthCheckGrpcExample(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: testAccCheckComputeRegionHealthCheckDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRegionHealthCheck_regionHealthCheckGrpcExample(context), + }, + { + ResourceName: "google_compute_region_health_check.grpc-region-health-check", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccComputeRegionHealthCheck_regionHealthCheckGrpcExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_compute_region_health_check" "grpc-health-check" { + name = "tf-test-grpc-region-health-check%{random_suffix}" + + timeout_sec = 1 + check_interval_sec = 1 + + grpc_health_check { + port = "443" + } +} +`, context) +} + +func TestAccComputeRegionHealthCheck_regionHealthCheckGrpcFullExample(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: testAccCheckComputeRegionHealthCheckDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRegionHealthCheck_regionHealthCheckGrpcFullExample(context), + }, + { + ResourceName: "google_compute_region_health_check.grpc-region-health-check", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccComputeRegionHealthCheck_regionHealthCheckGrpcFullExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_compute_region_health_check" "grpc-health-check" { + name = "tf-test-grpc-region-health-check%{random_suffix}" + + timeout_sec = 1 + check_interval_sec = 1 + + grpc_health_check { + port_name = "health-check-port" + port_specification = "USE_NAMED_PORT" + grpc_service_name = "testservice" + } +} +`, context) +} + func testAccCheckComputeRegionHealthCheckDestroyProducer(t *testing.T) func(s *terraform.State) error { return func(s *terraform.State) error { for name, rs := range s.RootModule().Resources { diff --git a/website/docs/r/compute_health_check.html.markdown b/website/docs/r/compute_health_check.html.markdown index 83c2ebb0ea2..12b5474fa2d 100644 --- a/website/docs/r/compute_health_check.html.markdown +++ b/website/docs/r/compute_health_check.html.markdown @@ -279,6 +279,48 @@ resource "google_compute_health_check" "http2-health-check" { } } ``` +
+## Example Usage - Health Check Grpc + + +```hcl +resource "google_compute_health_check" "grpc-health-check" { + name = "grpc-health-check" + + timeout_sec = 1 + check_interval_sec = 1 + + grpc_health_check { + port = "443" + } +} +``` + +## Example Usage - Health Check Grpc Full + + +```hcl +resource "google_compute_health_check" "grpc-health-check" { + name = "grpc-health-check" + + timeout_sec = 1 + check_interval_sec = 1 + + grpc_health_check { + port_name = "health-check-port" + port_specification = "USE_NAMED_PORT" + grpc_service_name = "testservice" + } +} +```