Search code examples
google-cloud-platformterraformopenapigoogle-cloud-api-gateway

How to deploy GCP API Gateway Config with terraform when using wildcards in paths


I am trying to deploy a GCP API Gateway config with terraform. The OpenAPI document is defined inside the tf file, since I need to use variables for the backend URLs.

When I use wildcards in my paths (/devices/{deviceID}), the deployment produces an error. Without wildcards (/device) it works without issues.

This is the resource specification with wildcards:

resource "google_api_gateway_api_config" "device_management" {
  provider             = google-beta
  project              = var.project_id
  api                  = google_api_gateway_api.device_management.api_id
  api_config_id_prefix = "${google_api_gateway_api.device_management.api_id}-"

  gateway_config {
    backend_config {
      google_service_account = google_service_account.device_registration.id
    }
  }

  openapi_documents {
    document {
      path     = "${google_api_gateway_api.device_management.api_id}_api_spec.yaml"
      contents = base64encode(
        jsonencode(
          {
            swagger : "2.0"
            info : {
              title : "Device Registration API"
              description : "Register devices and get their private keys"
              version : "0.0.1"
            }
            schemes : ["https"]
            produces : ["application/json"]
            x-google-allow : "configured"
            securityDefinitions : {
              api_key : {
                type : "apiKey"
                name : "apiKey"
                in : "query"
              }
              oauth2 : {
                authorizationUrl : ""
                flow : "implicit"
                type : "oauth2"
                x-google-issuer : google_service_account.device_registration.email
                x-google-jwks_uri : "https://www.googleapis.com/robot/v1/metadata/x509/${google_service_account.device_registration.email}"
                x-google-audiences : "device-registration"
              }
            }
            security : [
              { oauth2 : [] }
            ]
            paths : {
              "/devices/{deviceID}" : {
                post : {
                  summary : "Register a new device"
                  operationId : "registerDevice"
                  x-google-backend : {
                    address : google_cloudfunctions_function.device_management_register_device.https_trigger_url
                  }
                  responses : {
                    201 : {
                      description : "Device registered succesfully"
                      schema : {
                        type : "string"
                      }
                    }
                  }
                }
              }
            }
          }
        )
      )
    }
  }

When deploying this I get the following error:

Error: Error creating ApiConfig: googleapi: Error 400: Cannot convert to service config.
│ 'location: "unknown location"
│ kind: ERROR
│ message: "http: undefined field \'deviceID\' on message \'google.protobuf.Empty\'."
│ 
│  location: "unknown location"
│ kind: ERROR
│ message: "http: undefined field \'deviceID\' on message \'google.protobuf.Empty\'."
│ 
│  location: "device-registration_api_spec.yaml"
│ message: "apiKey \'apiKey\' is ignored. Only apiKey with \'name\' as \'key\' and \'in\' as \'query\', or \'name\' as \'api_key\' and \'in\' as \'query\', or \'name\'as \'x-api-key\' and \'in\' as \'header\' are supported"
│ 
│  location: "device-registration_api_spec.yaml: Operation \'post\' in path \'/devices/{deviceID}\'"
│ message: "Operation does not require an API key; callers may invoke the method without specifying an associated API-consuming project. To enable API key all the SecurityRequirement Objects (https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#security-requirement-object) inside security definition must reference at least one SecurityDefinition of type : \'apiKey\'."
│ 
│  location: "device-registration_api_spec.yaml: Operation \'get\' in path \'/devices/{deviceID}/key\'"
│ message: "Operation does not require an API key; callers may invoke the method without specifying an associated API-consuming project. To enable API key all the SecurityRequirement Objects (https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#security-requirement-object) inside security definition must reference at least one SecurityDefinition of type : \'apiKey\'."
│ '
│ com.google.apps.framework.request.BadRequestException: Cannot convert to service config.
│ 'location: "unknown location"
│ kind: ERROR
│ message: "http: undefined field \'deviceID\' on message \'google.protobuf.Empty\'."
│ 
│  location: "unknown location"
│ kind: ERROR
│ message: "http: undefined field \'deviceID\' on message \'google.protobuf.Empty\'."
│ 
│  location: "device-registration_api_spec.yaml"
│ message: "apiKey \'apiKey\' is ignored. Only apiKey with \'name\' as \'key\' and \'in\' as \'query\', or \'name\' as \'api_key\' and \'in\' as \'query\', or \'name\'as \'x-api-key\' and \'in\' as \'header\' are supported"
│ 
│  location: "device-registration_api_spec.yaml: Operation \'post\' in path \'/devices/{deviceID}\'"
│ message: "Operation does not require an API key; callers may invoke the method without specifying an associated API-consuming project. To enable API key all the SecurityRequirement Objects (https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#security-requirement-object) inside security definition must reference at least one SecurityDefinition of type : \'apiKey\'."
│ 
│  location: "device-registration_api_spec.yaml: Operation \'get\' in path \'/devices/{deviceID}/key\'"
│ message: "Operation does not require an API key; callers may invoke the method without specifying an associated API-consuming project. To enable API key all the SecurityRequirement Objects (https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#security-requirement-object) inside security definition must reference at least one SecurityDefinition of type : \'apiKey\'."
│ '
│ 
│   with module.iot_backend.google_api_gateway_api_config.device_management,
│   on ../../modules/iot_backend/device_management.tf line 92, in resource "google_api_gateway_api_config" "device_management":
│   92: resource "google_api_gateway_api_config" "device_management" {
│ 
╵

How can I get this to work with wildcards?


Solution

  • Based from your error logs, you did not define the deviceID inside a parameter section or block. Add a parameters section for the deviceID parameter. Here is an example:

    paths:
      /users/{userId}:
        get:
          summary: Gets a user by ID.
          parameters:
            - in: path
              name: userId
              type: integer
              required: true
              description: Numeric ID of the user to get.

    In your case, it should be like this:

    paths : {
                  "/devices/{deviceID}" : {
                    post : {
                      summary : "Register a new device"
                      operationId : "registerDevice"
                      parameters  : [
                         {
                           in : "path",
                           name : "deviceID",
                           type : "string",
                           required : "true"
                         }
                      ],
                      x-google-backend : {
                        address : google_cloudfunctions_function.device_management_register_device.https_trigger_url
                      }
                      responses : {
                        201 : {
                          description : "Device registered succesfully"
                          schema : {
                            type : "string"
                          }
                        }
                      }
                    }
                  }
               }

    You may refer to these documentation, OpenAPI spec and Swagger, for more information.