diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..5156646 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,90 @@ +# CLAUDE.md — Project context for AI assistants + +## Project Overview + +Terraform provider for VirtFusion (virtualization management platform). Written in Go using the [Terraform Plugin Framework](https://developer.hashicorp.com/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, merges `data` arrays, 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 calling `url.JoinPath` to avoid `%3F` encoding. +- List endpoints use `?results=N` to set page size (default 300 via `defaultResultsPerPage`). + +### Data Sources + +- List data sources use `GetAllPages` and expose an optional `results` attribute (default 300). +- Single-item data sources use `Get` and take a required `id` attribute. +- 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 token `url` fields must be marked `Sensitive: true` in schemas. + +### API Type Mappings (known quirks) + +- `ServerData.Suspended` is `bool` (API returns `true`/`false`, not `0`/`1`). +- `ServerData` uses `ownerId` (not `userId`) and nests resources under `cpu.cores`, `settings.resources.*`. +- `PackageData` uses `primaryStorage`, `primaryNetworkSpeedIn`, `primaryNetworkSpeedOut` (not `storage`, `networkSpeedInbound`, etc.). +- `IPBlockData.Type` is `int64` (4=IPv4, 6=IPv6). Gateway/netmask are nested under `ipv4`. +- 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 + +```bash +# 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.com` or `https://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.JoinPath` encodes `?` as `%3F` — never put query params in the path argument. The client handles this by splitting at `?` first. +- The VirtFusion `/servers` endpoint caps at 20 results per page regardless of the `results` parameter. `GetAllPages` handles this transparently by fetching all pages. +- Don't use `json:"storage"` for package storage — the API field is `json:"primaryStorage"`. +- Single server GET and list server responses have different shapes. `ServerData` type handles both via optional nested pointer fields. diff --git a/README.md b/README.md index ca861cb..704b589 100644 --- a/README.md +++ b/README.md @@ -1,73 +1,201 @@ -# Virtfusion Terraform Provider +# Terraform Provider for VirtFusion -

NOTE: This is a work in progress and is not yet ready for production use.

+A Terraform provider for managing [VirtFusion](https://virtfusion.com) virtualization platform resources. Automate server provisioning, user management, networking, and self-service billing through infrastructure as code. +## Requirements -## Overview +- [Terraform](https://developer.hashicorp.com/terraform/install) >= 1.0 +- [Go](https://golang.org/doc/install) >= 1.21 (for building from source) +- A VirtFusion control panel instance with API access -This is a Terraform provider for the Virtfusion API. It allows you to manage your Virtfusion resources using Terraform. +## Installation -# What can I do with this provider? +### From Release -Currently, you're able to manage the following resources: -* Create and delete virtual machines -* Create and delete SSH keys +Download the appropriate binary from the [Releases](https://git.ezscale.cloud/EZSCALE/terraform-provider-virtfusion/releases) page and place it in your Terraform plugins directory. -# How do I use this provider? +### From Source -Below is an example of how to use this provider to create a virtual machine and an SSH key. +```bash +git clone https://git.ezscale.cloud/EZSCALE/terraform-provider-virtfusion.git +cd terraform-provider-virtfusion +go install . +``` + +Then configure `~/.terraformrc` for local development: + +```hcl +provider_installation { + dev_overrides { + "registry.terraform.io/EZSCALE/virtfusion" = "/path/to/your/gopath/bin" + } + direct {} +} +``` + +## Quick Start ```hcl terraform { required_providers { virtfusion = { - source = "ezscale/virtfusion" - version = "0.0.3" + source = "registry.terraform.io/EZSCALE/virtfusion" } } } +variable "virtfusion_api_token" { + type = string + sensitive = true +} + provider "virtfusion" { - endpoint = "virtfusion.example.com" - api_token = "" -} - -variable "common" { - type = map(string) - default = { - hypervisor_id = 1 - package_id = 12 - user_id = 1 - } -} - -# Create a SSH key -resource "virtfusion_ssh" "key1" { - name = "My Test Key" - public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKWyBR+dk5M5MMfmH6Ss5QDSgcAvbCYu0DkqgPKH8O5T testkey@example.com" - user_id = var.common["user_id"] -} - -# Create a server -resource "virtfusion_server" "node1" { - hypervisor_id = var.common["hypervisor_id"] - package_id = var.common["package_id"] - user_id = var.common["user_id"] -} - -# Initialize the server with the OS we want, the SSH key we want, and the hostname we want. -resource "virtfusion_build" "node1" { - server_id = virtfusion_server.node1.id - name = "node1-demo" - hostname = "node1.example.com" - osid = 34 - vnc = true - ipv6 = true - ssh_keys = [virtfusion_ssh.key1.id] - email = true + endpoint = "https://cp.example.com" + api_token = var.virtfusion_api_token } ``` -# How can I contribute? +The provider can also be configured using environment variables: -If you'd like to contribute, please feel free to open a pull request. If you're unsure of what to work on, please check the issues tab for any open issues. \ No newline at end of file +```bash +export VIRTFUSION_ENDPOINT="https://cp.example.com" +export VIRTFUSION_API_TOKEN="your-api-token" +``` + +## Usage Examples + +### Create a user, SSH key, server, and build it + +```hcl +resource "virtfusion_user" "customer" { + name = "Jane Doe" + email = "jane@example.com" + ext_relation_id = "cust-001" +} + +resource "virtfusion_ssh_key" "key" { + user_id = virtfusion_user.customer.id + name = "deploy-key" + public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExample deploy@example.com" +} + +resource "virtfusion_server" "web" { + package_id = 19 + user_id = virtfusion_user.customer.id + hypervisor_id = 8 +} + +resource "virtfusion_server_build" "web" { + server_id = virtfusion_server.web.id + name = "web-server" + hostname = "web.example.com" + osid = 13 + vnc = true + ipv6 = true + ssh_keys = [virtfusion_ssh_key.key.id] +} +``` + +### Read infrastructure data + +```hcl +data "virtfusion_servers" "all" {} +data "virtfusion_packages" "all" {} +data "virtfusion_hypervisors" "all" {} + +output "server_count" { + value = length(data.virtfusion_servers.all.servers) +} +``` + +List data sources support a `results` parameter (default: 300) and automatically paginate through all pages: + +```hcl +data "virtfusion_servers" "all" { + results = 100 # override per-page limit +} +``` + +## Resources + +| Resource | Description | +|----------|-------------| +| `virtfusion_server` | Create and manage servers | +| `virtfusion_server_build` | Build (install OS on) a server | +| `virtfusion_server_firewall` | Manage server firewall rules | +| `virtfusion_server_ipv4` | Add IPv4 addresses to a server | +| `virtfusion_server_network_whitelist` | Manage network whitelist entries | +| `virtfusion_server_traffic_block` | Manage traffic blocks | +| `virtfusion_server_power_action` | Control server power state | +| `virtfusion_server_password_reset` | Reset server root password | +| `virtfusion_ssh_key` | Manage SSH keys | +| `virtfusion_user` | Manage users | +| `virtfusion_user_auth_token` | Generate user authentication tokens | +| `virtfusion_user_server_auth_token` | Generate server-scoped authentication tokens | +| `virtfusion_user_password_reset` | Reset user passwords | +| `virtfusion_ip_block_range` | Add IPv4 ranges to IP blocks | +| `virtfusion_self_service_credit` | Manage self-service billing credits | +| `virtfusion_self_service_resource_pack` | Manage resource packs | +| `virtfusion_self_service_hourly_group_profile` | Manage hourly billing group profiles | +| `virtfusion_self_service_hourly_resource_pack` | Manage hourly resource packs | +| `virtfusion_self_service_resource_group_profile` | Manage resource group profiles | +| `virtfusion_self_service_pack_servers_action` | Suspend/unsuspend/delete pack servers | + +## Data Sources + +| Data Source | Description | +|-------------|-------------| +| `virtfusion_server` | Read a single server by ID | +| `virtfusion_servers` | List all servers | +| `virtfusion_servers_by_user` | List servers owned by a user | +| `virtfusion_server_backups` | List server backups | +| `virtfusion_server_firewall` | Read server firewall configuration | +| `virtfusion_server_templates` | List available templates for a server | +| `virtfusion_server_traffic` | Read server traffic usage | +| `virtfusion_server_traffic_blocks` | List server traffic blocks | +| `virtfusion_server_vnc` | Get VNC connection info | +| `virtfusion_hypervisor` | Read a single hypervisor | +| `virtfusion_hypervisors` | List all hypervisors | +| `virtfusion_hypervisor_group` | Read a single hypervisor group | +| `virtfusion_hypervisor_groups` | List all hypervisor groups | +| `virtfusion_hypervisor_group_resources` | Read aggregated resources for a group | +| `virtfusion_package` | Read a single package | +| `virtfusion_packages` | List all packages | +| `virtfusion_package_templates` | List templates for a package | +| `virtfusion_ip_block` | Read a single IP block | +| `virtfusion_ip_blocks` | List all IP blocks | +| `virtfusion_ssh_key` | Read a single SSH key | +| `virtfusion_ssh_keys_by_user` | List SSH keys for a user | +| `virtfusion_user` | Read a user by external relation ID | +| `virtfusion_dns_service` | Read DNS service configuration | +| `virtfusion_iso` | Read ISO media info | +| `virtfusion_queue_item` | Read a queue item status | +| `virtfusion_self_service_currencies` | List available currencies | +| `virtfusion_self_service_resource_pack` | Read a resource pack | +| `virtfusion_self_service_hourly_stats` | Read hourly billing stats | +| `virtfusion_self_service_report` | Read self-service reports | +| `virtfusion_self_service_usage` | Read self-service usage | + +## Development + +### Build + +```bash +go build ./... +``` + +### Install locally + +```bash +go install . +``` + +### Run linter + +```bash +golangci-lint run +``` + +## License + +[MPL-2.0](LICENSE)