Skip to content

Commit 61619c9

Browse files
committed
Release v0.1.1: extensible provider system, docs, and tests
1 parent 5ac22e7 commit 61619c9

File tree

19 files changed

+902
-572
lines changed

19 files changed

+902
-572
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ CONFIG_SRC=configs/dynago.yml
44
CONFIG_DST=/etc/dynago/dynago.yml
55
SYSTEMD_UNIT=/etc/systemd/system/dynago.service
66

7-
VERSION := 0.1.0
7+
VERSION := 0.1.1
88
BUILD_TIME := $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
99
GIT_COMMIT := $(shell git rev-parse --short HEAD)
1010

README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,44 @@ providers:
145145
- Set `enabled: true` for the provider(s) you want to use.
146146
- For Cloudflare, set `proxied: true` to enable the orange cloud (proxy).
147147

148+
## Provider Configuration
149+
150+
Each provider’s configuration is defined by that provider’s Go package. The main config file’s `providers:` section is a map, and each provider receives its own sub-map at runtime.
151+
152+
For example, the Cloudflare provider expects:
153+
154+
```yaml
155+
providers:
156+
cloudflare:
157+
enabled: true
158+
api_token: "your-cloudflare-api-token"
159+
zone_id: "your-zone-id"
160+
record_name: "home.example.com"
161+
record_type: "A"
162+
proxied: true
163+
```
164+
165+
The Route53 provider expects:
166+
167+
```yaml
168+
providers:
169+
route53:
170+
enabled: false
171+
access_key_id: "AWS_ACCESS_KEY_ID"
172+
secret_access_key: "AWS_SECRET_ACCESS_KEY"
173+
hosted_zone_id: "Z1D633PJN98FT9"
174+
record_name: "home.example.com"
175+
record_type: "A"
176+
region: "us-east-1"
177+
```
178+
179+
**To add a new provider:**
180+
- Implement the `DNSProvider` interface in your own package.
181+
- Define your own config struct and document the expected YAML.
182+
- Unmarshal the config using the provided `config.ConfigFromMap` helper.
183+
184+
---
185+
148186
## Advanced
149187

150188
- **Run manually:**

cmd/dynago/main.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,21 @@
1+
// dynago - Dynamic DNS updater for Cloudflare, Route 53, and more.
2+
// Copyright (C) 2025 Aaron Mathis <[email protected]>
3+
//
4+
// This file is part of dynago.
5+
//
6+
// dynago is free software: you can redistribute it and/or modify
7+
// it under the terms of the GNU General Public License as published by
8+
// the Free Software Foundation, either version 3 of the License, or
9+
// (at your option) any later version.
10+
//
11+
// dynago is distributed in the hope that it will be useful,
12+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
// GNU General Public License for more details.
15+
//
16+
// You should have received a copy of the GNU General Public License
17+
// along with dynago. If not, see <https://www.gnu.org/licenses/>.
18+
119
// Command dynago is the entry point for the dynago dynamic DNS updater application.
220
//
321
// This command-line tool loads configuration, initializes logging, and starts the DNS update service.

docs/internal.md

Lines changed: 6 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -233,164 +233,14 @@ args: Arguments for the format string.
233233

234234
# provider
235235

236-
```go
237-
import "github.com/aaronlmathis/dynago/internal/provider"
238-
```
239-
240-
Package provider implements DNS provider interfaces and concrete implementations for dynago.
241-
242-
This package supports multiple DNS providers \(Cloudflare, Route53\) and provides a registry for enabled providers based on application configuration. Each provider implements the DNSProvider interface, which allows for querying and updating DNS records.
243-
244-
## Index
245-
246-
- [type CloudflareProvider](<#CloudflareProvider>)
247-
- [func \(c \*CloudflareProvider\) GetRecordIP\(\) \(string, error\)](<#CloudflareProvider.GetRecordIP>)
248-
- [func \(c \*CloudflareProvider\) ProviderName\(\) string](<#CloudflareProvider.ProviderName>)
249-
- [func \(c \*CloudflareProvider\) UpdateRecordIP\(ip string\) error](<#CloudflareProvider.UpdateRecordIP>)
250-
- [type DNSProvider](<#DNSProvider>)
251-
- [type DNSProviderRegistry](<#DNSProviderRegistry>)
252-
- [func NewDNSProviderRegistry\(cfg \*config.Config\) \(\*DNSProviderRegistry, error\)](<#NewDNSProviderRegistry>)
253-
- [type Route53Provider](<#Route53Provider>)
254-
- [func \(r \*Route53Provider\) GetRecordIP\(\) \(string, error\)](<#Route53Provider.GetRecordIP>)
255-
- [func \(r \*Route53Provider\) ProviderName\(\) string](<#Route53Provider.ProviderName>)
256-
- [func \(r \*Route53Provider\) UpdateRecordIP\(ip string\) error](<#Route53Provider.UpdateRecordIP>)
257-
258-
259-
<a name="CloudflareProvider"></a>
260-
## type CloudflareProvider
261-
262-
CloudflareProvider implements DNSProvider for Cloudflare.
263-
264-
It uses the Cloudflare Go SDK to query and update DNS records in a specified zone.
265-
266-
```go
267-
type CloudflareProvider struct {
268-
// contains filtered or unexported fields
269-
}
270-
```
271-
272-
<a name="CloudflareProvider.GetRecordIP"></a>
273-
### func \(\*CloudflareProvider\) GetRecordIP
274-
275-
```go
276-
func (c *CloudflareProvider) GetRecordIP() (string, error)
277-
```
278-
279-
GetRecordIP fetches the current IP address for the Cloudflare DNS record.
280-
281-
Returns the IP address as a string, or an error if the record is not found or the API call fails.
282-
283-
<a name="CloudflareProvider.ProviderName"></a>
284-
### func \(\*CloudflareProvider\) ProviderName
285-
286-
```go
287-
func (c *CloudflareProvider) ProviderName() string
288-
```
289-
290-
ProviderName returns the string "cloudflare" for Cloudflare providers.
291-
292-
<a name="CloudflareProvider.UpdateRecordIP"></a>
293-
### func \(\*CloudflareProvider\) UpdateRecordIP
294-
295-
```go
296-
func (c *CloudflareProvider) UpdateRecordIP(ip string) error
297-
```
298-
299-
UpdateRecordIP updates the Cloudflare DNS record to the given IP address.
300-
301-
ip: The new IP address to set in the DNS record. The proxied status is set according to config.
302-
303-
Returns an error if the update fails or the record is not found.
304-
305-
<a name="DNSProvider"></a>
306-
## type DNSProvider
307-
308-
DNSProvider defines the interface for DNS providers.
309-
310-
Implementations must provide methods to get and update the DNS record IP.
311-
312-
```go
313-
type DNSProvider interface {
314-
// GetRecordIP returns the current IP address configured in the DNS record.
315-
GetRecordIP() (string, error)
316-
// UpdateRecordIP updates the DNS record to the given IP address.
317-
UpdateRecordIP(ip string) error
318-
// ProviderName returns the name of the provider (e.g., "cloudflare", "route53").
319-
ProviderName() string
320-
}
321-
```
322-
323-
<a name="DNSProviderRegistry"></a>
324-
## type DNSProviderRegistry
325-
326-
DNSProviderRegistry holds all enabled DNS providers.
327-
328-
Providers is a slice of DNSProvider implementations that are enabled in the config.
329-
330-
```go
331-
type DNSProviderRegistry struct {
332-
Providers []DNSProvider
333-
}
334-
```
335-
336-
<a name="NewDNSProviderRegistry"></a>
337-
### func NewDNSProviderRegistry
338-
339-
```go
340-
func NewDNSProviderRegistry(cfg *config.Config) (*DNSProviderRegistry, error)
341-
```
342-
343-
NewDNSProviderRegistry creates a registry of enabled DNS providers based on config.
344-
345-
cfg: The loaded application configuration.
346-
347-
Returns a DNSProviderRegistry with all enabled providers, or an error if none are enabled.
348-
349-
<a name="Route53Provider"></a>
350-
## type Route53Provider
351-
352-
Route53Provider implements DNSProvider for AWS Route53.
353-
354-
It uses the AWS SDK to query and update DNS records in a specified hosted zone.
355-
356-
```go
357-
type Route53Provider struct {
358-
// contains filtered or unexported fields
359-
}
360-
```
361-
362-
<a name="Route53Provider.GetRecordIP"></a>
363-
### func \(\*Route53Provider\) GetRecordIP
364-
365-
```go
366-
func (r *Route53Provider) GetRecordIP() (string, error)
367-
```
368-
369-
GetRecordIP fetches the current IP address for the Route53 DNS record.
370-
371-
Returns the IP address as a string, or an error if the record is not found or the API call fails.
372-
373-
<a name="Route53Provider.ProviderName"></a>
374-
### func \(\*Route53Provider\) ProviderName
375-
376-
```go
377-
func (r *Route53Provider) ProviderName() string
378-
```
379-
380-
ProviderName returns the string "route53" for AWS Route53 providers.
381-
382-
<a name="Route53Provider.UpdateRecordIP"></a>
383-
### func \(\*Route53Provider\) UpdateRecordIP
384-
385-
```go
386-
func (r *Route53Provider) UpdateRecordIP(ip string) error
387-
```
388-
389-
UpdateRecordIP updates the Route53 DNS record to the given IP address.
236+
The provider system has been refactored and is now public and extensible. Provider interfaces, registry, and implementations are no longer in `internal/provider` but in the top-level `providers/` directory.
390237

391-
ip: The new IP address to set in the DNS record.
238+
- See [docs/providers.md](providers.md) for full documentation on the provider system, interface, and how to add new providers.
239+
- Each provider (e.g., Cloudflare, Route53) is implemented in its own package under `providers/`.
240+
- The provider registry and interface are now public in `providers/provider.go`.
241+
- Provider configuration is now loaded as a `map[string]any` and passed to each provider's `New` function for parsing.
392242

393-
Returns an error if the update fails.
243+
Legacy types and functions in `internal/provider` have been removed or replaced. Please refer to the new provider documentation for up-to-date API and usage.
394244

395245
# service
396246

docs/providers.md

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# dynago Providers System
2+
3+
This document describes the provider system in dynago, how to configure providers, and how to add new providers.
4+
5+
---
6+
7+
## Overview
8+
9+
dynago supports multiple DNS providers (e.g., Cloudflare, AWS Route53) for dynamic DNS updates. The provider system is designed to be extensible: each provider is implemented as a separate Go package under `providers/`, and new providers can be added easily by contributors.
10+
11+
- The provider interface and registry are public and live in `providers/provider.go`.
12+
- Each provider (e.g., Cloudflare, Route53) is implemented in its own subpackage (e.g., `providers/cloudflare/`).
13+
- Each provider defines and unmarshals its own config struct.
14+
- Provider configs are loaded as generic maps and passed to the provider's `New` function for parsing.
15+
16+
---
17+
18+
## Provider Interface
19+
20+
The core interface for all providers is defined in `providers/provider.go`:
21+
22+
```go
23+
// DNSProvider is the interface all DNS providers must implement.
24+
type DNSProvider interface {
25+
Name() string
26+
Enabled() bool
27+
Update(recordName, recordType, ip string) error
28+
}
29+
```
30+
31+
Providers must also register themselves using the registry in `provider.go`:
32+
33+
```go
34+
// RegisterProvider registers a provider constructor by name.
35+
func RegisterProvider(name string, constructor ProviderConstructor)
36+
```
37+
38+
---
39+
40+
## Adding a New Provider
41+
42+
1. **Create a new package:**
43+
- Add a new folder under `providers/` (e.g., `providers/example/`).
44+
- Implement a `Config` struct for your provider's settings.
45+
- Implement the `DNSProvider` interface.
46+
- Register your provider in an `init()` function.
47+
48+
2. **Example skeleton:**
49+
50+
```go
51+
// providers/example/example.go
52+
package example
53+
54+
type Config struct {
55+
Enabled bool `yaml:"enabled"`
56+
ApiKey string `yaml:"api_key"`
57+
// ...other fields...
58+
}
59+
60+
type ExampleProvider struct {
61+
cfg Config
62+
}
63+
64+
func (p *ExampleProvider) Name() string { return "example" }
65+
func (p *ExampleProvider) Enabled() bool { return p.cfg.Enabled }
66+
func (p *ExampleProvider) Update(recordName, recordType, ip string) error {
67+
// ...implementation...
68+
return nil
69+
}
70+
71+
func New(config map[string]any) (providers.DNSProvider, error) {
72+
var cfg Config
73+
// Unmarshal config map to struct (see ConfigFromMap helper)
74+
if err := providers.ConfigFromMap(config, &cfg); err != nil {
75+
return nil, err
76+
}
77+
return &ExampleProvider{cfg: cfg}, nil
78+
}
79+
80+
func init() {
81+
providers.RegisterProvider("example", New)
82+
}
83+
```
84+
85+
3. **Write tests:**
86+
- Add a `example_test.go` file with unit tests for your provider.
87+
88+
---
89+
90+
## Provider Configuration in YAML
91+
92+
Provider configs are specified under the `providers:` key in your YAML config file. Each provider's config is a map of options, passed directly to the provider's `New` function.
93+
94+
Example:
95+
96+
```yaml
97+
providers:
98+
cloudflare:
99+
enabled: true
100+
api_token: "your-cloudflare-api-token"
101+
zone_id: "your-zone-id"
102+
record_name: "home.example.com"
103+
record_type: "A"
104+
proxied: true
105+
route53:
106+
enabled: false
107+
access_key_id: "AWS_ACCESS_KEY_ID"
108+
secret_access_key: "AWS_SECRET_ACCESS_KEY"
109+
hosted_zone_id: "Z1D633PJN98FT9"
110+
record_name: "home.example.com"
111+
record_type: "A"
112+
region: "us-east-1"
113+
```
114+
115+
- Each provider can define its own config fields.
116+
- Only providers with `enabled: true` will be used.
117+
118+
---
119+
120+
## Provider Registry and Discovery
121+
122+
- The registry in `provider.go` allows dynago to discover and instantiate all registered providers at runtime.
123+
- Providers are enabled/disabled via config.
124+
- The registry makes it easy to add new providers without modifying core code.
125+
126+
---
127+
128+
## See Also
129+
- Example provider implementations: `providers/cloudflare/`, `providers/route53/`
130+
- Main configuration documentation: [README.md](README.md)

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ module github.com/aaronlmathis/dynago
22

33
go 1.24.3
44

5+
replace github.com/aaronlmathis/dynago/providers => ./providers/
56

67
require (
78
github.com/aws/aws-sdk-go-v2 v1.36.3

0 commit comments

Comments
 (0)