Some checks failed
CI / build (push) Failing after 34s
- CLAUDE.md: architecture guide, conventions, type mapping quirks, build commands, and common pitfalls for AI-assisted development - README.md: full resource/data source tables, quick start guide, environment variable configuration, and usage examples Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
4.5 KiB
4.5 KiB
CLAUDE.md — Project context for AI assistants
Project Overview
Terraform provider for VirtFusion (virtualization management platform). Written in Go using the Terraform Plugin Framework (not the older SDKv2).
- Registry address:
registry.terraform.io/EZSCALE/virtfusion - Git hosting:
https://git.ezscale.cloud/EZSCALE/terraform-provider-virtfusion(Gitea, not GitHub) - VirtFusion API: Laravel-based REST API with Bearer token auth. API docs defined in
openapi.yaml.
Architecture
main.go # Entry point, provider server
internal/
client/
client.go # HTTP client constructor, endpoint normalization
request.go # HTTP methods (Get, Post, Put, Delete, GetAllPages)
types.go # API request/response struct definitions
errors.go # APIError type with status code helpers
provider/
provider.go # Provider config, resource/datasource registration,
# shared pagination helpers (resultsSchemaAttribute, resultsQueryParam)
resource_*.go # Managed resources (20 total)
data_source_*.go # Data sources (30 total)
Key Conventions
API Client
- All HTTP methods return
(json.RawMessage, error)— raw JSON to be unmarshalled by the caller. GetAllPages(ctx, path)handles Laravel-style pagination automatically: fetches all pages, mergesdataarrays, returns a synthetic{"data": [...]}response. Use this for all list endpoints.- Query parameters must be appended to the path string (e.g.,
/servers?results=300). The client splits path from query before callingurl.JoinPathto avoid%3Fencoding. - List endpoints use
?results=Nto set page size (default 300 viadefaultResultsPerPage).
Data Sources
- List data sources use
GetAllPagesand expose an optionalresultsattribute (default 300). - Single-item data sources use
Getand take a requiredidattribute. - Server list responses use a flat structure; single server GET returns nested objects (
cpu.cores,settings.resources.memory, etc.).
Resources
- Server creation uses
ServerCreateRequest(POST/servers) then separate PUT/POST calls for update-only attributes (name, CPU throttle, VNC, suspend, etc.). - User resources are keyed by
ext_relation_id(external relation ID), not numeric ID. - Security: All user-supplied strings interpolated into URL paths MUST be wrapped with
url.PathEscape(). - Sensitive attributes:
api_token,password,token, and auth tokenurlfields must be markedSensitive: truein schemas.
API Type Mappings (known quirks)
ServerData.Suspendedisbool(API returnstrue/false, not0/1).ServerDatausesownerId(notuserId) and nests resources undercpu.cores,settings.resources.*.PackageDatausesprimaryStorage,primaryNetworkSpeedIn,primaryNetworkSpeedOut(notstorage,networkSpeedInbound, etc.).IPBlockData.Typeisint64(4=IPv4, 6=IPv6). Gateway/netmask are nested underipv4.- Hypervisor group resources returns a paginated array of per-hypervisor entries, not a single aggregate object.
- API error bodies are truncated to 500 bytes in error messages to prevent leaking sensitive data.
Build & Test
# Build
go build ./...
# Install locally (goes to $GOPATH/bin)
go install .
# Lint
golangci-lint run
# Run against live VirtFusion (read-only test)
# Requires ~/.terraformrc with dev_overrides and test/main.tf (gitignored)
cd test && terraform apply -auto-approve
Environment Variables
VIRTFUSION_ENDPOINT— API base URL (e.g.,https://cp.example.comorhttps://cp.example.com/api/v1)VIRTFUSION_API_TOKEN— API bearer token
CI/CD
Uses Gitea Actions (.gitea/workflows/). GoReleaser for binary releases (.goreleaser.yml).
Common Pitfalls
url.JoinPathencodes?as%3F— never put query params in the path argument. The client handles this by splitting at?first.- The VirtFusion
/serversendpoint caps at 20 results per page regardless of theresultsparameter.GetAllPageshandles this transparently by fetching all pages. - Don't use
json:"storage"for package storage — the API field isjson:"primaryStorage". - Single server GET and list server responses have different shapes.
ServerDatatype handles both via optional nested pointer fields.