|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Build & Development Commands |
| 6 | + |
| 7 | +```bash |
| 8 | +make build # Build binary: terraform-provider-namecheap |
| 9 | +make format # go fmt ./... |
| 10 | +make check # go vet ./... |
| 11 | +make lint # golangci-lint run (requires local install) |
| 12 | +make test # Unit tests: go test -v ./namecheap/... -count=1 -cover |
| 13 | +make testacc # Acceptance tests (requires env vars, see below) |
| 14 | +make docs # Generate docs with tfplugindocs |
| 15 | +make vendor # go mod vendor |
| 16 | +``` |
| 17 | + |
| 18 | +Run a single test: `go test -v ./namecheap/... -run TestFunctionName -count=1` |
| 19 | + |
| 20 | +### Acceptance Tests |
| 21 | + |
| 22 | +Require environment variables: `NAMECHEAP_USER_NAME`, `NAMECHEAP_API_USER`, `NAMECHEAP_API_KEY`, `NAMECHEAP_TEST_DOMAIN`. Set `NAMECHEAP_USE_SANDBOX=true` for sandbox testing. |
| 23 | + |
| 24 | +## Architecture |
| 25 | + |
| 26 | +This is a Terraform provider built with **terraform-plugin-sdk/v2** that manages Namecheap domain DNS configuration through the **go-namecheap-sdk/v2**. |
| 27 | + |
| 28 | +### Single Resource Provider |
| 29 | + |
| 30 | +The provider exposes one resource: `namecheap_domain_records`. All provider logic lives in the `namecheap/` package (package name: `namecheap_provider`). |
| 31 | + |
| 32 | +**Key files:** |
| 33 | +- `main.go` — Plugin entry point, serves the provider |
| 34 | +- `namecheap/provider.go` — Provider schema, config, and API client setup |
| 35 | +- `namecheap/namecheap_domain_record.go` — Resource CRUD schema and dispatch |
| 36 | +- `namecheap/namecheap_domain_record_functions.go` — Core business logic (~670 lines) |
| 37 | +- `namecheap/internal/mutexkv/` — Domain-level mutex for concurrent access |
| 38 | + |
| 39 | +### MERGE vs OVERWRITE Mode |
| 40 | + |
| 41 | +The central design pattern. Every CRUD operation has paired implementations: |
| 42 | + |
| 43 | +- **MERGE mode** (default): Multiple Terraform configs can manage different records on the same domain. Uses `ncMutexKV` (defined in provider.go) for domain-level locking to prevent race conditions. |
| 44 | +- **OVERWRITE mode**: Single Terraform config owns all records for a domain. No locking needed. |
| 45 | + |
| 46 | +Functions follow the naming convention: `{operation}Records{Mode}()` and `{operation}Nameservers{Mode}()` (e.g., `createRecordsMerge`, `readRecordsOverwrite`). |
| 47 | + |
| 48 | +### Address Normalization |
| 49 | + |
| 50 | +DNS records require address fixup before API calls: |
| 51 | +- `getFixedAddressOfRecord()` — Routes to type-specific fixers |
| 52 | +- `fixAddressEndWithDot()` — Appends trailing dot for CNAME, ALIAS, NS, MX records |
| 53 | +- `fixCAAAddressValue()` — Formats CAA record values with flags, tag, and quoted value |
| 54 | +- `filterDefaultParkingRecords()` — Strips Namecheap default parking records |
| 55 | + |
| 56 | +### Error Handling |
| 57 | + |
| 58 | +Uses `diag.Diagnostics` throughout for Terraform-native error reporting. API errors are wrapped with `diag.FromErr()`. |
| 59 | + |
| 60 | +## CI Pipeline |
| 61 | + |
| 62 | +Runs on push (`.github/workflows/ci.yml`): |
| 63 | +1. `go vet` + golangci-lint v1.54 + unit tests (ubuntu-latest) |
| 64 | +2. Acceptance tests on self-hosted EC2 runner (AL2023) against Namecheap sandbox |
| 65 | + |
| 66 | +## go-namecheap-sdk/v2 (Core Dependency) |
| 67 | + |
| 68 | +The provider is entirely built on `github.com/namecheap/go-namecheap-sdk/v2`. Understanding SDK patterns is critical. |
| 69 | + |
| 70 | +### SDK Client Structure |
| 71 | + |
| 72 | +The `*namecheap.Client` (stored as `meta interface{}` in provider) exposes three services: |
| 73 | +- `client.Domains` — `GetInfo()`, `GetList()` |
| 74 | +- `client.DomainsDNS` — `GetHosts()`, `SetHosts()`, `GetList()`, `SetCustom()`, `SetDefault()` |
| 75 | +- `client.DomainsNS` — `Create()`, `Delete()`, `GetInfo()`, `Update()` |
| 76 | + |
| 77 | +DNS methods accept full domain strings and parse internally. NS methods take pre-split `sld`/`tld` parameters. |
| 78 | + |
| 79 | +### Pointer-Heavy Design |
| 80 | + |
| 81 | +All SDK struct fields are pointers (`*string`, `*int`, `*bool`). Use the SDK helper constructors: `namecheap.String()`, `namecheap.Int()`, `namecheap.Bool()`, `namecheap.UInt8()`. Nil fields mean absent/unset values, not zero values. |
| 82 | + |
| 83 | +### MXPref Type Mismatch |
| 84 | + |
| 85 | +`GetHosts` returns `MXPref` as `*int`, but `SetHosts` expects `*uint8`. The provider bridges this with `namecheap.UInt8(uint8(*remoteRecord.MXPref))`. |
| 86 | + |
| 87 | +### Retry Logic |
| 88 | + |
| 89 | +The SDK retries on HTTP 405 (Namecheap's rate-limit response) with progressive delays: 1s, 5s, 15s, 30s, 50s. Retries are mutex-serialized. Total max wait: 101 seconds. |
| 90 | + |
| 91 | +### SetHosts Validation |
| 92 | + |
| 93 | +The SDK validates client-side before API calls: |
| 94 | +- Record type must be in `AllowedRecordTypeValues` (A, AAAA, ALIAS, CAA, CNAME, MX, MXE, NS, TXT, URL, URL301, FRAME) |
| 95 | +- TTL must be 60–60000 |
| 96 | +- MX records require `MXPref` and `EmailType == "MX"`; MXE requires `EmailType == "MXE"` and exactly 1 record |
| 97 | +- URL/URL301/FRAME records require protocol prefix; CAA iodef requires `http://` or `mailto:` |
| 98 | +- Email type must be in: NONE, MXE, MX, FWD, OX, GMAIL |
| 99 | + |
| 100 | +### SDK Gotchas |
| 101 | + |
| 102 | +- `SetCustom()` requires minimum 2 nameservers — the provider enforces this in merge logic too |
| 103 | +- `DomainsDNS.GetList()` silently falls back to `Domains.GetInfo()` on error 2019166 (FreeDNS domains) |
| 104 | +- `ParseDomain()` handles compound TLDs (`co.uk`, `gov.ua`) via `publicsuffix-go` |
| 105 | +- Default parking records (CNAME www→parkingpage.namecheap.com, URL @→http://www.domain) are returned by the API and must be filtered |
| 106 | +- `GetHosts` error checking uses `len(response.Errors) > 0` while all other methods use `response.Errors != nil && len(*response.Errors) > 0` |
| 107 | + |
| 108 | +## Key Dependencies |
| 109 | + |
| 110 | +- Go 1.21.5 |
| 111 | +- `github.com/hashicorp/terraform-plugin-sdk/v2` v2.31.0 |
| 112 | +- `github.com/namecheap/go-namecheap-sdk/v2` v2.4.0 |
| 113 | +- `github.com/stretchr/testify` v1.8.4 (tests) |
0 commit comments