Search code examples
google-cloud-platformterraformterraform-provider-gcp

Unexplained 400 error response using Terraform with Google cloud platform


I am trying to create the following uptime check resource on google cloud:

resource "google_monitoring_uptime_check_config" "create_uptime_check" {
  display_name = "my-uptime-check"
  timeout      = "5s"
  period       = "30s"

  http_check {
    path = "/endpoint/path"
    port = "8080"
    request_method = "GET"
    accepted_response_status_codes {
      status_class = "STATUS_CLASS_2XX"
    }
  }

  monitored_resource {
    type =  "uptime_url"
    labels = {
      project_id = "${var.project_id}"
      host       = "${var.host}"
    }
  }
 
  content_matchers {
    content = "UP"
    matcher = "MATCHES_JSON_PATH"
    json_path_matcher {
      json_path = "$.status"
      json_matcher = "EXACT_MATCH"
    }
  }

  checker_type = "STATIC_IP_CHECKERS"
}

This is all well and good. Terraform plan green lights everything. But when I apply the changes I get back the following error response back:

Error: Error creating UptimeCheckConfig: googleapi: Error 400: Unable to parse 'required_content' as JSON value. Make sure to quote strings and property names.

I could not understand what 'required_content' means here so i enabled debug logging in terraform which prints the following request json that is generated by terraform and sent to google cloud:

{
  "checkerType": "STATIC_IP_CHECKERS",
  "contentMatchers": [
   {
    "content": "UP",
    "jsonPathMatcher": {
     "jsonMatcher": "EXACT_MATCH",
     "jsonPath": "$.status"
    },
    "matcher": "MATCHES_JSON_PATH"
   }
  ],
  "displayName": "my-uptime-check",
  "httpCheck": {
   "acceptedResponseStatusCodes": [
    {
     "statusClass": "STATUS_CLASS_2XX"
    }
   ],
   "path": "/endpoint/path",
   "port": 80,
   "requestMethod": "GET"
  },
  "monitoredResource": {
   "labels": {
    "host": "my.host.com",
    "project_id": "my-project-id"
   },
   "type": "uptime_url"
  },
  "period": "30s",
  "timeout": "5s"
}

and the response received from google cloud is:

{
   "error": {
     "code": 400,
     "message": "Unable to parse 'required_content' as JSON value. Make sure to quote strings and property names.",
     "status": "INVALID_ARGUMENT"
   }
}

I cant seem to figure what im doing wrong. the terraform code conforms to what is in their docs. I even looked at google docs to see if im doing anything wrong but didnt find anything. Tried upgrading both terraform cli and google provider but nothing changed.

Thought about raising an issue on relevant github page but its clear from the debug logs that issue lies with google not terraform, well from what i understand at least.

Anybody got any clue what i am missing?


Solution

  • The problem in your code is that you didn't specify the content field as a string literal by adding appropriate quotes in the content_matchers block.

    When using json_path_matcher with EXACT_MATCH, you need to ensure the content field is identical to the response output, including all quotation marks. In JSON, string liberal is quoted. So, you should add that in your content field as: content = "\"UP\"". The following code I tested works:

    resource "google_monitoring_uptime_check_config" "create_uptime_check" {
      display_name = "my-uptime-check"
      timeout      = "5s"
      period       = "60s"
    
      http_check {
        path = "/endpoint/path"
        port = "8080"
        request_method = "GET"
        accepted_response_status_codes {
          status_class = "STATUS_CLASS_2XX"
        }
      }
    
      monitored_resource {
        type =  "uptime_url"
        labels = {
          project_id = "${var.project_id}"
          host       = "${var.host}"
        }
      }
    
      content_matchers {
        content = "\"UP\""
        matcher = "MATCHES_JSON_PATH"
        json_path_matcher {
          json_path = "$.status"
          json_matcher = "EXACT_MATCH"
        }
      }
    
      checker_type = "STATIC_IP_CHECKERS"
    }
    

    You can read more about this in Google doc. Obviously, your backend needs to return the UP in response.