[agent/claude] Migrate module path to dappco.re/go/core/infra. Update go.mo... #3
19 changed files with 172 additions and 53 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -1,2 +1,4 @@
|
|||
.core/
|
||||
.idea/
|
||||
.vscode/
|
||||
*.log
|
||||
.core/
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
coreerr "forge.lthn.ai/core/go-log"
|
||||
coreerr "dappco.re/go/core/log"
|
||||
)
|
||||
|
||||
// RetryConfig controls exponential backoff retry behaviour.
|
||||
|
|
|
|||
|
|
@ -137,7 +137,8 @@ func TestAPIClient_Do_Bad_ClientError(t *testing.T) {
|
|||
|
||||
err = c.Do(req, nil)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "test-api 404")
|
||||
assert.Contains(t, err.Error(), "test-api")
|
||||
assert.Contains(t, err.Error(), "HTTP 404")
|
||||
assert.Contains(t, err.Error(), "not found")
|
||||
}
|
||||
|
||||
|
|
@ -226,7 +227,8 @@ func TestAPIClient_Do_Bad_ExhaustsRetries(t *testing.T) {
|
|||
|
||||
err = c.Do(req, nil)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "exhaust-test 500")
|
||||
assert.Contains(t, err.Error(), "exhaust-test")
|
||||
assert.Contains(t, err.Error(), "HTTP 500")
|
||||
// 1 initial + 2 retries = 3 attempts
|
||||
assert.Equal(t, int32(3), attempts.Load())
|
||||
}
|
||||
|
|
@ -476,7 +478,8 @@ func TestAPIClient_DoRaw_Bad_ClientError(t *testing.T) {
|
|||
|
||||
_, err = c.DoRaw(req)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "raw-test 403")
|
||||
assert.Contains(t, err.Error(), "raw-test")
|
||||
assert.Contains(t, err.Error(), "HTTP 403")
|
||||
}
|
||||
|
||||
func TestAPIClient_DoRaw_Good_RetriesServerError(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import (
|
|||
"net/url"
|
||||
"strconv"
|
||||
|
||||
coreerr "forge.lthn.ai/core/go-log"
|
||||
coreerr "dappco.re/go/core/log"
|
||||
)
|
||||
|
||||
const cloudnsBaseURL = "https://api.cloudns.net"
|
||||
|
|
|
|||
|
|
@ -78,7 +78,8 @@ func TestCloudNSClient_DoRaw_Bad_HTTPError(t *testing.T) {
|
|||
|
||||
_, err = client.doRaw(req)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "cloudns API 403")
|
||||
assert.Contains(t, err.Error(), "cloudns API")
|
||||
assert.Contains(t, err.Error(), "HTTP 403")
|
||||
}
|
||||
|
||||
func TestCloudNSClient_DoRaw_Bad_ServerError(t *testing.T) {
|
||||
|
|
@ -102,7 +103,8 @@ func TestCloudNSClient_DoRaw_Bad_ServerError(t *testing.T) {
|
|||
|
||||
_, err = client.doRaw(req)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "cloudns API 500")
|
||||
assert.Contains(t, err.Error(), "cloudns API")
|
||||
assert.Contains(t, err.Error(), "HTTP 500")
|
||||
}
|
||||
|
||||
// --- Zone JSON parsing ---
|
||||
|
|
|
|||
|
|
@ -18,10 +18,10 @@ import (
|
|||
"slices"
|
||||
"strings"
|
||||
|
||||
"dappco.re/go/core/io"
|
||||
"dappco.re/go/core/log"
|
||||
"forge.lthn.ai/core/cli/pkg/cli"
|
||||
"forge.lthn.ai/core/go-i18n"
|
||||
"forge.lthn.ai/core/go-io"
|
||||
"forge.lthn.ai/core/go-log"
|
||||
"forge.lthn.ai/core/go-scm/repos"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ import (
|
|||
"os"
|
||||
"time"
|
||||
|
||||
"dappco.re/go/core/infra"
|
||||
coreerr "dappco.re/go/core/log"
|
||||
"forge.lthn.ai/core/cli/pkg/cli"
|
||||
coreerr "forge.lthn.ai/core/go-log"
|
||||
"forge.lthn.ai/core/go-infra"
|
||||
)
|
||||
|
||||
var dnsCmd = &cli.Command{
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ import (
|
|||
"os"
|
||||
"time"
|
||||
|
||||
"dappco.re/go/core/infra"
|
||||
coreerr "dappco.re/go/core/log"
|
||||
"forge.lthn.ai/core/cli/pkg/cli"
|
||||
coreerr "forge.lthn.ai/core/go-log"
|
||||
"forge.lthn.ai/core/go-infra"
|
||||
)
|
||||
|
||||
var lbCmd = &cli.Command{
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ import (
|
|||
"os"
|
||||
"time"
|
||||
|
||||
"dappco.re/go/core/infra"
|
||||
coreerr "dappco.re/go/core/log"
|
||||
"forge.lthn.ai/core/cli/pkg/cli"
|
||||
coreerr "forge.lthn.ai/core/go-log"
|
||||
"forge.lthn.ai/core/go-infra"
|
||||
)
|
||||
|
||||
var setupCmd = &cli.Command{
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import (
|
|||
"os/exec"
|
||||
"syscall"
|
||||
|
||||
coreerr "dappco.re/go/core/log"
|
||||
"forge.lthn.ai/core/cli/pkg/cli"
|
||||
coreerr "forge.lthn.ai/core/go-log"
|
||||
)
|
||||
|
||||
var sshCmd = &cli.Command{
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"forge.lthn.ai/core/go-ansible"
|
||||
"dappco.re/go/core/infra"
|
||||
coreerr "dappco.re/go/core/log"
|
||||
"forge.lthn.ai/core/cli/pkg/cli"
|
||||
coreerr "forge.lthn.ai/core/go-log"
|
||||
"forge.lthn.ai/core/go-infra"
|
||||
"forge.lthn.ai/core/go-ansible"
|
||||
)
|
||||
|
||||
var statusCmd = &cli.Command{
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
|
||||
coreerr "forge.lthn.ai/core/go-log"
|
||||
coreio "forge.lthn.ai/core/go-io"
|
||||
coreio "dappco.re/go/core/io"
|
||||
coreerr "dappco.re/go/core/log"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -5,14 +5,14 @@ description: Infrastructure provider API clients and YAML-based configuration fo
|
|||
|
||||
# go-infra
|
||||
|
||||
`forge.lthn.ai/core/go-infra` provides typed Go clients for infrastructure provider APIs (Hetzner Cloud, Hetzner Robot, CloudNS) and a declarative YAML configuration layer for describing production topology. It also ships CLI commands for production management (`core prod`) and security monitoring (`core monitor`).
|
||||
`dappco.re/go/core/infra` provides typed Go clients for infrastructure provider APIs (Hetzner Cloud, Hetzner Robot, CloudNS) and a declarative YAML configuration layer for describing production topology. It also ships CLI commands for production management (`core prod`) and security monitoring (`core monitor`).
|
||||
|
||||
The library has no framework dependencies beyond the Go standard library, YAML parsing, and testify for tests. All HTTP communication goes through a shared `APIClient` that handles retries, exponential backoff, and rate-limit compliance automatically.
|
||||
|
||||
## Module Path
|
||||
|
||||
```
|
||||
forge.lthn.ai/core/go-infra
|
||||
dappco.re/go/core/infra
|
||||
```
|
||||
|
||||
Requires **Go 1.26+**.
|
||||
|
|
@ -22,7 +22,7 @@ Requires **Go 1.26+**.
|
|||
### Using the API Clients Directly
|
||||
|
||||
```go
|
||||
import "forge.lthn.ai/core/go-infra"
|
||||
import "dappco.re/go/core/infra"
|
||||
|
||||
// Hetzner Cloud -- list all servers
|
||||
hc := infra.NewHCloudClient(os.Getenv("HCLOUD_TOKEN"))
|
||||
|
|
@ -40,7 +40,7 @@ changed, err := dns.EnsureRecord(ctx, "example.com", "www", "A", "1.2.3.4", 300)
|
|||
### Loading Infrastructure Configuration
|
||||
|
||||
```go
|
||||
import "forge.lthn.ai/core/go-infra"
|
||||
import "dappco.re/go/core/infra"
|
||||
|
||||
// Auto-discover infra.yaml by walking up from the current directory
|
||||
cfg, path, err := infra.Discover(".")
|
||||
|
|
|
|||
20
go.mod
20
go.mod
|
|
@ -1,21 +1,23 @@
|
|||
module forge.lthn.ai/core/go-infra
|
||||
module dappco.re/go/core/infra
|
||||
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
forge.lthn.ai/core/cli v0.3.5
|
||||
forge.lthn.ai/core/go-ansible v0.1.4
|
||||
forge.lthn.ai/core/go-i18n v0.1.5
|
||||
forge.lthn.ai/core/go-io v0.1.5
|
||||
forge.lthn.ai/core/go-log v0.0.4
|
||||
forge.lthn.ai/core/go-scm v0.3.4
|
||||
dappco.re/go/core/io v0.2.0
|
||||
dappco.re/go/core/log v0.1.0
|
||||
forge.lthn.ai/core/cli v0.3.7
|
||||
forge.lthn.ai/core/go-ansible v0.1.6
|
||||
forge.lthn.ai/core/go-i18n v0.1.7
|
||||
forge.lthn.ai/core/go-scm v0.3.6
|
||||
github.com/stretchr/testify v1.11.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
forge.lthn.ai/core/go v0.3.1 // indirect
|
||||
forge.lthn.ai/core/go-inference v0.1.4 // indirect
|
||||
forge.lthn.ai/core/go v0.3.3 // indirect
|
||||
forge.lthn.ai/core/go-inference v0.1.6 // indirect
|
||||
forge.lthn.ai/core/go-io v0.1.7 // indirect
|
||||
forge.lthn.ai/core/go-log v0.0.4 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/charmbracelet/bubbletea v1.3.10 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.4.3 // indirect
|
||||
|
|
|
|||
32
go.sum
32
go.sum
|
|
@ -1,19 +1,23 @@
|
|||
forge.lthn.ai/core/cli v0.3.5 h1:P7yK0DmSA1QnUMFuCjJZf/fk/akKPIxopQ6OwD8Sar8=
|
||||
forge.lthn.ai/core/cli v0.3.5/go.mod h1:SeArHx+hbpX5iZqgASCD7Q1EDoc6uaaGiGBotmNzIx4=
|
||||
forge.lthn.ai/core/go v0.3.1 h1:5FMTsUhLcxSr07F9q3uG0Goy4zq4eLivoqi8shSY4UM=
|
||||
forge.lthn.ai/core/go v0.3.1/go.mod h1:gE6c8h+PJ2287qNhVUJ5SOe1kopEwHEquvinstpuyJc=
|
||||
forge.lthn.ai/core/go-ansible v0.1.4 h1:kja8xFADQ97c0X1RqCTHrYG8SZwfCtP33Ms48bJDmmI=
|
||||
forge.lthn.ai/core/go-ansible v0.1.4/go.mod h1:TV7eGhm8z86iihVPpn5I21jzBZ3UoR/TRva6tNOCRoU=
|
||||
forge.lthn.ai/core/go-i18n v0.1.5 h1:B4hV4eTl63akZiplM8lswuttctrcSOCWyFSGBZmu6Nc=
|
||||
forge.lthn.ai/core/go-i18n v0.1.5/go.mod h1:hJsUxmqdPly73i3VkTDxvmbrpjxSd65hQVQqWA3+fnM=
|
||||
forge.lthn.ai/core/go-inference v0.1.4 h1:fuAgWbqsEDajHniqAKyvHYbRcBrkGEiGSqR2pfTMRY0=
|
||||
forge.lthn.ai/core/go-inference v0.1.4/go.mod h1:jfWz+IJX55wAH98+ic6FEqqGB6/P31CHlg7VY7pxREw=
|
||||
forge.lthn.ai/core/go-io v0.1.5 h1:+XJ1YhaGGFLGtcNbPtVlndTjk+pO0Ydi2hRDj5/cHOM=
|
||||
forge.lthn.ai/core/go-io v0.1.5/go.mod h1:FRtXSsi8W+U9vewCU+LBAqqbIj3wjXA4dBdSv3SAtWI=
|
||||
dappco.re/go/core/io v0.2.0 h1:zuudgIiTsQQ5ipVt97saWdGLROovbEB/zdVyy9/l+I4=
|
||||
dappco.re/go/core/io v0.2.0/go.mod h1:1QnQV6X9LNgFKfm8SkOtR9LLaj3bDcsOIeJOOyjbL5E=
|
||||
dappco.re/go/core/log v0.1.0 h1:pa71Vq2TD2aoEUQWFKwNcaJ3GBY8HbaNGqtE688Unyc=
|
||||
dappco.re/go/core/log v0.1.0/go.mod h1:Nkqb8gsXhZAO8VLpx7B8i1iAmohhzqA20b9Zr8VUcJs=
|
||||
forge.lthn.ai/core/cli v0.3.7 h1:1GrbaGg0wDGHr6+klSbbGyN/9sSbHvFbdySJznymhwg=
|
||||
forge.lthn.ai/core/cli v0.3.7/go.mod h1:DBUppJkA9P45ZFGgI2B8VXw1rAZxamHoI/KG7fRvTNs=
|
||||
forge.lthn.ai/core/go v0.3.3 h1:kYYZ2nRYy0/Be3cyuLJspRjLqTMxpckVyhb/7Sw2gd0=
|
||||
forge.lthn.ai/core/go v0.3.3/go.mod h1:Cp4ac25pghvO2iqOu59t1GyngTKVOzKB5/VPdhRi9CQ=
|
||||
forge.lthn.ai/core/go-ansible v0.1.6 h1:jTeW26Gqa4mhv+4newyrfwfg6TY/17nG9yWymZdBF1Y=
|
||||
forge.lthn.ai/core/go-ansible v0.1.6/go.mod h1:RcthHm/ouMCZLYW3R4LgVWASXvg2Ndc408GX5TxqDZE=
|
||||
forge.lthn.ai/core/go-i18n v0.1.7 h1:aHkAoc3W8fw3RPNvw/UszQbjyFWXHszzbZgty3SwyAA=
|
||||
forge.lthn.ai/core/go-i18n v0.1.7/go.mod h1:0VDjwtY99NSj2iqwrI09h5GUsJeM9s48MLkr+/Dn4G8=
|
||||
forge.lthn.ai/core/go-inference v0.1.6 h1:ce42zC0zO8PuISUyAukAN1NACEdWp5wF1mRgnh5+58E=
|
||||
forge.lthn.ai/core/go-inference v0.1.6/go.mod h1:jfWz+IJX55wAH98+ic6FEqqGB6/P31CHlg7VY7pxREw=
|
||||
forge.lthn.ai/core/go-io v0.1.7 h1:Tdb6sqh+zz1lsGJaNX9RFWM6MJ/RhSAyxfulLXrJsbk=
|
||||
forge.lthn.ai/core/go-io v0.1.7/go.mod h1:8lRLFk4Dnp5cR/Cyzh9WclD5566TbpdRgwcH7UZLWn4=
|
||||
forge.lthn.ai/core/go-log v0.0.4 h1:KTuCEPgFmuM8KJfnyQ8vPOU1Jg654W74h8IJvfQMfv0=
|
||||
forge.lthn.ai/core/go-log v0.0.4/go.mod h1:r14MXKOD3LF/sI8XUJQhRk/SZHBE7jAFVuCfgkXoZPw=
|
||||
forge.lthn.ai/core/go-scm v0.3.4 h1:McZvp2gI3wEPCF/jim8O4F1+Vp477N81TUiiklTq5hw=
|
||||
forge.lthn.ai/core/go-scm v0.3.4/go.mod h1:AOrx4CEmV8/Q73Cvd2LkbFniYGpk46mticpYmK5MnJA=
|
||||
forge.lthn.ai/core/go-scm v0.3.6 h1:LFNx8Fs82mrpxro/MPUM6tMiD4DqPmdu83UknXztQjc=
|
||||
forge.lthn.ai/core/go-scm v0.3.6/go.mod h1:IWFIYDfRH0mtRdqY5zV06l/RkmkPpBM6FcbKWhg1Qa8=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import (
|
|||
"net/http"
|
||||
"strings"
|
||||
|
||||
coreerr "forge.lthn.ai/core/go-log"
|
||||
coreerr "dappco.re/go/core/log"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
|||
|
|
@ -116,7 +116,8 @@ func TestHCloudClient_Do_Bad_APIError(t *testing.T) {
|
|||
var result struct{}
|
||||
err := client.get(context.Background(), "/servers", &result)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "hcloud API 403")
|
||||
assert.Contains(t, err.Error(), "hcloud API")
|
||||
assert.Contains(t, err.Error(), "HTTP 403")
|
||||
}
|
||||
|
||||
func TestHCloudClient_Do_Bad_APIErrorNoJSON(t *testing.T) {
|
||||
|
|
@ -136,7 +137,8 @@ func TestHCloudClient_Do_Bad_APIErrorNoJSON(t *testing.T) {
|
|||
|
||||
err := client.get(context.Background(), "/servers", nil)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "hcloud API 500")
|
||||
assert.Contains(t, err.Error(), "hcloud API")
|
||||
assert.Contains(t, err.Error(), "HTTP 500")
|
||||
}
|
||||
|
||||
func TestHCloudClient_Do_Good_NilResult(t *testing.T) {
|
||||
|
|
@ -218,7 +220,8 @@ func TestHRobotClient_Get_Bad_HTTPError(t *testing.T) {
|
|||
|
||||
err := client.get(context.Background(), "/server", nil)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "hrobot API 401")
|
||||
assert.Contains(t, err.Error(), "hrobot API")
|
||||
assert.Contains(t, err.Error(), "HTTP 401")
|
||||
}
|
||||
|
||||
// --- Type serialisation ---
|
||||
|
|
|
|||
45
kb/API-Clients.md
Normal file
45
kb/API-Clients.md
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# API Clients
|
||||
|
||||
Module: `dappco.re/go/core/infra`
|
||||
|
||||
## CloudNS Client
|
||||
|
||||
`NewCloudNSClient(authID, password)` creates a client for the CloudNS DNS API (`api.cloudns.net`).
|
||||
|
||||
### DNS Record Operations
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `ListZones(ctx)` | Returns all DNS zones |
|
||||
| `ListRecords(ctx, domain)` | Returns all records for a zone |
|
||||
| `CreateRecord(ctx, domain, host, type, value, ttl)` | Creates a record, returns ID |
|
||||
| `UpdateRecord(ctx, domain, id, host, type, value, ttl)` | Updates an existing record |
|
||||
| `DeleteRecord(ctx, domain, id)` | Deletes a record by ID |
|
||||
| `EnsureRecord(ctx, domain, host, type, value, ttl)` | Idempotent create-or-update, returns `(changed bool, err)` |
|
||||
|
||||
### ACME Helpers
|
||||
|
||||
- `SetACMEChallenge(ctx, domain, value)` — Creates `_acme-challenge` TXT record (TTL 60s)
|
||||
- `ClearACMEChallenge(ctx, domain)` — Removes all `_acme-challenge` TXT records
|
||||
|
||||
## Hetzner Cloud Client
|
||||
|
||||
`NewHCloudClient(token)` creates a client for the Hetzner Cloud API (`api.hetzner.cloud/v1`). Uses Bearer token auth.
|
||||
|
||||
### Types
|
||||
|
||||
- `HCloudServer` — ID, Name, Status, PublicNet (IPv4), PrivateNet, ServerType, Datacenter, Labels
|
||||
- `HCloudLoadBalancer` — ID, Name, PublicNet, Algorithm, Services, Targets, Location, Labels
|
||||
- `HCloudLBCreateRequest` — Creation parameters for load balancers
|
||||
|
||||
## Hetzner Robot Client
|
||||
|
||||
`NewHRobotClient(user, password)` creates a client for dedicated server management (`robot-ws.your-server.de`). Uses Basic auth.
|
||||
|
||||
- `HRobotServer` — ServerIP, ServerName, Product, Datacenter, Status, PaidUntil
|
||||
|
||||
## Infrastructure Config
|
||||
|
||||
`Load(path)` reads `infra.yaml`. `Discover(startDir)` walks up directories looking for `infra.yaml`.
|
||||
|
||||
Helper: `cfg.HostsByRole(role)` filters hosts, `cfg.AppServers()` returns hosts with role "app".
|
||||
58
kb/Home.md
Normal file
58
kb/Home.md
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
# go-infra
|
||||
|
||||
Module: `dappco.re/go/core/infra`
|
||||
|
||||
Infrastructure management for the Host UK production environment. Provides API clients for CloudNS DNS, Hetzner Cloud, and Hetzner Robot, plus a declarative infrastructure configuration model loaded from `infra.yaml`.
|
||||
|
||||
## Architecture
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `config.go` | `Config` struct and `Load()`/`Discover()` for `infra.yaml` |
|
||||
| `client.go` | `APIClient` — shared HTTP client with auth, retries, JSON handling |
|
||||
| `cloudns.go` | `CloudNSClient` — DNS record management via CloudNS API |
|
||||
| `hetzner.go` | `HCloudClient` (Cloud API) + `HRobotClient` (Robot API) |
|
||||
|
||||
CLI commands in `cmd/prod/` (dns, lb, setup, ssh, status) and `cmd/monitor/`.
|
||||
|
||||
## Key Types
|
||||
|
||||
### Infrastructure Config
|
||||
|
||||
- **`Config`** — Top-level config: `Hosts`, `LoadBalancer`, `Network`, `DNS`, `SSL`, `Database`, `Cache`, `Containers`, `S3`, `CDN`, `CICD`, `Monitoring`, `Backups`
|
||||
- **`Host`** — Server: `FQDN`, `IP`, `PrivateIP`, `Type` (hcloud/hrobot), `Role` (bastion/app/builder), `SSH`, `Services`
|
||||
- **`LoadBalancer`** — LB config with backends, health checks, listeners, SSL
|
||||
- **`DNS`** — Provider config with zones and records
|
||||
- **`Database`** — Cluster config (engine, version, nodes, backup)
|
||||
|
||||
### API Clients
|
||||
|
||||
- **`CloudNSClient`** — DNS operations: `ListZones()`, `ListRecords()`, `CreateRecord()`, `UpdateRecord()`, `DeleteRecord()`, `EnsureRecord()`, `SetACMEChallenge()`, `ClearACMEChallenge()`
|
||||
- **`HCloudClient`** — Hetzner Cloud: `ListServers()`, `ListLoadBalancers()`, `GetLoadBalancer()`, `CreateLoadBalancer()`, `DeleteLoadBalancer()`, `CreateSnapshot()`
|
||||
- **`HRobotClient`** — Hetzner Robot (dedicated): `ListServers()`, `GetServer()`
|
||||
- **`APIClient`** — Shared HTTP client with configurable auth and error handling
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
import "dappco.re/go/core/infra"
|
||||
|
||||
// Load infrastructure config
|
||||
cfg, path, _ := infra.Discover(".")
|
||||
|
||||
// Get app servers
|
||||
appServers := cfg.AppServers()
|
||||
|
||||
// DNS management
|
||||
dns := infra.NewCloudNSClient(authID, password)
|
||||
changed, _ := dns.EnsureRecord(ctx, "example.com", "www", "A", "1.2.3.4", 300)
|
||||
|
||||
// Hetzner Cloud
|
||||
hcloud := infra.NewHCloudClient(token)
|
||||
servers, _ := hcloud.ListServers(ctx)
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `gopkg.in/yaml.v3` — YAML config parsing
|
||||
- No core ecosystem dependencies (standalone)
|
||||
Loading…
Add table
Reference in a new issue