Skip to content

Commit

Permalink
Adds the synthetic monitor target type as an option for uptime checks. (
Browse files Browse the repository at this point in the history
#8709) (#15623)

* Adds synthetic_monitor type that lives alongside resource_group and monitored_resource
* removes requirement for either tcp_check or http_check to be provided, as neither is required when synthetic_monitor is provided
* Adds acceptance test, and example. A new test fixutre w/ zip file is provided for these flows.

Signed-off-by: Modular Magician <magic-modules@google.com>
  • Loading branch information
modular-magician authored Aug 24, 2023
1 parent 0e253e0 commit a948669
Show file tree
Hide file tree
Showing 5 changed files with 259 additions and 4 deletions.
3 changes: 3 additions & 0 deletions .changelog/8709.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
monitoring: added `synthetic_monitor` to `google_monitoring_uptime_check_config` resource
```
117 changes: 113 additions & 4 deletions google/services/monitoring/resource_monitoring_uptime_check_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,6 @@ func ResourceMonitoringUptimeCheckConfig() *schema.Resource {
},
},
},
ExactlyOneOf: []string{"http_check", "tcp_check"},
},
"monitored_resource": {
Type: schema.TypeList,
Expand All @@ -249,7 +248,7 @@ func ResourceMonitoringUptimeCheckConfig() *schema.Resource {
},
},
},
ExactlyOneOf: []string{"monitored_resource", "resource_group"},
ExactlyOneOf: []string{"monitored_resource", "resource_group", "synthetic_monitor"},
},
"period": {
Type: schema.TypeString,
Expand Down Expand Up @@ -283,7 +282,7 @@ func ResourceMonitoringUptimeCheckConfig() *schema.Resource {
},
},
},
ExactlyOneOf: []string{"monitored_resource", "resource_group"},
ExactlyOneOf: []string{"monitored_resource", "resource_group", "synthetic_monitor"},
},
"selected_regions": {
Type: schema.TypeList,
Expand All @@ -293,6 +292,36 @@ func ResourceMonitoringUptimeCheckConfig() *schema.Resource {
Type: schema.TypeString,
},
},
"synthetic_monitor": {
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Description: `A Synthetic Monitor deployed to a Cloud Functions V2 instance.`,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"cloud_function_v2": {
Type: schema.TypeList,
Required: true,
ForceNew: true,
Description: `Target a Synthetic Monitor GCFv2 Instance`,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: `The fully qualified name of the cloud function resource.`,
},
},
},
ExactlyOneOf: []string{},
},
},
},
ExactlyOneOf: []string{"monitored_resource", "resource_group", "synthetic_monitor"},
},
"tcp_check": {
Type: schema.TypeList,
Optional: true,
Expand All @@ -307,7 +336,6 @@ func ResourceMonitoringUptimeCheckConfig() *schema.Resource {
},
},
},
ExactlyOneOf: []string{"http_check", "tcp_check"},
},
"name": {
Type: schema.TypeString,
Expand Down Expand Up @@ -398,6 +426,12 @@ func resourceMonitoringUptimeCheckConfigCreate(d *schema.ResourceData, meta inte
} else if v, ok := d.GetOkExists("monitored_resource"); !tpgresource.IsEmptyValue(reflect.ValueOf(monitoredResourceProp)) && (ok || !reflect.DeepEqual(v, monitoredResourceProp)) {
obj["monitoredResource"] = monitoredResourceProp
}
syntheticMonitorProp, err := expandMonitoringUptimeCheckConfigSyntheticMonitor(d.Get("synthetic_monitor"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("synthetic_monitor"); !tpgresource.IsEmptyValue(reflect.ValueOf(syntheticMonitorProp)) && (ok || !reflect.DeepEqual(v, syntheticMonitorProp)) {
obj["syntheticMonitor"] = syntheticMonitorProp
}

lockName, err := tpgresource.ReplaceVars(d, config, "stackdriver/groups/{{project}}")
if err != nil {
Expand Down Expand Up @@ -549,6 +583,9 @@ func resourceMonitoringUptimeCheckConfigRead(d *schema.ResourceData, meta interf
if err := d.Set("monitored_resource", flattenMonitoringUptimeCheckConfigMonitoredResource(res["monitoredResource"], d, config)); err != nil {
return fmt.Errorf("Error reading UptimeCheckConfig: %s", err)
}
if err := d.Set("synthetic_monitor", flattenMonitoringUptimeCheckConfigSyntheticMonitor(res["syntheticMonitor"], d, config)); err != nil {
return fmt.Errorf("Error reading UptimeCheckConfig: %s", err)
}

return nil
}
Expand Down Expand Up @@ -1053,6 +1090,36 @@ func flattenMonitoringUptimeCheckConfigMonitoredResourceLabels(v interface{}, d
return v
}

func flattenMonitoringUptimeCheckConfigSyntheticMonitor(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["cloud_function_v2"] =
flattenMonitoringUptimeCheckConfigSyntheticMonitorCloudFunctionV2(original["cloudFunctionV2"], d, config)
return []interface{}{transformed}
}
func flattenMonitoringUptimeCheckConfigSyntheticMonitorCloudFunctionV2(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["name"] =
flattenMonitoringUptimeCheckConfigSyntheticMonitorCloudFunctionV2Name(original["name"], d, config)
return []interface{}{transformed}
}
func flattenMonitoringUptimeCheckConfigSyntheticMonitorCloudFunctionV2Name(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
return v
}

func expandMonitoringUptimeCheckConfigDisplayName(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
return v, nil
}
Expand Down Expand Up @@ -1451,3 +1518,45 @@ func expandMonitoringUptimeCheckConfigMonitoredResourceLabels(v interface{}, d t
}
return m, nil
}

func expandMonitoringUptimeCheckConfigSyntheticMonitor(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{})

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

return transformed, nil
}

func expandMonitoringUptimeCheckConfigSyntheticMonitorCloudFunctionV2(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{})

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

return transformed, nil
}

func expandMonitoringUptimeCheckConfigSyntheticMonitorCloudFunctionV2Name(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
return v, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,81 @@ resource "google_monitoring_group" "check" {
`, context)
}

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

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

acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
CheckDestroy: testAccCheckMonitoringUptimeCheckConfigDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccMonitoringUptimeCheckConfig_uptimeCheckConfigSyntheticMonitorExample(context),
},
{
ResourceName: "google_monitoring_uptime_check_config.synthetic_monitor",
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func testAccMonitoringUptimeCheckConfig_uptimeCheckConfigSyntheticMonitorExample(context map[string]interface{}) string {
return acctest.Nprintf(`
resource "google_storage_bucket" "bucket" {
name = "%{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_cloudfunctions2_function" "function" {
name = "tf_test_synthetic_function%{random_suffix}"
location = "us-central1"
build_config {
runtime = "nodejs16"
entry_point = "SyntheticFunction" # Set the entry point
source {
storage_source {
bucket = google_storage_bucket.bucket.name
object = google_storage_bucket_object.object.name
}
}
}
service_config {
max_instance_count = 1
available_memory = "256M"
timeout_seconds = 60
}
}
resource "google_monitoring_uptime_check_config" "synthetic_monitor" {
display_name = "tf_test_synthetic_monitor%{random_suffix}"
timeout = "60s"
synthetic_monitor {
cloud_function_v2 {
name = google_cloudfunctions2_function.function.id
}
}
}
`, context)
}

func testAccCheckMonitoringUptimeCheckConfigDestroyProducer(t *testing.T) func(s *terraform.State) error {
return func(s *terraform.State) error {
for name, rs := range s.RootModule().Resources {
Expand Down
Binary file not shown.
68 changes: 68 additions & 0 deletions website/docs/r/monitoring_uptime_check_config.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,55 @@ resource "google_monitoring_group" "check" {
filter = "resource.metadata.name=has_substring(\"foo\")"
}
```
## Example Usage - Uptime Check Config Synthetic Monitor


```hcl
resource "google_storage_bucket" "bucket" {
name = "my-project-name-gcf-source" # 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 = "synthetic-fn-source.zip" # Add path to the zipped function source code
}
resource "google_cloudfunctions2_function" "function" {
name = "synthetic_function"
location = "us-central1"
build_config {
runtime = "nodejs16"
entry_point = "SyntheticFunction" # Set the entry point
source {
storage_source {
bucket = google_storage_bucket.bucket.name
object = google_storage_bucket_object.object.name
}
}
}
service_config {
max_instance_count = 1
available_memory = "256M"
timeout_seconds = 60
}
}
resource "google_monitoring_uptime_check_config" "synthetic_monitor" {
display_name = "synthetic_monitor"
timeout = "60s"
synthetic_monitor {
cloud_function_v2 {
name = google_cloudfunctions2_function.function.id
}
}
}
```

## Argument Reference

Expand Down Expand Up @@ -231,6 +280,11 @@ The following arguments are supported:
The monitored resource (https://cloud.google.com/monitoring/api/resources) associated with the configuration. The following monitored resource types are supported for uptime checks: uptime_url gce_instance gae_app aws_ec2_instance aws_elb_load_balancer k8s_service servicedirectory_service
Structure is [documented below](#nested_monitored_resource).

* `synthetic_monitor` -
(Optional)
A Synthetic Monitor deployed to a Cloud Functions V2 instance.
Structure is [documented below](#nested_synthetic_monitor).

* `project` - (Optional) The ID of the project in which the resource belongs.
If it is not provided, the provider project is used.

Expand Down Expand Up @@ -366,6 +420,20 @@ The following arguments are supported:
(Required)
Values for all of the labels listed in the associated monitored resource descriptor. For example, Compute Engine VM instances use the labels "project_id", "instance_id", and "zone".

<a name="nested_synthetic_monitor"></a>The `synthetic_monitor` block supports:

* `cloud_function_v2` -
(Required)
Target a Synthetic Monitor GCFv2 Instance
Structure is [documented below](#nested_cloud_function_v2).


<a name="nested_cloud_function_v2"></a>The `cloud_function_v2` block supports:

* `name` -
(Required)
The fully qualified name of the cloud function resource.

## Attributes Reference

In addition to the arguments listed above, the following computed attributes are exported:
Expand Down

0 comments on commit a948669

Please sign in to comment.