Identify Resources within OpenAPI

Brandon Croft,api

This is part of a series of notes about code generating a Terraform Provider using OpenAPI

OpenAPI purports to describe RESTful APIs. In it's specification introduction, you'll find:

The OpenAPI Specification (OAS) defines a standard, language-agnostic interface to RESTful APIs

It's technically true, but the spec does not begin with the concept of a resource that have a collection of operations, it begins with a list of paths:

'/pets':
  get:
    description: ...
  post:
    description: ...
'/pets/{petId}':
  get:
    description: ...
  post:
    description: ...
  delete:
    description: ...

Taken together, these paths and methods may represent what we know as a RESTful resource, but the specification does not bind them that way. All these must play a role in the definition of a Terraform resource, but no rules exist to tell us exactly which would define one.

Bingo Card Approach

The approach I've decided on is to probe the spec for resources, then check off as many Terraform requirements as I can uncover for each one. If all the requirements are met, mostly identifiable CRUD actions, then Terraform can reasonably manage it as a resource. The probe I've chosen is derived from the Rails concept of resource for lack of a more popular alternative. Resources that don't meet the requirements are eligible to be data sources.

Here is how I determine which paths belong to a resource:

Without configuration input, I try to come up with a key that is derived from the parts of the paths without any parameters or non-alphanumeric characters. For instance:

/pets/ and /pets/{petId} both discard their parameters and slashes to become keyed "Pets"

This key is used to aggregate paths together and then their methods can be probed in the next step. It would be nice if you could configure the tool with non-conforming paths, so if you named your resource's paths /pets/ and /pet/{petId}, you can still bind these together under a common key.

Here are the requirements:

  1. Supports a "create" operation by collection (POST /pets) that returns a 201 or 200
  2. Supports a "show" operation by identity (GET /pets/1) that returns a 200 or 203 response.
  3. Supports a "delete" operation by identity (DELETE /pets/1) that returns a 200 or 204 response.
  4. (Optional) Supports an "update" operation by identity (PUT/PATCH/POST /pets/1) that returns a 200 response. Important caveat: If an endpoint supports create & delete but not update, the resource can be re-created when modified. This is common in distributed systems like Nomad.

Other elements must match, such as the response content types.

If any collection of paths that create matching keys fulfill all those requirements, a Terraform Resource schema can be generated. For non-conforming paths, users can manually specify the openAPI bindings for each operation.

You can play around with these REST probing rules by using tfpgen examine which will apply these rules to any valid OAPI 3 spec. Here is the output for the canonical petstore example:

petstore.yaml

In petstore, the only resource we found was for the pets. Store orders came close to being a resource, but it lacks a delete operation. I need to find some more specifications to get a sense of whether the bingo card approach is adequate.

GitHub link (opens in a new tab)

© Brandon Croft.