Skip to content
New issue

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

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

Already on GitHub? # to your account

refactor: add service package and start splitting HTTP handling #1595

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion api/api_interface_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ type CacheControl interface {
FlushCaches(ctx context.Context)
}

func RegisterOpenAPIEndpoints(router chi.Router, impl StrictServerInterface) {
func registerOpenAPIEndpoints(router chi.Router, impl StrictServerInterface) {
middleware := []StrictMiddlewareFunc{ctxWithHTTPRequestMiddleware}

HandlerFromMuxWithBaseURL(NewStrictHandler(impl, middleware), router, "/api")
Expand Down
2 changes: 1 addition & 1 deletion api/api_interface_impl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ var _ = Describe("API implementation tests", func() {
Describe("RegisterOpenAPIEndpoints", func() {
It("adds routes", func() {
rtr := chi.NewRouter()
RegisterOpenAPIEndpoints(rtr, sut)
registerOpenAPIEndpoints(rtr, sut)

Expect(rtr.Routes()).ShouldNot(BeEmpty())
})
Expand Down
27 changes: 27 additions & 0 deletions api/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package api

import (
"github.com/0xERR0R/blocky/config"
"github.com/0xERR0R/blocky/service"
"github.com/0xERR0R/blocky/util"
)

// Service implements service.HTTPService.
type Service struct {
service.SimpleHTTP
}

func NewService(cfg config.APIService, server StrictServerInterface) *Service {
endpoints := util.ConcatSlices(
service.EndpointsFromAddrs(service.HTTPProtocol, cfg.Addrs.HTTP),
service.EndpointsFromAddrs(service.HTTPSProtocol, cfg.Addrs.HTTPS),
)

s := &Service{
SimpleHTTP: service.NewSimpleHTTP("API", endpoints),
}

registerOpenAPIEndpoints(s.Router(), server)

return s
}
43 changes: 43 additions & 0 deletions api/service_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package api

import (
"github.com/0xERR0R/blocky/config"
"github.com/0xERR0R/blocky/service"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

var _ = Describe("API Service", func() {
var (
cfg config.APIService
sut *Service
err error
)

BeforeEach(func() {
cfg, err = config.WithDefaults[config.APIService]()
Expect(err).Should(Succeed())

cfg.Addrs.HTTP = config.ListenConfig{":80"}
cfg.Addrs.HTTPS = config.ListenConfig{":443"}
})

JustBeforeEach(func() {
sut = NewService(cfg, nil)
})

Describe("NewService", func() {
When("enabled", func() {
It("uses the configured addresses", func() {
Expect(sut.ExposeOn()).Should(ContainElements(
Equal(service.Endpoint{Protocol: "http", AddrConf: ":80"}),
Equal(service.Endpoint{Protocol: "https", AddrConf: ":443"}),
))
})

It("sets up routes", func() {
Expect(sut.Router().Routes()).ShouldNot(BeEmpty())
})
})
})
})
5 changes: 5 additions & 0 deletions cmd/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"github.com/0xERR0R/blocky/config"
"github.com/0xERR0R/blocky/evt"
"github.com/0xERR0R/blocky/log"
"github.com/0xERR0R/blocky/metrics"
"github.com/0xERR0R/blocky/server"
"github.com/0xERR0R/blocky/util"

Expand Down Expand Up @@ -49,6 +50,10 @@
ctx, cancelFn := context.WithCancel(context.Background())
defer cancelFn()

if cfg.Prometheus.Enable {
metrics.StartCollection()

Check warning on line 54 in cmd/serve.go

View check run for this annotation

Codecov / codecov/patch

cmd/serve.go#L54

Added line #L54 was not covered by tests
}

srv, err := server.NewServer(ctx, cfg)
if err != nil {
return fmt.Errorf("can't start server: %w", err)
Expand Down
31 changes: 31 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@
Redis Redis `yaml:"redis"`
Log log.Config `yaml:"log"`
Ports Ports `yaml:"ports"`
Services Services `yaml:"-"` // not user exposed yet
MinTLSServeVer TLSVersion `yaml:"minTlsServeVersion" default:"1.2"`
CertFile string `yaml:"certFile"`
KeyFile string `yaml:"keyFile"`
Expand Down Expand Up @@ -263,6 +264,19 @@
} `yaml:",inline"`
}

// Services holds network service related configuration.
//
// The actual config layout is not decided yet.
// See https://github.com/0xERR0R/blocky/issues/1206
//
// The `yaml` struct tags are just for manual testing,
// and require replacing `yaml:"-"` in Config to work.
type Services struct {
API APIService `yaml:"control-api"`
DoH DoHService `yaml:"dns-over-https"`
Metrics MetricsService `yaml:"metrics"`
}

type Ports struct {
DNS ListenConfig `yaml:"dns" default:"53"`
HTTP ListenConfig `yaml:"http"`
Expand Down Expand Up @@ -601,6 +615,23 @@
cfg.Upstreams.validate(logger)
}

// CopyPortsToServices sets Services values to match Ports.
//
// This should be replaced with a migration once everything from Ports is supported in Services.
// Done this way for now to avoid creating temporary generic services and updating all Ports related code at once.
func (cfg *Config) CopyPortsToServices() {
httpAddrs := AllHTTPAddrs{
HTTPAddrs: HTTPAddrs{HTTP: cfg.Ports.HTTP},
HTTPSAddrs: HTTPSAddrs{HTTPS: cfg.Ports.HTTPS},

Check warning on line 625 in config/config.go

View check run for this annotation

Codecov / codecov/patch

config/config.go#L622-L625

Added lines #L622 - L625 were not covered by tests
}

cfg.Services = Services{
API: APIService{Addrs: httpAddrs},
DoH: DoHService{Addrs: httpAddrs},
Metrics: MetricsService{Addrs: httpAddrs},

Check warning on line 631 in config/config.go

View check run for this annotation

Codecov / codecov/patch

config/config.go#L628-L631

Added lines #L628 - L631 were not covered by tests
}
}

// ConvertPort converts string representation into a valid port (0 - 65535)
func ConvertPort(in string) (uint16, error) {
const (
Expand Down
25 changes: 25 additions & 0 deletions config/http_services.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package config

type (
APIService HTTPService
DoHService HTTPService
MetricsService HTTPService
)

// HTTPService can be used by any service that uses HTTP(S).
type HTTPService struct {
Addrs AllHTTPAddrs `yaml:"addrs"`
}

type AllHTTPAddrs struct {
HTTPAddrs `yaml:",inline"`
HTTPSAddrs `yaml:",inline"`
}

type HTTPAddrs struct {
HTTP ListenConfig `yaml:"http"`
}

type HTTPSAddrs struct {
HTTPS ListenConfig `yaml:"https"`
}
60 changes: 60 additions & 0 deletions helpertest/tls.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package helpertest

import (
"crypto/tls"
"crypto/x509"
"sync"

"github.com/0xERR0R/blocky/util"
. "github.com/onsi/gomega"
)

const tlsTestServerName = "test.blocky.invalid"

type tlsData struct {
ServerCfg *tls.Config
ClientCfg *tls.Config
}

// Lazy init
//
//nolint:gochecknoglobals
var (
initTLSData sync.Once
tlsDataStorage tlsData
)

func getTLSData() tlsData {
initTLSData.Do(func() {
cert, err := util.TLSGenerateSelfSignedCert([]string{tlsTestServerName})
Expect(err).Should(Succeed())

tlsDataStorage.ServerCfg = &tls.Config{
Certificates: []tls.Certificate{cert},
MinVersion: tls.VersionTLS13,
}

certPool := x509.NewCertPool()
certPool.AddCert(cert.Leaf)

tlsDataStorage.ClientCfg = &tls.Config{
RootCAs: certPool,
ServerName: tlsTestServerName,
MinVersion: tls.VersionTLS13,
}
})

return tlsDataStorage
}

// TLSTestServerConfig returns a TLS Config for use by test servers.
func TLSTestServerConfig() *tls.Config {
return getTLSData().ServerCfg.Clone()
}

// TLSTestServerConfig returns a TLS Config for use by test clients.
//
// This is required to connect to a test TLS server, otherwise TLS verification fails.
func TLSTestClientConfig() *tls.Config {
return getTLSData().ClientCfg.Clone()
}
17 changes: 5 additions & 12 deletions metrics/metrics.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
package metrics

import (
"github.com/0xERR0R/blocky/config"

"github.com/go-chi/chi/v5"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus/promhttp"
)

//nolint:gochecknoglobals
Expand All @@ -17,12 +13,9 @@
_ = Reg.Register(c)
}

// Start starts prometheus endpoint
func Start(router *chi.Mux, cfg config.Metrics) {
if cfg.Enable {
_ = Reg.Register(collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}))
_ = Reg.Register(collectors.NewGoCollector())
router.Handle(cfg.Path, promhttp.InstrumentMetricHandler(Reg,
promhttp.HandlerFor(Reg, promhttp.HandlerOpts{})))
}
func StartCollection() {
_ = Reg.Register(collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}))
_ = Reg.Register(collectors.NewGoCollector())

Check warning on line 18 in metrics/metrics.go

View check run for this annotation

Codecov / codecov/patch

metrics/metrics.go#L16-L18

Added lines #L16 - L18 were not covered by tests

registerEventListeners()

Check warning on line 20 in metrics/metrics.go

View check run for this annotation

Codecov / codecov/patch

metrics/metrics.go#L20

Added line #L20 was not covered by tests
}
4 changes: 2 additions & 2 deletions metrics/metrics_event_publisher.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
"github.com/prometheus/client_golang/prometheus"
)

// RegisterEventListeners registers all metric handlers by the event bus
func RegisterEventListeners() {
// registerEventListeners registers all metric handlers on the event bus
func registerEventListeners() {

Check warning on line 15 in metrics/metrics_event_publisher.go

View check run for this annotation

Codecov / codecov/patch

metrics/metrics_event_publisher.go#L15

Added line #L15 was not covered by tests
registerBlockingEventListeners()
registerCachingEventListeners()
registerApplicationEventListeners()
Expand Down
36 changes: 36 additions & 0 deletions metrics/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package metrics

import (
"github.com/0xERR0R/blocky/config"
"github.com/0xERR0R/blocky/service"
"github.com/0xERR0R/blocky/util"
"github.com/prometheus/client_golang/prometheus/promhttp"
)

// Service implements service.HTTPService.
type Service struct {
service.SimpleHTTP
}

func NewService(cfg config.MetricsService, metricsCfg config.Metrics) *Service {
endpoints := util.ConcatSlices(
service.EndpointsFromAddrs(service.HTTPProtocol, cfg.Addrs.HTTP),
service.EndpointsFromAddrs(service.HTTPSProtocol, cfg.Addrs.HTTPS),
)

if !metricsCfg.Enable || len(endpoints) == 0 {
// Avoid setting up collectors and listeners
return new(Service)
}

s := &Service{
SimpleHTTP: service.NewSimpleHTTP("Metrics", endpoints),
}

s.Router().Handle(
metricsCfg.Path,
promhttp.InstrumentMetricHandler(Reg, promhttp.HandlerFor(Reg, promhttp.HandlerOpts{})),
)

return s
}
Loading