Introduce Lifecycle for every endpoint and manage listeners in the renamed Orchestrator
- merge packages to get a more concise layout because plugins are no more and therefore there's not a lot to be exported - fix test logger - rework config parsing to be easier and more transparent - remove unnecessary APIs because dynamic endpoint handling is rather a won't implement
This commit is contained in:
parent
dd4b191abb
commit
d70ba748f5
79 changed files with 1839 additions and 2036 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -23,3 +23,4 @@
|
|||
dist/
|
||||
out/
|
||||
.task/
|
||||
public/
|
|
@ -1,4 +1,4 @@
|
|||
image: registry.gitlab.com/inetmock/ci-image
|
||||
image: registry.gitlab.com/inetmock/ci-image/go
|
||||
|
||||
stages:
|
||||
- test
|
||||
|
@ -14,6 +14,13 @@ test:
|
|||
junit: out/report.xml
|
||||
cobertura: out/coverage.xml
|
||||
|
||||
integration-test:
|
||||
stage: test
|
||||
services:
|
||||
- docker:dind
|
||||
script:
|
||||
- task integration-test
|
||||
|
||||
lint:
|
||||
stage: test
|
||||
script:
|
||||
|
@ -42,3 +49,16 @@ release:
|
|||
script:
|
||||
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
||||
- goreleaser release --rm-dist
|
||||
|
||||
docs:
|
||||
stage: release
|
||||
image: registry.gitlab.com/inetmock/ci-image/mdbook
|
||||
only:
|
||||
refs:
|
||||
- master
|
||||
- tags
|
||||
script:
|
||||
- mdbook build -d ./../public ./docs
|
||||
artifacts:
|
||||
paths:
|
||||
- public
|
23
README.md
23
README.md
|
@ -1,13 +1,9 @@
|
|||
# INetMock
|
||||
|
||||
![Go](https://gitlab.com/inetmock/inetmock/workflows/Go/badge.svg)
|
||||
|
||||
[![pipeline status](https://gitlab.com/inetmock/inetmock/badges/master/pipeline.svg)](https://gitlab.com/inetmock/inetmock/-/commits/master)
|
||||
[![coverage report](https://gitlab.com/inetmock/inetmock/badges/master/coverage.svg)](https://gitlab.com/inetmock/inetmock/-/commits/master)
|
||||
[![Go Report Card](https://goreportcard.com/badge/gitlab.com/inetmock/inetmock)](https://goreportcard.com/report/gitlab.com/inetmock/inetmock)
|
||||
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=baez90_inetmock&metric=alert_status)](https://sonarcloud.io/dashboard?id=baez90_inetmock)
|
||||
[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=baez90_inetmock&metric=ncloc)](https://sonarcloud.io/dashboard?id=baez90_inetmock)
|
||||
[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=baez90_inetmock&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=baez90_inetmock)
|
||||
[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=baez90_inetmock&metric=security_rating)](https://sonarcloud.io/dashboard?id=baez90_inetmock)
|
||||
[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=baez90_inetmock&metric=bugs)](https://sonarcloud.io/dashboard?id=baez90_inetmock)
|
||||
[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=baez90_inetmock&metric=vulnerabilities)](https://sonarcloud.io/dashboard?id=baez90_inetmock)
|
||||
|
||||
INetMock is kind of a fork of [INetSim](https://www.inetsim.org/).
|
||||
"Kind of" in terms that both applications overlap in their functionality to serve as "fake internet" routers.
|
||||
|
@ -17,18 +13,15 @@ HTTPS, DNS, DNS-over-TLS (DoT) requests and to act as an HTTP proxy. The most no
|
|||
is that it issues proper TLS certificates on the fly signed by a CA certificate that can be deployed to client systems
|
||||
to achieve "proper" TLS encryption - as long as the client does not use certificate pinning or something similar.
|
||||
|
||||
A second advantage is that INetMock is a complete rewrite in Go, based on a plugin system that allows dynamic
|
||||
configuration while it has a way smaller memory footprint and far better startup and shutdown times. It also does not
|
||||
enforce `root` privileges as it is also possible to run the application with the required capabilities to open ports
|
||||
e.g. with SystemD (a sample unit file can be found in the `deploy/` directory).
|
||||
A second advantage is that INetMock is a complete rewrite in Go.
|
||||
It has a way smaller memory footprint and far better startup and shutdown times.
|
||||
It also does not enforce `root` privileges as it is also possible to run the application with the required capabilities to open ports e.g. with SystemD (a sample unit file can be found in the `deploy/` directory).
|
||||
|
||||
_This project is still heavy work-in-progress. There may be breaking changes at any time. There's no guarantee for
|
||||
anything except no kittens will be harmed!_
|
||||
_This project is still heavy work-in-progress. There may be breaking changes at any time. There's no guarantee for anything except no kittens will be harmed!_
|
||||
|
||||
## Docs
|
||||
|
||||
Docs are available either in the [`docs/`](./docs/) directory or as rendered markdown book at
|
||||
the [GitHub pages](https://baez90.github.io/inetmock/).
|
||||
Docs are available either in the [`docs/`](./docs/) directory or as rendered markdown book at the [GitHub pages](https://baez90.github.io/inetmock/).
|
||||
|
||||
## Contribution/feature requests
|
||||
|
||||
|
|
15
Taskfile.yml
15
Taskfile.yml
|
@ -6,6 +6,8 @@ vars:
|
|||
IMCTL_PKG: gitlab.com/inetmock/inetmock/cmd/imctl
|
||||
PROTO_FILES:
|
||||
sh: find ./api/ -type f -name "*.proto" -printf "%p "
|
||||
BENCHMARKS:
|
||||
sh: find . -type f -name "*_bench_test.go"
|
||||
|
||||
env:
|
||||
GOOS: linux
|
||||
|
@ -53,6 +55,15 @@ tasks:
|
|||
- gocover-cobertura < {{ .OUT_DIR }}/cov.out > {{ .OUT_DIR }}/coverage.xml
|
||||
- rm -f {{ .OUT_DIR }}/cov-raw.out
|
||||
|
||||
integration-test:
|
||||
deps:
|
||||
- generate
|
||||
cmds:
|
||||
- |
|
||||
{{ range .BENCHMARKS | splitLines -}}
|
||||
go test -bench=. {{ . }}
|
||||
{{ end }}
|
||||
|
||||
cli-cover-report:
|
||||
deps:
|
||||
- test
|
||||
|
@ -99,3 +110,7 @@ tasks:
|
|||
- test
|
||||
cmds:
|
||||
- goreleaser release
|
||||
|
||||
docs:
|
||||
cmds:
|
||||
- mdbook build -d ./../public ./docs
|
|
@ -1,28 +0,0 @@
|
|||
syntax = "proto3";
|
||||
|
||||
option go_package = "gitlab.com/inetmock/inetmock/internal/rpc";
|
||||
option java_multiple_files = true;
|
||||
option java_package = "com.github.baez90.inetmock.rpc";
|
||||
option java_outer_classname = "EndpointsProto";
|
||||
|
||||
package inetmock.rpc;
|
||||
|
||||
message GetEndpointsRequest {
|
||||
}
|
||||
|
||||
message GetEndpointsResponse {
|
||||
repeated Endpoint endpoints = 1;
|
||||
}
|
||||
|
||||
message Endpoint {
|
||||
string id = 1;
|
||||
string name = 2;
|
||||
string handler = 3;
|
||||
string listenAddress = 4;
|
||||
int32 port = 5;
|
||||
}
|
||||
|
||||
service Endpoints {
|
||||
rpc GetEndpoints (GetEndpointsRequest) returns (GetEndpointsResponse) {
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
syntax = "proto3";
|
||||
|
||||
option go_package = "gitlab.com/inetmock/inetmock/internal/rpc";
|
||||
option java_multiple_files = true;
|
||||
option java_package = "com.github.baez90.inetmock.rpc";
|
||||
option java_outer_classname = "HandlersProto";
|
||||
|
||||
package inetmock.rpc;
|
||||
|
||||
service Handlers {
|
||||
rpc GetHandlers (GetHandlersRequest) returns (GetHandlersResponse) {
|
||||
}
|
||||
}
|
||||
|
||||
message GetHandlersRequest {
|
||||
}
|
||||
|
||||
message GetHandlersResponse {
|
||||
repeated string handlers = 1;
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"gitlab.com/inetmock/inetmock/internal/format"
|
||||
"gitlab.com/inetmock/inetmock/internal/rpc"
|
||||
)
|
||||
|
||||
var (
|
||||
getEndpoints = &cobra.Command{
|
||||
Use: "get",
|
||||
Short: "Get all running endpoints",
|
||||
RunE: runGetEndpoints,
|
||||
}
|
||||
|
||||
endpointsCmd = &cobra.Command{
|
||||
Use: "endpoints",
|
||||
Short: "endpoints is the entrypoint to all kind of commands to interact with endpoints",
|
||||
Aliases: []string{"ep", "endpoint"},
|
||||
}
|
||||
)
|
||||
|
||||
type printableEndpoint struct {
|
||||
ID string
|
||||
Name string
|
||||
Handler string
|
||||
ListenAddress string
|
||||
Port int
|
||||
}
|
||||
|
||||
func fromEndpoint(ep *rpc.Endpoint) *printableEndpoint {
|
||||
return &printableEndpoint{
|
||||
ID: ep.Id,
|
||||
Name: ep.Name,
|
||||
Handler: ep.Handler,
|
||||
ListenAddress: ep.ListenAddress,
|
||||
Port: int(ep.Port),
|
||||
}
|
||||
}
|
||||
|
||||
func fromEndpoints(eps []*rpc.Endpoint) (out []*printableEndpoint) {
|
||||
for idx := range eps {
|
||||
out = append(out, fromEndpoint(eps[idx]))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func runGetEndpoints(_ *cobra.Command, _ []string) (err error) {
|
||||
endpointsClient := rpc.NewEndpointsClient(conn)
|
||||
ctx, cancel := context.WithTimeout(cliApp.Context(), grpcTimeout)
|
||||
defer cancel()
|
||||
var endpointsResp *rpc.GetEndpointsResponse
|
||||
if endpointsResp, err = endpointsClient.GetEndpoints(ctx, &rpc.GetEndpointsRequest{}); err != nil {
|
||||
fmt.Printf("Failed to get the endpoints: %v", err)
|
||||
os.Exit(11)
|
||||
}
|
||||
|
||||
writer := format.Writer(outputFormat, os.Stdout)
|
||||
if err = writer.Write(fromEndpoints(endpointsResp.Endpoints)); err != nil {
|
||||
fmt.Printf("Error occurred during writing response values: %v\n", err)
|
||||
}
|
||||
return
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"gitlab.com/inetmock/inetmock/internal/format"
|
||||
"gitlab.com/inetmock/inetmock/internal/rpc"
|
||||
)
|
||||
|
||||
var (
|
||||
getHandlersCmd = &cobra.Command{
|
||||
Use: "get",
|
||||
Short: "Get all registered handlers",
|
||||
Run: runGetHandlers,
|
||||
}
|
||||
|
||||
handlerCmd = &cobra.Command{
|
||||
Use: "handlers",
|
||||
Short: "handlers is the entrypoint to all kind of commands to interact with handlers",
|
||||
Aliases: []string{"handler"},
|
||||
}
|
||||
)
|
||||
|
||||
type printableHandler struct {
|
||||
Handler string
|
||||
}
|
||||
|
||||
func fromHandlers(hs []string) (handlers []*printableHandler) {
|
||||
for idx := range hs {
|
||||
handlers = append(handlers, &printableHandler{
|
||||
Handler: hs[idx],
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func runGetHandlers(_ *cobra.Command, _ []string) {
|
||||
handlersClient := rpc.NewHandlersClient(conn)
|
||||
|
||||
ctx, cancel := context.WithTimeout(cliApp.Context(), grpcTimeout)
|
||||
defer cancel()
|
||||
var err error
|
||||
var handlersResp *rpc.GetHandlersResponse
|
||||
|
||||
if handlersResp, err = handlersClient.GetHandlers(ctx, &rpc.GetHandlersRequest{}); err != nil {
|
||||
fmt.Printf("Failed to get the endpoints: %v", err)
|
||||
os.Exit(11)
|
||||
}
|
||||
|
||||
writer := format.Writer(outputFormat, os.Stdout)
|
||||
if err = writer.Write(fromHandlers(handlersResp.Handlers)); err != nil {
|
||||
fmt.Printf("Error occurred during writing response values: %v\n", err)
|
||||
}
|
||||
}
|
|
@ -22,12 +22,10 @@ var (
|
|||
)
|
||||
|
||||
func main() {
|
||||
endpointsCmd.AddCommand(getEndpoints)
|
||||
handlerCmd.AddCommand(getHandlersCmd)
|
||||
healthCmd.AddCommand(generalHealthCmd, containerHealthCmd)
|
||||
|
||||
cliApp = app.NewApp("imctl", "IMCTL is the CLI app to interact with an INetMock server").
|
||||
WithCommands(endpointsCmd, handlerCmd, healthCmd, auditCmd).
|
||||
WithCommands(healthCmd, auditCmd).
|
||||
WithInitTasks(func(_ *cobra.Command, _ []string) (err error) {
|
||||
return initGRPCConnection()
|
||||
}).
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
|
||||
"github.com/spf13/cobra"
|
||||
"gitlab.com/inetmock/inetmock/pkg/cert"
|
||||
"gitlab.com/inetmock/inetmock/pkg/config"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
@ -64,11 +63,11 @@ func runGenerateCA(_ *cobra.Command, _ []string) {
|
|||
zap.String(generateCACertOutPath, certOutPath),
|
||||
)
|
||||
|
||||
generator := cert.NewDefaultGenerator(config.CertOptions{
|
||||
generator := cert.NewDefaultGenerator(cert.CertOptions{
|
||||
CertCachePath: certOutPath,
|
||||
Curve: config.CurveType(curveName),
|
||||
Validity: config.ValidityByPurpose{
|
||||
CA: config.ValidityDuration{
|
||||
Curve: cert.CurveType(curveName),
|
||||
Validity: cert.ValidityByPurpose{
|
||||
CA: cert.ValidityDuration{
|
||||
NotAfterRelative: notAfter,
|
||||
NotBeforeRelative: notBefore,
|
||||
},
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"gitlab.com/inetmock/inetmock/internal/rpc"
|
||||
"gitlab.com/inetmock/inetmock/pkg/config"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
@ -14,41 +11,48 @@ var (
|
|||
Use: "serve",
|
||||
Short: "Starts the INetMock server",
|
||||
Long: ``,
|
||||
Run: startINetMock,
|
||||
RunE: startINetMock,
|
||||
}
|
||||
)
|
||||
|
||||
func startINetMock(_ *cobra.Command, _ []string) {
|
||||
func startINetMock(_ *cobra.Command, _ []string) (err error) {
|
||||
rpcAPI := rpc.NewINetMockAPI(serverApp)
|
||||
logger := serverApp.Logger().Named("inetmock").With(zap.String("command", "serve"))
|
||||
logger := serverApp.Logger()
|
||||
|
||||
for endpointName, endpointHandler := range serverApp.Config().EndpointConfigs() {
|
||||
handlerSubConfig := serverApp.Config().Viper().Sub(strings.Join([]string{config.EndpointsKey, endpointName, config.OptionsKey}, "."))
|
||||
endpointHandler.Options = handlerSubConfig
|
||||
if err := serverApp.EndpointManager().CreateEndpoint(endpointName, endpointHandler); err != nil {
|
||||
logger.Warn(
|
||||
"error occurred while creating endpoint",
|
||||
zap.String("endpointName", endpointName),
|
||||
zap.String("handlerName", endpointHandler.Handler),
|
||||
zap.Error(err),
|
||||
)
|
||||
cfg := serverApp.Config()
|
||||
endpointOrchestrator := serverApp.EndpointManager()
|
||||
|
||||
for name, spec := range cfg.ListenerSpecs() {
|
||||
if spec.Name == "" {
|
||||
spec.Name = name
|
||||
}
|
||||
if err = endpointOrchestrator.RegisterListener(spec); err != nil {
|
||||
logger.Error("Failed to register listener", zap.Error(err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
serverApp.EndpointManager().StartEndpoints()
|
||||
if err := rpcAPI.StartServer(); err != nil {
|
||||
errChan := serverApp.EndpointManager().StartEndpoints()
|
||||
if err = rpcAPI.StartServer(); err != nil {
|
||||
serverApp.Shutdown()
|
||||
logger.Error(
|
||||
"failed to start gRPC API",
|
||||
zap.Error(err),
|
||||
)
|
||||
}
|
||||
|
||||
<-serverApp.Context().Done()
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case err := <-errChan:
|
||||
logger.Error("got error from endpoint", zap.Error(err))
|
||||
case <-serverApp.Context().Done():
|
||||
break loop
|
||||
}
|
||||
}
|
||||
|
||||
logger.Info(
|
||||
"App context canceled - shutting down",
|
||||
)
|
||||
logger.Info("App context canceled - shutting down")
|
||||
|
||||
rpcAPI.StopServer()
|
||||
serverApp.EndpointManager().ShutdownEndpoints()
|
||||
return
|
||||
}
|
||||
|
|
|
@ -41,6 +41,19 @@ x-response-rules: &httpResponseRules
|
|||
matcher: Path
|
||||
response: /var/lib/inetmock/fakeFiles/default.html
|
||||
|
||||
x-http-handlers: &httpHandlers
|
||||
endpoints:
|
||||
plainHttp:
|
||||
handler: http_mock
|
||||
tls: false
|
||||
options:
|
||||
<<: *httpResponseRules
|
||||
https:
|
||||
handler: http_mock
|
||||
tls: true
|
||||
options:
|
||||
<<: *httpResponseRules
|
||||
|
||||
api:
|
||||
listen: unix:///var/run/inetmock/inetmock.sock
|
||||
|
||||
|
@ -60,61 +73,94 @@ tls:
|
|||
privateKeyPath: /var/lib/inetmock/ca/ca.key
|
||||
certCachePath: /var/lib/inetmock/certs
|
||||
|
||||
endpoints:
|
||||
plainHttp:
|
||||
handler: http_mock
|
||||
listenAddress: 0.0.0.0
|
||||
ports:
|
||||
- 80
|
||||
- 8080
|
||||
options:
|
||||
<<: *httpResponseRules
|
||||
https:
|
||||
handler: http_mock
|
||||
listenAddress: 0.0.0.0
|
||||
ports:
|
||||
- 443
|
||||
- 8443
|
||||
options:
|
||||
tls: true
|
||||
<<: *httpResponseRules
|
||||
proxy:
|
||||
handler: http_proxy
|
||||
listenAddress: 0.0.0.0
|
||||
ports:
|
||||
- 3128
|
||||
options:
|
||||
target:
|
||||
ipAddress: 127.0.0.1
|
||||
port: 80
|
||||
plainDns:
|
||||
handler: dns_mock
|
||||
listenAddress: 0.0.0.0
|
||||
ports:
|
||||
- 53
|
||||
options:
|
||||
rules:
|
||||
- pattern: ".*\\.google\\.com"
|
||||
response: 1.1.1.1
|
||||
- pattern: ".*\\.reddit\\.com"
|
||||
response: 2.2.2.2
|
||||
fallback:
|
||||
strategy: incremental
|
||||
args:
|
||||
startIP: 10.0.10.0
|
||||
dnsOverTlsDowngrade:
|
||||
handler: tls_interceptor
|
||||
listenAddress: 0.0.0.0
|
||||
ports:
|
||||
- 853
|
||||
options:
|
||||
target:
|
||||
ipAddress: 127.0.0.1
|
||||
port: 53
|
||||
metrics:
|
||||
handler: metrics_exporter
|
||||
listenAddress: 0.0.0.0
|
||||
ports:
|
||||
- 9110
|
||||
options:
|
||||
route: /metrics
|
||||
listeners:
|
||||
udp_53:
|
||||
name: ''
|
||||
protocol: udp
|
||||
listenAddress: ''
|
||||
port: 53
|
||||
endpoints:
|
||||
plainDns:
|
||||
handler: dns_mock
|
||||
options:
|
||||
rules:
|
||||
- pattern: ".*\\.google\\.com"
|
||||
response: 1.1.1.1
|
||||
- pattern: ".*\\.reddit\\.com"
|
||||
response: 2.2.2.2
|
||||
fallback:
|
||||
strategy: incremental
|
||||
args:
|
||||
startIP: 10.0.10.0
|
||||
tcp_80:
|
||||
name: ''
|
||||
protocol: tcp
|
||||
listenAddress: ''
|
||||
port: 80
|
||||
<<: *httpHandlers
|
||||
tcp_443:
|
||||
name: ''
|
||||
protocol: tcp
|
||||
listenAddress: ''
|
||||
port: 443
|
||||
<<: *httpHandlers
|
||||
tcp_853:
|
||||
name: ''
|
||||
protocol: tcp
|
||||
listenAddress: ''
|
||||
port: 853
|
||||
endpoints:
|
||||
DoT:
|
||||
handler: dns_mock
|
||||
tls: true
|
||||
options:
|
||||
rules:
|
||||
- pattern: ".*\\.google\\.com"
|
||||
response: 1.1.1.1
|
||||
- pattern: ".*\\.reddit\\.com"
|
||||
response: 2.2.2.2
|
||||
fallback:
|
||||
strategy: incremental
|
||||
args:
|
||||
startIP: 10.0.10.0
|
||||
tcp_3128:
|
||||
name: ''
|
||||
protocol: tcp
|
||||
listenAddress: ''
|
||||
port: 3128
|
||||
endpoints:
|
||||
proxyPlain:
|
||||
handler: http_proxy
|
||||
options:
|
||||
target:
|
||||
ipAddress: 127.0.0.1
|
||||
port: 80
|
||||
proxyTls:
|
||||
handler: http_proxy
|
||||
tls: true
|
||||
options:
|
||||
target:
|
||||
ipAddress: 127.0.0.1
|
||||
port: 443
|
||||
tcp_8080:
|
||||
name: ''
|
||||
protocol: tcp
|
||||
listenAddress: ''
|
||||
port: 8080
|
||||
<<: *httpHandlers
|
||||
tcp_8443:
|
||||
name: ''
|
||||
protocol: tcp
|
||||
listenAddress: ''
|
||||
port: 8443
|
||||
<<: *httpHandlers
|
||||
tcp_9110:
|
||||
name: ''
|
||||
protocol: tcp
|
||||
listenAddress: ''
|
||||
port: 9110
|
||||
endpoints:
|
||||
metrics:
|
||||
handler: metrics_exporter
|
||||
options:
|
||||
route: /metrics
|
162
config.yaml
162
config.yaml
|
@ -41,6 +41,19 @@ x-response-rules: &httpResponseRules
|
|||
matcher: Path
|
||||
response: ./assets/fakeFiles/default.html
|
||||
|
||||
x-http-handlers: &httpHandlers
|
||||
endpoints:
|
||||
plainHttp:
|
||||
handler: http_mock
|
||||
tls: false
|
||||
options:
|
||||
<<: *httpResponseRules
|
||||
https:
|
||||
handler: http_mock
|
||||
tls: true
|
||||
options:
|
||||
<<: *httpResponseRules
|
||||
|
||||
api:
|
||||
listen: unix:///var/run/inetmock.sock
|
||||
|
||||
|
@ -60,61 +73,94 @@ tls:
|
|||
privateKeyPath: ./assets/demoCA/ca.key
|
||||
certCachePath: /tmp/inetmock/
|
||||
|
||||
endpoints:
|
||||
plainHttp:
|
||||
handler: http_mock
|
||||
listenAddress: 0.0.0.0
|
||||
ports:
|
||||
- 80
|
||||
- 8080
|
||||
options:
|
||||
<<: *httpResponseRules
|
||||
https:
|
||||
handler: http_mock
|
||||
listenAddress: 0.0.0.0
|
||||
ports:
|
||||
- 443
|
||||
- 8443
|
||||
options:
|
||||
tls: true
|
||||
<<: *httpResponseRules
|
||||
proxy:
|
||||
handler: http_proxy
|
||||
listenAddress: 0.0.0.0
|
||||
ports:
|
||||
- 3128
|
||||
options:
|
||||
target:
|
||||
ipAddress: 127.0.0.1
|
||||
port: 80
|
||||
plainDns:
|
||||
handler: dns_mock
|
||||
listenAddress: 0.0.0.0
|
||||
ports:
|
||||
- 53
|
||||
options:
|
||||
rules:
|
||||
- pattern: ".*\\.google\\.com"
|
||||
response: 1.1.1.1
|
||||
- pattern: ".*\\.reddit\\.com"
|
||||
response: 2.2.2.2
|
||||
fallback:
|
||||
strategy: incremental
|
||||
args:
|
||||
startIP: 10.0.10.0
|
||||
dnsOverTlsDowngrade:
|
||||
handler: tls_interceptor
|
||||
listenAddress: 0.0.0.0
|
||||
ports:
|
||||
- 853
|
||||
options:
|
||||
target:
|
||||
ipAddress: 127.0.0.1
|
||||
port: 53
|
||||
metrics:
|
||||
handler: metrics_exporter
|
||||
listenAddress: 0.0.0.0
|
||||
ports:
|
||||
- 9110
|
||||
options:
|
||||
route: /metrics
|
||||
listeners:
|
||||
udp_53:
|
||||
name: ''
|
||||
protocol: udp
|
||||
listenAddress: ''
|
||||
port: 1053
|
||||
endpoints:
|
||||
plainDns:
|
||||
handler: dns_mock
|
||||
options:
|
||||
rules:
|
||||
- pattern: ".*\\.google\\.com"
|
||||
response: 1.1.1.1
|
||||
- pattern: ".*\\.reddit\\.com"
|
||||
response: 2.2.2.2
|
||||
fallback:
|
||||
strategy: incremental
|
||||
args:
|
||||
startIP: 10.0.10.0
|
||||
tcp_80:
|
||||
name: ''
|
||||
protocol: tcp
|
||||
listenAddress: ''
|
||||
port: 80
|
||||
<<: *httpHandlers
|
||||
tcp_443:
|
||||
name: ''
|
||||
protocol: tcp
|
||||
listenAddress: ''
|
||||
port: 443
|
||||
<<: *httpHandlers
|
||||
tcp_853:
|
||||
name: ''
|
||||
protocol: tcp
|
||||
listenAddress: ''
|
||||
port: 853
|
||||
endpoints:
|
||||
DoT:
|
||||
handler: dns_mock
|
||||
tls: true
|
||||
options:
|
||||
rules:
|
||||
- pattern: ".*\\.google\\.com"
|
||||
response: 1.1.1.1
|
||||
- pattern: ".*\\.reddit\\.com"
|
||||
response: 2.2.2.2
|
||||
fallback:
|
||||
strategy: incremental
|
||||
args:
|
||||
startIP: 10.0.10.0
|
||||
tcp_3128:
|
||||
name: ''
|
||||
protocol: tcp
|
||||
listenAddress: ''
|
||||
port: 3128
|
||||
endpoints:
|
||||
proxyPlain:
|
||||
handler: http_proxy
|
||||
options:
|
||||
target:
|
||||
ipAddress: 127.0.0.1
|
||||
port: 80
|
||||
proxyTls:
|
||||
handler: http_proxy
|
||||
tls: true
|
||||
options:
|
||||
target:
|
||||
ipAddress: 127.0.0.1
|
||||
port: 443
|
||||
tcp_8080:
|
||||
name: ''
|
||||
protocol: tcp
|
||||
listenAddress: ''
|
||||
port: 8080
|
||||
<<: *httpHandlers
|
||||
tcp_8443:
|
||||
name: ''
|
||||
protocol: tcp
|
||||
listenAddress: ''
|
||||
port: 8443
|
||||
<<: *httpHandlers
|
||||
tcp_9110:
|
||||
name: ''
|
||||
protocol: tcp
|
||||
listenAddress: ''
|
||||
port: 9110
|
||||
endpoints:
|
||||
metrics:
|
||||
handler: metrics_exporter
|
||||
options:
|
||||
route: /metrics
|
12
go.mod
12
go.mod
|
@ -4,16 +4,20 @@ go 1.15
|
|||
|
||||
require (
|
||||
github.com/bwmarrin/snowflake v0.3.0
|
||||
github.com/docker/go-connections v0.4.0
|
||||
github.com/golang/mock v1.4.4
|
||||
github.com/golang/protobuf v1.4.3
|
||||
github.com/google/uuid v1.2.0
|
||||
github.com/imdario/mergo v0.3.11
|
||||
github.com/miekg/dns v1.1.35
|
||||
github.com/olekukonko/tablewriter v0.0.4
|
||||
github.com/jinzhu/copier v0.2.4
|
||||
github.com/miekg/dns v1.1.38
|
||||
github.com/mitchellh/mapstructure v1.4.1
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/prometheus/client_golang v1.9.0
|
||||
github.com/spf13/cobra v1.1.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/soheilhy/cmux v0.1.4
|
||||
github.com/spf13/cobra v1.1.2
|
||||
github.com/spf13/viper v1.7.1
|
||||
github.com/testcontainers/testcontainers-go v0.9.0
|
||||
go.uber.org/multierr v1.6.0
|
||||
go.uber.org/zap v1.16.0
|
||||
google.golang.org/grpc v1.35.0
|
||||
|
|
168
go.sum
168
go.sum
|
@ -11,10 +11,16 @@ cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqCl
|
|||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||
github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q=
|
||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||
github.com/Microsoft/hcsshim v0.8.6 h1:ZfF0+zZeYdzMIVMZHKtDKJvLHj76XCuVae/jNkjj0IA=
|
||||
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
|
@ -28,7 +34,6 @@ github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk5
|
|||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
||||
|
@ -45,6 +50,7 @@ github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJm
|
|||
github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0=
|
||||
github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=
|
||||
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
|
@ -53,12 +59,15 @@ github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+
|
|||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
||||
github.com/containerd/containerd v1.4.1 h1:pASeJT3R3YyVn+94qEPk0SnU1OQ20Jd/T+SPKy9xehY=
|
||||
github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc h1:TP+534wVlf61smEIq1nwLLAjQVEK2EADoW3CX9AuT+8=
|
||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
|
@ -74,20 +83,28 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible h1:dvc1KSkIYTVjZgHf/CTC2diTYC8PzhaA5sFISRfNVrE=
|
||||
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v17.12.0-ce-rc1.0.20200916142827-bd33bbf0497b+incompatible h1:SiUATuP//KecDjpOK2tvZJgeScYAklvyjfK8JZlU6fo=
|
||||
github.com/docker/docker v17.12.0-ce-rc1.0.20200916142827-bd33bbf0497b+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk=
|
||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/elazarl/goproxy v0.0.0-20201021153353-00ad82a08272 h1:Am81SElhR3XCQBunTisljzNkNese2T1FiV8jP79+dqg=
|
||||
github.com/elazarl/goproxy v0.0.0-20201021153353-00ad82a08272/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
|
||||
github.com/elazarl/goproxy v0.0.0-20210110162100-a92cc753f88e h1:/cwV7t2xezilMljIftb7WlFtzGANRCnoOhPjtl2ifcs=
|
||||
github.com/elazarl/goproxy v0.0.0-20210110162100-a92cc753f88e/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
|
||||
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
|
||||
github.com/elazarl/goproxy/ext v0.0.0-20201021153353-00ad82a08272 h1:xOHQWEGsftkjjFV2KIrC9vOz+iOinvZ7H6EAjSznqUk=
|
||||
github.com/elazarl/goproxy/ext v0.0.0-20201021153353-00ad82a08272/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
|
||||
github.com/elazarl/goproxy/ext v0.0.0-20210110162100-a92cc753f88e h1:CQn2/8fi3kmpT9BTiHEELgdxAOQNVZc9GoPA4qnQzrs=
|
||||
github.com/elazarl/goproxy/ext v0.0.0-20210110162100-a92cc753f88e/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
|
||||
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
|
@ -95,9 +112,9 @@ github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVB
|
|||
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
|
@ -105,15 +122,25 @@ github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgO
|
|||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
|
||||
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
|
@ -123,6 +150,7 @@ github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71
|
|||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
|
@ -159,9 +187,9 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGa
|
|||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
|
@ -192,6 +220,7 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
|
|||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
||||
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
|
||||
|
@ -199,12 +228,15 @@ github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH
|
|||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
||||
github.com/jinzhu/copier v0.2.4 h1:dT3tI+8GzU8DjJFCj9mLYtjfRtUmK7edauduQdcZCpI=
|
||||
github.com/jinzhu/copier v0.2.4/go.mod h1:24xnZezI2Yqac9J61UC6/dG/k76ttpq0DdJI3QmUvro=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
|
@ -214,38 +246,33 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8
|
|||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
||||
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
||||
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.4 h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQSeYtvkY=
|
||||
github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
|
||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
|
||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.31 h1:sJFOl9BgwbYAWOGEwr61FU28pqsBNdpRBnhGXtO06Oo=
|
||||
github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/miekg/dns v1.1.35 h1:oTfOaDH+mZkdcgdIjH6yBajRGtIwcwcaR+rt23ZSrJs=
|
||||
github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/miekg/dns v1.1.38 h1:MtIY+fmHUVVgv1AXzmKMWcwdCYxTRPG1EDjpqF4RCEw=
|
||||
github.com/miekg/dns v1.1.38/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
|
@ -255,14 +282,14 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu
|
|||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.4.0 h1:7ks8ZkOP5/ujthUsT07rNv+nkLXCQWKNHuwzOAesEks=
|
||||
github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE=
|
||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
|
||||
|
@ -276,12 +303,22 @@ github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtb
|
|||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
|
||||
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
|
||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=
|
||||
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
|
||||
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
||||
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
|
@ -295,17 +332,15 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
|
|||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM=
|
||||
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
||||
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
|
||||
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
||||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
|
@ -347,14 +382,8 @@ github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFB
|
|||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4=
|
||||
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.3.0 h1:Uehi/mxLK0eiUc0H0++5tpMGTexB8wZ598MIgU8VpDM=
|
||||
github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
|
@ -365,36 +394,29 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg
|
|||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.5.1 h1:VHu76Lk0LSP1x254maIu2bplkWpfBWI+B+6fdoZprcg=
|
||||
github.com/spf13/afero v1.5.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
|
||||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4=
|
||||
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
||||
github.com/spf13/cobra v1.1.2 h1:frHO75w/dH7kEc+e2KYZZKY4+PLrp39OqI77oB8m0KQ=
|
||||
github.com/spf13/cobra v1.1.2/go.mod h1:ZjwqWkCg0LnXvLRIfTLdB4Y/MCO3gMHHJ2KFxQZy4xE=
|
||||
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
|
||||
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
|
@ -409,15 +431,19 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy
|
|||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/testcontainers/testcontainers-go v0.9.0 h1:ZyftCfROjGrKlxk3MOUn2DAzWrUtzY/mj17iAkdUIvI=
|
||||
github.com/testcontainers/testcontainers-go v0.9.0/go.mod h1:b22BFXhRbg4PJmeMVWh6ftqjyZHgiIl3w274e9r3C2E=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
||||
|
@ -425,6 +451,7 @@ go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
|||
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
|
@ -452,13 +479,10 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
|
|||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
|
@ -493,7 +517,6 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
|
||||
|
@ -503,11 +526,10 @@ golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7
|
|||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -536,37 +558,26 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e h1:AyodaIpKjppX+cBfTASF2E1US3H2JFBj920Ot3rtDjs=
|
||||
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930 h1:vRgIt+nup/B/BwIS0g2oC0haq0iqbV3ZA+u6+0TlNCo=
|
||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180810170437-e96c4e24768d/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
@ -590,6 +601,7 @@ golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc h1:NCy3Ohtk6Iny5V/reW2Ktyp
|
|||
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA=
|
||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114 h1:DnSr2mCsxyCE6ZgIkmcWUQY2R5cH/6wL7eIxEmQOMSE=
|
||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
@ -619,8 +631,8 @@ google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgX
|
|||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506 h1:uLBY0yHDCj2PMQ98KWDSIDFwn9zK2zh+tgWtbvPPBjI=
|
||||
google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98 h1:LCO0fg4kb6WwkXQXRQQgUYsFeFb5taTX5WAx5O/Vt28=
|
||||
google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
|
||||
|
@ -633,10 +645,11 @@ google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
|
|||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.34.0 h1:raiipEjMOIC/TO2AvyTxP25XFdLxNIBwzDh3FM3XztI=
|
||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.35.0 h1:TwIQcH3es+MojMVojxxfQ3l3OF2KzlRxML2xZq0kRo8=
|
||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc/examples v0.0.0-20210210171350-ce29c77c5fa1 h1:dtHfS+TF/MZg8X+DaxrDGiq+5z8OrIJJc1yX+8cQ0cU=
|
||||
google.golang.org/grpc/examples v0.0.0-20210210171350-ce29c77c5fa1/go.mod h1:Ly7ZA/ARzg8fnPU9TyZIxoz33sEUuWX7txiqs8lPTgE=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
|
@ -659,13 +672,13 @@ gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS
|
|||
gopkg.in/elazarl/goproxy.v1 v1.0.0-20180725130230-947c36da3153 h1:i2sumy6EgvN2dbX7HPhoDc7hLyoym3OYdU5HlvUUrpE=
|
||||
gopkg.in/elazarl/goproxy.v1 v1.0.0-20180725130230-947c36da3153/go.mod h1:xzjpkyedLMz3EXUTBbkRuuGPsxfsBX3Sy7J6kC9Gvoc=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
|
||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
|
@ -680,10 +693,11 @@ gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
|||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools v0.0.0-20181223230014-1083505acf35 h1:zpdCK+REwbk+rqjJmHhiCN6iBIigrZ39glqSF0P3KF0=
|
||||
gotest.tools v0.0.0-20181223230014-1083505acf35/go.mod h1:R//lfYlUuTOTfblYI3lGoAAAebUdzjvbmQsuB7Ykd90=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
|
|
@ -10,15 +10,12 @@ import (
|
|||
|
||||
"github.com/spf13/cobra"
|
||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
||||
"gitlab.com/inetmock/inetmock/pkg/api"
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit/sink"
|
||||
"gitlab.com/inetmock/inetmock/pkg/cert"
|
||||
"gitlab.com/inetmock/inetmock/pkg/config"
|
||||
"gitlab.com/inetmock/inetmock/pkg/health"
|
||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
"gitlab.com/inetmock/inetmock/pkg/path"
|
||||
"go.uber.org/multierr"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
@ -41,12 +38,12 @@ const (
|
|||
)
|
||||
|
||||
type App interface {
|
||||
api.PluginContext
|
||||
EventStream() audit.EventStream
|
||||
Config() config.Config
|
||||
Config() Config
|
||||
Checker() health.Checker
|
||||
EndpointManager() endpoint.EndpointManager
|
||||
HandlerRegistry() api.HandlerRegistry
|
||||
Logger() logging.Logger
|
||||
EndpointManager() endpoint.Orchestrator
|
||||
HandlerRegistry() endpoint.HandlerRegistry
|
||||
Context() context.Context
|
||||
RootCommand() *cobra.Command
|
||||
MustRun()
|
||||
|
@ -58,7 +55,7 @@ type App interface {
|
|||
|
||||
// WithHandlerRegistry builds up the handler registry
|
||||
// requires nothing
|
||||
WithHandlerRegistry(registrations ...api.Registration) App
|
||||
WithHandlerRegistry(registrations ...endpoint.Registration) App
|
||||
|
||||
// WithHealthChecker adds the health checker mechanism
|
||||
// requires nothing
|
||||
|
@ -115,12 +112,12 @@ func (a *app) Logger() logging.Logger {
|
|||
return val.(logging.Logger)
|
||||
}
|
||||
|
||||
func (a *app) Config() config.Config {
|
||||
func (a *app) Config() Config {
|
||||
val := a.ctx.Value(configKey)
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
return val.(config.Config)
|
||||
return val.(Config)
|
||||
}
|
||||
|
||||
func (a *app) CertStore() cert.Store {
|
||||
|
@ -139,12 +136,12 @@ func (a *app) Checker() health.Checker {
|
|||
return val.(health.Checker)
|
||||
}
|
||||
|
||||
func (a *app) EndpointManager() endpoint.EndpointManager {
|
||||
func (a *app) EndpointManager() endpoint.Orchestrator {
|
||||
val := a.ctx.Value(endpointManagerKey)
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
return val.(endpoint.EndpointManager)
|
||||
return val.(endpoint.Orchestrator)
|
||||
}
|
||||
|
||||
func (a *app) Audit() audit.Emitter {
|
||||
|
@ -163,12 +160,12 @@ func (a *app) EventStream() audit.EventStream {
|
|||
return val.(audit.EventStream)
|
||||
}
|
||||
|
||||
func (a *app) HandlerRegistry() api.HandlerRegistry {
|
||||
func (a *app) HandlerRegistry() endpoint.HandlerRegistry {
|
||||
val := a.ctx.Value(handlerRegistryKey)
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
return val.(api.HandlerRegistry)
|
||||
return val.(endpoint.HandlerRegistry)
|
||||
}
|
||||
|
||||
func (a *app) Context() context.Context {
|
||||
|
@ -192,8 +189,8 @@ func (a *app) WithCommands(cmds ...*cobra.Command) App {
|
|||
|
||||
// WithHandlerRegistry builds up the handler registry
|
||||
// requires nothing
|
||||
func (a *app) WithHandlerRegistry(registrations ...api.Registration) App {
|
||||
registry := api.NewHandlerRegistry()
|
||||
func (a *app) WithHandlerRegistry(registrations ...endpoint.Registration) App {
|
||||
registry := endpoint.NewHandlerRegistry()
|
||||
|
||||
for _, registration := range registrations {
|
||||
if err := registration(registry); err != nil {
|
||||
|
@ -242,11 +239,12 @@ func (a *app) WithLogger() App {
|
|||
// requires WithHandlerRegistry, WithHealthChecker and WithLogger
|
||||
func (a *app) WithEndpointManager() App {
|
||||
a.lateInitTasks = append(a.lateInitTasks, func(_ *cobra.Command, _ []string) (err error) {
|
||||
epMgr := endpoint.NewEndpointManager(
|
||||
epMgr := endpoint.NewOrchestrator(
|
||||
a.Context(),
|
||||
a.CertStore(),
|
||||
a.HandlerRegistry(),
|
||||
a.Logger().Named("EndpointManager"),
|
||||
a.Checker(),
|
||||
a,
|
||||
a.Audit(),
|
||||
a.Logger().Named("Orchestrator"),
|
||||
)
|
||||
|
||||
a.ctx = context.WithValue(a.ctx, endpointManagerKey, epMgr)
|
||||
|
@ -261,7 +259,7 @@ func (a *app) WithCertStore() App {
|
|||
a.lateInitTasks = append(a.lateInitTasks, func(cmd *cobra.Command, args []string) (err error) {
|
||||
var certStore cert.Store
|
||||
if certStore, err = cert.NewDefaultStore(
|
||||
a.Config(),
|
||||
a.Config().TLSConfig(),
|
||||
a.Logger().Named("CertStore"),
|
||||
); err != nil {
|
||||
return
|
||||
|
@ -310,7 +308,7 @@ func (a *app) WithEventStream() App {
|
|||
// requires nothing
|
||||
func (a *app) WithConfig() App {
|
||||
a.lateInitTasks = append(a.lateInitTasks, func(cmd *cobra.Command, _ []string) (err error) {
|
||||
cfg := config.CreateConfig(cmd.Flags())
|
||||
cfg := CreateConfig()
|
||||
if err = cfg.ReadConfig(configFilePath); err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -343,7 +341,9 @@ func NewApp(name, short string) App {
|
|||
|
||||
a.rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) (err error) {
|
||||
for _, initTask := range a.lateInitTasks {
|
||||
err = multierr.Append(err, initTask(cmd, args))
|
||||
if err = initTask(cmd, args); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1,20 +1,17 @@
|
|||
package config
|
||||
package app
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
||||
"gitlab.com/inetmock/inetmock/pkg/cert"
|
||||
"gitlab.com/inetmock/inetmock/pkg/path"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func CreateConfig(flags *pflag.FlagSet) Config {
|
||||
logger, _ := logging.CreateLogger()
|
||||
func CreateConfig() Config {
|
||||
configInstance := &config{
|
||||
logger: logger.Named("Config"),
|
||||
cfg: viper.New(),
|
||||
cfg: viper.New(),
|
||||
}
|
||||
|
||||
configInstance.cfg.SetConfigName("config")
|
||||
|
@ -23,7 +20,6 @@ func CreateConfig(flags *pflag.FlagSet) Config {
|
|||
configInstance.cfg.AddConfigPath("$HOME/.inetmock")
|
||||
configInstance.cfg.AddConfigPath(".")
|
||||
configInstance.cfg.SetEnvPrefix("INetMock")
|
||||
_ = configInstance.cfg.BindPFlags(flags)
|
||||
configInstance.cfg.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
|
||||
configInstance.cfg.AutomaticEnv()
|
||||
|
||||
|
@ -41,17 +37,15 @@ func CreateConfig(flags *pflag.FlagSet) Config {
|
|||
type Config interface {
|
||||
ReadConfig(configFilePath string) error
|
||||
ReadConfigString(config, format string) error
|
||||
Viper() *viper.Viper
|
||||
TLSConfig() CertOptions
|
||||
TLSConfig() cert.CertOptions
|
||||
APIConfig() RPC
|
||||
EndpointConfigs() map[string]EndpointConfig
|
||||
ListenerSpecs() map[string]endpoint.ListenerSpec
|
||||
}
|
||||
|
||||
type config struct {
|
||||
cfg *viper.Viper
|
||||
logger logging.Logger
|
||||
TLS CertOptions
|
||||
Endpoints map[string]EndpointConfig
|
||||
TLS cert.CertOptions
|
||||
Listeners map[string]endpoint.ListenerSpec
|
||||
API RPC
|
||||
}
|
||||
|
||||
|
@ -69,30 +63,23 @@ func (c *config) ReadConfigString(config, format string) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func (c *config) EndpointConfigs() map[string]EndpointConfig {
|
||||
return c.Endpoints
|
||||
func (c config) ListenerSpecs() map[string]endpoint.ListenerSpec {
|
||||
return c.Listeners
|
||||
}
|
||||
|
||||
func (c *config) TLSConfig() CertOptions {
|
||||
func (c config) TLSConfig() cert.CertOptions {
|
||||
return c.TLS
|
||||
}
|
||||
|
||||
func (c *config) Viper() *viper.Viper {
|
||||
return c.cfg
|
||||
}
|
||||
|
||||
func (c *config) ReadConfig(configFilePath string) (err error) {
|
||||
if configFilePath != "" && path.FileExists(configFilePath) {
|
||||
c.logger.Info(
|
||||
"loading config from passed config file path",
|
||||
zap.String("configFilePath", configFilePath),
|
||||
)
|
||||
c.cfg.SetConfigFile(configFilePath)
|
||||
}
|
||||
if err = c.cfg.ReadInConfig(); err != nil {
|
||||
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
||||
err = nil
|
||||
c.logger.Warn("failed to load config")
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
70
internal/app/config_test.go
Normal file
70
internal/app/config_test.go
Normal file
|
@ -0,0 +1,70 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
||||
)
|
||||
|
||||
func Test_config_ReadConfig(t *testing.T) {
|
||||
type args struct {
|
||||
config string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantListeners map[string]endpoint.ListenerSpec
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Test endpoints config",
|
||||
args: args{
|
||||
//language=yaml
|
||||
config: `
|
||||
listeners:
|
||||
tcp_80:
|
||||
name: ''
|
||||
protocol: tcp
|
||||
listenAddress: ''
|
||||
port: 80
|
||||
tcp_443:
|
||||
name: ''
|
||||
protocol: tcp
|
||||
listenAddress: ''
|
||||
port: 443
|
||||
`,
|
||||
},
|
||||
wantListeners: map[string]endpoint.ListenerSpec{
|
||||
"tcp_80": {
|
||||
Name: "",
|
||||
Protocol: "tcp",
|
||||
Address: "",
|
||||
Port: 80,
|
||||
Endpoints: nil,
|
||||
},
|
||||
"tcp_443": {
|
||||
Name: "",
|
||||
Protocol: "tcp",
|
||||
Address: "",
|
||||
Port: 443,
|
||||
Endpoints: nil,
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfg := CreateConfig()
|
||||
if err := cfg.ReadConfigString(tt.args.config, "yaml"); (err != nil) != tt.wantErr {
|
||||
t.Errorf("ReadConfig() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(tt.wantListeners, cfg.ListenerSpecs()) {
|
||||
t.Errorf("want = %v, got = %v", tt.wantListeners, cfg.ListenerSpecs())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
6
internal/app/constants.go
Normal file
6
internal/app/constants.go
Normal file
|
@ -0,0 +1,6 @@
|
|||
package app
|
||||
|
||||
const (
|
||||
EndpointsKey = "endpoints"
|
||||
OptionsKey = "options"
|
||||
)
|
|
@ -1,4 +1,4 @@
|
|||
package config
|
||||
package app
|
||||
|
||||
var (
|
||||
registeredDefaults = make(map[string]interface{})
|
|
@ -1,4 +1,4 @@
|
|||
package config
|
||||
package app
|
||||
|
||||
import "net/url"
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package config
|
||||
package app
|
||||
|
||||
import (
|
||||
"net/url"
|
31
internal/endpoint/api.go
Normal file
31
internal/endpoint/api.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
//go:generate mockgen -source=$GOFILE -destination=./../../internal/mock/endpoint/protocol_handler.mock.go -package=endpoint_mock
|
||||
|
||||
package endpoint
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"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 {
|
||||
Name() string
|
||||
Logger() logging.Logger
|
||||
CertStore() cert.Store
|
||||
Audit() audit.Emitter
|
||||
Context() context.Context
|
||||
Uplink() Uplink
|
||||
UnmarshalOptions(cfg interface{}) error
|
||||
}
|
||||
|
||||
type ProtocolHandler interface {
|
||||
Start(ctx Lifecycle) error
|
||||
}
|
||||
|
||||
type MultiplexHandler interface {
|
||||
ProtocolHandler
|
||||
Matchers() []cmux.Matcher
|
||||
}
|
|
@ -1,55 +1,42 @@
|
|||
//go:generate mockgen -source=$GOFILE -destination=./../../internal/mock/endpoints/endpoint.mock.go -package=endpoints_mock
|
||||
package endpoint
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gitlab.com/inetmock/inetmock/pkg/api"
|
||||
"gitlab.com/inetmock/inetmock/pkg/config"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type Endpoint interface {
|
||||
Id() uuid.UUID
|
||||
Start(ctx api.PluginContext) error
|
||||
Shutdown(ctx context.Context) error
|
||||
Name() string
|
||||
Handler() string
|
||||
Listen() string
|
||||
Port() uint16
|
||||
const (
|
||||
startupTimeoutDuration = 100 * time.Millisecond
|
||||
)
|
||||
|
||||
type Endpoint struct {
|
||||
Spec
|
||||
name string
|
||||
uplink Uplink
|
||||
}
|
||||
|
||||
type endpoint struct {
|
||||
id uuid.UUID
|
||||
name string
|
||||
handler api.ProtocolHandler
|
||||
config config.HandlerConfig
|
||||
}
|
||||
func (e Endpoint) Start(lifecycle Lifecycle) (err error) {
|
||||
startupResult := make(chan error)
|
||||
ctx, cancel := context.WithTimeout(lifecycle.Context(), startupTimeoutDuration)
|
||||
defer cancel()
|
||||
|
||||
func (e endpoint) Id() uuid.UUID {
|
||||
return e.id
|
||||
}
|
||||
go func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
lifecycle.Logger().Fatal("Startup error recovered", zap.Any("recovered", r))
|
||||
}
|
||||
}()
|
||||
|
||||
func (e endpoint) Name() string {
|
||||
return e.name
|
||||
}
|
||||
startupResult <- e.Handler.Start(lifecycle)
|
||||
}()
|
||||
|
||||
func (e endpoint) Handler() string {
|
||||
return e.config.HandlerName
|
||||
}
|
||||
select {
|
||||
case err = <-startupResult:
|
||||
case <-ctx.Done():
|
||||
err = ErrStartupTimeout
|
||||
}
|
||||
|
||||
func (e endpoint) Listen() string {
|
||||
return e.config.ListenAddress
|
||||
}
|
||||
|
||||
func (e endpoint) Port() uint16 {
|
||||
return e.config.Port
|
||||
}
|
||||
|
||||
func (e *endpoint) Start(ctx api.PluginContext) (err error) {
|
||||
return e.handler.Start(ctx, e.config)
|
||||
}
|
||||
|
||||
func (e *endpoint) Shutdown(ctx context.Context) (err error) {
|
||||
return e.handler.Shutdown(ctx)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1,179 +0,0 @@
|
|||
package endpoint
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gitlab.com/inetmock/inetmock/pkg/api"
|
||||
"gitlab.com/inetmock/inetmock/pkg/config"
|
||||
"gitlab.com/inetmock/inetmock/pkg/health"
|
||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const (
|
||||
startupTimeoutDuration = 100 * time.Millisecond
|
||||
)
|
||||
|
||||
type EndpointManager interface {
|
||||
RegisteredEndpoints() []Endpoint
|
||||
StartedEndpoints() []Endpoint
|
||||
CreateEndpoint(name string, multiHandlerConfig config.EndpointConfig) error
|
||||
StartEndpoints()
|
||||
ShutdownEndpoints()
|
||||
}
|
||||
|
||||
func NewEndpointManager(registry api.HandlerRegistry, logging logging.Logger, checker health.Checker, pluginContext api.PluginContext) EndpointManager {
|
||||
return &endpointManager{
|
||||
registry: registry,
|
||||
logger: logging,
|
||||
checker: checker,
|
||||
pluginContext: pluginContext,
|
||||
}
|
||||
}
|
||||
|
||||
type endpointManager struct {
|
||||
registry api.HandlerRegistry
|
||||
logger logging.Logger
|
||||
checker health.Checker
|
||||
pluginContext api.PluginContext
|
||||
registeredEndpoints []Endpoint
|
||||
properlyStartedEndpoints []Endpoint
|
||||
}
|
||||
|
||||
func (e endpointManager) RegisteredEndpoints() []Endpoint {
|
||||
return e.registeredEndpoints
|
||||
}
|
||||
|
||||
func (e endpointManager) StartedEndpoints() []Endpoint {
|
||||
return e.properlyStartedEndpoints
|
||||
}
|
||||
|
||||
func (e *endpointManager) CreateEndpoint(name string, endpointConfig config.EndpointConfig) error {
|
||||
for _, handlerConfig := range endpointConfig.HandlerConfigs() {
|
||||
if handler, ok := e.registry.HandlerForName(endpointConfig.Handler); ok {
|
||||
e.registeredEndpoints = append(e.registeredEndpoints, &endpoint{
|
||||
id: uuid.New(),
|
||||
name: name,
|
||||
handler: handler,
|
||||
config: handlerConfig,
|
||||
})
|
||||
} else {
|
||||
return fmt.Errorf("no matching handler registered for names %s", endpointConfig.Handler)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *endpointManager) StartEndpoints() {
|
||||
startTime := time.Now()
|
||||
for _, endpoint := range e.registeredEndpoints {
|
||||
endpointLogger := e.logger.With(
|
||||
zap.String("endpoint", endpoint.Name()),
|
||||
)
|
||||
endpointLogger.Info("Starting endpoint")
|
||||
if ok := startEndpoint(endpoint, e.pluginContext, endpointLogger); ok {
|
||||
_ = e.checker.RegisterCheck(
|
||||
endpointComponentName(endpoint),
|
||||
health.StaticResultCheckWithMessage(health.HEALTHY, "Successfully started"),
|
||||
)
|
||||
e.properlyStartedEndpoints = append(e.properlyStartedEndpoints, endpoint)
|
||||
endpointLogger.Info("successfully started endpoint")
|
||||
} else {
|
||||
_ = e.checker.RegisterCheck(
|
||||
endpointComponentName(endpoint),
|
||||
health.StaticResultCheckWithMessage(health.UNHEALTHY, "failed to start"),
|
||||
)
|
||||
endpointLogger.Error("error occurred during endpoint startup - will be skipped for now")
|
||||
}
|
||||
}
|
||||
endpointStartupDuration := time.Since(startTime)
|
||||
e.logger.Info(
|
||||
"Startup of all endpoints completed",
|
||||
zap.Duration("startupTime", endpointStartupDuration),
|
||||
)
|
||||
}
|
||||
|
||||
func (e *endpointManager) ShutdownEndpoints() {
|
||||
waitGroup := new(sync.WaitGroup)
|
||||
waitGroup.Add(len(e.properlyStartedEndpoints))
|
||||
|
||||
parentCtx, _ := context.WithTimeout(context.Background(), shutdownTimeout)
|
||||
|
||||
perHandlerTimeout := e.shutdownTimePerEndpoint()
|
||||
|
||||
for _, endpoint := range e.properlyStartedEndpoints {
|
||||
ctx, _ := context.WithTimeout(parentCtx, perHandlerTimeout)
|
||||
endpointLogger := e.logger.With(
|
||||
zap.String("endpoint", endpoint.Name()),
|
||||
)
|
||||
endpointLogger.Info("Triggering shutdown of endpoint")
|
||||
go shutdownEndpoint(ctx, endpoint, endpointLogger, waitGroup)
|
||||
}
|
||||
|
||||
waitGroup.Wait()
|
||||
}
|
||||
|
||||
func startEndpoint(ep Endpoint, ctx api.PluginContext, logger logging.Logger) (success bool) {
|
||||
startSuccessful := make(chan bool)
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logger.Fatal(
|
||||
"recovered panic during startup of endpoint",
|
||||
zap.Any("recovered", r),
|
||||
)
|
||||
startSuccessful <- false
|
||||
}
|
||||
}()
|
||||
|
||||
if err := ep.Start(ctx); err != nil {
|
||||
logger.Error(
|
||||
"failed to start endpoint",
|
||||
zap.Error(err),
|
||||
)
|
||||
startSuccessful <- false
|
||||
} else {
|
||||
startSuccessful <- true
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case success = <-startSuccessful:
|
||||
case <-time.After(startupTimeoutDuration):
|
||||
success = false
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func shutdownEndpoint(ctx context.Context, ep Endpoint, logger logging.Logger, wg *sync.WaitGroup) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logger.Fatal(
|
||||
"recovered panic during shutdown of endpoint",
|
||||
zap.Any("recovered", r),
|
||||
)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
if err := ep.Shutdown(ctx); err != nil {
|
||||
logger.Error(
|
||||
"Failed to shutdown endpoint",
|
||||
zap.Error(err),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *endpointManager) shutdownTimePerEndpoint() time.Duration {
|
||||
return time.Duration((float64(shutdownTimeout) * 0.9) / float64(len(e.properlyStartedEndpoints)))
|
||||
}
|
||||
|
||||
func endpointComponentName(ep Endpoint) string {
|
||||
return fmt.Sprintf("endpoint_%s", ep.Name())
|
||||
}
|
|
@ -1,139 +0,0 @@
|
|||
package endpoint
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
api_mock "gitlab.com/inetmock/inetmock/internal/mock/api"
|
||||
logging_mock "gitlab.com/inetmock/inetmock/internal/mock/logging"
|
||||
plugins_mock "gitlab.com/inetmock/inetmock/internal/mock/plugins"
|
||||
"gitlab.com/inetmock/inetmock/pkg/api"
|
||||
"gitlab.com/inetmock/inetmock/pkg/config"
|
||||
"gitlab.com/inetmock/inetmock/pkg/health"
|
||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
)
|
||||
|
||||
func Test_endpointManager_CreateEndpoint(t *testing.T) {
|
||||
type fields struct {
|
||||
logger logging.Logger
|
||||
registry api.HandlerRegistry
|
||||
}
|
||||
type args struct {
|
||||
name string
|
||||
multiHandlerConfig config.EndpointConfig
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
wantErr bool
|
||||
wantEndpoints int
|
||||
}{
|
||||
{
|
||||
name: "Test add endpoint",
|
||||
wantErr: false,
|
||||
wantEndpoints: 1,
|
||||
fields: fields{
|
||||
logger: func() logging.Logger {
|
||||
return logging_mock.NewMockLogger(gomock.NewController(t))
|
||||
}(),
|
||||
registry: func() api.HandlerRegistry {
|
||||
registry := plugins_mock.NewMockHandlerRegistry(gomock.NewController(t))
|
||||
registry.
|
||||
EXPECT().
|
||||
HandlerForName("sampleHandler").
|
||||
MinTimes(1).
|
||||
MaxTimes(1).
|
||||
Return(api_mock.NewMockProtocolHandler(gomock.NewController(t)), true)
|
||||
return registry
|
||||
}(),
|
||||
},
|
||||
args: args{
|
||||
name: "sampleEndpoint",
|
||||
multiHandlerConfig: config.EndpointConfig{
|
||||
Handler: "sampleHandler",
|
||||
Ports: []uint16{80},
|
||||
ListenAddress: "0.0.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Test add unknown handler",
|
||||
wantErr: true,
|
||||
wantEndpoints: 0,
|
||||
fields: fields{
|
||||
logger: func() logging.Logger {
|
||||
return logging_mock.NewMockLogger(gomock.NewController(t))
|
||||
}(),
|
||||
registry: func() api.HandlerRegistry {
|
||||
registry := plugins_mock.NewMockHandlerRegistry(gomock.NewController(t))
|
||||
registry.
|
||||
EXPECT().
|
||||
HandlerForName("sampleHandler").
|
||||
MinTimes(1).
|
||||
MaxTimes(1).
|
||||
Return(nil, false)
|
||||
return registry
|
||||
}(),
|
||||
},
|
||||
args: args{
|
||||
name: "sampleEndpoint",
|
||||
multiHandlerConfig: config.EndpointConfig{
|
||||
Handler: "sampleHandler",
|
||||
Ports: []uint16{80},
|
||||
ListenAddress: "0.0.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
e := NewEndpointManager(tt.fields.registry, tt.fields.logger, health.New(), nil)
|
||||
if err := e.CreateEndpoint(tt.args.name, tt.args.multiHandlerConfig); (err != nil) != tt.wantErr {
|
||||
t.Errorf("CreateEndpoint() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
|
||||
if len(e.RegisteredEndpoints()) != tt.wantEndpoints {
|
||||
t.Errorf("RegisteredEndpoints() = %d, want = 1", len(e.RegisteredEndpoints()))
|
||||
return
|
||||
}
|
||||
|
||||
if len(e.RegisteredEndpoints()) > 0 && e.RegisteredEndpoints()[0].Name() != tt.args.name {
|
||||
t.Errorf("Name() = %s, want = %s", e.RegisteredEndpoints()[0].Name(), tt.args.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_endpointManager_StartedEndpoints(t *testing.T) {
|
||||
type fields struct {
|
||||
logger logging.Logger
|
||||
registeredEndpoints []Endpoint
|
||||
properlyStartedEndpoints []Endpoint
|
||||
registry api.HandlerRegistry
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want []Endpoint
|
||||
}{
|
||||
{
|
||||
name: "",
|
||||
fields: fields{},
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
e := endpointManager{
|
||||
logger: tt.fields.logger,
|
||||
registeredEndpoints: tt.fields.registeredEndpoints,
|
||||
properlyStartedEndpoints: tt.fields.properlyStartedEndpoints,
|
||||
}
|
||||
if got := e.StartedEndpoints(); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("StartedEndpoints() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -7,38 +7,45 @@ import (
|
|||
"net"
|
||||
"unsafe"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
const (
|
||||
randomIPStrategyName = "random"
|
||||
incrementalIPStrategyName = "incremental"
|
||||
startIPConfigKey = "startIP"
|
||||
)
|
||||
|
||||
var (
|
||||
fallbackStrategies map[string]ResolverFactory
|
||||
defaultStartIPIncrementalStrategy = net.ParseIP("10.10.0.1")
|
||||
fallbackStrategies = map[string]ResolverFactory{
|
||||
incrementalIPStrategyName: func(args map[string]interface{}) ResolverFallback {
|
||||
tmp := struct {
|
||||
StartIP string
|
||||
}{}
|
||||
var startIp net.IP
|
||||
if err := mapstructure.Decode(args, &tmp); err == nil {
|
||||
startIp = net.ParseIP(tmp.StartIP)
|
||||
}
|
||||
if startIp == nil || len(startIp) == 0 {
|
||||
startIp = defaultStartIPIncrementalStrategy
|
||||
}
|
||||
return &incrementalIPFallback{
|
||||
latestIp: ipToInt32(startIp),
|
||||
}
|
||||
},
|
||||
randomIPStrategyName: func(map[string]interface{}) ResolverFallback {
|
||||
return &randomIPFallback{}
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type ResolverFactory func(conf *viper.Viper) ResolverFallback
|
||||
type ResolverFactory func(args map[string]interface{}) ResolverFallback
|
||||
|
||||
func init() {
|
||||
fallbackStrategies = make(map[string]ResolverFactory)
|
||||
fallbackStrategies[incrementalIPStrategyName] = func(conf *viper.Viper) ResolverFallback {
|
||||
return &incrementalIPFallback{
|
||||
latestIp: ipToInt32(net.ParseIP(conf.GetString(startIPConfigKey))),
|
||||
}
|
||||
}
|
||||
fallbackStrategies[randomIPStrategyName] = func(conf *viper.Viper) ResolverFallback {
|
||||
return &randomIPFallback{}
|
||||
}
|
||||
}
|
||||
|
||||
func CreateResolverFallback(name string, config *viper.Viper) ResolverFallback {
|
||||
func CreateResolverFallback(name string, args map[string]interface{}) ResolverFallback {
|
||||
if factory, ok := fallbackStrategies[name]; ok {
|
||||
return factory(config)
|
||||
return factory(args)
|
||||
} else {
|
||||
return fallbackStrategies[randomIPStrategyName](config)
|
||||
return fallbackStrategies[randomIPStrategyName](args)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,36 +2,35 @@ package mock
|
|||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"gitlab.com/inetmock/inetmock/pkg/api"
|
||||
"gitlab.com/inetmock/inetmock/pkg/config"
|
||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type dnsHandler struct {
|
||||
logger logging.Logger
|
||||
dnsServer []*dns.Server
|
||||
dnsServer *dns.Server
|
||||
}
|
||||
|
||||
func (d *dnsHandler) Start(pluginCtx api.PluginContext, config config.HandlerConfig) (err error) {
|
||||
func (d *dnsHandler) Start(lifecycle endpoint.Lifecycle) (err error) {
|
||||
var options dnsOptions
|
||||
if options, err = loadFromConfig(config.Options); err != nil {
|
||||
if options, err = loadFromConfig(lifecycle); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
listenAddr := config.ListenAddr()
|
||||
d.logger = pluginCtx.Logger().With(
|
||||
zap.String("handler_name", config.HandlerName),
|
||||
zap.String("address", listenAddr),
|
||||
d.logger = lifecycle.Logger().With(
|
||||
zap.String("handler_name", lifecycle.Name()),
|
||||
zap.String("address", lifecycle.Uplink().Addr().String()),
|
||||
)
|
||||
|
||||
handler := ®exHandler{
|
||||
handlerName: config.HandlerName,
|
||||
handlerName: lifecycle.Name(),
|
||||
fallback: options.Fallback,
|
||||
logger: pluginCtx.Logger(),
|
||||
auditEmitter: pluginCtx.Audit(),
|
||||
logger: lifecycle.Logger(),
|
||||
auditEmitter: lifecycle.Audit(),
|
||||
}
|
||||
|
||||
for _, rule := range options.Rules {
|
||||
|
@ -43,31 +42,24 @@ func (d *dnsHandler) Start(pluginCtx api.PluginContext, config config.HandlerCon
|
|||
handler.AddRule(rule)
|
||||
}
|
||||
|
||||
d.logger = d.logger.With(
|
||||
zap.String("address", listenAddr),
|
||||
)
|
||||
|
||||
d.dnsServer = []*dns.Server{
|
||||
{
|
||||
Addr: listenAddr,
|
||||
Net: "udp",
|
||||
Handler: handler,
|
||||
},
|
||||
{
|
||||
Addr: listenAddr,
|
||||
Net: "tcp",
|
||||
Handler: handler,
|
||||
},
|
||||
if lifecycle.Uplink().Listener != nil {
|
||||
d.dnsServer = &dns.Server{
|
||||
Listener: lifecycle.Uplink().Listener,
|
||||
Handler: handler,
|
||||
}
|
||||
} else {
|
||||
d.dnsServer = &dns.Server{
|
||||
PacketConn: lifecycle.Uplink().PacketConn,
|
||||
Handler: handler,
|
||||
}
|
||||
}
|
||||
|
||||
for _, dnsServer := range d.dnsServer {
|
||||
go d.startServer(dnsServer)
|
||||
}
|
||||
go d.startServer()
|
||||
return
|
||||
}
|
||||
|
||||
func (d *dnsHandler) startServer(dnsServer *dns.Server) {
|
||||
if err := dnsServer.ListenAndServe(); err != nil {
|
||||
func (d *dnsHandler) startServer() {
|
||||
if err := d.dnsServer.ActivateAndServe(); err != nil {
|
||||
d.logger.Error(
|
||||
"failed to start DNS server listener",
|
||||
zap.Error(err),
|
||||
|
@ -75,15 +67,11 @@ func (d *dnsHandler) startServer(dnsServer *dns.Server) {
|
|||
}
|
||||
}
|
||||
|
||||
func (d *dnsHandler) Shutdown(ctx context.Context) error {
|
||||
d.logger.Info("shutting down DNS mock")
|
||||
for _, dnsServer := range d.dnsServer {
|
||||
if err := dnsServer.ShutdownContext(ctx); err != nil {
|
||||
d.logger.Error(
|
||||
"failed to shutdown server",
|
||||
zap.Error(err),
|
||||
)
|
||||
}
|
||||
func (d *dnsHandler) shutdownOnEnd(ctx context.Context) {
|
||||
<-ctx.Done()
|
||||
shutdownCtx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
||||
defer cancel()
|
||||
if err := d.dnsServer.ShutdownContext(shutdownCtx); err != nil {
|
||||
d.logger.Error("failed to shutdown DNS server", zap.Error(err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
77
internal/endpoint/handler/dns/mock/handler_bench_test.go
Normal file
77
internal/endpoint/handler/dns/mock/handler_bench_test.go
Normal file
|
@ -0,0 +1,77 @@
|
|||
package mock
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/testcontainers/testcontainers-go"
|
||||
"gitlab.com/inetmock/inetmock/internal/test/integration"
|
||||
)
|
||||
|
||||
const (
|
||||
charSet = "abcdedfghijklmnopqrstABCDEFGHIJKLMNOP"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().Unix())
|
||||
}
|
||||
|
||||
func Benchmark_dnsHandler(b *testing.B) {
|
||||
var err error
|
||||
var endpoint string
|
||||
if endpoint, err = setupContainer(b, "53/udp"); err != nil {
|
||||
b.Errorf("setupContainer() error = %v", err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
resolv := resolver(endpoint)
|
||||
for pb.Next() {
|
||||
lookupCtx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
||||
_, err := resolv.LookupHost(lookupCtx, fmt.Sprintf("www.%s.com", randomString(8)))
|
||||
cancel()
|
||||
if err != nil {
|
||||
b.Errorf("LookupHost() error = %v", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func randomString(length int) (result string) {
|
||||
buffer := strings.Builder{}
|
||||
for i := 0; i < length; i++ {
|
||||
buffer.WriteByte(charSet[rand.Intn(len(charSet))])
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
func setupContainer(b *testing.B, port string) (httpEndpoint string, err error) {
|
||||
b.Helper()
|
||||
|
||||
startupCtx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
defer cancel()
|
||||
var inetMockContainer testcontainers.Container
|
||||
if inetMockContainer, err = integration.SetupINetMockContainer(startupCtx, b, port); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
httpEndpoint, err = inetMockContainer.PortEndpoint(startupCtx, nat.Port(port), "")
|
||||
return
|
||||
}
|
||||
|
||||
func resolver(endpoint string) net.Resolver {
|
||||
return net.Resolver{
|
||||
PreferGo: true,
|
||||
Dial: func(ctx context.Context, network, address string) (conn net.Conn, err error) {
|
||||
dialer := net.Dialer{}
|
||||
return dialer.DialContext(ctx, "udp", endpoint)
|
||||
},
|
||||
}
|
||||
}
|
|
@ -4,11 +4,7 @@ import (
|
|||
"net"
|
||||
"regexp"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
const (
|
||||
fallbackArgsConfigKey = "fallback.args"
|
||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
||||
)
|
||||
|
||||
type resolverRule struct {
|
||||
|
@ -21,7 +17,7 @@ type dnsOptions struct {
|
|||
Fallback ResolverFallback
|
||||
}
|
||||
|
||||
func loadFromConfig(config *viper.Viper) (options dnsOptions, err error) {
|
||||
func loadFromConfig(lifecycle endpoint.Lifecycle) (options dnsOptions, err error) {
|
||||
type rule struct {
|
||||
Pattern string
|
||||
Response string
|
||||
|
@ -29,6 +25,7 @@ func loadFromConfig(config *viper.Viper) (options dnsOptions, err error) {
|
|||
|
||||
type fallback struct {
|
||||
Strategy string
|
||||
Args map[string]interface{}
|
||||
}
|
||||
|
||||
opts := struct {
|
||||
|
@ -36,7 +33,7 @@ func loadFromConfig(config *viper.Viper) (options dnsOptions, err error) {
|
|||
Fallback fallback
|
||||
}{}
|
||||
|
||||
err = config.Unmarshal(&opts)
|
||||
err = lifecycle.UnmarshalOptions(&opts)
|
||||
|
||||
for _, rule := range opts.Rules {
|
||||
var err error
|
||||
|
@ -53,7 +50,7 @@ func loadFromConfig(config *viper.Viper) (options dnsOptions, err error) {
|
|||
|
||||
options.Fallback = CreateResolverFallback(
|
||||
opts.Fallback.Strategy,
|
||||
config.Sub(fallbackArgsConfigKey),
|
||||
opts.Fallback.Args,
|
||||
)
|
||||
|
||||
return
|
||||
|
|
|
@ -2,7 +2,7 @@ package mock
|
|||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"gitlab.com/inetmock/inetmock/pkg/api"
|
||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
||||
"gitlab.com/inetmock/inetmock/pkg/metrics"
|
||||
)
|
||||
|
||||
|
@ -17,7 +17,7 @@ var (
|
|||
requestDurationHistogram *prometheus.HistogramVec
|
||||
)
|
||||
|
||||
func AddDNSMock(registry api.HandlerRegistry) (err error) {
|
||||
func AddDNSMock(registry endpoint.HandlerRegistry) (err error) {
|
||||
if totalHandledRequestsCounter, err = metrics.Counter(
|
||||
name,
|
||||
"handled_requests_total",
|
||||
|
@ -46,7 +46,7 @@ func AddDNSMock(registry api.HandlerRegistry) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
registry.RegisterHandler(name, func() api.ProtocolHandler {
|
||||
registry.RegisterHandler(name, func() endpoint.ProtocolHandler {
|
||||
return &dnsHandler{}
|
||||
})
|
||||
|
||||
|
|
|
@ -23,11 +23,11 @@ func EventFromRequest(request *http.Request, app audit.AppProtocol) audit.Event
|
|||
ProtocolDetails: httpDetails,
|
||||
}
|
||||
|
||||
if request.TLS != nil {
|
||||
if state, ok := tlsConnectionState(request.Context()); ok {
|
||||
ev.TLS = &audit.TLSDetails{
|
||||
Version: audit.TLSVersionToEntity(request.TLS.Version).String(),
|
||||
CipherSuite: tls.CipherSuiteName(request.TLS.CipherSuite),
|
||||
ServerName: request.TLS.ServerName,
|
||||
Version: audit.TLSVersionToEntity(state.Version).String(),
|
||||
CipherSuite: tls.CipherSuiteName(state.CipherSuite),
|
||||
ServerName: state.ServerName,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,10 @@ package http
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
|
||||
"github.com/soheilhy/cmux"
|
||||
)
|
||||
|
||||
type httpContextKey string
|
||||
|
@ -10,14 +13,35 @@ type httpContextKey string
|
|||
const (
|
||||
remoteAddrKey httpContextKey = "gitlab.com/inetmock/inetmock/internal/endpoint/handler/http/context/remoteAddr"
|
||||
localAddrKey httpContextKey = "gitlab.com/inetmock/inetmock/internal/endpoint/handler/http/context/localAddr"
|
||||
tlsStateKey httpContextKey = "gitlab.com/inetmock/inetmock/internal/endpoint/handler/http/context/tlsState"
|
||||
)
|
||||
|
||||
func StoreConnPropertiesInContext(ctx context.Context, c net.Conn) context.Context {
|
||||
ctx = context.WithValue(ctx, remoteAddrKey, c.RemoteAddr())
|
||||
ctx = context.WithValue(ctx, localAddrKey, c.LocalAddr())
|
||||
ctx = addTLSConnectionStateToContext(ctx, c)
|
||||
return ctx
|
||||
}
|
||||
|
||||
func addTLSConnectionStateToContext(ctx context.Context, c net.Conn) context.Context {
|
||||
switch subConn := c.(type) {
|
||||
case *tls.Conn:
|
||||
return context.WithValue(ctx, tlsStateKey, subConn.ConnectionState())
|
||||
case *cmux.MuxConn:
|
||||
return addTLSConnectionStateToContext(ctx, subConn.Conn)
|
||||
default:
|
||||
return ctx
|
||||
}
|
||||
}
|
||||
|
||||
func tlsConnectionState(ctx context.Context) (tls.ConnectionState, bool) {
|
||||
val := ctx.Value(tlsStateKey)
|
||||
if val == nil {
|
||||
return tls.ConnectionState{}, false
|
||||
}
|
||||
return val.(tls.ConnectionState), true
|
||||
}
|
||||
|
||||
func localAddr(ctx context.Context) net.Addr {
|
||||
val := ctx.Value(localAddrKey)
|
||||
if val == nil {
|
||||
|
|
|
@ -2,14 +2,13 @@ package mock
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/soheilhy/cmux"
|
||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
||||
imHttp "gitlab.com/inetmock/inetmock/internal/endpoint/handler/http"
|
||||
"gitlab.com/inetmock/inetmock/pkg/api"
|
||||
"gitlab.com/inetmock/inetmock/pkg/config"
|
||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
@ -25,71 +24,62 @@ type httpHandler struct {
|
|||
server *http.Server
|
||||
}
|
||||
|
||||
func (p *httpHandler) Start(ctx api.PluginContext, config config.HandlerConfig) (err error) {
|
||||
p.logger = ctx.Logger().With(
|
||||
func (p *httpHandler) Matchers() []cmux.Matcher {
|
||||
return []cmux.Matcher{cmux.HTTP1()}
|
||||
}
|
||||
|
||||
func (p *httpHandler) Start(lifecycle endpoint.Lifecycle) (err error) {
|
||||
p.logger = lifecycle.Logger().With(
|
||||
zap.String("protocol_handler", name),
|
||||
)
|
||||
|
||||
var options httpOptions
|
||||
if options, err = loadFromConfig(config.Options); err != nil {
|
||||
if options, err = loadFromConfig(lifecycle); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
p.logger = p.logger.With(
|
||||
zap.String("handler_name", config.HandlerName),
|
||||
zap.String("address", config.ListenAddr()),
|
||||
zap.String("address", lifecycle.Uplink().Addr().String()),
|
||||
)
|
||||
|
||||
router := &RegexpHandler{
|
||||
logger: p.logger,
|
||||
emitter: ctx.Audit(),
|
||||
handlerName: config.HandlerName,
|
||||
emitter: lifecycle.Audit(),
|
||||
handlerName: lifecycle.Name(),
|
||||
}
|
||||
p.server = &http.Server{
|
||||
Addr: config.ListenAddr(),
|
||||
Handler: router,
|
||||
ConnContext: imHttp.StoreConnPropertiesInContext,
|
||||
}
|
||||
|
||||
if options.TLS {
|
||||
p.server.TLSConfig = ctx.CertStore().TLSConfig()
|
||||
p.server.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler))
|
||||
}
|
||||
|
||||
for _, rule := range options.Rules {
|
||||
router.setupRoute(rule)
|
||||
}
|
||||
|
||||
go p.startServer(options.TLS)
|
||||
go p.startServer(lifecycle.Uplink().Listener)
|
||||
go p.shutdownOnCancel(lifecycle.Context())
|
||||
return
|
||||
}
|
||||
|
||||
func (p *httpHandler) Shutdown(ctx context.Context) (err error) {
|
||||
func (p *httpHandler) shutdownOnCancel(ctx context.Context) {
|
||||
<-ctx.Done()
|
||||
p.logger.Info("Shutting down HTTP mock")
|
||||
if err = p.server.Shutdown(ctx); err != nil {
|
||||
if err := p.server.Close(); err != nil {
|
||||
p.logger.Error(
|
||||
"failed to shutdown HTTP server",
|
||||
zap.Error(err),
|
||||
)
|
||||
err = fmt.Errorf(
|
||||
"failed to shutdown HTTP server: %w",
|
||||
err,
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *httpHandler) startServer(tls bool) {
|
||||
var listen func() error
|
||||
if tls {
|
||||
listen = func() error {
|
||||
return p.server.ListenAndServeTLS("", "")
|
||||
func (p *httpHandler) startServer(listener net.Listener) {
|
||||
defer func() {
|
||||
if err := listener.Close(); err != nil {
|
||||
p.logger.Warn("failed to close listener", zap.Error(err))
|
||||
}
|
||||
} else {
|
||||
listen = p.server.ListenAndServe
|
||||
}
|
||||
|
||||
if err := listen(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
}()
|
||||
if err := p.server.Serve(listener); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
p.logger.Error(
|
||||
"failed to start http listener",
|
||||
zap.Error(err),
|
||||
|
|
155
internal/endpoint/handler/http/mock/handler_bench_test.go
Normal file
155
internal/endpoint/handler/http/mock/handler_bench_test.go
Normal file
|
@ -0,0 +1,155 @@
|
|||
package mock_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/testcontainers/testcontainers-go"
|
||||
"gitlab.com/inetmock/inetmock/internal/test/integration"
|
||||
)
|
||||
|
||||
const (
|
||||
charSet = "abcdedfghijklmnopqrstABCDEFGHIJKLMNOP"
|
||||
)
|
||||
|
||||
var (
|
||||
availableExtensions = []string{"gif", "html", "ico", "jpg", "png", "txt"}
|
||||
)
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().Unix())
|
||||
}
|
||||
|
||||
func Benchmark_httpHandler(b *testing.B) {
|
||||
type benchmark struct {
|
||||
name string
|
||||
port string
|
||||
scheme string
|
||||
}
|
||||
benchmarks := []benchmark{
|
||||
{
|
||||
name: "HTTP",
|
||||
port: "80/tcp",
|
||||
scheme: "http",
|
||||
},
|
||||
{
|
||||
name: "HTTPS",
|
||||
port: "443/tcp",
|
||||
scheme: "https",
|
||||
},
|
||||
}
|
||||
scenario := func(bm benchmark) func(bm *testing.B) {
|
||||
return func(b *testing.B) {
|
||||
var err error
|
||||
var endpoint string
|
||||
if endpoint, err = setupContainer(b, bm.scheme, bm.port); err != nil {
|
||||
b.Errorf("setupContainer() error = %v", err)
|
||||
}
|
||||
|
||||
var httpClient *http.Client
|
||||
if httpClient, err = setupHTTPClient(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
extension := availableExtensions[rand.Intn(len(availableExtensions))]
|
||||
|
||||
reqUrl, _ := url.Parse(fmt.Sprintf("%s/%s.%s", endpoint, randomString(15), extension))
|
||||
|
||||
req := &http.Request{
|
||||
Method: http.MethodGet,
|
||||
URL: reqUrl,
|
||||
Close: false,
|
||||
Host: "www.inetmock.com",
|
||||
}
|
||||
if resp, err := httpClient.Do(req); err != nil {
|
||||
b.Error(err)
|
||||
} else if resp.StatusCode != 200 {
|
||||
b.Errorf("Got status code %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
for _, bm := range benchmarks {
|
||||
b.Run(bm.name, scenario(bm))
|
||||
}
|
||||
}
|
||||
|
||||
func randomString(length int) (result string) {
|
||||
buffer := strings.Builder{}
|
||||
for i := 0; i < length; i++ {
|
||||
buffer.WriteByte(charSet[rand.Intn(len(charSet))])
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
func setupContainer(b *testing.B, scheme, port string) (httpEndpoint string, err error) {
|
||||
b.Helper()
|
||||
|
||||
startupCtx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
defer cancel()
|
||||
var inetMockContainer testcontainers.Container
|
||||
if inetMockContainer, err = integration.SetupINetMockContainer(startupCtx, b, port); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
httpEndpoint, err = inetMockContainer.PortEndpoint(startupCtx, nat.Port(port), scheme)
|
||||
return
|
||||
}
|
||||
|
||||
func setupHTTPClient() (client *http.Client, err error) {
|
||||
_, fileName, _, _ := runtime.Caller(0)
|
||||
|
||||
var repoRoot string
|
||||
if repoRoot, err = filepath.Abs(filepath.Join(filepath.Dir(fileName), "..", "..", "..", "..", "..")); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var demoCABytes []byte
|
||||
if demoCABytes, err = ioutil.ReadFile(filepath.Join(repoRoot, "assets", "demoCA", "ca.pem")); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
rootCaPool := x509.NewCertPool()
|
||||
if !rootCaPool.AppendCertsFromPEM(demoCABytes) {
|
||||
err = errors.New("failed to add CA key")
|
||||
return
|
||||
}
|
||||
|
||||
client = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).DialContext,
|
||||
TLSClientConfig: &tls.Config{
|
||||
RootCAs: rootCaPool,
|
||||
},
|
||||
ForceAttemptHTTP2: true,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -1,147 +0,0 @@
|
|||
package mock_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/spf13/viper"
|
||||
"gitlab.com/inetmock/inetmock/internal/endpoint/handler/http/mock"
|
||||
api_mock "gitlab.com/inetmock/inetmock/internal/mock/api"
|
||||
audit_mock "gitlab.com/inetmock/inetmock/internal/mock/audit"
|
||||
"gitlab.com/inetmock/inetmock/pkg/api"
|
||||
"gitlab.com/inetmock/inetmock/pkg/config"
|
||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var (
|
||||
testLogger = logging.NewLogger(zap.NewNop())
|
||||
availableExtensions = []string{"gif", "html", "ico", "jpg", "png", "txt"}
|
||||
charSet = "abcdedfghijklmnopqrstABCDEFGHIJKLMNOP"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().Unix())
|
||||
}
|
||||
|
||||
func Benchmark_httpHandler(b *testing.B) {
|
||||
ctrl := gomock.NewController(b)
|
||||
defer ctrl.Finish()
|
||||
|
||||
listenPort := randomHighPort()
|
||||
_, handler := setupHandler(b, ctrl, listenPort)
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
extension := availableExtensions[rand.Intn(len(availableExtensions))]
|
||||
if resp, err := http.Get(fmt.Sprintf("http://localhost:%d/%s.%s", listenPort, randomString(15), extension)); err != nil {
|
||||
b.Error(err)
|
||||
} else if resp.StatusCode != 200 {
|
||||
b.Error("")
|
||||
}
|
||||
}
|
||||
|
||||
defer handler.Shutdown(context.Background())
|
||||
}
|
||||
|
||||
func randomString(length int) (result string) {
|
||||
buffer := strings.Builder{}
|
||||
for i := 0; i < length; i++ {
|
||||
buffer.WriteByte(charSet[rand.Intn(len(charSet))])
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
func setupHandler(b *testing.B, ctrl *gomock.Controller, listenPort uint16) (api.HandlerRegistry, api.ProtocolHandler) {
|
||||
b.Helper()
|
||||
|
||||
registry := api.NewHandlerRegistry()
|
||||
if err := mock.AddHTTPMock(registry); err != nil {
|
||||
b.Errorf("AddHTTPMock() error = %v", err)
|
||||
}
|
||||
handler, ok := registry.HandlerForName("http_mock")
|
||||
if !ok {
|
||||
b.Error("handler not registered")
|
||||
}
|
||||
|
||||
emitter := audit_mock.NewMockEmitter(ctrl)
|
||||
emitter.EXPECT().Emit(gomock.Any()).AnyTimes()
|
||||
|
||||
mockApp := api_mock.NewMockPluginContext(ctrl)
|
||||
mockApp.EXPECT().
|
||||
Logger().
|
||||
Return(testLogger)
|
||||
|
||||
mockApp.EXPECT().
|
||||
Audit().
|
||||
Return(emitter)
|
||||
|
||||
v := viper.New()
|
||||
v.Set("rules", []map[string]string{
|
||||
{
|
||||
"pattern": ".*\\.(?i)gif",
|
||||
"response": "./../../assets/fakeFiles/default.gif",
|
||||
},
|
||||
{
|
||||
"pattern": ".*\\.(?i)html",
|
||||
"response": "./../../assets/fakeFiles/default.html",
|
||||
},
|
||||
{
|
||||
"pattern": ".*\\.(?i)ico",
|
||||
"response": "./../../assets/fakeFiles/default.ico",
|
||||
},
|
||||
{
|
||||
"pattern": ".*\\.(?i)jpg",
|
||||
"response": "./../../assets/fakeFiles/default.jpg",
|
||||
},
|
||||
{
|
||||
"pattern": ".*\\.(?i)png",
|
||||
"response": "./../../assets/fakeFiles/default.png",
|
||||
},
|
||||
{
|
||||
"pattern": ".*\\.(?i)txt",
|
||||
"response": "./../../assets/fakeFiles/default.txt",
|
||||
},
|
||||
})
|
||||
|
||||
handlerConfig := config.HandlerConfig{
|
||||
HandlerName: "http_test",
|
||||
Port: listenPort,
|
||||
ListenAddress: "localhost",
|
||||
Options: v,
|
||||
}
|
||||
|
||||
if err := handler.Start(mockApp, handlerConfig); err != nil {
|
||||
b.Error(err)
|
||||
b.FailNow()
|
||||
}
|
||||
|
||||
return registry, handler
|
||||
}
|
||||
|
||||
func randomHighPort() uint16 {
|
||||
var err error
|
||||
var listener net.Listener
|
||||
defer func() {
|
||||
if listener != nil {
|
||||
_ = listener.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
if listener, err = net.Listen("tcp", ":0"); err == nil {
|
||||
parts := strings.Split(listener.Addr().String(), ":")
|
||||
port, _ := strconv.Atoi(parts[len(parts)-1])
|
||||
return uint16(port)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ import (
|
|||
"path/filepath"
|
||||
"regexp"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -50,11 +50,10 @@ func (tr targetRule) Response() string {
|
|||
}
|
||||
|
||||
type httpOptions struct {
|
||||
TLS bool
|
||||
Rules []targetRule
|
||||
}
|
||||
|
||||
func loadFromConfig(config *viper.Viper) (options httpOptions, err error) {
|
||||
func loadFromConfig(lifecycle endpoint.Lifecycle) (options httpOptions, err error) {
|
||||
type tmpCfg struct {
|
||||
Pattern string
|
||||
Response string
|
||||
|
@ -63,16 +62,13 @@ func loadFromConfig(config *viper.Viper) (options httpOptions, err error) {
|
|||
}
|
||||
|
||||
tmpRules := struct {
|
||||
TLS bool
|
||||
Rules []tmpCfg
|
||||
}{}
|
||||
|
||||
if err = config.Unmarshal(&tmpRules); err != nil {
|
||||
if err = lifecycle.UnmarshalOptions(&tmpRules); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
options.TLS = tmpRules.TLS
|
||||
|
||||
for _, i := range tmpRules.Rules {
|
||||
var rulePattern *regexp.Regexp
|
||||
var matchTargetValue RequestMatchTarget
|
||||
|
|
|
@ -4,15 +4,16 @@ import (
|
|||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
endpoint_mock "gitlab.com/inetmock/inetmock/internal/mock/endpoint"
|
||||
)
|
||||
|
||||
func Test_loadFromConfig(t *testing.T) {
|
||||
type args struct {
|
||||
config string
|
||||
config map[string]interface{}
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
|
@ -23,11 +24,18 @@ func Test_loadFromConfig(t *testing.T) {
|
|||
{
|
||||
name: "Parse default config",
|
||||
args: args{
|
||||
config: `
|
||||
rules:
|
||||
- pattern: ".*\\.(?i)exe"
|
||||
response: ./assets/fakeFiles/sample.exe
|
||||
`,
|
||||
config: map[string]interface{}{
|
||||
"rules": []struct {
|
||||
Pattern string
|
||||
Matcher string
|
||||
Response string
|
||||
}{
|
||||
{
|
||||
Pattern: ".*\\.(?i)exe",
|
||||
Response: "./assets/fakeFiles/sample.exe",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantOptions: httpOptions{
|
||||
Rules: []targetRule{
|
||||
|
@ -47,12 +55,19 @@ rules:
|
|||
{
|
||||
name: "Parse config with path matcher",
|
||||
args: args{
|
||||
config: `
|
||||
rules:
|
||||
- pattern: ".*\\.(?i)exe"
|
||||
matcher: Path
|
||||
response: ./assets/fakeFiles/sample.exe
|
||||
`,
|
||||
config: map[string]interface{}{
|
||||
"rules": []struct {
|
||||
Pattern string
|
||||
Matcher string
|
||||
Response string
|
||||
}{
|
||||
{
|
||||
Pattern: ".*\\.(?i)exe",
|
||||
Response: "./assets/fakeFiles/sample.exe",
|
||||
Matcher: "Path",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantOptions: httpOptions{
|
||||
Rules: []targetRule{
|
||||
|
@ -72,13 +87,21 @@ rules:
|
|||
{
|
||||
name: "Parse config with header matcher",
|
||||
args: args{
|
||||
config: `
|
||||
rules:
|
||||
- pattern: "^application/octet-stream$"
|
||||
target: Content-Type
|
||||
matcher: Header
|
||||
response: ./assets/fakeFiles/sample.exe
|
||||
`,
|
||||
config: map[string]interface{}{
|
||||
"rules": []struct {
|
||||
Pattern string
|
||||
Matcher string
|
||||
Target string
|
||||
Response string
|
||||
}{
|
||||
{
|
||||
Pattern: "^application/octet-stream$",
|
||||
Response: "./assets/fakeFiles/sample.exe",
|
||||
Target: "Content-Type",
|
||||
Matcher: "Header",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantOptions: httpOptions{
|
||||
Rules: []targetRule{
|
||||
|
@ -98,17 +121,24 @@ rules:
|
|||
{
|
||||
name: "Parse config with header matcher and TLS true",
|
||||
args: args{
|
||||
config: `
|
||||
tls: true
|
||||
rules:
|
||||
- pattern: "^application/octet-stream$"
|
||||
target: Content-Type
|
||||
matcher: Header
|
||||
response: ./assets/fakeFiles/sample.exe
|
||||
`,
|
||||
config: map[string]interface{}{
|
||||
"tls": true,
|
||||
"rules": []struct {
|
||||
Pattern string
|
||||
Matcher string
|
||||
Target string
|
||||
Response string
|
||||
}{
|
||||
{
|
||||
Pattern: "^application/octet-stream$",
|
||||
Response: "./assets/fakeFiles/sample.exe",
|
||||
Target: "Content-Type",
|
||||
Matcher: "Header",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantOptions: httpOptions{
|
||||
TLS: true,
|
||||
Rules: []targetRule{
|
||||
{
|
||||
pattern: regexp.MustCompile("^application/octet-stream$"),
|
||||
|
@ -126,10 +156,15 @@ rules:
|
|||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
v := viper.New()
|
||||
v.SetConfigType("yaml")
|
||||
_ = v.ReadConfig(strings.NewReader(tt.args.config))
|
||||
gotOptions, err := loadFromConfig(v)
|
||||
ctrl := gomock.NewController(t)
|
||||
t.Cleanup(ctrl.Finish)
|
||||
lcMock := endpoint_mock.NewMockLifecycle(ctrl)
|
||||
|
||||
lcMock.EXPECT().UnmarshalOptions(gomock.Any()).Do(func(cfg interface{}) {
|
||||
_ = mapstructure.Decode(tt.args.config, cfg)
|
||||
})
|
||||
|
||||
gotOptions, err := loadFromConfig(lcMock)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("loadFromConfig() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
|
|
|
@ -2,7 +2,7 @@ package mock
|
|||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"gitlab.com/inetmock/inetmock/pkg/api"
|
||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
||||
"gitlab.com/inetmock/inetmock/pkg/metrics"
|
||||
)
|
||||
|
||||
|
@ -11,7 +11,7 @@ var (
|
|||
requestDurationHistogram *prometheus.HistogramVec
|
||||
)
|
||||
|
||||
func AddHTTPMock(registry api.HandlerRegistry) (err error) {
|
||||
func AddHTTPMock(registry endpoint.HandlerRegistry) (err error) {
|
||||
if totalRequestCounter == nil {
|
||||
if totalRequestCounter, err = metrics.Counter(
|
||||
name,
|
||||
|
@ -36,7 +36,7 @@ func AddHTTPMock(registry api.HandlerRegistry) (err error) {
|
|||
}
|
||||
}
|
||||
|
||||
registry.RegisterHandler(name, func() api.ProtocolHandler {
|
||||
registry.RegisterHandler(name, func() endpoint.ProtocolHandler {
|
||||
return &httpHandler{}
|
||||
})
|
||||
|
||||
|
|
|
@ -3,12 +3,12 @@ package proxy
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/soheilhy/cmux"
|
||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
||||
imHttp "gitlab.com/inetmock/inetmock/internal/endpoint/handler/http"
|
||||
"gitlab.com/inetmock/inetmock/pkg/api"
|
||||
"gitlab.com/inetmock/inetmock/pkg/config"
|
||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
"go.uber.org/zap"
|
||||
"gopkg.in/elazarl/goproxy.v1"
|
||||
|
@ -24,46 +24,49 @@ type httpProxy struct {
|
|||
server *http.Server
|
||||
}
|
||||
|
||||
func (h *httpProxy) Start(ctx api.PluginContext, cfg config.HandlerConfig) (err error) {
|
||||
func (h *httpProxy) Matchers() []cmux.Matcher {
|
||||
return []cmux.Matcher{cmux.HTTP1()}
|
||||
}
|
||||
|
||||
func (h *httpProxy) Start(lifecycle endpoint.Lifecycle) (err error) {
|
||||
var opts httpProxyOptions
|
||||
if err = cfg.Options.Unmarshal(&opts); err != nil {
|
||||
if err = lifecycle.UnmarshalOptions(&opts); err != nil {
|
||||
return
|
||||
}
|
||||
listenAddr := cfg.ListenAddr()
|
||||
|
||||
h.server = &http.Server{
|
||||
Addr: listenAddr,
|
||||
Handler: h.proxy,
|
||||
ConnContext: imHttp.StoreConnPropertiesInContext,
|
||||
}
|
||||
h.logger = h.logger.With(
|
||||
zap.String("handler_name", cfg.HandlerName),
|
||||
zap.String("address", listenAddr),
|
||||
zap.String("handler_name", lifecycle.Name()),
|
||||
zap.String("address", lifecycle.Uplink().Addr().String()),
|
||||
)
|
||||
|
||||
tlsConfig := ctx.CertStore().TLSConfig()
|
||||
tlsConfig := lifecycle.CertStore().TLSConfig()
|
||||
|
||||
proxyHandler := &proxyHttpHandler{
|
||||
handlerName: cfg.HandlerName,
|
||||
handlerName: lifecycle.Name(),
|
||||
options: opts,
|
||||
logger: h.logger,
|
||||
emitter: ctx.Audit(),
|
||||
emitter: lifecycle.Audit(),
|
||||
}
|
||||
|
||||
proxyHTTPSHandler := &proxyHttpsHandler{
|
||||
handlerName: cfg.HandlerName,
|
||||
tlsConfig: tlsConfig,
|
||||
logger: h.logger,
|
||||
emitter: ctx.Audit(),
|
||||
options: opts,
|
||||
tlsConfig: tlsConfig,
|
||||
emitter: lifecycle.Audit(),
|
||||
}
|
||||
|
||||
h.proxy.OnRequest().Do(proxyHandler)
|
||||
h.proxy.OnRequest().HandleConnect(proxyHTTPSHandler)
|
||||
go h.startProxy()
|
||||
go h.startProxy(lifecycle.Uplink().Listener)
|
||||
go h.shutdownOnContextDone(lifecycle.Context())
|
||||
return
|
||||
}
|
||||
|
||||
func (h *httpProxy) startProxy() {
|
||||
if err := h.server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
func (h *httpProxy) startProxy(listener net.Listener) {
|
||||
if err := h.server.Serve(listener); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
h.logger.Error(
|
||||
"failed to start proxy server",
|
||||
zap.Error(err),
|
||||
|
@ -71,18 +74,15 @@ func (h *httpProxy) startProxy() {
|
|||
}
|
||||
}
|
||||
|
||||
func (h *httpProxy) Shutdown(ctx context.Context) (err error) {
|
||||
func (h *httpProxy) shutdownOnContextDone(ctx context.Context) {
|
||||
<-ctx.Done()
|
||||
var err error
|
||||
h.logger.Info("Shutting down HTTP proxy")
|
||||
if err = h.server.Shutdown(ctx); err != nil {
|
||||
if err = h.server.Close(); err != nil {
|
||||
h.logger.Error(
|
||||
"failed to shutdown proxy endpoint",
|
||||
zap.Error(err),
|
||||
)
|
||||
|
||||
err = fmt.Errorf(
|
||||
"failed to shutdown proxy endpoint: %w",
|
||||
err,
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
167
internal/endpoint/handler/http/proxy/handler_bench_test.go
Normal file
167
internal/endpoint/handler/http/proxy/handler_bench_test.go
Normal file
|
@ -0,0 +1,167 @@
|
|||
package proxy_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/testcontainers/testcontainers-go"
|
||||
"gitlab.com/inetmock/inetmock/internal/test/integration"
|
||||
)
|
||||
|
||||
const (
|
||||
charSet = "abcdedfghijklmnopqrstABCDEFGHIJKLMNOP"
|
||||
)
|
||||
|
||||
var (
|
||||
availableExtensions = []string{"gif", "html", "ico", "jpg", "png", "txt"}
|
||||
)
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().Unix())
|
||||
}
|
||||
|
||||
func Benchmark_httpProxy(b *testing.B) {
|
||||
type benchmark struct {
|
||||
name string
|
||||
port string
|
||||
scheme string
|
||||
}
|
||||
benchmarks := []benchmark{
|
||||
{
|
||||
name: "HTTP",
|
||||
port: "3128/tcp",
|
||||
scheme: "http",
|
||||
},
|
||||
{
|
||||
name: "HTTPS",
|
||||
port: "3128/tcp",
|
||||
scheme: "https",
|
||||
},
|
||||
}
|
||||
scenario := func(bm benchmark) func(bm *testing.B) {
|
||||
return func(b *testing.B) {
|
||||
var err error
|
||||
var endpoint string
|
||||
if endpoint, err = setupContainer(b, bm.port); err != nil {
|
||||
b.Errorf("setupContainer() error = %v", err)
|
||||
}
|
||||
|
||||
var httpClient *http.Client
|
||||
if httpClient, err = setupHTTPClient(fmt.Sprintf("http://%s", endpoint), fmt.Sprintf("https://%s", endpoint)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
extension := availableExtensions[rand.Intn(len(availableExtensions))]
|
||||
|
||||
reqUrl, _ := url.Parse(fmt.Sprintf("%s://%s/%s.%s", bm.scheme, endpoint, randomString(15), extension))
|
||||
|
||||
req := &http.Request{
|
||||
Method: http.MethodGet,
|
||||
URL: reqUrl,
|
||||
Close: false,
|
||||
Host: "www.inetmock.com",
|
||||
}
|
||||
if resp, err := httpClient.Do(req); err != nil {
|
||||
b.Error(err)
|
||||
} else if resp.StatusCode != 200 {
|
||||
b.Errorf("Got status code %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
for _, bm := range benchmarks {
|
||||
b.Run(bm.name, scenario(bm))
|
||||
}
|
||||
}
|
||||
|
||||
func randomString(length int) (result string) {
|
||||
buffer := strings.Builder{}
|
||||
for i := 0; i < length; i++ {
|
||||
buffer.WriteByte(charSet[rand.Intn(len(charSet))])
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
func setupContainer(b *testing.B, port string) (httpEndpoint string, err error) {
|
||||
b.Helper()
|
||||
|
||||
startupCtx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
defer cancel()
|
||||
var inetMockContainer testcontainers.Container
|
||||
if inetMockContainer, err = integration.SetupINetMockContainer(startupCtx, b, port); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
httpEndpoint, err = inetMockContainer.PortEndpoint(startupCtx, nat.Port(port), "")
|
||||
return
|
||||
}
|
||||
|
||||
func setupHTTPClient(httpEndpoint, httpsEndpoint string) (client *http.Client, err error) {
|
||||
_, fileName, _, _ := runtime.Caller(0)
|
||||
|
||||
var repoRoot string
|
||||
if repoRoot, err = filepath.Abs(filepath.Join(filepath.Dir(fileName), "..", "..", "..", "..", "..")); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var demoCABytes []byte
|
||||
if demoCABytes, err = ioutil.ReadFile(filepath.Join(repoRoot, "assets", "demoCA", "ca.pem")); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
rootCaPool := x509.NewCertPool()
|
||||
if !rootCaPool.AppendCertsFromPEM(demoCABytes) {
|
||||
err = errors.New("failed to add CA key")
|
||||
return
|
||||
}
|
||||
|
||||
client = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: func(req *http.Request) (*url.URL, error) {
|
||||
switch req.URL.Scheme {
|
||||
case "http":
|
||||
return url.Parse(httpEndpoint)
|
||||
case "https":
|
||||
return url.Parse(httpsEndpoint)
|
||||
default:
|
||||
return nil, errors.New("unknown scheme")
|
||||
}
|
||||
},
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).DialContext,
|
||||
TLSClientConfig: &tls.Config{
|
||||
RootCAs: rootCaPool,
|
||||
},
|
||||
ForceAttemptHTTP2: true,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -3,8 +3,8 @@ package proxy
|
|||
import (
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
imHttp "gitlab.com/inetmock/inetmock/internal/endpoint/handler/http"
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
||||
|
@ -13,6 +13,23 @@ import (
|
|||
"gopkg.in/elazarl/goproxy.v1"
|
||||
)
|
||||
|
||||
type proxyHttpsHandler struct {
|
||||
options httpProxyOptions
|
||||
tlsConfig *tls.Config
|
||||
emitter audit.Emitter
|
||||
}
|
||||
|
||||
func (p *proxyHttpsHandler) HandleConnect(_ string, ctx *goproxy.ProxyCtx) (*goproxy.ConnectAction, string) {
|
||||
p.emitter.Emit(imHttp.EventFromRequest(ctx.Req, audit.AppProtocol_HTTP_PROXY))
|
||||
|
||||
return &goproxy.ConnectAction{
|
||||
Action: goproxy.ConnectAccept,
|
||||
TLSConfig: func(host string, ctx *goproxy.ProxyCtx) (*tls.Config, error) {
|
||||
return p.tlsConfig, nil
|
||||
},
|
||||
}, p.options.Target.host()
|
||||
}
|
||||
|
||||
type proxyHttpHandler struct {
|
||||
handlerName string
|
||||
options httpProxyOptions
|
||||
|
@ -20,38 +37,19 @@ type proxyHttpHandler struct {
|
|||
emitter audit.Emitter
|
||||
}
|
||||
|
||||
type proxyHttpsHandler struct {
|
||||
handlerName string
|
||||
tlsConfig *tls.Config
|
||||
logger logging.Logger
|
||||
emitter audit.Emitter
|
||||
}
|
||||
|
||||
func (p *proxyHttpsHandler) HandleConnect(req string, _ *goproxy.ProxyCtx) (*goproxy.ConnectAction, string) {
|
||||
totalHttpsRequestCounter.WithLabelValues(p.handlerName).Inc()
|
||||
p.logger.Info(
|
||||
"Intercepting HTTPS proxy request",
|
||||
zap.String("request", req),
|
||||
)
|
||||
|
||||
return &goproxy.ConnectAction{
|
||||
Action: goproxy.ConnectMitm,
|
||||
TLSConfig: func(host string, ctx *goproxy.ProxyCtx) (*tls.Config, error) {
|
||||
return p.tlsConfig, nil
|
||||
},
|
||||
}, ""
|
||||
}
|
||||
|
||||
func (p *proxyHttpHandler) Handle(req *http.Request, ctx *goproxy.ProxyCtx) (retReq *http.Request, resp *http.Response) {
|
||||
timer := prometheus.NewTimer(requestDurationHistogram.WithLabelValues(p.handlerName))
|
||||
defer timer.ObserveDuration()
|
||||
totalRequestCounter.WithLabelValues(p.handlerName).Inc()
|
||||
|
||||
retReq = req
|
||||
p.emitter.Emit(imHttp.EventFromRequest(req, audit.AppProtocol_HTTP_PROXY))
|
||||
|
||||
var err error
|
||||
if resp, err = ctx.RoundTrip(p.redirectHTTPRequest(req)); err != nil {
|
||||
var redirectReq *http.Request
|
||||
if redirectReq, err = redirectHTTPRequest(p.options.Target.host(), req); err != nil {
|
||||
return req, nil
|
||||
}
|
||||
if resp, err = ctx.RoundTrip(redirectReq); err != nil {
|
||||
p.logger.Error(
|
||||
"error while doing roundtrip",
|
||||
zap.Error(err),
|
||||
|
@ -62,35 +60,11 @@ func (p *proxyHttpHandler) Handle(req *http.Request, ctx *goproxy.ProxyCtx) (ret
|
|||
return
|
||||
}
|
||||
|
||||
func (p proxyHttpHandler) redirectHTTPRequest(originalRequest *http.Request) (redirectReq *http.Request) {
|
||||
redirectReq = &http.Request{
|
||||
Method: originalRequest.Method,
|
||||
URL: &url.URL{
|
||||
Host: p.options.Target.host(),
|
||||
Path: originalRequest.URL.Path,
|
||||
ForceQuery: originalRequest.URL.ForceQuery,
|
||||
Fragment: originalRequest.URL.Fragment,
|
||||
Opaque: originalRequest.URL.Opaque,
|
||||
RawPath: originalRequest.URL.RawPath,
|
||||
RawQuery: originalRequest.URL.RawQuery,
|
||||
User: originalRequest.URL.User,
|
||||
},
|
||||
Proto: originalRequest.Proto,
|
||||
ProtoMajor: originalRequest.ProtoMajor,
|
||||
ProtoMinor: originalRequest.ProtoMinor,
|
||||
Header: originalRequest.Header,
|
||||
Body: originalRequest.Body,
|
||||
GetBody: originalRequest.GetBody,
|
||||
ContentLength: originalRequest.ContentLength,
|
||||
TransferEncoding: originalRequest.TransferEncoding,
|
||||
Close: false,
|
||||
Host: originalRequest.Host,
|
||||
Form: originalRequest.Form,
|
||||
PostForm: originalRequest.PostForm,
|
||||
MultipartForm: originalRequest.MultipartForm,
|
||||
Trailer: originalRequest.Trailer,
|
||||
func redirectHTTPRequest(targetHost string, originalRequest *http.Request) (redirectReq *http.Request, err error) {
|
||||
redirectReq = new(http.Request)
|
||||
if err = copier.Copy(redirectReq, originalRequest); err != nil {
|
||||
return
|
||||
}
|
||||
redirectReq = redirectReq.WithContext(originalRequest.Context())
|
||||
|
||||
originalRequest.URL.Host = targetHost
|
||||
return
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package proxy
|
|||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"gitlab.com/inetmock/inetmock/pkg/api"
|
||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
"gitlab.com/inetmock/inetmock/pkg/metrics"
|
||||
"go.uber.org/zap"
|
||||
|
@ -11,12 +11,10 @@ import (
|
|||
|
||||
var (
|
||||
handlerNameLblName = "handler_name"
|
||||
totalRequestCounter *prometheus.CounterVec
|
||||
totalHttpsRequestCounter *prometheus.CounterVec
|
||||
requestDurationHistogram *prometheus.HistogramVec
|
||||
)
|
||||
|
||||
func AddHTTPProxy(registry api.HandlerRegistry) (err error) {
|
||||
func AddHTTPProxy(registry endpoint.HandlerRegistry) (err error) {
|
||||
var logger logging.Logger
|
||||
if logger, err = logging.CreateLogger(); err != nil {
|
||||
return
|
||||
|
@ -25,19 +23,11 @@ func AddHTTPProxy(registry api.HandlerRegistry) (err error) {
|
|||
zap.String("protocol_handler", name),
|
||||
)
|
||||
|
||||
if totalRequestCounter, err = metrics.Counter(name, "total_requests", "", handlerNameLblName); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if requestDurationHistogram, err = metrics.Histogram(name, "request_duration", "", nil, handlerNameLblName); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if totalHttpsRequestCounter, err = metrics.Counter(name, "total_https_requests", "", handlerNameLblName); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
registry.RegisterHandler(name, func() api.ProtocolHandler {
|
||||
registry.RegisterHandler(name, func() endpoint.ProtocolHandler {
|
||||
return &httpProxy{
|
||||
logger: logger,
|
||||
proxy: goproxy.NewProxyHttpServer(),
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
package metrics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"gitlab.com/inetmock/inetmock/pkg/api"
|
||||
"gitlab.com/inetmock/inetmock/pkg/config"
|
||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
@ -21,35 +19,37 @@ type metricsExporter struct {
|
|||
server *http.Server
|
||||
}
|
||||
|
||||
func (m *metricsExporter) Start(_ api.PluginContext, config config.HandlerConfig) (err error) {
|
||||
exporterOptions := metricsExporterOptions{}
|
||||
if err = config.Options.Unmarshal(&exporterOptions); err != nil {
|
||||
func (m *metricsExporter) Start(lifecycle endpoint.Lifecycle) (err error) {
|
||||
var exporterOptions metricsExporterOptions
|
||||
if err = lifecycle.UnmarshalOptions(&exporterOptions); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
m.logger = m.logger.With(
|
||||
zap.String("handler_name", config.HandlerName),
|
||||
zap.String("address", config.ListenAddr()),
|
||||
zap.String("handler_name", lifecycle.Name()),
|
||||
zap.String("address", lifecycle.Uplink().Addr().String()),
|
||||
)
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle(exporterOptions.Route, promhttp.Handler())
|
||||
m.server = &http.Server{
|
||||
Addr: config.ListenAddr(),
|
||||
Handler: mux,
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := m.server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
if err := m.server.Serve(lifecycle.Uplink().Listener); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
m.logger.Error(
|
||||
"Error occurred while serving metrics",
|
||||
zap.Error(err),
|
||||
)
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
<-lifecycle.Context().Done()
|
||||
if err := m.server.Close(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
m.logger.Error("failed to stop metrics server", zap.Error(err))
|
||||
}
|
||||
}()
|
||||
return
|
||||
}
|
||||
|
||||
func (m *metricsExporter) Shutdown(ctx context.Context) error {
|
||||
return m.server.Shutdown(ctx)
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package metrics
|
||||
|
||||
import (
|
||||
"gitlab.com/inetmock/inetmock/pkg/api"
|
||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func AddMetricsExporter(registry api.HandlerRegistry) (err error) {
|
||||
func AddMetricsExporter(registry endpoint.HandlerRegistry) (err error) {
|
||||
var logger logging.Logger
|
||||
if logger, err = logging.CreateLogger(); err != nil {
|
||||
return
|
||||
|
@ -15,7 +15,7 @@ func AddMetricsExporter(registry api.HandlerRegistry) (err error) {
|
|||
zap.String("protocol_handler", name),
|
||||
)
|
||||
|
||||
registry.RegisterHandler(name, func() api.ProtocolHandler {
|
||||
registry.RegisterHandler(name, func() endpoint.ProtocolHandler {
|
||||
return &metricsExporter{
|
||||
logger: logger,
|
||||
}
|
||||
|
|
|
@ -3,14 +3,13 @@ package interceptor
|
|||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"gitlab.com/inetmock/inetmock/pkg/api"
|
||||
"gitlab.com/inetmock/inetmock/pkg/config"
|
||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
@ -30,36 +29,28 @@ type tlsInterceptor struct {
|
|||
connectionsMutex *sync.Mutex
|
||||
}
|
||||
|
||||
func (t *tlsInterceptor) Start(ctx api.PluginContext, config config.HandlerConfig) (err error) {
|
||||
t.name = config.HandlerName
|
||||
func (t *tlsInterceptor) Start(ctx endpoint.Lifecycle) (err error) {
|
||||
t.name = ctx.Name()
|
||||
|
||||
if err = config.Options.Unmarshal(&t.options); err != nil {
|
||||
if err = ctx.UnmarshalOptions(&t.options); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
t.logger = t.logger.With(
|
||||
zap.String("handler_name", config.HandlerName),
|
||||
zap.String("address", config.ListenAddr()),
|
||||
zap.String("handler_name", ctx.Name()),
|
||||
zap.String("address", ctx.Uplink().Addr().String()),
|
||||
zap.String("Target", t.options.Target.address()),
|
||||
)
|
||||
|
||||
if t.listener, err = tls.Listen("tcp", config.ListenAddr(), ctx.CertStore().TLSConfig()); err != nil {
|
||||
t.logger.Fatal(
|
||||
"failed to create tls listener",
|
||||
zap.Error(err),
|
||||
)
|
||||
err = fmt.Errorf(
|
||||
"failed to create tls listener: %w",
|
||||
err,
|
||||
)
|
||||
return
|
||||
}
|
||||
t.listener = tls.NewListener(ctx.Uplink().Listener, ctx.CertStore().TLSConfig())
|
||||
|
||||
go t.startListener()
|
||||
go t.shutdownOnContextDone(ctx.Context())
|
||||
return
|
||||
}
|
||||
|
||||
func (t *tlsInterceptor) Shutdown(ctx context.Context) (err error) {
|
||||
func (t *tlsInterceptor) shutdownOnContextDone(ctx context.Context) {
|
||||
<-ctx.Done()
|
||||
t.logger.Info("Shutting down TLS interceptor")
|
||||
t.shutdownRequested = true
|
||||
done := make(chan struct{})
|
||||
|
@ -71,17 +62,13 @@ func (t *tlsInterceptor) Shutdown(ctx context.Context) (err error) {
|
|||
select {
|
||||
case <-done:
|
||||
return
|
||||
case <-ctx.Done():
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
for _, proxyConn := range t.currentConnections {
|
||||
if err = proxyConn.Close(); err != nil {
|
||||
if err := proxyConn.Close(); err != nil {
|
||||
t.logger.Error(
|
||||
"error while closing remaining proxy connections",
|
||||
zap.Error(err),
|
||||
)
|
||||
err = fmt.Errorf(
|
||||
"error while closing remaining proxy connections: %w",
|
||||
err,
|
||||
)
|
||||
}
|
||||
}
|
||||
return
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
|
||||
"github.com/google/uuid"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"gitlab.com/inetmock/inetmock/pkg/api"
|
||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
"gitlab.com/inetmock/inetmock/pkg/metrics"
|
||||
"go.uber.org/zap"
|
||||
|
@ -18,7 +18,7 @@ var (
|
|||
requestDurationHistogram *prometheus.HistogramVec
|
||||
)
|
||||
|
||||
func AddTLSInterceptor(registry api.HandlerRegistry) (err error) {
|
||||
func AddTLSInterceptor(registry endpoint.HandlerRegistry) (err error) {
|
||||
var logger logging.Logger
|
||||
if logger, err = logging.CreateLogger(); err != nil {
|
||||
panic(err)
|
||||
|
@ -37,7 +37,7 @@ func AddTLSInterceptor(registry api.HandlerRegistry) (err error) {
|
|||
|
||||
}
|
||||
|
||||
registry.RegisterHandler(name, func() api.ProtocolHandler {
|
||||
registry.RegisterHandler(name, func() endpoint.ProtocolHandler {
|
||||
return &tlsInterceptor{
|
||||
logger: logger,
|
||||
currentConnectionsCount: new(sync.WaitGroup),
|
||||
|
|
69
internal/endpoint/lifecycle.go
Normal file
69
internal/endpoint/lifecycle.go
Normal file
|
@ -0,0 +1,69 @@
|
|||
package endpoint
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"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 {
|
||||
endpointName string
|
||||
ctx context.Context
|
||||
logger logging.Logger
|
||||
certStore cert.Store
|
||||
emitter audit.Emitter
|
||||
uplink Uplink
|
||||
tls bool
|
||||
opts map[string]interface{}
|
||||
}
|
||||
|
||||
func NewEndpointLifecycleFromContext(
|
||||
endpointName string,
|
||||
ctx context.Context,
|
||||
logger logging.Logger,
|
||||
certStore cert.Store,
|
||||
emitter audit.Emitter,
|
||||
uplink Uplink,
|
||||
opts map[string]interface{},
|
||||
) Lifecycle {
|
||||
return &endpointLifecycle{
|
||||
endpointName: endpointName,
|
||||
ctx: ctx,
|
||||
logger: logger,
|
||||
certStore: certStore,
|
||||
emitter: emitter,
|
||||
uplink: uplink,
|
||||
opts: opts,
|
||||
}
|
||||
}
|
||||
|
||||
func (e endpointLifecycle) Name() string {
|
||||
return e.endpointName
|
||||
}
|
||||
|
||||
func (e endpointLifecycle) Uplink() 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 {
|
||||
return mapstructure.Decode(e.opts, cfg)
|
||||
}
|
143
internal/endpoint/listener.go
Normal file
143
internal/endpoint/listener.go
Normal file
|
@ -0,0 +1,143 @@
|
|||
//go:generate go-enum -f $GOFILE --lower --marshal --names
|
||||
|
||||
package endpoint
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/soheilhy/cmux"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrUDPMultiplexer = errors.New("UDP listeners don't support multiplexing")
|
||||
ErrMultiplexingNotSupported = errors.New("not all handlers do support multiplexing")
|
||||
)
|
||||
|
||||
/* ENUM(
|
||||
UDP,
|
||||
TCP
|
||||
)
|
||||
*/
|
||||
type NetProto int
|
||||
|
||||
type HandlerReference string
|
||||
|
||||
func (h HandlerReference) ToLower() HandlerReference {
|
||||
return HandlerReference(strings.ToLower(string(h)))
|
||||
}
|
||||
|
||||
type ListenerSpec struct {
|
||||
Name string
|
||||
Protocol string
|
||||
Address string `mapstructure:"listenAddress"`
|
||||
Port uint16
|
||||
Endpoints map[string]Spec
|
||||
Uplink *Uplink `mapstructure:"-"`
|
||||
}
|
||||
|
||||
type Spec struct {
|
||||
HandlerRef HandlerReference `mapstructure:"handler"`
|
||||
TLS bool
|
||||
Handler ProtocolHandler `mapstructure:"-"`
|
||||
Options map[string]interface{}
|
||||
}
|
||||
|
||||
func (l *ListenerSpec) ConfigureMultiplexing(tlsConfig *tls.Config) (endpoints []Endpoint, muxes []cmux.CMux, err error) {
|
||||
if l.Uplink == nil {
|
||||
if err = l.setupUplink(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(l.Endpoints) <= 1 {
|
||||
for name, s := range l.Endpoints {
|
||||
endpoints = append(endpoints, Endpoint{
|
||||
name: fmt.Sprintf("%s:%s", l.Name, name),
|
||||
uplink: *l.Uplink,
|
||||
Spec: s,
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if l.Uplink.Proto == NetProtoUDP {
|
||||
err = ErrUDPMultiplexer
|
||||
return
|
||||
}
|
||||
|
||||
var epNames []string
|
||||
var multiplexEndpoints = make(map[string]MultiplexHandler)
|
||||
for name, spec := range l.Endpoints {
|
||||
epNames = append(epNames, name)
|
||||
if ep, ok := spec.Handler.(MultiplexHandler); !ok {
|
||||
err = fmt.Errorf("handler %s %w", spec.HandlerRef, ErrMultiplexingNotSupported)
|
||||
return
|
||||
} else {
|
||||
multiplexEndpoints[name] = ep
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(epNames)
|
||||
|
||||
plainMux := cmux.New(l.Uplink.Listener)
|
||||
tlsListener := plainMux.Match(cmux.TLS())
|
||||
tlsListener = tls.NewListener(tlsListener, tlsConfig)
|
||||
tlsMux := cmux.New(tlsListener)
|
||||
|
||||
var tlsRequired = false
|
||||
|
||||
for _, epName := range epNames {
|
||||
epSpec := l.Endpoints[epName]
|
||||
var epMux = plainMux
|
||||
if epSpec.TLS {
|
||||
epMux = tlsMux
|
||||
tlsRequired = true
|
||||
}
|
||||
epListener := Endpoint{
|
||||
name: fmt.Sprintf("%s:%s", l.Name, epName),
|
||||
uplink: Uplink{
|
||||
Proto: NetProtoTCP,
|
||||
Listener: epMux.Match(multiplexEndpoints[epName].Matchers()...),
|
||||
},
|
||||
Spec: epSpec,
|
||||
}
|
||||
|
||||
endpoints = append(endpoints, epListener)
|
||||
}
|
||||
|
||||
muxes = append(muxes, plainMux)
|
||||
|
||||
if tlsRequired {
|
||||
muxes = append(muxes, tlsMux)
|
||||
} else {
|
||||
_ = tlsListener.Close()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (l *ListenerSpec) setupUplink() (err error) {
|
||||
l.Uplink = new(Uplink)
|
||||
switch l.Protocol {
|
||||
case "udp", "udp4", "udp6":
|
||||
l.Uplink.Proto = NetProtoUDP
|
||||
l.Uplink.PacketConn, err = net.ListenUDP(l.Protocol, &net.UDPAddr{
|
||||
IP: net.ParseIP(l.Address),
|
||||
Port: int(l.Port),
|
||||
})
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
l.Uplink.Proto = NetProtoTCP
|
||||
l.Uplink.Listener, err = net.ListenTCP(l.Protocol, &net.TCPAddr{
|
||||
IP: net.ParseIP(l.Address),
|
||||
Port: int(l.Port),
|
||||
})
|
||||
default:
|
||||
err = errors.New("protocol not supported")
|
||||
}
|
||||
return
|
||||
}
|
102
internal/endpoint/orchestrator.go
Normal file
102
internal/endpoint/orchestrator.go
Normal file
|
@ -0,0 +1,102 @@
|
|||
package endpoint
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/soheilhy/cmux"
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
||||
"gitlab.com/inetmock/inetmock/pkg/cert"
|
||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrStartupTimeout = errors.New("endpoint did not start in time")
|
||||
)
|
||||
|
||||
type Orchestrator interface {
|
||||
RegisterListener(spec ListenerSpec) error
|
||||
StartEndpoints() (errChan chan error)
|
||||
}
|
||||
|
||||
func NewOrchestrator(appCtx context.Context, certStore cert.Store, registry HandlerRegistry, emitter audit.Emitter, logging logging.Logger) Orchestrator {
|
||||
return &orchestrator{
|
||||
appCtx: appCtx,
|
||||
registry: registry,
|
||||
logger: logging,
|
||||
certStore: certStore,
|
||||
emitter: emitter,
|
||||
}
|
||||
}
|
||||
|
||||
type orchestrator struct {
|
||||
appCtx context.Context
|
||||
registry HandlerRegistry
|
||||
logger logging.Logger
|
||||
certStore cert.Store
|
||||
emitter audit.Emitter
|
||||
|
||||
endpointListeners []Endpoint
|
||||
muxes []cmux.CMux
|
||||
}
|
||||
|
||||
func (e *orchestrator) RegisterListener(spec ListenerSpec) (err error) {
|
||||
for name, s := range spec.Endpoints {
|
||||
if handler, registered := e.registry.HandlerForName(s.HandlerRef); registered {
|
||||
s.Handler = handler
|
||||
spec.Endpoints[name] = s
|
||||
}
|
||||
}
|
||||
|
||||
var endpoints []Endpoint
|
||||
var muxes []cmux.CMux
|
||||
if endpoints, muxes, err = spec.ConfigureMultiplexing(e.certStore.TLSConfig()); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
e.endpointListeners = append(e.endpointListeners, endpoints...)
|
||||
e.muxes = append(e.muxes, muxes...)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (e *orchestrator) StartEndpoints() (errChan chan error) {
|
||||
errChan = make(chan error)
|
||||
for _, epListener := range e.endpointListeners {
|
||||
endpointLogger := e.logger.With(
|
||||
zap.String("epListener", epListener.name),
|
||||
)
|
||||
endpointLogger.Info("Starting epListener")
|
||||
lifecycle := NewEndpointLifecycleFromContext(
|
||||
epListener.name,
|
||||
e.appCtx,
|
||||
e.logger.With(zap.String("epListener", epListener.name)),
|
||||
e.certStore,
|
||||
e.emitter,
|
||||
epListener.uplink,
|
||||
epListener.Options,
|
||||
)
|
||||
|
||||
if err := epListener.Start(lifecycle); err == nil {
|
||||
endpointLogger.Info("successfully started epListener")
|
||||
} else {
|
||||
endpointLogger.Error("error occurred during epListener startup - will be skipped for now")
|
||||
}
|
||||
}
|
||||
e.logger.Info("Startup of all endpoints completed")
|
||||
|
||||
for _, mux := range e.muxes {
|
||||
go func(mux cmux.CMux) {
|
||||
mux.HandleError(func(err error) bool {
|
||||
errChan <- err
|
||||
return true
|
||||
})
|
||||
if err := mux.Serve(); err != nil && !errors.Is(err, cmux.ErrListenerClosed) {
|
||||
errChan <- err
|
||||
}
|
||||
}(mux)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
47
internal/endpoint/registration.go
Normal file
47
internal/endpoint/registration.go
Normal file
|
@ -0,0 +1,47 @@
|
|||
//go:generate mockgen -source=$GOFILE -destination=./../../internal/mock/endpoint/handler_registry.mock.go -package=endpoint_mock
|
||||
package endpoint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Registration func(registry HandlerRegistry) error
|
||||
|
||||
type HandlerRegistry interface {
|
||||
RegisterHandler(handlerRef HandlerReference, handlerProvider func() ProtocolHandler)
|
||||
AvailableHandlers() []HandlerReference
|
||||
HandlerForName(handlerRef HandlerReference) (ProtocolHandler, bool)
|
||||
}
|
||||
|
||||
func NewHandlerRegistry() HandlerRegistry {
|
||||
return &handlerRegistry{
|
||||
handlers: make(map[HandlerReference]func() ProtocolHandler),
|
||||
}
|
||||
}
|
||||
|
||||
type handlerRegistry struct {
|
||||
handlers map[HandlerReference]func() ProtocolHandler
|
||||
}
|
||||
|
||||
func (h handlerRegistry) AvailableHandlers() (availableHandlers []HandlerReference) {
|
||||
for s := range h.handlers {
|
||||
availableHandlers = append(availableHandlers, s)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (h *handlerRegistry) HandlerForName(handlerRef HandlerReference) (instance ProtocolHandler, ok bool) {
|
||||
var provider func() ProtocolHandler
|
||||
if provider, ok = h.handlers[handlerRef.ToLower()]; ok {
|
||||
instance = provider()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (h *handlerRegistry) RegisterHandler(handlerRef HandlerReference, handlerProvider func() ProtocolHandler) {
|
||||
handlerRef = handlerRef.ToLower()
|
||||
if _, exists := h.handlers[handlerRef]; exists {
|
||||
panic(fmt.Sprintf("handler with name %s is already registered - there's something strange...in the neighborhood", handlerRef))
|
||||
}
|
||||
h.handlers[handlerRef] = handlerProvider
|
||||
}
|
33
internal/endpoint/uplink.go
Normal file
33
internal/endpoint/uplink.go
Normal file
|
@ -0,0 +1,33 @@
|
|||
package endpoint
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"go.uber.org/multierr"
|
||||
)
|
||||
|
||||
type Uplink struct {
|
||||
Proto NetProto
|
||||
Listener net.Listener
|
||||
PacketConn net.PacketConn
|
||||
}
|
||||
|
||||
func (u Uplink) Addr() net.Addr {
|
||||
if u.Listener != nil {
|
||||
return u.Listener.Addr()
|
||||
}
|
||||
if u.PacketConn != nil {
|
||||
return u.PacketConn.LocalAddr()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u Uplink) Close() (err error) {
|
||||
if u.Listener != nil {
|
||||
err = multierr.Append(err, u.Listener.Close())
|
||||
}
|
||||
if u.PacketConn != nil {
|
||||
err = multierr.Append(err, u.PacketConn.Close())
|
||||
}
|
||||
return
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
||||
)
|
||||
|
||||
type endpointsServer struct {
|
||||
UnimplementedEndpointsServer
|
||||
endpointsManager endpoint.EndpointManager
|
||||
}
|
||||
|
||||
func (e endpointsServer) GetEndpoints(_ context.Context, _ *GetEndpointsRequest) (*GetEndpointsResponse, error) {
|
||||
eps := rpcEndpointsFromEndpoints(e.endpointsManager.StartedEndpoints())
|
||||
return &GetEndpointsResponse{
|
||||
Endpoints: *eps,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func rpcEndpointsFromEndpoints(eps []endpoint.Endpoint) *[]*Endpoint {
|
||||
out := make([]*Endpoint, 0)
|
||||
for _, ep := range eps {
|
||||
out = append(out, rpcEndpointFromEndpoint(ep))
|
||||
}
|
||||
return &out
|
||||
}
|
||||
|
||||
func rpcEndpointFromEndpoint(ep endpoint.Endpoint) *Endpoint {
|
||||
return &Endpoint{
|
||||
Id: ep.Id().String(),
|
||||
Name: ep.Name(),
|
||||
Handler: ep.Handler(),
|
||||
ListenAddress: ep.Listen(),
|
||||
Port: int32(ep.Port()),
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ package rpc
|
|||
import (
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
app2 "gitlab.com/inetmock/inetmock/internal/app"
|
||||
|
@ -42,13 +43,6 @@ func (i *inetmockAPI) StartServer() (err error) {
|
|||
}
|
||||
i.server = grpc.NewServer()
|
||||
|
||||
RegisterHandlersServer(i.server, &handlersServer{
|
||||
registry: i.app.HandlerRegistry(),
|
||||
})
|
||||
RegisterEndpointsServer(i.server, &endpointsServer{
|
||||
endpointsManager: i.app.EndpointManager(),
|
||||
})
|
||||
|
||||
RegisterHealthServer(i.server, &healthServer{
|
||||
app: i.app,
|
||||
})
|
||||
|
@ -98,6 +92,11 @@ func (i *inetmockAPI) startServerAsync(listener net.Listener) {
|
|||
func createListenerFromURL(url *url.URL) (lis net.Listener, err error) {
|
||||
switch url.Scheme {
|
||||
case "unix":
|
||||
if _, err = os.Stat(url.Path); err == nil {
|
||||
if err = os.Remove(url.Path); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
lis, err = net.Listen(url.Scheme, url.Path)
|
||||
default:
|
||||
lis, err = net.Listen(url.Scheme, url.Host)
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitlab.com/inetmock/inetmock/pkg/api"
|
||||
)
|
||||
|
||||
type handlersServer struct {
|
||||
UnimplementedHandlersServer
|
||||
registry api.HandlerRegistry
|
||||
}
|
||||
|
||||
func (h *handlersServer) GetHandlers(_ context.Context, _ *GetHandlersRequest) (*GetHandlersResponse, error) {
|
||||
return &GetHandlersResponse{
|
||||
Handlers: h.registry.AvailableHandlers(),
|
||||
}, nil
|
||||
}
|
64
internal/test/integration/testcontainer.go
Normal file
64
internal/test/integration/testcontainer.go
Normal file
|
@ -0,0 +1,64 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/testcontainers/testcontainers-go"
|
||||
"github.com/testcontainers/testcontainers-go/wait"
|
||||
)
|
||||
|
||||
func SetupINetMockContainer(ctx context.Context, tb testing.TB, exposedPorts ...string) (imContainer testcontainers.Container, err error) {
|
||||
_, fileName, _, _ := runtime.Caller(0)
|
||||
|
||||
var repoRoot string
|
||||
if repoRoot, err = filepath.Abs(filepath.Join(filepath.Dir(fileName), "..", "..", "..")); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var waitStrategies []wait.Strategy
|
||||
|
||||
var tcpPortPresent = false
|
||||
for _, port := range exposedPorts {
|
||||
if strings.Contains(port, "tcp") {
|
||||
tcpPortPresent = true
|
||||
waitStrategies = append(waitStrategies, wait.ForListeningPort(nat.Port(port)))
|
||||
}
|
||||
}
|
||||
|
||||
if !tcpPortPresent {
|
||||
exposedPorts = append(exposedPorts, "80/tcp")
|
||||
waitStrategies = append(waitStrategies, wait.ForListeningPort("80/tcp"))
|
||||
}
|
||||
|
||||
req := testcontainers.ContainerRequest{
|
||||
FromDockerfile: testcontainers.FromDockerfile{
|
||||
Context: repoRoot,
|
||||
Dockerfile: filepath.Join("./", "testdata", "integration.dockerfile"),
|
||||
},
|
||||
ExposedPorts: exposedPorts,
|
||||
WaitingFor: wait.ForAll(waitStrategies...),
|
||||
}
|
||||
|
||||
imContainer, err = testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
|
||||
ContainerRequest: req,
|
||||
Started: true,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
tb.Cleanup(func() {
|
||||
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
_ = imContainer.Terminate(shutdownCtx)
|
||||
})
|
||||
|
||||
return
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
//go:generate mockgen -source=$GOFILE -destination=./../../internal/mock/api/protocol_handler.mock.go -package=api_mock
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
||||
"gitlab.com/inetmock/inetmock/pkg/cert"
|
||||
"gitlab.com/inetmock/inetmock/pkg/config"
|
||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
)
|
||||
|
||||
type PluginContext interface {
|
||||
Logger() logging.Logger
|
||||
CertStore() cert.Store
|
||||
Audit() audit.Emitter
|
||||
}
|
||||
|
||||
type ProtocolHandler interface {
|
||||
Start(ctx PluginContext, config config.HandlerConfig) error
|
||||
Shutdown(ctx context.Context) error
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
//go:generate mockgen -source=$GOFILE -destination=./../../internal/mock/plugins/handler_registry.mock.go -package=plugins_mock
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
pluginFileNamePattern = regexp.MustCompile(`[\w\-]+\.so$`)
|
||||
)
|
||||
|
||||
type Registration func(registry HandlerRegistry) error
|
||||
|
||||
type HandlerRegistry interface {
|
||||
RegisterHandler(handlerName string, handlerProvider func() ProtocolHandler)
|
||||
AvailableHandlers() []string
|
||||
HandlerForName(handlerName string) (ProtocolHandler, bool)
|
||||
}
|
||||
|
||||
func NewHandlerRegistry() HandlerRegistry {
|
||||
return &handlerRegistry{
|
||||
handlers: make(map[string]func() ProtocolHandler),
|
||||
}
|
||||
}
|
||||
|
||||
type handlerRegistry struct {
|
||||
handlers map[string]func() ProtocolHandler
|
||||
}
|
||||
|
||||
func (h handlerRegistry) AvailableHandlers() (availableHandlers []string) {
|
||||
for s := range h.handlers {
|
||||
availableHandlers = append(availableHandlers, s)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (h *handlerRegistry) HandlerForName(handlerName string) (instance ProtocolHandler, ok bool) {
|
||||
handlerName = strings.ToLower(handlerName)
|
||||
var provider func() ProtocolHandler
|
||||
if provider, ok = h.handlers[handlerName]; ok {
|
||||
instance = provider()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (h *handlerRegistry) RegisterHandler(handlerName string, handlerProvider func() ProtocolHandler) {
|
||||
handlerName = strings.ToLower(handlerName)
|
||||
if _, exists := h.handlers[handlerName]; exists {
|
||||
panic(fmt.Sprintf("handler with name %s is already registered - there's something strange...in the neighborhood", handlerName))
|
||||
}
|
||||
h.handlers[handlerName] = handlerProvider
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_handlerRegistry_HandlerForName(t *testing.T) {
|
||||
type fields struct {
|
||||
handlers map[string]func() ProtocolHandler
|
||||
}
|
||||
type args struct {
|
||||
handlerName string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
wantInstance ProtocolHandler
|
||||
wantOk bool
|
||||
}{
|
||||
{
|
||||
name: "No instance if nothing is registered",
|
||||
fields: fields{},
|
||||
args: args{},
|
||||
wantInstance: nil,
|
||||
wantOk: false,
|
||||
},
|
||||
{
|
||||
name: "Nil instance from pseudo factory",
|
||||
fields: fields{
|
||||
handlers: map[string]func() ProtocolHandler{
|
||||
"pseudo": func() ProtocolHandler {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
handlerName: "pseudo",
|
||||
},
|
||||
wantInstance: nil,
|
||||
wantOk: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
h := &handlerRegistry{
|
||||
handlers: tt.fields.handlers,
|
||||
}
|
||||
gotInstance, gotOk := h.HandlerForName(tt.args.handlerName)
|
||||
if !reflect.DeepEqual(gotInstance, tt.wantInstance) {
|
||||
t.Errorf("HandlerForName() gotInstance = %v, want %v", gotInstance, tt.wantInstance)
|
||||
}
|
||||
if gotOk != tt.wantOk {
|
||||
t.Errorf("HandlerForName() gotOk = %v, want %v", gotOk, tt.wantOk)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -29,7 +29,7 @@ type Event struct {
|
|||
TLS *TLSDetails
|
||||
}
|
||||
|
||||
func (e *Event) ProtoMessage() *EventEntity {
|
||||
func (e Event) ProtoMessage() *EventEntity {
|
||||
var tlsDetails *TLSDetailsEntity = nil
|
||||
if e.TLS != nil {
|
||||
tlsDetails = e.TLS.ProtoMessage()
|
||||
|
|
|
@ -12,7 +12,6 @@ import (
|
|||
|
||||
"github.com/golang/mock/gomock"
|
||||
certmock "gitlab.com/inetmock/inetmock/internal/mock/cert"
|
||||
"gitlab.com/inetmock/inetmock/pkg/config"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -286,13 +285,13 @@ func Test_fileSystemCache_Put(t *testing.T) {
|
|||
}
|
||||
|
||||
func setupCertGen() Generator {
|
||||
return NewDefaultGenerator(config.CertOptions{
|
||||
Validity: config.ValidityByPurpose{
|
||||
Server: config.ValidityDuration{
|
||||
return NewDefaultGenerator(CertOptions{
|
||||
Validity: ValidityByPurpose{
|
||||
Server: ValidityDuration{
|
||||
NotBeforeRelative: serverRelativeValidity,
|
||||
NotAfterRelative: serverRelativeValidity,
|
||||
},
|
||||
CA: config.ValidityDuration{
|
||||
CA: ValidityDuration{
|
||||
NotBeforeRelative: caRelativeValidity,
|
||||
NotAfterRelative: caRelativeValidity,
|
||||
},
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package config
|
||||
package cert
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
|
@ -1,13 +1,14 @@
|
|||
package cert
|
||||
|
||||
const (
|
||||
defaultServerValidityDuration = "168h"
|
||||
defaultCAValidityDuration = "17520h"
|
||||
CurveTypeP224 CurveType = "P224"
|
||||
CurveTypeP256 CurveType = "P256"
|
||||
CurveTypeP384 CurveType = "P384"
|
||||
CurveTypeP521 CurveType = "P521"
|
||||
CurveTypeED25519 CurveType = "ED25519"
|
||||
|
||||
certCachePathConfigKey = "tls.certCachePath"
|
||||
ecdsaCurveConfigKey = "tls.ecdsaCurve"
|
||||
caCertValidityNotBeforeKey = "tls.validity.ca.notBeforeRelative"
|
||||
caCertValidityNotAfterKey = "tls.validity.ca.notAfterRelative"
|
||||
serverCertValidityNotBeforeKey = "tls.validity.server.notBeforeRelative"
|
||||
serverCertValidityNotAfterKey = "tls.validity.server.notAfterRelative"
|
||||
TLSVersionSSL3 TLSVersion = "SSL3"
|
||||
TLSVersionTLS10 TLSVersion = "TLS10"
|
||||
TLSVersionTLS11 TLSVersion = "TLS11"
|
||||
TLSVersionTLS12 TLSVersion = "TLS12"
|
||||
)
|
||||
|
|
|
@ -1,44 +1,20 @@
|
|||
package cert
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"gitlab.com/inetmock/inetmock/pkg/defaulting"
|
||||
"github.com/imdario/mergo"
|
||||
)
|
||||
|
||||
var (
|
||||
certOptionsDefaulter defaulting.Defaulter = func(instance interface{}) {
|
||||
switch o := instance.(type) {
|
||||
case *GenerationOptions:
|
||||
|
||||
if len(o.Country) < 1 {
|
||||
o.Country = []string{"US"}
|
||||
}
|
||||
|
||||
if len(o.Locality) < 1 {
|
||||
o.Locality = []string{"San Francisco"}
|
||||
}
|
||||
|
||||
if len(o.Organization) < 1 {
|
||||
o.Organization = []string{"INetMock"}
|
||||
}
|
||||
|
||||
if len(o.StreetAddress) < 1 {
|
||||
o.StreetAddress = []string{"Golden Gate Bridge"}
|
||||
}
|
||||
|
||||
if len(o.PostalCode) < 1 {
|
||||
o.PostalCode = []string{"94016"}
|
||||
}
|
||||
|
||||
if len(o.Province) < 1 {
|
||||
o.Province = []string{""}
|
||||
}
|
||||
}
|
||||
defaultOptions = &GenerationOptions{
|
||||
Country: []string{"US"},
|
||||
Locality: []string{"San Francisco"},
|
||||
Organization: []string{"INetMock"},
|
||||
StreetAddress: []string{"Golden Gate Bridge"},
|
||||
PostalCode: []string{"94016"},
|
||||
Province: []string{""},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
certOptionsType := reflect.TypeOf(GenerationOptions{})
|
||||
defaulters.Register(certOptionsType, certOptionsDefaulter)
|
||||
func applyDefaultGenerationOptions(opts *GenerationOptions) error {
|
||||
return mergo.Merge(opts, defaultOptions)
|
||||
}
|
||||
|
|
|
@ -6,13 +6,14 @@ import (
|
|||
)
|
||||
|
||||
func Test_certOptionsDefaulter(t *testing.T) {
|
||||
tests := []struct {
|
||||
type testCase struct {
|
||||
name string
|
||||
arg GenerationOptions
|
||||
expected GenerationOptions
|
||||
}{
|
||||
}
|
||||
tests := []testCase{
|
||||
{
|
||||
name: "",
|
||||
name: "Empty options",
|
||||
arg: GenerationOptions{
|
||||
CommonName: "CA",
|
||||
},
|
||||
|
@ -26,14 +27,50 @@ func Test_certOptionsDefaulter(t *testing.T) {
|
|||
Province: []string{""},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Options with country set",
|
||||
arg: GenerationOptions{
|
||||
CommonName: "CA",
|
||||
Country: []string{"DE"},
|
||||
},
|
||||
expected: GenerationOptions{
|
||||
CommonName: "CA",
|
||||
Country: []string{"DE"},
|
||||
Locality: []string{"San Francisco"},
|
||||
Organization: []string{"INetMock"},
|
||||
StreetAddress: []string{"Golden Gate Bridge"},
|
||||
PostalCode: []string{"94016"},
|
||||
Province: []string{""},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Options with organization set set",
|
||||
arg: GenerationOptions{
|
||||
CommonName: "CA",
|
||||
Organization: []string{"inetmock"},
|
||||
},
|
||||
expected: GenerationOptions{
|
||||
CommonName: "CA",
|
||||
Country: []string{"US"},
|
||||
Locality: []string{"San Francisco"},
|
||||
Organization: []string{"inetmock"},
|
||||
StreetAddress: []string{"Golden Gate Bridge"},
|
||||
PostalCode: []string{"94016"},
|
||||
Province: []string{""},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
certOptionsDefaulter(&tt.arg)
|
||||
scenario := func(tt testCase) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
if err := applyDefaultGenerationOptions(&tt.arg); err != nil {
|
||||
t.Errorf("applyDefaultGenerationOptions() error = %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(tt.expected, tt.arg) {
|
||||
t.Errorf("Apply defaulter expected=%v got=%v", tt.expected, tt.arg)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, scenario(tt))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,13 +10,6 @@ import (
|
|||
"encoding/pem"
|
||||
"math/big"
|
||||
"net"
|
||||
|
||||
"gitlab.com/inetmock/inetmock/pkg/config"
|
||||
"gitlab.com/inetmock/inetmock/pkg/defaulting"
|
||||
)
|
||||
|
||||
var (
|
||||
defaulters = defaulting.New()
|
||||
)
|
||||
|
||||
type GenerationOptions struct {
|
||||
|
@ -37,11 +30,11 @@ type Generator interface {
|
|||
ServerCert(options GenerationOptions, ca *tls.Certificate) (*tls.Certificate, error)
|
||||
}
|
||||
|
||||
func NewDefaultGenerator(options config.CertOptions) Generator {
|
||||
func NewDefaultGenerator(options CertOptions) Generator {
|
||||
return NewGenerator(options, NewTimeSource(), defaultKeyProvider(options))
|
||||
}
|
||||
|
||||
func NewGenerator(options config.CertOptions, source TimeSource, provider KeyProvider) Generator {
|
||||
func NewGenerator(options CertOptions, source TimeSource, provider KeyProvider) Generator {
|
||||
return &generator{
|
||||
options: options,
|
||||
provider: provider,
|
||||
|
@ -50,7 +43,7 @@ func NewGenerator(options config.CertOptions, source TimeSource, provider KeyPro
|
|||
}
|
||||
|
||||
type generator struct {
|
||||
options config.CertOptions
|
||||
options CertOptions
|
||||
provider KeyProvider
|
||||
timeSource TimeSource
|
||||
outDir string
|
||||
|
@ -65,7 +58,7 @@ func (g *generator) privateKey() (key interface{}, err error) {
|
|||
}
|
||||
|
||||
func (g *generator) ServerCert(options GenerationOptions, ca *tls.Certificate) (cert *tls.Certificate, err error) {
|
||||
defaulters.Apply(&options)
|
||||
applyDefaultGenerationOptions(&options)
|
||||
var serialNumber *big.Int
|
||||
if serialNumber, err = generateSerialNumber(); err != nil {
|
||||
return
|
||||
|
@ -116,7 +109,7 @@ func (g *generator) ServerCert(options GenerationOptions, ca *tls.Certificate) (
|
|||
}
|
||||
|
||||
func (g generator) CACert(options GenerationOptions) (crt *tls.Certificate, err error) {
|
||||
defaulters.Apply(&options)
|
||||
applyDefaultGenerationOptions(&options)
|
||||
|
||||
var privateKey interface{}
|
||||
var serialNumber *big.Int
|
||||
|
|
|
@ -1,16 +1,10 @@
|
|||
package cert
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"gitlab.com/inetmock/inetmock/pkg/config"
|
||||
)
|
||||
|
||||
func init() {
|
||||
/*func init() {
|
||||
config.AddDefaultValue(certCachePathConfigKey, os.TempDir())
|
||||
config.AddDefaultValue(ecdsaCurveConfigKey, string(config.CurveTypeED25519))
|
||||
config.AddDefaultValue(ecdsaCurveConfigKey, string(CurveTypeED25519))
|
||||
config.AddDefaultValue(caCertValidityNotBeforeKey, defaultCAValidityDuration)
|
||||
config.AddDefaultValue(caCertValidityNotAfterKey, defaultCAValidityDuration)
|
||||
config.AddDefaultValue(serverCertValidityNotBeforeKey, defaultServerValidityDuration)
|
||||
config.AddDefaultValue(serverCertValidityNotAfterKey, defaultServerValidityDuration)
|
||||
}
|
||||
}*/
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"gitlab.com/inetmock/inetmock/pkg/config"
|
||||
)
|
||||
|
||||
func readViper(cfg string) *viper.Viper {
|
||||
|
@ -26,7 +25,7 @@ func Test_loadFromConfig(t *testing.T) {
|
|||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want config.CertOptions
|
||||
want CertOptions
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
|
@ -49,19 +48,19 @@ tls:
|
|||
certCachePath: /tmp/inetmock/
|
||||
`),
|
||||
},
|
||||
want: config.CertOptions{
|
||||
RootCACert: config.File{
|
||||
want: CertOptions{
|
||||
RootCACert: File{
|
||||
PublicKeyPath: "./ca.pem",
|
||||
PrivateKeyPath: "./ca.key",
|
||||
},
|
||||
CertCachePath: "/tmp/inetmock/",
|
||||
Curve: config.CurveTypeP256,
|
||||
Validity: config.ValidityByPurpose{
|
||||
CA: config.ValidityDuration{
|
||||
Curve: CurveTypeP256,
|
||||
Validity: ValidityByPurpose{
|
||||
CA: ValidityDuration{
|
||||
NotBeforeRelative: 17520 * time.Hour,
|
||||
NotAfterRelative: 17520 * time.Hour,
|
||||
},
|
||||
Server: config.ValidityDuration{
|
||||
Server: ValidityDuration{
|
||||
NotBeforeRelative: 168 * time.Hour,
|
||||
NotAfterRelative: 168 * time.Hour,
|
||||
},
|
||||
|
@ -77,7 +76,7 @@ tls:
|
|||
privateKey: ./ca.key
|
||||
`),
|
||||
},
|
||||
want: config.CertOptions{},
|
||||
want: CertOptions{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
|
@ -89,7 +88,7 @@ tls:
|
|||
publicKey: ./ca.pem
|
||||
`),
|
||||
},
|
||||
want: config.CertOptions{},
|
||||
want: CertOptions{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
|
@ -102,19 +101,19 @@ tls:
|
|||
privateKey: ./ca.key
|
||||
`),
|
||||
},
|
||||
want: config.CertOptions{
|
||||
RootCACert: config.File{
|
||||
want: CertOptions{
|
||||
RootCACert: File{
|
||||
PublicKeyPath: "./ca.pem",
|
||||
PrivateKeyPath: "./ca.key",
|
||||
},
|
||||
CertCachePath: os.TempDir(),
|
||||
Curve: config.CurveTypeED25519,
|
||||
Validity: config.ValidityByPurpose{
|
||||
CA: config.ValidityDuration{
|
||||
Curve: CurveTypeED25519,
|
||||
Validity: ValidityByPurpose{
|
||||
CA: ValidityDuration{
|
||||
NotBeforeRelative: 17520 * time.Hour,
|
||||
NotAfterRelative: 17520 * time.Hour,
|
||||
},
|
||||
Server: config.ValidityDuration{
|
||||
Server: ValidityDuration{
|
||||
NotBeforeRelative: 168 * time.Hour,
|
||||
NotAfterRelative: 168 * time.Hour,
|
||||
},
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
"crypto/x509"
|
||||
"net"
|
||||
|
||||
"gitlab.com/inetmock/inetmock/pkg/config"
|
||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
@ -18,7 +17,7 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
defaultKeyProvider = func(options config.CertOptions) func() (key interface{}, err error) {
|
||||
defaultKeyProvider = func(options CertOptions) func() (key interface{}, err error) {
|
||||
return func() (key interface{}, err error) {
|
||||
return privateKeyForCurve(options)
|
||||
}
|
||||
|
@ -34,20 +33,20 @@ type Store interface {
|
|||
}
|
||||
|
||||
func NewDefaultStore(
|
||||
config config.Config,
|
||||
options CertOptions,
|
||||
logger logging.Logger,
|
||||
) (Store, error) {
|
||||
timeSource := NewTimeSource()
|
||||
return NewStore(
|
||||
config.TLSConfig(),
|
||||
NewFileSystemCache(config.TLSConfig().CertCachePath, timeSource),
|
||||
NewDefaultGenerator(config.TLSConfig()),
|
||||
options,
|
||||
NewFileSystemCache(options.CertCachePath, timeSource),
|
||||
NewDefaultGenerator(options),
|
||||
logger,
|
||||
)
|
||||
}
|
||||
|
||||
func NewStore(
|
||||
options config.CertOptions,
|
||||
options CertOptions,
|
||||
cache Cache,
|
||||
generator Generator,
|
||||
logger logging.Logger,
|
||||
|
@ -67,7 +66,7 @@ func NewStore(
|
|||
}
|
||||
|
||||
type store struct {
|
||||
options config.CertOptions
|
||||
options CertOptions
|
||||
caCert *tls.Certificate
|
||||
cache Cache
|
||||
timeSource TimeSource
|
||||
|
@ -147,15 +146,15 @@ func (s *store) GetCertificate(serverName string, ip string) (cert *tls.Certific
|
|||
return
|
||||
}
|
||||
|
||||
func privateKeyForCurve(options config.CertOptions) (privateKey interface{}, err error) {
|
||||
func privateKeyForCurve(options CertOptions) (privateKey interface{}, err error) {
|
||||
switch options.Curve {
|
||||
case config.CurveTypeP224:
|
||||
case CurveTypeP224:
|
||||
privateKey, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
|
||||
case config.CurveTypeP256:
|
||||
case CurveTypeP256:
|
||||
privateKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
case config.CurveTypeP384:
|
||||
case CurveTypeP384:
|
||||
privateKey, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
||||
case config.CurveTypeP521:
|
||||
case CurveTypeP521:
|
||||
privateKey, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
|
||||
default:
|
||||
privateKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func Test_config_ReadConfig(t *testing.T) {
|
||||
type args struct {
|
||||
flags *pflag.FlagSet
|
||||
config string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
matcher func(Config) bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Test endpoints config",
|
||||
args: args{
|
||||
flags: pflag.NewFlagSet("", pflag.ContinueOnError),
|
||||
config: `
|
||||
endpoints:
|
||||
plainHttp:
|
||||
handler: http_mock
|
||||
listenAddress: 0.0.0.0
|
||||
ports:
|
||||
- 80
|
||||
- 8080
|
||||
options: {}
|
||||
proxy:
|
||||
handler: http_proxy
|
||||
listenAddress: 0.0.0.0
|
||||
ports:
|
||||
- 3128
|
||||
options:
|
||||
target:
|
||||
ipAddress: 127.0.0.1
|
||||
port: 80
|
||||
`,
|
||||
},
|
||||
matcher: func(c Config) bool {
|
||||
if len(c.EndpointConfigs()) < 1 {
|
||||
t.Error("Expected EndpointConfigs to be set but is empty")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfg := CreateConfig(tt.args.flags)
|
||||
if err := cfg.ReadConfigString(tt.args.config, "yaml"); (err != nil) != tt.wantErr {
|
||||
t.Errorf("ReadConfig() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
if !tt.matcher(cfg) {
|
||||
t.Error("matcher error")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package config
|
||||
|
||||
const (
|
||||
EndpointsKey = "endpoints"
|
||||
OptionsKey = "options"
|
||||
|
||||
CurveTypeP224 CurveType = "P224"
|
||||
CurveTypeP256 CurveType = "P256"
|
||||
CurveTypeP384 CurveType = "P384"
|
||||
CurveTypeP521 CurveType = "P521"
|
||||
CurveTypeED25519 CurveType = "ED25519"
|
||||
|
||||
TLSVersionSSL3 TLSVersion = "SSL3"
|
||||
TLSVersionTLS10 TLSVersion = "TLS10"
|
||||
TLSVersionTLS11 TLSVersion = "TLS11"
|
||||
TLSVersionTLS12 TLSVersion = "TLS12"
|
||||
)
|
|
@ -1,18 +0,0 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type HandlerConfig struct {
|
||||
HandlerName string
|
||||
Port uint16
|
||||
ListenAddress string
|
||||
Options *viper.Viper
|
||||
}
|
||||
|
||||
func (h HandlerConfig) ListenAddr() string {
|
||||
return fmt.Sprintf("%s:%d", h.ListenAddress, h.Port)
|
||||
}
|
|
@ -1,168 +0,0 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func Test_handlerConfig_HandlerName(t *testing.T) {
|
||||
type fields struct {
|
||||
handlerName string
|
||||
port uint16
|
||||
listenAddress string
|
||||
options *viper.Viper
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "Get empty Handler for uninitialized struct",
|
||||
fields: fields{},
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "Get expected Handler for initialized struct",
|
||||
fields: fields{
|
||||
handlerName: "sampleHandler",
|
||||
},
|
||||
want: "sampleHandler",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
h := HandlerConfig{
|
||||
HandlerName: tt.fields.handlerName,
|
||||
Port: tt.fields.port,
|
||||
ListenAddress: tt.fields.listenAddress,
|
||||
Options: tt.fields.options,
|
||||
}
|
||||
if got := h.HandlerName; got != tt.want {
|
||||
t.Errorf("Handler() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_handlerConfig_ListenAddress(t *testing.T) {
|
||||
type fields struct {
|
||||
handlerName string
|
||||
port uint16
|
||||
listenAddress string
|
||||
options *viper.Viper
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "Get empty ListenAddress for uninitialized struct",
|
||||
fields: fields{},
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "Get expected ListenAddress for initialized struct",
|
||||
fields: fields{
|
||||
listenAddress: "0.0.0.0",
|
||||
},
|
||||
want: "0.0.0.0",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
h := HandlerConfig{
|
||||
HandlerName: tt.fields.handlerName,
|
||||
Port: tt.fields.port,
|
||||
ListenAddress: tt.fields.listenAddress,
|
||||
Options: tt.fields.options,
|
||||
}
|
||||
if got := h.ListenAddress; got != tt.want {
|
||||
t.Errorf("ListenAddress() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_handlerConfig_Options(t *testing.T) {
|
||||
type fields struct {
|
||||
handlerName string
|
||||
port uint16
|
||||
listenAddress string
|
||||
options *viper.Viper
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want *viper.Viper
|
||||
}{
|
||||
{
|
||||
name: "Get nil Options for uninitialized struct",
|
||||
fields: fields{},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "Get expected Options for initialized struct",
|
||||
fields: fields{
|
||||
options: viper.New(),
|
||||
},
|
||||
want: viper.New(),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
h := HandlerConfig{
|
||||
HandlerName: tt.fields.handlerName,
|
||||
Port: tt.fields.port,
|
||||
ListenAddress: tt.fields.listenAddress,
|
||||
Options: tt.fields.options,
|
||||
}
|
||||
if got := h.Options; !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Options() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_handlerConfig_Port(t *testing.T) {
|
||||
type fields struct {
|
||||
handlerName string
|
||||
port uint16
|
||||
listenAddress string
|
||||
options *viper.Viper
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want uint16
|
||||
}{
|
||||
{
|
||||
name: "Get empty Port for uninitialized struct",
|
||||
fields: fields{},
|
||||
want: 0,
|
||||
},
|
||||
{
|
||||
name: "Get expected Port for initialized struct",
|
||||
fields: fields{
|
||||
port: 80,
|
||||
},
|
||||
want: 80,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
h := HandlerConfig{
|
||||
HandlerName: tt.fields.handlerName,
|
||||
Port: tt.fields.port,
|
||||
ListenAddress: tt.fields.listenAddress,
|
||||
Options: tt.fields.options,
|
||||
}
|
||||
if got := h.Port; got != tt.want {
|
||||
t.Errorf("Port() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type EndpointConfig struct {
|
||||
Handler string
|
||||
Ports []uint16
|
||||
ListenAddress string
|
||||
Options *viper.Viper
|
||||
}
|
||||
|
||||
func (m EndpointConfig) HandlerConfigs() []HandlerConfig {
|
||||
configs := make([]HandlerConfig, 0)
|
||||
for _, port := range m.Ports {
|
||||
configs = append(configs, HandlerConfig{
|
||||
HandlerName: m.Handler,
|
||||
Port: port,
|
||||
ListenAddress: m.ListenAddress,
|
||||
Options: m.Options,
|
||||
})
|
||||
}
|
||||
return configs
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
package defaulting
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type Defaulter func(instance interface{})
|
||||
|
||||
type Registry interface {
|
||||
Register(t reflect.Type, defaulter ...Defaulter)
|
||||
Apply(instance interface{})
|
||||
}
|
||||
|
||||
func New() Registry {
|
||||
return ®istry{
|
||||
defaulters: make(map[reflect.Type][]Defaulter),
|
||||
}
|
||||
}
|
||||
|
||||
type registry struct {
|
||||
defaulters map[reflect.Type][]Defaulter
|
||||
}
|
||||
|
||||
func (r *registry) Register(t reflect.Type, defaulter ...Defaulter) {
|
||||
var given []Defaulter
|
||||
if r, ok := r.defaulters[t]; ok {
|
||||
given = r
|
||||
}
|
||||
|
||||
given = append(given, defaulter...)
|
||||
|
||||
r.defaulters[t] = given
|
||||
}
|
||||
|
||||
func (r *registry) Apply(instance interface{}) {
|
||||
if defs, ok := r.defaulters[reflect.TypeOf(instance)]; ok {
|
||||
for _, def := range defs {
|
||||
def(instance)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
package defaulting
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_registry_Apply(t *testing.T) {
|
||||
type sample struct {
|
||||
i int
|
||||
}
|
||||
type fields struct {
|
||||
defaulters map[reflect.Type][]Defaulter
|
||||
}
|
||||
type args struct {
|
||||
instance interface{}
|
||||
}
|
||||
type expect struct {
|
||||
result interface{}
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
expect expect
|
||||
}{
|
||||
{
|
||||
name: "Expect setting a sample value",
|
||||
fields: fields{
|
||||
defaulters: map[reflect.Type][]Defaulter{
|
||||
reflect.TypeOf(&sample{}): {func(instance interface{}) {
|
||||
if i, ok := instance.(*sample); ok {
|
||||
i.i = 42
|
||||
}
|
||||
}},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
instance: &sample{},
|
||||
},
|
||||
expect: expect{
|
||||
result: &sample{
|
||||
i: 42,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := ®istry{
|
||||
defaulters: tt.fields.defaulters,
|
||||
}
|
||||
|
||||
r.Apply(tt.args.instance)
|
||||
|
||||
if !reflect.DeepEqual(tt.expect.result, tt.args.instance) {
|
||||
t.Errorf("Apply() expected = %v got %v", tt.args.instance, tt.expect.result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_registry_Register(t *testing.T) {
|
||||
type sample struct {
|
||||
}
|
||||
type fields struct {
|
||||
defaulters map[reflect.Type][]Defaulter
|
||||
}
|
||||
type args struct {
|
||||
t reflect.Type
|
||||
defaulter []Defaulter
|
||||
}
|
||||
type expect struct {
|
||||
length int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
expect expect
|
||||
}{
|
||||
{
|
||||
name: "",
|
||||
fields: fields{
|
||||
defaulters: make(map[reflect.Type][]Defaulter),
|
||||
},
|
||||
args: args{
|
||||
t: reflect.TypeOf(sample{}),
|
||||
defaulter: []Defaulter{func(instance interface{}) {
|
||||
|
||||
}},
|
||||
},
|
||||
expect: expect{
|
||||
length: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := ®istry{
|
||||
defaulters: tt.fields.defaulters,
|
||||
}
|
||||
r.Register(tt.args.t, tt.args.defaulter...)
|
||||
|
||||
if length := len(r.defaulters); length != tt.expect.length {
|
||||
t.Errorf("len(r.defaulters) expect %d got %d", tt.expect.length, length)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -42,7 +42,7 @@ func ParseLevel(levelString string) zap.AtomicLevel {
|
|||
}
|
||||
|
||||
func CreateLogger() (Logger, error) {
|
||||
if zapLogger, err := loggingConfig.Build(); err != nil {
|
||||
if zapLogger, err := loggingConfig.Build(zap.AddCallerSkip(2)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return NewLogger(zapLogger), nil
|
||||
|
@ -55,11 +55,3 @@ func CreateTestLogger(tb testing.TB) Logger {
|
|||
encoder: zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig()),
|
||||
}
|
||||
}
|
||||
|
||||
func MustCreateLogger() Logger {
|
||||
if logger, err := CreateLogger(); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
return logger
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,17 +19,19 @@ type testLogger struct {
|
|||
|
||||
func (t testLogger) Named(s string) Logger {
|
||||
return testLogger{
|
||||
name: s,
|
||||
tb: t.tb,
|
||||
fields: t.fields,
|
||||
encoder: t.encoder,
|
||||
name: s,
|
||||
tb: t.tb,
|
||||
fields: t.fields,
|
||||
}
|
||||
}
|
||||
|
||||
func (t testLogger) With(fields ...zap.Field) Logger {
|
||||
return &testLogger{
|
||||
name: t.name,
|
||||
fields: append(t.fields, fields...),
|
||||
tb: t.tb,
|
||||
encoder: t.encoder,
|
||||
name: t.name,
|
||||
fields: append(t.fields, fields...),
|
||||
tb: t.tb,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
20
testdata/integration.dockerfile
vendored
Normal file
20
testdata/integration.dockerfile
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
FROM golang:1.15-alpine as build
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY ./ ./
|
||||
|
||||
RUN go build -o inetmock ./cmd/inetmock
|
||||
|
||||
FROM alpine:3.13
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=build /app/inetmock ./
|
||||
COPY --from=build /app/config-container.yaml /etc/inetmock/config.yaml
|
||||
COPY --from=build /app/assets/fakeFiles /var/lib/inetmock/fakeFiles
|
||||
COPY --from=build /app/assets/demoCA /var/lib/inetmock/ca
|
||||
|
||||
RUN mkdir -p /var/run/inetmock
|
||||
|
||||
CMD /app/inetmock serve
|
Loading…
Reference in a new issue