2

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?

Mr P
  • 53
  • 6
  • You are providing the path to a YML file while the contents are JSON encoded? Not familiar with GCP that much, but can that work? – Marko E Jul 18 '22 at 08:36
  • Since yaml is a superset of json, any json should be valid yaml. The yamlencode() function is in experimental state and it is recommended to use jsonencode() instead as described [here](https://www.terraform.io/language/functions/yamlencode) – Mr P Jul 18 '22 at 08:41
  • @MrP, let me know if the answer provided below works. – Catherine O Jul 22 '22 at 00:03

1 Answers1

1

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.

Catherine O
  • 943
  • 1
  • 9
  • This resolved the issue.Thanks! I had to change one detail. The required parameter has to be given as a boolean and not as a string: `required: true`. – Mr P Aug 05 '22 at 07:16