refactor code

This commit is contained in:
DataHearth 2021-03-15 19:13:11 +01:00
parent e366dd3613
commit 38c4a95b65
8 changed files with 133 additions and 52 deletions

37
main.go
View File

@ -1,6 +1,9 @@
package ddnsclient
import (
"os"
"os/signal"
"syscall"
"time"
"github.com/datahearth/ddnsclient/pkg/providers/ovh"
@ -11,21 +14,43 @@ import (
// Start create a new instance of ddns-client
func Start(logger logrus.FieldLogger) error {
log := logger.WithFields(logrus.Fields{
"pkg": "ddnsclient",
"component": "root",
})
log.Debugln("create OVH provider")
ovh, err := ovh.NewOVH(logger)
if err != nil {
return err
}
log.Debugln("creating watcher with OVH provider")
w, err := watcher.NewWatcher(logger, ovh, viper.GetString("web-ip"))
if err != nil {
return err
}
c := make(chan bool)
go w.Run(c)
for {
}
sigc := make(chan os.Signal, 1)
signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
chClose := make(chan struct{})
chErr := make(chan error)
defer close(chClose)
defer close(chErr)
defer close(sigc)
return nil
log.Infoln("Start watching periodically for changes!")
go w.Run(time.NewTicker(viper.GetDuration("update-time")*time.Second), chClose, chErr)
for {
select {
case err := <-chErr:
log.WithError(err).Errorln("An error occured while running the watcher. Retrying in the next tick")
continue
case <-sigc:
log.Infoln("Interrupt signal received. Stopping watcher...")
chClose <- struct{}{}
return nil
}
}
}

View File

@ -11,23 +11,24 @@ import (
)
type ovh struct {
ovhConfig config
ovhConfig utils.ProviderConfig
logger logrus.FieldLogger
}
// NewOVH returns a new instance of the OVH provider
func NewOVH(logger logrus.FieldLogger) (providers.Provider, error) {
var ovhConfig config
if c, ok := viper.GetStringMap("provider")["ovh"].(config); ok {
var ovhConfig utils.ProviderConfig
if c, ok := viper.GetStringMap("provider")["ovh"].(utils.ProviderConfig); ok {
ovhConfig = c
} else {
return nil, ErrNilOvhConfig
return nil, utils.ErrNilOvhConfig
}
if logger == nil {
return nil, utils.ErrNilLogger
}
logger = logger.WithField("pkg", "provider-ovh")
return &ovh{
ovhConfig: ovhConfig,
logger: logger,
@ -37,11 +38,16 @@ func NewOVH(logger logrus.FieldLogger) (providers.Provider, error) {
func (ovh *ovh) UpdateIP(subdomain, ip string) error {
newURL := strings.ReplaceAll(ovh.ovhConfig["url"].(string), "SUBDOMAIN", subdomain)
newURL = strings.ReplaceAll(newURL, "NEWIP", ip)
logger := ovh.logger.WithFields(logrus.Fields{
"component": "update-ip",
"ovh-update-url": newURL,
})
// * create GET request
req, err := http.NewRequest("GET", newURL, nil)
if err != nil {
return err
logger.WithError(err).WithField("request-type", "GET").Errorln(utils.ErrCreateNewRequest.Error())
return utils.ErrCreateNewRequest
}
req.SetBasicAuth(ovh.ovhConfig["username"].(string), ovh.ovhConfig["password"].(string))
@ -49,10 +55,11 @@ func (ovh *ovh) UpdateIP(subdomain, ip string) error {
c := new(http.Client)
resp, err := c.Do(req)
if err != nil {
return err
logger.WithError(err).Errorln(utils.ErrUpdateRequest.Error())
return utils.ErrUpdateRequest
}
if resp.StatusCode != 200 {
logger.WithField("status-code", resp.StatusCode).Errorln(utils.ErrWrongStatusCode.Error())
return utils.ErrWrongStatusCode
}

View File

@ -1,12 +0,0 @@
package ovh
import "errors"
type (
config map[string]interface{}
)
var (
// ErrNilOvhConfig is thrown when OVH configuration is empty
ErrNilOvhConfig = errors.New("OVH config is mandatory")
)

View File

@ -27,7 +27,7 @@ func NewSubdomain(logger logrus.FieldLogger, subdomainAddr string) (Subdomain, e
return nil, utils.ErrNilLogger
}
logger = logger.WithField("package", "http")
logger = logger.WithField("pkg", "subdomain")
return &subdomain{
logger: logger,
@ -37,20 +37,25 @@ func NewSubdomain(logger logrus.FieldLogger, subdomainAddr string) (Subdomain, e
// RetrieveSubdomainIP will retrieve the subdomain IP with a HEAD request
func (sd *subdomain) retrieveSubdomainIP() error {
logger := sd.logger.WithField("component", "retrieve-subdomain-ip")
resp, err := h.Head(sd.subdomainAddr)
if err != nil {
return err
logger.WithError(err).WithField("subdomain", sd.subdomainAddr).Errorln(utils.ErrHeadRemoteIP.Error())
return utils.ErrHeadRemoteIP
}
if resp.StatusCode != 200 {
logger.WithField("status-code", resp.StatusCode).Errorln(utils.ErrWrongStatusCode.Error())
return utils.ErrWrongStatusCode
}
h, _, err := net.SplitHostPort(resp.Request.RemoteAddr)
host, _, err := net.SplitHostPort(resp.Request.RemoteAddr)
if err != nil {
return err
logger.WithError(err).WithField("remote-address", resp.Request.RemoteAddr).Errorln()
return utils.ErrSplitAddr
}
sd.ip = h
sd.ip = host
return nil
}
@ -58,6 +63,7 @@ func (sd *subdomain) retrieveSubdomainIP() error {
// CheckIPAddr will compare the srvIP passed in parameter and the subIP retrieved from the head request
func (sd *subdomain) CheckIPAddr(srvIP string) (bool, error) {
if err := sd.retrieveSubdomainIP(); err != nil {
sd.logger.WithError(err).WithField("component", "check-ip-address").Errorln("failed to retrieve subdomain ip address")
return false, err
}

View File

@ -2,11 +2,34 @@ package utils
import "errors"
// * Errors
var (
// ErrReadConfigFile is thrown when viper failed to read config file
ErrReadConfigFile = errors.New("failed to read config file")
// ErrNilLogger is thrown when the parameter logger is nil
ErrNilLogger = errors.New("logger can't be nil")
ErrNilLogger = errors.New("logger is mandatory")
// ErrNilOvhConfig is thrown when OVH configuration is empty
ErrNilOvhConfig = errors.New("OVH config is mandatory")
// ErrNilProvider ...
ErrNilProvider = errors.New("provider is mandatory")
// ErrNilHTTP ...
ErrNilHTTP = errors.New("http is mandatory")
// ErrWrongStatusCode is thrown when the response status code isn't a 200
ErrWrongStatusCode = errors.New("response sent an non 200 status code")
// ErrGetServerIP is thrown when HTTP can't contact the web-ip service
ErrGetServerIP = errors.New("failed to fetch server IP")
// ErrParseHTTPBody is thrown when the HTTP service can't parse the body response
ErrParseHTTPBody = errors.New("can't parse response body")
// ErrHeadRemoteIP ...
ErrHeadRemoteIP = errors.New("failed to fetch subdomain informations with HEAD")
// ErrSplitAddr ...
ErrSplitAddr = errors.New("can't split subdomain remote IP address")
// ErrCreateNewRequest ...
ErrCreateNewRequest = errors.New("can't create http request")
// ErrUpdateRequest ...
ErrUpdateRequest = errors.New("failed to set new IP address")
)
type (
ProviderConfig map[string]interface{}
)

View File

@ -64,20 +64,31 @@ func AggregateSubdomains(subdomains []string, domain string) []string {
}
// RetrieveServerIP will use the defined web-ip service to get the server public address and save it to the struct
func RetrieveServerIP(webIP) (string, error) {
func RetrieveServerIP(webIP string) (string, error) {
logger := logrus.WithFields(logrus.Fields{
"pkg": "utils",
"component": "server-ip",
})
// * retrieve client's server IP
resp, err := http.Get(webIP)
if err != nil {
return "", err
logger.WithError(err).WithField("web-ip", webIP).Errorln(ErrGetServerIP.Error())
return "", ErrGetServerIP
}
if resp.StatusCode != 200 {
logger.WithError(err).WithFields(logrus.Fields{
"web-ip": webIP,
"statuc-code": resp.StatusCode,
}).Errorln(ErrWrongStatusCode.Error())
return "", ErrWrongStatusCode
}
// * get ip from body
d, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
logger.WithError(err).WithField("web-ip", webIP).Errorln(ErrParseHTTPBody.Error())
return "", ErrParseHTTPBody
}
return string(d), nil

View File

@ -1,8 +0,0 @@
package watcher
import "errors"
var (
ErrNilProvider = errors.New("provider can't be nil")
ErrNilHTTP = errors.New("http can't be nil")
)

View File

@ -1,6 +1,8 @@
package watcher
import (
"time"
"github.com/datahearth/ddnsclient/pkg/providers"
"github.com/datahearth/ddnsclient/pkg/subdomain"
"github.com/datahearth/ddnsclient/pkg/utils"
@ -9,7 +11,7 @@ import (
)
type Watcher interface {
Run(chan bool) error
Run(*time.Ticker, chan struct{}, chan error)
}
type watcher struct {
@ -25,11 +27,13 @@ func NewWatcher(logger logrus.FieldLogger, provider providers.Provider, webIP st
return nil, utils.ErrNilLogger
}
if provider == nil {
return nil, ErrNilProvider
return nil, utils.ErrNilProvider
}
if webIP == "" {
webIP = "http://dynamicdns.park-your-domain.com/getip"
}
logger = logger.WithField("pkg", "watcher")
domain := viper.GetStringMap("watcher")["domain"].(string)
sbs := utils.AggregateSubdomains(viper.GetStringMap("watcher")["subdomains"].([]string), domain)
subdomains := make([]subdomain.Subdomain, len(sbs))
@ -51,24 +55,49 @@ func NewWatcher(logger logrus.FieldLogger, provider providers.Provider, webIP st
}, nil
}
func (w *watcher) Run(close chan bool) error {
func (w *watcher) Run(t *time.Ticker, chClose chan struct{}, chErr chan error) {
logger := w.logger.WithField("component", "run")
for {
select {
case <-close:
return nil
default:
case <-chClose:
t.Stop()
logger.Infoln("Close watcher channel triggered. Ticker stoped")
return
case <-t.C:
logger.Infoln("Starting DDNS check")
srvIP, err := utils.RetrieveServerIP(w.webIP)
if err != nil {
return err
chErr <- err
continue
}
logger.WithField("server-ip", srvIP).Debugln("Server IP retrieved. Checking subdomains...")
for _, sd := range w.subdomains {
ok, err := sd.CheckIPAddr(srvIP)
if err != nil {
return err
logger.WithError(err).WithField("server-ip", srvIP).Errorln("failed to check ip addresses")
chErr <- err
continue
}
if !ok {
w.provider.UpdateIP(sd.GetSubdomainIP(), srvIP)
subIP := sd.GetSubdomainIP()
logger.WithFields(logrus.Fields{
"server-ip": srvIP,
"subdomain-ip": subIP,
}).Infoln("IP addresses doesn't match. Updating subdomain's ip...")
if err := w.provider.UpdateIP(subIP, srvIP); err != nil {
logger.WithError(err).WithFields(logrus.Fields{
"server-ip": srvIP,
"subdomain-ip": subIP,
}).Errorln("failed to update subdomain's ip")
chErr <- err
continue
}
logger.WithFields(logrus.Fields{
"server-ip": srvIP,
"subdomain-ip": subIP,
}).Infoln("Subdomain updated successfully!")
}
}
}