Async resource provisioning with polling
Many infrastructure APIs accept a create or delete request immediately and finish the work in the background. The API returns a 202 (or 204) with an identifier, and the resource is not actually usable, or fully removed, until a subsequent GET confirms the new state.
Speakeasy-generated Terraform providers can wait for those transitions automatically with the x-speakeasy-polling extension wired into the create and delete entity operations. This guide shows how to apply polling without touching the base OpenAPI document, by layering an overlay.
Scenario: a database that provisions asynchronously
The base API exposes a Database resource with an asynchronous create and delete:
POST /databasesreturns202 Acceptedonce the request is queued.GET /databases/{id}returns the currentstatus:pending,ready, orfailed.DELETE /databases/{id}returns204 No Contentwhile tear-down continues asynchronously.
Base spec (excerpt)
paths:
/databases:
post:
operationId: createDatabase
x-speakeasy-entity-operation: Database#create
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/DatabaseRequest"
responses:
"202":
description: Database creation accepted; provisioning asynchronously.
content:
application/json:
schema:
$ref: "#/components/schemas/Database"
/databases/{id}:
parameters:
- name: id
in: path
required: true
schema:
type: string
get:
operationId: getDatabase
x-speakeasy-entity-operation: Database#read
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: "#/components/schemas/Database"
"404":
description: Not found
delete:
operationId: deleteDatabase
x-speakeasy-entity-operation: Database#delete
responses:
"204":
description: Deletion accepted; tear-down is asynchronous.
components:
schemas:
Database:
type: object
required: [id, status]
properties:
id:
type: string
name:
type: string
status:
type: string
enum: [pending, ready, failed]Overlay: add polling to create and delete
Apply polling as an overlay so the base spec stays untouched. The overlay does two things:
- Declares named polling strategies on the
GEToperation. - Wires those strategies into the create and delete entity operations so Terraform waits after each lifecycle step.
overlay: 1.0.0
info:
title: Add async polling to Database
version: 0.0.0
actions:
# 1. Define polling strategies on the read operation.
- target: $.paths['/databases/{id}'].get
update:
x-speakeasy-polling:
- name: WaitForReady
successCriteria:
- condition: $statusCode == 200
- condition: $response.body#/status == "ready"
failureCriteria:
- condition: $statusCode == 200
- condition: $response.body#/status == "failed"
- name: WaitForDeleted
successCriteria:
- condition: $statusCode == 404
# 2. Attach the read operation as a follow-up to create and delete,
# using each polling strategy.
- target: $.paths['/databases/{id}'].get
update:
x-speakeasy-entity-operation:
- Database#read
- entityOperation: Database#create#2
options:
polling:
name: WaitForReady
delaySeconds: 2
intervalSeconds: 5
limitCount: 120
- entityOperation: Database#delete#2
options:
polling:
name: WaitForDeleted
intervalSeconds: 5
limitCount: 60Apply the overlay alongside the base spec with speakeasy overlay apply -s openapi.yaml -o overlay.yaml, or attach it to a Terraform source with speakeasy configure sources.
What this produces
- After
terraform applycreates a database, the provider callsGET /databases/{id}on a 5-second interval, after a 2-second initial delay, untilstatus == "ready". Polling fails fast ifstatus == "failed"or after 120 attempts. - After
terraform destroy, the provider polls until theGETreturns404, confirming tear-down completed. - The
#2suffix onDatabase#create#2andDatabase#delete#2tells the generator to run theGETas a second step of the create or delete lifecycle. That is how the read operation is reused as a wait step without colliding with the normalDatabase#read.
Tuning knobs
The behavior of each strategy is controlled by a small set of fields on x-speakeasy-polling. Per-resource values under options.polling (shown in the overlay above) override the defaults declared on the operation.
delaySeconds: Initial wait before the first poll.intervalSeconds: Time between polls.limitCount: Maximum poll attempts before giving up.successCriteria: All conditions must be true within a single poll response to stop polling successfully.failureCriteria: All conditions must be true within a single poll response to stop polling and surface an error. Use multiple criteria to assert against the same response (e.g.$statusCode == 200and$response.body#/status == "failed"); failure does not compose across separate polls.
Conditions use OpenAPI runtime expressions . The two most common contexts are $statusCode and $response.body#/<json-pointer>. See the polling reference for the full criterion grammar, including regex matching.
Related
Last updated on