diff --git a/internal/aghos/filewalker_internal_test.go b/internal/aghos/filewalker_internal_test.go index bb162812..732afc9b 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 b39ecbbd..26201df2 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 da8ee912..7e04f0c0 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 c79a603f..d22c1fdd 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 7aae35ee..ea919889 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 bd2c0823..9141d132 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 212da4d6..52855524 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[ConfigType any] struct { + EmptyService + + Conf ConfigType +} + +// 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 5b329abf..d2cc9c80 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/configmgr" + "github.com/AdguardTeam/AdGuardHome/internal/version" "github.com/AdguardTeam/golibs/log" ) @@ -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, - }) + // TODO(a.garipov): Set up configuration file name. + const confFile = "AdGuardHome.1.yaml" - err := web.Start() + 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() diff --git a/internal/next/cmd/signal.go b/internal/next/cmd/signal.go index 122f3f2c..640d090b 100644 --- a/internal/next/cmd/signal.go +++ b/internal/next/cmd/signal.go @@ -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 } @@ -24,12 +32,51 @@ 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 @@ -37,11 +84,11 @@ const ( ) // 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 { @@ -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 } diff --git a/internal/next/configmgr/config.go b/internal/next/configmgr/config.go new file mode 100644 index 00000000..d11d8c1a --- /dev/null +++ b/internal/next/configmgr/config.go @@ -0,0 +1,40 @@ +package configmgr + +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/configmgr/configmgr.go b/internal/next/configmgr/configmgr.go new file mode 100644 index 00000000..5b042274 --- /dev/null +++ b/internal/next/configmgr/configmgr.go @@ -0,0 +1,205 @@ +// Package configmgr defines the AdGuard Home on-disk configuration entities and +// configuration manager. +package configmgr + +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 + +// Manager handles full and partial changes in the configuration, persisting +// them to disk if necessary. +type Manager 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 *Manager 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 *Manager, 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 = &Manager{ + updMu: &sync.RWMutex{}, + current: conf, + fileName: fileName, + } + + // TODO(a.garipov): Get the context with the timeout from the arguments? + const assemblyTimeout = 5 * time.Second + ctx, cancel := context.WithTimeout(context.Background(), assemblyTimeout) + 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 *Manager) 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): Fill from config file. + 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 *Manager) DNS() (dns agh.ServiceWithConfig[*dnssvc.Config]) { + m.updMu.RLock() + defer m.updMu.RUnlock() + + return m.dns +} + +// UpdateDNS implements the [websvc.ConfigManager] interface for *Manager. The +// fields of c must not be modified after calling UpdateDNS. +func (m *Manager) 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 *Manager) 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 *Manager) Web() (web agh.ServiceWithConfig[*websvc.Config]) { + m.updMu.RLock() + defer m.updMu.RUnlock() + + return m.web +} + +// UpdateWeb implements the [websvc.ConfigManager] interface for *Manager. The +// fields of c must not be modified after calling UpdateWeb. +func (m *Manager) 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 *Manager) 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/websvc/dns_test.go b/internal/next/websvc/dns_test.go index f774c3d8..d0efec87 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 b58eecb9..c6107cd0 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 baf384da..d79be735 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 dadb4b55..3dfc63fc 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 75f7d001..05422889 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 dbce77d5..39ab3038 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 c998a611..8d993d66 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