Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: simplify config and refactor pp #69

Merged
merged 8 commits into from
Aug 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ jobs:
with:
go-version: ${{ matrix.go-version }}
- uses: actions/checkout@v2
- run: go test -coverpkg=./... -coverprofile=coverage.txt -race -v ./...
- run: |
go install github.com/golang/mock/mockgen@latest
go generate ./...
go test -coverpkg=./... -coverprofile=coverage.txt -race -v ./...
- uses: codecov/codecov-action@v2
with:
file: ./coverage.txt
Expand Down
6 changes: 4 additions & 2 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ By default, public IP addresses are obtained using [Cloudflare via DNS-over-HTTP
The updater honors `PGID` and `PUID` and will drop Linux capabilities (divided superuser privileges).
</details>

<details><summary>🔌 The source code depends on five external libraries (outside the Go project).</summary>
<details><summary>🔌 The source code depends on six external libraries (outside the Go project).</summary>

- [cap](https://sites.google.com/site/fullycapable):\
Manipulation of Linux capabilities.
Expand All @@ -54,7 +54,9 @@ The updater honors `PGID` and `PUID` and will drop Linux capabilities (divided s
Parsing of Cron expressions.
- [go-cache](https://github.com/patrickmn/go-cache):\
Essentially `map[string]interface{}` with expiration times.
- [testify](https://github.com/stretchr/testify) (only for testing):\
- [mock](https://github.com/golang/mock) (for testing only):\
A comprehensive, semi-official framework for mocking.
- [testify](https://github.com/stretchr/testify) (for testing only):\
A comprehensive tool set for testing Go programs.
</details>

Expand Down
95 changes: 50 additions & 45 deletions cmd/ddns.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,49 +30,56 @@ func signalWait(signal chan os.Signal, d time.Duration) (os.Signal, bool) {

var Version string //nolint:gochecknoglobals

func welcome() {
func welcome(ppfmt pp.PP) {
if Version == "" {
pp.TopPrintf(pp.EmojiStar, "Cloudflare DDNS")
ppfmt.Noticef(pp.EmojiStar, "Cloudflare DDNS")
return
}

pp.TopPrintf(pp.EmojiStar, "Cloudflare DDNS (%s)", Version)
ppfmt.Noticef(pp.EmojiStar, "Cloudflare DDNS (%s)", Version)
}

func initConfig(ctx context.Context) (*config.Config, api.Handle) {
func initConfig(ctx context.Context, ppfmt pp.PP) (*config.Config, api.Handle) {
// reading the config
c := config.Default()
if !c.ReadEnv(pp.NoIndent) {
pp.TopPrintf(pp.EmojiBye, "Bye!")
if !c.ReadEnv(ppfmt) {
ppfmt.Noticef(pp.EmojiBye, "Bye!")
os.Exit(1)
}
if !c.Normalize(pp.NoIndent) {
pp.TopPrintf(pp.EmojiBye, "Bye!")
if !c.Normalize(ppfmt) {
ppfmt.Noticef(pp.EmojiBye, "Bye!")
os.Exit(1)
}

if !c.Quiet {
config.PrintConfig(pp.NoIndent, c)
}
config.PrintConfig(ppfmt, c)

// getting the handler
h, ok := c.Auth.New(ctx, pp.NoIndent, c.CacheExpiration)
h, ok := c.Auth.New(ctx, ppfmt, c.CacheExpiration)
if !ok {
pp.TopPrintf(pp.EmojiBye, "Bye!")
ppfmt.Noticef(pp.EmojiBye, "Bye!")
os.Exit(1)
}

return c, h
}

func main() { //nolint:funlen,gocognit,cyclop
welcome()
func main() { //nolint:funlen,cyclop
ppfmt := pp.New(os.Stdout)
if !config.ReadQuiet("QUIET", &ppfmt) {
ppfmt.Noticef(pp.EmojiUserError, "Bye!")
return
}
if !ppfmt.IsEnabledFor(pp.Info) {
ppfmt.Noticef(pp.EmojiMute, "Quiet mode enabled")
}

welcome(ppfmt)

// dropping the superuser privilege
dropPriviledges(pp.NoIndent)
dropPriviledges(ppfmt)

// printing the current privileges
printPriviledges(pp.NoIndent)
printPriviledges(ppfmt)

// catching SIGINT and SIGTERM
chanSignal := make(chan os.Signal, 1)
Expand All @@ -82,71 +89,69 @@ func main() { //nolint:funlen,gocognit,cyclop
ctx := context.Background()

// reading the config
c, h := initConfig(ctx)
c, h := initConfig(ctx, ppfmt)

first := true
mainLoop:
for {
next := c.UpdateCron.Next()
if !first || c.UpdateOnStart {
updateIPs(ctx, pp.NoIndent, c, h)
updateIPs(ctx, ppfmt, c, h)
}
first = false

if next.IsZero() {
if c.DeleteOnStop {
pp.TopPrintf(pp.EmojiUserError, "No scheduled updates in near future. Deleting all managed records . . .")
clearIPs(ctx, pp.NoIndent, c, h)
pp.TopPrintf(pp.EmojiBye, "Done now. Bye!")
ppfmt.Errorf(pp.EmojiUserError, "No scheduled updates in near future. Deleting all managed records . . .")
clearIPs(ctx, ppfmt, c, h)
ppfmt.Noticef(pp.EmojiBye, "Done now. Bye!")
} else {
pp.TopPrintf(pp.EmojiUserError, "No scheduled updates in near future.")
pp.TopPrintf(pp.EmojiBye, "Bye!")
ppfmt.Errorf(pp.EmojiUserError, "No scheduled updates in near future")
ppfmt.Noticef(pp.EmojiBye, "Bye!")
}

break mainLoop
}

interval := time.Until(next)
if !c.Quiet {
switch {
case interval < -IntervalLargeGap:
pp.TopPrintf(pp.EmojiNow, "Checking the IP addresses now (running behind by %v) . . .",
-interval.Round(IntervalUnit))
case interval < IntervalUnit:
pp.TopPrintf(pp.EmojiNow, "Checking the IP addresses now . . .")
case interval < IntervalLargeGap:
pp.TopPrintf(pp.EmojiNow, "Checking the IP addresses in less than %v . . .", IntervalLargeGap)
default:
pp.TopPrintf(pp.EmojiAlarm, "Checking the IP addresses in about %v . . .", interval.Round(IntervalUnit))
}
switch {
case interval < -IntervalLargeGap:
ppfmt.Infof(pp.EmojiNow, "Checking the IP addresses now (running behind by %v) . . .",
-interval.Round(IntervalUnit))
case interval < IntervalUnit:
ppfmt.Infof(pp.EmojiNow, "Checking the IP addresses now . . .")
case interval < IntervalLargeGap:
ppfmt.Infof(pp.EmojiNow, "Checking the IP addresses in less than %v . . .", IntervalLargeGap)
default:
ppfmt.Infof(pp.EmojiAlarm, "Checking the IP addresses in about %v . . .", interval.Round(IntervalUnit))
}

if sig, ok := signalWait(chanSignal, interval); !ok {
continue mainLoop
} else {
switch sig.(syscall.Signal) {
case syscall.SIGHUP:
pp.TopPrintf(pp.EmojiSignal, "Caught signal: %v.", sig)
ppfmt.Noticef(pp.EmojiSignal, "Caught signal: %v", sig)
h.FlushCache()

pp.TopPrintf(pp.EmojiNow, "Restarting . . .")
c, h = initConfig(ctx)
ppfmt.Noticef(pp.EmojiNow, "Restarting . . .")
c, h = initConfig(ctx, ppfmt)
continue mainLoop

case syscall.SIGINT, syscall.SIGTERM:
if c.DeleteOnStop {
pp.TopPrintf(pp.EmojiSignal, "Caught signal: %v. Deleting all managed records . . .", sig)
clearIPs(ctx, pp.NoIndent, c, h)
pp.TopPrintf(pp.EmojiBye, "Done now. Bye!")
ppfmt.Noticef(pp.EmojiSignal, "Caught signal: %v. Deleting all managed records . . .", sig)
clearIPs(ctx, ppfmt, c, h)
ppfmt.Noticef(pp.EmojiBye, "Done now. Bye!")
} else {
pp.TopPrintf(pp.EmojiSignal, "Caught signal: %v.", sig)
pp.TopPrintf(pp.EmojiBye, "Bye!")
ppfmt.Noticef(pp.EmojiSignal, "Caught signal: %v", sig)
ppfmt.Noticef(pp.EmojiBye, "Bye!")
}

break mainLoop

default:
pp.TopPrintf(pp.EmojiSignal, "Caught and ignored unexpected signal: %v.", sig)
ppfmt.Noticef(pp.EmojiSignal, "Caught and ignored unexpected signal: %v", sig)
continue mainLoop
}
}
Expand Down
41 changes: 17 additions & 24 deletions cmd/ip.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import (
"github.com/favonia/cloudflare-ddns/internal/updator"
)

func setIP(ctx context.Context, indent pp.Indent, c *config.Config, h api.Handle, ipNet ipnet.Type, ip net.IP) {
func setIP(ctx context.Context, ppfmt pp.PP, c *config.Config, h api.Handle, ipNet ipnet.Type, ip net.IP) {
for _, target := range c.Domains[ipNet] {
ctx, cancel := context.WithTimeout(ctx, c.UpdateTimeout)
defer cancel()

_ = updator.Do(ctx, indent, c.Quiet,
_ = updator.Do(ctx, ppfmt,
&updator.Args{
Handle: h,
Domain: target,
Expand All @@ -28,39 +28,32 @@ func setIP(ctx context.Context, indent pp.Indent, c *config.Config, h api.Handle
}
}

func updateIP(ctx context.Context, indent pp.Indent, c *config.Config, h api.Handle, ipNet ipnet.Type) {
func updateIP(ctx context.Context, ppfmt pp.PP, c *config.Config, h api.Handle, ipNet ipnet.Type) {
ctx, cancel := context.WithTimeout(ctx, c.DetectionTimeout)
defer cancel()

ip := c.Policy[ipNet].GetIP(ctx, indent, ipNet)
ip := c.Policy[ipNet].GetIP(ctx, ppfmt, ipNet)
if ip == nil {
pp.TopPrintf(pp.EmojiError, "Failed to detect the %s address.", ipNet)
ppfmt.Errorf(pp.EmojiError, "Failed to detect the %s address", ipNet.Describe())
return
}

if !c.Quiet {
pp.TopPrintf(pp.EmojiInternet, "Detected the %s address: %v", ipNet, ip)
}

setIP(ctx, indent, c, h, ipNet, ip)
ppfmt.Infof(pp.EmojiInternet, "Detected the %s address: %v", ipNet.Describe(), ip)
setIP(ctx, ppfmt, c, h, ipNet, ip)
}

func updateIPs(ctx context.Context, indent pp.Indent, c *config.Config, h api.Handle) {
if c.Policy[ipnet.IP4].IsManaged() {
updateIP(ctx, indent, c, h, ipnet.IP4)
}

if c.Policy[ipnet.IP6].IsManaged() {
updateIP(ctx, indent, c, h, ipnet.IP6)
func updateIPs(ctx context.Context, ppfmt pp.PP, c *config.Config, h api.Handle) {
for _, ipNet := range []ipnet.Type{ipnet.IP4, ipnet.IP6} {
if c.Policy[ipNet].IsManaged() {
updateIP(ctx, ppfmt, c, h, ipNet)
}
}
}

func clearIPs(ctx context.Context, indent pp.Indent, c *config.Config, h api.Handle) {
if c.Policy[ipnet.IP4].IsManaged() {
setIP(ctx, indent, c, h, ipnet.IP4, nil)
}

if c.Policy[ipnet.IP6].IsManaged() {
setIP(ctx, indent, c, h, ipnet.IP6, nil)
func clearIPs(ctx context.Context, ppfmt pp.PP, c *config.Config, h api.Handle) {
for _, ipNet := range []ipnet.Type{ipnet.IP4, ipnet.IP6} {
if c.Policy[ipNet].IsManaged() {
setIP(ctx, ppfmt, c, h, ipNet, nil)
}
}
}
Loading