From 7287a86d283489127453009267911003cea5227e Mon Sep 17 00:00:00 2001 From: Ainar Garipov Date: Fri, 7 Oct 2022 13:39:44 +0300 Subject: [PATCH] WIP all: impl dynamic reconfiguration --- internal/aghos/filewalker_internal_test.go | 6 +- internal/aghos/os.go | 10 + internal/aghos/os_unix.go | 8 + internal/aghos/os_windows.go | 8 + internal/aghtest/interface.go | 17 +- internal/aghtest/interface_test.go | 8 +- internal/next/agh/agh.go | 40 +++- internal/next/cmd/cmd.go | 33 ++-- internal/next/cmd/signal.go | 48 ++++- internal/next/svc/config.go | 40 ++++ internal/next/svc/configmanager.go | 202 +++++++++++++++++++++ internal/next/svc/svc.go | 3 + internal/next/websvc/dns_test.go | 3 +- internal/next/websvc/http.go | 3 +- internal/next/websvc/http_test.go | 3 +- internal/next/websvc/settings_test.go | 5 +- internal/next/websvc/websvc.go | 15 +- internal/next/websvc/websvc_test.go | 13 +- scripts/make/go-build.sh | 4 +- 19 files changed, 402 insertions(+), 67 deletions(-) create mode 100644 internal/next/svc/config.go create mode 100644 internal/next/svc/configmanager.go create mode 100644 internal/next/svc/svc.go diff --git a/internal/aghos/filewalker_internal_test.go b/internal/aghos/filewalker_internal_test.go index bb162812fdb..732afc9bac2 100644 --- a/internal/aghos/filewalker_internal_test.go +++ b/internal/aghos/filewalker_internal_test.go @@ -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 } diff --git a/internal/aghos/os.go b/internal/aghos/os.go index b39ecbbd857..26201df2277 100644 --- a/internal/aghos/os.go +++ b/internal/aghos/os.go @@ -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) diff --git a/internal/aghos/os_unix.go b/internal/aghos/os_unix.go index da8ee912d8c..7e04f0c0c82 100644 --- a/internal/aghos/os_unix.go +++ b/internal/aghos/os_unix.go @@ -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 diff --git a/internal/aghos/os_windows.go b/internal/aghos/os_windows.go index c79a603fd8c..d22c1fdd4db 100644 --- a/internal/aghos/os_windows.go +++ b/internal/aghos/os_windows.go @@ -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 diff --git a/internal/aghtest/interface.go b/internal/aghtest/interface.go index 7aae35ee3ee..ea91988904b 100644 --- a/internal/aghtest/interface.go +++ b/internal/aghtest/interface.go @@ -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" ) @@ -88,7 +89,7 @@ func (l *Listener) Close() (err error) { return l.OnClose() } -// Module AdGuardHome +// Module adguard-home // Package aghos @@ -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() diff --git a/internal/aghtest/interface_test.go b/internal/aghtest/interface_test.go index bd2c0823e84..9141d132c54 100644 --- a/internal/aghtest/interface_test.go +++ b/internal/aghtest/interface_test.go @@ -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. diff --git a/internal/next/agh/agh.go b/internal/next/agh/agh.go index 212da4d685b..c70207f1d0c 100644 --- a/internal/next/agh/agh.go +++ b/internal/next/agh/agh.go @@ -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" @@ -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[T any] struct { + EmptyService + + Conf T +} + +// Config implements the [ServiceWithConfig] interface for +// *EmptyServiceWithConfig. +func (s *EmptyServiceWithConfig[ConfigType]) Config() (conf ConfigType) { + return s.Conf +} diff --git a/internal/next/cmd/cmd.go b/internal/next/cmd/cmd.go index 5b329abf4a6..1d526afc172 100644 --- a/internal/next/cmd/cmd.go +++ b/internal/next/cmd/cmd.go @@ -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/svc" + "github.com/AdguardTeam/AdGuardHome/internal/version" "github.com/AdguardTeam/golibs/log" ) @@ -24,29 +25,33 @@ 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 := svc.New(confFile, start) + fatalOnError(err) + + web := confMgr.Web() + err = web.Start() + fatalOnError(err) + + dns := confMgr.DNS() + err = dns.Start() fatalOnError(err) sigHdlr := newSignalHandler( web, + dns, ) - go sigHdlr.handle() + go sigHdlr.handle(confFile, start) select {} } diff --git a/internal/next/cmd/signal.go b/internal/next/cmd/signal.go index 122f3f2c7dd..5ae35d4f37c 100644 --- a/internal/next/cmd/signal.go +++ b/internal/next/cmd/signal.go @@ -2,9 +2,11 @@ package cmd import ( "os" + "time" "github.com/AdguardTeam/AdGuardHome/internal/aghos" "github.com/AdguardTeam/AdGuardHome/internal/next/agh" + "github.com/AdguardTeam/AdGuardHome/internal/next/svc" "github.com/AdguardTeam/golibs/log" ) @@ -18,18 +20,49 @@ type signalHandler struct { } // handle processes OS signals. -func (h *signalHandler) handle() { +func (h *signalHandler) handle(confFile string, start time.Time) { defer log.OnPanic("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(confFile, start) + } else if aghos.IsShutdownSignal(sig) { + h.shutdown(true) } } } +// reconfigure rereads the configuration file and updates and restarts services. +func (h *signalHandler) reconfigure(confFile string, start time.Time) { + log.Info("sighdlr: reconfiguring adguard home") + + h.shutdown(false) + + // 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 := svc.New(confFile, 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 @@ -37,7 +70,7 @@ const ( ) // shutdown gracefully shuts down all services. -func (h *signalHandler) shutdown() { +func (h *signalHandler) shutdown(shouldExit bool) { ctx, cancel := ctxWithDefaultTimeout() defer cancel() @@ -54,7 +87,11 @@ func (h *signalHandler) shutdown() { log.Info("sighdlr: shutting down adguard home") - os.Exit(status) + if shouldExit { + log.Info("sighdlr: exiting with status %d", status) + + os.Exit(status) + } } // newSignalHandler returns a new signalHandler that shuts down svcs. @@ -65,6 +102,7 @@ func newSignalHandler(svcs ...agh.Service) (h *signalHandler) { } aghos.NotifyShutdownSignal(h.signal) + aghos.NotifyReconfigureSignal(h.signal) return h } diff --git a/internal/next/svc/config.go b/internal/next/svc/config.go new file mode 100644 index 00000000000..480995aba02 --- /dev/null +++ b/internal/next/svc/config.go @@ -0,0 +1,40 @@ +package svc + +import ( + "net/netip" + + "github.com/AdguardTeam/golibs/timeutil" +) + +// Configuration Structures + +// config is the top-level on-disk configuration structure. +type config struct { + DNS *dnsConfig `yaml:"dns"` + HTTP *httpConfig `yaml:"http"` + // TODO(a.garipov): Use. + SchemaVersion int `yaml:"schema_version"` + // TODO(a.garipov): Use. + DebugPprof bool `yaml:"debug_pprof"` + Verbose bool `yaml:"verbose"` +} + +// dnsConfig is the on-disk DNS configuration. +// +// TODO(a.garipov): Validate. +type dnsConfig struct { + Addresses []netip.AddrPort `yaml:"addresses"` + BootstrapDNS []string `yaml:"bootstrap_dns"` + UpstreamDNS []string `yaml:"upstream_dns"` + UpstreamTimeout timeutil.Duration `yaml:"upstream_timeout"` +} + +// httpConfig is the on-disk web API configuration. +// +// TODO(a.garipov): Validate. +type httpConfig struct { + Addresses []netip.AddrPort `yaml:"addresses"` + SecureAddresses []netip.AddrPort `yaml:"secure_addresses"` + Timeout timeutil.Duration `yaml:"timeout"` + ForceHTTPS bool `yaml:"force_https"` +} diff --git a/internal/next/svc/configmanager.go b/internal/next/svc/configmanager.go new file mode 100644 index 00000000000..bad74e8e799 --- /dev/null +++ b/internal/next/svc/configmanager.go @@ -0,0 +1,202 @@ +package svc + +import ( + "context" + "fmt" + "os" + "sync" + "time" + + "github.com/AdguardTeam/AdGuardHome/internal/next/agh" + "github.com/AdguardTeam/AdGuardHome/internal/next/dnssvc" + "github.com/AdguardTeam/AdGuardHome/internal/next/websvc" + "github.com/AdguardTeam/golibs/errors" + "github.com/AdguardTeam/golibs/log" + "gopkg.in/yaml.v3" +) + +// Configuration Manager + +// ConfigManager handles full and partial changes in the configuration, +// persisting them to disk if necessary. +type ConfigManager struct { + // updMu makes sure that at most one reconfiguration is performed at a time. + // updMu protects all fields below. + updMu *sync.RWMutex + + // dns is the DNS service. + dns *dnssvc.Service + + // Web is the Web API service. + web *websvc.Service + + // current is the current configuration. + current *config + + // fileName is the name of the configuration file. + fileName string +} + +// New creates a new ConfigManager that persists changes to the file pointed to +// by fileName. It reads the configuration file and populates the service +// fields. start is the startup time of AdGuard Home. +func New(fileName string, start time.Time) (m *ConfigManager, err error) { + defer func() { err = errors.Annotate(err, "reading config") }() + + conf := &config{} + f, err := os.Open(fileName) + if err != nil { + // Don't wrap the error, because it's informative enough as is. + return nil, err + } + defer func() { err = errors.WithDeferred(err, f.Close()) }() + + err = yaml.NewDecoder(f).Decode(conf) + if err != nil { + // Don't wrap the error, because it's informative enough as is. + return nil, err + } + + // TODO(a.garipov): Move into a separate function and add other logging + // settings. + if conf.Verbose { + log.SetLevel(log.DEBUG) + } + + // TODO(a.garipov): Validate the configuration structure. Return an error + // if it's incorrect. + + m = &ConfigManager{ + updMu: &sync.RWMutex{}, + current: conf, + fileName: fileName, + } + + // TODO(a.garipov): Get the context from the arguments? + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + err = m.assemble(ctx, conf, start) + if err != nil { + // Don't wrap the error, because it's informative enough as is. + return nil, err + } + + return m, nil +} + +// assemble creates all services and puts them into the corresponding fields. +// The fields of conf must not be modified after calling assemble. +func (m *ConfigManager) assemble(ctx context.Context, conf *config, start time.Time) (err error) { + dnsConf := &dnssvc.Config{ + Addresses: conf.DNS.Addresses, + BootstrapServers: conf.DNS.BootstrapDNS, + UpstreamServers: conf.DNS.UpstreamDNS, + UpstreamTimeout: conf.DNS.UpstreamTimeout.Duration, + } + err = m.updateDNS(ctx, dnsConf) + if err != nil { + return fmt.Errorf("assembling dnssvc: %w", err) + } + + webSvcConf := &websvc.Config{ + ConfigManager: m, + // TODO(a.garipov): this. + TLS: nil, + Start: start, + Addresses: conf.HTTP.Addresses, + SecureAddresses: conf.HTTP.SecureAddresses, + Timeout: conf.HTTP.Timeout.Duration, + ForceHTTPS: conf.HTTP.ForceHTTPS, + } + + err = m.updateWeb(ctx, webSvcConf) + if err != nil { + return fmt.Errorf("assembling websvc: %w", err) + } + + return nil +} + +// DNS returns the current DNS service. It is safe for concurrent use. +func (m *ConfigManager) DNS() (dns agh.ServiceWithConfig[*dnssvc.Config]) { + m.updMu.RLock() + defer m.updMu.RUnlock() + + return m.dns +} + +// UpdateDNS implements the websvc.ConfigManager interface for *ConfigManager. +// The fields of c must not be modified after calling UpdateDNS. +func (m *ConfigManager) UpdateDNS(ctx context.Context, c *dnssvc.Config) (err error) { + m.updMu.Lock() + defer m.updMu.Unlock() + + // TODO(a.garipov): Update and write the configuration file. Return an + // error if something went wrong. + + err = m.updateDNS(ctx, c) + if err != nil { + return fmt.Errorf("reassembling dnssvc: %w", err) + } + + return nil +} + +// updateDNS recreates the DNS service. m.updMu is expected to be locked. +func (m *ConfigManager) updateDNS(ctx context.Context, c *dnssvc.Config) (err error) { + if prev := m.dns; prev != nil { + err = prev.Shutdown(ctx) + if err != nil { + return fmt.Errorf("shutting down dns svc: %w", err) + } + } + + svc, err := dnssvc.New(c) + if err != nil { + return fmt.Errorf("creating dns svc: %w", err) + } + + m.dns = svc + + return nil +} + +// Web returns the current web service. It is safe for concurrent use. +func (m *ConfigManager) Web() (web agh.ServiceWithConfig[*websvc.Config]) { + m.updMu.RLock() + defer m.updMu.RUnlock() + + return m.web +} + +// UpdateWeb implements the websvc.ConfigManager interface for *ConfigManager. +// The fields of c must not be modified after calling UpdateWeb. +func (m *ConfigManager) UpdateWeb(ctx context.Context, c *websvc.Config) (err error) { + m.updMu.Lock() + defer m.updMu.Unlock() + + // TODO(a.garipov): Update and write the configuration file. Return an + // error if something went wrong. + + err = m.updateWeb(ctx, c) + if err != nil { + return fmt.Errorf("reassembling websvc: %w", err) + } + + return nil +} + +// updateWeb recreates the web service. m.upd is expected to be locked. +func (m *ConfigManager) updateWeb(ctx context.Context, c *websvc.Config) (err error) { + if prev := m.web; prev != nil { + err = prev.Shutdown(ctx) + if err != nil { + return fmt.Errorf("shutting down web svc: %w", err) + } + } + + m.web = websvc.New(c) + + return nil +} diff --git a/internal/next/svc/svc.go b/internal/next/svc/svc.go new file mode 100644 index 00000000000..6c98d73492e --- /dev/null +++ b/internal/next/svc/svc.go @@ -0,0 +1,3 @@ +// Package svc defines the AdGuard Home service locator, assembler, as well as +// on-disk configuration manager. +package svc diff --git a/internal/next/websvc/dns_test.go b/internal/next/websvc/dns_test.go index f774c3d87dd..d0efec8734a 100644 --- a/internal/next/websvc/dns_test.go +++ b/internal/next/websvc/dns_test.go @@ -11,6 +11,7 @@ import ( "time" "github.com/AdguardTeam/AdGuardHome/internal/aghtest" + "github.com/AdguardTeam/AdGuardHome/internal/next/agh" "github.com/AdguardTeam/AdGuardHome/internal/next/dnssvc" "github.com/AdguardTeam/AdGuardHome/internal/next/websvc" "github.com/stretchr/testify/assert" @@ -28,7 +29,7 @@ func TestService_HandlePatchSettingsDNS(t *testing.T) { // TODO(a.garipov): Use [atomic.Bool] in Go 1.19. var numStarted uint64 confMgr := newConfigManager() - confMgr.onDNS = func() (s websvc.ServiceWithConfig[*dnssvc.Config]) { + confMgr.onDNS = func() (s agh.ServiceWithConfig[*dnssvc.Config]) { return &aghtest.ServiceWithConfig[*dnssvc.Config]{ OnStart: func() (err error) { atomic.AddUint64(&numStarted, 1) diff --git a/internal/next/websvc/http.go b/internal/next/websvc/http.go index b58eecb9499..c6107cd0501 100644 --- a/internal/next/websvc/http.go +++ b/internal/next/websvc/http.go @@ -8,6 +8,7 @@ import ( "net/netip" "time" + "github.com/AdguardTeam/AdGuardHome/internal/next/agh" "github.com/AdguardTeam/golibs/log" ) @@ -89,7 +90,7 @@ func (svc *Service) handlePatchSettingsHTTP(w http.ResponseWriter, r *http.Reque // TODO(a.garipov): Consider better ways to do this. const maxUpdDur = 10 * time.Second updStart := time.Now() - var newSvc ServiceWithConfig[*Config] + var newSvc agh.ServiceWithConfig[*Config] for newSvc = svc.confMgr.Web(); newSvc == svc; { if time.Since(updStart) >= maxUpdDur { log.Error("websvc: failed to update svc after %s", maxUpdDur) diff --git a/internal/next/websvc/http_test.go b/internal/next/websvc/http_test.go index baf384da296..d79be735d88 100644 --- a/internal/next/websvc/http_test.go +++ b/internal/next/websvc/http_test.go @@ -10,6 +10,7 @@ import ( "testing" "time" + "github.com/AdguardTeam/AdGuardHome/internal/next/agh" "github.com/AdguardTeam/AdGuardHome/internal/next/websvc" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -24,7 +25,7 @@ func TestService_HandlePatchSettingsHTTP(t *testing.T) { } confMgr := newConfigManager() - confMgr.onWeb = func() (s websvc.ServiceWithConfig[*websvc.Config]) { + confMgr.onWeb = func() (s agh.ServiceWithConfig[*websvc.Config]) { return websvc.New(&websvc.Config{ TLS: &tls.Config{ Certificates: []tls.Certificate{{}}, diff --git a/internal/next/websvc/settings_test.go b/internal/next/websvc/settings_test.go index dadb4b55ea3..3dfc63fc8a2 100644 --- a/internal/next/websvc/settings_test.go +++ b/internal/next/websvc/settings_test.go @@ -9,6 +9,7 @@ import ( "testing" "time" + "github.com/AdguardTeam/AdGuardHome/internal/next/agh" "github.com/AdguardTeam/AdGuardHome/internal/next/dnssvc" "github.com/AdguardTeam/AdGuardHome/internal/next/websvc" "github.com/stretchr/testify/assert" @@ -33,7 +34,7 @@ func TestService_HandleGetSettingsAll(t *testing.T) { } confMgr := newConfigManager() - confMgr.onDNS = func() (s websvc.ServiceWithConfig[*dnssvc.Config]) { + confMgr.onDNS = func() (s agh.ServiceWithConfig[*dnssvc.Config]) { c, err := dnssvc.New(&dnssvc.Config{ Addresses: wantDNS.Addresses, UpstreamServers: wantDNS.UpstreamServers, @@ -45,7 +46,7 @@ func TestService_HandleGetSettingsAll(t *testing.T) { return c } - confMgr.onWeb = func() (s websvc.ServiceWithConfig[*websvc.Config]) { + confMgr.onWeb = func() (s agh.ServiceWithConfig[*websvc.Config]) { return websvc.New(&websvc.Config{ TLS: &tls.Config{ Certificates: []tls.Certificate{{}}, diff --git a/internal/next/websvc/websvc.go b/internal/next/websvc/websvc.go index 75f7d001f69..054228897a1 100644 --- a/internal/next/websvc/websvc.go +++ b/internal/next/websvc/websvc.go @@ -24,21 +24,10 @@ import ( httptreemux "github.com/dimfeld/httptreemux/v5" ) -// ServiceWithConfig is an extension of the [agh.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 { - agh.Service - - Config() (c ConfigType) -} - // ConfigManager is the configuration manager interface. type ConfigManager interface { - DNS() (svc ServiceWithConfig[*dnssvc.Config]) - Web() (svc ServiceWithConfig[*Config]) + DNS() (svc agh.ServiceWithConfig[*dnssvc.Config]) + Web() (svc agh.ServiceWithConfig[*Config]) UpdateDNS(ctx context.Context, c *dnssvc.Config) (err error) UpdateWeb(ctx context.Context, c *Config) (err error) diff --git a/internal/next/websvc/websvc_test.go b/internal/next/websvc/websvc_test.go index dbce77d58a2..39ab30389df 100644 --- a/internal/next/websvc/websvc_test.go +++ b/internal/next/websvc/websvc_test.go @@ -12,6 +12,7 @@ import ( "time" "github.com/AdguardTeam/AdGuardHome/internal/aghtest" + "github.com/AdguardTeam/AdGuardHome/internal/next/agh" "github.com/AdguardTeam/AdGuardHome/internal/next/dnssvc" "github.com/AdguardTeam/AdGuardHome/internal/next/websvc" "github.com/AdguardTeam/golibs/testutil" @@ -34,20 +35,20 @@ var _ websvc.ConfigManager = (*configManager)(nil) // configManager is a [websvc.ConfigManager] for tests. type configManager struct { - onDNS func() (svc websvc.ServiceWithConfig[*dnssvc.Config]) - onWeb func() (svc websvc.ServiceWithConfig[*websvc.Config]) + onDNS func() (svc agh.ServiceWithConfig[*dnssvc.Config]) + onWeb func() (svc agh.ServiceWithConfig[*websvc.Config]) onUpdateDNS func(ctx context.Context, c *dnssvc.Config) (err error) onUpdateWeb func(ctx context.Context, c *websvc.Config) (err error) } // DNS implements the [websvc.ConfigManager] interface for *configManager. -func (m *configManager) DNS() (svc websvc.ServiceWithConfig[*dnssvc.Config]) { +func (m *configManager) DNS() (svc agh.ServiceWithConfig[*dnssvc.Config]) { return m.onDNS() } // Web implements the [websvc.ConfigManager] interface for *configManager. -func (m *configManager) Web() (svc websvc.ServiceWithConfig[*websvc.Config]) { +func (m *configManager) Web() (svc agh.ServiceWithConfig[*websvc.Config]) { return m.onWeb() } @@ -64,8 +65,8 @@ func (m *configManager) UpdateWeb(ctx context.Context, c *websvc.Config) (err er // newConfigManager returns a *configManager all methods of which panic. func newConfigManager() (m *configManager) { return &configManager{ - onDNS: func() (svc websvc.ServiceWithConfig[*dnssvc.Config]) { panic("not implemented") }, - onWeb: func() (svc websvc.ServiceWithConfig[*websvc.Config]) { panic("not implemented") }, + onDNS: func() (svc agh.ServiceWithConfig[*dnssvc.Config]) { panic("not implemented") }, + onWeb: func() (svc agh.ServiceWithConfig[*websvc.Config]) { panic("not implemented") }, onUpdateDNS: func(_ context.Context, _ *dnssvc.Config) (err error) { panic("not implemented") }, diff --git a/scripts/make/go-build.sh b/scripts/make/go-build.sh index c998a61168d..8d993d66fb5 100644 --- a/scripts/make/go-build.sh +++ b/scripts/make/go-build.sh @@ -124,11 +124,11 @@ GO111MODULE='on' export CGO_ENABLED GO111MODULE # Build the new binary if requested. -if [ "${V1API:-0}" -eq '0' ] +if [ "${NEXTAPI:-0}" -eq '0' ] then tags_flags='--tags=' else - tags_flags='--tags=v1' + tags_flags='--tags=next' fi readonly tags_flags