1
0
Fork 0
This commit is contained in:
Henrik Hautakoski 2025-10-13 16:58:42 +02:00
parent 49563af412
commit 555c553686
18 changed files with 481 additions and 742 deletions

View file

@ -0,0 +1,109 @@
package digitalocean
import (
"context"
"errors"
"testing"
"github.com/digitalocean/godo"
"github.com/stretchr/testify/assert"
)
type mock struct {
t *testing.T
records_by_type map[string][]godo.DomainRecord
edit_record_request *godo.DomainRecordEditRequest
edit_record_error error
}
func (m mock) List(context.Context, *godo.ListOptions) ([]godo.Domain, *godo.Response, error) {
m.t.Error("List called when it should not have been")
return nil, nil, nil
}
func (m mock) Get(context.Context, string) (*godo.Domain, *godo.Response, error) {
m.t.Error("Get called when it should not have been")
return nil, nil, nil
}
func (m mock) Create(context.Context, *godo.DomainCreateRequest) (*godo.Domain, *godo.Response, error) {
m.t.Error("Create called when it should not have been")
return nil, nil, nil
}
func (m mock) Delete(context.Context, string) (*godo.Response, error) {
m.t.Error("Delete called when it should not have been")
return nil, nil
}
func (m mock) Records(context.Context, string, *godo.ListOptions) ([]godo.DomainRecord, *godo.Response, error) {
m.t.Error("Records called when it should not have been")
return nil, nil, nil
}
func (m mock) RecordsByType(_ context.Context, name string, t string, opt *godo.ListOptions) ([]godo.DomainRecord, *godo.Response, error) {
var err error
// Only care about "A" records
assert.Equal(m.t, "A", t)
r, ok := m.records_by_type[name]
if !ok {
err = errors.New("Record not found")
}
return r, nil, err
}
func (m mock) RecordsByName(context.Context, string, string, *godo.ListOptions) ([]godo.DomainRecord, *godo.Response, error) {
m.t.Error("RecordsByName called when it should not have been")
return nil, nil, nil
}
func (m mock) RecordsByTypeAndName(context.Context, string, string, string, *godo.ListOptions) ([]godo.DomainRecord, *godo.Response, error) {
m.t.Error("RecordsByTypeAndName called when it should not have been")
return nil, nil, nil
}
func (m mock) Record(context.Context, string, int) (*godo.DomainRecord, *godo.Response, error) {
m.t.Error("Record called when it should not have been")
return nil, nil, nil
}
func (m mock) DeleteRecord(context.Context, string, int) (*godo.Response, error) {
m.t.Error("DeleteRecord called when it should not have been")
return nil, nil
}
func (m mock) EditRecord(_ context.Context, domain string, id int, req *godo.DomainRecordEditRequest) (*godo.DomainRecord, *godo.Response, error) {
if m.edit_record_request == nil {
m.t.Error("EditRecord called with empty request")
}
if m.edit_record_error != nil {
return nil, nil, m.edit_record_error
}
assert.Equal(m.t, m.edit_record_request, req)
record := godo.DomainRecord{
ID: id,
Type: req.Type,
Name: req.Name,
Data: req.Data,
Priority: req.Priority,
Port: req.Port,
TTL: req.TTL,
Weight: req.Weight,
Flags: req.Flags,
Tag: req.Tag,
}
return &record, nil, nil
}
func (m mock) CreateRecord(context.Context, string, *godo.DomainRecordEditRequest) (*godo.DomainRecord, *godo.Response, error) {
m.t.Error("CreateRecord called when it should not have been")
return nil, nil, nil
}

View file

@ -0,0 +1,67 @@
package digitalocean
import (
"context"
"errors"
"net"
"strconv"
"dnsupdater/dns"
"github.com/digitalocean/godo"
)
type Service struct {
api godo.DomainsService
}
func New(token string) Service {
return Service{
api: godo.NewFromToken(token).Domains,
}
}
func Factory(args map[string]any) (any, error) {
t, ok := args["token"]
if !ok {
return nil, errors.New("did not find token")
}
token, ok := t.(string)
if !ok {
return nil, errors.New("token must be a string")
}
return New(token), nil
}
func (d Service) List(domain_name string) (dns.RecordList, error) {
fetchedRecords, _, err := d.api.RecordsByType(context.Background(), domain_name, "A", &godo.ListOptions{
PerPage: 50,
})
if err != nil {
return nil, err
}
records := dns.RecordList{}
for _, rec := range fetchedRecords {
records.Add(dns.Record{
Id: strconv.Itoa(rec.ID),
Name: rec.Name,
Ip: net.ParseIP(rec.Data),
})
}
return records, nil
}
func (d Service) Update(domain, recordID, ip string) error {
id, err := strconv.Atoi(recordID)
if err != nil {
return err
}
_, _, err = d.api.EditRecord(context.Background(), domain, id, &godo.DomainRecordEditRequest{
Data: ip,
})
return err
}

View file

@ -0,0 +1,103 @@
package digitalocean
import (
"errors"
"net"
"testing"
"dnsupdater/dns"
"github.com/digitalocean/godo"
"github.com/stretchr/testify/assert"
)
func TestDigitalOceanService_New(t *testing.T) {
assert.Equal(t, Service{
api: godo.NewFromToken("token").Domains,
}, New("token"))
}
func TestDigitalOceanService_List(t *testing.T) {
expected := dns.RecordList{
{
Id: "236718",
Name: "sub1",
Ip: net.IPv4(161, 125, 137, 64),
},
{
Id: "23123131",
Name: "sub2",
Ip: net.IPv4(154, 63, 46, 159),
},
}
service := Service{
api: mock{
t: t,
records_by_type: map[string][]godo.DomainRecord{
"example.com": {
{
ID: 236718,
Type: "A",
Name: "sub1",
Data: "161.125.137.64",
Priority: 10,
TTL: 1800,
},
{
ID: 23123131,
Type: "A",
Name: "sub2",
Data: "154.63.46.159",
Priority: 5,
TTL: 1800,
},
},
},
},
}
// Test fetch.
records, err := service.List("example.com")
assert.NoError(t, err)
assert.Equal(t, expected, records)
}
func TestDigitalOceanService_Update(t *testing.T) {
mockApi := mock{
t: t,
records_by_type: map[string][]godo.DomainRecord{
"example.com": {
{
ID: 1337,
Name: "www",
Data: "80.17.42.157",
Priority: 10,
TTL: 360,
Port: 22,
Weight: 100,
Flags: 0xf1,
Tag: "some_tag",
},
},
},
edit_record_request: &godo.DomainRecordEditRequest{
Data: "221.135.170.186",
},
}
service := Service{
api: &mockApi,
}
err := service.Update("example.com", "1337", net.IPv4(221, 135, 170, 186).String())
assert.NoError(t, err)
mockApi.edit_record_error = errors.New("Error")
err = service.Update("invalid.com", "1340", net.IPv4(72, 82, 118, 186).String())
assert.Error(t, err)
err = service.Update("example.com", "1337", net.IPv4(221, 135, 170, 186).String())
assert.Error(t, err)
}