Skip to content

feat: provider skeleton with endpoint resource and chains data source - #1

Merged
AlejandroFabianCampos merged 4 commits into
mainfrom
feat/provider-skeleton
Sep 22, 2026
Merged

AlejandroFabianCampos merged 4 commits into
mainfrom
feat/provider-skeleton

Conversation

@AlejandroFabianCampos

@AlejandroFabianCampos AlejandroFabianCampos commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

First PR for the official Quicknode Terraform provider. It establishes the codegen pipeline, the client, the first resource, and everything the repo needs to be public and publishable to the Terraform Registry.

Nothing is published to the Registry yet — see Not done below.

Provider

Code generation (api/admin/)

The Go client is generated from the Admin API's OpenAPI spec. The spec is vendored verbatim and a separate patch file carries the local schema corrections, so the vendored copy can be refreshed without losing local changes to a merge conflict:

  • openapi.upstream.json — vendored verbatim, refreshed with make vendor
  • patches.json — JSON Pointer corrections applied before generation
  • gen/main.go — applies the patches, emits openapi.json
  • admin.gen.go — generated client, committed so builds don't require the toolchain

Client (internal/client/)

Hand-written domain layer over the generated client, which produces anonymous inline structs unusable as a public surface. Owns the domain model (Endpoint, Chain, Tag) and normalizes the API's response envelopes into a single error type.

Includes a retry transport with client-side rate limiting, exponential backoff with jitter, Retry-After honoring, and retries on 429/502/503/504. Terraform parallelizes applies ten ways by default, so throttling belongs in the provider rather than in the user's config.

Provider (internal/provider/)

  • quicknode_endpoint — create, read, update, delete, import
  • quicknode_chains — data source

Registry publishing

  • terraform-registry-manifest.json declaring protocol 6.0. Without it the Registry assumes protocol 5 and every terraform init fails.
  • .goreleaser.yml producing the multi-platform zips, SHA256SUMS and detached GPG signature the Registry requires. goreleaser check passes.
  • .github/workflows/release.yml — tag-triggered, imports the signing key, runs GoReleaser.

Two secrets need to exist before the first tag: GPG_PRIVATE_KEY and PASSPHRASE, with the public half registered on the Registry.

Documentation

docs/ is generated by tfplugindocs from the provider schema plus examples/, with templates/index.md.tmpl carrying the provider overview. examples/ follows the layout the generator reads, so resource examples and the import command appear in the published docs automatically.

CI fails if docs/ is stale relative to the schema.

CI and conventions

  • .github/workflows/test.yml — build, golangci-lint, docs freshness, unit tests, and acceptance tests gated to pushes on main
  • .golangci.yml — standard set plus bodyclose, errorlint, nilerr, misspell and others, with generated code excluded. Zero issues.
  • .gitattributes marking generated and vendored files so GitHub collapses them in diffs and language stats
  • CONTRIBUTING.md, SECURITY.md, CHANGELOG.md, CODEOWNERS, Dependabot

The README was rewritten to match the convention across published providers — contributor-facing, with usage reference living in docs/ rather than duplicated in the README.

Two bugs worth calling out

Both were caught during review of this branch.

URL composition. The auth token is not at a fixed position in an endpoint URL — some chains append a path suffix after it (https://<host>/<token>/evm). Telling callers to join a base URL to a token produces a broken URL on those chains and a working one everywhere else, which is the worst failure mode available. The resource exposes http_url_with_token and wss_url_with_token straight from the API so nothing has to be composed.

Non-convergent plans. chain and network carry RequiresReplace, and Read writes the API's canonical slug back into state. Matching the configured slug case-insensitively at plan time therefore means chain = "ETH" validates, gets stored, gets overwritten with eth on the next refresh, and proposes a destroy-and-recreate on every plan forever. validateChainNetwork matches exactly and emits a diagnostic naming the canonical spelling.

Reconciliation

The endpoint resource is identified solely by the API's numeric ID. There is no user-settable unique name, so a provider cannot adopt an existing endpoint or detect a duplicate create. The mitigation is remote state plus terraform import.

A 404 on read removes the resource from state instead of erroring, so deleting an endpoint in the dashboard proposes a recreate rather than wedging every subsequent plan until someone runs terraform state rm by hand.

Not done

  • No live apply has been run. Reads are verified against the real API; create, update and delete are not. Acceptance tests against a throwaway paid account are the gate before this is trustworthy, and the CI job for them is wired but needs QUICKNODE_API_KEY in secrets.
  • security_options on the endpoint, per-entry security resources, rate-limit resources, and data.quicknode_endpoint(s) are designed but unimplemented.
  • The provider has not been published to the Registry. That claims the quicknode/quicknode namespace and is effectively permanent.

Verification

make lint (gofmt, go vet, golangci-lint) — zero issues. make test passes. make docs is clean. goreleaser check passes. terraform fmt -check on examples/ is clean. TestProviderSchema exercises GetProviderSchema over real protocol v6, so the schema is known to serve.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@AlejandroFabianCampos
AlejandroFabianCampos merged commit f91942a into main Sep 22, 2026
4 checks passed
@AlejandroFabianCampos
AlejandroFabianCampos deleted the feat/provider-skeleton branch September 22, 2026 19:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant