Skip to content

Commit

Permalink
chore(lint): add goerr113 linter
Browse files Browse the repository at this point in the history
- Change database id to be `uint` instead of `int`
- Define and use sentinel errors
- Return `string` instead of `error` when appropriate (linode)
  • Loading branch information
qdm12 committed Aug 30, 2022
1 parent 88474c8 commit 7fcd0f9
Show file tree
Hide file tree
Showing 12 changed files with 67 additions and 47 deletions.
5 changes: 3 additions & 2 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ issues:
- gomnd
- path: _test\.go
linters:
- dupl
- containedctx
- dupl
- goerr113

linters:
enable:
Expand Down Expand Up @@ -51,7 +52,7 @@ linters:
- gomoddirectives
- goprintffuncname
- gosec
# - goerr113 # TODO
- goerr113
- grouper
- importas
- interfacebloat
Expand Down
12 changes: 6 additions & 6 deletions internal/data/memory.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
package data

import (
"errors"
"fmt"

"github.com/qdm12/ddns-updater/internal/records"
)

func (db *Database) Select(id int) (record records.Record, err error) {
var ErrRecordNotFound = errors.New("record not found")

func (db *Database) Select(id uint) (record records.Record, err error) {
db.RLock()
defer db.RUnlock()
if id < 0 {
return record, fmt.Errorf("id %d cannot be lower than 0", id)
}
if id > len(db.data)-1 {
return record, fmt.Errorf("no record config found for id %d", id)
if int(id) > len(db.data)-1 {
return record, fmt.Errorf("%w: for id %d", ErrRecordNotFound, id)
}
return db.data[id], nil
}
Expand Down
9 changes: 3 additions & 6 deletions internal/data/persistence.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,11 @@ func (db *Database) GetEvents(domain, host string) (events []models.HistoryEvent
return db.persistentDB.GetEvents(domain, host)
}

func (db *Database) Update(id int, record records.Record) (err error) {
func (db *Database) Update(id uint, record records.Record) (err error) {
db.Lock()
defer db.Unlock()
if id < 0 {
return fmt.Errorf("id %d cannot be lower than 0", id)
}
if id > len(db.data)-1 {
return fmt.Errorf("no record config found for id %d", id)
if int(id) > len(db.data)-1 {
return fmt.Errorf("%w: for id %d", ErrRecordNotFound, id)
}
currentCount := len(db.data[id].History)
newCount := len(record.History)
Expand Down
15 changes: 11 additions & 4 deletions internal/health/check.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package health

import (
"errors"
"fmt"
"net"
"strings"
Expand All @@ -17,12 +18,18 @@ func MakeIsHealthy(db AllSelecter, lookupIP lookupIPFunc, logger logging.Logger)
}
}

var (
ErrRecordUpdateFailed = errors.New("record update failed")
ErrRecordIPNotSet = errors.New("record IP not set")
ErrLookupMismatch = errors.New("lookup IP addresses do not match")
)

// isHealthy checks all the records were updated successfully and returns an error if not.
func isHealthy(db AllSelecter, lookupIP lookupIPFunc) (err error) {
records := db.SelectAll()
for _, record := range records {
if record.Status == constants.FAIL {
return fmt.Errorf("%s", record.String())
return fmt.Errorf("%w: %s", ErrRecordUpdateFailed, record.String())
} else if record.Settings.Proxied() {
continue
}
Expand All @@ -33,7 +40,7 @@ func isHealthy(db AllSelecter, lookupIP lookupIPFunc) (err error) {
}
currentIP := record.History.GetCurrentIP()
if currentIP == nil {
return fmt.Errorf("no database set IP address found for %s", hostname)
return fmt.Errorf("%w: for hostname %s", ErrRecordIPNotSet, hostname)
}
found := false
lookedUpIPsString := make([]string, len(lookedUpIPs))
Expand All @@ -45,8 +52,8 @@ func isHealthy(db AllSelecter, lookupIP lookupIPFunc) (err error) {
}
}
if !found {
return fmt.Errorf("lookup IP addresses for %s are %s instead of %s",
hostname, strings.Join(lookedUpIPsString, ","), currentIP)
return fmt.Errorf("%w: %s instead of %s for %s",
ErrLookupMismatch, strings.Join(lookedUpIPsString, ","), currentIP, hostname)
}
}
return nil
Expand Down
4 changes: 2 additions & 2 deletions internal/health/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func NewClient() *Client {
}
}

var ErrParseHealthServerAddress = errors.New("cannot parse health server address")
var ErrUnhealthy = errors.New("program is unhealthy")

// Query sends an HTTP request to the other instance of
// the program, and to its internal healthcheck server.
Expand All @@ -49,5 +49,5 @@ func (c *Client) Query(ctx context.Context, port uint16) error {
return fmt.Errorf("reading body from response with status %s: %w", resp.Status, err)
}

return fmt.Errorf(string(b))
return fmt.Errorf("%w: %s", ErrUnhealthy, string(b))
}
21 changes: 16 additions & 5 deletions internal/persistence/json/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package json

import (
"encoding/json"
"errors"
"fmt"
"sync"
"time"
Expand Down Expand Up @@ -56,25 +57,35 @@ func NewDatabase(dataDir string) (*Database, error) {
return &db, nil
}

var (
ErrDomainEmpty = errors.New("domain is empty")
ErrHostIsEmpty = errors.New("host is empty")
ErrIPRecordsMisordered = errors.New("IP records are not ordered correctly by time")
ErrIPEmpty = errors.New("IP is empty")
ErrIPTimeEmpty = errors.New("time of IP is empty")
)

func (db *Database) Check() error {
for _, record := range db.data.Records {
switch {
case len(record.Domain) == 0:
return fmt.Errorf("domain is empty for record %s", record)
return fmt.Errorf("%w: for record %s", ErrDomainEmpty, record)
case len(record.Host) == 0:
return fmt.Errorf("host is empty for record %s", record)
return fmt.Errorf("%w: for record %s", ErrHostIsEmpty, record)
}
var t time.Time
for i, event := range record.Events {
if event.Time.Before(t) {
return fmt.Errorf("IP records are not ordered correctly by time")
return fmt.Errorf("%w", ErrIPRecordsMisordered)
}
t = event.Time
switch {
case event.IP == nil:
return fmt.Errorf("IP %d of %d is empty for record %s", i+1, len(record.Events), record)
return fmt.Errorf("%w: IP %d of %d for record %s",
ErrIPEmpty, i+1, len(record.Events), record)
case event.Time.IsZero():
return fmt.Errorf("time of IP %d of %d is empty for record %s", i+1, len(record.Events), record)
return fmt.Errorf("%w: IP %d of %d for record %s",
ErrIPTimeEmpty, i+1, len(record.Events), record)
}
}
}
Expand Down
5 changes: 3 additions & 2 deletions internal/settings/providers/digitalocean/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,9 +200,10 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip net.IP) (

newIP = net.ParseIP(responseData.DomainRecord.Data)
if newIP == nil {
return nil, fmt.Errorf("IP address received %q is malformed", responseData.DomainRecord.Data)
return nil, fmt.Errorf("%w: %s", errors.ErrIPReceivedMalformed, responseData.DomainRecord.Data)
} else if !newIP.Equal(ip) {
return nil, fmt.Errorf("updated IP address %s is not the IP address %s sent for update", newIP, ip)
return nil, fmt.Errorf("%w: sent %s but received %s ",
errors.ErrIPReceivedMismatch, ip, newIP)
}
return newIP, nil
}
2 changes: 1 addition & 1 deletion internal/settings/providers/dreamhost/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func New(data json.RawMessage, domain, host string, ipVersion ipversion.IPVersio

func (p *Provider) isValid() error {
if !p.matcher.DreamhostKey(p.key) {
return fmt.Errorf("invalid key format")
return fmt.Errorf("%w: %s", errors.ErrMalformedKey, p.key)
}
return nil
}
Expand Down
16 changes: 8 additions & 8 deletions internal/settings/providers/linode/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func (p *Provider) getDomainID(ctx context.Context, client *http.Client) (domain

if response.StatusCode != http.StatusOK {
err = fmt.Errorf("%w: %d", errors.ErrBadHTTPStatus, response.StatusCode)
return 0, fmt.Errorf("%w: %s", err, p.getError(response.Body))
return 0, fmt.Errorf("%w: %s", err, p.getErrorMessage(response.Body))
}

decoder := json.NewDecoder(response.Body)
Expand Down Expand Up @@ -212,7 +212,7 @@ func (p *Provider) getRecordID(ctx context.Context, client *http.Client,

if response.StatusCode != http.StatusOK {
err = fmt.Errorf("%w: %d", errors.ErrBadHTTPStatus, response.StatusCode)
return 0, fmt.Errorf("%w: %s", err, p.getError(response.Body))
return 0, fmt.Errorf("%w: %s", err, p.getErrorMessage(response.Body))
}

decoder := json.NewDecoder(response.Body)
Expand Down Expand Up @@ -276,7 +276,7 @@ func (p *Provider) createRecord(ctx context.Context, client *http.Client,

if response.StatusCode != http.StatusOK {
err = fmt.Errorf("%w: %d", errors.ErrBadHTTPStatus, response.StatusCode)
return fmt.Errorf("%w: %s", err, p.getError(response.Body))
return fmt.Errorf("%w: %s", err, p.getErrorMessage(response.Body))
}

var responseData domainRecord
Expand Down Expand Up @@ -329,7 +329,7 @@ func (p *Provider) updateRecord(ctx context.Context, client *http.Client,

if response.StatusCode != http.StatusOK {
err = fmt.Errorf("%w: %d", errors.ErrBadHTTPStatus, response.StatusCode)
return fmt.Errorf("%w: %s", err, p.getError(response.Body))
return fmt.Errorf("%w: %s", err, p.getErrorMessage(response.Body))
}

data.IP = ""
Expand All @@ -348,14 +348,14 @@ func (p *Provider) updateRecord(ctx context.Context, client *http.Client,
return nil
}

func (p *Provider) getError(body io.Reader) (err error) {
func (p *Provider) getErrorMessage(body io.Reader) (message string) {
var errorObj linodeErrors
b, err := io.ReadAll(body)
if err != nil {
return err
return fmt.Sprintf("reading body: %s", err)
}
if err := json.Unmarshal(b, &errorObj); err != nil {
return fmt.Errorf("%s", utils.ToSingleLine(string(b)))
return utils.ToSingleLine(string(b))
}
return fmt.Errorf("%v", errorObj)
return fmt.Sprintf("%v", errorObj)
}
6 changes: 3 additions & 3 deletions internal/update/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ type PublicIPFetcher interface {
}

type UpdaterInterface interface {
Update(ctx context.Context, recordID int, ip net.IP, now time.Time) (err error)
Update(ctx context.Context, recordID uint, ip net.IP, now time.Time) (err error)
}

type Database interface {
Select(recordID int) (record records.Record, err error)
Select(recordID uint) (record records.Record, err error)
SelectAll() (records []records.Record)
Update(recordID int, record records.Record) (err error)
Update(recordID uint, record records.Record) (err error)
}
12 changes: 7 additions & 5 deletions internal/update/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,11 @@ func (r *Runner) getNewIPs(ctx context.Context, doIP, doIPv4, doIPv6 bool, ipv6M
}

func (r *Runner) getRecordIDsToUpdate(ctx context.Context, records []librecords.Record,
ip, ipv4, ipv6 net.IP, now time.Time, ipv6Mask net.IPMask) (recordIDs map[int]struct{}) {
recordIDs = make(map[int]struct{})
for id, record := range records {
ip, ipv4, ipv6 net.IP, now time.Time, ipv6Mask net.IPMask) (recordIDs map[uint]struct{}) {
recordIDs = make(map[uint]struct{})
for i, record := range records {
if shouldUpdate := r.shouldUpdateRecord(ctx, record, ip, ipv4, ipv6, now, ipv6Mask); shouldUpdate {
id := uint(i)
recordIDs[id] = struct{}{}
}
}
Expand Down Expand Up @@ -239,7 +240,7 @@ func getIPMatchingVersion(ip, ipv4, ipv6 net.IP, ipVersion ipversion.IPVersion)
return nil
}

func setInitialUpToDateStatus(db Database, id int, updateIP net.IP, now time.Time) error {
func setInitialUpToDateStatus(db Database, id uint, updateIP net.IP, now time.Time) error {
record, err := db.Select(id)
if err != nil {
return err
Expand Down Expand Up @@ -268,7 +269,8 @@ func (r *Runner) updateNecessary(ctx context.Context, ipv6Mask net.IPMask) (erro
now := r.timeNow()
recordIDs := r.getRecordIDsToUpdate(ctx, records, ip, ipv4, ipv6, now, ipv6Mask)

for id, record := range records {
for i, record := range records {
id := uint(i)
_, requireUpdate := recordIDs[id]
if requireUpdate || record.Status != constants.UNSET {
continue
Expand Down
7 changes: 4 additions & 3 deletions internal/update/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func NewUpdater(db Database, client *http.Client, notify notifyFunc, logger logg
}
}

func (u *Updater) Update(ctx context.Context, id int, ip net.IP, now time.Time) (err error) {
func (u *Updater) Update(ctx context.Context, id uint, ip net.IP, now time.Time) (err error) {
record, err := u.db.Select(id)
if err != nil {
return err
Expand All @@ -50,10 +50,11 @@ func (u *Updater) Update(ctx context.Context, id int, ip net.IP, now time.Time)
if errors.Is(err, settingserrors.ErrAbuse) {
lastBan := time.Unix(now.Unix(), 0)
record.LastBan = &lastBan
message := record.Settings.BuildDomainName() + ": " + record.Message +
domainName := record.Settings.BuildDomainName()
message := domainName + ": " + record.Message +
", no more updates will be attempted for an hour"
u.notify(message)
err = fmt.Errorf(message)
err = fmt.Errorf("%w: for domain %s, no more update will be attempted for 1h", err, domainName)
} else {
record.LastBan = nil // clear a previous ban
}
Expand Down

0 comments on commit 7fcd0f9

Please sign in to comment.