Archived
Merge pull request #53 from beatz174-bit/codex/harden-terraform-provider-dynu-for-production-readiness
Harden provider for production readiness
This commit is contained in:
@@ -2,6 +2,7 @@ name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
@@ -16,14 +17,27 @@ jobs:
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
- name: Verify formatting and run quality gate
|
||||
run: ./scripts/check.sh
|
||||
- name: Setup Terraform
|
||||
uses: hashicorp/setup-terraform@v3
|
||||
|
||||
- name: Build provider binary (stamped metadata)
|
||||
- name: Verify go mod tidy is clean
|
||||
run: |
|
||||
VERSION="${GITHUB_REF_NAME:-dev}"
|
||||
COMMIT="$(git rev-parse --short HEAD)"
|
||||
DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
go mod tidy
|
||||
git diff --exit-code go.mod go.sum
|
||||
|
||||
go build -o terraform-provider-dynu \
|
||||
-ldflags="-X main.version=${VERSION} -X main.commit=${COMMIT} -X main.date=${DATE}"
|
||||
- name: Verify gofmt
|
||||
run: |
|
||||
files="$(git ls-files '*.go')"
|
||||
test -z "$(gofmt -l ${files})"
|
||||
|
||||
- name: Go vet
|
||||
run: go vet ./...
|
||||
|
||||
- name: Go tests
|
||||
run: go test ./...
|
||||
|
||||
- name: Terraform fmt check
|
||||
run: terraform fmt -check -recursive examples
|
||||
|
||||
- name: Repository quality gate
|
||||
run: ./scripts/check.sh
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
name: release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
goreleaser:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
distribution: goreleaser
|
||||
version: '~> v2'
|
||||
args: release --clean
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
+19
-10
@@ -1,12 +1,12 @@
|
||||
# If you prefer the allow list template instead of the deny list, see community template:
|
||||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||
#
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
terraform-provider-dynu
|
||||
terraform-provider-dynu_v*
|
||||
dist/
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
@@ -17,9 +17,6 @@ coverage.*
|
||||
*.coverprofile
|
||||
profile.cov
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
# Go workspace file
|
||||
go.work
|
||||
go.work.sum
|
||||
@@ -27,10 +24,22 @@ go.work.sum
|
||||
# env file
|
||||
.env
|
||||
|
||||
# Terraform local artifacts
|
||||
.terraform/
|
||||
**/.terraform/
|
||||
*.tfvars
|
||||
!*.tfvars.example
|
||||
terraform.tfvars
|
||||
*.tfstate
|
||||
*.tfstate.*
|
||||
.terraform.lock.hcl
|
||||
|
||||
# Codex/dev artifacts
|
||||
.codex/bin/
|
||||
.codex/tmp/
|
||||
.codex/cache/
|
||||
.codex/logs/
|
||||
|
||||
# Editor/IDE
|
||||
# .idea/
|
||||
# .vscode/
|
||||
|
||||
*.tfvars
|
||||
!*.tfvars.example
|
||||
*.tfstate
|
||||
@@ -0,0 +1,33 @@
|
||||
version: 2
|
||||
project_name: terraform-provider-dynu
|
||||
|
||||
before:
|
||||
hooks:
|
||||
- go mod tidy
|
||||
- go test ./...
|
||||
|
||||
builds:
|
||||
- id: terraform-provider-dynu
|
||||
binary: terraform-provider-dynu
|
||||
main: ./main.go
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
ldflags:
|
||||
- -s -w -X main.version={{ .Version }} -X main.commit={{ .ShortCommit }} -X main.date={{ .Date }}
|
||||
goos: [linux, darwin, windows]
|
||||
goarch: [amd64, arm64]
|
||||
ignore:
|
||||
- goos: windows
|
||||
goarch: arm64
|
||||
|
||||
archives:
|
||||
- id: provider-archives
|
||||
builds: [terraform-provider-dynu]
|
||||
format: zip
|
||||
name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}'
|
||||
|
||||
checksum:
|
||||
name_template: 'terraform-provider-dynu_{{ .Version }}_SHA256SUMS'
|
||||
|
||||
changelog:
|
||||
use: git
|
||||
@@ -10,7 +10,11 @@ A standalone Terraform provider for Dynu DNS and domain management.
|
||||
- `dynu_domains`
|
||||
- `dynu_domain`
|
||||
- `dynu_dns_records`
|
||||
- Provider authentication via `api_key`.
|
||||
- Provider authentication via explicit `api_key` configuration.
|
||||
|
||||
## Important safety note
|
||||
|
||||
Deleting `dynu_domain` deletes the full Dynu DNS zone for that domain. Treat destroy plans carefully.
|
||||
|
||||
## Minimal usage example
|
||||
|
||||
@@ -31,22 +35,8 @@ variable "dynu_api_key" {
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
resource "dynu_domain" "example" {
|
||||
name = "my-test-domain.example"
|
||||
ttl = 300
|
||||
}
|
||||
|
||||
resource "dynu_dns_record" "www" {
|
||||
hostname = "www.${dynu_domain.example.name}"
|
||||
record_type = "A"
|
||||
content = "198.51.100.20"
|
||||
ttl = 300
|
||||
}
|
||||
```
|
||||
|
||||
For a live end-to-end workflow that exercises multiple record types, see `examples/live_safe_dns_record/README.md`.
|
||||
|
||||
## Resources
|
||||
|
||||
- `dynu_domain`
|
||||
@@ -58,43 +48,15 @@ For a live end-to-end workflow that exercises multiple record types, see `exampl
|
||||
- `dynu_domain`
|
||||
- `dynu_dns_records`
|
||||
|
||||
## Testing
|
||||
|
||||
Run Go unit/integration tests:
|
||||
|
||||
```bash
|
||||
go test ./...
|
||||
```
|
||||
|
||||
Run repository checks:
|
||||
|
||||
```bash
|
||||
./scripts/fix.sh
|
||||
./scripts/check.sh
|
||||
```
|
||||
|
||||
Run the live end-to-end Terraform example (opt-in, uses real Dynu account data):
|
||||
|
||||
```bash
|
||||
cd examples/live_safe_dns_record
|
||||
cp terraform.tfvars.example terraform.tfvars
|
||||
terraform validate
|
||||
terraform plan
|
||||
# terraform apply
|
||||
# terraform destroy
|
||||
```
|
||||
|
||||
## Development
|
||||
## Local development and dev overrides
|
||||
|
||||
This provider is not yet published to the Terraform Registry. Use `dev_overrides` with a local build.
|
||||
|
||||
1. Build the provider binary:
|
||||
|
||||
```bash
|
||||
go build -o terraform-provider-dynu
|
||||
```
|
||||
|
||||
2. Configure `~/.terraformrc`:
|
||||
`~/.terraformrc`:
|
||||
|
||||
```hcl
|
||||
provider_installation {
|
||||
@@ -106,7 +68,7 @@ provider_installation {
|
||||
}
|
||||
```
|
||||
|
||||
3. Validate locally without relying on registry publishing:
|
||||
Validate locally:
|
||||
|
||||
```bash
|
||||
cd examples/read_only
|
||||
@@ -115,4 +77,39 @@ terraform validate
|
||||
terraform plan
|
||||
```
|
||||
|
||||
When provider code/config changes, rebuild `terraform-provider-dynu` before re-running Terraform commands.
|
||||
If provider code/config changes, rebuild `terraform-provider-dynu` before running Terraform again.
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
./scripts/fix.sh
|
||||
./scripts/check.sh
|
||||
go test ./...
|
||||
go vet ./...
|
||||
terraform fmt -check -recursive examples
|
||||
```
|
||||
|
||||
### Optional live acceptance tests
|
||||
|
||||
Live tests are opt-in and destructive for test records. They never run by default.
|
||||
|
||||
```bash
|
||||
DYNU_ACC=1 \
|
||||
DYNU_ACC_API_KEY="***" \
|
||||
DYNU_ACC_TEST_DOMAIN="example.com" \
|
||||
./scripts/testacc.sh --live
|
||||
```
|
||||
|
||||
Use a disposable domain/subdomain only.
|
||||
|
||||
## Release
|
||||
|
||||
Build a local stamped binary:
|
||||
|
||||
```bash
|
||||
./build.sh v0.1.0
|
||||
```
|
||||
|
||||
Tagged releases (`v*`) run `.github/workflows/release.yml` with GoReleaser to produce multi-platform archives and checksums.
|
||||
|
||||
Terraform Registry signing is not configured in this repository yet.
|
||||
|
||||
@@ -1,309 +0,0 @@
|
||||
{
|
||||
"version": 4,
|
||||
"terraform_version": "1.14.9",
|
||||
"serial": 127,
|
||||
"lineage": "c95cc108-bbc0-0021-4151-832dec79ca2c",
|
||||
"outputs": {
|
||||
"record_hostnames": {
|
||||
"value": {
|
||||
"a_ipv4": "codex-a-manual.lan.ddnsgeek.com",
|
||||
"aaaa_ipv6": "codex-aaaa-manual.lan.ddnsgeek.com",
|
||||
"cname": "codex-cname-manual.lan.ddnsgeek.com",
|
||||
"dynamic_a": "codex-dynamic-a-manual.lan.ddnsgeek.com",
|
||||
"dynamic_aaaa": "codex-dynamic-aaaa-manual.lan.ddnsgeek.com"
|
||||
},
|
||||
"type": [
|
||||
"object",
|
||||
{
|
||||
"a_ipv4": "string",
|
||||
"aaaa_ipv6": "string",
|
||||
"cname": "string",
|
||||
"dynamic_a": "string",
|
||||
"dynamic_aaaa": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"record_ids": {
|
||||
"value": {
|
||||
"a_ipv4": "9695470/19453289",
|
||||
"aaaa_ipv6": "9695470/19453288",
|
||||
"cname": "9695470/19453146",
|
||||
"dynamic_a": "9695470/19453149",
|
||||
"dynamic_aaaa": "9695470/19453145"
|
||||
},
|
||||
"type": [
|
||||
"object",
|
||||
{
|
||||
"a_ipv4": "string",
|
||||
"aaaa_ipv6": "string",
|
||||
"cname": "string",
|
||||
"dynamic_a": "string",
|
||||
"dynamic_aaaa": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"record_values": {
|
||||
"value": {
|
||||
"a_ipv4": {
|
||||
"content": "192.0.2.123",
|
||||
"type": "A"
|
||||
},
|
||||
"aaaa_ipv6": {
|
||||
"content": "2001:db8::123",
|
||||
"type": "AAAA"
|
||||
},
|
||||
"cname": {
|
||||
"content": "example.com",
|
||||
"type": "CNAME"
|
||||
},
|
||||
"dynamic_a": {
|
||||
"content": null,
|
||||
"type": "A"
|
||||
},
|
||||
"dynamic_aaaa": {
|
||||
"content": null,
|
||||
"type": "AAAA"
|
||||
}
|
||||
},
|
||||
"type": [
|
||||
"object",
|
||||
{
|
||||
"a_ipv4": [
|
||||
"object",
|
||||
{
|
||||
"content": "string",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"aaaa_ipv6": [
|
||||
"object",
|
||||
{
|
||||
"content": "string",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"cname": [
|
||||
"object",
|
||||
{
|
||||
"content": "string",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"dynamic_a": [
|
||||
"object",
|
||||
{
|
||||
"content": "string",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"dynamic_aaaa": [
|
||||
"object",
|
||||
{
|
||||
"content": "string",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"mode": "managed",
|
||||
"type": "dynu_dns_record",
|
||||
"name": "a_ipv4",
|
||||
"provider": "provider[\"registry.terraform.io/dynu/dynu\"]",
|
||||
"instances": [
|
||||
{
|
||||
"schema_version": 0,
|
||||
"attributes": {
|
||||
"content": "192.0.2.123",
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"dynamic": false,
|
||||
"enabled": true,
|
||||
"group": null,
|
||||
"host": null,
|
||||
"hostname": "codex-a-manual.lan.ddnsgeek.com",
|
||||
"id": "9695470/19453289",
|
||||
"node_name": "codex-a-manual",
|
||||
"record_type": "A",
|
||||
"ttl": 300,
|
||||
"updated_on": "2026-04-29T06:17:45"
|
||||
},
|
||||
"sensitive_attributes": [],
|
||||
"identity_schema_version": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"mode": "managed",
|
||||
"type": "dynu_dns_record",
|
||||
"name": "aaaa_ipv6",
|
||||
"provider": "provider[\"registry.terraform.io/dynu/dynu\"]",
|
||||
"instances": [
|
||||
{
|
||||
"schema_version": 0,
|
||||
"attributes": {
|
||||
"content": "2001:db8::123",
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"dynamic": false,
|
||||
"enabled": true,
|
||||
"group": null,
|
||||
"host": null,
|
||||
"hostname": "codex-aaaa-manual.lan.ddnsgeek.com",
|
||||
"id": "9695470/19453288",
|
||||
"node_name": "codex-aaaa-manual",
|
||||
"record_type": "AAAA",
|
||||
"ttl": 300,
|
||||
"updated_on": "2026-04-29T06:16:25"
|
||||
},
|
||||
"sensitive_attributes": [],
|
||||
"identity_schema_version": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"mode": "managed",
|
||||
"type": "dynu_dns_record",
|
||||
"name": "cname",
|
||||
"provider": "provider[\"registry.terraform.io/dynu/dynu\"]",
|
||||
"instances": [
|
||||
{
|
||||
"schema_version": 0,
|
||||
"attributes": {
|
||||
"content": "example.com",
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"dynamic": false,
|
||||
"enabled": true,
|
||||
"group": null,
|
||||
"host": "example.com",
|
||||
"hostname": "codex-cname-manual.lan.ddnsgeek.com",
|
||||
"id": "9695470/19453146",
|
||||
"node_name": "codex-cname-manual",
|
||||
"record_type": "CNAME",
|
||||
"ttl": 300,
|
||||
"updated_on": "2026-04-29T06:16:25"
|
||||
},
|
||||
"sensitive_attributes": [],
|
||||
"identity_schema_version": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"mode": "managed",
|
||||
"type": "dynu_dns_record",
|
||||
"name": "dynamic_a",
|
||||
"provider": "provider[\"registry.terraform.io/dynu/dynu\"]",
|
||||
"instances": [
|
||||
{
|
||||
"schema_version": 0,
|
||||
"attributes": {
|
||||
"content": null,
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"dynamic": true,
|
||||
"enabled": true,
|
||||
"group": "home",
|
||||
"host": null,
|
||||
"hostname": "codex-dynamic-a-manual.lan.ddnsgeek.com",
|
||||
"id": "9695470/19453149",
|
||||
"node_name": "codex-dynamic-a-manual",
|
||||
"record_type": "A",
|
||||
"ttl": 300,
|
||||
"updated_on": "2026-04-29T06:16:27"
|
||||
},
|
||||
"sensitive_attributes": [],
|
||||
"identity_schema_version": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"mode": "managed",
|
||||
"type": "dynu_dns_record",
|
||||
"name": "dynamic_aaaa",
|
||||
"provider": "provider[\"registry.terraform.io/dynu/dynu\"]",
|
||||
"instances": [
|
||||
{
|
||||
"schema_version": 0,
|
||||
"attributes": {
|
||||
"content": null,
|
||||
"domain_id": 9695470,
|
||||
"domain_name": "lan.ddnsgeek.com",
|
||||
"dynamic": true,
|
||||
"enabled": true,
|
||||
"group": null,
|
||||
"host": null,
|
||||
"hostname": "codex-dynamic-aaaa-manual.lan.ddnsgeek.com",
|
||||
"id": "9695470/19453145",
|
||||
"node_name": "codex-dynamic-aaaa-manual",
|
||||
"record_type": "AAAA",
|
||||
"ttl": 300,
|
||||
"updated_on": "2026-04-29T06:16:25"
|
||||
},
|
||||
"sensitive_attributes": [],
|
||||
"identity_schema_version": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"check_results": [
|
||||
{
|
||||
"object_kind": "var",
|
||||
"config_addr": "var.dynu_root_domain",
|
||||
"status": "pass",
|
||||
"objects": [
|
||||
{
|
||||
"object_addr": "var.dynu_root_domain",
|
||||
"status": "pass"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"object_kind": "var",
|
||||
"config_addr": "var.test_suffix",
|
||||
"status": "pass",
|
||||
"objects": [
|
||||
{
|
||||
"object_addr": "var.test_suffix",
|
||||
"status": "pass"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"object_kind": "var",
|
||||
"config_addr": "var.test_ipv4",
|
||||
"status": "pass",
|
||||
"objects": [
|
||||
{
|
||||
"object_addr": "var.test_ipv4",
|
||||
"status": "pass"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"object_kind": "var",
|
||||
"config_addr": "var.test_ipv6",
|
||||
"status": "pass",
|
||||
"objects": [
|
||||
{
|
||||
"object_addr": "var.test_ipv6",
|
||||
"status": "pass"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"object_kind": "var",
|
||||
"config_addr": "var.test_cname_target",
|
||||
"status": "pass",
|
||||
"objects": [
|
||||
{
|
||||
"object_addr": "var.test_cname_target",
|
||||
"status": "pass"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
+9
-8
@@ -11,19 +11,20 @@ if [[ "${mode}" == "mock" ]]; then
|
||||
exec "$(dirname "$0")/test-integration.sh"
|
||||
fi
|
||||
|
||||
if [[ "${TF_ACC:-}" != "1" ]]; then
|
||||
echo "[testacc][error] live mode requires TF_ACC=1" >&2
|
||||
if [[ "${DYNU_ACC:-}" != "1" ]]; then
|
||||
echo "[testacc][error] live mode requires DYNU_ACC=1" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "${DYNU_API_KEY:-}" ]]; then
|
||||
echo "[testacc][error] live mode requires DYNU_API_KEY" >&2
|
||||
if [[ -z "${DYNU_ACC_API_KEY:-}" ]]; then
|
||||
echo "[testacc][error] live mode requires DYNU_ACC_API_KEY" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "${DYNU_DOMAIN:-}" ]]; then
|
||||
echo "[testacc][warn] DYNU_DOMAIN not set; domain-specific acceptance tests will skip"
|
||||
if [[ -z "${DYNU_ACC_TEST_DOMAIN:-}" ]]; then
|
||||
echo "[testacc][error] live mode requires DYNU_ACC_TEST_DOMAIN" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[testacc] running live acceptance tests"
|
||||
go test ./internal/provider -run '^TestAcc' -count=1 -v
|
||||
echo "[testacc] running live acceptance tests (destructive: creates/updates/deletes records)"
|
||||
TF_ACC=1 DYNU_API_KEY="${DYNU_ACC_API_KEY}" DYNU_DOMAIN="${DYNU_ACC_TEST_DOMAIN}" go test ./internal/provider -run '^TestAcc' -count=1 -v
|
||||
|
||||
Reference in New Issue
Block a user