Merge branch '10-handler-event-stream' into 'master'
Resolve "Handler Event Stream" Closes #13 and #10 See merge request inetmock/inetmock!12
This commit is contained in:
commit
dd4b191abb
90 changed files with 3323 additions and 481 deletions
|
@ -9,6 +9,10 @@ test:
|
|||
stage: test
|
||||
script:
|
||||
- task cli-cover-report
|
||||
artifacts:
|
||||
reports:
|
||||
junit: out/report.xml
|
||||
cobertura: out/coverage.xml
|
||||
|
||||
lint:
|
||||
stage: test
|
||||
|
|
|
@ -7,7 +7,7 @@ before:
|
|||
builds:
|
||||
- id: "inetmock"
|
||||
binary: inetmock
|
||||
main: ./cmd/inetmock/main.go
|
||||
main: ./cmd/inetmock/
|
||||
ldflags:
|
||||
- -w -s
|
||||
env:
|
||||
|
@ -19,7 +19,7 @@ builds:
|
|||
- amd64
|
||||
- id: "imctl"
|
||||
binary: imctl
|
||||
main: ./cmd/imctl/main.go
|
||||
main: ./cmd/imctl/
|
||||
ldflags:
|
||||
- -w -s
|
||||
goos:
|
||||
|
@ -65,7 +65,7 @@ release:
|
|||
name: inetmock
|
||||
|
||||
dockers:
|
||||
- binaries:
|
||||
- ids:
|
||||
- inetmock
|
||||
- imctl
|
||||
image_templates:
|
||||
|
@ -81,4 +81,4 @@ dockers:
|
|||
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||
extra_files:
|
||||
- config-container.yaml
|
||||
- assets/fakeFiles/
|
||||
- assets/
|
||||
|
|
|
@ -5,7 +5,7 @@ vars:
|
|||
INETMOCK_PKG: gitlab.com/inetmock/inetmock/cmd/inetmock
|
||||
IMCTL_PKG: gitlab.com/inetmock/inetmock/cmd/imctl
|
||||
PROTO_FILES:
|
||||
sh: find ./api/ -type f -name "*.proto" -printf "%f "
|
||||
sh: find ./api/ -type f -name "*.proto" -printf "%p "
|
||||
|
||||
env:
|
||||
GOOS: linux
|
||||
|
@ -26,7 +26,7 @@ tasks:
|
|||
sources:
|
||||
- "**/*.proto"
|
||||
cmds:
|
||||
- protoc --proto_path ./api/ --go_out=./internal/rpc --go_opt=paths=source_relative --go-grpc_out=./internal/rpc --go-grpc_opt=paths=source_relative {{ .PROTO_FILES }}
|
||||
- protoc --proto_path ./api/proto/ --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative {{ .PROTO_FILES }}
|
||||
|
||||
go-generate:
|
||||
sources:
|
||||
|
@ -46,8 +46,11 @@ tasks:
|
|||
- generate
|
||||
cmds:
|
||||
- mkdir -p {{ .OUT_DIR }}
|
||||
- go test -coverprofile={{ .OUT_DIR }}/cov-raw.out -v ./...
|
||||
- cmd: go test -coverprofile={{ .OUT_DIR }}/cov-raw.out -covermode count -v ./... 2>&1 | tee {{ .OUT_DIR }}/test_output
|
||||
ignore_error: true
|
||||
- cat {{ .OUT_DIR }}/test_output | go-junit-report -set-exit-code > {{ .OUT_DIR }}/report.xml
|
||||
- grep -v "generated" {{ .OUT_DIR }}/cov-raw.out > {{ .OUT_DIR }}/cov.out
|
||||
- gocover-cobertura < {{ .OUT_DIR }}/cov.out > {{ .OUT_DIR }}/coverage.xml
|
||||
- rm -f {{ .OUT_DIR }}/cov-raw.out
|
||||
|
||||
cli-cover-report:
|
||||
|
|
52
api/proto/internal/rpc/audit.proto
Normal file
52
api/proto/internal/rpc/audit.proto
Normal file
|
@ -0,0 +1,52 @@
|
|||
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 = "AuditProto";
|
||||
|
||||
import 'pkg/audit/event_entity.proto';
|
||||
|
||||
package inetmock.rpc;
|
||||
|
||||
enum FileOpenMode {
|
||||
TRUNCATE = 0;
|
||||
APPEND = 1;
|
||||
}
|
||||
|
||||
message WatchEventsRequest {
|
||||
string watcherName = 1;
|
||||
}
|
||||
|
||||
message RegisterFileSinkRequest {
|
||||
string targetPath = 1;
|
||||
FileOpenMode openMode = 2;
|
||||
uint32 permissions = 3;
|
||||
}
|
||||
|
||||
message RegisterFileSinkResponse {
|
||||
|
||||
}
|
||||
|
||||
message RemoveFileSinkRequest {
|
||||
string targetPath = 1;
|
||||
}
|
||||
|
||||
message RemoveFileSinkResponse {
|
||||
bool SinkGotRemoved = 1;
|
||||
}
|
||||
|
||||
message ListSinksRequest {
|
||||
|
||||
}
|
||||
|
||||
message ListSinksResponse {
|
||||
repeated string sinks = 1;
|
||||
}
|
||||
|
||||
service Audit {
|
||||
rpc WatchEvents (WatchEventsRequest) returns (stream inetmock.audit.EventEntity);
|
||||
rpc RegisterFileSink (RegisterFileSinkRequest) returns (RegisterFileSinkResponse);
|
||||
rpc RemoveFileSink (RemoveFileSinkRequest) returns (RemoveFileSinkResponse);
|
||||
rpc ListSinks(ListSinksRequest) returns (ListSinksResponse);
|
||||
}
|
|
@ -5,12 +5,7 @@ option java_multiple_files = true;
|
|||
option java_package = "com.github.baez90.inetmock.rpc";
|
||||
option java_outer_classname = "EndpointsProto";
|
||||
|
||||
package inetmock;
|
||||
|
||||
service Endpoints {
|
||||
rpc GetEndpoints (GetEndpointsRequest) returns (GetEndpointsResponse) {
|
||||
}
|
||||
}
|
||||
package inetmock.rpc;
|
||||
|
||||
message GetEndpointsRequest {
|
||||
}
|
||||
|
@ -26,3 +21,8 @@ message Endpoint {
|
|||
string listenAddress = 4;
|
||||
int32 port = 5;
|
||||
}
|
||||
|
||||
service Endpoints {
|
||||
rpc GetEndpoints (GetEndpointsRequest) returns (GetEndpointsResponse) {
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ option java_multiple_files = true;
|
|||
option java_package = "com.github.baez90.inetmock.rpc";
|
||||
option java_outer_classname = "HandlersProto";
|
||||
|
||||
package inetmock;
|
||||
package inetmock.rpc;
|
||||
|
||||
service Handlers {
|
||||
rpc GetHandlers (GetHandlersRequest) returns (GetHandlersResponse) {
|
|
@ -5,7 +5,7 @@ option java_multiple_files = true;
|
|||
option java_package = "com.github.baez90.inetmock.rpc";
|
||||
option java_outer_classname = "HealthProto";
|
||||
|
||||
package inetmock;
|
||||
package inetmock.rpc;
|
||||
|
||||
service Health {
|
||||
rpc GetHealth (HealthRequest) returns (HealthResponse) {
|
42
api/proto/pkg/audit/details/dns_details.proto
Normal file
42
api/proto/pkg/audit/details/dns_details.proto
Normal file
|
@ -0,0 +1,42 @@
|
|||
syntax = "proto3";
|
||||
|
||||
option go_package = "gitlab.com/inetmock/inetmock/pkg/audit/details";
|
||||
option java_multiple_files = true;
|
||||
option java_package = "com.github.baez90.inetmock.audit.details";
|
||||
option java_outer_classname = "HandlerEventProto";
|
||||
|
||||
package inetmock.audit;
|
||||
|
||||
enum DNSOpCode {
|
||||
Query = 0;
|
||||
Status = 2;
|
||||
Notify = 4;
|
||||
Update = 5;
|
||||
}
|
||||
|
||||
enum ResourceRecordType {
|
||||
UnknownRR = 0;
|
||||
A = 1;
|
||||
NS = 2;
|
||||
CNAME = 5;
|
||||
SOA = 6;
|
||||
PTR = 12;
|
||||
HINFO = 13;
|
||||
MINFO = 14;
|
||||
MX = 15;
|
||||
TXT = 16;
|
||||
RP = 17;
|
||||
AAAA = 28;
|
||||
SRV = 33;
|
||||
NAPTR = 35;
|
||||
}
|
||||
|
||||
message DNSQuestionEntity {
|
||||
ResourceRecordType type = 1;
|
||||
string name = 2;
|
||||
}
|
||||
|
||||
message DNSDetailsEntity {
|
||||
DNSOpCode opcode = 1;
|
||||
repeated DNSQuestionEntity questions = 2;
|
||||
}
|
32
api/proto/pkg/audit/details/http_details.proto
Normal file
32
api/proto/pkg/audit/details/http_details.proto
Normal file
|
@ -0,0 +1,32 @@
|
|||
syntax = "proto3";
|
||||
|
||||
option go_package = "gitlab.com/inetmock/inetmock/pkg/audit/details";
|
||||
option java_multiple_files = true;
|
||||
option java_package = "com.github.baez90.inetmock.audit.details";
|
||||
option java_outer_classname = "HandlerEventProto";
|
||||
|
||||
package inetmock.audit;
|
||||
|
||||
enum HTTPMethod {
|
||||
GET = 0;
|
||||
HEAD = 1;
|
||||
POST = 2;
|
||||
PUT = 3;
|
||||
DELETE = 4;
|
||||
CONNECT = 5;
|
||||
OPTIONS = 6;
|
||||
TRACE = 7;
|
||||
PATCH = 8;
|
||||
}
|
||||
|
||||
message HTTPHeaderValue {
|
||||
repeated string values = 1;
|
||||
}
|
||||
|
||||
message HTTPDetailsEntity {
|
||||
HTTPMethod method = 1;
|
||||
string host = 2;
|
||||
string uri = 3;
|
||||
string proto = 4;
|
||||
map<string, HTTPHeaderValue> headers = 5;
|
||||
}
|
51
api/proto/pkg/audit/event_entity.proto
Normal file
51
api/proto/pkg/audit/event_entity.proto
Normal file
|
@ -0,0 +1,51 @@
|
|||
syntax = "proto3";
|
||||
|
||||
option go_package = "gitlab.com/inetmock/inetmock/pkg/audit";
|
||||
option java_multiple_files = true;
|
||||
option java_package = "com.github.baez90.inetmock.audit";
|
||||
option java_outer_classname = "HandlerEventProto";
|
||||
|
||||
package inetmock.audit;
|
||||
|
||||
import "google/protobuf/any.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
enum TransportProtocol {
|
||||
UNKNOWN_TRANSPORT = 0;
|
||||
TCP = 1;
|
||||
UDP = 2;
|
||||
}
|
||||
|
||||
enum AppProtocol {
|
||||
UNKNOWN_APPLICATION = 0;
|
||||
DNS = 1;
|
||||
HTTP = 2;
|
||||
HTTP_PROXY = 3;
|
||||
}
|
||||
|
||||
enum TLSVersion {
|
||||
SSLv30 = 0;
|
||||
TLS10 = 1;
|
||||
TLS11 = 2;
|
||||
TLS12 = 3;
|
||||
TLS13 = 4;
|
||||
}
|
||||
|
||||
message TLSDetailsEntity {
|
||||
TLSVersion version = 1;
|
||||
string cipherSuite = 2;
|
||||
string serverName = 3;
|
||||
}
|
||||
|
||||
message EventEntity {
|
||||
int64 id = 1;
|
||||
google.protobuf.Timestamp timestamp = 2;
|
||||
TransportProtocol transport = 3;
|
||||
AppProtocol application = 4;
|
||||
bytes sourceIP = 5;
|
||||
bytes destinationIP = 6;
|
||||
uint32 sourcePort = 7;
|
||||
uint32 destinationPort = 8;
|
||||
TLSDetailsEntity tls = 9;
|
||||
google.protobuf.Any protocolDetails = 10;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgTTz25fFLS2WO4hXD
|
||||
162B059HEe+MAQtV4iGXf7HfKCihRANCAAT3D181Tzrz6i9Mx75pmyAsg+itojO9
|
||||
sHXZSswmfsh46IVK46m0hXNHgPvD2WYW5m1PHvRl3B0vDo/2Y6sOU/Q9
|
||||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgEk8KFe6Vl3xefH82
|
||||
Na91uPQkBz4D/s1xE+59+kaRM1+hRANCAASnjLNiOc4UNsRBIQN+FOv8CEd6ftDH
|
||||
Egg2dWUgGCFsgE2VaEG+jOwamBzTjCWbr0azIc+Brupd0CAChq8hVeaM
|
||||
-----END PRIVATE KEY-----
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIB3DCCAYKgAwIBAgIQHQIFIEcNZjsDP+wDtGPMXzAKBggqhkjOPQQDAjBOMRAw
|
||||
DgYDVQQGEwdnZXJtYW55MREwDwYDVQQHEwhEb3J0bXVuZDERMA8GA1UEChMISU5l
|
||||
dE1vY2sxFDASBgNVBAMTC0lOZXRNb2NrIENBMB4XDTIwMDYxNTEwNTEzNloXDTIw
|
||||
MDYxNTEwNTEzNlowTjEQMA4GA1UEBhMHZ2VybWFueTERMA8GA1UEBxMIRG9ydG11
|
||||
bmQxETAPBgNVBAoTCElOZXRNb2NrMRQwEgYDVQQDEwtJTmV0TW9jayBDQTBZMBMG
|
||||
ByqGSM49AgEGCCqGSM49AwEHA0IABPcPXzVPOvPqL0zHvmmbICyD6K2iM72wddlK
|
||||
zCZ+yHjohUrjqbSFc0eA+8PZZhbmbU8e9GXcHS8Oj/Zjqw5T9D2jQjBAMA4GA1Ud
|
||||
DwEB/wQEAwIChDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwDwYDVR0T
|
||||
AQH/BAUwAwEB/zAKBggqhkjOPQQDAgNIADBFAiBecJsOL7ej0kCkWOnoQJpW3JuY
|
||||
KQIxQBT+XXPKEJj14AIhANG4twTloC3amz8Y7Zn3DVtvjXlTgg8YwjBFG+JioQOe
|
||||
MIICGjCCAb+gAwIBAgIQZutydXTVGWPa32GhLwn4tDAKBggqhkjOPQQDAjBcMRAw
|
||||
DgYDVQQGEwdnZXJtYW55MQwwCgYDVQQIEwNOUlcxETAPBgNVBAcTCERvcnRtdW5k
|
||||
MREwDwYDVQQKEwhJTmV0TW9jazEUMBIGA1UEAxMLSU5ldE1vY2sgQ0EwIBcNMDEw
|
||||
MjAxMDgyODA5WhgPMjA1MTAxMjAwODI4MDlaMFwxEDAOBgNVBAYTB2dlcm1hbnkx
|
||||
DDAKBgNVBAgTA05SVzERMA8GA1UEBxMIRG9ydG11bmQxETAPBgNVBAoTCElOZXRN
|
||||
b2NrMRQwEgYDVQQDEwtJTmV0TW9jayBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEH
|
||||
A0IABKeMs2I5zhQ2xEEhA34U6/wIR3p+0McSCDZ1ZSAYIWyATZVoQb6M7BqYHNOM
|
||||
JZuvRrMhz4Gu6l3QIAKGryFV5oyjYTBfMA4GA1UdDwEB/wQEAwIChDAdBgNVHSUE
|
||||
FjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
|
||||
FgQUkD5REm4Z34O8em3l5cXmHgd3S4swCgYIKoZIzj0EAwIDSQAwRgIhAJ5xWTz/
|
||||
/51Jyo7sO4hi3FhyDYJXeC0koRSQCvMggWuPAiEAjpgtX/UFukTAfTFsDp8q3AXZ
|
||||
Kn0ejVWjmkh4K7nEJ5s=
|
||||
-----END CERTIFICATE-----
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Runtime layer
|
||||
FROM alpine:3.12
|
||||
FROM alpine:3.13
|
||||
|
||||
# Create appuser and group.
|
||||
ARG USER=inetmock
|
||||
|
@ -20,6 +20,7 @@ RUN addgroup -S -g "${GROUP_ID}" "${GROUP}" && \
|
|||
|
||||
COPY --chown=$USER:$GROUP inetmock imctl /usr/lib/inetmock/bin/
|
||||
COPY --chown=$USER:$GROUP assets/fakeFiles /var/lib/inetmock/fakeFiles/
|
||||
COPY --chown=$USER:$GROUP assets/demoCA /var/lib/inetmock/ca
|
||||
COPY config-container.yaml /etc/inetmock/config.yaml
|
||||
|
||||
RUN mkdir -p /var/run/inetmock /var/lib/inetmock/certs /usr/lib/inetmock && \
|
||||
|
|
118
cmd/imctl/audit_sinks.go
Normal file
118
cmd/imctl/audit_sinks.go
Normal file
|
@ -0,0 +1,118 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"gitlab.com/inetmock/inetmock/internal/format"
|
||||
"gitlab.com/inetmock/inetmock/internal/rpc"
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
||||
)
|
||||
|
||||
var (
|
||||
listSinksCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Aliases: []string{"ls", "dir"},
|
||||
Short: "List all subscribed sinks",
|
||||
RunE: runListSinks,
|
||||
}
|
||||
addFileCmd = &cobra.Command{
|
||||
Use: "add-file",
|
||||
Aliases: []string{"add"},
|
||||
Short: "subscribe events to a file",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runAddFile,
|
||||
}
|
||||
|
||||
removeFileCmd = &cobra.Command{
|
||||
Use: "remove-file",
|
||||
Aliases: []string{"rm", "del"},
|
||||
Short: "remove file subscription",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runRemoveFile,
|
||||
}
|
||||
|
||||
readFileCmd = &cobra.Command{
|
||||
Use: "read-file",
|
||||
Aliases: []string{"cat"},
|
||||
Short: "reads an audit file and prints the events",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runReadFile,
|
||||
}
|
||||
)
|
||||
|
||||
type printableSink struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func runListSinks(*cobra.Command, []string) (err error) {
|
||||
auditClient := rpc.NewAuditClient(conn)
|
||||
ctx, cancel := context.WithTimeout(cliApp.Context(), grpcTimeout)
|
||||
defer cancel()
|
||||
|
||||
var resp *rpc.ListSinksResponse
|
||||
if resp, err = auditClient.ListSinks(ctx, &rpc.ListSinksRequest{}); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var sinks []printableSink
|
||||
for _, s := range resp.Sinks {
|
||||
sinks = append(sinks, printableSink{Name: s})
|
||||
}
|
||||
|
||||
writer := format.Writer(outputFormat, os.Stdout)
|
||||
err = writer.Write(sinks)
|
||||
return
|
||||
}
|
||||
|
||||
func runAddFile(_ *cobra.Command, args []string) (err error) {
|
||||
auditClient := rpc.NewAuditClient(conn)
|
||||
ctx, cancel := context.WithTimeout(cliApp.Context(), grpcTimeout)
|
||||
defer cancel()
|
||||
|
||||
_, err = auditClient.RegisterFileSink(ctx, &rpc.RegisterFileSinkRequest{TargetPath: args[0]})
|
||||
return
|
||||
}
|
||||
|
||||
func runRemoveFile(_ *cobra.Command, args []string) (err error) {
|
||||
auditClient := rpc.NewAuditClient(conn)
|
||||
ctx, cancel := context.WithTimeout(cliApp.Context(), grpcTimeout)
|
||||
defer cancel()
|
||||
|
||||
_, err = auditClient.RemoveFileSink(ctx, &rpc.RemoveFileSinkRequest{TargetPath: args[0]})
|
||||
return
|
||||
}
|
||||
|
||||
func runReadFile(_ *cobra.Command, args []string) (err error) {
|
||||
if len(args) != 1 {
|
||||
return errors.New("expected only 1 argument")
|
||||
}
|
||||
|
||||
var reader io.ReadCloser
|
||||
if reader, err = os.Open(args[0]); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
eventReader := audit.NewEventReader(reader)
|
||||
var ev audit.Event
|
||||
|
||||
for err == nil {
|
||||
if ev, err = eventReader.Read(); err == nil {
|
||||
var jsonBytes []byte
|
||||
if jsonBytes, err = json.Marshal(ev); err == nil {
|
||||
fmt.Println(string(jsonBytes))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if errors.Is(err, io.EOF) {
|
||||
err = nil
|
||||
}
|
||||
|
||||
return
|
||||
}
|
54
cmd/imctl/audit_watch.go
Normal file
54
cmd/imctl/audit_watch.go
Normal file
|
@ -0,0 +1,54 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"gitlab.com/inetmock/inetmock/internal/rpc"
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
||||
)
|
||||
|
||||
var (
|
||||
watchEventsCmd = &cobra.Command{
|
||||
Use: "watch",
|
||||
Short: "Watch all audit events",
|
||||
RunE: watchAuditEvents,
|
||||
}
|
||||
|
||||
auditCmd = &cobra.Command{
|
||||
Use: "audit",
|
||||
Short: "Interact with the audit stream",
|
||||
}
|
||||
|
||||
listenerName string
|
||||
)
|
||||
|
||||
func watchAuditEvents(_ *cobra.Command, _ []string) (err error) {
|
||||
auditClient := rpc.NewAuditClient(conn)
|
||||
|
||||
var watchClient rpc.Audit_WatchEventsClient
|
||||
if watchClient, err = auditClient.WatchEvents(cliApp.Context(), &rpc.WatchEventsRequest{WatcherName: listenerName}); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
go func() {
|
||||
var protoEv *audit.EventEntity
|
||||
for protoEv, err = watchClient.Recv(); err == nil; protoEv, err = watchClient.Recv() {
|
||||
ev := audit.NewEventFromProto(protoEv)
|
||||
var out []byte
|
||||
out, err = json.Marshal(ev)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
fmt.Println(string(out))
|
||||
}
|
||||
}()
|
||||
|
||||
<-cliApp.Context().Done()
|
||||
err = watchClient.CloseSend()
|
||||
|
||||
return
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package cmd
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -8,14 +8,13 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
"gitlab.com/inetmock/inetmock/internal/format"
|
||||
"gitlab.com/inetmock/inetmock/internal/rpc"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
var (
|
||||
getEndpoints = &cobra.Command{
|
||||
Use: "get",
|
||||
Short: "Get all running endpoints",
|
||||
Run: runGetEndpoints,
|
||||
RunE: runGetEndpoints,
|
||||
}
|
||||
|
||||
endpointsCmd = &cobra.Command{
|
||||
|
@ -26,7 +25,7 @@ var (
|
|||
)
|
||||
|
||||
type printableEndpoint struct {
|
||||
Id string
|
||||
ID string
|
||||
Name string
|
||||
Handler string
|
||||
ListenAddress string
|
||||
|
@ -35,7 +34,7 @@ type printableEndpoint struct {
|
|||
|
||||
func fromEndpoint(ep *rpc.Endpoint) *printableEndpoint {
|
||||
return &printableEndpoint{
|
||||
Id: ep.Id,
|
||||
ID: ep.Id,
|
||||
Name: ep.Name,
|
||||
Handler: ep.Handler,
|
||||
ListenAddress: ep.ListenAddress,
|
||||
|
@ -50,16 +49,10 @@ func fromEndpoints(eps []*rpc.Endpoint) (out []*printableEndpoint) {
|
|||
return
|
||||
}
|
||||
|
||||
func runGetEndpoints(_ *cobra.Command, _ []string) {
|
||||
var err error
|
||||
var conn *grpc.ClientConn
|
||||
|
||||
if conn, err = grpc.Dial(inetMockSocketPath, grpc.WithInsecure()); err != nil {
|
||||
fmt.Printf("Failed to connecto INetMock socket: %v\n", err)
|
||||
os.Exit(10)
|
||||
}
|
||||
func runGetEndpoints(_ *cobra.Command, _ []string) (err error) {
|
||||
endpointsClient := rpc.NewEndpointsClient(conn)
|
||||
ctx, _ := context.WithTimeout(context.Background(), grpcTimeout)
|
||||
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)
|
||||
|
@ -70,4 +63,5 @@ func runGetEndpoints(_ *cobra.Command, _ []string) {
|
|||
if err = writer.Write(fromEndpoints(endpointsResp.Endpoints)); err != nil {
|
||||
fmt.Printf("Error occurred during writing response values: %v\n", err)
|
||||
}
|
||||
return
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package cmd
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -8,7 +8,6 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
"gitlab.com/inetmock/inetmock/internal/format"
|
||||
"gitlab.com/inetmock/inetmock/internal/rpc"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -39,15 +38,11 @@ func fromHandlers(hs []string) (handlers []*printableHandler) {
|
|||
}
|
||||
|
||||
func runGetHandlers(_ *cobra.Command, _ []string) {
|
||||
var err error
|
||||
var conn *grpc.ClientConn
|
||||
|
||||
if conn, err = grpc.Dial(inetMockSocketPath, grpc.WithInsecure()); err != nil {
|
||||
fmt.Printf("Failed to connecto INetMock socket: %v\n", err)
|
||||
os.Exit(10)
|
||||
}
|
||||
handlersClient := rpc.NewHandlersClient(conn)
|
||||
ctx, _ := context.WithTimeout(context.Background(), grpcTimeout)
|
||||
|
||||
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 {
|
|
@ -1,4 +1,4 @@
|
|||
package cmd
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -8,7 +8,6 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
"gitlab.com/inetmock/inetmock/internal/format"
|
||||
"gitlab.com/inetmock/inetmock/internal/rpc"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -55,15 +54,10 @@ func fromComponentsHealth(componentsHealth map[string]*rpc.ComponentHealth) (com
|
|||
}
|
||||
|
||||
func getHealthResult() (healthResp *rpc.HealthResponse, err error) {
|
||||
var conn *grpc.ClientConn
|
||||
|
||||
if conn, err = grpc.Dial(inetMockSocketPath, grpc.WithInsecure()); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var healthClient = rpc.NewHealthClient(conn)
|
||||
ctx, _ := context.WithTimeout(context.Background(), grpcTimeout)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout)
|
||||
healthResp, err = healthClient.GetHealth(ctx, &rpc.HealthRequest{})
|
||||
cancel()
|
||||
return
|
||||
}
|
||||
|
|
@ -1,9 +1,64 @@
|
|||
package main
|
||||
|
||||
import "gitlab.com/inetmock/inetmock/internal/cmd"
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/spf13/cobra"
|
||||
"gitlab.com/inetmock/inetmock/internal/app"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
var (
|
||||
inetMockSocketPath string
|
||||
outputFormat string
|
||||
grpcTimeout time.Duration
|
||||
cliApp app.App
|
||||
conn *grpc.ClientConn
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := cmd.ExecuteClientCommand(); err != nil {
|
||||
panic(err)
|
||||
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).
|
||||
WithInitTasks(func(_ *cobra.Command, _ []string) (err error) {
|
||||
return initGRPCConnection()
|
||||
}).
|
||||
WithLogger()
|
||||
|
||||
cliApp.RootCommand().PersistentFlags().StringVar(&inetMockSocketPath, "socket-path", "unix:///var/run/inetmock.sock", "Path to the INetMock socket file")
|
||||
cliApp.RootCommand().PersistentFlags().StringVarP(&outputFormat, "format", "f", "table", "Output format to use. Possible values: table, json, yaml")
|
||||
cliApp.RootCommand().PersistentFlags().DurationVar(&grpcTimeout, "grpc-timeout", 5*time.Second, "Timeout to connect to the gRPC API")
|
||||
|
||||
currentUser := ""
|
||||
if usr, err := user.Current(); err == nil {
|
||||
currentUser = usr.Username
|
||||
} else {
|
||||
currentUser = uuid.New().String()
|
||||
}
|
||||
|
||||
hostname := "."
|
||||
if hn, err := os.Hostname(); err == nil {
|
||||
hostname = hn
|
||||
}
|
||||
|
||||
watchEventsCmd.PersistentFlags().StringVar(&listenerName, "listener-name", fmt.Sprintf("%s\\%s is watching", hostname, currentUser), "set listener name - defaults to the current username, if the user cannot be determined a random UUID will be used")
|
||||
auditCmd.AddCommand(listSinksCmd, watchEventsCmd, addFileCmd, removeFileCmd, readFileCmd)
|
||||
|
||||
cliApp.MustRun()
|
||||
}
|
||||
|
||||
func initGRPCConnection() (err error) {
|
||||
dialCtx, cancel := context.WithTimeout(cliApp.Context(), grpcTimeout)
|
||||
conn, err = grpc.DialContext(dialCtx, inetMockSocketPath, grpc.WithInsecure())
|
||||
cancel()
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package cmd
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
|
@ -8,7 +8,6 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
"gitlab.com/inetmock/inetmock/pkg/cert"
|
||||
"gitlab.com/inetmock/inetmock/pkg/config"
|
||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
@ -34,6 +33,7 @@ var (
|
|||
certOutPath, curveName string
|
||||
)
|
||||
|
||||
//nolint:lll
|
||||
func init() {
|
||||
generateCaCmd = &cobra.Command{
|
||||
Use: "generate-ca",
|
||||
|
@ -57,7 +57,7 @@ func init() {
|
|||
}
|
||||
|
||||
func runGenerateCA(_ *cobra.Command, _ []string) {
|
||||
logger := server.Logger().Named("generate-ca")
|
||||
logger := serverApp.Logger().Named("generate-ca")
|
||||
|
||||
logger = logger.With(
|
||||
zap.String(generateCACurveName, curveName),
|
||||
|
@ -108,25 +108,3 @@ func runGenerateCA(_ *cobra.Command, _ []string) {
|
|||
}
|
||||
logger.Info("completed certificate generation")
|
||||
}
|
||||
|
||||
func getDurationFlag(cmd *cobra.Command, flagName string, logger logging.Logger) (val time.Duration, err error) {
|
||||
if val, err = cmd.Flags().GetDuration(flagName); err != nil {
|
||||
logger.Error(
|
||||
"failed to parse parse flag",
|
||||
zap.String("flag", flagName),
|
||||
zap.Error(err),
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getStringFlag(cmd *cobra.Command, flagName string, logger logging.Logger) (val string, err error) {
|
||||
if val, err = cmd.Flags().GetString(flagName); err != nil {
|
||||
logger.Error(
|
||||
"failed to parse parse flag",
|
||||
zap.String("flag", flagName),
|
||||
zap.Error(err),
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
|
@ -1,14 +1,33 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"gitlab.com/inetmock/inetmock/internal/cmd"
|
||||
_ "gitlab.com/inetmock/inetmock/plugins/dns_mock"
|
||||
_ "gitlab.com/inetmock/inetmock/plugins/http_mock"
|
||||
_ "gitlab.com/inetmock/inetmock/plugins/http_proxy"
|
||||
_ "gitlab.com/inetmock/inetmock/plugins/metrics_exporter"
|
||||
_ "gitlab.com/inetmock/inetmock/plugins/tls_interceptor"
|
||||
"gitlab.com/inetmock/inetmock/internal/app"
|
||||
dns "gitlab.com/inetmock/inetmock/internal/endpoint/handler/dns/mock"
|
||||
http "gitlab.com/inetmock/inetmock/internal/endpoint/handler/http/mock"
|
||||
"gitlab.com/inetmock/inetmock/internal/endpoint/handler/http/proxy"
|
||||
"gitlab.com/inetmock/inetmock/internal/endpoint/handler/metrics"
|
||||
"gitlab.com/inetmock/inetmock/internal/endpoint/handler/tls/interceptor"
|
||||
)
|
||||
|
||||
var (
|
||||
serverApp app.App
|
||||
)
|
||||
|
||||
func main() {
|
||||
cmd.ExecuteServerCommand()
|
||||
serverApp = app.NewApp("inetmock", "INetMock is lightweight internet mock").
|
||||
WithHandlerRegistry(
|
||||
http.AddHTTPMock,
|
||||
dns.AddDNSMock,
|
||||
interceptor.AddTLSInterceptor,
|
||||
proxy.AddHTTPProxy,
|
||||
metrics.AddMetricsExporter).
|
||||
WithCommands(serveCmd, generateCaCmd).
|
||||
WithConfig().
|
||||
WithLogger().
|
||||
WithHealthChecker().
|
||||
WithCertStore().
|
||||
WithEventStream().
|
||||
WithEndpointManager()
|
||||
|
||||
serverApp.MustRun()
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package cmd
|
||||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
@ -19,13 +19,13 @@ var (
|
|||
)
|
||||
|
||||
func startINetMock(_ *cobra.Command, _ []string) {
|
||||
rpcAPI := rpc.NewINetMockAPI(server)
|
||||
logger := server.Logger().Named("inetmock").With(zap.String("command", "serve"))
|
||||
rpcAPI := rpc.NewINetMockAPI(serverApp)
|
||||
logger := serverApp.Logger().Named("inetmock").With(zap.String("command", "serve"))
|
||||
|
||||
for endpointName, endpointHandler := range server.Config().EndpointConfigs() {
|
||||
handlerSubConfig := server.Config().Viper().Sub(strings.Join([]string{config.EndpointsKey, endpointName, config.OptionsKey}, "."))
|
||||
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 := server.EndpointManager().CreateEndpoint(endpointName, endpointHandler); err != nil {
|
||||
if err := serverApp.EndpointManager().CreateEndpoint(endpointName, endpointHandler); err != nil {
|
||||
logger.Warn(
|
||||
"error occurred while creating endpoint",
|
||||
zap.String("endpointName", endpointName),
|
||||
|
@ -35,7 +35,7 @@ func startINetMock(_ *cobra.Command, _ []string) {
|
|||
}
|
||||
}
|
||||
|
||||
server.EndpointManager().StartEndpoints()
|
||||
serverApp.EndpointManager().StartEndpoints()
|
||||
if err := rpcAPI.StartServer(); err != nil {
|
||||
logger.Error(
|
||||
"failed to start gRPC API",
|
||||
|
@ -43,12 +43,12 @@ func startINetMock(_ *cobra.Command, _ []string) {
|
|||
)
|
||||
}
|
||||
|
||||
<-server.Context().Done()
|
||||
<-serverApp.Context().Done()
|
||||
|
||||
logger.Info(
|
||||
"App context canceled - shutting down",
|
||||
)
|
||||
|
||||
rpcAPI.StopServer()
|
||||
server.EndpointManager().ShutdownEndpoints()
|
||||
serverApp.EndpointManager().ShutdownEndpoints()
|
||||
}
|
|
@ -1,18 +1,44 @@
|
|||
x-response-rules: &httpResponseRules
|
||||
rules:
|
||||
- pattern: ".*\\.(?i)exe"
|
||||
matcher: Path
|
||||
- pattern: "^application/octet-stream$"
|
||||
target: Accept
|
||||
matcher: Header
|
||||
response: /var/lib/inetmock/fakeFiles/sample.exe
|
||||
- pattern: ".*\\.(?i)(jpg|jpeg)"
|
||||
- pattern: "^image/jpeg$"
|
||||
target: Accept
|
||||
matcher: Header
|
||||
response: /var/lib/inetmock/fakeFiles/default.jpg
|
||||
- pattern: ".*\\.(?i)(jpg|jpeg)"
|
||||
matcher: Path
|
||||
response: /var/lib/inetmock/fakeFiles/default.jpg
|
||||
- pattern: "^image/png$"
|
||||
target: Accept
|
||||
matcher: Header
|
||||
response: /var/lib/inetmock/fakeFiles/default.png
|
||||
- pattern: ".*\\.(?i)png"
|
||||
matcher: Path
|
||||
response: /var/lib/inetmock/fakeFiles/default.png
|
||||
- pattern: ".*\\.(?i)gif"
|
||||
matcher: Path
|
||||
response: /var/lib/inetmock/fakeFiles/default.gif
|
||||
- pattern: ".*\\.(?i)ico"
|
||||
matcher: Path
|
||||
response: /var/lib/inetmock/fakeFiles/default.ico
|
||||
- pattern: ".*\\.(?i)txt"
|
||||
- pattern: "^text/plain$"
|
||||
target: Accept
|
||||
matcher: Header
|
||||
response: /var/lib/inetmock/fakeFiles/default.txt
|
||||
- pattern: ".*\\.(?i)txt"
|
||||
matcher: Path
|
||||
response: /var/lib/inetmock/fakeFiles/default.txt
|
||||
- pattern: "^text/html$"
|
||||
target: Accept
|
||||
matcher: Header
|
||||
response: /var/lib/inetmock/fakeFiles/default.html
|
||||
- pattern: ".*"
|
||||
matcher: Path
|
||||
response: /var/lib/inetmock/fakeFiles/default.html
|
||||
|
||||
api:
|
||||
|
@ -43,6 +69,15 @@ endpoints:
|
|||
- 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
|
||||
|
@ -52,16 +87,6 @@ endpoints:
|
|||
target:
|
||||
ipAddress: 127.0.0.1
|
||||
port: 80
|
||||
httpsDowngrade:
|
||||
handler: tls_interceptor
|
||||
listenAddress: 0.0.0.0
|
||||
ports:
|
||||
- 443
|
||||
- 8443
|
||||
options:
|
||||
target:
|
||||
ipAddress: 127.0.0.1
|
||||
port: 80
|
||||
plainDns:
|
||||
handler: dns_mock
|
||||
listenAddress: 0.0.0.0
|
||||
|
@ -86,3 +111,10 @@ endpoints:
|
|||
target:
|
||||
ipAddress: 127.0.0.1
|
||||
port: 53
|
||||
metrics:
|
||||
handler: metrics_exporter
|
||||
listenAddress: 0.0.0.0
|
||||
ports:
|
||||
- 9110
|
||||
options:
|
||||
route: /metrics
|
19
config.yaml
19
config.yaml
|
@ -69,6 +69,15 @@ endpoints:
|
|||
- 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
|
||||
|
@ -78,16 +87,6 @@ endpoints:
|
|||
target:
|
||||
ipAddress: 127.0.0.1
|
||||
port: 80
|
||||
httpsDowngrade:
|
||||
handler: tls_interceptor
|
||||
listenAddress: 0.0.0.0
|
||||
ports:
|
||||
- 443
|
||||
- 8443
|
||||
options:
|
||||
target:
|
||||
ipAddress: 127.0.0.1
|
||||
port: 80
|
||||
plainDns:
|
||||
handler: dns_mock
|
||||
listenAddress: 0.0.0.0
|
||||
|
|
17
go.mod
17
go.mod
|
@ -3,18 +3,21 @@ module gitlab.com/inetmock/inetmock
|
|||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/bwmarrin/snowflake v0.3.0
|
||||
github.com/golang/mock v1.4.4
|
||||
github.com/golang/protobuf v1.4.2
|
||||
github.com/google/uuid v1.1.2
|
||||
github.com/miekg/dns v1.1.31
|
||||
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/prometheus/client_golang v1.7.1
|
||||
github.com/spf13/cobra v1.0.0
|
||||
github.com/prometheus/client_golang v1.9.0
|
||||
github.com/spf13/cobra v1.1.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/viper v1.7.1
|
||||
go.uber.org/multierr v1.6.0
|
||||
go.uber.org/zap v1.16.0
|
||||
google.golang.org/grpc v1.34.0
|
||||
google.golang.org/grpc v1.35.0
|
||||
google.golang.org/protobuf v1.25.0
|
||||
gopkg.in/elazarl/goproxy.v1 v1.0.0-20180725130230-947c36da3153
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
)
|
||||
|
|
245
go.sum
245
go.sum
|
@ -14,15 +14,27 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
|
|||
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/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=
|
||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
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=
|
||||
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
|
||||
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
|
@ -30,51 +42,79 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
|||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
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/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=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||
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-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
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/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=
|
||||
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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/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/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/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.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
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=
|
||||
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
|
||||
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/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=
|
||||
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
|
||||
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-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
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/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/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=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
|
@ -83,7 +123,6 @@ 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=
|
||||
|
@ -94,6 +133,7 @@ github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0
|
|||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
|
@ -108,21 +148,30 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi
|
|||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
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/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=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
|
||||
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
|
@ -133,6 +182,7 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX
|
|||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
|
@ -142,37 +192,60 @@ 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/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=
|
||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
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/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.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=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
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/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/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-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/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/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=
|
||||
|
@ -182,102 +255,194 @@ 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/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=
|
||||
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
|
||||
github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
|
||||
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
|
||||
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
|
||||
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/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/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
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=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
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/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=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||
github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
|
||||
github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.9.0 h1:Rrch9mh17XcxvEu9D9DEpb4isxjGBtcevQjKvxPRQIU=
|
||||
github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
|
||||
github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.15.0 h1:4fgOnadei3EZvgRwxJ7RMpG1k1pOZth5Pc13tyspaKM=
|
||||
github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8=
|
||||
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=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
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/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/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/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=
|
||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
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/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
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/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=
|
||||
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/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=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM=
|
||||
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
|
@ -286,8 +451,14 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
|||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
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=
|
||||
|
@ -310,11 +481,13 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
|||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
|
@ -325,10 +498,13 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR
|
|||
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=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
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=
|
||||
|
@ -343,9 +519,11 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -355,13 +533,25 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
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-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=
|
||||
|
@ -370,9 +560,14 @@ 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/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-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=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
|
@ -395,16 +590,19 @@ 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/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=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
|
@ -413,6 +611,7 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn
|
|||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
|
@ -420,21 +619,24 @@ 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-20201214200347-8c77b98c765d h1:HV9Z9qMhQEsdlvxNFELgQ11RkMzO3CMkjEySjCtuLes=
|
||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
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/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=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
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.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
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/cmd/protoc-gen-go-grpc v1.0.1 h1:M8spwkmx0pHrPq+uMdl22w5CvJ/Y+oAJTIs9oGoCpOE=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.0.1/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||
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/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=
|
||||
|
@ -453,12 +655,19 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33
|
|||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
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/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/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=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
@ -466,8 +675,16 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
|||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
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-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
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=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
@ -475,3 +692,5 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
|||
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
|
||||
|
|
|
@ -9,13 +9,16 @@ import (
|
|||
"syscall"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"gitlab.com/inetmock/inetmock/internal/endpoints"
|
||||
"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"
|
||||
)
|
||||
|
||||
|
@ -25,34 +28,76 @@ var (
|
|||
developmentLogs bool
|
||||
)
|
||||
|
||||
type contextKey string
|
||||
|
||||
const (
|
||||
loggerKey contextKey = "gitlab.com/inetmock/inetmock/app/context/logger"
|
||||
configKey contextKey = "gitlab.com/inetmock/inetmock/app/context/config"
|
||||
handlerRegistryKey contextKey = "gitlab.com/inetmock/inetmock/app/context/handlerRegistry"
|
||||
healthCheckerKey contextKey = "gitlab.com/inetmock/inetmock/app/context/healthChecker"
|
||||
endpointManagerKey contextKey = "gitlab.com/inetmock/inetmock/app/context/endpointManager"
|
||||
certStoreKey contextKey = "gitlab.com/inetmock/inetmock/app/context/certStore"
|
||||
eventStreamKey contextKey = "gitlab.com/inetmock/inetmock/app/context/eventStream"
|
||||
)
|
||||
|
||||
type App interface {
|
||||
api.PluginContext
|
||||
EventStream() audit.EventStream
|
||||
Config() config.Config
|
||||
Checker() health.Checker
|
||||
EndpointManager() endpoints.EndpointManager
|
||||
EndpointManager() endpoint.EndpointManager
|
||||
HandlerRegistry() api.HandlerRegistry
|
||||
Context() context.Context
|
||||
RootCommand() *cobra.Command
|
||||
MustRun()
|
||||
Shutdown()
|
||||
|
||||
// WithCommands adds subcommands to the root command
|
||||
// requires nothing
|
||||
WithCommands(cmds ...*cobra.Command) App
|
||||
|
||||
// WithHandlerRegistry builds up the handler registry
|
||||
// requires nothing
|
||||
WithHandlerRegistry(registrations ...api.Registration) App
|
||||
|
||||
// WithHealthChecker adds the health checker mechanism
|
||||
// requires nothing
|
||||
WithHealthChecker() App
|
||||
|
||||
// WithLogger configures the logging system
|
||||
// requires nothing
|
||||
WithLogger() App
|
||||
|
||||
// WithEndpointManager creates an endpoint manager instance and adds it to the context
|
||||
// requires WithHandlerRegistry, WithHealthChecker and WithLogger
|
||||
WithEndpointManager() App
|
||||
|
||||
// WithCertStore initializes the cert store
|
||||
// requires WithLogger and WithConfig
|
||||
WithCertStore() App
|
||||
|
||||
// WithEventStream adds the audit event stream
|
||||
// requires WithLogger
|
||||
WithEventStream() App
|
||||
|
||||
// WithConfig loads the config
|
||||
// requires nothing
|
||||
WithConfig() App
|
||||
|
||||
WithInitTasks(task ...func(cmd *cobra.Command, args []string) (err error)) App
|
||||
}
|
||||
|
||||
type app struct {
|
||||
cfg config.Config
|
||||
rootCmd *cobra.Command
|
||||
rootLogger logging.Logger
|
||||
certStore cert.Store
|
||||
checker health.Checker
|
||||
endpointManager endpoints.EndpointManager
|
||||
registry api.HandlerRegistry
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
lateInitTasks []func(cmd *cobra.Command, args []string) (err error)
|
||||
}
|
||||
|
||||
func (a *app) MustRun() {
|
||||
if err := a.rootCmd.Execute(); err != nil {
|
||||
if a.rootLogger != nil {
|
||||
a.rootLogger.Error(
|
||||
if a.Logger() != nil {
|
||||
a.Logger().Error(
|
||||
"Failed to run inetmock",
|
||||
zap.Error(err),
|
||||
)
|
||||
|
@ -62,60 +107,232 @@ func (a *app) MustRun() {
|
|||
}
|
||||
}
|
||||
|
||||
func (a app) Logger() logging.Logger {
|
||||
return a.rootLogger
|
||||
func (a *app) Logger() logging.Logger {
|
||||
val := a.ctx.Value(loggerKey)
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
return val.(logging.Logger)
|
||||
}
|
||||
|
||||
func (a app) Config() config.Config {
|
||||
return a.cfg
|
||||
func (a *app) Config() config.Config {
|
||||
val := a.ctx.Value(configKey)
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
return val.(config.Config)
|
||||
}
|
||||
|
||||
func (a app) CertStore() cert.Store {
|
||||
return a.certStore
|
||||
func (a *app) CertStore() cert.Store {
|
||||
val := a.ctx.Value(certStoreKey)
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
return val.(cert.Store)
|
||||
}
|
||||
|
||||
func (a app) Checker() health.Checker {
|
||||
return a.checker
|
||||
func (a *app) Checker() health.Checker {
|
||||
val := a.ctx.Value(healthCheckerKey)
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
return val.(health.Checker)
|
||||
}
|
||||
|
||||
func (a app) EndpointManager() endpoints.EndpointManager {
|
||||
return a.endpointManager
|
||||
func (a *app) EndpointManager() endpoint.EndpointManager {
|
||||
val := a.ctx.Value(endpointManagerKey)
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
return val.(endpoint.EndpointManager)
|
||||
}
|
||||
|
||||
func (a app) HandlerRegistry() api.HandlerRegistry {
|
||||
return a.registry
|
||||
func (a *app) Audit() audit.Emitter {
|
||||
val := a.ctx.Value(eventStreamKey)
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
return val.(audit.Emitter)
|
||||
}
|
||||
|
||||
func (a app) Context() context.Context {
|
||||
func (a *app) EventStream() audit.EventStream {
|
||||
val := a.ctx.Value(eventStreamKey)
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
return val.(audit.EventStream)
|
||||
}
|
||||
|
||||
func (a *app) HandlerRegistry() api.HandlerRegistry {
|
||||
val := a.ctx.Value(handlerRegistryKey)
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
return val.(api.HandlerRegistry)
|
||||
}
|
||||
|
||||
func (a *app) Context() context.Context {
|
||||
return a.ctx
|
||||
}
|
||||
|
||||
func (a app) Shutdown() {
|
||||
func (a *app) RootCommand() *cobra.Command {
|
||||
return a.rootCmd
|
||||
}
|
||||
|
||||
func (a *app) Shutdown() {
|
||||
a.cancel()
|
||||
}
|
||||
|
||||
// WithCommands adds subcommands to the root command
|
||||
// requires nothing
|
||||
func (a *app) WithCommands(cmds ...*cobra.Command) App {
|
||||
a.rootCmd.AddCommand(cmds...)
|
||||
return a
|
||||
}
|
||||
|
||||
func NewApp(registrations ...api.Registration) (inetmockApp App, err error) {
|
||||
// WithHandlerRegistry builds up the handler registry
|
||||
// requires nothing
|
||||
func (a *app) WithHandlerRegistry(registrations ...api.Registration) App {
|
||||
registry := api.NewHandlerRegistry()
|
||||
|
||||
for _, registration := range registrations {
|
||||
if err = registration(registry); err != nil {
|
||||
if err := registration(registry); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
a.ctx = context.WithValue(a.ctx, handlerRegistryKey, registry)
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// WithHealthChecker adds the health checker mechanism
|
||||
// requires nothing
|
||||
func (a *app) WithHealthChecker() App {
|
||||
checker := health.New()
|
||||
a.ctx = context.WithValue(a.ctx, healthCheckerKey, checker)
|
||||
return a
|
||||
}
|
||||
|
||||
// WithLogger configures the logging system
|
||||
// requires nothing
|
||||
func (a *app) WithLogger() App {
|
||||
a.lateInitTasks = append(a.lateInitTasks, func(cmd *cobra.Command, args []string) (err error) {
|
||||
logging.ConfigureLogging(
|
||||
logging.ParseLevel(logLevel),
|
||||
developmentLogs,
|
||||
map[string]interface{}{
|
||||
"cwd": path.WorkingDirectory(),
|
||||
"cmd": cmd.Name(),
|
||||
"args": args,
|
||||
},
|
||||
)
|
||||
|
||||
var logger logging.Logger
|
||||
if logger, err = logging.CreateLogger(); err != nil {
|
||||
return
|
||||
}
|
||||
a.ctx = context.WithValue(a.ctx, loggerKey, logger)
|
||||
return
|
||||
})
|
||||
return a
|
||||
}
|
||||
|
||||
ctx, cancel := initAppContext()
|
||||
// WithEndpointManager creates an endpoint manager instance and adds it to the context
|
||||
// requires WithHandlerRegistry, WithHealthChecker and WithLogger
|
||||
func (a *app) WithEndpointManager() App {
|
||||
a.lateInitTasks = append(a.lateInitTasks, func(_ *cobra.Command, _ []string) (err error) {
|
||||
epMgr := endpoint.NewEndpointManager(
|
||||
a.HandlerRegistry(),
|
||||
a.Logger().Named("EndpointManager"),
|
||||
a.Checker(),
|
||||
a,
|
||||
)
|
||||
|
||||
a.ctx = context.WithValue(a.ctx, endpointManagerKey, epMgr)
|
||||
return
|
||||
})
|
||||
return a
|
||||
}
|
||||
|
||||
// WithCertStore initializes the cert store
|
||||
// requires WithLogger and WithConfig
|
||||
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.Logger().Named("CertStore"),
|
||||
); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
a.ctx = context.WithValue(a.ctx, certStoreKey, certStore)
|
||||
return
|
||||
})
|
||||
return a
|
||||
}
|
||||
|
||||
// WithEventStream adds the audit event stream
|
||||
// requires WithLogger
|
||||
func (a *app) WithEventStream() App {
|
||||
a.lateInitTasks = append(a.lateInitTasks, func(_ *cobra.Command, _ []string) (err error) {
|
||||
var eventStream audit.EventStream
|
||||
eventStream, err = audit.NewEventStream(
|
||||
a.Logger().Named("EventStream"),
|
||||
audit.WithSinkBufferSize(10),
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = eventStream.RegisterSink(a.ctx, sink.NewLogSink(a.Logger().Named("LogSink"))); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var metricSink audit.Sink
|
||||
if metricSink, err = sink.NewMetricSink(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = eventStream.RegisterSink(a.ctx, metricSink); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
a.ctx = context.WithValue(a.ctx, eventStreamKey, eventStream)
|
||||
return
|
||||
})
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// WithConfig loads the config
|
||||
// requires nothing
|
||||
func (a *app) WithConfig() App {
|
||||
a.lateInitTasks = append(a.lateInitTasks, func(cmd *cobra.Command, _ []string) (err error) {
|
||||
cfg := config.CreateConfig(cmd.Flags())
|
||||
if err = cfg.ReadConfig(configFilePath); err != nil {
|
||||
return
|
||||
}
|
||||
a.ctx = context.WithValue(a.ctx, configKey, cfg)
|
||||
return
|
||||
})
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *app) WithInitTasks(task ...func(cmd *cobra.Command, args []string) (err error)) App {
|
||||
a.lateInitTasks = append(a.lateInitTasks, task...)
|
||||
return a
|
||||
}
|
||||
|
||||
func NewApp(name, short string) App {
|
||||
ctx, cancel := initAppContext()
|
||||
a := &app{
|
||||
rootCmd: &cobra.Command{
|
||||
Short: "INetMock is lightweight internet mock",
|
||||
Use: name,
|
||||
Short: short,
|
||||
},
|
||||
checker: health.New(),
|
||||
registry: registry,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
}
|
||||
|
@ -125,31 +342,13 @@ func NewApp(registrations ...api.Registration) (inetmockApp App, err error) {
|
|||
a.rootCmd.PersistentFlags().BoolVar(&developmentLogs, "development-logs", false, "Enable development mode logs")
|
||||
|
||||
a.rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) (err error) {
|
||||
logging.ConfigureLogging(
|
||||
logging.ParseLevel(logLevel),
|
||||
developmentLogs,
|
||||
map[string]interface{}{
|
||||
"cwd": path.WorkingDirectory(),
|
||||
},
|
||||
)
|
||||
|
||||
if a.rootLogger, err = logging.CreateLogger(); err != nil {
|
||||
for _, initTask := range a.lateInitTasks {
|
||||
err = multierr.Append(err, initTask(cmd, args))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
a.cfg = config.CreateConfig(cmd.Flags())
|
||||
|
||||
if err = a.cfg.ReadConfig(configFilePath); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
a.certStore, err = cert.NewDefaultStore(a.cfg, a.rootLogger)
|
||||
a.endpointManager = endpoints.NewEndpointManager(a.registry, a.Logger().Named("EndpointManager"), a.checker, a)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
return a, nil
|
||||
return a
|
||||
}
|
||||
|
||||
func initAppContext() (context.Context, context.CancelFunc) {
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
cliCmd = &cobra.Command{
|
||||
Use: "",
|
||||
Short: "IMCTL is the CLI app to interact with an INetMock server",
|
||||
}
|
||||
|
||||
inetMockSocketPath string
|
||||
outputFormat string
|
||||
grpcTimeout time.Duration
|
||||
)
|
||||
|
||||
func init() {
|
||||
cliCmd.PersistentFlags().StringVar(&inetMockSocketPath, "socket-path", "./inetmock.sock", "Path to the INetMock socket file")
|
||||
cliCmd.PersistentFlags().StringVarP(&outputFormat, "format", "f", "table", "Output format to use. Possible values: table, json, yaml")
|
||||
cliCmd.PersistentFlags().DurationVar(&grpcTimeout, "grpc-timeout", 5*time.Second, "Timeout to connect to the gRPC API")
|
||||
|
||||
cliCmd.AddCommand(endpointsCmd, handlerCmd, healthCmd)
|
||||
endpointsCmd.AddCommand(getEndpoints)
|
||||
handlerCmd.AddCommand(getHandlersCmd)
|
||||
healthCmd.AddCommand(generalHealthCmd)
|
||||
healthCmd.AddCommand(containerHealthCmd)
|
||||
}
|
||||
|
||||
func ExecuteClientCommand() error {
|
||||
return cliCmd.Execute()
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"gitlab.com/inetmock/inetmock/internal/app"
|
||||
"gitlab.com/inetmock/inetmock/plugins/dns_mock"
|
||||
"gitlab.com/inetmock/inetmock/plugins/http_mock"
|
||||
"gitlab.com/inetmock/inetmock/plugins/http_proxy"
|
||||
"gitlab.com/inetmock/inetmock/plugins/metrics_exporter"
|
||||
"gitlab.com/inetmock/inetmock/plugins/tls_interceptor"
|
||||
)
|
||||
|
||||
var (
|
||||
server app.App
|
||||
)
|
||||
|
||||
func ExecuteServerCommand() {
|
||||
var err error
|
||||
if server, err = app.NewApp(
|
||||
http_mock.AddHTTPMock,
|
||||
dns_mock.AddDNSMock,
|
||||
tls_interceptor.AddTLSInterceptor,
|
||||
http_proxy.AddHTTPProxy,
|
||||
metrics_exporter.AddMetricsExporter,
|
||||
); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
server.
|
||||
WithCommands(serveCmd, generateCaCmd).
|
||||
MustRun()
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package endpoints
|
||||
package endpoint
|
||||
|
||||
import "time"
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
//go:generate mockgen -source=$GOFILE -destination=./../../internal/mock/endpoints/endpoint.mock.go -package=endpoints_mock
|
||||
package endpoints
|
||||
package endpoint
|
||||
|
||||
import (
|
||||
"context"
|
|
@ -1,4 +1,4 @@
|
|||
package endpoints
|
||||
package endpoint
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -99,7 +99,7 @@ func (e *endpointManager) StartEndpoints() {
|
|||
}
|
||||
|
||||
func (e *endpointManager) ShutdownEndpoints() {
|
||||
var waitGroup sync.WaitGroup
|
||||
waitGroup := new(sync.WaitGroup)
|
||||
waitGroup.Add(len(e.properlyStartedEndpoints))
|
||||
|
||||
parentCtx, _ := context.WithTimeout(context.Background(), shutdownTimeout)
|
||||
|
@ -112,7 +112,7 @@ func (e *endpointManager) ShutdownEndpoints() {
|
|||
zap.String("endpoint", endpoint.Name()),
|
||||
)
|
||||
endpointLogger.Info("Triggering shutdown of endpoint")
|
||||
go shutdownEndpoint(ctx, endpoint, endpointLogger, &waitGroup)
|
||||
go shutdownEndpoint(ctx, endpoint, endpointLogger, waitGroup)
|
||||
}
|
||||
|
||||
waitGroup.Wait()
|
||||
|
@ -149,8 +149,6 @@ func startEndpoint(ep Endpoint, ctx api.PluginContext, logger logging.Logger) (s
|
|||
success = false
|
||||
}
|
||||
|
||||
close(startSuccessful)
|
||||
|
||||
return
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package endpoints
|
||||
package endpoint
|
||||
|
||||
import (
|
||||
"reflect"
|
|
@ -1,4 +1,4 @@
|
|||
package dns_mock
|
||||
package mock
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
|
@ -1,4 +1,4 @@
|
|||
package dns_mock
|
||||
package mock
|
||||
|
||||
import (
|
||||
"net"
|
|
@ -1,4 +1,4 @@
|
|||
package dns_mock
|
||||
package mock
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -15,14 +15,14 @@ type dnsHandler struct {
|
|||
dnsServer []*dns.Server
|
||||
}
|
||||
|
||||
func (d *dnsHandler) Start(_ api.PluginContext, config config.HandlerConfig) (err error) {
|
||||
func (d *dnsHandler) Start(pluginCtx api.PluginContext, config config.HandlerConfig) (err error) {
|
||||
var options dnsOptions
|
||||
if options, err = loadFromConfig(config.Options); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
listenAddr := config.ListenAddr()
|
||||
d.logger = d.logger.With(
|
||||
d.logger = pluginCtx.Logger().With(
|
||||
zap.String("handler_name", config.HandlerName),
|
||||
zap.String("address", listenAddr),
|
||||
)
|
||||
|
@ -30,7 +30,8 @@ func (d *dnsHandler) Start(_ api.PluginContext, config config.HandlerConfig) (er
|
|||
handler := ®exHandler{
|
||||
handlerName: config.HandlerName,
|
||||
fallback: options.Fallback,
|
||||
logger: d.logger,
|
||||
logger: pluginCtx.Logger(),
|
||||
auditEmitter: pluginCtx.Audit(),
|
||||
}
|
||||
|
||||
for _, rule := range options.Rules {
|
|
@ -1,4 +1,4 @@
|
|||
package dns_mock
|
||||
package mock
|
||||
|
||||
import (
|
||||
"net"
|
139
internal/endpoint/handler/dns/mock/regex_handler.go
Normal file
139
internal/endpoint/handler/dns/mock/regex_handler.go
Normal file
|
@ -0,0 +1,139 @@
|
|||
package mock
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit/details"
|
||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type regexHandler struct {
|
||||
handlerName string
|
||||
routes []resolverRule
|
||||
fallback ResolverFallback
|
||||
auditEmitter audit.Emitter
|
||||
logger logging.Logger
|
||||
}
|
||||
|
||||
func (rh *regexHandler) AddRule(rule resolverRule) {
|
||||
rh.routes = append(rh.routes, rule)
|
||||
}
|
||||
|
||||
func (rh *regexHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
|
||||
timer := prometheus.NewTimer(requestDurationHistogram.WithLabelValues(rh.handlerName))
|
||||
defer func() {
|
||||
timer.ObserveDuration()
|
||||
}()
|
||||
|
||||
rh.recordRequest(r, w.LocalAddr(), w.RemoteAddr())
|
||||
|
||||
m := new(dns.Msg)
|
||||
m.Compress = false
|
||||
m.SetReply(r)
|
||||
|
||||
if r.Opcode == dns.OpcodeQuery {
|
||||
rh.handleQuery(m)
|
||||
}
|
||||
if err := w.WriteMsg(m); err != nil {
|
||||
rh.logger.Error(
|
||||
"Failed to write DNS response message",
|
||||
zap.Error(err),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (rh *regexHandler) handleQuery(m *dns.Msg) {
|
||||
for _, q := range m.Question {
|
||||
switch q.Qtype {
|
||||
case dns.TypeA:
|
||||
totalHandledRequestsCounter.WithLabelValues(rh.handlerName).Inc()
|
||||
for _, rule := range rh.routes {
|
||||
if !rule.pattern.MatchString(q.Name) {
|
||||
continue
|
||||
}
|
||||
m.Authoritative = true
|
||||
answer := &dns.A{
|
||||
Hdr: dns.RR_Header{
|
||||
Name: q.Name,
|
||||
Rrtype: dns.TypeA,
|
||||
Class: dns.ClassINET,
|
||||
Ttl: 60,
|
||||
},
|
||||
A: rule.response,
|
||||
}
|
||||
m.Answer = append(m.Answer, answer)
|
||||
rh.logger.Info(
|
||||
"matched DNS rule",
|
||||
zap.String("pattern", rule.pattern.String()),
|
||||
zap.String("response", rule.response.String()),
|
||||
)
|
||||
return
|
||||
}
|
||||
rh.handleFallbackForMessage(m, q)
|
||||
default:
|
||||
unhandledRequestsCounter.WithLabelValues(rh.handlerName).Inc()
|
||||
rh.logger.Warn(
|
||||
"Unhandled DNS question type - no response will be sent",
|
||||
zap.Uint16("question_type", q.Qtype),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (rh *regexHandler) handleFallbackForMessage(m *dns.Msg, q dns.Question) {
|
||||
fallbackIP := rh.fallback.GetIP()
|
||||
answer := &dns.A{
|
||||
Hdr: dns.RR_Header{
|
||||
Name: q.Name,
|
||||
Rrtype: dns.TypeA,
|
||||
Class: dns.ClassINET,
|
||||
Ttl: 60,
|
||||
},
|
||||
A: fallbackIP,
|
||||
}
|
||||
rh.logger.Info(
|
||||
"Falling back to generated IP",
|
||||
zap.String("response", fallbackIP.String()),
|
||||
)
|
||||
m.Authoritative = true
|
||||
m.Answer = append(m.Answer, answer)
|
||||
}
|
||||
|
||||
func (rh *regexHandler) recordRequest(m *dns.Msg, localAddr, remoteAddr net.Addr) {
|
||||
dnsDetails := &details.DNS{
|
||||
OPCode: details.DNSOpCode(m.Opcode),
|
||||
}
|
||||
|
||||
for _, q := range m.Question {
|
||||
dnsDetails.Questions = append(dnsDetails.Questions, details.DNSQuestion{
|
||||
RRType: details.ResourceRecordType(q.Qtype),
|
||||
Name: q.Name,
|
||||
})
|
||||
}
|
||||
|
||||
ev := audit.Event{
|
||||
Transport: guessTransportFromAddr(localAddr),
|
||||
Application: audit.AppProtocol_DNS,
|
||||
ProtocolDetails: dnsDetails,
|
||||
}
|
||||
|
||||
ev.SetSourceIPFromAddr(remoteAddr)
|
||||
ev.SetDestinationIPFromAddr(localAddr)
|
||||
|
||||
rh.auditEmitter.Emit(ev)
|
||||
}
|
||||
|
||||
func guessTransportFromAddr(addr net.Addr) audit.TransportProtocol {
|
||||
switch addr.(type) {
|
||||
case *net.TCPAddr:
|
||||
return audit.TransportProtocol_TCP
|
||||
case *net.UDPAddr:
|
||||
return audit.TransportProtocol_UDP
|
||||
default:
|
||||
return audit.TransportProtocol_UNKNOWN_TRANSPORT
|
||||
}
|
||||
}
|
|
@ -1,11 +1,9 @@
|
|||
package dns_mock
|
||||
package mock
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"gitlab.com/inetmock/inetmock/pkg/api"
|
||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
"gitlab.com/inetmock/inetmock/pkg/metrics"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -20,14 +18,6 @@ var (
|
|||
)
|
||||
|
||||
func AddDNSMock(registry api.HandlerRegistry) (err error) {
|
||||
var logger logging.Logger
|
||||
if logger, err = logging.CreateLogger(); err != nil {
|
||||
return
|
||||
}
|
||||
logger = logger.With(
|
||||
zap.String("protocol_handler", name),
|
||||
)
|
||||
|
||||
if totalHandledRequestsCounter, err = metrics.Counter(
|
||||
name,
|
||||
"handled_requests_total",
|
||||
|
@ -57,9 +47,7 @@ func AddDNSMock(registry api.HandlerRegistry) (err error) {
|
|||
}
|
||||
|
||||
registry.RegisterHandler(name, func() api.ProtocolHandler {
|
||||
return &dnsHandler{
|
||||
logger: logger,
|
||||
}
|
||||
return &dnsHandler{}
|
||||
})
|
||||
|
||||
return
|
38
internal/endpoint/handler/http/audit.go
Normal file
38
internal/endpoint/handler/http/audit.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit/details"
|
||||
)
|
||||
|
||||
func EventFromRequest(request *http.Request, app audit.AppProtocol) audit.Event {
|
||||
httpDetails := details.HTTP{
|
||||
Method: request.Method,
|
||||
Host: request.Host,
|
||||
URI: request.RequestURI,
|
||||
Proto: request.Proto,
|
||||
Headers: request.Header,
|
||||
}
|
||||
|
||||
ev := audit.Event{
|
||||
Transport: audit.TransportProtocol_TCP,
|
||||
Application: app,
|
||||
ProtocolDetails: httpDetails,
|
||||
}
|
||||
|
||||
if request.TLS != nil {
|
||||
ev.TLS = &audit.TLSDetails{
|
||||
Version: audit.TLSVersionToEntity(request.TLS.Version).String(),
|
||||
CipherSuite: tls.CipherSuiteName(request.TLS.CipherSuite),
|
||||
ServerName: request.TLS.ServerName,
|
||||
}
|
||||
}
|
||||
|
||||
ev.SetDestinationIPFromAddr(localAddr(request.Context()))
|
||||
ev.SetSourceIPFromAddr(remoteAddr(request.Context()))
|
||||
|
||||
return ev
|
||||
}
|
35
internal/endpoint/handler/http/conn_context.go
Normal file
35
internal/endpoint/handler/http/conn_context.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
)
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
func StoreConnPropertiesInContext(ctx context.Context, c net.Conn) context.Context {
|
||||
ctx = context.WithValue(ctx, remoteAddrKey, c.RemoteAddr())
|
||||
ctx = context.WithValue(ctx, localAddrKey, c.LocalAddr())
|
||||
return ctx
|
||||
}
|
||||
|
||||
func localAddr(ctx context.Context) net.Addr {
|
||||
val := ctx.Value(localAddrKey)
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
return val.(net.Addr)
|
||||
}
|
||||
|
||||
func remoteAddr(ctx context.Context) net.Addr {
|
||||
val := ctx.Value(remoteAddrKey)
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
return val.(net.Addr)
|
||||
}
|
|
@ -1,11 +1,13 @@
|
|||
package http_mock
|
||||
package mock
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
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"
|
||||
|
@ -40,15 +42,25 @@ func (p *httpHandler) Start(ctx api.PluginContext, config config.HandlerConfig)
|
|||
|
||||
router := &RegexpHandler{
|
||||
logger: p.logger,
|
||||
emitter: ctx.Audit(),
|
||||
handlerName: config.HandlerName,
|
||||
}
|
||||
p.server = &http.Server{Addr: config.ListenAddr(), Handler: router}
|
||||
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()
|
||||
go p.startServer(options.TLS)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -67,8 +79,17 @@ func (p *httpHandler) Shutdown(ctx context.Context) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func (p *httpHandler) startServer() {
|
||||
if err := p.server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
func (p *httpHandler) startServer(tls bool) {
|
||||
var listen func() error
|
||||
if tls {
|
||||
listen = func() error {
|
||||
return p.server.ListenAndServeTLS("", "")
|
||||
}
|
||||
} else {
|
||||
listen = p.server.ListenAndServe
|
||||
}
|
||||
|
||||
if err := listen(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
p.logger.Error(
|
||||
"failed to start http listener",
|
||||
zap.Error(err),
|
|
@ -1,4 +1,4 @@
|
|||
package http_mock_test
|
||||
package mock_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -13,11 +13,12 @@ import (
|
|||
|
||||
"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"
|
||||
"gitlab.com/inetmock/inetmock/plugins/http_mock"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
@ -64,7 +65,7 @@ func setupHandler(b *testing.B, ctrl *gomock.Controller, listenPort uint16) (api
|
|||
b.Helper()
|
||||
|
||||
registry := api.NewHandlerRegistry()
|
||||
if err := http_mock.AddHTTPMock(registry); err != nil {
|
||||
if err := mock.AddHTTPMock(registry); err != nil {
|
||||
b.Errorf("AddHTTPMock() error = %v", err)
|
||||
}
|
||||
handler, ok := registry.HandlerForName("http_mock")
|
||||
|
@ -72,11 +73,18 @@ func setupHandler(b *testing.B, ctrl *gomock.Controller, listenPort uint16) (api
|
|||
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{
|
||||
{
|
|
@ -1,5 +1,5 @@
|
|||
//go:generate go-enum -f $GOFILE --lower --marshal --names
|
||||
package http_mock
|
||||
package mock
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
@ -50,6 +50,7 @@ func (tr targetRule) Response() string {
|
|||
}
|
||||
|
||||
type httpOptions struct {
|
||||
TLS bool
|
||||
Rules []targetRule
|
||||
}
|
||||
|
||||
|
@ -62,6 +63,7 @@ func loadFromConfig(config *viper.Viper) (options httpOptions, err error) {
|
|||
}
|
||||
|
||||
tmpRules := struct {
|
||||
TLS bool
|
||||
Rules []tmpCfg
|
||||
}{}
|
||||
|
||||
|
@ -69,6 +71,8 @@ func loadFromConfig(config *viper.Viper) (options httpOptions, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
options.TLS = tmpRules.TLS
|
||||
|
||||
for _, i := range tmpRules.Rules {
|
||||
var rulePattern *regexp.Regexp
|
||||
var matchTargetValue RequestMatchTarget
|
||||
|
@ -82,7 +86,7 @@ func loadFromConfig(config *viper.Viper) (options httpOptions, err error) {
|
|||
}
|
||||
|
||||
if absoluteResponsePath, parseErr = filepath.Abs(i.Response); parseErr != nil {
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
options.Rules = append(options.Rules, targetRule{
|
|
@ -1,4 +1,4 @@
|
|||
package http_mock
|
||||
package mock
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
@ -95,6 +95,34 @@ rules:
|
|||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
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
|
||||
`,
|
||||
},
|
||||
wantOptions: httpOptions{
|
||||
TLS: true,
|
||||
Rules: []targetRule{
|
||||
{
|
||||
pattern: regexp.MustCompile("^application/octet-stream$"),
|
||||
response: func() string {
|
||||
p, _ := filepath.Abs("./assets/fakeFiles/sample.exe")
|
||||
return p
|
||||
}(),
|
||||
requestMatchTarget: RequestMatchTargetHeader,
|
||||
targetKey: "Content-Type",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
|
@ -1,11 +1,12 @@
|
|||
package http_mock
|
||||
package mock
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
imHttp "gitlab.com/inetmock/inetmock/internal/endpoint/handler/http"
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
@ -19,16 +20,13 @@ type RegexpHandler struct {
|
|||
handlerName string
|
||||
logger logging.Logger
|
||||
routes []*route
|
||||
emitter audit.Emitter
|
||||
}
|
||||
|
||||
func (h *RegexpHandler) Handler(rule targetRule, handler http.Handler) {
|
||||
h.routes = append(h.routes, &route{rule, handler})
|
||||
}
|
||||
|
||||
func (h *RegexpHandler) HandleFunc(rule targetRule, handler func(http.ResponseWriter, *http.Request)) {
|
||||
h.routes = append(h.routes, &route{rule, http.HandlerFunc(handler)})
|
||||
}
|
||||
|
||||
func (h *RegexpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
timer := prometheus.NewTimer(requestDurationHistogram.WithLabelValues(h.handlerName))
|
||||
defer timer.ObserveDuration()
|
||||
|
@ -53,25 +51,18 @@ func (h *RegexpHandler) setupRoute(rule targetRule) {
|
|||
zap.String("response", rule.Response()),
|
||||
)
|
||||
|
||||
h.Handler(rule, createHandlerForTarget(h.logger, rule.response))
|
||||
}
|
||||
|
||||
func createHandlerForTarget(logger logging.Logger, targetPath string) http.Handler {
|
||||
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||||
headerWriter := &bytes.Buffer{}
|
||||
request.Header.Write(headerWriter)
|
||||
|
||||
logger.Info(
|
||||
"Handling request",
|
||||
zap.String("source", request.RemoteAddr),
|
||||
zap.String("host", request.Host),
|
||||
zap.String("method", request.Method),
|
||||
zap.String("protocol", request.Proto),
|
||||
zap.String("path", request.RequestURI),
|
||||
zap.String("response", targetPath),
|
||||
zap.Reflect("headers", request.Header),
|
||||
)
|
||||
|
||||
http.ServeFile(writer, request, targetPath)
|
||||
h.Handler(rule, emittingFileHandler{
|
||||
emitter: h.emitter,
|
||||
targetPath: rule.response,
|
||||
})
|
||||
}
|
||||
|
||||
type emittingFileHandler struct {
|
||||
emitter audit.Emitter
|
||||
targetPath string
|
||||
}
|
||||
|
||||
func (f emittingFileHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||
f.emitter.Emit(imHttp.EventFromRequest(request, audit.AppProtocol_HTTP))
|
||||
http.ServeFile(writer, request, f.targetPath)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package http_mock
|
||||
package mock
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
|
@ -1,4 +1,4 @@
|
|||
package http_proxy
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -6,6 +6,7 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
|
||||
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"
|
||||
|
@ -29,7 +30,11 @@ func (h *httpProxy) Start(ctx api.PluginContext, cfg config.HandlerConfig) (err
|
|||
return
|
||||
}
|
||||
listenAddr := cfg.ListenAddr()
|
||||
h.server = &http.Server{Addr: listenAddr, Handler: h.proxy}
|
||||
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),
|
||||
|
@ -41,16 +46,18 @@ func (h *httpProxy) Start(ctx api.PluginContext, cfg config.HandlerConfig) (err
|
|||
handlerName: cfg.HandlerName,
|
||||
options: opts,
|
||||
logger: h.logger,
|
||||
emitter: ctx.Audit(),
|
||||
}
|
||||
|
||||
proxyHttpsHandler := &proxyHttpsHandler{
|
||||
proxyHTTPSHandler := &proxyHttpsHandler{
|
||||
handlerName: cfg.HandlerName,
|
||||
tlsConfig: tlsConfig,
|
||||
logger: h.logger,
|
||||
emitter: ctx.Audit(),
|
||||
}
|
||||
|
||||
h.proxy.OnRequest().Do(proxyHandler)
|
||||
h.proxy.OnRequest().HandleConnect(proxyHttpsHandler)
|
||||
h.proxy.OnRequest().HandleConnect(proxyHTTPSHandler)
|
||||
go h.startProxy()
|
||||
return
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package http_proxy
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -1,12 +1,13 @@
|
|||
package http_proxy
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
imHttp "gitlab.com/inetmock/inetmock/internal/endpoint/handler/http"
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
"go.uber.org/zap"
|
||||
"gopkg.in/elazarl/goproxy.v1"
|
||||
|
@ -16,12 +17,14 @@ type proxyHttpHandler struct {
|
|||
handlerName string
|
||||
options httpProxyOptions
|
||||
logger logging.Logger
|
||||
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) {
|
||||
|
@ -45,15 +48,7 @@ func (p *proxyHttpHandler) Handle(req *http.Request, ctx *goproxy.ProxyCtx) (ret
|
|||
totalRequestCounter.WithLabelValues(p.handlerName).Inc()
|
||||
|
||||
retReq = req
|
||||
p.logger.Info(
|
||||
"Handling request",
|
||||
zap.String("source", req.RemoteAddr),
|
||||
zap.String("host", req.Host),
|
||||
zap.String("method", req.Method),
|
||||
zap.String("protocol", req.Proto),
|
||||
zap.String("path", req.RequestURI),
|
||||
zap.Reflect("headers", req.Header),
|
||||
)
|
||||
p.emitter.Emit(imHttp.EventFromRequest(req, audit.AppProtocol_HTTP_PROXY))
|
||||
|
||||
var err error
|
||||
if resp, err = ctx.RoundTrip(p.redirectHTTPRequest(req)); err != nil {
|
||||
|
@ -95,7 +90,7 @@ func (p proxyHttpHandler) redirectHTTPRequest(originalRequest *http.Request) (re
|
|||
MultipartForm: originalRequest.MultipartForm,
|
||||
Trailer: originalRequest.Trailer,
|
||||
}
|
||||
redirectReq = redirectReq.WithContext(context.Background())
|
||||
redirectReq = redirectReq.WithContext(originalRequest.Context())
|
||||
|
||||
return
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package http_proxy
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
|
@ -1,4 +1,4 @@
|
|||
package metrics_exporter
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"context"
|
|
@ -1,4 +1,4 @@
|
|||
package metrics_exporter
|
||||
package metrics
|
||||
|
||||
type metricsExporterOptions struct {
|
||||
Route string
|
|
@ -1,4 +1,4 @@
|
|||
package metrics_exporter
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"gitlab.com/inetmock/inetmock/pkg/api"
|
|
@ -1,4 +1,4 @@
|
|||
package tls_interceptor
|
||||
package interceptor
|
||||
|
||||
import (
|
||||
"context"
|
|
@ -1,4 +1,4 @@
|
|||
package tls_interceptor
|
||||
package interceptor
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -1,4 +1,4 @@
|
|||
package tls_interceptor
|
||||
package interceptor
|
||||
|
||||
import (
|
||||
"net"
|
|
@ -1,4 +1,4 @@
|
|||
package tls_interceptor
|
||||
package interceptor
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -1,4 +1,4 @@
|
|||
package tls_interceptor
|
||||
package interceptor
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
@ -40,7 +40,7 @@ func AddTLSInterceptor(registry api.HandlerRegistry) (err error) {
|
|||
registry.RegisterHandler(name, func() api.ProtocolHandler {
|
||||
return &tlsInterceptor{
|
||||
logger: logger,
|
||||
currentConnectionsCount: &sync.WaitGroup{},
|
||||
currentConnectionsCount: new(sync.WaitGroup),
|
||||
currentConnections: make(map[uuid.UUID]*proxyConn),
|
||||
connectionsMutex: &sync.Mutex{},
|
||||
}
|
|
@ -75,6 +75,15 @@ func (t *tblWriter) getData(val reflect.Value, numberOfFields int) (data []strin
|
|||
}
|
||||
|
||||
func value(val reflect.Value) string {
|
||||
|
||||
if val.IsZero() {
|
||||
return ""
|
||||
}
|
||||
|
||||
if stringer, isStringer := val.Interface().(fmt.Stringer); isStringer {
|
||||
return stringer.String()
|
||||
}
|
||||
|
||||
switch val.Kind() {
|
||||
case reflect.Ptr:
|
||||
return value(val.Elem())
|
||||
|
@ -84,6 +93,8 @@ func value(val reflect.Value) string {
|
|||
return strconv.FormatBool(val.Bool())
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return strconv.FormatInt(val.Int(), 10)
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return strconv.FormatUint(val.Uint(), 10)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return strconv.FormatFloat(val.Float(), 'f', 6, 64)
|
||||
default:
|
||||
|
|
74
internal/rpc/audit_server.go
Normal file
74
internal/rpc/audit_server.go
Normal file
|
@ -0,0 +1,74 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit/sink"
|
||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type auditServer struct {
|
||||
UnimplementedAuditServer
|
||||
logger logging.Logger
|
||||
eventStream audit.EventStream
|
||||
}
|
||||
|
||||
func (a *auditServer) ListSinks(context.Context, *ListSinksRequest) (*ListSinksResponse, error) {
|
||||
return &ListSinksResponse{
|
||||
Sinks: a.eventStream.Sinks(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *auditServer) WatchEvents(req *WatchEventsRequest, srv Audit_WatchEventsServer) (err error) {
|
||||
a.logger.Info("watcher attached", zap.String("name", req.WatcherName))
|
||||
err = a.eventStream.RegisterSink(srv.Context(), sink.NewGenericSink(req.WatcherName, func(ev audit.Event) {
|
||||
if err = srv.Send(ev.ProtoMessage()); err != nil {
|
||||
return
|
||||
}
|
||||
}))
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
<-srv.Context().Done()
|
||||
a.logger.Info("Watcher detached", zap.String("name", req.WatcherName))
|
||||
return
|
||||
}
|
||||
|
||||
func (a *auditServer) RegisterFileSink(_ context.Context, req *RegisterFileSinkRequest) (resp *RegisterFileSinkResponse, err error) {
|
||||
var writer io.WriteCloser
|
||||
var flags int
|
||||
|
||||
switch req.OpenMode {
|
||||
case FileOpenMode_APPEND:
|
||||
flags = os.O_CREATE | os.O_WRONLY | os.O_APPEND
|
||||
default:
|
||||
flags = os.O_CREATE | os.O_WRONLY | os.O_TRUNC
|
||||
}
|
||||
|
||||
var permissions = os.FileMode(req.Permissions)
|
||||
if permissions == 0 {
|
||||
permissions = 644
|
||||
}
|
||||
|
||||
if writer, err = os.OpenFile(req.TargetPath, flags, permissions); err != nil {
|
||||
return
|
||||
}
|
||||
if err = a.eventStream.RegisterSink(context.Background(), sink.NewWriterSink(req.TargetPath, audit.NewEventWriter(writer))); err != nil {
|
||||
return
|
||||
}
|
||||
resp = &RegisterFileSinkResponse{}
|
||||
return
|
||||
}
|
||||
|
||||
func (a *auditServer) RemoveFileSink(_ context.Context, req *RemoveFileSinkRequest) (*RemoveFileSinkResponse, error) {
|
||||
gotRemoved := a.eventStream.RemoveSink(req.TargetPath)
|
||||
return &RemoveFileSinkResponse{
|
||||
SinkGotRemoved: gotRemoved,
|
||||
}, nil
|
||||
}
|
|
@ -3,12 +3,12 @@ package rpc
|
|||
import (
|
||||
"context"
|
||||
|
||||
"gitlab.com/inetmock/inetmock/internal/endpoints"
|
||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
||||
)
|
||||
|
||||
type endpointsServer struct {
|
||||
UnimplementedEndpointsServer
|
||||
endpointsManager endpoints.EndpointManager
|
||||
endpointsManager endpoint.EndpointManager
|
||||
}
|
||||
|
||||
func (e endpointsServer) GetEndpoints(_ context.Context, _ *GetEndpointsRequest) (*GetEndpointsResponse, error) {
|
||||
|
@ -18,7 +18,7 @@ func (e endpointsServer) GetEndpoints(_ context.Context, _ *GetEndpointsRequest)
|
|||
}, nil
|
||||
}
|
||||
|
||||
func rpcEndpointsFromEndpoints(eps []endpoints.Endpoint) *[]*Endpoint {
|
||||
func rpcEndpointsFromEndpoints(eps []endpoint.Endpoint) *[]*Endpoint {
|
||||
out := make([]*Endpoint, 0)
|
||||
for _, ep := range eps {
|
||||
out = append(out, rpcEndpointFromEndpoint(ep))
|
||||
|
@ -26,7 +26,7 @@ func rpcEndpointsFromEndpoints(eps []endpoints.Endpoint) *[]*Endpoint {
|
|||
return &out
|
||||
}
|
||||
|
||||
func rpcEndpointFromEndpoint(ep endpoints.Endpoint) *Endpoint {
|
||||
func rpcEndpointFromEndpoint(ep endpoint.Endpoint) *Endpoint {
|
||||
return &Endpoint{
|
||||
Id: ep.Id().String(),
|
||||
Name: ep.Name(),
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/reflection"
|
||||
)
|
||||
|
||||
type INetMockAPI interface {
|
||||
|
@ -52,6 +53,13 @@ func (i *inetmockAPI) StartServer() (err error) {
|
|||
app: i.app,
|
||||
})
|
||||
|
||||
RegisterAuditServer(i.server, &auditServer{
|
||||
logger: i.app.Logger(),
|
||||
eventStream: i.app.EventStream(),
|
||||
})
|
||||
|
||||
reflection.Register(i.server)
|
||||
|
||||
go i.startServerAsync(lis)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -11,9 +11,6 @@ type handlersServer struct {
|
|||
registry api.HandlerRegistry
|
||||
}
|
||||
|
||||
func (h *handlersServer) mustEmbedUnimplementedHandlersServer() {
|
||||
}
|
||||
|
||||
func (h *handlersServer) GetHandlers(_ context.Context, _ *GetHandlersRequest) (*GetHandlersResponse, error) {
|
||||
return &GetHandlersResponse{
|
||||
Handlers: h.registry.AvailableHandlers(),
|
||||
|
|
|
@ -3,12 +3,12 @@ package rpc
|
|||
import (
|
||||
"context"
|
||||
|
||||
app2 "gitlab.com/inetmock/inetmock/internal/app"
|
||||
"gitlab.com/inetmock/inetmock/internal/app"
|
||||
)
|
||||
|
||||
type healthServer struct {
|
||||
UnimplementedHealthServer
|
||||
app app2.App
|
||||
app app.App
|
||||
}
|
||||
|
||||
func (h healthServer) GetHealth(_ context.Context, _ *HealthRequest) (resp *HealthResponse, err error) {
|
||||
|
|
|
@ -4,6 +4,7 @@ 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"
|
||||
|
@ -12,6 +13,7 @@ import (
|
|||
type PluginContext interface {
|
||||
Logger() logging.Logger
|
||||
CertStore() cert.Store
|
||||
Audit() audit.Emitter
|
||||
}
|
||||
|
||||
type ProtocolHandler interface {
|
||||
|
|
30
pkg/audit/api.go
Normal file
30
pkg/audit/api.go
Normal file
|
@ -0,0 +1,30 @@
|
|||
//go:generate mockgen -source=$GOFILE -destination=./../../internal/mock/audit/audit.mock.go -package=audit_mock
|
||||
|
||||
package audit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrSinkAlreadyRegistered = errors.New("sink with same name already registered")
|
||||
)
|
||||
|
||||
type Emitter interface {
|
||||
Emit(ev Event)
|
||||
}
|
||||
|
||||
type Sink interface {
|
||||
Name() string
|
||||
OnSubscribe(evs <-chan Event)
|
||||
}
|
||||
|
||||
type EventStream interface {
|
||||
io.Closer
|
||||
Emitter
|
||||
RegisterSink(ctx context.Context, s Sink) error
|
||||
Sinks() []string
|
||||
RemoveSink(name string) (exists bool)
|
||||
}
|
47
pkg/audit/details/dns_details.go
Normal file
47
pkg/audit/details/dns_details.go
Normal file
|
@ -0,0 +1,47 @@
|
|||
package details
|
||||
|
||||
import (
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
)
|
||||
|
||||
func NewDNSFromWireFormat(entity *DNSDetailsEntity) DNS {
|
||||
d := DNS{
|
||||
OPCode: entity.Opcode,
|
||||
}
|
||||
|
||||
for _, q := range entity.Questions {
|
||||
d.Questions = append(d.Questions, DNSQuestion{
|
||||
RRType: q.Type,
|
||||
Name: q.Name,
|
||||
})
|
||||
}
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
type DNSQuestion struct {
|
||||
RRType ResourceRecordType
|
||||
Name string
|
||||
}
|
||||
|
||||
type DNS struct {
|
||||
OPCode DNSOpCode
|
||||
Questions []DNSQuestion
|
||||
}
|
||||
|
||||
func (d DNS) MarshalToWireFormat() (any *anypb.Any, err error) {
|
||||
detailsEntity := &DNSDetailsEntity{
|
||||
Opcode: d.OPCode,
|
||||
}
|
||||
|
||||
for _, q := range d.Questions {
|
||||
detailsEntity.Questions = append(detailsEntity.Questions, &DNSQuestionEntity{
|
||||
Type: q.RRType,
|
||||
Name: q.Name,
|
||||
})
|
||||
}
|
||||
|
||||
any, err = anypb.New(detailsEntity)
|
||||
|
||||
return
|
||||
}
|
59
pkg/audit/details/http_details.go
Normal file
59
pkg/audit/details/http_details.go
Normal file
|
@ -0,0 +1,59 @@
|
|||
package details
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
)
|
||||
|
||||
type HTTP struct {
|
||||
Method string
|
||||
Host string
|
||||
URI string
|
||||
Proto string
|
||||
Headers http.Header
|
||||
}
|
||||
|
||||
func NewHTTPFromWireFormat(entity *HTTPDetailsEntity) HTTP {
|
||||
headers := http.Header{}
|
||||
for name, values := range entity.Headers {
|
||||
for idx := range values.Values {
|
||||
headers.Add(name, values.Values[idx])
|
||||
}
|
||||
}
|
||||
|
||||
return HTTP{
|
||||
Method: entity.Method.String(),
|
||||
Host: entity.Host,
|
||||
URI: entity.Uri,
|
||||
Proto: entity.Proto,
|
||||
Headers: headers,
|
||||
}
|
||||
}
|
||||
|
||||
func (d HTTP) MarshalToWireFormat() (any *anypb.Any, err error) {
|
||||
var method = HTTPMethod_GET
|
||||
if methodValue, known := HTTPMethod_value[strings.ToUpper(d.Method)]; known {
|
||||
method = HTTPMethod(methodValue)
|
||||
}
|
||||
|
||||
headers := make(map[string]*HTTPHeaderValue)
|
||||
|
||||
for k, v := range d.Headers {
|
||||
headers[k] = &HTTPHeaderValue{
|
||||
Values: v,
|
||||
}
|
||||
}
|
||||
|
||||
protoDetails := &HTTPDetailsEntity{
|
||||
Method: method,
|
||||
Host: d.Host,
|
||||
Uri: d.URI,
|
||||
Proto: d.Proto,
|
||||
Headers: headers,
|
||||
}
|
||||
|
||||
any, err = anypb.New(protoDetails)
|
||||
return
|
||||
}
|
134
pkg/audit/event.go
Normal file
134
pkg/audit/event.go
Normal file
|
@ -0,0 +1,134 @@
|
|||
package audit
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit/details"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
type Details interface {
|
||||
MarshalToWireFormat() (*anypb.Any, error)
|
||||
}
|
||||
|
||||
type Event struct {
|
||||
ID int64
|
||||
Timestamp time.Time
|
||||
Transport TransportProtocol
|
||||
Application AppProtocol
|
||||
SourceIP net.IP
|
||||
DestinationIP net.IP
|
||||
SourcePort uint16
|
||||
DestinationPort uint16
|
||||
ProtocolDetails Details
|
||||
TLS *TLSDetails
|
||||
}
|
||||
|
||||
func (e *Event) ProtoMessage() *EventEntity {
|
||||
var tlsDetails *TLSDetailsEntity = nil
|
||||
if e.TLS != nil {
|
||||
tlsDetails = e.TLS.ProtoMessage()
|
||||
}
|
||||
|
||||
var detailsEntity *anypb.Any = nil
|
||||
if e.ProtocolDetails != nil {
|
||||
if any, err := e.ProtocolDetails.MarshalToWireFormat(); err == nil {
|
||||
detailsEntity = any
|
||||
}
|
||||
}
|
||||
|
||||
return &EventEntity{
|
||||
Id: e.ID,
|
||||
Timestamp: timestamppb.New(e.Timestamp),
|
||||
Transport: e.Transport,
|
||||
Application: e.Application,
|
||||
SourceIP: e.SourceIP,
|
||||
DestinationIP: e.DestinationIP,
|
||||
SourcePort: uint32(e.SourcePort),
|
||||
DestinationPort: uint32(e.DestinationPort),
|
||||
Tls: tlsDetails,
|
||||
ProtocolDetails: detailsEntity,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Event) ApplyDefaults(id int64) {
|
||||
e.ID = id
|
||||
emptyTime := time.Time{}
|
||||
if e.Timestamp == emptyTime {
|
||||
e.Timestamp = time.Now().UTC()
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Event) SetSourceIPFromAddr(remoteAddr net.Addr) {
|
||||
ip, port := parseIPPortFromAddr(remoteAddr)
|
||||
e.SourceIP = ip
|
||||
e.SourcePort = port
|
||||
}
|
||||
|
||||
func (e *Event) SetDestinationIPFromAddr(localAddr net.Addr) {
|
||||
ip, port := parseIPPortFromAddr(localAddr)
|
||||
e.DestinationIP = ip
|
||||
e.DestinationPort = port
|
||||
}
|
||||
|
||||
func NewEventFromProto(msg *EventEntity) (ev Event) {
|
||||
ev = Event{
|
||||
ID: msg.GetId(),
|
||||
Timestamp: msg.GetTimestamp().AsTime(),
|
||||
Transport: msg.GetTransport(),
|
||||
Application: msg.GetApplication(),
|
||||
SourceIP: msg.SourceIP,
|
||||
DestinationIP: msg.DestinationIP,
|
||||
SourcePort: uint16(msg.GetSourcePort()),
|
||||
DestinationPort: uint16(msg.GetDestinationPort()),
|
||||
ProtocolDetails: guessDetailsFromApp(msg.GetProtocolDetails()),
|
||||
TLS: NewTLSDetailsFromProto(msg.GetTls()),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func parseIPPortFromAddr(addr net.Addr) (ip net.IP, port uint16) {
|
||||
if addr == nil {
|
||||
return
|
||||
}
|
||||
switch a := addr.(type) {
|
||||
case *net.TCPAddr:
|
||||
return a.IP, uint16(a.Port)
|
||||
case *net.UDPAddr:
|
||||
return a.IP, uint16(a.Port)
|
||||
case *net.UnixAddr:
|
||||
return
|
||||
default:
|
||||
ipPortSplit := strings.Split(addr.String(), ":")
|
||||
if len(ipPortSplit) != 2 {
|
||||
return
|
||||
}
|
||||
|
||||
ip = net.ParseIP(ipPortSplit[0])
|
||||
if p, err := strconv.Atoi(ipPortSplit[1]); err == nil {
|
||||
port = uint16(p)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func guessDetailsFromApp(any *anypb.Any) Details {
|
||||
var detailsProto proto.Message
|
||||
var err error
|
||||
if detailsProto, err = any.UnmarshalNew(); err != nil {
|
||||
return nil
|
||||
}
|
||||
switch any.TypeUrl {
|
||||
case "type.googleapis.com/inetmock.audit.HTTPDetailsEntity":
|
||||
return details.NewHTTPFromWireFormat(detailsProto.(*details.HTTPDetailsEntity))
|
||||
case "type.googleapis.com/inetmock.audit.DNSDetailsEntity":
|
||||
return details.NewDNSFromWireFormat(detailsProto.(*details.DNSDetailsEntity))
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
145
pkg/audit/event_stream.go
Normal file
145
pkg/audit/event_stream.go
Normal file
|
@ -0,0 +1,145 @@
|
|||
package audit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/bwmarrin/snowflake"
|
||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func init() {
|
||||
snowflake.Epoch = time.Unix(0, 0).Unix()
|
||||
}
|
||||
|
||||
type eventStream struct {
|
||||
logger logging.Logger
|
||||
buffer chan *Event
|
||||
sinks map[string]*registeredSink
|
||||
lock sync.Locker
|
||||
idGenerator *snowflake.Node
|
||||
sinkBufferSize int
|
||||
sinkConsumptionTimeout time.Duration
|
||||
}
|
||||
|
||||
type registeredSink struct {
|
||||
downstream chan Event
|
||||
lock sync.Locker
|
||||
}
|
||||
|
||||
func NewEventStream(logger logging.Logger, options ...EventStreamOption) (es EventStream, err error) {
|
||||
cfg := newEventStreamCfg()
|
||||
|
||||
for _, opt := range options {
|
||||
opt(&cfg)
|
||||
}
|
||||
|
||||
var node *snowflake.Node
|
||||
node, err = snowflake.NewNode(cfg.generatorIndex)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
generatorIdx++
|
||||
underlying := &eventStream{
|
||||
logger: logger,
|
||||
sinks: make(map[string]*registeredSink),
|
||||
buffer: make(chan *Event, cfg.bufferSize),
|
||||
sinkBufferSize: cfg.sinkBuffersize,
|
||||
sinkConsumptionTimeout: cfg.sinkConsumptionTimeout,
|
||||
idGenerator: node,
|
||||
lock: &sync.Mutex{},
|
||||
}
|
||||
|
||||
// start distribute workers
|
||||
for i := 0; i < cfg.distributeParallelization; i++ {
|
||||
go underlying.distribute()
|
||||
}
|
||||
|
||||
es = underlying
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (e *eventStream) Emit(ev Event) {
|
||||
ev.ApplyDefaults(e.idGenerator.Generate().Int64())
|
||||
select {
|
||||
case e.buffer <- &ev:
|
||||
e.logger.Debug("pushed event to distribute loop")
|
||||
default:
|
||||
e.logger.Warn("buffer is full")
|
||||
}
|
||||
}
|
||||
|
||||
func (e *eventStream) RemoveSink(name string) (exists bool) {
|
||||
e.lock.Lock()
|
||||
defer e.lock.Unlock()
|
||||
|
||||
var sink *registeredSink
|
||||
sink, exists = e.sinks[name]
|
||||
if !exists {
|
||||
return
|
||||
}
|
||||
sink.lock.Lock()
|
||||
defer sink.lock.Unlock()
|
||||
delete(e.sinks, name)
|
||||
close(sink.downstream)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (e *eventStream) RegisterSink(ctx context.Context, s Sink) error {
|
||||
name := s.Name()
|
||||
|
||||
if _, exists := e.sinks[name]; exists {
|
||||
return ErrSinkAlreadyRegistered
|
||||
}
|
||||
|
||||
rs := ®isteredSink{
|
||||
downstream: make(chan Event, e.sinkBufferSize),
|
||||
lock: new(sync.Mutex),
|
||||
}
|
||||
|
||||
s.OnSubscribe(rs.downstream)
|
||||
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
e.RemoveSink(name)
|
||||
}()
|
||||
|
||||
e.sinks[name] = rs
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e eventStream) Sinks() (sinks []string) {
|
||||
for name := range e.sinks {
|
||||
sinks = append(sinks, name)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (e *eventStream) Close() error {
|
||||
close(e.buffer)
|
||||
for _, rs := range e.sinks {
|
||||
close(rs.downstream)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *eventStream) distribute() {
|
||||
for ev := range e.buffer {
|
||||
for name, rs := range e.sinks {
|
||||
rs.lock.Lock()
|
||||
e.logger.Debug("notify sink", zap.String("sink", name))
|
||||
select {
|
||||
case rs.downstream <- *ev:
|
||||
e.logger.Debug("pushed event to sink channel")
|
||||
case <-time.After(e.sinkConsumptionTimeout):
|
||||
e.logger.Warn("sink consummation timed out")
|
||||
}
|
||||
rs.lock.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
216
pkg/audit/event_stream_test.go
Normal file
216
pkg/audit/event_stream_test.go
Normal file
|
@ -0,0 +1,216 @@
|
|||
package audit_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit/details"
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit/sink"
|
||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
"gitlab.com/inetmock/inetmock/pkg/wait"
|
||||
)
|
||||
|
||||
var (
|
||||
noOpSink = sink.NewNoOpSink("test defaultSink")
|
||||
testEvents = []*audit.Event{
|
||||
{
|
||||
Transport: audit.TransportProtocol_TCP,
|
||||
Application: audit.AppProtocol_HTTP,
|
||||
SourceIP: net.ParseIP("127.0.0.1").To4(),
|
||||
DestinationIP: net.ParseIP("127.0.0.1").To4(),
|
||||
SourcePort: 32344,
|
||||
DestinationPort: 80,
|
||||
TLS: &audit.TLSDetails{
|
||||
Version: audit.TLSVersionToEntity(tls.VersionTLS13).String(),
|
||||
CipherSuite: tls.CipherSuiteName(tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA),
|
||||
ServerName: "localhost",
|
||||
},
|
||||
ProtocolDetails: details.HTTP{
|
||||
Method: "GET",
|
||||
Host: "localhost",
|
||||
URI: "http://localhost/asdf",
|
||||
Proto: "HTTP 1.1",
|
||||
Headers: http.Header{
|
||||
"Accept": []string{"application/json"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Transport: audit.TransportProtocol_TCP,
|
||||
Application: audit.AppProtocol_DNS,
|
||||
SourceIP: net.ParseIP("::1").To16(),
|
||||
DestinationIP: net.ParseIP("::1").To16(),
|
||||
SourcePort: 32344,
|
||||
DestinationPort: 80,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func wgMockSink(t testing.TB, wg *sync.WaitGroup) audit.Sink {
|
||||
return sink.NewGenericSink(
|
||||
"WG mock sink",
|
||||
func(event audit.Event) {
|
||||
t.Logf("Got event = %v", event)
|
||||
wg.Done()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func Test_eventStream_RegisterSink(t *testing.T) {
|
||||
type args struct {
|
||||
s audit.Sink
|
||||
}
|
||||
type testCase struct {
|
||||
name string
|
||||
args args
|
||||
setup func(e audit.EventStream)
|
||||
wantErr bool
|
||||
}
|
||||
tests := []testCase{
|
||||
{
|
||||
name: "Register test defaultSink",
|
||||
args: args{
|
||||
s: noOpSink,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Fail due to already registered defaultSink",
|
||||
args: args{
|
||||
s: noOpSink,
|
||||
},
|
||||
setup: func(e audit.EventStream) {
|
||||
_ = e.RegisterSink(context.Background(), noOpSink)
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
scenario := func(tt testCase) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
var err error
|
||||
var e audit.EventStream
|
||||
if e, err = audit.NewEventStream(logging.CreateTestLogger(t)); err != nil {
|
||||
t.Errorf("NewEventStream() error = %v", err)
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
_ = e.Close()
|
||||
})
|
||||
|
||||
if tt.setup != nil {
|
||||
tt.setup(e)
|
||||
}
|
||||
|
||||
if err := e.RegisterSink(context.Background(), tt.args.s); (err != nil) != tt.wantErr {
|
||||
t.Errorf("RegisterSink() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
|
||||
found := false
|
||||
for _, s := range e.Sinks() {
|
||||
if found = s == tt.args.s.Name(); found {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("expected defaultSink name %s not found in registered sinks %v", tt.args.s.Name(), e.Sinks())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, scenario(tt))
|
||||
}
|
||||
}
|
||||
|
||||
func Test_eventStream_Emit(t *testing.T) {
|
||||
type args struct {
|
||||
evs []*audit.Event
|
||||
opts []audit.EventStreamOption
|
||||
}
|
||||
type testCase struct {
|
||||
name string
|
||||
args args
|
||||
subscribe bool
|
||||
}
|
||||
tests := []testCase{
|
||||
{
|
||||
name: "Expect to get a single event",
|
||||
subscribe: true,
|
||||
args: args{
|
||||
opts: []audit.EventStreamOption{},
|
||||
evs: testEvents[:1],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Expect to get multiple events",
|
||||
subscribe: true,
|
||||
args: args{
|
||||
opts: []audit.EventStreamOption{},
|
||||
evs: testEvents,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Emit without subscribe sink",
|
||||
args: args{
|
||||
opts: []audit.EventStreamOption{audit.WithBufferSize(0)},
|
||||
evs: testEvents[:1],
|
||||
},
|
||||
subscribe: false,
|
||||
},
|
||||
}
|
||||
|
||||
scenario := func(tt testCase) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
var err error
|
||||
var e audit.EventStream
|
||||
if e, err = audit.NewEventStream(logging.CreateTestLogger(t), tt.args.opts...); err != nil {
|
||||
t.Errorf("NewEventStream() error = %v", err)
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
_ = e.Close()
|
||||
})
|
||||
|
||||
emittedWaitGroup := new(sync.WaitGroup)
|
||||
receivedWaitGroup := new(sync.WaitGroup)
|
||||
|
||||
emittedWaitGroup.Add(len(tt.args.evs))
|
||||
|
||||
if tt.subscribe {
|
||||
receivedWaitGroup.Add(len(tt.args.evs))
|
||||
if err := e.RegisterSink(context.Background(), wgMockSink(t, receivedWaitGroup)); err != nil {
|
||||
t.Errorf("RegisterSink() error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
go func(evs []*audit.Event, wg *sync.WaitGroup) {
|
||||
for _, ev := range evs {
|
||||
e.Emit(*ev)
|
||||
wg.Done()
|
||||
}
|
||||
}(tt.args.evs, emittedWaitGroup)
|
||||
|
||||
select {
|
||||
case <-wait.ForWaitGroupDone(emittedWaitGroup):
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
t.Errorf("not all events emitted in time")
|
||||
}
|
||||
|
||||
select {
|
||||
case <-wait.ForWaitGroupDone(receivedWaitGroup):
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Errorf("did not get all expected events in time")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, scenario(tt))
|
||||
}
|
||||
}
|
61
pkg/audit/event_test.go
Normal file
61
pkg/audit/event_test.go
Normal file
|
@ -0,0 +1,61 @@
|
|||
package audit
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit/details"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
)
|
||||
|
||||
func Test_guessDetailsFromApp(t *testing.T) {
|
||||
|
||||
mustAny := func(msg proto.Message) *anypb.Any {
|
||||
a, err := anypb.New(msg)
|
||||
if err != nil {
|
||||
panic(a)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
type args struct {
|
||||
any *anypb.Any
|
||||
}
|
||||
type testCase struct {
|
||||
name string
|
||||
args args
|
||||
want Details
|
||||
}
|
||||
tests := []testCase{
|
||||
{
|
||||
name: "HTTP etails",
|
||||
args: args{
|
||||
any: mustAny(&details.HTTPDetailsEntity{
|
||||
Method: details.HTTPMethod_GET,
|
||||
Host: "localhost",
|
||||
Uri: "http://localhost/asdf",
|
||||
Proto: "HTTP",
|
||||
}),
|
||||
},
|
||||
want: details.HTTP{
|
||||
Method: "GET",
|
||||
Host: "localhost",
|
||||
URI: "http://localhost/asdf",
|
||||
Proto: "HTTP",
|
||||
Headers: http.Header{},
|
||||
},
|
||||
},
|
||||
}
|
||||
scenario := func(tt testCase) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
if got := guessDetailsFromApp(tt.args.any); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("guessDetailsFromApp() = %v, want %v", got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, scenario(tt))
|
||||
}
|
||||
}
|
74
pkg/audit/options.go
Normal file
74
pkg/audit/options.go
Normal file
|
@ -0,0 +1,74 @@
|
|||
package audit
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultEventStreamBufferSize = 100
|
||||
defaultSinkBufferSize = 0
|
||||
defaultSinkConsumptionTimeout = 50 * time.Millisecond
|
||||
)
|
||||
|
||||
var (
|
||||
defaultDistributeParallelization int
|
||||
generatorIdx int64 = 1
|
||||
WithBufferSize = func(bufferSize int) EventStreamOption {
|
||||
return func(cfg *eventStreamCfg) {
|
||||
cfg.bufferSize = bufferSize
|
||||
}
|
||||
}
|
||||
WithGeneratorIndex = func(generatorIndex int64) EventStreamOption {
|
||||
return func(cfg *eventStreamCfg) {
|
||||
cfg.generatorIndex = generatorIndex
|
||||
}
|
||||
}
|
||||
WithSinkBufferSize = func(bufferSize int) EventStreamOption {
|
||||
return func(cfg *eventStreamCfg) {
|
||||
cfg.sinkBuffersize = bufferSize
|
||||
}
|
||||
}
|
||||
WithSinkConsumptionTimeout = func(timeout time.Duration) EventStreamOption {
|
||||
return func(cfg *eventStreamCfg) {
|
||||
cfg.sinkConsumptionTimeout = timeout
|
||||
}
|
||||
}
|
||||
WithDistributeParallelization = func(parallelization int) EventStreamOption {
|
||||
return func(cfg *eventStreamCfg) {
|
||||
if parallelization <= 0 || parallelization > runtime.NumCPU() {
|
||||
return
|
||||
}
|
||||
cfg.distributeParallelization = parallelization
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
if defaultDistributeParallelization = runtime.NumCPU() / 2; defaultDistributeParallelization < 2 {
|
||||
defaultDistributeParallelization = 2
|
||||
}
|
||||
}
|
||||
|
||||
type EventStreamOption func(cfg *eventStreamCfg)
|
||||
|
||||
type eventStreamCfg struct {
|
||||
bufferSize int
|
||||
sinkBuffersize int
|
||||
generatorIndex int64
|
||||
distributeParallelization int
|
||||
sinkConsumptionTimeout time.Duration
|
||||
}
|
||||
|
||||
func newEventStreamCfg() eventStreamCfg {
|
||||
cfg := eventStreamCfg{
|
||||
generatorIndex: generatorIdx,
|
||||
sinkBuffersize: defaultSinkBufferSize,
|
||||
bufferSize: defaultEventStreamBufferSize,
|
||||
sinkConsumptionTimeout: defaultSinkConsumptionTimeout,
|
||||
distributeParallelization: defaultDistributeParallelization,
|
||||
}
|
||||
generatorIdx++
|
||||
|
||||
return cfg
|
||||
}
|
87
pkg/audit/reader.go
Normal file
87
pkg/audit/reader.go
Normal file
|
@ -0,0 +1,87 @@
|
|||
package audit
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
lengthBufferSize = 4
|
||||
defaultPayloadBufferSize = 4096
|
||||
)
|
||||
|
||||
type Reader interface {
|
||||
Read() (Event, error)
|
||||
}
|
||||
|
||||
type EventReaderOption func(reader *eventReader)
|
||||
|
||||
var (
|
||||
WithReaderByteOrder = func(order binary.ByteOrder) EventReaderOption {
|
||||
return func(reader *eventReader) {
|
||||
reader.byteOrder = order
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
func NewEventReader(source io.Reader, opts ...EventReaderOption) Reader {
|
||||
reader := &eventReader{
|
||||
source: source,
|
||||
byteOrder: defaultOrder,
|
||||
lengthPool: &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, lengthBufferSize)
|
||||
},
|
||||
},
|
||||
payloadPool: &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, defaultPayloadBufferSize)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(reader)
|
||||
}
|
||||
|
||||
return reader
|
||||
}
|
||||
|
||||
type eventReader struct {
|
||||
lengthPool *sync.Pool
|
||||
payloadPool *sync.Pool
|
||||
byteOrder binary.ByteOrder
|
||||
source io.Reader
|
||||
}
|
||||
|
||||
func (e eventReader) Read() (ev Event, err error) {
|
||||
lengthBuf := e.lengthPool.Get().([]byte)
|
||||
if _, err = e.source.Read(lengthBuf); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
length := e.byteOrder.Uint32(lengthBuf)
|
||||
var msgBuf []byte
|
||||
if length <= defaultPayloadBufferSize {
|
||||
msgBuf = e.payloadPool.Get().([]byte)[:length]
|
||||
} else {
|
||||
msgBuf = make([]byte, length)
|
||||
}
|
||||
|
||||
if _, err = e.source.Read(msgBuf); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
protoEv := new(EventEntity)
|
||||
|
||||
if err = proto.Unmarshal(msgBuf, protoEv); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ev = NewEventFromProto(protoEv)
|
||||
|
||||
return
|
||||
}
|
99
pkg/audit/reader_test.go
Normal file
99
pkg/audit/reader_test.go
Normal file
|
@ -0,0 +1,99 @@
|
|||
package audit_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
||||
)
|
||||
|
||||
var (
|
||||
//nolint:lll
|
||||
httpPayloadBytesLittleEndian = `dd000000120b088092b8c398feffffff01180120022a047f00000132047f00000138d8fc0140504a3308041224544c535f45434448455f45434453415f574954485f4145535f3235365f4342435f5348411a096c6f63616c686f73745282010a34747970652e676f6f676c65617069732e636f6d2f696e65746d6f636b2e61756469742e4854545044657461696c73456e74697479124a12096c6f63616c686f73741a15687474703a2f2f6c6f63616c686f73742f6173646622084854545020312e312a1c0a0641636365707412120a106170706c69636174696f6e2f6a736f6e`
|
||||
//nolint:lll
|
||||
httpPayloadBytesBigEndian = `000000dd120b088092b8c398feffffff01180120022a047f00000132047f00000138d8fc0140504a3308041224544c535f45434448455f45434453415f574954485f4145535f3235365f4342435f5348411a096c6f63616c686f73745282010a34747970652e676f6f676c65617069732e636f6d2f696e65746d6f636b2e61756469742e4854545044657461696c73456e74697479124a12096c6f63616c686f73741a15687474703a2f2f6c6f63616c686f73742f6173646622084854545020312e312a1c0a0641636365707412120a106170706c69636174696f6e2f6a736f6e`
|
||||
//nolint:lll
|
||||
dnsPayloadBytesLittleEndian = `3b000000120b088092b8c398feffffff01180120012a100000000000000000000000000000000132100000000000000000000000000000000138d8fc014050`
|
||||
//nolint:lll
|
||||
dnsPayloadBytesBigEndian = `0000003b120b088092b8c398feffffff01180120012a100000000000000000000000000000000132100000000000000000000000000000000138d8fc014050`
|
||||
)
|
||||
|
||||
func mustDecodeHex(hexBytes string) io.Reader {
|
||||
b, err := hex.DecodeString(hexBytes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bytes.NewReader(b)
|
||||
}
|
||||
|
||||
func Test_eventReader_Read(t *testing.T) {
|
||||
type fields struct {
|
||||
source io.Reader
|
||||
order binary.ByteOrder
|
||||
}
|
||||
type testCase struct {
|
||||
name string
|
||||
fields fields
|
||||
wantEv *audit.Event
|
||||
wantErr bool
|
||||
}
|
||||
tests := []testCase{
|
||||
{
|
||||
name: "Read HTTP payload - little endian",
|
||||
fields: fields{
|
||||
source: mustDecodeHex(httpPayloadBytesLittleEndian),
|
||||
order: binary.LittleEndian,
|
||||
},
|
||||
wantEv: testEvents[0],
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Read HTTP payload - big endian",
|
||||
fields: fields{
|
||||
source: mustDecodeHex(httpPayloadBytesBigEndian),
|
||||
order: binary.BigEndian,
|
||||
},
|
||||
wantEv: testEvents[0],
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Read DNS payload - little endian",
|
||||
fields: fields{
|
||||
source: mustDecodeHex(dnsPayloadBytesLittleEndian),
|
||||
order: binary.LittleEndian,
|
||||
},
|
||||
wantEv: testEvents[1],
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Read DNS payload - big endian",
|
||||
fields: fields{
|
||||
source: mustDecodeHex(dnsPayloadBytesBigEndian),
|
||||
order: binary.BigEndian,
|
||||
},
|
||||
wantEv: testEvents[1],
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
scenario := func(tt testCase) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
e := audit.NewEventReader(tt.fields.source, audit.WithReaderByteOrder(tt.fields.order))
|
||||
gotEv, err := e.Read()
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Read() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
if err == nil && !reflect.DeepEqual(gotEv, *tt.wantEv) {
|
||||
t.Errorf("Read() gotEv = %v, want %v", gotEv, tt.wantEv)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, scenario(tt))
|
||||
}
|
||||
}
|
33
pkg/audit/sink/generic_sink.go
Normal file
33
pkg/audit/sink/generic_sink.go
Normal file
|
@ -0,0 +1,33 @@
|
|||
package sink
|
||||
|
||||
import (
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
||||
)
|
||||
|
||||
func NewNoOpSink(name string) audit.Sink {
|
||||
return NewGenericSink(name, func(_ audit.Event) {})
|
||||
}
|
||||
|
||||
func NewGenericSink(name string, consumer func(ev audit.Event)) audit.Sink {
|
||||
return &genericSink{
|
||||
name: name,
|
||||
consumer: consumer,
|
||||
}
|
||||
}
|
||||
|
||||
type genericSink struct {
|
||||
name string
|
||||
consumer func(ev audit.Event)
|
||||
}
|
||||
|
||||
func (g genericSink) Name() string {
|
||||
return g.name
|
||||
}
|
||||
|
||||
func (g genericSink) OnSubscribe(evs <-chan audit.Event) {
|
||||
go func(consumer func(ev audit.Event), evs <-chan audit.Event) {
|
||||
for ev := range evs {
|
||||
consumer(ev)
|
||||
}
|
||||
}(g.consumer, evs)
|
||||
}
|
65
pkg/audit/sink/generic_sink_test.go
Normal file
65
pkg/audit/sink/generic_sink_test.go
Normal file
|
@ -0,0 +1,65 @@
|
|||
package sink_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit/sink"
|
||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
"gitlab.com/inetmock/inetmock/pkg/wait"
|
||||
)
|
||||
|
||||
func Test_genericSink_OnSubscribe(t *testing.T) {
|
||||
type testCase struct {
|
||||
name string
|
||||
events []*audit.Event
|
||||
}
|
||||
tests := []testCase{
|
||||
{
|
||||
name: "Get a single log line",
|
||||
events: testEvents[:1],
|
||||
},
|
||||
{
|
||||
name: "Get multiple events",
|
||||
events: testEvents,
|
||||
},
|
||||
}
|
||||
scenario := func(tt testCase) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
wg := new(sync.WaitGroup)
|
||||
wg.Add(len(tt.events))
|
||||
|
||||
genericSink := sink.NewGenericSink(t.Name(), func(ev audit.Event) {
|
||||
wg.Done()
|
||||
})
|
||||
|
||||
var evs audit.EventStream
|
||||
var err error
|
||||
if evs, err = audit.NewEventStream(logging.CreateTestLogger(t)); err != nil {
|
||||
t.Errorf("NewEventStream() error = %v", err)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
t.Cleanup(cancel)
|
||||
if err = evs.RegisterSink(ctx, genericSink); err != nil {
|
||||
t.Errorf("RegisterSink() error = %v", err)
|
||||
}
|
||||
|
||||
for _, ev := range tt.events {
|
||||
evs.Emit(*ev)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
t.Errorf("not all events recorded in time")
|
||||
case <-wait.ForWaitGroupDone(wg):
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, scenario(tt))
|
||||
}
|
||||
}
|
53
pkg/audit/sink/log_sink.go
Normal file
53
pkg/audit/sink/log_sink.go
Normal file
|
@ -0,0 +1,53 @@
|
|||
package sink
|
||||
|
||||
import (
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const (
|
||||
logSinkName = "logging"
|
||||
)
|
||||
|
||||
func NewLogSink(logger logging.Logger) audit.Sink {
|
||||
return &logSink{
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
type logSink struct {
|
||||
logger logging.Logger
|
||||
}
|
||||
|
||||
func (logSink) Name() string {
|
||||
return logSinkName
|
||||
}
|
||||
|
||||
func (l logSink) OnSubscribe(evs <-chan audit.Event) {
|
||||
go func(logger logging.Logger, evs <-chan audit.Event) {
|
||||
for ev := range evs {
|
||||
eventLogger := logger
|
||||
|
||||
if ev.TLS != nil {
|
||||
eventLogger = eventLogger.With(
|
||||
zap.String("tls_server_name", ev.TLS.ServerName),
|
||||
zap.String("tls_cipher_suite", ev.TLS.CipherSuite),
|
||||
zap.String("tls_version", ev.TLS.Version),
|
||||
)
|
||||
}
|
||||
|
||||
eventLogger.Info(
|
||||
"handled request",
|
||||
zap.Time("timestamp", ev.Timestamp),
|
||||
zap.String("application", ev.Application.String()),
|
||||
zap.String("transport", ev.Transport.String()),
|
||||
zap.String("source_ip", ev.SourceIP.String()),
|
||||
zap.Uint16("source_port", ev.SourcePort),
|
||||
zap.String("destination_ip", ev.DestinationIP.String()),
|
||||
zap.Uint16("destination_port", ev.DestinationPort),
|
||||
zap.Any("details", ev.ProtocolDetails),
|
||||
)
|
||||
}
|
||||
}(l.logger, evs)
|
||||
}
|
115
pkg/audit/sink/log_sink_test.go
Normal file
115
pkg/audit/sink/log_sink_test.go
Normal file
|
@ -0,0 +1,115 @@
|
|||
package sink_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
logging_mock "gitlab.com/inetmock/inetmock/internal/mock/logging"
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit/sink"
|
||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
"gitlab.com/inetmock/inetmock/pkg/wait"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
//nolint:dupl
|
||||
func Test_logSink_OnSubscribe(t *testing.T) {
|
||||
type fields struct {
|
||||
loggerSetup func(t *testing.T, wg *sync.WaitGroup) logging.Logger
|
||||
}
|
||||
type testCase struct {
|
||||
name string
|
||||
fields fields
|
||||
events []*audit.Event
|
||||
}
|
||||
tests := []testCase{
|
||||
{
|
||||
name: "Get a single log line",
|
||||
fields: fields{
|
||||
loggerSetup: func(t *testing.T, wg *sync.WaitGroup) logging.Logger {
|
||||
ctrl := gomock.NewController(t)
|
||||
t.Cleanup(ctrl.Finish)
|
||||
loggerMock := logging_mock.NewMockLogger(ctrl)
|
||||
|
||||
loggerMock.
|
||||
EXPECT().
|
||||
With(gomock.Any()).
|
||||
Return(loggerMock)
|
||||
|
||||
loggerMock.
|
||||
EXPECT().
|
||||
Info("handled request", gomock.Any()).
|
||||
Do(func(_ string, _ ...zap.Field) {
|
||||
wg.Done()
|
||||
}).
|
||||
Times(1)
|
||||
|
||||
return loggerMock
|
||||
},
|
||||
},
|
||||
events: testEvents[:1],
|
||||
},
|
||||
{
|
||||
name: "Get multiple events",
|
||||
fields: fields{
|
||||
loggerSetup: func(t *testing.T, wg *sync.WaitGroup) logging.Logger {
|
||||
ctrl := gomock.NewController(t)
|
||||
t.Cleanup(ctrl.Finish)
|
||||
loggerMock := logging_mock.NewMockLogger(ctrl)
|
||||
|
||||
loggerMock.
|
||||
EXPECT().
|
||||
With(gomock.Any()).
|
||||
Return(loggerMock)
|
||||
|
||||
loggerMock.
|
||||
EXPECT().
|
||||
Info("handled request", gomock.Any()).
|
||||
Do(func(_ string, _ ...zap.Field) {
|
||||
wg.Done()
|
||||
}).
|
||||
Times(2)
|
||||
|
||||
return loggerMock
|
||||
},
|
||||
},
|
||||
events: testEvents,
|
||||
},
|
||||
}
|
||||
scenario := func(tt testCase) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
wg := new(sync.WaitGroup)
|
||||
wg.Add(len(tt.events))
|
||||
|
||||
logSink := sink.NewLogSink(tt.fields.loggerSetup(t, wg))
|
||||
var evs audit.EventStream
|
||||
var err error
|
||||
if evs, err = audit.NewEventStream(logging.CreateTestLogger(t)); err != nil {
|
||||
t.Errorf("NewEventStream() error = %v", err)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
t.Cleanup(cancel)
|
||||
if err = evs.RegisterSink(ctx, logSink); err != nil {
|
||||
t.Errorf("RegisterSink() error = %v", err)
|
||||
}
|
||||
|
||||
for _, ev := range tt.events {
|
||||
evs.Emit(*ev)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
t.Errorf("not all events recorded in time")
|
||||
case <-wait.ForWaitGroupDone(wg):
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, scenario(tt))
|
||||
}
|
||||
}
|
34
pkg/audit/sink/metric_sink.go
Normal file
34
pkg/audit/sink/metric_sink.go
Normal file
|
@ -0,0 +1,34 @@
|
|||
package sink
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
||||
"gitlab.com/inetmock/inetmock/pkg/metrics"
|
||||
)
|
||||
|
||||
func NewMetricSink() (sink audit.Sink, err error) {
|
||||
var totalEventsCounter *prometheus.CounterVec
|
||||
if totalEventsCounter, err = metrics.Counter("audit", "events_total", "", "application", "transport"); err != nil {
|
||||
return
|
||||
}
|
||||
sink = &metricSink{
|
||||
eventCounter: totalEventsCounter,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type metricSink struct {
|
||||
eventCounter *prometheus.CounterVec
|
||||
}
|
||||
|
||||
func (metricSink) Name() string {
|
||||
return "metrics"
|
||||
}
|
||||
|
||||
func (m metricSink) OnSubscribe(evs <-chan audit.Event) {
|
||||
go func(evs <-chan audit.Event) {
|
||||
for ev := range evs {
|
||||
m.eventCounter.WithLabelValues(ev.Application.String(), ev.Transport.String()).Inc()
|
||||
}
|
||||
}(evs)
|
||||
}
|
45
pkg/audit/sink/writer_sink.go
Normal file
45
pkg/audit/sink/writer_sink.go
Normal file
|
@ -0,0 +1,45 @@
|
|||
package sink
|
||||
|
||||
import "gitlab.com/inetmock/inetmock/pkg/audit"
|
||||
|
||||
type WriterSinkOption func(sink *writerCloserSink)
|
||||
|
||||
var (
|
||||
WithCloseOnExit WriterSinkOption = func(sink *writerCloserSink) {
|
||||
sink.closeOnExit = true
|
||||
}
|
||||
)
|
||||
|
||||
func NewWriterSink(name string, target audit.Writer, opts ...WriterSinkOption) audit.Sink {
|
||||
sink := &writerCloserSink{
|
||||
name: name,
|
||||
target: target,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(sink)
|
||||
}
|
||||
|
||||
return sink
|
||||
}
|
||||
|
||||
type writerCloserSink struct {
|
||||
name string
|
||||
target audit.Writer
|
||||
closeOnExit bool
|
||||
}
|
||||
|
||||
func (f writerCloserSink) Name() string {
|
||||
return f.name
|
||||
}
|
||||
|
||||
func (f writerCloserSink) OnSubscribe(evs <-chan audit.Event) {
|
||||
go func(target audit.Writer, closeOnExit bool, evs <-chan audit.Event) {
|
||||
for ev := range evs {
|
||||
_ = target.Write(&ev)
|
||||
}
|
||||
if closeOnExit {
|
||||
_ = target.Close()
|
||||
}
|
||||
}(f.target, f.closeOnExit, evs)
|
||||
}
|
118
pkg/audit/sink/writer_sink_test.go
Normal file
118
pkg/audit/sink/writer_sink_test.go
Normal file
|
@ -0,0 +1,118 @@
|
|||
package sink_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
audit_mock "gitlab.com/inetmock/inetmock/internal/mock/audit"
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit/details"
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit/sink"
|
||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
"gitlab.com/inetmock/inetmock/pkg/wait"
|
||||
)
|
||||
|
||||
var (
|
||||
testEvents = []*audit.Event{
|
||||
{
|
||||
Transport: audit.TransportProtocol_TCP,
|
||||
Application: audit.AppProtocol_HTTP,
|
||||
SourceIP: net.ParseIP("127.0.0.1").To4(),
|
||||
DestinationIP: net.ParseIP("127.0.0.1").To4(),
|
||||
SourcePort: 32344,
|
||||
DestinationPort: 80,
|
||||
TLS: &audit.TLSDetails{
|
||||
Version: audit.TLSVersionToEntity(tls.VersionTLS13).String(),
|
||||
CipherSuite: tls.CipherSuiteName(tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA),
|
||||
ServerName: "localhost",
|
||||
},
|
||||
ProtocolDetails: details.HTTP{
|
||||
Method: "GET",
|
||||
Host: "localhost",
|
||||
URI: "http://localhost/asdf",
|
||||
Proto: "HTTP 1.1",
|
||||
Headers: http.Header{
|
||||
"Accept": []string{"application/json"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Transport: audit.TransportProtocol_TCP,
|
||||
Application: audit.AppProtocol_DNS,
|
||||
SourceIP: net.ParseIP("::1").To16(),
|
||||
DestinationIP: net.ParseIP("::1").To16(),
|
||||
SourcePort: 32344,
|
||||
DestinationPort: 80,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func Test_writerCloserSink_OnSubscribe(t *testing.T) {
|
||||
type testCase struct {
|
||||
name string
|
||||
events []*audit.Event
|
||||
}
|
||||
tests := []testCase{
|
||||
{
|
||||
name: "Get a single event",
|
||||
events: testEvents[:1],
|
||||
},
|
||||
{
|
||||
name: "Get multiple events",
|
||||
events: testEvents,
|
||||
},
|
||||
}
|
||||
scenario := func(tt testCase) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
wg := new(sync.WaitGroup)
|
||||
wg.Add(len(tt.events))
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
t.Cleanup(ctrl.Finish)
|
||||
|
||||
writerMock := audit_mock.NewMockWriter(ctrl)
|
||||
writerMock.
|
||||
EXPECT().
|
||||
Write(gomock.Any()).
|
||||
Do(func(_ *audit.Event) {
|
||||
wg.Done()
|
||||
}).
|
||||
Times(len(tt.events))
|
||||
|
||||
writerCloserSink := sink.NewWriterSink("WriterMock", writerMock)
|
||||
var evs audit.EventStream
|
||||
var err error
|
||||
|
||||
if evs, err = audit.NewEventStream(logging.CreateTestLogger(t)); err != nil {
|
||||
t.Errorf("NewEventStream() error = %v", err)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
t.Cleanup(cancel)
|
||||
|
||||
if err = evs.RegisterSink(ctx, writerCloserSink); err != nil {
|
||||
t.Errorf("RegisterSink() error = %v", err)
|
||||
}
|
||||
|
||||
for _, ev := range tt.events {
|
||||
evs.Emit(*ev)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
t.Errorf("not all events recorded in time")
|
||||
case <-wait.ForWaitGroupDone(wg):
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, scenario(tt))
|
||||
}
|
||||
}
|
46
pkg/audit/tls_details.go
Normal file
46
pkg/audit/tls_details.go
Normal file
|
@ -0,0 +1,46 @@
|
|||
package audit
|
||||
|
||||
import "crypto/tls"
|
||||
|
||||
var (
|
||||
tlsToEntity = map[uint16]TLSVersion{
|
||||
tls.VersionSSL30: TLSVersion_SSLv30,
|
||||
tls.VersionTLS10: TLSVersion_TLS10,
|
||||
tls.VersionTLS11: TLSVersion_TLS11,
|
||||
tls.VersionTLS12: TLSVersion_TLS12,
|
||||
tls.VersionTLS13: TLSVersion_TLS13,
|
||||
}
|
||||
)
|
||||
|
||||
type TLSDetails struct {
|
||||
Version string
|
||||
CipherSuite string
|
||||
ServerName string
|
||||
}
|
||||
|
||||
func TLSVersionToEntity(version uint16) TLSVersion {
|
||||
if v, known := tlsToEntity[version]; known {
|
||||
return v
|
||||
}
|
||||
return TLSVersion_SSLv30
|
||||
}
|
||||
|
||||
func NewTLSDetailsFromProto(entity *TLSDetailsEntity) *TLSDetails {
|
||||
if entity == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &TLSDetails{
|
||||
Version: entity.GetVersion().String(),
|
||||
CipherSuite: entity.GetCipherSuite(),
|
||||
ServerName: entity.GetServerName(),
|
||||
}
|
||||
}
|
||||
|
||||
func (d TLSDetails) ProtoMessage() *TLSDetailsEntity {
|
||||
return &TLSDetailsEntity{
|
||||
Version: TLSVersion(TLSVersion_value[d.Version]),
|
||||
CipherSuite: d.CipherSuite,
|
||||
ServerName: d.ServerName,
|
||||
}
|
||||
}
|
89
pkg/audit/writer.go
Normal file
89
pkg/audit/writer.go
Normal file
|
@ -0,0 +1,89 @@
|
|||
//go:generate mockgen -source=$GOFILE -destination=./../../internal/mock/audit/writer.mock.go -package=audit_mock
|
||||
|
||||
package audit
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
var (
|
||||
WithWriterByteOrder = func(order binary.ByteOrder) func(writer *eventWriter) {
|
||||
return func(writer *eventWriter) {
|
||||
writer.byteOrder = order
|
||||
}
|
||||
}
|
||||
defaultOrder = binary.BigEndian
|
||||
ErrValueMostNotBeNil = errors.New("event value must not be nil")
|
||||
)
|
||||
|
||||
type Writer interface {
|
||||
io.Closer
|
||||
Write(ev *Event) error
|
||||
}
|
||||
|
||||
type EventWriterOption func(writer *eventWriter)
|
||||
|
||||
func NewEventWriter(target io.Writer, opts ...EventWriterOption) Writer {
|
||||
writer := &eventWriter{
|
||||
target: target,
|
||||
byteOrder: defaultOrder,
|
||||
lengthPool: &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, lengthBufferSize)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(writer)
|
||||
}
|
||||
|
||||
return writer
|
||||
}
|
||||
|
||||
type eventWriter struct {
|
||||
lengthPool *sync.Pool
|
||||
target io.Writer
|
||||
byteOrder binary.ByteOrder
|
||||
}
|
||||
|
||||
type syncer interface {
|
||||
Sync() error
|
||||
}
|
||||
|
||||
func (e eventWriter) Write(ev *Event) (err error) {
|
||||
if ev == nil {
|
||||
return ErrValueMostNotBeNil
|
||||
}
|
||||
var bytes []byte
|
||||
|
||||
if bytes, err = proto.Marshal(ev.ProtoMessage()); err != nil {
|
||||
return
|
||||
}
|
||||
buf := e.lengthPool.Get().([]byte)
|
||||
e.byteOrder.PutUint32(buf, uint32(len(bytes)))
|
||||
|
||||
if _, err = e.target.Write(buf); err != nil {
|
||||
return
|
||||
}
|
||||
if _, err = e.target.Write(bytes); err != nil {
|
||||
return
|
||||
}
|
||||
if syncerInst, ok := e.target.(syncer); ok {
|
||||
err = syncerInst.Sync()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (e eventWriter) Close() error {
|
||||
if closer, isCloser := e.target.(io.Closer); isCloser {
|
||||
return closer.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
117
pkg/audit/writer_test.go
Normal file
117
pkg/audit/writer_test.go
Normal file
|
@ -0,0 +1,117 @@
|
|||
//go:generate mockgen -source=$GOFILE -destination=./../../internal/mock/audit/writercloser.mock.go -package=audit_mock
|
||||
|
||||
package audit_test
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
audit_mock "gitlab.com/inetmock/inetmock/internal/mock/audit"
|
||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
||||
)
|
||||
|
||||
type WriterCloserSyncer interface {
|
||||
io.WriteCloser
|
||||
Sync() error
|
||||
}
|
||||
|
||||
func Test_eventWriter_Write(t *testing.T) {
|
||||
type fields struct {
|
||||
order binary.ByteOrder
|
||||
}
|
||||
type args struct {
|
||||
evs []*audit.Event
|
||||
}
|
||||
type testCase struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
wantErr bool
|
||||
}
|
||||
tests := []testCase{
|
||||
{
|
||||
name: "Write a single event - little endian",
|
||||
fields: fields{
|
||||
order: binary.LittleEndian,
|
||||
},
|
||||
args: args{
|
||||
evs: testEvents[:1],
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Write a single event - big endian",
|
||||
fields: fields{
|
||||
order: binary.BigEndian,
|
||||
},
|
||||
args: args{
|
||||
evs: testEvents[:1],
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Write multiple events - little endian",
|
||||
fields: fields{
|
||||
order: binary.LittleEndian,
|
||||
},
|
||||
args: args{
|
||||
evs: testEvents,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Write multiple events - big endian",
|
||||
fields: fields{
|
||||
order: binary.BigEndian,
|
||||
},
|
||||
args: args{
|
||||
evs: testEvents,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
scenario := func(tt testCase) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
t.Cleanup(ctrl.Finish)
|
||||
writerMock := audit_mock.NewMockWriterCloserSyncer(ctrl)
|
||||
calls := make([]*gomock.Call, 0)
|
||||
for i := 0; i < len(tt.args.evs); i++ {
|
||||
calls = append(calls,
|
||||
writerMock.
|
||||
EXPECT().
|
||||
Write(gomock.Any()).
|
||||
Do(func(data []byte) {
|
||||
t.Logf("got payload = %s", hex.EncodeToString(data))
|
||||
t.Logf("got length %d", tt.fields.order.Uint32(data))
|
||||
}),
|
||||
writerMock.
|
||||
EXPECT().
|
||||
Write(gomock.Any()).
|
||||
Do(func(data []byte) {
|
||||
t.Logf("got payload = %s", hex.EncodeToString(data))
|
||||
}),
|
||||
writerMock.
|
||||
EXPECT().
|
||||
Sync(),
|
||||
)
|
||||
}
|
||||
gomock.InOrder(calls...)
|
||||
|
||||
e := audit.NewEventWriter(writerMock, audit.WithWriterByteOrder(tt.fields.order))
|
||||
|
||||
for _, ev := range tt.args.evs {
|
||||
if err := e.Write(ev); (err != nil) != tt.wantErr {
|
||||
t.Errorf("Write() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, scenario(tt))
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ package logging
|
|||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
|
@ -48,6 +49,13 @@ func CreateLogger() (Logger, error) {
|
|||
}
|
||||
}
|
||||
|
||||
func CreateTestLogger(tb testing.TB) Logger {
|
||||
return &testLogger{
|
||||
tb: tb,
|
||||
encoder: zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig()),
|
||||
}
|
||||
}
|
||||
|
||||
func MustCreateLogger() Logger {
|
||||
if logger, err := CreateLogger(); err != nil {
|
||||
panic(err)
|
||||
|
|
131
pkg/logging/test_logger.go
Normal file
131
pkg/logging/test_logger.go
Normal file
|
@ -0,0 +1,131 @@
|
|||
package logging
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
type testLogger struct {
|
||||
name string
|
||||
fields []zap.Field
|
||||
tb testing.TB
|
||||
encoder zapcore.Encoder
|
||||
}
|
||||
|
||||
func (t testLogger) Named(s string) Logger {
|
||||
return testLogger{
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
func (t testLogger) Debug(msg string, fields ...zap.Field) {
|
||||
t.tb.Helper()
|
||||
buf, err := t.encoder.EncodeEntry(zapcore.Entry{
|
||||
Level: zapcore.DebugLevel,
|
||||
Time: time.Now(),
|
||||
LoggerName: t.name,
|
||||
Message: msg,
|
||||
Caller: zapcore.NewEntryCaller(runtime.Caller(2)),
|
||||
}, append(t.fields, fields...))
|
||||
|
||||
if err == nil {
|
||||
t.tb.Log(buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (t testLogger) Info(msg string, fields ...zap.Field) {
|
||||
t.tb.Helper()
|
||||
buf, err := t.encoder.EncodeEntry(zapcore.Entry{
|
||||
Level: zapcore.InfoLevel,
|
||||
Time: time.Now(),
|
||||
LoggerName: t.name,
|
||||
Message: msg,
|
||||
Caller: zapcore.NewEntryCaller(runtime.Caller(2)),
|
||||
}, append(t.fields, fields...))
|
||||
|
||||
if err == nil {
|
||||
t.tb.Log(buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (t testLogger) Warn(msg string, fields ...zap.Field) {
|
||||
t.tb.Helper()
|
||||
buf, err := t.encoder.EncodeEntry(zapcore.Entry{
|
||||
Level: zapcore.WarnLevel,
|
||||
Time: time.Now(),
|
||||
LoggerName: t.name,
|
||||
Message: msg,
|
||||
Caller: zapcore.NewEntryCaller(runtime.Caller(2)),
|
||||
}, append(t.fields, fields...))
|
||||
|
||||
if err == nil {
|
||||
t.tb.Log(buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (t testLogger) Error(msg string, fields ...zap.Field) {
|
||||
t.tb.Helper()
|
||||
buf, err := t.encoder.EncodeEntry(zapcore.Entry{
|
||||
Level: zapcore.ErrorLevel,
|
||||
Time: time.Now(),
|
||||
LoggerName: t.name,
|
||||
Message: msg,
|
||||
Caller: zapcore.NewEntryCaller(runtime.Caller(2)),
|
||||
Stack: string(debug.Stack()),
|
||||
}, append(t.fields, fields...))
|
||||
|
||||
if err == nil {
|
||||
t.tb.Log(buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (t testLogger) Panic(msg string, fields ...zap.Field) {
|
||||
t.tb.Helper()
|
||||
buf, err := t.encoder.EncodeEntry(zapcore.Entry{
|
||||
Level: zapcore.PanicLevel,
|
||||
Time: time.Now(),
|
||||
LoggerName: t.name,
|
||||
Message: msg,
|
||||
Caller: zapcore.NewEntryCaller(runtime.Caller(2)),
|
||||
Stack: string(debug.Stack()),
|
||||
}, append(t.fields, fields...))
|
||||
|
||||
if err == nil {
|
||||
t.tb.Error(buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (t testLogger) Fatal(msg string, fields ...zap.Field) {
|
||||
t.tb.Helper()
|
||||
buf, err := t.encoder.EncodeEntry(zapcore.Entry{
|
||||
Level: zapcore.FatalLevel,
|
||||
Time: time.Now(),
|
||||
LoggerName: t.name,
|
||||
Message: msg,
|
||||
Caller: zapcore.NewEntryCaller(runtime.Caller(2)),
|
||||
Stack: string(debug.Stack()),
|
||||
}, append(t.fields, fields...))
|
||||
|
||||
if err == nil {
|
||||
t.tb.Error(buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (t testLogger) Sync() error {
|
||||
return nil
|
||||
}
|
14
pkg/wait/wg.go
Normal file
14
pkg/wait/wg.go
Normal file
|
@ -0,0 +1,14 @@
|
|||
package wait
|
||||
|
||||
import "sync"
|
||||
|
||||
func ForWaitGroupDone(wg *sync.WaitGroup) <-chan struct{} {
|
||||
done := make(chan struct{})
|
||||
|
||||
go func(wg *sync.WaitGroup) {
|
||||
wg.Wait()
|
||||
close(done)
|
||||
}(wg)
|
||||
|
||||
return done
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
package dns_mock
|
||||
|
||||
import (
|
||||
"github.com/miekg/dns"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type regexHandler struct {
|
||||
handlerName string
|
||||
routes []resolverRule
|
||||
fallback ResolverFallback
|
||||
logger logging.Logger
|
||||
}
|
||||
|
||||
func (rh *regexHandler) AddRule(rule resolverRule) {
|
||||
rh.routes = append(rh.routes, rule)
|
||||
}
|
||||
|
||||
func (rh regexHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
|
||||
timer := prometheus.NewTimer(requestDurationHistogram.WithLabelValues(rh.handlerName))
|
||||
defer func() {
|
||||
timer.ObserveDuration()
|
||||
}()
|
||||
|
||||
m := new(dns.Msg)
|
||||
m.Compress = false
|
||||
m.SetReply(r)
|
||||
|
||||
switch r.Opcode {
|
||||
case dns.OpcodeQuery:
|
||||
rh.handleQuery(m)
|
||||
}
|
||||
if err := w.WriteMsg(m); err != nil {
|
||||
rh.logger.Error(
|
||||
"Failed to write DNS response message",
|
||||
zap.Error(err),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (rh regexHandler) handleQuery(m *dns.Msg) {
|
||||
for _, q := range m.Question {
|
||||
rh.logger.Info(
|
||||
"handling question",
|
||||
zap.String("question", q.Name),
|
||||
)
|
||||
switch q.Qtype {
|
||||
case dns.TypeA:
|
||||
totalHandledRequestsCounter.WithLabelValues(rh.handlerName).Inc()
|
||||
for _, rule := range rh.routes {
|
||||
if rule.pattern.MatchString(q.Name) {
|
||||
m.Authoritative = true
|
||||
answer := &dns.A{
|
||||
Hdr: dns.RR_Header{
|
||||
Name: q.Name,
|
||||
Rrtype: dns.TypeA,
|
||||
Class: dns.ClassINET,
|
||||
Ttl: 60,
|
||||
},
|
||||
A: rule.response,
|
||||
}
|
||||
m.Answer = append(m.Answer, answer)
|
||||
rh.logger.Info(
|
||||
"matched DNS rule",
|
||||
zap.String("pattern", rule.pattern.String()),
|
||||
zap.String("response", rule.response.String()),
|
||||
)
|
||||
return
|
||||
}
|
||||
}
|
||||
rh.handleFallbackForMessage(m, q)
|
||||
default:
|
||||
unhandledRequestsCounter.WithLabelValues(rh.handlerName).Inc()
|
||||
rh.logger.Warn(
|
||||
"Unhandled DNS question type - no response will be sent",
|
||||
zap.Uint16("question_type", q.Qtype),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (rh regexHandler) handleFallbackForMessage(m *dns.Msg, q dns.Question) {
|
||||
fallbackIP := rh.fallback.GetIP()
|
||||
answer := &dns.A{
|
||||
Hdr: dns.RR_Header{
|
||||
Name: q.Name,
|
||||
Rrtype: dns.TypeA,
|
||||
Class: dns.ClassINET,
|
||||
Ttl: 60,
|
||||
},
|
||||
A: fallbackIP,
|
||||
}
|
||||
rh.logger.Info(
|
||||
"Falling back to generated IP",
|
||||
zap.String("response", fallbackIP.String()),
|
||||
)
|
||||
m.Authoritative = true
|
||||
m.Answer = append(m.Answer, answer)
|
||||
}
|
Loading…
Reference in a new issue