Restructure config and setup of handlers
- move initialization and dependency resolving to registration of protocol handlers - replace file opening in HTTP mock with fs.FS - depends on having a FS that returns an io.ReadSeeker - could be worked around in the future - update config for new routing rules to see how they look like in production - extend grammar tests
This commit is contained in:
parent
45a6d11d5b
commit
9f53b01e49
19 changed files with 248 additions and 215 deletions
|
@ -10,27 +10,18 @@ import (
|
||||||
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/app"
|
"gitlab.com/inetmock/inetmock/internal/app"
|
||||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
||||||
dns "gitlab.com/inetmock/inetmock/internal/endpoint/handler/dns/mock"
|
|
||||||
http "gitlab.com/inetmock/inetmock/internal/endpoint/handler/http/mock"
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/endpoint/handler/http/proxy"
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/endpoint/handler/metrics"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/cert"
|
"gitlab.com/inetmock/inetmock/pkg/cert"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
serverApp app.App
|
serverApp app.App
|
||||||
cfg appConfig
|
cfg appConfig
|
||||||
registrations = []endpoint.Registration{
|
|
||||||
http.AddHTTPMock,
|
|
||||||
dns.AddDNSMock,
|
|
||||||
proxy.AddHTTPProxy,
|
|
||||||
metrics.AddMetricsExporter,
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Data struct {
|
type Data struct {
|
||||||
PCAP string
|
PCAP string
|
||||||
Audit string
|
Audit string
|
||||||
|
FakeFiles string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Data) setup() (err error) {
|
func (d *Data) setup() (err error) {
|
||||||
|
|
|
@ -1,10 +1,17 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
||||||
|
dnsmock "gitlab.com/inetmock/inetmock/internal/endpoint/handler/dns/mock"
|
||||||
|
"gitlab.com/inetmock/inetmock/internal/endpoint/handler/http/mock"
|
||||||
|
"gitlab.com/inetmock/inetmock/internal/endpoint/handler/http/proxy"
|
||||||
|
"gitlab.com/inetmock/inetmock/internal/endpoint/handler/metrics"
|
||||||
"gitlab.com/inetmock/inetmock/internal/pcap"
|
"gitlab.com/inetmock/inetmock/internal/pcap"
|
||||||
audit2 "gitlab.com/inetmock/inetmock/internal/pcap/consumers/audit"
|
audit2 "gitlab.com/inetmock/inetmock/internal/pcap/consumers/audit"
|
||||||
"gitlab.com/inetmock/inetmock/internal/rpc"
|
"gitlab.com/inetmock/inetmock/internal/rpc"
|
||||||
|
@ -39,12 +46,7 @@ func startINetMock(_ *cobra.Command, _ []string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, registration := range registrations {
|
fakeFileFS := os.DirFS(cfg.Data.FakeFiles)
|
||||||
if err = registration(registry); err != nil {
|
|
||||||
appLogger.Error("Failed to run registration", zap.Error(err))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var certStore cert.Store
|
var certStore cert.Store
|
||||||
if certStore, err = cert.NewDefaultStore(cfg.TLS, appLogger.Named("CertStore")); err != nil {
|
if certStore, err = cert.NewDefaultStore(cfg.TLS, appLogger.Named("CertStore")); err != nil {
|
||||||
|
@ -57,8 +59,12 @@ func startINetMock(_ *cobra.Command, _ []string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err = setupEndpointHandlers(registry, appLogger, eventStream, certStore, fakeFileFS); err != nil {
|
||||||
|
appLogger.Error("Failed to run registration", zap.Error(err))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
var endpointOrchestrator = endpoint.NewOrchestrator(
|
var endpointOrchestrator = endpoint.NewOrchestrator(
|
||||||
serverApp.Context(),
|
|
||||||
certStore,
|
certStore,
|
||||||
registry,
|
registry,
|
||||||
eventStream,
|
eventStream,
|
||||||
|
@ -86,7 +92,7 @@ func startINetMock(_ *cobra.Command, _ []string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
errChan := endpointOrchestrator.StartEndpoints()
|
errChan := endpointOrchestrator.StartEndpoints(serverApp.Context())
|
||||||
if err := rpcAPI.StartServer(); err != nil {
|
if err := rpcAPI.StartServer(); err != nil {
|
||||||
serverApp.Shutdown()
|
serverApp.Shutdown()
|
||||||
appLogger.Error(
|
appLogger.Error(
|
||||||
|
@ -148,6 +154,22 @@ func setupEventStream(appLogger logging.Logger) (audit.EventStream, error) {
|
||||||
return evenStream, nil
|
return evenStream, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setupEndpointHandlers(registry endpoint.HandlerRegistry, logger logging.Logger, emitter audit.Emitter, store cert.Store, fakeFileFS fs.FS) (err error) {
|
||||||
|
if err = mock.AddHTTPMock(registry, logger, emitter, fakeFileFS); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = proxy.AddHTTPProxy(registry, logger, emitter, store); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = dnsmock.AddDNSMock(registry, logger, emitter); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = metrics.AddMetricsExporter(registry, logger); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
//nolint:deadcode
|
//nolint:deadcode
|
||||||
func startAuditConsumer(eventStream audit.EventStream) error {
|
func startAuditConsumer(eventStream audit.EventStream) error {
|
||||||
recorder := pcap.NewRecorder()
|
recorder := pcap.NewRecorder()
|
||||||
|
|
55
config.yaml
55
config.yaml
|
@ -1,45 +1,19 @@
|
||||||
x-response-rules: &httpResponseRules
|
x-response-rules: &httpResponseRules
|
||||||
rules:
|
rules:
|
||||||
- pattern: ".*\\.(?i)exe"
|
- PathPattern("") => File("sample.exe")
|
||||||
matcher: Path
|
- Header("Accept", "application/octet-stream") => File("sample.exe")
|
||||||
- pattern: "^application/octet-stream$"
|
- Header("Accept", "image/jpeg") => File("default.jpg")
|
||||||
target: Accept
|
- PathPattern(".*\\.(?i)(jpg|jpeg)") => File("default.jpg")
|
||||||
matcher: Header
|
- Header("Accept", "image/png") => File("default.png")
|
||||||
response: ./assets/fakeFiles/sample.exe
|
- PathPattern(".*\\.(?i)png") => File("default.png")
|
||||||
- pattern: "^image/jpeg$"
|
- Header("Accept", "image/gif") => File("default.gif")
|
||||||
target: Accept
|
- PathPattern(".*\\.(?i)gif") => File("default.gif")
|
||||||
matcher: Header
|
- Header("Accept", "image/x-icon") => File("default.ico")
|
||||||
response: ./assets/fakeFiles/default.jpg
|
- PathPattern(".*\\.(?i)ico") => File("default.ico")
|
||||||
- pattern: ".*\\.(?i)(jpg|jpeg)"
|
- Header("Accept", "text/plain") => File("default.txt")
|
||||||
matcher: Path
|
- PathPattern(".*\\.(?i)txt") => File("default.txt")
|
||||||
response: ./assets/fakeFiles/default.jpg
|
- Header("Accept", "text/html") => File("default.html")
|
||||||
- pattern: "^image/png$"
|
- PathPattern(".*") => File("default.html")
|
||||||
target: Accept
|
|
||||||
matcher: Header
|
|
||||||
response: ./assets/fakeFiles/default.png
|
|
||||||
- pattern: ".*\\.(?i)png"
|
|
||||||
matcher: Path
|
|
||||||
response: ./assets/fakeFiles/default.png
|
|
||||||
- pattern: ".*\\.(?i)gif"
|
|
||||||
matcher: Path
|
|
||||||
response: ./assets/fakeFiles/default.gif
|
|
||||||
- pattern: ".*\\.(?i)ico"
|
|
||||||
matcher: Path
|
|
||||||
response: ./assets/fakeFiles/default.ico
|
|
||||||
- pattern: "^text/plain$"
|
|
||||||
target: Accept
|
|
||||||
matcher: Header
|
|
||||||
response: ./assets/fakeFiles/default.txt
|
|
||||||
- pattern: ".*\\.(?i)txt"
|
|
||||||
matcher: Path
|
|
||||||
response: ./assets/fakeFiles/default.txt
|
|
||||||
- pattern: "^text/html$"
|
|
||||||
target: Accept
|
|
||||||
matcher: Header
|
|
||||||
response: ./assets/fakeFiles/default.html
|
|
||||||
- pattern: ".*"
|
|
||||||
matcher: Path
|
|
||||||
response: ./assets/fakeFiles/default.html
|
|
||||||
|
|
||||||
x-http-handlers: &httpHandlers
|
x-http-handlers: &httpHandlers
|
||||||
endpoints:
|
endpoints:
|
||||||
|
@ -57,6 +31,7 @@ x-http-handlers: &httpHandlers
|
||||||
data:
|
data:
|
||||||
pcap: /var/lib/inetmock/data/pcap
|
pcap: /var/lib/inetmock/data/pcap
|
||||||
audit: /var/lib/inetmock/data/audit
|
audit: /var/lib/inetmock/data/audit
|
||||||
|
fakeFiles: ./assets/fakeFiles
|
||||||
|
|
||||||
api:
|
api:
|
||||||
listen: unix:///var/run/inetmock.sock
|
listen: unix:///var/run/inetmock.sock
|
||||||
|
|
|
@ -6,24 +6,16 @@ import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/soheilhy/cmux"
|
"github.com/soheilhy/cmux"
|
||||||
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/cert"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Lifecycle interface {
|
type Lifecycle interface {
|
||||||
Name() string
|
Name() string
|
||||||
Logger() logging.Logger
|
|
||||||
CertStore() cert.Store
|
|
||||||
Audit() audit.Emitter
|
|
||||||
Context() context.Context
|
|
||||||
Uplink() Uplink
|
Uplink() Uplink
|
||||||
UnmarshalOptions(cfg interface{}) error
|
UnmarshalOptions(cfg interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProtocolHandler interface {
|
type ProtocolHandler interface {
|
||||||
Start(ctx Lifecycle) error
|
Start(ctx context.Context, lifecycle Lifecycle) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type MultiplexHandler interface {
|
type MultiplexHandler interface {
|
||||||
|
|
|
@ -5,6 +5,8 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -17,19 +19,19 @@ type Endpoint struct {
|
||||||
uplink Uplink
|
uplink Uplink
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Endpoint) Start(lifecycle Lifecycle) (err error) {
|
func (e *Endpoint) Start(ctx context.Context, logger logging.Logger, lifecycle Lifecycle) (err error) {
|
||||||
startupResult := make(chan error)
|
startupResult := make(chan error)
|
||||||
ctx, cancel := context.WithTimeout(lifecycle.Context(), startupTimeoutDuration)
|
ctx, cancel := context.WithTimeout(ctx, startupTimeoutDuration)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
lifecycle.Logger().Fatal("Startup error recovered", zap.Any("recovered", r))
|
logger.Fatal("Startup error recovered", zap.Any("recovered", r))
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
startupResult <- e.Handler.Start(lifecycle)
|
startupResult <- e.Handler.Start(ctx, lifecycle)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
||||||
|
"gitlab.com/inetmock/inetmock/pkg/audit"
|
||||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -15,17 +16,18 @@ const shutdownTimeout = 100 * time.Millisecond
|
||||||
|
|
||||||
type dnsHandler struct {
|
type dnsHandler struct {
|
||||||
logger logging.Logger
|
logger logging.Logger
|
||||||
|
emitter audit.Emitter
|
||||||
dnsServer *dns.Server
|
dnsServer *dns.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dnsHandler) Start(lifecycle endpoint.Lifecycle) error {
|
func (d *dnsHandler) Start(ctx context.Context, lifecycle endpoint.Lifecycle) error {
|
||||||
var err error
|
var err error
|
||||||
var options dnsOptions
|
var options dnsOptions
|
||||||
if options, err = loadFromConfig(lifecycle); err != nil {
|
if options, err = loadFromConfig(lifecycle); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger = lifecycle.Logger().With(
|
d.logger = d.logger.With(
|
||||||
zap.String("handler_name", lifecycle.Name()),
|
zap.String("handler_name", lifecycle.Name()),
|
||||||
zap.String("address", lifecycle.Uplink().Addr().String()),
|
zap.String("address", lifecycle.Uplink().Addr().String()),
|
||||||
)
|
)
|
||||||
|
@ -33,8 +35,8 @@ func (d *dnsHandler) Start(lifecycle endpoint.Lifecycle) error {
|
||||||
handler := ®exHandler{
|
handler := ®exHandler{
|
||||||
handlerName: lifecycle.Name(),
|
handlerName: lifecycle.Name(),
|
||||||
fallback: options.Fallback,
|
fallback: options.Fallback,
|
||||||
logger: lifecycle.Logger(),
|
logger: d.logger,
|
||||||
auditEmitter: lifecycle.Audit(),
|
auditEmitter: d.emitter,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, rule := range options.Rules {
|
for _, rule := range options.Rules {
|
||||||
|
@ -59,6 +61,7 @@ func (d *dnsHandler) Start(lifecycle endpoint.Lifecycle) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
go d.startServer()
|
go d.startServer()
|
||||||
|
go d.shutdownOnEnd(ctx)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@ import (
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
||||||
|
"gitlab.com/inetmock/inetmock/pkg/audit"
|
||||||
|
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||||
"gitlab.com/inetmock/inetmock/pkg/metrics"
|
"gitlab.com/inetmock/inetmock/pkg/metrics"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -21,7 +23,7 @@ var (
|
||||||
initLock sync.Locker = new(sync.Mutex)
|
initLock sync.Locker = new(sync.Mutex)
|
||||||
)
|
)
|
||||||
|
|
||||||
func AddDNSMock(registry endpoint.HandlerRegistry) error {
|
func AddDNSMock(registry endpoint.HandlerRegistry, logger logging.Logger, emitter audit.Emitter) error {
|
||||||
initLock.Lock()
|
initLock.Lock()
|
||||||
defer initLock.Unlock()
|
defer initLock.Unlock()
|
||||||
|
|
||||||
|
@ -61,7 +63,10 @@ func AddDNSMock(registry endpoint.HandlerRegistry) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
registry.RegisterHandler(name, func() endpoint.ProtocolHandler {
|
registry.RegisterHandler(name, func() endpoint.ProtocolHandler {
|
||||||
return &dnsHandler{}
|
return &dnsHandler{
|
||||||
|
logger: logger,
|
||||||
|
emitter: emitter,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -3,6 +3,7 @@ package mock
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"io/fs"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
@ -11,6 +12,7 @@ import (
|
||||||
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
||||||
imHttp "gitlab.com/inetmock/inetmock/internal/endpoint/handler/http"
|
imHttp "gitlab.com/inetmock/inetmock/internal/endpoint/handler/http"
|
||||||
|
"gitlab.com/inetmock/inetmock/pkg/audit"
|
||||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -21,16 +23,18 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type httpHandler struct {
|
type httpHandler struct {
|
||||||
logger logging.Logger
|
logger logging.Logger
|
||||||
server *http.Server
|
fakeFileFS fs.FS
|
||||||
|
server *http.Server
|
||||||
|
emitter audit.Emitter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *httpHandler) Matchers() []cmux.Matcher {
|
func (p *httpHandler) Matchers() []cmux.Matcher {
|
||||||
return []cmux.Matcher{cmux.HTTP1()}
|
return []cmux.Matcher{cmux.HTTP1()}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *httpHandler) Start(lifecycle endpoint.Lifecycle) error {
|
func (p *httpHandler) Start(ctx context.Context, lifecycle endpoint.Lifecycle) error {
|
||||||
p.logger = lifecycle.Logger().With(
|
p.logger = p.logger.With(
|
||||||
zap.String("protocol_handler", name),
|
zap.String("protocol_handler", name),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -45,9 +49,10 @@ func (p *httpHandler) Start(lifecycle endpoint.Lifecycle) error {
|
||||||
)
|
)
|
||||||
|
|
||||||
router := &RegexHandler{
|
router := &RegexHandler{
|
||||||
logger: p.logger,
|
|
||||||
emitter: lifecycle.Audit(),
|
|
||||||
handlerName: lifecycle.Name(),
|
handlerName: lifecycle.Name(),
|
||||||
|
logger: p.logger,
|
||||||
|
emitter: p.emitter,
|
||||||
|
fakeFileFS: p.fakeFileFS,
|
||||||
}
|
}
|
||||||
p.server = &http.Server{
|
p.server = &http.Server{
|
||||||
Handler: router,
|
Handler: router,
|
||||||
|
@ -59,7 +64,7 @@ func (p *httpHandler) Start(lifecycle endpoint.Lifecycle) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
go p.startServer(lifecycle.Uplink().Listener)
|
go p.startServer(lifecycle.Uplink().Listener)
|
||||||
go p.shutdownOnCancel(lifecycle.Context())
|
go p.shutdownOnCancel(ctx)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
package mock
|
package mock
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
|
"io/fs"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
@ -27,13 +28,15 @@ type RegexHandler struct {
|
||||||
logger logging.Logger
|
logger logging.Logger
|
||||||
routes []*route
|
routes []*route
|
||||||
emitter audit.Emitter
|
emitter audit.Emitter
|
||||||
|
fakeFileFS fs.FS
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRegexHandler(name string, logger logging.Logger, emitter audit.Emitter) *RegexHandler {
|
func NewRegexHandler(name string, logger logging.Logger, emitter audit.Emitter, fakeFileFS fs.FS) *RegexHandler {
|
||||||
return &RegexHandler{
|
return &RegexHandler{
|
||||||
handlerName: name,
|
handlerName: name,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
emitter: emitter,
|
emitter: emitter,
|
||||||
|
fakeFileFS: fakeFileFS,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,23 +71,32 @@ func (h *RegexHandler) AddRouteRule(rule TargetRule) {
|
||||||
h.Handler(rule, emittingFileHandler{
|
h.Handler(rule, emittingFileHandler{
|
||||||
emitter: h.emitter,
|
emitter: h.emitter,
|
||||||
targetPath: rule.response,
|
targetPath: rule.response,
|
||||||
|
fs: h.fakeFileFS,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type emittingFileHandler struct {
|
type emittingFileHandler struct {
|
||||||
emitter audit.Emitter
|
emitter audit.Emitter
|
||||||
targetPath string
|
targetPath string
|
||||||
|
fs fs.FS
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f emittingFileHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
func (f emittingFileHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||||
f.emitter.Emit(imHttp.EventFromRequest(request, v1.AppProtocol_APP_PROTOCOL_HTTP))
|
f.emitter.Emit(imHttp.EventFromRequest(request, v1.AppProtocol_APP_PROTOCOL_HTTP))
|
||||||
file, err := os.Open(f.targetPath)
|
file, err := f.fs.Open(f.targetPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(writer, err.Error(), 500)
|
http.Error(writer, err.Error(), 500)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var rs io.ReadSeeker
|
||||||
|
var ok bool
|
||||||
|
if rs, ok = file.(io.ReadSeeker); !ok {
|
||||||
|
http.Error(writer, "internal server error", 500)
|
||||||
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
_ = file.Close()
|
_ = file.Close()
|
||||||
}()
|
}()
|
||||||
//nolint:gosec
|
//nolint:gosec
|
||||||
http.ServeContent(writer, request, path.Base(request.RequestURI), time.Now().Add(-(time.Duration(rand.Int()) * time.Millisecond)), file)
|
http.ServeContent(writer, request, path.Base(request.RequestURI), time.Now().Add(-(time.Duration(rand.Int()) * time.Millisecond)), rs)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
package mock_test
|
package mock_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/hex"
|
|
||||||
"io"
|
"io"
|
||||||
|
"io/fs"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path/filepath"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"testing/fstest"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
"github.com/maxatome/go-testdeep/td"
|
"github.com/maxatome/go-testdeep/td"
|
||||||
|
@ -19,6 +20,19 @@ import (
|
||||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultHTMLContent = `<html>
|
||||||
|
<head>
|
||||||
|
<title>INetSim default HTML page</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p></p>
|
||||||
|
<p align="center">This is the default HTML page for INetMock HTTP mock handler.</p>
|
||||||
|
<p align="center">This file is an HTML document.</p>
|
||||||
|
</body>
|
||||||
|
</html>`
|
||||||
|
)
|
||||||
|
|
||||||
//nolint:funlen
|
//nolint:funlen
|
||||||
func TestRegexpHandler_ServeHTTP(t *testing.T) {
|
func TestRegexpHandler_ServeHTTP(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
@ -28,25 +42,32 @@ func TestRegexpHandler_ServeHTTP(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
rules []mock.TargetRule
|
rules []mock.TargetRule
|
||||||
emitterSetup func(tb testing.TB, ctrl *gomock.Controller) audit.Emitter
|
emitterSetup func(tb testing.TB, ctrl *gomock.Controller) audit.Emitter
|
||||||
|
fakeFileFS fs.FS
|
||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
req *http.Request
|
req *http.Request
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
fields fields
|
fields fields
|
||||||
args args
|
args args
|
||||||
wantErr bool
|
wantErr bool
|
||||||
wantStatus interface{}
|
wantStatus interface{}
|
||||||
wantRespHash string
|
want string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "GET /index.html",
|
name: "GET /index.html",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
rules: []mock.TargetRule{
|
rules: []mock.TargetRule{
|
||||||
mock.MustPathTargetRule(`\.(?i)(htm|html)$`, filepath.Join("testdata", "default.html")),
|
mock.MustPathTargetRule(`\.(?i)(htm|html)$`, "default.html"),
|
||||||
},
|
},
|
||||||
emitterSetup: defaultEmitter,
|
emitterSetup: defaultEmitter,
|
||||||
|
fakeFileFS: fstest.MapFS{
|
||||||
|
"default.html": &fstest.MapFile{
|
||||||
|
Data: []byte(defaultHTMLContent),
|
||||||
|
ModTime: time.Now().Add(-1337 * time.Second),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
req: &http.Request{
|
req: &http.Request{
|
||||||
|
@ -54,16 +75,22 @@ func TestRegexpHandler_ServeHTTP(t *testing.T) {
|
||||||
Method: http.MethodGet,
|
Method: http.MethodGet,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantRespHash: "c2a3f8995831dd1e79cb753619a55752692168f6cf846b07405f2070492f481c",
|
want: defaultHTMLContent,
|
||||||
wantStatus: td.Between(200, 299),
|
wantStatus: td.Between(200, 299),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "GET /profile.htm",
|
name: "GET /profile.htm",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
rules: []mock.TargetRule{
|
rules: []mock.TargetRule{
|
||||||
mock.MustPathTargetRule(`\.(?i)(htm|html)$`, filepath.Join("testdata", "default.html")),
|
mock.MustPathTargetRule(`\.(?i)(htm|html)$`, "default.html"),
|
||||||
},
|
},
|
||||||
emitterSetup: defaultEmitter,
|
emitterSetup: defaultEmitter,
|
||||||
|
fakeFileFS: fstest.MapFS{
|
||||||
|
"default.html": &fstest.MapFile{
|
||||||
|
Data: []byte(defaultHTMLContent),
|
||||||
|
ModTime: time.Now().Add(-1337 * time.Second),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
req: &http.Request{
|
req: &http.Request{
|
||||||
|
@ -71,17 +98,23 @@ func TestRegexpHandler_ServeHTTP(t *testing.T) {
|
||||||
Method: http.MethodGet,
|
Method: http.MethodGet,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantRespHash: "c2a3f8995831dd1e79cb753619a55752692168f6cf846b07405f2070492f481c",
|
want: defaultHTMLContent,
|
||||||
wantStatus: td.Between(200, 299),
|
wantStatus: td.Between(200, 299),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "GET with Accept: text/html",
|
name: "GET with Accept: text/html",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
rules: []mock.TargetRule{
|
rules: []mock.TargetRule{
|
||||||
mock.MustPathTargetRule(`\.(?i)(htm|html)$`, filepath.Join("testdata", "default.html")),
|
mock.MustPathTargetRule(`\.(?i)(htm|html)$`, "default.html"),
|
||||||
mock.MustHeaderTargetRule("Accept", "(?i)text/html", filepath.Join("testdata", "default.html")),
|
mock.MustHeaderTargetRule("Accept", "(?i)text/html", "default.html"),
|
||||||
},
|
},
|
||||||
emitterSetup: defaultEmitter,
|
emitterSetup: defaultEmitter,
|
||||||
|
fakeFileFS: fstest.MapFS{
|
||||||
|
"default.html": &fstest.MapFile{
|
||||||
|
Data: []byte(defaultHTMLContent),
|
||||||
|
ModTime: time.Now().Add(-1337 * time.Second),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
req: &http.Request{
|
req: &http.Request{
|
||||||
|
@ -92,8 +125,8 @@ func TestRegexpHandler_ServeHTTP(t *testing.T) {
|
||||||
Method: http.MethodGet,
|
Method: http.MethodGet,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantRespHash: "c2a3f8995831dd1e79cb753619a55752692168f6cf846b07405f2070492f481c",
|
want: defaultHTMLContent,
|
||||||
wantStatus: td.Between(200, 299),
|
wantStatus: td.Between(200, 299),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
|
@ -102,7 +135,7 @@ func TestRegexpHandler_ServeHTTP(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
logger := logging.CreateTestLogger(t)
|
logger := logging.CreateTestLogger(t)
|
||||||
ctrl := gomock.NewController(t)
|
ctrl := gomock.NewController(t)
|
||||||
h := mock.NewRegexHandler(t.Name(), logger, tt.fields.emitterSetup(t, ctrl))
|
h := mock.NewRegexHandler(t.Name(), logger, tt.fields.emitterSetup(t, ctrl), tt.fields.fakeFileFS)
|
||||||
|
|
||||||
for _, rule := range tt.fields.rules {
|
for _, rule := range tt.fields.rules {
|
||||||
h.AddRouteRule(rule)
|
h.AddRouteRule(rule)
|
||||||
|
@ -121,11 +154,10 @@ func TestRegexpHandler_ServeHTTP(t *testing.T) {
|
||||||
|
|
||||||
td.Cmp(t, resp.StatusCode, tt.wantStatus)
|
td.Cmp(t, resp.StatusCode, tt.wantStatus)
|
||||||
|
|
||||||
sha256Hash := sha256.New()
|
builder := new(strings.Builder)
|
||||||
_, err = io.Copy(sha256Hash, resp.Body)
|
_, err = io.Copy(builder, resp.Body)
|
||||||
td.CmpNoError(t, err)
|
td.CmpNoError(t, err)
|
||||||
computedHash := hex.EncodeToString(sha256Hash.Sum(nil))
|
td.Cmp(t, builder.String(), tt.want)
|
||||||
td.Cmp(t, computedHash, tt.wantRespHash)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
package mock
|
package mock
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/fs"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
||||||
|
"gitlab.com/inetmock/inetmock/pkg/audit"
|
||||||
|
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||||
"gitlab.com/inetmock/inetmock/pkg/metrics"
|
"gitlab.com/inetmock/inetmock/pkg/metrics"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -46,13 +49,16 @@ func InitMetrics() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddHTTPMock(registry endpoint.HandlerRegistry) (err error) {
|
func AddHTTPMock(registry endpoint.HandlerRegistry, logger logging.Logger, emitter audit.Emitter, fakeFileFS fs.FS) (err error) {
|
||||||
if err := InitMetrics(); err != nil {
|
if err := InitMetrics(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
registry.RegisterHandler(name, func() endpoint.ProtocolHandler {
|
registry.RegisterHandler(name, func() endpoint.ProtocolHandler {
|
||||||
return &httpHandler{}
|
return &httpHandler{
|
||||||
|
emitter: emitter,
|
||||||
|
logger: logger,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
|
@ -12,6 +12,8 @@ import (
|
||||||
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
||||||
imHttp "gitlab.com/inetmock/inetmock/internal/endpoint/handler/http"
|
imHttp "gitlab.com/inetmock/inetmock/internal/endpoint/handler/http"
|
||||||
|
"gitlab.com/inetmock/inetmock/pkg/audit"
|
||||||
|
"gitlab.com/inetmock/inetmock/pkg/cert"
|
||||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,16 +22,18 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type httpProxy struct {
|
type httpProxy struct {
|
||||||
logger logging.Logger
|
logger logging.Logger
|
||||||
proxy *goproxy.ProxyHttpServer
|
proxy *goproxy.ProxyHttpServer
|
||||||
server *http.Server
|
certStore cert.Store
|
||||||
|
emitter audit.Emitter
|
||||||
|
server *http.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpProxy) Matchers() []cmux.Matcher {
|
func (h *httpProxy) Matchers() []cmux.Matcher {
|
||||||
return []cmux.Matcher{cmux.HTTP1()}
|
return []cmux.Matcher{cmux.HTTP1()}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpProxy) Start(lifecycle endpoint.Lifecycle) error {
|
func (h *httpProxy) Start(ctx context.Context, lifecycle endpoint.Lifecycle) error {
|
||||||
var opts httpProxyOptions
|
var opts httpProxyOptions
|
||||||
if err := lifecycle.UnmarshalOptions(&opts); err != nil {
|
if err := lifecycle.UnmarshalOptions(&opts); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -44,25 +48,25 @@ func (h *httpProxy) Start(lifecycle endpoint.Lifecycle) error {
|
||||||
zap.String("address", lifecycle.Uplink().Addr().String()),
|
zap.String("address", lifecycle.Uplink().Addr().String()),
|
||||||
)
|
)
|
||||||
|
|
||||||
tlsConfig := lifecycle.CertStore().TLSConfig()
|
tlsConfig := h.certStore.TLSConfig()
|
||||||
|
|
||||||
proxyHandler := &proxyHTTPHandler{
|
proxyHandler := &proxyHTTPHandler{
|
||||||
handlerName: lifecycle.Name(),
|
handlerName: lifecycle.Name(),
|
||||||
options: opts,
|
options: opts,
|
||||||
logger: h.logger,
|
logger: h.logger,
|
||||||
emitter: lifecycle.Audit(),
|
emitter: h.emitter,
|
||||||
}
|
}
|
||||||
|
|
||||||
proxyHTTPSHandler := &proxyHTTPSHandler{
|
proxyHTTPSHandler := &proxyHTTPSHandler{
|
||||||
options: opts,
|
options: opts,
|
||||||
tlsConfig: tlsConfig,
|
tlsConfig: tlsConfig,
|
||||||
emitter: lifecycle.Audit(),
|
emitter: h.emitter,
|
||||||
}
|
}
|
||||||
|
|
||||||
h.proxy.OnRequest().Do(proxyHandler)
|
h.proxy.OnRequest().Do(proxyHandler)
|
||||||
h.proxy.OnRequest().HandleConnect(proxyHTTPSHandler)
|
h.proxy.OnRequest().HandleConnect(proxyHTTPSHandler)
|
||||||
go h.startProxy(lifecycle.Uplink().Listener)
|
go h.startProxy(lifecycle.Uplink().Listener)
|
||||||
go h.shutdownOnContextDone(lifecycle.Context())
|
go h.shutdownOnContextDone(ctx)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@ import (
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
||||||
|
"gitlab.com/inetmock/inetmock/pkg/audit"
|
||||||
|
"gitlab.com/inetmock/inetmock/pkg/cert"
|
||||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||||
"gitlab.com/inetmock/inetmock/pkg/metrics"
|
"gitlab.com/inetmock/inetmock/pkg/metrics"
|
||||||
)
|
)
|
||||||
|
@ -15,11 +17,7 @@ var (
|
||||||
requestDurationHistogram *prometheus.HistogramVec
|
requestDurationHistogram *prometheus.HistogramVec
|
||||||
)
|
)
|
||||||
|
|
||||||
func AddHTTPProxy(registry endpoint.HandlerRegistry) (err error) {
|
func AddHTTPProxy(registry endpoint.HandlerRegistry, logger logging.Logger, emitter audit.Emitter, store cert.Store) (err error) {
|
||||||
var logger logging.Logger
|
|
||||||
if logger, err = logging.CreateLogger(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logger = logger.With(
|
logger = logger.With(
|
||||||
zap.String("protocol_handler", name),
|
zap.String("protocol_handler", name),
|
||||||
)
|
)
|
||||||
|
@ -30,8 +28,10 @@ func AddHTTPProxy(registry endpoint.HandlerRegistry) (err error) {
|
||||||
|
|
||||||
registry.RegisterHandler(name, func() endpoint.ProtocolHandler {
|
registry.RegisterHandler(name, func() endpoint.ProtocolHandler {
|
||||||
return &httpProxy{
|
return &httpProxy{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
proxy: goproxy.NewProxyHttpServer(),
|
emitter: emitter,
|
||||||
|
certStore: store,
|
||||||
|
proxy: goproxy.NewProxyHttpServer(),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package metrics
|
package metrics
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
@ -20,7 +21,7 @@ type metricsExporter struct {
|
||||||
server *http.Server
|
server *http.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *metricsExporter) Start(lifecycle endpoint.Lifecycle) error {
|
func (m *metricsExporter) Start(ctx context.Context, lifecycle endpoint.Lifecycle) error {
|
||||||
var exporterOptions metricsExporterOptions
|
var exporterOptions metricsExporterOptions
|
||||||
if err := lifecycle.UnmarshalOptions(&exporterOptions); err != nil {
|
if err := lifecycle.UnmarshalOptions(&exporterOptions); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -47,7 +48,7 @@ func (m *metricsExporter) Start(lifecycle endpoint.Lifecycle) error {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
<-lifecycle.Context().Done()
|
<-ctx.Done()
|
||||||
if err := m.server.Close(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
if err := m.server.Close(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||||
m.logger.Error("failed to stop metrics server", zap.Error(err))
|
m.logger.Error("failed to stop metrics server", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,7 @@ import (
|
||||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
func AddMetricsExporter(registry endpoint.HandlerRegistry) (err error) {
|
func AddMetricsExporter(registry endpoint.HandlerRegistry, logger logging.Logger) (err error) {
|
||||||
var logger logging.Logger
|
|
||||||
if logger, err = logging.CreateLogger(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logger = logger.With(
|
logger = logger.With(
|
||||||
zap.String("protocol_handler", name),
|
zap.String("protocol_handler", name),
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,40 +1,22 @@
|
||||||
package endpoint
|
package endpoint
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/cert"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type endpointLifecycle struct {
|
type endpointLifecycle struct {
|
||||||
endpointName string
|
endpointName string
|
||||||
ctx context.Context
|
|
||||||
logger logging.Logger
|
|
||||||
certStore cert.Store
|
|
||||||
emitter audit.Emitter
|
|
||||||
uplink Uplink
|
uplink Uplink
|
||||||
opts map[string]interface{}
|
opts map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEndpointLifecycleFromContext(
|
func NewEndpointLifecycle(
|
||||||
endpointName string,
|
endpointName string,
|
||||||
ctx context.Context,
|
|
||||||
logger logging.Logger,
|
|
||||||
certStore cert.Store,
|
|
||||||
emitter audit.Emitter,
|
|
||||||
uplink Uplink,
|
uplink Uplink,
|
||||||
opts map[string]interface{},
|
opts map[string]interface{},
|
||||||
) Lifecycle {
|
) Lifecycle {
|
||||||
return &endpointLifecycle{
|
return &endpointLifecycle{
|
||||||
endpointName: endpointName,
|
endpointName: endpointName,
|
||||||
ctx: ctx,
|
|
||||||
logger: logger,
|
|
||||||
certStore: certStore,
|
|
||||||
emitter: emitter,
|
|
||||||
uplink: uplink,
|
uplink: uplink,
|
||||||
opts: opts,
|
opts: opts,
|
||||||
}
|
}
|
||||||
|
@ -48,22 +30,6 @@ func (e *endpointLifecycle) Uplink() Uplink {
|
||||||
return e.uplink
|
return e.uplink
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *endpointLifecycle) Logger() logging.Logger {
|
|
||||||
return e.logger
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *endpointLifecycle) CertStore() cert.Store {
|
|
||||||
return e.certStore
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *endpointLifecycle) Audit() audit.Emitter {
|
|
||||||
return e.emitter
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *endpointLifecycle) Context() context.Context {
|
|
||||||
return e.ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *endpointLifecycle) UnmarshalOptions(cfg interface{}) error {
|
func (e *endpointLifecycle) UnmarshalOptions(cfg interface{}) error {
|
||||||
return mapstructure.Decode(e.opts, cfg)
|
return mapstructure.Decode(e.opts, cfg)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,18 +18,16 @@ var (
|
||||||
|
|
||||||
type Orchestrator interface {
|
type Orchestrator interface {
|
||||||
RegisterListener(spec ListenerSpec) error
|
RegisterListener(spec ListenerSpec) error
|
||||||
StartEndpoints() (errChan chan error)
|
StartEndpoints(ctx context.Context) (errChan chan error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewOrchestrator(
|
func NewOrchestrator(
|
||||||
appCtx context.Context,
|
|
||||||
certStore cert.Store,
|
certStore cert.Store,
|
||||||
registry HandlerRegistry,
|
registry HandlerRegistry,
|
||||||
emitter audit.Emitter,
|
emitter audit.Emitter,
|
||||||
logger logging.Logger,
|
logger logging.Logger,
|
||||||
) Orchestrator {
|
) Orchestrator {
|
||||||
return &orchestrator{
|
return &orchestrator{
|
||||||
appCtx: appCtx,
|
|
||||||
registry: registry,
|
registry: registry,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
certStore: certStore,
|
certStore: certStore,
|
||||||
|
@ -38,7 +36,6 @@ func NewOrchestrator(
|
||||||
}
|
}
|
||||||
|
|
||||||
type orchestrator struct {
|
type orchestrator struct {
|
||||||
appCtx context.Context
|
|
||||||
registry HandlerRegistry
|
registry HandlerRegistry
|
||||||
logger logging.Logger
|
logger logging.Logger
|
||||||
certStore cert.Store
|
certStore cert.Store
|
||||||
|
@ -68,24 +65,20 @@ func (e *orchestrator) RegisterListener(spec ListenerSpec) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *orchestrator) StartEndpoints() chan error {
|
func (e *orchestrator) StartEndpoints(ctx context.Context) chan error {
|
||||||
var errChan = make(chan error)
|
var errChan = make(chan error)
|
||||||
for _, epListener := range e.endpointListeners {
|
for _, epListener := range e.endpointListeners {
|
||||||
endpointLogger := e.logger.With(
|
endpointLogger := e.logger.With(
|
||||||
zap.String("epListener", epListener.name),
|
zap.String("epListener", epListener.name),
|
||||||
)
|
)
|
||||||
endpointLogger.Info("Starting epListener")
|
endpointLogger.Info("Starting epListener")
|
||||||
lifecycle := NewEndpointLifecycleFromContext(
|
lifecycle := NewEndpointLifecycle(
|
||||||
epListener.name,
|
epListener.name,
|
||||||
e.appCtx,
|
|
||||||
e.logger.With(zap.String("epListener", epListener.name)),
|
|
||||||
e.certStore,
|
|
||||||
e.emitter,
|
|
||||||
epListener.uplink,
|
epListener.uplink,
|
||||||
epListener.Options,
|
epListener.Options,
|
||||||
)
|
)
|
||||||
|
|
||||||
if err := epListener.Start(lifecycle); err == nil {
|
if err := epListener.Start(ctx, e.logger.With(zap.String("epListener", epListener.name)), lifecycle); err == nil {
|
||||||
endpointLogger.Info("successfully started epListener")
|
endpointLogger.Info("successfully started epListener")
|
||||||
} else {
|
} else {
|
||||||
endpointLogger.Error("error occurred during epListener startup - will be skipped for now")
|
endpointLogger.Error("error occurred during epListener startup - will be skipped for now")
|
||||||
|
|
|
@ -2,43 +2,61 @@ package endpoint_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"testing/fstest"
|
||||||
|
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
"github.com/maxatome/go-testdeep/td"
|
"github.com/maxatome/go-testdeep/td"
|
||||||
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
||||||
dnsmock "gitlab.com/inetmock/inetmock/internal/endpoint/handler/dns/mock"
|
dnsmock "gitlab.com/inetmock/inetmock/internal/endpoint/handler/dns/mock"
|
||||||
httpmock "gitlab.com/inetmock/inetmock/internal/endpoint/handler/http/mock"
|
httpmock "gitlab.com/inetmock/inetmock/internal/endpoint/handler/http/mock"
|
||||||
|
audit_mock "gitlab.com/inetmock/inetmock/internal/mock/audit"
|
||||||
|
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_handlerRegistry_AvailableHandlers(t *testing.T) {
|
func Test_handlerRegistry_AvailableHandlers(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
handlerRegistry endpoint.HandlerRegistry
|
handlerRegistrySetup func(tb testing.TB, ctrl *gomock.Controller) endpoint.HandlerRegistry
|
||||||
wantAvailableHandlers interface{}
|
wantAvailableHandlers interface{}
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Empty registry",
|
name: "Empty registry",
|
||||||
handlerRegistry: endpoint.NewHandlerRegistry(),
|
handlerRegistrySetup: func(testing.TB, *gomock.Controller) endpoint.HandlerRegistry {
|
||||||
|
return endpoint.NewHandlerRegistry()
|
||||||
|
},
|
||||||
wantAvailableHandlers: td.Nil(),
|
wantAvailableHandlers: td.Nil(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Single handler registered",
|
name: "Single handler registered",
|
||||||
handlerRegistry: func() endpoint.HandlerRegistry {
|
handlerRegistrySetup: func(tb testing.TB, ctrl *gomock.Controller) endpoint.HandlerRegistry {
|
||||||
|
tb.Helper()
|
||||||
registry := endpoint.NewHandlerRegistry()
|
registry := endpoint.NewHandlerRegistry()
|
||||||
_ = httpmock.AddHTTPMock(registry)
|
logger := logging.CreateTestLogger(tb)
|
||||||
|
emitter := audit_mock.NewMockEmitter(ctrl)
|
||||||
|
if err := httpmock.AddHTTPMock(registry, logger, emitter, new(fstest.MapFS)); err != nil {
|
||||||
|
tb.Fatalf("AddHTTPMock() error = %v", err)
|
||||||
|
}
|
||||||
return registry
|
return registry
|
||||||
}(),
|
},
|
||||||
wantAvailableHandlers: td.Set(endpoint.HandlerReference("http_mock")),
|
wantAvailableHandlers: td.Set(endpoint.HandlerReference("http_mock")),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Multiple handler registered",
|
name: "Multiple handler registered",
|
||||||
handlerRegistry: func() endpoint.HandlerRegistry {
|
handlerRegistrySetup: func(tb testing.TB, ctrl *gomock.Controller) endpoint.HandlerRegistry {
|
||||||
|
tb.Helper()
|
||||||
registry := endpoint.NewHandlerRegistry()
|
registry := endpoint.NewHandlerRegistry()
|
||||||
_ = httpmock.AddHTTPMock(registry)
|
logger := logging.CreateTestLogger(tb)
|
||||||
_ = dnsmock.AddDNSMock(registry)
|
emitter := audit_mock.NewMockEmitter(ctrl)
|
||||||
|
if err := httpmock.AddHTTPMock(registry, logger, emitter, new(fstest.MapFS)); err != nil {
|
||||||
|
tb.Fatalf("AddHTTPMock() error = %v", err)
|
||||||
|
}
|
||||||
|
if err := dnsmock.AddDNSMock(registry, logger, emitter); err != nil {
|
||||||
|
tb.Fatalf("AddHTTPMock() error = %v", err)
|
||||||
|
}
|
||||||
return registry
|
return registry
|
||||||
}(),
|
},
|
||||||
wantAvailableHandlers: td.Set(
|
wantAvailableHandlers: td.Set(
|
||||||
endpoint.HandlerReference("dns_mock"),
|
endpoint.HandlerReference("dns_mock"),
|
||||||
endpoint.HandlerReference("http_mock"),
|
endpoint.HandlerReference("http_mock"),
|
||||||
|
@ -49,7 +67,8 @@ func Test_handlerRegistry_AvailableHandlers(t *testing.T) {
|
||||||
tt := tc
|
tt := tc
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
gotAvailableHandlers := tt.handlerRegistry.AvailableHandlers()
|
ctrl := gomock.NewController(t)
|
||||||
|
gotAvailableHandlers := tt.handlerRegistrySetup(t, ctrl).AvailableHandlers()
|
||||||
td.Cmp(t, gotAvailableHandlers, tt.wantAvailableHandlers)
|
td.Cmp(t, gotAvailableHandlers, tt.wantAvailableHandlers)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -58,26 +77,34 @@ func Test_handlerRegistry_AvailableHandlers(t *testing.T) {
|
||||||
func Test_handlerRegistry_HandlerForName(t *testing.T) {
|
func Test_handlerRegistry_HandlerForName(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
handlerRegistry endpoint.HandlerRegistry
|
handlerRegistrySetup func(tb testing.TB, ctrl *gomock.Controller) endpoint.HandlerRegistry
|
||||||
handlerRef endpoint.HandlerReference
|
handlerRef endpoint.HandlerReference
|
||||||
wantInstance interface{}
|
wantInstance interface{}
|
||||||
wantOk bool
|
wantOk bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Empty registry",
|
name: "Empty registry",
|
||||||
handlerRegistry: endpoint.NewHandlerRegistry(),
|
handlerRegistrySetup: func(tb testing.TB, _ *gomock.Controller) endpoint.HandlerRegistry {
|
||||||
handlerRef: "http_mock",
|
tb.Helper()
|
||||||
wantInstance: nil,
|
return endpoint.NewHandlerRegistry()
|
||||||
wantOk: false,
|
},
|
||||||
|
handlerRef: "http_mock",
|
||||||
|
wantInstance: nil,
|
||||||
|
wantOk: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Registry with HTTP mock registered",
|
name: "Registry with HTTP mock registered",
|
||||||
handlerRegistry: func() endpoint.HandlerRegistry {
|
handlerRegistrySetup: func(tb testing.TB, ctrl *gomock.Controller) endpoint.HandlerRegistry {
|
||||||
|
tb.Helper()
|
||||||
registry := endpoint.NewHandlerRegistry()
|
registry := endpoint.NewHandlerRegistry()
|
||||||
_ = httpmock.AddHTTPMock(registry)
|
logger := logging.CreateTestLogger(tb)
|
||||||
|
emitter := audit_mock.NewMockEmitter(ctrl)
|
||||||
|
if err := httpmock.AddHTTPMock(registry, logger, emitter, new(fstest.MapFS)); err != nil {
|
||||||
|
tb.Fatalf("AddHTTPMock() error = %v", err)
|
||||||
|
}
|
||||||
return registry
|
return registry
|
||||||
}(),
|
},
|
||||||
handlerRef: "http_mock",
|
handlerRef: "http_mock",
|
||||||
wantInstance: td.NotNil(),
|
wantInstance: td.NotNil(),
|
||||||
wantOk: true,
|
wantOk: true,
|
||||||
|
@ -87,7 +114,8 @@ func Test_handlerRegistry_HandlerForName(t *testing.T) {
|
||||||
tt := tc
|
tt := tc
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
gotInstance, gotOk := tt.handlerRegistry.HandlerForName(tt.handlerRef)
|
ctrl := gomock.NewController(t)
|
||||||
|
gotInstance, gotOk := tt.handlerRegistrySetup(t, ctrl).HandlerForName(tt.handlerRef)
|
||||||
td.Cmp(t, gotInstance, tt.wantInstance)
|
td.Cmp(t, gotInstance, tt.wantInstance)
|
||||||
td.Cmp(t, gotOk, tt.wantOk)
|
td.Cmp(t, gotOk, tt.wantOk)
|
||||||
})
|
})
|
||||||
|
|
|
@ -22,11 +22,11 @@ func TestParse(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "Terminator only - string argument",
|
name: "Terminator only - string argument",
|
||||||
args: args{
|
args: args{
|
||||||
rule: `=> ReturnFile("default.html")`,
|
rule: `=> File("default.html")`,
|
||||||
},
|
},
|
||||||
want: &Routing{
|
want: &Routing{
|
||||||
Terminator: &Method{
|
Terminator: &Method{
|
||||||
Name: "ReturnFile",
|
Name: "File",
|
||||||
Params: []Param{
|
Params: []Param{
|
||||||
{
|
{
|
||||||
String: stringRef("default.html"),
|
String: stringRef("default.html"),
|
||||||
|
@ -73,7 +73,7 @@ func TestParse(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "path pattern and terminator",
|
name: "path pattern and terminator",
|
||||||
args: args{
|
args: args{
|
||||||
rule: `PathPattern("/index.html") => ReturnFile("default.html")`,
|
rule: `PathPattern(".*\\.(?i)png") => ReturnFile("default.html")`,
|
||||||
},
|
},
|
||||||
want: &Routing{
|
want: &Routing{
|
||||||
Terminator: &Method{
|
Terminator: &Method{
|
||||||
|
@ -90,7 +90,7 @@ func TestParse(t *testing.T) {
|
||||||
Name: "PathPattern",
|
Name: "PathPattern",
|
||||||
Params: []Param{
|
Params: []Param{
|
||||||
{
|
{
|
||||||
String: stringRef("/index.html"),
|
String: stringRef(`.*\.(?i)png`),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
Loading…
Add table
Reference in a new issue