Skip to content

Commit

Permalink
Pull request: hup-reload
Browse files Browse the repository at this point in the history
Merge in DNS/adguard-home from hup-reload to master

Squashed commit of the following:

commit 5cd4ab8
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Oct 7 19:58:17 2022 +0300

    next: imp signal hdlr

commit 8fd18e7
Merge: a878045 f1dd333
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Oct 7 19:46:48 2022 +0300

    Merge branch 'master' into hup-reload

commit a878045
Merge: 349dbe5 960a7a7
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Oct 7 15:49:23 2022 +0300

    Merge branch 'master' into hup-reload

commit 349dbe5
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Oct 7 15:43:52 2022 +0300

    next: imp docs, names

commit 7287a86
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Oct 7 13:39:44 2022 +0300

    WIP all: impl dynamic reconfiguration
  • Loading branch information
ainar-g committed Oct 10, 2022
1 parent f1dd333 commit f5602d9
Show file tree
Hide file tree
Showing 18 changed files with 418 additions and 71 deletions.
6 changes: 3 additions & 3 deletions internal/aghos/filewalker_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ import (
// errFSOpen.
type errFS struct{}

// errFSOpen is returned from errGlobFS.Open.
// errFSOpen is returned from errFS.Open.
const errFSOpen errors.Error = "test open error"

// Open implements the fs.FS interface for *errGlobFS. fsys is always nil and
// err is always errFSOpen.
// Open implements the fs.FS interface for *errFS. fsys is always nil and err
// is always errFSOpen.
func (efs *errFS) Open(name string) (fsys fs.File, err error) {
return nil, errFSOpen
}
Expand Down
10 changes: 10 additions & 0 deletions internal/aghos/os.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,21 @@ func RootDirFS() (fsys fs.FS) {
return os.DirFS("")
}

// NotifyReconfigureSignal notifies c on receiving reconfigure signals.
func NotifyReconfigureSignal(c chan<- os.Signal) {
notifyReconfigureSignal(c)
}

// NotifyShutdownSignal notifies c on receiving shutdown signals.
func NotifyShutdownSignal(c chan<- os.Signal) {
notifyShutdownSignal(c)
}

// IsReconfigureSignal returns true if sig is a reconfigure signal.
func IsReconfigureSignal(sig os.Signal) (ok bool) {
return isReconfigureSignal(sig)
}

// IsShutdownSignal returns true if sig is a shutdown signal.
func IsShutdownSignal(sig os.Signal) (ok bool) {
return isShutdownSignal(sig)
Expand Down
8 changes: 8 additions & 0 deletions internal/aghos/os_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,18 @@ import (
"golang.org/x/sys/unix"
)

func notifyReconfigureSignal(c chan<- os.Signal) {
signal.Notify(c, unix.SIGHUP)
}

func notifyShutdownSignal(c chan<- os.Signal) {
signal.Notify(c, unix.SIGINT, unix.SIGQUIT, unix.SIGTERM)
}

func isReconfigureSignal(sig os.Signal) (ok bool) {
return sig == unix.SIGHUP
}

func isShutdownSignal(sig os.Signal) (ok bool) {
switch sig {
case
Expand Down
8 changes: 8 additions & 0 deletions internal/aghos/os_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,20 @@ func isOpenWrt() (ok bool) {
return false
}

func notifyReconfigureSignal(c chan<- os.Signal) {
signal.Notify(c, windows.SIGHUP)
}

func notifyShutdownSignal(c chan<- os.Signal) {
// syscall.SIGTERM is processed automatically. See go doc os/signal,
// section Windows.
signal.Notify(c, os.Interrupt)
}

func isReconfigureSignal(sig os.Signal) (ok bool) {
return sig == windows.SIGHUP
}

func isShutdownSignal(sig os.Signal) (ok bool) {
switch sig {
case
Expand Down
17 changes: 10 additions & 7 deletions internal/aghtest/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"net"

"github.com/AdguardTeam/AdGuardHome/internal/aghos"
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/miekg/dns"
)
Expand Down Expand Up @@ -88,7 +89,7 @@ func (l *Listener) Close() (err error) {
return l.OnClose()
}

// Module AdGuardHome
// Module adguard-home

// Package aghos

Expand Down Expand Up @@ -117,29 +118,31 @@ func (w *FSWatcher) Close() (err error) {
return w.OnClose()
}

// Package websvc
// Package agh

// ServiceWithConfig is a mock [websvc.ServiceWithConfig] implementation for
// tests.
// type check
var _ agh.ServiceWithConfig[struct{}] = (*ServiceWithConfig[struct{}])(nil)

// ServiceWithConfig is a mock [agh.ServiceWithConfig] implementation for tests.
type ServiceWithConfig[ConfigType any] struct {
OnStart func() (err error)
OnShutdown func(ctx context.Context) (err error)
OnConfig func() (c ConfigType)
}

// Start implements the [websvc.ServiceWithConfig] interface for
// Start implements the [agh.ServiceWithConfig] interface for
// *ServiceWithConfig.
func (s *ServiceWithConfig[_]) Start() (err error) {
return s.OnStart()
}

// Shutdown implements the [websvc.ServiceWithConfig] interface for
// Shutdown implements the [agh.ServiceWithConfig] interface for
// *ServiceWithConfig.
func (s *ServiceWithConfig[_]) Shutdown(ctx context.Context) (err error) {
return s.OnShutdown(ctx)
}

// Config implements the [websvc.ServiceWithConfig] interface for
// Config implements the [agh.ServiceWithConfig] interface for
// *ServiceWithConfig.
func (s *ServiceWithConfig[ConfigType]) Config() (c ConfigType) {
return s.OnConfig()
Expand Down
8 changes: 1 addition & 7 deletions internal/aghtest/interface_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
package aghtest_test

import (
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/AdGuardHome/internal/next/websvc"
)

// type check
var _ websvc.ServiceWithConfig[struct{}] = (*aghtest.ServiceWithConfig[struct{}])(nil)
// Put interface checks that cause import cycles here.
40 changes: 35 additions & 5 deletions internal/next/agh/agh.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
// Package agh contains common entities and interfaces of AdGuard Home.
//
// TODO(a.garipov): Move to the upper-level internal/.
package agh

import "context"
Expand All @@ -23,11 +21,43 @@ type Service interface {
// type check
var _ Service = EmptyService{}

// EmptyService is a Service that does nothing.
// EmptyService is a [Service] that does nothing.
//
// TODO(a.garipov): Remove if unnecessary.
type EmptyService struct{}

// Start implements the Service interface for EmptyService.
// Start implements the [Service] interface for EmptyService.
func (EmptyService) Start() (err error) { return nil }

// Shutdown implements the Service interface for EmptyService.
// Shutdown implements the [Service] interface for EmptyService.
func (EmptyService) Shutdown(_ context.Context) (err error) { return nil }

// ServiceWithConfig is an extension of the [Service] interface for services
// that can return their configuration.
//
// TODO(a.garipov): Consider removing this generic interface if we figure out
// how to make it testable in a better way.
type ServiceWithConfig[ConfigType any] interface {
Service

Config() (c ConfigType)
}

// type check
var _ ServiceWithConfig[struct{}] = (*EmptyServiceWithConfig[struct{}])(nil)

// EmptyServiceWithConfig is a ServiceWithConfig that does nothing. Its Config
// method returns Conf.
//
// TODO(a.garipov): Remove if unnecessary.
type EmptyServiceWithConfig[ConfigType any] struct {
EmptyService

Conf ConfigType
}

// Config implements the [ServiceWithConfig] interface for
// *EmptyServiceWithConfig.
func (s *EmptyServiceWithConfig[ConfigType]) Config() (conf ConfigType) {
return s.Conf
}
33 changes: 20 additions & 13 deletions internal/next/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import (
"context"
"io/fs"
"math/rand"
"net/netip"
"os"
"time"

"github.com/AdguardTeam/AdGuardHome/internal/next/websvc"
"github.com/AdguardTeam/AdGuardHome/internal/next/configmgr"
"github.com/AdguardTeam/AdGuardHome/internal/version"
"github.com/AdguardTeam/golibs/log"
)

Expand All @@ -24,26 +25,32 @@ func Main(clientBuildFS fs.FS) {

// TODO(a.garipov): Set up logging.

log.Info("starting adguard home, version %s, pid %d", version.Version(), os.Getpid())

// Web Service

// TODO(a.garipov): Use in the Web service.
_ = clientBuildFS

// TODO(a.garipov): Make configurable.
web := websvc.New(&websvc.Config{
// TODO(a.garipov): Use an actual implementation.
ConfigManager: nil,
Addresses: []netip.AddrPort{netip.MustParseAddrPort("127.0.0.1:3001")},
Start: start,
Timeout: 60 * time.Second,
ForceHTTPS: false,
})

err := web.Start()
// TODO(a.garipov): Set up configuration file name.
const confFile = "AdGuardHome.1.yaml"

confMgr, err := configmgr.New(confFile, start)
fatalOnError(err)

web := confMgr.Web()
err = web.Start()
fatalOnError(err)

dns := confMgr.DNS()
err = dns.Start()
fatalOnError(err)

sigHdlr := newSignalHandler(
confFile,
start,
web,
dns,
)

go sigHdlr.handle()
Expand Down
68 changes: 58 additions & 10 deletions internal/next/cmd/signal.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,26 @@ package cmd

import (
"os"
"time"

"github.com/AdguardTeam/AdGuardHome/internal/aghos"
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
"github.com/AdguardTeam/AdGuardHome/internal/next/configmgr"
"github.com/AdguardTeam/golibs/log"
)

// signalHandler processes incoming signals and shuts services down.
type signalHandler struct {
// signal is the channel to which OS signals are sent.
signal chan os.Signal

// services are the services that are shut down before application
// exiting.
// confFile is the path to the configuration file.
confFile string

// start is the time at which AdGuard Home has been started.
start time.Time

// services are the services that are shut down before application exiting.
services []agh.Service
}

Expand All @@ -24,24 +32,63 @@ func (h *signalHandler) handle() {
for sig := range h.signal {
log.Info("sighdlr: received signal %q", sig)

if aghos.IsShutdownSignal(sig) {
h.shutdown()
if aghos.IsReconfigureSignal(sig) {
h.reconfigure()
} else if aghos.IsShutdownSignal(sig) {
status := h.shutdown()
log.Info("sighdlr: exiting with status %d", status)

os.Exit(status)
}
}
}

// reconfigure rereads the configuration file and updates and restarts services.
func (h *signalHandler) reconfigure() {
log.Info("sighdlr: reconfiguring adguard home")

status := h.shutdown()
if status != statusSuccess {
log.Info("sighdlr: reconfiruging: exiting with status %d", status)

os.Exit(status)
}

// TODO(a.garipov): This is a very rough way to do it. Some services can be
// reconfigured without the full shutdown, and the error handling is
// currently not the best.

confMgr, err := configmgr.New(h.confFile, h.start)
fatalOnError(err)

web := confMgr.Web()
err = web.Start()
fatalOnError(err)

dns := confMgr.DNS()
err = dns.Start()
fatalOnError(err)

h.services = []agh.Service{
dns,
web,
}

log.Info("sighdlr: successfully reconfigured adguard home")
}

// Exit status constants.
const (
statusSuccess = 0
statusError = 1
)

// shutdown gracefully shuts down all services.
func (h *signalHandler) shutdown() {
func (h *signalHandler) shutdown() (status int) {
ctx, cancel := ctxWithDefaultTimeout()
defer cancel()

status := statusSuccess
status = statusSuccess

log.Info("sighdlr: shutting down services")
for i, service := range h.services {
Expand All @@ -52,19 +99,20 @@ func (h *signalHandler) shutdown() {
}
}

log.Info("sighdlr: shutting down adguard home")

os.Exit(status)
return status
}

// newSignalHandler returns a new signalHandler that shuts down svcs.
func newSignalHandler(svcs ...agh.Service) (h *signalHandler) {
func newSignalHandler(confFile string, start time.Time, svcs ...agh.Service) (h *signalHandler) {
h = &signalHandler{
signal: make(chan os.Signal, 1),
confFile: confFile,
start: start,
services: svcs,
}

aghos.NotifyShutdownSignal(h.signal)
aghos.NotifyReconfigureSignal(h.signal)

return h
}
Loading

0 comments on commit f5602d9

Please sign in to comment.