Add advanced matching options to HTTP handler

- move to Gitlab
- make code better testable
- create app abstraction for server
- cleanup
This commit is contained in:
Peter 2020-12-26 13:11:49 +00:00
parent 57a7e10e74
commit 49e58ac2e4
86 changed files with 1436 additions and 967 deletions

23
.editorconfig Normal file
View file

@ -0,0 +1,23 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = false
max_line_length = 120
tab_width = 4
[{*.bash, *.sh, *.zsh}]
indent_size = 2
tab_width = 2
[{*.go, *.go2}]
indent_style = tab
[{*.har, *.jsb2, *.jsb3, *.json, .babelrc, .eslintrc, .stylelintrc, bowerrc, jest.config}]
indent_size = 2
[{*.yaml, *.yml}]
indent_size = 2

View file

@ -21,7 +21,7 @@ jobs:
run: echo ${{ secrets.GITHUB_TOKEN }} | docker login docker.pkg.github.com -u baez90 --password-stdin run: echo ${{ secrets.GITHUB_TOKEN }} | docker login docker.pkg.github.com -u baez90 --password-stdin
- name: Build the Docker image - name: Build the Docker image
run: docker build . --file Dockerfile --tag $IMAGE_NAME run: docker build . --file inetmock.dockerfile --tag $IMAGE_NAME
- name: Push image to GitHub packages - name: Push image to GitHub packages
run: | run: |

View file

@ -1,6 +1,6 @@
name: Go name: Go
on: [push, pull_request] on: [ push, pull_request ]
jobs: jobs:
@ -9,42 +9,41 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Set up Go 1.14 - name: Set up Go 1.15
uses: actions/setup-go@v1 uses: actions/setup-go@v2
with: with:
go-version: 1.14.x go-version: '^1.15'
id: go id: go
- name: Install Protoc - name: Install Protoc
uses: arduino/setup-protoc@master uses: arduino/setup-protoc@master
with: with:
version: '3.x' version: '3.x'
- name: Check out code into the Go module directory - name: Check out code into the Go module directory
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:
lfs: true lfs: true
- name: Install mockgen - name: Install mockgen
run: go get -u github.com/golang/mock/mockgen@latest run: go get -u github.com/golang/mock/mockgen@latest
- name: Install go-enuum - name: Install go-enuum
run: go get -u github.com/abice/go-enum run: go get -u github.com/abice/go-enum
- name: Install protoc-gen-go - name: Install protoc-gen-go
run: go install github.com/golang/protobuf/protoc-gen-go run: go install github.com/golang/protobuf/protoc-gen-go
- name: Unshallow
run: git fetch --prune --unshallow
- name: Build & test - name: Unshallow
run: make run: git fetch --prune --unshallow
- name: Run GoReleaser - name: Build & test
uses: goreleaser/goreleaser-action@v1 run: make
with:
version: latest - name: Run GoReleaser
args: release --rm-dist uses: goreleaser/goreleaser-action@v2
key: ${{ secrets.YOUR_PRIVATE_KEY }} with:
env: version: latest
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

4
.gitignore vendored
View file

@ -11,6 +11,7 @@
/imctl /imctl
./main ./main
**/*.mock.go **/*.mock.go
**/*_enum.go
**/*.pb.go **/*.pb.go
############### ###############
@ -20,4 +21,5 @@
.idea/ .idea/
.vscode/ .vscode/
dist/ dist/
out/ out/
.task/

41
.gitlab-ci.yml Normal file
View file

@ -0,0 +1,41 @@
image: registry.gitlab.com/inetmock/ci-image
stages:
- test
- build
- release
test:
stage: test
script:
- task cli-cover-report
lint:
stage: test
script:
- golangci-lint run
allow_failure: true
snapshot-release:
stage: build
services:
- docker:dind
before_script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
script:
- task snapshot-release
except:
- tags
release:
stage: release
services:
- docker:dind
only:
- tags
variables:
GITLAB_TOKEN: $CI_JOB_TOKEN
GIT_DEPTH: 0
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- goreleaser release --rm-dist

136
.golangci.yml Normal file
View file

@ -0,0 +1,136 @@
linters-settings:
depguard:
list-type: blacklist
packages:
# logging is allowed only by logutils.Log, logrus
# is allowed to use only in logutils package
- github.com/sirupsen/logrus
packages-with-error-message:
- github.com/sirupsen/logrus: "logging is allowed only by logutils.Log"
dupl:
threshold: 100
funlen:
lines: 100
statements: 50
gci:
local-prefixes: gitlab.com/inetmock/inetmock
goconst:
min-len: 2
min-occurrences: 2
gocritic:
enabled-tags:
- diagnostic
- experimental
- opinionated
- performance
- style
disabled-checks:
- dupImport # https://github.com/go-critic/go-critic/issues/845
- ifElseChain
- octalLiteral
- whyNoLint
- wrapperFunc
gocyclo:
min-complexity: 15
goimports:
local-prefixes: gitlab.com/inetmock/inetmock
golint:
min-confidence: 0
gomnd:
settings:
mnd:
# don't include the "operation" and "assign"
checks: argument,case,condition,return
govet:
check-shadowing: true
lll:
line-length: 140
maligned:
suggest-new: true
misspell:
locale: US
nolintlint:
allow-leading-space: true # don't require machine-readable nolint directives (i.e. with no leading space)
allow-unused: false # report any unused nolint directives
require-explanation: false # don't require an explanation for nolint directives
require-specific: false # don't require nolint directives to be specific about which linter is being skipped
linters:
# please, do not use `enable-all`: it's deprecated and will be removed soon.
# inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint
disable-all: true
enable:
- bodyclose
- deadcode
- depguard
- dogsled
- dupl
- errcheck
- exhaustive
- funlen
- gochecknoinits
- goconst
- gocritic
- gocyclo
- gofmt
- goimports
- golint
- gomnd
- goprintffuncname
- gosec
- gosimple
- govet
- ineffassign
- interfacer
- lll
- misspell
- nakedret
- noctx
- nolintlint
- rowserrcheck
- scopelint
- staticcheck
- structcheck
- stylecheck
- typecheck
- unconvert
- unparam
- unused
- varcheck
- whitespace
# don't enable:
# - asciicheck
# - gochecknoglobals
# - gocognit
# - godot
# - godox
# - goerr113
# - maligned
# - nestif
# - prealloc
# - testpackage
# - wsl
issues:
# Excluding configuration per-path, per-linter, per-text and per-source
exclude-rules:
- path: _test\.go
linters:
- gomnd
# https://github.com/go-critic/go-critic/issues/926
- linters:
- gocritic
text: "unnecessaryDefer:"
run:
skip-dirs:
- internal/mock
# golangci.com configuration
# https://github.com/golangci/golangci/wiki/Configuration
service:
golangci-lint-version: 1.23.x # use the fixed version to not introduce new linters unexpectedly
prepare:
- echo "here I can run custom commands, but no preparation needed for this repo"

View file

@ -3,7 +3,7 @@
before: before:
hooks: hooks:
# You may remove this if you don't use go modules. # You may remove this if you don't use go modules.
- make generate - task generate
builds: builds:
- id: "inetmock" - id: "inetmock"
binary: inetmock binary: inetmock
@ -11,12 +11,12 @@ builds:
ldflags: ldflags:
- -w -s - -w -s
env: env:
- CGO_ENABLED=0 - CGO_ENABLED=0
goos: goos:
- linux - linux
- darwin - darwin
goarch: goarch:
- amd64 - amd64
- id: "imctl" - id: "imctl"
binary: imctl binary: imctl
main: ./cmd/imctl/main.go main: ./cmd/imctl/main.go
@ -32,7 +32,7 @@ builds:
archives: archives:
- id: inetmock - id: inetmock
builds: builds:
- inetmock - inetmock
name_template: "{{ .ProjectName }}_server_{{ .Version }}_{{ .Os }}_{{ .Arch }}" name_template: "{{ .ProjectName }}_server_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
replacements: replacements:
amd64: x86_64 amd64: x86_64
@ -47,7 +47,7 @@ archives:
replacements: replacements:
amd64: x86_64 amd64: x86_64
wrap_in_directory: true wrap_in_directory: true
files: [] files: [ ]
checksum: checksum:
name_template: 'checksums.txt' name_template: 'checksums.txt'
snapshot: snapshot:
@ -56,5 +56,29 @@ changelog:
sort: asc sort: asc
filters: filters:
exclude: exclude:
- '^docs:' - '^docs:'
- '^test:' - '^test:'
release:
gitlab:
owner: inetmock
name: inetmock
dockers:
- binaries:
- inetmock
- imctl
image_templates:
- registry.gitlab.com/inetmock/inetmock:latest
- registry.gitlab.com/inetmock/inetmock:{{ .Tag }}
- registry.gitlab.com/inetmock/inetmock:{{ .Major }}
dockerfile: build/docker/inetmock.dockerfile
build_flag_templates:
- "--label=org.opencontainers.image.created={{.Date}}"
- "--label=org.opencontainers.image.title={{.ProjectName}}"
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
- "--label=org.opencontainers.image.version={{.Version}}"
extra_files:
- config-container.yaml
- assets/fakeFiles/

View file

@ -1,81 +0,0 @@
VERSION = $(shell git describe --dirty --tags --always)
DIR = $(dir $(realpath $(firstword $(MAKEFILE_LIST))))
SERVER_BUILD_PATH = github.com/baez90/inetmock/cmd/inetmock
CLI_BUILD_PATH = github.com/baez90/inetmock/cmd/imctl
PKGS = $(shell go list ./...)
TEST_PKGS = $(shell go list ./...)
PROTO_FILES = $(shell find $(DIR)api/ -type f -name "*.proto")
GOARGS = GOOS=linux GOARCH=amd64
GO_BUILD_ARGS = -ldflags='-w -s'
GO_CONTAINER_BUILD_ARGS = -ldflags='-w -s' -a -installsuffix cgo
GO_DEBUG_BUILD_ARGS = -gcflags "all=-N -l"
SERVER_BINARY_NAME = inetmock
CLI_BINARY_NAME = imctl
DEBUG_PORT = 2345
DEBUG_ARGS?= serve --development-logs=true
CONTAINER_BUILDER ?= podman
DOCKER_IMAGE ?= inetmock
.PHONY: clean all format deps update-deps compile compile-server compile-cli debug generate protoc snapshot-release test cli-cover-report html-cover-report $(GO_GEN_FILES)
all: clean format generate compile test
clean:
@find $(DIR) -type f \( -name "*.out" -or -name "*.so" \) -exec rm -f {} \;
@rm -rf $(DIR)*.so
@find $(DIR) -type f -name "*.pb.go" -exec rm -f {} \;
@find $(DIR) -type f -name "*.mock.go" -exec rm -f {} \;
@rm -f $(DIR)$(SERVER_BINARY_NAME) $(DIR)$(CLI_BINARY_NAME) $(DIR)main
format:
@go fmt $(PKGS)
deps:
@go build -v $(SERVER_BUILD_PATH)
update-deps:
@go mod tidy
@go get -u $(DIR)/...
compile-server: deps
ifdef DEBUG
@echo 'Compiling for debugging...'
@$(GOARGS) go build $(GO_DEBUG_BUILD_ARGS) -o $(DIR)$(SERVER_BINARY_NAME) $(SERVER_BUILD_PATH)
else ifdef CONTAINER
@echo 'Compiling for container usage...'
@$(GOARGS) go build $(GO_CONTAINER_BUILD_ARGS) -o $(DIR)$(SERVER_BINARY_NAME) $(SERVER_BUILD_PATH)
else
@echo 'Compiling for normal Linux env...'
@$(GOARGS) go build $(GO_BUILD_ARGS) -o $(DIR)$(SERVER_BINARY_NAME) $(SERVER_BUILD_PATH)
endif
compile-cli: deps
@$(GOARGS) go build $(GO_BUILD_ARGS) -o $(CLI_BINARY_NAME) $(CLI_BUILD_PATH)
compile: compile-server compile-cli
debug:
dlv debug $(SERVER_BUILD_PATH) \
--headless \
--listen=:2345 \
--api-version=2 \
-- $(DEBUG_ARGS)
generate:
@go generate ./...
@protoc --proto_path $(DIR)api/ --go_out=plugins=grpc:internal/rpc --go_opt=paths=source_relative $(shell find $(DIR)api/ -type f -name "*.proto")
snapshot-release:
@goreleaser release --snapshot --skip-publish --rm-dist
container:
@$(CONTAINER_BUILDER) build -t $(DOCKER_IMAGE):latest -f $(DIR)Dockerfile $(DIR)
test:
@go test -coverprofile=./cov-raw.out -v $(TEST_PKGS)
@cat ./cov-raw.out | grep -v "generated" > ./cov.out
cli-cover-report: test
@go tool cover -func=cov.out
html-cover-report: test
@go tool cover -html=cov.out -o .coverage.html

View file

@ -1,7 +1,7 @@
# INetMock # INetMock
![Go](https://github.com/baez90/inetmock/workflows/Go/badge.svg) ![Go](https://gitlab.com/inetmock/inetmock/workflows/Go/badge.svg)
[![Go Report Card](https://goreportcard.com/badge/github.com/baez90/inetmock)](https://goreportcard.com/report/github.com/baez90/inetmock) [![Go Report Card](https://goreportcard.com/badge/gitlab.com/inetmock/inetmock)](https://goreportcard.com/report/gitlab.com/inetmock/inetmock)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=baez90_inetmock&metric=alert_status)](https://sonarcloud.io/dashboard?id=baez90_inetmock) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=baez90_inetmock&metric=alert_status)](https://sonarcloud.io/dashboard?id=baez90_inetmock)
[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=baez90_inetmock&metric=ncloc)](https://sonarcloud.io/dashboard?id=baez90_inetmock) [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=baez90_inetmock&metric=ncloc)](https://sonarcloud.io/dashboard?id=baez90_inetmock)
[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=baez90_inetmock&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=baez90_inetmock) [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=baez90_inetmock&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=baez90_inetmock)
@ -12,22 +12,26 @@
INetMock is kind of a fork of [INetSim](https://www.inetsim.org/). INetMock is kind of a fork of [INetSim](https://www.inetsim.org/).
"Kind of" in terms that both applications overlap in their functionality to serve as "fake internet" routers. "Kind of" in terms that both applications overlap in their functionality to serve as "fake internet" routers.
INetMock right now does **not** implement so many protocols like INetSim. INetMock right now does **not** implement so many protocols like INetSim. In fact it is only able to respond to HTTP,
In fact it is only able to respond to HTTP, HTTPS, DNS, DNS-over-TLS (DoT) requests and to act as an HTTP proxy. HTTPS, DNS, DNS-over-TLS (DoT) requests and to act as an HTTP proxy. The most notable advantage of INetMOck over INetSim
The most notable advantage of INetMOck over INetSim is that it issues proper TLS certificates on the fly signed by a CA certificate that can be deployed to client systems to achieve "proper" TLS encryption - as long as the client does not use certificate pinning or something similar. is that it issues proper TLS certificates on the fly signed by a CA certificate that can be deployed to client systems
to achieve "proper" TLS encryption - as long as the client does not use certificate pinning or something similar.
A second advantage is that INetMock is a complete rewrite in Go, based on a plugin system that allows dynamic configuration while it has a way smaller memory footprint and far better startup and shutdown times. A second advantage is that INetMock is a complete rewrite in Go, based on a plugin system that allows dynamic
It also does not enforce `root` privileges as it is also possible to run the application with the required capabilities to open ports e.g. with SystemD (a sample unit file can be found in the `deploy/` directory). configuration while it has a way smaller memory footprint and far better startup and shutdown times. It also does not
enforce `root` privileges as it is also possible to run the application with the required capabilities to open ports
e.g. with SystemD (a sample unit file can be found in the `deploy/` directory).
_This project is still heavy work-in-progress. There may be breaking changes at any time. There's no guarantee for anything except no kittens will be harmed!_ _This project is still heavy work-in-progress. There may be breaking changes at any time. There's no guarantee for
anything except no kittens will be harmed!_
## Docs ## Docs
Docs are available either in the [`docs/`](./docs/) directory or as rendered markdown book at the [GitHub pages](https://baez90.github.io/inetmock/). Docs are available either in the [`docs/`](./docs/) directory or as rendered markdown book at
the [GitHub pages](https://baez90.github.io/inetmock/).
## Contribution/feature requests ## Contribution/feature requests
Please create an issue for any proposal, feature requests, found bug,... Please create an issue for any proposal, feature requests, found bug,... I'm glad for every kind of feedback!
I'm glad for every kind of feedback!
Right now I've no special workflow for pull requests but I will look into every proposed change. Right now I've no special workflow for pull requests but I will look into every proposed change.

98
Taskfile.yml Normal file
View file

@ -0,0 +1,98 @@
version: '3'
vars:
OUT_DIR: ./out
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 "
env:
GOOS: linux
GOARCH: amd64
CGO_ENABLED: 0
tasks:
clean:
cmds:
- find . -type f \( -name "*.pb.go" -or -name "*.mock.go" \) -exec rm -f {} \;
- rm -rf ./main {{ .OUT_DIR }}
format:
cmds:
- go fmt ./...
protoc:
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 }}
go-generate:
sources:
- "**/*.go"
cmds:
- go generate -x ./...
generate:
deps:
- go-generate
- protoc
test:
sources:
- "**/*.go"
deps:
- generate
cmds:
- mkdir -p {{ .OUT_DIR }}
- go test -coverprofile={{ .OUT_DIR }}/cov-raw.out -v ./...
- grep -v "generated" {{ .OUT_DIR }}/cov-raw.out > {{ .OUT_DIR }}/cov.out
- rm -f {{ .OUT_DIR }}/cov-raw.out
cli-cover-report:
deps:
- test
cmds:
- go tool cover -func={{ .OUT_DIR }}/cov.out
html-cover-report:
deps:
- test
cmds:
- go tool cover -html={{ .OUT_DIR }}/cov.out -o {{ .OUT_DIR }}/coverage.html
build-inetmock:
deps:
- test
cmds:
- mkdir -p {{ .OUT_DIR }}
- go build -ldflags='-w -s' -o {{ .OUT_DIR }}/inetmock {{ .INETMOCK_PKG }}
debug-inetmock:
cmds:
- dlv --listen=:2345 --headless=true --api-version=2 --accept-multiclient --output {{ .OUT_DIR }}/__debug_bin debug {{ .INETMOCK_PKG }} -- serve
build-imctl:
deps:
- test
cmds:
- mkdir -p {{ .OUT_DIR }}
- go build -ldflags='-w -s' -o {{ .OUT_DIR }}/imctl {{ .IMCTL_PKG }}
build-all:
deps:
- build-inetmock
- build-imctl
snapshot-release:
deps:
- test
cmds:
- goreleaser release --snapshot --skip-publish --rm-dist
release:
deps:
- test
cmds:
- goreleaser release

View file

@ -1,6 +1,6 @@
syntax = "proto3"; syntax = "proto3";
option go_package = "github.com/baez90/inetmock/internal/rpc"; option go_package = "gitlab.com/inetmock/inetmock/internal/rpc";
option java_multiple_files = true; option java_multiple_files = true;
option java_package = "com.github.baez90.inetmock.rpc"; option java_package = "com.github.baez90.inetmock.rpc";
option java_outer_classname = "EndpointsProto"; option java_outer_classname = "EndpointsProto";

View file

@ -1,6 +1,6 @@
syntax = "proto3"; syntax = "proto3";
option go_package = "github.com/baez90/inetmock/internal/rpc"; option go_package = "gitlab.com/inetmock/inetmock/internal/rpc";
option java_multiple_files = true; option java_multiple_files = true;
option java_package = "com.github.baez90.inetmock.rpc"; option java_package = "com.github.baez90.inetmock.rpc";
option java_outer_classname = "HandlersProto"; option java_outer_classname = "HandlersProto";
@ -8,7 +8,8 @@ option java_outer_classname = "HandlersProto";
package inetmock; package inetmock;
service Handlers { service Handlers {
rpc GetHandlers(GetHandlersRequest) returns (GetHandlersResponse) {} rpc GetHandlers (GetHandlersRequest) returns (GetHandlersResponse) {
}
} }
message GetHandlersRequest { message GetHandlersRequest {

View file

@ -1,6 +1,6 @@
syntax = "proto3"; syntax = "proto3";
option go_package = "github.com/baez90/inetmock/internal/rpc"; option go_package = "gitlab.com/inetmock/inetmock/internal/rpc";
option java_multiple_files = true; option java_multiple_files = true;
option java_package = "com.github.baez90.inetmock.rpc"; option java_package = "com.github.baez90.inetmock.rpc";
option java_outer_classname = "HealthProto"; option java_outer_classname = "HealthProto";
@ -8,7 +8,8 @@ option java_outer_classname = "HealthProto";
package inetmock; package inetmock;
service Health { service Health {
rpc GetHealth(HealthRequest) returns (HealthResponse) {} rpc GetHealth (HealthRequest) returns (HealthResponse) {
}
} }
enum HealthState { enum HealthState {

View file

@ -1,4 +1,5 @@
FROM golang:1.15-alpine as build # Runtime layer
FROM alpine:3.12
# Create appuser and group. # Create appuser and group.
ARG USER=inetmock ARG USER=inetmock
@ -6,13 +7,7 @@ ARG GROUP=inetmock
ARG USER_ID=10001 ARG USER_ID=10001
ARG GROUP_ID=10001 ARG GROUP_ID=10001
ENV CGO_ENABLED=0 RUN addgroup -S -g "${GROUP_ID}" "${GROUP}" && \
# Prepare build stage - can be cached
WORKDIR /work
RUN apk add -U --no-cache \
make protoc gcc musl-dev && \
addgroup -S -g "${GROUP_ID}" "${GROUP}" && \
adduser \ adduser \
--disabled-password \ --disabled-password \
--gecos "" \ --gecos "" \
@ -23,31 +18,8 @@ RUN apk add -U --no-cache \
--uid "${USER_ID}" \ --uid "${USER_ID}" \
"${USER}" "${USER}"
# Fetch dependencies COPY --chown=$USER:$GROUP inetmock imctl /usr/lib/inetmock/bin/
COPY Makefile go.mod go.sum ./ COPY --chown=$USER:$GROUP assets/fakeFiles /var/lib/inetmock/fakeFiles/
RUN go mod download && \
go get -u github.com/golang/mock/mockgen@latest && \
go get -u github.com/abice/go-enum && \
go install github.com/golang/protobuf/protoc-gen-go
COPY ./ ./
# Build binaries
RUN make CONTAINER=yes
# Runtime layer
FROM alpine:3.12
# Create appuser and group.
ARG USER=inetmock
ARG GROUP=inetmock
ARG USER_ID=10001
ARG GROUP_ID=10001
COPY --from=build /etc/group /etc/passwd /etc/
COPY --from=build --chown=$USER:$GROUP /work/inetmock /work/imctl /usr/lib/inetmock/bin/
COPY --chown=$USER:$GROUP ./assets/fakeFiles/ /var/lib/inetmock/fakeFiles/
COPY config-container.yaml /etc/inetmock/config.yaml COPY config-container.yaml /etc/inetmock/config.yaml
RUN mkdir -p /var/run/inetmock /var/lib/inetmock/certs /usr/lib/inetmock && \ RUN mkdir -p /var/run/inetmock /var/lib/inetmock/certs /usr/lib/inetmock && \

View file

@ -1,6 +1,6 @@
package main package main
import "github.com/baez90/inetmock/internal/cmd" import "gitlab.com/inetmock/inetmock/internal/cmd"
func main() { func main() {
if err := cmd.ExecuteClientCommand(); err != nil { if err := cmd.ExecuteClientCommand(); err != nil {

View file

@ -1,25 +1,24 @@
package main package main
import ( import (
"github.com/baez90/inetmock/internal/cmd" "fmt"
"go.uber.org/zap"
"os"
_ "github.com/baez90/inetmock/plugins/dns_mock" "gitlab.com/inetmock/inetmock/internal/cmd"
_ "github.com/baez90/inetmock/plugins/http_mock" _ "gitlab.com/inetmock/inetmock/plugins/dns_mock"
_ "github.com/baez90/inetmock/plugins/http_proxy" _ "gitlab.com/inetmock/inetmock/plugins/http_mock"
_ "github.com/baez90/inetmock/plugins/metrics_exporter" _ "gitlab.com/inetmock/inetmock/plugins/http_proxy"
_ "github.com/baez90/inetmock/plugins/tls_interceptor" _ "gitlab.com/inetmock/inetmock/plugins/metrics_exporter"
_ "gitlab.com/inetmock/inetmock/plugins/tls_interceptor"
"go.uber.org/zap"
) )
func main() { func main() {
logger, _ := zap.NewProduction() logger, _ := zap.NewProduction()
defer logger.Sync() defer func() {
if err := logger.Sync(); err != nil {
fmt.Printf(err.Error())
}
}()
if err := cmd.ExecuteServerCommand(); err != nil { cmd.ExecuteServerCommand()
logger.Error("Failed to run inetmock",
zap.Error(err),
)
os.Exit(1)
}
} }

View file

@ -39,15 +39,15 @@ endpoints:
handler: http_mock handler: http_mock
listenAddress: 0.0.0.0 listenAddress: 0.0.0.0
ports: ports:
- 80 - 80
- 8080 - 8080
options: options:
<<: *httpResponseRules <<: *httpResponseRules
proxy: proxy:
handler: http_proxy handler: http_proxy
listenAddress: 0.0.0.0 listenAddress: 0.0.0.0
ports: ports:
- 3128 - 3128
options: options:
target: target:
ipAddress: 127.0.0.1 ipAddress: 127.0.0.1
@ -56,8 +56,8 @@ endpoints:
handler: tls_interceptor handler: tls_interceptor
listenAddress: 0.0.0.0 listenAddress: 0.0.0.0
ports: ports:
- 443 - 443
- 8443 - 8443
options: options:
target: target:
ipAddress: 127.0.0.1 ipAddress: 127.0.0.1
@ -66,7 +66,7 @@ endpoints:
handler: dns_mock handler: dns_mock
listenAddress: 0.0.0.0 listenAddress: 0.0.0.0
ports: ports:
- 53 - 53
options: options:
rules: rules:
- pattern: ".*\\.google\\.com" - pattern: ".*\\.google\\.com"
@ -81,7 +81,7 @@ endpoints:
handler: tls_interceptor handler: tls_interceptor
listenAddress: 0.0.0.0 listenAddress: 0.0.0.0
ports: ports:
- 853 - 853
options: options:
target: target:
ipAddress: 127.0.0.1 ipAddress: 127.0.0.1

View file

@ -1,18 +1,44 @@
x-response-rules: &httpResponseRules x-response-rules: &httpResponseRules
rules: rules:
- pattern: ".*\\.(?i)exe" - pattern: ".*\\.(?i)exe"
matcher: Path
- pattern: "^application/octet-stream$"
target: Accept
matcher: Header
response: ./assets/fakeFiles/sample.exe response: ./assets/fakeFiles/sample.exe
- pattern: ".*\\.(?i)(jpg|jpeg)" - pattern: "^image/jpeg$"
target: Accept
matcher: Header
response: ./assets/fakeFiles/default.jpg response: ./assets/fakeFiles/default.jpg
- pattern: ".*\\.(?i)(jpg|jpeg)"
matcher: Path
response: ./assets/fakeFiles/default.jpg
- pattern: "^image/png$"
target: Accept
matcher: Header
response: ./assets/fakeFiles/default.png
- pattern: ".*\\.(?i)png" - pattern: ".*\\.(?i)png"
matcher: Path
response: ./assets/fakeFiles/default.png response: ./assets/fakeFiles/default.png
- pattern: ".*\\.(?i)gif" - pattern: ".*\\.(?i)gif"
matcher: Path
response: ./assets/fakeFiles/default.gif response: ./assets/fakeFiles/default.gif
- pattern: ".*\\.(?i)ico" - pattern: ".*\\.(?i)ico"
matcher: Path
response: ./assets/fakeFiles/default.ico response: ./assets/fakeFiles/default.ico
- pattern: ".*\\.(?i)txt" - pattern: "^text/plain$"
target: Accept
matcher: Header
response: ./assets/fakeFiles/default.txt response: ./assets/fakeFiles/default.txt
- pattern: ".*\\.(?i)txt"
matcher: Path
response: ./assets/fakeFiles/default.txt
- pattern: "^text/html$"
target: Accept
matcher: Header
response: ./assets/fakeFiles/default.html
- pattern: ".*" - pattern: ".*"
matcher: Path
response: ./assets/fakeFiles/default.html response: ./assets/fakeFiles/default.html
api: api:
@ -39,15 +65,15 @@ endpoints:
handler: http_mock handler: http_mock
listenAddress: 0.0.0.0 listenAddress: 0.0.0.0
ports: ports:
- 80 - 80
- 8080 - 8080
options: options:
<<: *httpResponseRules <<: *httpResponseRules
proxy: proxy:
handler: http_proxy handler: http_proxy
listenAddress: 0.0.0.0 listenAddress: 0.0.0.0
ports: ports:
- 3128 - 3128
options: options:
target: target:
ipAddress: 127.0.0.1 ipAddress: 127.0.0.1
@ -56,8 +82,8 @@ endpoints:
handler: tls_interceptor handler: tls_interceptor
listenAddress: 0.0.0.0 listenAddress: 0.0.0.0
ports: ports:
- 443 - 443
- 8443 - 8443
options: options:
target: target:
ipAddress: 127.0.0.1 ipAddress: 127.0.0.1
@ -66,7 +92,7 @@ endpoints:
handler: dns_mock handler: dns_mock
listenAddress: 0.0.0.0 listenAddress: 0.0.0.0
ports: ports:
- 53 - 53
options: options:
rules: rules:
- pattern: ".*\\.google\\.com" - pattern: ".*\\.google\\.com"
@ -81,7 +107,7 @@ endpoints:
handler: tls_interceptor handler: tls_interceptor
listenAddress: 0.0.0.0 listenAddress: 0.0.0.0
ports: ports:
- 853 - 853
options: options:
target: target:
ipAddress: 127.0.0.1 ipAddress: 127.0.0.1

View file

@ -2,8 +2,8 @@
## Plugins & handlers ## Plugins & handlers
_INetMock_ is based on plugins that ship one or more __protocol handlers__. _INetMock_ is based on plugins that ship one or more __protocol handlers__. Examples for protocol handlers are HTTP or
Examples for protocol handlers are HTTP or DNS but also TLS. DNS but also TLS.
The application ships with the following handlers: The application ships with the following handlers:
@ -11,9 +11,11 @@ The application ships with the following handlers:
* `dns_mock` * `dns_mock`
* `tls_interceptor` * `tls_interceptor`
The configuration of an so called endpoint always specifies which handler should be used, which IP address and port it should listen on and some handler specific `options`. The configuration of an so called endpoint always specifies which handler should be used, which IP address and port it
This way the whole system is very flexible and can be configured for various individual scenarios. should listen on and some handler specific `options`. This way the whole system is very flexible and can be configured
for various individual scenarios.
## Commands ## Commands
Beside of __protocol handlers__ a plugin can also ship custom commands e.g. the `tls_interceptor` ships a `generate-ca` command to bootstrap a certificate authority key-pair that can be reused for multiple instances. Beside of __protocol handlers__ a plugin can also ship custom commands e.g. the `tls_interceptor` ships a `generate-ca`
command to bootstrap a certificate authority key-pair that can be reused for multiple instances.

View file

@ -3,12 +3,12 @@
## Intro ## Intro
The `dns_mock` handler expects an array of rules how it should respond to dfferent DNS queries and a fallback strategy. The `dns_mock` handler expects an array of rules how it should respond to dfferent DNS queries and a fallback strategy.
Currently only queries for __A__ records are supported. Currently only queries for __A__ records are supported. The rules are primarily meant to define some exceptions or well
The rules are primarily meant to define some exceptions or well known DNS responses e.g. to return to right Google DNS IP but for everything else it will return dummy IPs. known DNS responses e.g. to return to right Google DNS IP but for everything else it will return dummy IPs.
The rules for the `dns_mock` handler are equivalent to the `http_mock` rules. The rules for the `dns_mock` handler are equivalent to the `http_mock` rules. Every rule consists of a `pattern` that
Every rule consists of a `pattern` that specifies a query name e.g. a single host, a wildcard domain, a wildcard top-level domain or even a _"match all"_ rule is possible. specifies a query name e.g. a single host, a wildcard domain, a wildcard top-level domain or even a _"match all"_ rule
These rules are evaluated in the same order they are defined in the `config.yaml`. is possible. These rules are evaluated in the same order they are defined in the `config.yaml`.
The fallback strategy is taken into account whenever a query does not match a rule. The fallback strategy is taken into account whenever a query does not match a rule.
@ -17,18 +17,19 @@ Right now the following fallback strategies are available:
* _random_ * _random_
* _incremental_ * _incremental_
Just like the handler is configured via the `options` object the fallback strategies are configured via an `args` object. Just like the handler is configured via the `options` object the fallback strategies are configured via an `args`
object.
### _random_ fallback ### _random_ fallback
The _random_ fallback strategy is the easier one of the both. The _random_ fallback strategy is the easier one of the both. It doesn't take any argument and it just shuffles a random
It doesn't take any argument and it just shuffles a random IP address for every request no matter if it was already asked for this IP or not. IP address for every request no matter if it was already asked for this IP or not.
### _incremental_ fallback ### _incremental_ fallback
The _incremental_ fallback is little bit more intelligent. The _incremental_ fallback is little bit more intelligent. It takes a `startIP` as an argument which defines from which
It takes a `startIP` as an argument which defines from which IP address the strategy starts counting up to respond to DNS queries. IP address the strategy starts counting up to respond to DNS queries. Just like the _incremental_ strategy it is _
Just like the _incremental_ strategy it is _stateless_ and does not store any already given response for later reuse (at least for now). stateless_ and does not store any already given response for later reuse (at least for now).
## Configuration ## Configuration
@ -50,8 +51,9 @@ endpoints:
### Matching a whole domain ### Matching a whole domain
While matching a single host is nice2have it's not very helpful in most cases - except for some edge cases where it might be necesary to specifically return a certain IP address. While matching a single host is nice2have it's not very helpful in most cases - except for some edge cases where it
But it's also possible to match a whole domain no matter what subdomain or sub-subdomain or whatever is requested like this: might be necesary to specifically return a certain IP address. But it's also possible to match a whole domain no matter
what subdomain or sub-subdomain or whatever is requested like this:
```yml ```yml
endpoints: endpoints:
@ -67,8 +69,8 @@ endpoints:
### Matching a whole TLD ### Matching a whole TLD
In some cases it might also be interesting to distinguish between different requested TLDs. In some cases it might also be interesting to distinguish between different requested TLDs. Therefore it might be
Therefore it might be interesting to define one IP address to resolve to for every TLD that should be distinguishable. interesting to define one IP address to resolve to for every TLD that should be distinguishable.
```yml ```yml
endpoints: endpoints:
@ -84,8 +86,8 @@ endpoints:
### Matching any query ### Matching any query
Last but not least it is obvously also possible to match any query. Last but not least it is obvously also possible to match any query. This is comparable to a _"static"_ fallback strategy
This is comparable to a _"static"_ fallback strategy in cases where different IP addresses are not necessary but the network setup should be as easy as possible. in cases where different IP addresses are not necessary but the network setup should be as easy as possible.
```yml ```yml
endpoints: endpoints:
@ -103,9 +105,9 @@ endpoints:
#### _random_ #### _random_
Like previously mentioned the _random_ strategy is easy as it can be. Like previously mentioned the _random_ strategy is easy as it can be. It just takes a random unsigned integer of 32
It just takes a random unsigned integer of 32 bits, converts it to an IP address and returns this address as response. bits, converts it to an IP address and returns this address as response. Therefore no further configuration is necessary
Therefore no further configuration is necessary for now. for now.
```yml ```yml
endpoints: endpoints:
@ -121,9 +123,9 @@ endpoints:
#### _incremental_ #### _incremental_
Also like previously mentioned the _incremental_ fallback strategy is fairly easy to setup. Also like previously mentioned the _incremental_ fallback strategy is fairly easy to setup. It just takes a `startIP` as
It just takes a `startIP` as argument which is used to count upwards. argument which is used to count upwards. It does __not__ check for an interval or something like this right now so a
It does __not__ check for an interval or something like this right now so a overflow might occur. overflow might occur.
```yml ```yml
endpoints: endpoints:

View file

@ -2,10 +2,12 @@
## Intro ## Intro
The `http_mock` handler expects an array of rules how it should respond to different request paths. The `http_mock` handler expects an array of rules how it should respond to different request paths. This allows to e.g.
This allows to e.g. return an image if the request path contains something like _"asdf.jpg"_ but with binary if the request path contains something like _"malicous.exe"_. return an image if the request path contains something like _"asdf.jpg"_ but with binary if the request path contains
something like _"malicous.exe"_.
A _"catch all"_ rule could return in any case an HTML page or if nothing is provided the handler returns an HTTP 404 status code. A _"catch all"_ rule could return in any case an HTML page or if nothing is provided the handler returns an HTTP 404
status code.
The rules are taken into account in the same order than they are defined in the `config.yaml`. The rules are taken into account in the same order than they are defined in the `config.yaml`.
@ -33,8 +35,8 @@ endpoints:
### Matching a file extensions ### Matching a file extensions
While matching a static path might be nice as an example it's not very useful. While matching a static path might be nice as an example it's not very useful. Returning a given file for all kinds of
Returning a given file for all kinds of of request paths based on the requested file extension is way more handy: of request paths based on the requested file extension is way more handy:
```yml ```yml
endpoints: endpoints:
@ -62,7 +64,8 @@ endpoints:
response: ./assets/fakeFiles/default.jpg response: ./assets/fakeFiles/default.jpg
``` ```
This way the extension ignores any case and matches both `.jpg` and `.jpeg` (and of course also e.g. `.JpEg` and so on and so forth). This way the extension ignores any case and matches both `.jpg` and `.jpeg` (and of course also e.g. `.JpEg` and so on
and so forth).
The default `config.yaml` already ships with some basic rules to handle the most common file extensions. The default `config.yaml` already ships with some basic rules to handle the most common file extensions.

View file

@ -1,14 +1,15 @@
# `config.yaml` # `config.yaml`
## Intro ## Intro
The configuration of _INetMock_ is mostly done in the `config.yaml`. The configuration of _INetMock_ is mostly done in the `config.yaml`. It defines which endpoints should be started with
It defines which endpoints should be started with which handler and a few more things. which handler and a few more things.
Every endpoint has a name that is used for logging and as already mentioned consists of listening IP and port, the handler and its options. Every endpoint has a name that is used for logging and as already mentioned consists of listening IP and port, the
handler and its options.
INetMock comes with _"Batteries included"_ and ships with a basic `config.yaml` that defines a basic set of endpoints for: INetMock comes with _"Batteries included"_ and ships with a basic `config.yaml` that defines a basic set of endpoints
for:
* HTTP * HTTP
* HTTPS * HTTPS

8
go.mod
View file

@ -1,11 +1,11 @@
module github.com/baez90/inetmock module gitlab.com/inetmock/inetmock
go 1.15 go 1.15
require ( require (
github.com/golang/mock v1.4.4 github.com/golang/mock v1.4.4
github.com/golang/protobuf v1.4.2 github.com/golang/protobuf v1.4.2
github.com/google/uuid v1.1.1 github.com/google/uuid v1.1.2
github.com/miekg/dns v1.1.31 github.com/miekg/dns v1.1.31
github.com/olekukonko/tablewriter v0.0.4 github.com/olekukonko/tablewriter v0.0.4
github.com/prometheus/client_golang v1.7.1 github.com/prometheus/client_golang v1.7.1
@ -13,8 +13,8 @@ require (
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.7.1 github.com/spf13/viper v1.7.1
go.uber.org/zap v1.16.0 go.uber.org/zap v1.16.0
google.golang.org/grpc v1.29.1 google.golang.org/grpc v1.34.0
google.golang.org/protobuf v1.23.0 google.golang.org/protobuf v1.25.0
gopkg.in/elazarl/goproxy.v1 v1.0.0-20180725130230-947c36da3153 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-20200615113413-eeeca48fe776
) )

88
go.sum
View file

@ -37,6 +37,7 @@ github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 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-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/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 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.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
@ -50,19 +51,19 @@ 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/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/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/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/elazarl/goproxy v0.0.0-20200809112317-0581fc3aee2d h1:rtM8HsT3NG37YPjz8sYSbUSdElP9lUsQENYzJDZDUBE= github.com/elazarl/goproxy v0.0.0-20201021153353-00ad82a08272 h1:Am81SElhR3XCQBunTisljzNkNese2T1FiV8jP79+dqg=
github.com/elazarl/goproxy v0.0.0-20200809112317-0581fc3aee2d/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= 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-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
github.com/elazarl/goproxy/ext v0.0.0-20200809112317-0581fc3aee2d h1:st1tmvy+4duoRj+RaeeJoECWCWM015fBtf/4aR+hhqk= github.com/elazarl/goproxy/ext v0.0.0-20201021153353-00ad82a08272 h1:xOHQWEGsftkjjFV2KIrC9vOz+iOinvZ7H6EAjSznqUk=
github.com/elazarl/goproxy/ext v0.0.0-20200809112317-0581fc3aee2d/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= github.com/elazarl/goproxy/ext v0.0.0-20201021153353-00ad82a08272/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 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.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 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/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= 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.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/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-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.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
@ -77,8 +78,6 @@ github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4er
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 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.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -90,8 +89,11 @@ github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:x
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= 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.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/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 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/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@ -99,6 +101,8 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@ -106,6 +110,8 @@ github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OI
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 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 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/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/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 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/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 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
@ -148,7 +154,6 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 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/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.1/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/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 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@ -158,14 +163,10 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 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 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 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-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.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54= 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.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 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 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA=
@ -181,8 +182,6 @@ 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 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 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8=
github.com/mitchellh/mapstructure v1.3.3/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-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/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 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
@ -194,12 +193,9 @@ github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FW
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= 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.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/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
@ -245,24 +241,16 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 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 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.4.0 h1:jsLTaI1zwYO3vjrzHalkVcIHXTNmdQFepW4OI8H3+x8=
github.com/spf13/afero v1.4.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= 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.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 v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= 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.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= 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.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.3/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 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 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.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
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 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -271,6 +259,7 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 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 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
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 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
@ -283,18 +272,12 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.uber.org/atomic v1.4.0/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.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= 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.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.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= 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.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 h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 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.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=
go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM= go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM=
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= 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= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@ -303,12 +286,8 @@ 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-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 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/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 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002094018-c90954cbb977 h1:yH6opeNE+0SY+7pXT4gclZUoKHogXeC2EvOSHGOMGPU=
golang.org/x/crypto v0.0.0-20201002094018-c90954cbb977/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -348,8 +327,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwL
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/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 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-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 h1:YfxMZzv3PjGonQYNUaeU2+DhAdqOxerQ30JFB6WgAXo= golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw=
golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/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-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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -377,20 +356,20 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc= 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-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/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-20200106162015-b016eb3dc98e/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 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201223074533-0d417f636930 h1:vRgIt+nup/B/BwIS0g2oC0haq0iqbV3ZA+u6+0TlNCo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 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/time v0.0.0-20181108054448-85acf8d2951c/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-20190308202827-9d24e82272b4/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-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -414,6 +393,7 @@ golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc h1:NCy3Ohtk6Iny5V/reW2Ktypo4zIpWBdRJ1uFMjBxdg8= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc h1:NCy3Ohtk6Iny5V/reW2Ktypo4zIpWBdRJ1uFMjBxdg8=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 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-20191216052735-49a3e744a425/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-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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -438,21 +418,35 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 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/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 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.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
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 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 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/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 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-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
@ -464,8 +458,6 @@ gopkg.in/elazarl/goproxy.v1 v1.0.0-20180725130230-947c36da3153/go.mod h1:xzjpkye
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= 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.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.61.0 h1:LBCdW4FmFYL4s/vDZD1RQYX7oAR6IjujCYgMdbHBR10=
gopkg.in/ini.v1 v1.61.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 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.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@ -474,8 +466,6 @@ 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.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= 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.5/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.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= 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-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@ -485,5 +475,3 @@ 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 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 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= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

167
internal/app/app.go Normal file
View file

@ -0,0 +1,167 @@
//go:generate mockgen -source=$GOFILE -destination=./mock/app.mock.go -package=mock
package app
import (
"context"
"os"
"os/signal"
"syscall"
"github.com/spf13/cobra"
"gitlab.com/inetmock/inetmock/internal/endpoints"
"gitlab.com/inetmock/inetmock/pkg/api"
"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/zap"
)
var (
configFilePath string
logLevel string
developmentLogs bool
)
type App interface {
api.PluginContext
Config() config.Config
Checker() health.Checker
EndpointManager() endpoints.EndpointManager
HandlerRegistry() api.HandlerRegistry
Context() context.Context
MustRun()
Shutdown()
WithCommands(cmds ...*cobra.Command) 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
}
func (a *app) MustRun() {
if err := a.rootCmd.Execute(); err != nil {
if a.rootLogger != nil {
a.rootLogger.Error(
"Failed to run inetmock",
zap.Error(err),
)
} else {
panic(err)
}
}
}
func (a app) Logger() logging.Logger {
return a.rootLogger
}
func (a app) Config() config.Config {
return a.cfg
}
func (a app) CertStore() cert.Store {
return a.certStore
}
func (a app) Checker() health.Checker {
return a.checker
}
func (a app) EndpointManager() endpoints.EndpointManager {
return a.endpointManager
}
func (a app) HandlerRegistry() api.HandlerRegistry {
return a.registry
}
func (a app) Context() context.Context {
return a.ctx
}
func (a app) Shutdown() {
a.cancel()
}
func (a *app) WithCommands(cmds ...*cobra.Command) App {
a.rootCmd.AddCommand(cmds...)
return a
}
func NewApp(registrations ...api.Registration) (inetmockApp App, err error) {
registry := api.NewHandlerRegistry()
for _, registration := range registrations {
if err = registration(registry); err != nil {
return
}
}
ctx, cancel := initAppContext()
a := &app{
rootCmd: &cobra.Command{
Short: "INetMock is lightweight internet mock",
},
checker: health.New(),
registry: registry,
ctx: ctx,
cancel: cancel,
}
a.rootCmd.PersistentFlags().StringVar(&configFilePath, "config", "", "Path to config file that should be used")
a.rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "info", "logging level to use")
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 {
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
}
func initAppContext() (context.Context, context.CancelFunc) {
ctx, cancel := context.WithCancel(context.Background())
signals := make(chan os.Signal, 1)
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
go func() {
<-signals
cancel()
}()
return ctx, cancel
}

View file

@ -3,12 +3,13 @@ package cmd
import ( import (
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"github.com/baez90/inetmock/pkg/cert"
"github.com/baez90/inetmock/pkg/config"
"github.com/baez90/inetmock/pkg/logging"
"github.com/spf13/cobra"
"go.uber.org/zap"
"time" "time"
"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"
) )
const ( const (
@ -58,6 +59,8 @@ func runGenerateCA(_ *cobra.Command, _ []string) {
var notBefore, notAfter time.Duration var notBefore, notAfter time.Duration
var err error var err error
logger := server.Logger().Named("generate-ca")
if certOutPath, err = getStringFlag(generateCaCmd, generateCACertOutPath, logger); err != nil { if certOutPath, err = getStringFlag(generateCaCmd, generateCACertOutPath, logger); err != nil {
return return
} }
@ -71,8 +74,6 @@ func runGenerateCA(_ *cobra.Command, _ []string) {
return return
} }
logger, _ := logging.CreateLogger()
logger = logger.With( logger = logger.With(
zap.String(generateCACurveName, curveName), zap.String(generateCACurveName, curveName),
zap.String(generateCACertOutPath, certOutPath), zap.String(generateCACertOutPath, certOutPath),

View file

@ -1,8 +1,9 @@
package cmd package cmd
import ( import (
"github.com/spf13/cobra"
"time" "time"
"github.com/spf13/cobra"
) )
var ( var (

View file

@ -3,11 +3,12 @@ package cmd
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/baez90/inetmock/internal/format"
"github.com/baez90/inetmock/internal/rpc"
"github.com/spf13/cobra"
"google.golang.org/grpc"
"os" "os"
"github.com/spf13/cobra"
"gitlab.com/inetmock/inetmock/internal/format"
"gitlab.com/inetmock/inetmock/internal/rpc"
"google.golang.org/grpc"
) )
var ( var (

View file

@ -3,11 +3,12 @@ package cmd
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/baez90/inetmock/internal/format"
"github.com/baez90/inetmock/internal/rpc"
"github.com/spf13/cobra"
"google.golang.org/grpc"
"os" "os"
"github.com/spf13/cobra"
"gitlab.com/inetmock/inetmock/internal/format"
"gitlab.com/inetmock/inetmock/internal/rpc"
"google.golang.org/grpc"
) )
var ( var (

View file

@ -3,11 +3,12 @@ package cmd
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/baez90/inetmock/internal/format"
"github.com/baez90/inetmock/internal/rpc"
"github.com/spf13/cobra"
"google.golang.org/grpc"
"os" "os"
"github.com/spf13/cobra"
"gitlab.com/inetmock/inetmock/internal/format"
"gitlab.com/inetmock/inetmock/internal/rpc"
"google.golang.org/grpc"
) )
var ( var (

View file

@ -1,24 +1,16 @@
package cmd package cmd
import ( import (
"github.com/baez90/inetmock/internal/endpoints"
"github.com/baez90/inetmock/internal/rpc"
"github.com/baez90/inetmock/pkg/api"
"github.com/baez90/inetmock/pkg/config"
"github.com/baez90/inetmock/pkg/health"
"github.com/baez90/inetmock/pkg/logging"
"github.com/baez90/inetmock/pkg/path"
"github.com/spf13/cobra"
"go.uber.org/zap"
"os"
"os/signal"
"strings" "strings"
"syscall"
"github.com/spf13/cobra"
"gitlab.com/inetmock/inetmock/internal/rpc"
"gitlab.com/inetmock/inetmock/pkg/config"
"go.uber.org/zap"
) )
var ( var (
endpointManager endpoints.EndpointManager serveCmd = &cobra.Command{
serveCmd = &cobra.Command{
Use: "serve", Use: "serve",
Short: "Starts the INetMock server", Short: "Starts the INetMock server",
Long: ``, Long: ``,
@ -26,51 +18,14 @@ var (
} }
) )
func onServerInit() (err error) {
logging.ConfigureLogging(
logging.ParseLevel(logLevel),
developmentLogs,
map[string]interface{}{"cwd": path.WorkingDirectory()},
)
logger, _ = logging.CreateLogger()
config.CreateConfig(serverCmd.Flags())
appConfig := config.Instance()
if err = appConfig.ReadConfig(configFilePath); err != nil {
logger.Error(
"failed to read config file",
zap.Error(err),
)
return
}
if err = api.InitServices(appConfig, logger); err != nil {
logger.Error(
"failed to initialize app services",
zap.Error(err),
)
return
}
return nil
}
func startINetMock(_ *cobra.Command, _ []string) { func startINetMock(_ *cobra.Command, _ []string) {
if err := onServerInit(); err != nil { rpcAPI := rpc.NewINetMockAPI(server)
panic(err) logger := server.Logger().Named("inetmock").With(zap.String("command", "serve"))
}
endpointManager = endpoints.NewEndpointManager(health.CheckerInstance(), logger)
cfg := config.Instance()
rpcAPI := rpc.NewINetMockAPI(
cfg,
endpointManager,
api.Registry(),
)
for endpointName, endpointHandler := range cfg.EndpointConfigs() { for endpointName, endpointHandler := range server.Config().EndpointConfigs() {
handlerSubConfig := cfg.Viper().Sub(strings.Join([]string{config.EndpointsKey, endpointName, config.OptionsKey}, ".")) handlerSubConfig := server.Config().Viper().Sub(strings.Join([]string{config.EndpointsKey, endpointName, config.OptionsKey}, "."))
endpointHandler.Options = handlerSubConfig endpointHandler.Options = handlerSubConfig
if err := endpointManager.CreateEndpoint(endpointName, endpointHandler); err != nil { if err := server.EndpointManager().CreateEndpoint(endpointName, endpointHandler); err != nil {
logger.Warn( logger.Warn(
"error occurred while creating endpoint", "error occurred while creating endpoint",
zap.String("endpointName", endpointName), zap.String("endpointName", endpointName),
@ -80,7 +35,7 @@ func startINetMock(_ *cobra.Command, _ []string) {
} }
} }
endpointManager.StartEndpoints() server.EndpointManager().StartEndpoints()
if err := rpcAPI.StartServer(); err != nil { if err := rpcAPI.StartServer(); err != nil {
logger.Error( logger.Error(
"failed to start gRPC API", "failed to start gRPC API",
@ -88,17 +43,12 @@ func startINetMock(_ *cobra.Command, _ []string) {
) )
} }
signalChannel := make(chan os.Signal, 1) <-server.Context().Done()
signal.Notify(signalChannel, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
// block until canceled
s := <-signalChannel
logger.Info( logger.Info(
"got signal to quit", "App context canceled - shutting down",
zap.String("signal", s.String()),
) )
rpcAPI.StopServer() rpcAPI.StopServer()
endpointManager.ShutdownEndpoints() server.EndpointManager().ShutdownEndpoints()
} }

View file

@ -1,35 +1,34 @@
package cmd package cmd
import ( import (
"github.com/baez90/inetmock/pkg/logging" "fmt"
"github.com/spf13/cobra" "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 ( var (
logger logging.Logger server app.App
serverCmd *cobra.Command
configFilePath string
logLevel string
developmentLogs bool
) )
func init() { func ExecuteServerCommand() {
serverCmd = &cobra.Command{ var err error
Use: "", if server, err = app.NewApp(
Short: "INetMock is lightweight internet mock", 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.
serverCmd.PersistentFlags().StringVar(&configFilePath, "config", "", "Path to config file that should be used") WithCommands(serveCmd, generateCaCmd).
serverCmd.PersistentFlags().StringVar(&logLevel, "log-level", "info", "logging level to use") MustRun()
serverCmd.PersistentFlags().BoolVar(&developmentLogs, "development-logs", false, "Enable development mode logs")
serverCmd.AddCommand(
serveCmd,
generateCaCmd,
)
}
func ExecuteServerCommand() error {
return serverCmd.Execute()
} }

View file

@ -3,14 +3,15 @@ package endpoints
import ( import (
"context" "context"
"github.com/baez90/inetmock/pkg/api"
"github.com/baez90/inetmock/pkg/config"
"github.com/google/uuid" "github.com/google/uuid"
"gitlab.com/inetmock/inetmock/pkg/api"
"gitlab.com/inetmock/inetmock/pkg/config"
) )
type Endpoint interface { type Endpoint interface {
Id() uuid.UUID Id() uuid.UUID
Start() error Start(ctx api.PluginContext) error
Shutdown(ctx context.Context) error Shutdown(ctx context.Context) error
Name() string Name() string
Handler() string Handler() string
@ -45,8 +46,8 @@ func (e endpoint) Port() uint16 {
return e.config.Port return e.config.Port
} }
func (e *endpoint) Start() (err error) { func (e *endpoint) Start(ctx api.PluginContext) (err error) {
return e.handler.Start(e.config) return e.handler.Start(ctx, e.config)
} }
func (e *endpoint) Shutdown(ctx context.Context) (err error) { func (e *endpoint) Shutdown(ctx context.Context) (err error) {

View file

@ -3,14 +3,15 @@ package endpoints
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/baez90/inetmock/pkg/api"
"github.com/baez90/inetmock/pkg/config"
"github.com/baez90/inetmock/pkg/health"
"github.com/baez90/inetmock/pkg/logging"
"github.com/google/uuid"
"go.uber.org/zap"
"sync" "sync"
"time" "time"
"github.com/google/uuid"
"gitlab.com/inetmock/inetmock/pkg/api"
"gitlab.com/inetmock/inetmock/pkg/config"
"gitlab.com/inetmock/inetmock/pkg/health"
"gitlab.com/inetmock/inetmock/pkg/logging"
"go.uber.org/zap"
) )
const ( const (
@ -20,25 +21,27 @@ const (
type EndpointManager interface { type EndpointManager interface {
RegisteredEndpoints() []Endpoint RegisteredEndpoints() []Endpoint
StartedEndpoints() []Endpoint StartedEndpoints() []Endpoint
CreateEndpoint(name string, multiHandlerConfig config.MultiHandlerConfig) error CreateEndpoint(name string, multiHandlerConfig config.EndpointConfig) error
StartEndpoints() StartEndpoints()
ShutdownEndpoints() ShutdownEndpoints()
} }
func NewEndpointManager(checker health.Checker, logger logging.Logger) EndpointManager { func NewEndpointManager(registry api.HandlerRegistry, logging logging.Logger, checker health.Checker, pluginContext api.PluginContext) EndpointManager {
return &endpointManager{ return &endpointManager{
logger: logger, registry: registry,
checker: checker, logger: logging,
registry: api.Registry(), checker: checker,
pluginContext: pluginContext,
} }
} }
type endpointManager struct { type endpointManager struct {
registry api.HandlerRegistry
logger logging.Logger logger logging.Logger
checker health.Checker checker health.Checker
pluginContext api.PluginContext
registeredEndpoints []Endpoint registeredEndpoints []Endpoint
properlyStartedEndpoints []Endpoint properlyStartedEndpoints []Endpoint
registry api.HandlerRegistry
} }
func (e endpointManager) RegisteredEndpoints() []Endpoint { func (e endpointManager) RegisteredEndpoints() []Endpoint {
@ -49,9 +52,9 @@ func (e endpointManager) StartedEndpoints() []Endpoint {
return e.properlyStartedEndpoints return e.properlyStartedEndpoints
} }
func (e *endpointManager) CreateEndpoint(name string, multiHandlerConfig config.MultiHandlerConfig) error { func (e *endpointManager) CreateEndpoint(name string, endpointConfig config.EndpointConfig) error {
for _, handlerConfig := range multiHandlerConfig.HandlerConfigs() { for _, handlerConfig := range endpointConfig.HandlerConfigs() {
if handler, ok := e.registry.HandlerForName(multiHandlerConfig.Handler); ok { if handler, ok := e.registry.HandlerForName(endpointConfig.Handler); ok {
e.registeredEndpoints = append(e.registeredEndpoints, &endpoint{ e.registeredEndpoints = append(e.registeredEndpoints, &endpoint{
id: uuid.New(), id: uuid.New(),
name: name, name: name,
@ -59,7 +62,7 @@ func (e *endpointManager) CreateEndpoint(name string, multiHandlerConfig config.
config: handlerConfig, config: handlerConfig,
}) })
} else { } else {
return fmt.Errorf("no matching handler registered for names %s", multiHandlerConfig.Handler) return fmt.Errorf("no matching handler registered for names %s", endpointConfig.Handler)
} }
} }
@ -73,7 +76,7 @@ func (e *endpointManager) StartEndpoints() {
zap.String("endpoint", endpoint.Name()), zap.String("endpoint", endpoint.Name()),
) )
endpointLogger.Info("Starting endpoint") endpointLogger.Info("Starting endpoint")
if ok := startEndpoint(endpoint, endpointLogger); ok { if ok := startEndpoint(endpoint, e.pluginContext, endpointLogger); ok {
_ = e.checker.RegisterCheck( _ = e.checker.RegisterCheck(
endpointComponentName(endpoint), endpointComponentName(endpoint),
health.StaticResultCheckWithMessage(health.HEALTHY, "Successfully started"), health.StaticResultCheckWithMessage(health.HEALTHY, "Successfully started"),
@ -115,7 +118,7 @@ func (e *endpointManager) ShutdownEndpoints() {
waitGroup.Wait() waitGroup.Wait()
} }
func startEndpoint(ep Endpoint, logger logging.Logger) (success bool) { func startEndpoint(ep Endpoint, ctx api.PluginContext, logger logging.Logger) (success bool) {
startSuccessful := make(chan bool) startSuccessful := make(chan bool)
go func() { go func() {
@ -129,7 +132,7 @@ func startEndpoint(ep Endpoint, logger logging.Logger) (success bool) {
} }
}() }()
if err := ep.Start(); err != nil { if err := ep.Start(ctx); err != nil {
logger.Error( logger.Error(
"failed to start endpoint", "failed to start endpoint",
zap.Error(err), zap.Error(err),
@ -146,6 +149,8 @@ func startEndpoint(ep Endpoint, logger logging.Logger) (success bool) {
success = false success = false
} }
close(startSuccessful)
return return
} }

View file

@ -1,27 +1,27 @@
package endpoints package endpoints
import ( import (
api_mock "github.com/baez90/inetmock/internal/mock/api"
logging_mock "github.com/baez90/inetmock/internal/mock/logging"
plugins_mock "github.com/baez90/inetmock/internal/mock/plugins"
"github.com/baez90/inetmock/pkg/api"
"github.com/baez90/inetmock/pkg/config"
"github.com/baez90/inetmock/pkg/logging"
"github.com/golang/mock/gomock"
"reflect" "reflect"
"testing" "testing"
"github.com/golang/mock/gomock"
api_mock "gitlab.com/inetmock/inetmock/internal/mock/api"
logging_mock "gitlab.com/inetmock/inetmock/internal/mock/logging"
plugins_mock "gitlab.com/inetmock/inetmock/internal/mock/plugins"
"gitlab.com/inetmock/inetmock/pkg/api"
"gitlab.com/inetmock/inetmock/pkg/config"
"gitlab.com/inetmock/inetmock/pkg/health"
"gitlab.com/inetmock/inetmock/pkg/logging"
) )
func Test_endpointManager_CreateEndpoint(t *testing.T) { func Test_endpointManager_CreateEndpoint(t *testing.T) {
type fields struct { type fields struct {
logger logging.Logger logger logging.Logger
registeredEndpoints []Endpoint registry api.HandlerRegistry
properlyStartedEndpoints []Endpoint
registry api.HandlerRegistry
} }
type args struct { type args struct {
name string name string
multiHandlerConfig config.MultiHandlerConfig multiHandlerConfig config.EndpointConfig
} }
tests := []struct { tests := []struct {
name string name string
@ -38,8 +38,6 @@ func Test_endpointManager_CreateEndpoint(t *testing.T) {
logger: func() logging.Logger { logger: func() logging.Logger {
return logging_mock.NewMockLogger(gomock.NewController(t)) return logging_mock.NewMockLogger(gomock.NewController(t))
}(), }(),
registeredEndpoints: nil,
properlyStartedEndpoints: nil,
registry: func() api.HandlerRegistry { registry: func() api.HandlerRegistry {
registry := plugins_mock.NewMockHandlerRegistry(gomock.NewController(t)) registry := plugins_mock.NewMockHandlerRegistry(gomock.NewController(t))
registry. registry.
@ -53,7 +51,7 @@ func Test_endpointManager_CreateEndpoint(t *testing.T) {
}, },
args: args{ args: args{
name: "sampleEndpoint", name: "sampleEndpoint",
multiHandlerConfig: config.MultiHandlerConfig{ multiHandlerConfig: config.EndpointConfig{
Handler: "sampleHandler", Handler: "sampleHandler",
Ports: []uint16{80}, Ports: []uint16{80},
ListenAddress: "0.0.0.0", ListenAddress: "0.0.0.0",
@ -68,8 +66,6 @@ func Test_endpointManager_CreateEndpoint(t *testing.T) {
logger: func() logging.Logger { logger: func() logging.Logger {
return logging_mock.NewMockLogger(gomock.NewController(t)) return logging_mock.NewMockLogger(gomock.NewController(t))
}(), }(),
registeredEndpoints: nil,
properlyStartedEndpoints: nil,
registry: func() api.HandlerRegistry { registry: func() api.HandlerRegistry {
registry := plugins_mock.NewMockHandlerRegistry(gomock.NewController(t)) registry := plugins_mock.NewMockHandlerRegistry(gomock.NewController(t))
registry. registry.
@ -83,7 +79,7 @@ func Test_endpointManager_CreateEndpoint(t *testing.T) {
}, },
args: args{ args: args{
name: "sampleEndpoint", name: "sampleEndpoint",
multiHandlerConfig: config.MultiHandlerConfig{ multiHandlerConfig: config.EndpointConfig{
Handler: "sampleHandler", Handler: "sampleHandler",
Ports: []uint16{80}, Ports: []uint16{80},
ListenAddress: "0.0.0.0", ListenAddress: "0.0.0.0",
@ -93,13 +89,7 @@ func Test_endpointManager_CreateEndpoint(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
e := &endpointManager{ e := NewEndpointManager(tt.fields.registry, tt.fields.logger, health.New(), nil)
logger: tt.fields.logger,
registeredEndpoints: tt.fields.registeredEndpoints,
properlyStartedEndpoints: tt.fields.properlyStartedEndpoints,
registry: tt.fields.registry,
}
if err := e.CreateEndpoint(tt.args.name, tt.args.multiHandlerConfig); (err != nil) != tt.wantErr { if err := e.CreateEndpoint(tt.args.name, tt.args.multiHandlerConfig); (err != nil) != tt.wantErr {
t.Errorf("CreateEndpoint() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("CreateEndpoint() error = %v, wantErr %v", err, tt.wantErr)
} }
@ -140,7 +130,6 @@ func Test_endpointManager_StartedEndpoints(t *testing.T) {
logger: tt.fields.logger, logger: tt.fields.logger,
registeredEndpoints: tt.fields.registeredEndpoints, registeredEndpoints: tt.fields.registeredEndpoints,
properlyStartedEndpoints: tt.fields.properlyStartedEndpoints, properlyStartedEndpoints: tt.fields.properlyStartedEndpoints,
registry: tt.fields.registry,
} }
if got := e.StartedEndpoints(); !reflect.DeepEqual(got, tt.want) { if got := e.StartedEndpoints(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("StartedEndpoints() = %v, want %v", got, tt.want) t.Errorf("StartedEndpoints() = %v, want %v", got, tt.want)

View file

@ -1,184 +0,0 @@
package endpoints
import (
"context"
"fmt"
apimock "github.com/baez90/inetmock/internal/mock/api"
"github.com/baez90/inetmock/pkg/api"
"github.com/baez90/inetmock/pkg/config"
"github.com/golang/mock/gomock"
"reflect"
"testing"
)
var (
anyContext = context.Background()
)
func Test_endpoint_Name(t *testing.T) {
type fields struct {
name string
handler api.ProtocolHandler
config config.HandlerConfig
}
tests := []struct {
name string
fields fields
want string
}{
{
name: "Empty Name if struct is uninitialized",
fields: fields{},
want: "",
},
{
name: "Expected Name if struct is initialized",
fields: fields{
name: "sampleHandler",
},
want: "sampleHandler",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
e := endpoint{
name: tt.fields.name,
handler: tt.fields.handler,
config: tt.fields.config,
}
if got := e.Name(); got != tt.want {
t.Errorf("Name() = %v, want %v", got, tt.want)
}
})
}
}
func Test_endpoint_Shutdown(t *testing.T) {
type fields struct {
name string
handler api.ProtocolHandler
config config.HandlerConfig
}
tests := []struct {
name string
fields fields
wantErr bool
}{
{
name: "Expect no error if mocked handler does not return one",
fields: fields{
handler: func() api.ProtocolHandler {
handler := apimock.NewMockProtocolHandler(gomock.NewController(t))
handler.EXPECT().
Shutdown(gomock.Any()).
MaxTimes(1).
Return(nil)
return handler
}(),
},
wantErr: false,
},
{
name: "Expect error if mocked handler returns one",
fields: fields{
handler: func() api.ProtocolHandler {
handler := apimock.NewMockProtocolHandler(gomock.NewController(t))
handler.EXPECT().
Shutdown(gomock.AssignableToTypeOf(reflect.TypeOf(context.Background()))).
MaxTimes(1).
Return(fmt.Errorf(""))
return handler
}(),
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
e := &endpoint{
name: tt.fields.name,
handler: tt.fields.handler,
config: tt.fields.config,
}
if err := e.Shutdown(context.Background()); (err != nil) != tt.wantErr {
t.Errorf("Shutdown() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func Test_endpoint_Start(t *testing.T) {
demoHandlerConfig := config.HandlerConfig{
HandlerName: "sampleHandler",
Port: 80,
ListenAddress: "0.0.0.0",
}
type fields struct {
name string
handler api.ProtocolHandler
config config.HandlerConfig
}
tests := []struct {
name string
fields fields
wantErr bool
}{
{
name: "Expect no error if mocked handler does not return one",
fields: fields{
handler: func() api.ProtocolHandler {
handler := apimock.NewMockProtocolHandler(gomock.NewController(t))
handler.EXPECT().
Start(gomock.Any()).
MaxTimes(1).
Return(nil)
return handler
}(),
},
wantErr: false,
},
{
name: "Expect error if mocked handler returns one",
fields: fields{
handler: func() api.ProtocolHandler {
handler := apimock.NewMockProtocolHandler(gomock.NewController(t))
handler.EXPECT().
Start(gomock.Any()).
MaxTimes(1).
Return(fmt.Errorf(""))
return handler
}(),
},
wantErr: true,
},
{
name: "Expect config to be passed to Start call",
fields: fields{
config: demoHandlerConfig,
handler: func() api.ProtocolHandler {
handler := apimock.NewMockProtocolHandler(gomock.NewController(t))
handler.EXPECT().
Start(demoHandlerConfig).
MaxTimes(1).
Return(fmt.Errorf(""))
return handler
}(),
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
e := &endpoint{
name: tt.fields.name,
handler: tt.fields.handler,
config: tt.fields.config,
}
if err := e.Start(); (err != nil) != tt.wantErr {
t.Errorf("Start() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

View file

@ -2,10 +2,11 @@ package format
import ( import (
"encoding/json" "encoding/json"
"github.com/olekukonko/tablewriter"
"gopkg.in/yaml.v3"
"io" "io"
"strings" "strings"
"github.com/olekukonko/tablewriter"
"gopkg.in/yaml.v3"
) )
type consoleWriterFactory func(io.Writer) ConsoleWriter type consoleWriterFactory func(io.Writer) ConsoleWriter

View file

@ -3,9 +3,10 @@ package format
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/olekukonko/tablewriter"
"reflect" "reflect"
"strconv" "strconv"
"github.com/olekukonko/tablewriter"
) )
type tblWriter struct { type tblWriter struct {
@ -59,7 +60,7 @@ func (t *tblWriter) Write(in interface{}) (err error) {
t.tableWriter.Render() t.tableWriter.Render()
t.tableWriter.ClearRows() t.tableWriter.ClearRows()
return return err
} }
func (t *tblWriter) getData(val reflect.Value, numberOfFields int) (data []string) { func (t *tblWriter) getData(val reflect.Value, numberOfFields int) (data []string) {

View file

@ -2,10 +2,12 @@ package rpc
import ( import (
"context" "context"
"github.com/baez90/inetmock/internal/endpoints"
"gitlab.com/inetmock/inetmock/internal/endpoints"
) )
type endpointsServer struct { type endpointsServer struct {
UnimplementedEndpointsServer
endpointsManager endpoints.EndpointManager endpointsManager endpoints.EndpointManager
} }

View file

@ -1,15 +1,14 @@
package rpc package rpc
import ( import (
"github.com/baez90/inetmock/internal/endpoints"
"github.com/baez90/inetmock/pkg/api"
"github.com/baez90/inetmock/pkg/config"
"github.com/baez90/inetmock/pkg/logging"
"go.uber.org/zap"
"google.golang.org/grpc"
"net" "net"
"net/url" "net/url"
"time" "time"
app2 "gitlab.com/inetmock/inetmock/internal/app"
"gitlab.com/inetmock/inetmock/pkg/logging"
"go.uber.org/zap"
"google.golang.org/grpc"
) )
type INetMockAPI interface { type INetMockAPI interface {
@ -18,23 +17,20 @@ type INetMockAPI interface {
} }
type inetmockAPI struct { type inetmockAPI struct {
url *url.URL app app2.App
server *grpc.Server url *url.URL
endpointManager endpoints.EndpointManager server *grpc.Server
registry api.HandlerRegistry logger logging.Logger
logger logging.Logger serverRunning bool
serverRunning bool
} }
func NewINetMockAPI( func NewINetMockAPI(
config config.Config, app app2.App,
epMgr endpoints.EndpointManager,
registry api.HandlerRegistry,
) INetMockAPI { ) INetMockAPI {
return &inetmockAPI{ return &inetmockAPI{
url: config.APIConfig().ListenURL(), app: app,
endpointManager: epMgr, url: app.Config().APIConfig().ListenURL(),
registry: registry, logger: app.Logger().Named("api"),
} }
} }
@ -46,13 +42,15 @@ func (i *inetmockAPI) StartServer() (err error) {
i.server = grpc.NewServer() i.server = grpc.NewServer()
RegisterHandlersServer(i.server, &handlersServer{ RegisterHandlersServer(i.server, &handlersServer{
registry: i.registry, registry: i.app.HandlerRegistry(),
}) })
RegisterEndpointsServer(i.server, &endpointsServer{ RegisterEndpointsServer(i.server, &endpointsServer{
endpointsManager: i.endpointManager, endpointsManager: i.app.EndpointManager(),
}) })
RegisterHealthServer(i.server, &healthServer{}) RegisterHealthServer(i.server, &healthServer{
app: i.app,
})
go i.startServerAsync(lis) go i.startServerAsync(lis)
return return

View file

@ -2,13 +2,18 @@ package rpc
import ( import (
"context" "context"
"github.com/baez90/inetmock/pkg/api"
"gitlab.com/inetmock/inetmock/pkg/api"
) )
type handlersServer struct { type handlersServer struct {
UnimplementedHandlersServer
registry api.HandlerRegistry registry api.HandlerRegistry
} }
func (h *handlersServer) mustEmbedUnimplementedHandlersServer() {
}
func (h *handlersServer) GetHandlers(_ context.Context, _ *GetHandlersRequest) (*GetHandlersResponse, error) { func (h *handlersServer) GetHandlers(_ context.Context, _ *GetHandlersRequest) (*GetHandlersResponse, error) {
return &GetHandlersResponse{ return &GetHandlersResponse{
Handlers: h.registry.AvailableHandlers(), Handlers: h.registry.AvailableHandlers(),

View file

@ -2,14 +2,17 @@ package rpc
import ( import (
"context" "context"
"github.com/baez90/inetmock/pkg/health"
app2 "gitlab.com/inetmock/inetmock/internal/app"
) )
type healthServer struct { type healthServer struct {
UnimplementedHealthServer
app app2.App
} }
func (h healthServer) GetHealth(_ context.Context, _ *HealthRequest) (resp *HealthResponse, err error) { func (h healthServer) GetHealth(_ context.Context, _ *HealthRequest) (resp *HealthResponse, err error) {
checker := health.CheckerInstance() checker := h.app.Checker()
result := checker.IsHealthy() result := checker.IsHealthy()
resp = &HealthResponse{ resp = &HealthResponse{

View file

@ -3,15 +3,18 @@ package api
import ( import (
"context" "context"
"github.com/baez90/inetmock/pkg/config"
"go.uber.org/zap" "gitlab.com/inetmock/inetmock/pkg/cert"
"gitlab.com/inetmock/inetmock/pkg/config"
"gitlab.com/inetmock/inetmock/pkg/logging"
) )
type PluginInstanceFactory func() ProtocolHandler type PluginContext interface {
Logger() logging.Logger
type LoggingFactory func() (*zap.Logger, error) CertStore() cert.Store
}
type ProtocolHandler interface { type ProtocolHandler interface {
Start(config config.HandlerConfig) error Start(ctx PluginContext, config config.HandlerConfig) error
Shutdown(ctx context.Context) error Shutdown(ctx context.Context) error
} }

View file

@ -8,12 +8,25 @@ import (
) )
var ( var (
registry HandlerRegistry
pluginFileNamePattern = regexp.MustCompile(`[\w\-]+\.so$`) pluginFileNamePattern = regexp.MustCompile(`[\w\-]+\.so$`)
) )
type Registration func(registry HandlerRegistry) error
type HandlerRegistry interface {
RegisterHandler(handlerName string, handlerProvider func() ProtocolHandler)
AvailableHandlers() []string
HandlerForName(handlerName string) (ProtocolHandler, bool)
}
func NewHandlerRegistry() HandlerRegistry {
return &handlerRegistry{
handlers: make(map[string]func() ProtocolHandler),
}
}
type handlerRegistry struct { type handlerRegistry struct {
handlers map[string]PluginInstanceFactory handlers map[string]func() ProtocolHandler
} }
func (h handlerRegistry) AvailableHandlers() (availableHandlers []string) { func (h handlerRegistry) AvailableHandlers() (availableHandlers []string) {
@ -25,33 +38,17 @@ func (h handlerRegistry) AvailableHandlers() (availableHandlers []string) {
func (h *handlerRegistry) HandlerForName(handlerName string) (instance ProtocolHandler, ok bool) { func (h *handlerRegistry) HandlerForName(handlerName string) (instance ProtocolHandler, ok bool) {
handlerName = strings.ToLower(handlerName) handlerName = strings.ToLower(handlerName)
var provider PluginInstanceFactory var provider func() ProtocolHandler
if provider, ok = h.handlers[handlerName]; ok { if provider, ok = h.handlers[handlerName]; ok {
instance = provider() instance = provider()
} }
return return
} }
func (h *handlerRegistry) RegisterHandler(handlerName string, handlerProvider PluginInstanceFactory) { func (h *handlerRegistry) RegisterHandler(handlerName string, handlerProvider func() ProtocolHandler) {
handlerName = strings.ToLower(handlerName) handlerName = strings.ToLower(handlerName)
if _, exists := h.handlers[handlerName]; exists { if _, exists := h.handlers[handlerName]; exists {
panic(fmt.Sprintf("handler with name %s is already registered - there's something strange...in the neighborhood", handlerName)) panic(fmt.Sprintf("handler with name %s is already registered - there's something strange...in the neighborhood", handlerName))
} }
h.handlers[handlerName] = handlerProvider h.handlers[handlerName] = handlerProvider
} }
func Registry() HandlerRegistry {
return registry
}
func init() {
registry = &handlerRegistry{
handlers: make(map[string]PluginInstanceFactory),
}
}
type HandlerRegistry interface {
RegisterHandler(handlerName string, handlerProvider PluginInstanceFactory)
AvailableHandlers() []string
HandlerForName(handlerName string) (ProtocolHandler, bool)
}

View file

@ -7,7 +7,7 @@ import (
func Test_handlerRegistry_HandlerForName(t *testing.T) { func Test_handlerRegistry_HandlerForName(t *testing.T) {
type fields struct { type fields struct {
handlers map[string]PluginInstanceFactory handlers map[string]func() ProtocolHandler
} }
type args struct { type args struct {
handlerName string handlerName string
@ -29,7 +29,7 @@ func Test_handlerRegistry_HandlerForName(t *testing.T) {
{ {
name: "Nil instance from pseudo factory", name: "Nil instance from pseudo factory",
fields: fields{ fields: fields{
handlers: map[string]PluginInstanceFactory{ handlers: map[string]func() ProtocolHandler{
"pseudo": func() ProtocolHandler { "pseudo": func() ProtocolHandler {
return nil return nil
}, },

View file

@ -1,41 +0,0 @@
package api
import (
"github.com/baez90/inetmock/pkg/cert"
"github.com/baez90/inetmock/pkg/config"
"github.com/baez90/inetmock/pkg/logging"
)
var (
svcs Services
)
type Services interface {
CertStore() cert.Store
}
type services struct {
certStore cert.Store
}
func InitServices(
config config.Config,
logger logging.Logger,
) error {
certStore, err := cert.NewDefaultStore(config, logger)
if err != nil {
return err
}
svcs = &services{
certStore: certStore,
}
return nil
}
func ServicesInstance() Services {
return svcs
}
func (s *services) CertStore() cert.Store {
return s.certStore
}

View file

@ -4,14 +4,15 @@ import (
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"fmt" "fmt"
certmock "github.com/baez90/inetmock/internal/mock/cert"
"github.com/baez90/inetmock/pkg/config"
"github.com/golang/mock/gomock"
"os" "os"
"path" "path"
"reflect" "reflect"
"testing" "testing"
"time" "time"
"github.com/golang/mock/gomock"
certmock "gitlab.com/inetmock/inetmock/internal/mock/cert"
"gitlab.com/inetmock/inetmock/pkg/config"
) )
const ( const (

View file

@ -1,8 +1,9 @@
package cert package cert
import ( import (
"github.com/baez90/inetmock/pkg/defaulting"
"reflect" "reflect"
"gitlab.com/inetmock/inetmock/pkg/defaulting"
) )
var ( var (

View file

@ -8,10 +8,11 @@ import (
"crypto/x509" "crypto/x509"
"crypto/x509/pkix" "crypto/x509/pkix"
"encoding/pem" "encoding/pem"
"github.com/baez90/inetmock/pkg/config"
"github.com/baez90/inetmock/pkg/defaulting"
"math/big" "math/big"
"net" "net"
"gitlab.com/inetmock/inetmock/pkg/config"
"gitlab.com/inetmock/inetmock/pkg/defaulting"
) )
var ( var (

View file

@ -1,8 +1,9 @@
package cert package cert
import ( import (
"github.com/baez90/inetmock/pkg/config"
"os" "os"
"gitlab.com/inetmock/inetmock/pkg/config"
) )
func init() { func init() {

View file

@ -1,12 +1,13 @@
package cert package cert
import ( import (
"github.com/baez90/inetmock/pkg/config"
"github.com/spf13/viper"
"os" "os"
"strings" "strings"
"testing" "testing"
"time" "time"
"github.com/spf13/viper"
"gitlab.com/inetmock/inetmock/pkg/config"
) )
func readViper(cfg string) *viper.Viper { func readViper(cfg string) *viper.Viper {

View file

@ -6,9 +6,10 @@ import (
"encoding/pem" "encoding/pem"
"errors" "errors"
"fmt" "fmt"
"github.com/baez90/inetmock/pkg/path"
"os" "os"
"path/filepath" "path/filepath"
"gitlab.com/inetmock/inetmock/pkg/path"
) )
const ( const (

View file

@ -6,10 +6,11 @@ import (
"crypto/rand" "crypto/rand"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"github.com/baez90/inetmock/pkg/config"
"github.com/baez90/inetmock/pkg/logging"
"go.uber.org/zap"
"net" "net"
"gitlab.com/inetmock/inetmock/pkg/config"
"gitlab.com/inetmock/inetmock/pkg/logging"
"go.uber.org/zap"
) )
const ( const (

View file

@ -1,19 +1,16 @@
package config package config
import ( import (
"github.com/baez90/inetmock/pkg/logging" "strings"
"github.com/baez90/inetmock/pkg/path"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"github.com/spf13/viper" "github.com/spf13/viper"
"gitlab.com/inetmock/inetmock/pkg/logging"
"gitlab.com/inetmock/inetmock/pkg/path"
"go.uber.org/zap" "go.uber.org/zap"
"strings"
) )
var ( func CreateConfig(flags *pflag.FlagSet) Config {
appConfig Config
)
func CreateConfig(flags *pflag.FlagSet) {
logger, _ := logging.CreateLogger() logger, _ := logging.CreateLogger()
configInstance := &config{ configInstance := &config{
logger: logger.Named("Config"), logger: logger.Named("Config"),
@ -38,11 +35,7 @@ func CreateConfig(flags *pflag.FlagSet) {
configInstance.cfg.RegisterAlias(k, v) configInstance.cfg.RegisterAlias(k, v)
} }
appConfig = configInstance return configInstance
}
func Instance() Config {
return appConfig
} }
type Config interface { type Config interface {
@ -51,14 +44,14 @@ type Config interface {
Viper() *viper.Viper Viper() *viper.Viper
TLSConfig() CertOptions TLSConfig() CertOptions
APIConfig() RPC APIConfig() RPC
EndpointConfigs() map[string]MultiHandlerConfig EndpointConfigs() map[string]EndpointConfig
} }
type config struct { type config struct {
cfg *viper.Viper cfg *viper.Viper
logger logging.Logger logger logging.Logger
TLS CertOptions TLS CertOptions
Endpoints map[string]MultiHandlerConfig Endpoints map[string]EndpointConfig
API RPC API RPC
} }
@ -76,7 +69,7 @@ func (c *config) ReadConfigString(config, format string) (err error) {
return return
} }
func (c *config) EndpointConfigs() map[string]MultiHandlerConfig { func (c *config) EndpointConfigs() map[string]EndpointConfig {
return c.Endpoints return c.Endpoints
} }

View file

@ -1,8 +1,9 @@
package config package config
import ( import (
"github.com/spf13/pflag"
"testing" "testing"
"github.com/spf13/pflag"
) )
func Test_config_ReadConfig(t *testing.T) { func Test_config_ReadConfig(t *testing.T) {
@ -53,8 +54,7 @@ endpoints:
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
CreateConfig(tt.args.flags) cfg := CreateConfig(tt.args.flags)
cfg := Instance()
if err := cfg.ReadConfigString(tt.args.config, "yaml"); (err != nil) != tt.wantErr { if err := cfg.ReadConfigString(tt.args.config, "yaml"); (err != nil) != tt.wantErr {
t.Errorf("ReadConfig() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("ReadConfig() error = %v, wantErr %v", err, tt.wantErr)
return return

View file

@ -2,6 +2,7 @@ package config
import ( import (
"fmt" "fmt"
"github.com/spf13/viper" "github.com/spf13/viper"
) )

View file

@ -1,9 +1,10 @@
package config package config
import ( import (
"github.com/spf13/viper"
"reflect" "reflect"
"testing" "testing"
"github.com/spf13/viper"
) )
func Test_handlerConfig_HandlerName(t *testing.T) { func Test_handlerConfig_HandlerName(t *testing.T) {

View file

@ -4,14 +4,14 @@ import (
"github.com/spf13/viper" "github.com/spf13/viper"
) )
type MultiHandlerConfig struct { type EndpointConfig struct {
Handler string Handler string
Ports []uint16 Ports []uint16
ListenAddress string ListenAddress string
Options *viper.Viper Options *viper.Viper
} }
func (m MultiHandlerConfig) HandlerConfigs() []HandlerConfig { func (m EndpointConfig) HandlerConfigs() []HandlerConfig {
configs := make([]HandlerConfig, 0) configs := make([]HandlerConfig, 0)
for _, port := range m.Ports { for _, port := range m.Ports {
configs = append(configs, HandlerConfig{ configs = append(configs, HandlerConfig{

View file

@ -27,9 +27,7 @@ func (r *registry) Register(t reflect.Type, defaulter ...Defaulter) {
given = r given = r
} }
for _, d := range defaulter { given = append(given, defaulter...)
given = append(given, d)
}
r.defaulters[t] = given r.defaulters[t] = given
} }

View file

@ -29,8 +29,7 @@ func Test_registry_Apply(t *testing.T) {
fields: fields{ fields: fields{
defaulters: map[reflect.Type][]Defaulter{ defaulters: map[reflect.Type][]Defaulter{
reflect.TypeOf(&sample{}): {func(instance interface{}) { reflect.TypeOf(&sample{}): {func(instance interface{}) {
switch i := instance.(type) { if i, ok := instance.(*sample); ok {
case *sample:
i.i = 42 i.i = 42
} }
}}, }},

View file

@ -26,16 +26,11 @@ type Result struct {
type Check func() CheckResult type Check func() CheckResult
var ( var (
CheckForComponentAlreadyRegistered = errors.New("a check for the requested component is already registered") ErrCheckForComponentAlreadyRegistered = errors.New("a check for the requested component is already registered")
checkerInstance *checker
) )
func init() { func New() Checker {
checkerInstance = &checker{ return &checker{
componentChecks: map[string]Check{}, componentChecks: map[string]Check{},
} }
} }
func CheckerInstance() Checker {
return checkerInstance
}

View file

@ -13,7 +13,7 @@ type Checker interface {
func (c *checker) RegisterCheck(component string, check Check) error { func (c *checker) RegisterCheck(component string, check Check) error {
if _, exists := c.componentChecks[component]; exists { if _, exists := c.componentChecks[component]; exists {
return fmt.Errorf("component: %s: %w", component, CheckForComponentAlreadyRegistered) return fmt.Errorf("component: %s: %w", component, ErrCheckForComponentAlreadyRegistered)
} }
c.componentChecks[component] = check c.componentChecks[component] = check
@ -30,7 +30,7 @@ func (c *checker) IsHealthy() (r Result) {
return return
} }
func max(s1 Status, s2 Status) Status { func max(s1, s2 Status) Status {
var max Status var max Status
if s1 > s2 { if s1 > s2 {
max = s1 max = s1

View file

@ -1,9 +1,10 @@
package logging package logging
import ( import (
"strings"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zapcore" "go.uber.org/zap/zapcore"
"strings"
) )
var ( var (
@ -46,3 +47,11 @@ func CreateLogger() (Logger, error) {
return NewLogger(zapLogger), nil return NewLogger(zapLogger), nil
} }
} }
func MustCreateLogger() Logger {
if logger, err := CreateLogger(); err != nil {
panic(err)
} else {
return logger
}
}

View file

@ -1,9 +1,10 @@
package logging package logging
import ( import (
"go.uber.org/zap"
"reflect" "reflect"
"testing" "testing"
"go.uber.org/zap"
) )
func TestParseLevel(t *testing.T) { func TestParseLevel(t *testing.T) {

View file

@ -10,7 +10,6 @@ import (
) )
func TestFileExists(t *testing.T) { func TestFileExists(t *testing.T) {
tmpFile, err := ioutil.TempFile("", "inetmock") tmpFile, err := ioutil.TempFile("", "inetmock")
if err != nil { if err != nil {

View file

@ -2,11 +2,12 @@ package dns_mock
import ( import (
"encoding/binary" "encoding/binary"
"github.com/spf13/viper"
"math" "math"
"math/rand" "math/rand"
"net" "net"
"unsafe" "unsafe"
"github.com/spf13/viper"
) )
const ( const (

View file

@ -2,9 +2,11 @@ package dns_mock
import ( import (
"context" "context"
"github.com/baez90/inetmock/pkg/config"
"github.com/baez90/inetmock/pkg/logging"
"github.com/miekg/dns" "github.com/miekg/dns"
"gitlab.com/inetmock/inetmock/pkg/api"
"gitlab.com/inetmock/inetmock/pkg/config"
"gitlab.com/inetmock/inetmock/pkg/logging"
"go.uber.org/zap" "go.uber.org/zap"
) )
@ -13,7 +15,7 @@ type dnsHandler struct {
dnsServer []*dns.Server dnsServer []*dns.Server
} }
func (d *dnsHandler) Start(config config.HandlerConfig) (err error) { func (d *dnsHandler) Start(_ api.PluginContext, config config.HandlerConfig) (err error) {
var options dnsOptions var options dnsOptions
if options, err = loadFromConfig(config.Options); err != nil { if options, err = loadFromConfig(config.Options); err != nil {
return return

View file

@ -1,9 +1,10 @@
package dns_mock package dns_mock
import ( import (
"github.com/spf13/viper"
"net" "net"
"regexp" "regexp"
"github.com/spf13/viper"
) )
const ( const (

View file

@ -1,9 +1,9 @@
package dns_mock package dns_mock
import ( import (
"github.com/baez90/inetmock/pkg/logging"
"github.com/miekg/dns" "github.com/miekg/dns"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"gitlab.com/inetmock/inetmock/pkg/logging"
"go.uber.org/zap" "go.uber.org/zap"
) )

View file

@ -1,10 +1,10 @@
package dns_mock package dns_mock
import ( import (
"github.com/baez90/inetmock/pkg/api"
"github.com/baez90/inetmock/pkg/logging"
"github.com/baez90/inetmock/pkg/metrics"
"github.com/prometheus/client_golang/prometheus" "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" "go.uber.org/zap"
) )
@ -19,11 +19,10 @@ var (
requestDurationHistogram *prometheus.HistogramVec requestDurationHistogram *prometheus.HistogramVec
) )
func init() { func AddDNSMock(registry api.HandlerRegistry) (err error) {
var err error
var logger logging.Logger var logger logging.Logger
if logger, err = logging.CreateLogger(); err != nil { if logger, err = logging.CreateLogger(); err != nil {
panic(err) return
} }
logger = logger.With( logger = logger.With(
zap.String("protocol_handler", name), zap.String("protocol_handler", name),
@ -35,7 +34,7 @@ func init() {
"", "",
handlerNameLblName, handlerNameLblName,
); err != nil { ); err != nil {
panic(err) return
} }
if unhandledRequestsCounter, err = metrics.Counter( if unhandledRequestsCounter, err = metrics.Counter(
@ -44,7 +43,7 @@ func init() {
"", "",
handlerNameLblName, handlerNameLblName,
); err != nil { ); err != nil {
panic(err) return
} }
if requestDurationHistogram, err = metrics.Histogram( if requestDurationHistogram, err = metrics.Histogram(
@ -54,12 +53,14 @@ func init() {
nil, nil,
handlerNameLblName, handlerNameLblName,
); err != nil { ); err != nil {
panic(err) return
} }
api.Registry().RegisterHandler(name, func() api.ProtocolHandler { registry.RegisterHandler(name, func() api.ProtocolHandler {
return &dnsHandler{ return &dnsHandler{
logger: logger, logger: logger,
} }
}) })
return
} }

View file

@ -4,10 +4,12 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"github.com/baez90/inetmock/pkg/config"
"github.com/baez90/inetmock/pkg/logging"
"go.uber.org/zap"
"net/http" "net/http"
"gitlab.com/inetmock/inetmock/pkg/api"
"gitlab.com/inetmock/inetmock/pkg/config"
"gitlab.com/inetmock/inetmock/pkg/logging"
"go.uber.org/zap"
) )
const ( const (
@ -21,8 +23,16 @@ type httpHandler struct {
server *http.Server server *http.Server
} }
func (p *httpHandler) Start(config config.HandlerConfig) (err error) { func (p *httpHandler) Start(ctx api.PluginContext, config config.HandlerConfig) (err error) {
options := loadFromConfig(config.Options) p.logger = ctx.Logger().With(
zap.String("protocol_handler", name),
)
var options httpOptions
if options, err = loadFromConfig(config.Options); err != nil {
return
}
p.logger = p.logger.With( p.logger = p.logger.With(
zap.String("handler_name", config.HandlerName), zap.String("handler_name", config.HandlerName),
zap.String("address", config.ListenAddr()), zap.String("address", config.ListenAddr()),

View file

@ -0,0 +1,139 @@
package http_mock_test
import (
"context"
"fmt"
"math/rand"
"net"
"net/http"
"strconv"
"strings"
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/spf13/viper"
api_mock "gitlab.com/inetmock/inetmock/internal/mock/api"
"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"
)
var (
testLogger = logging.NewLogger(zap.NewNop())
availableExtensions = []string{"gif", "html", "ico", "jpg", "png", "txt"}
charSet = "abcdedfghijklmnopqrstABCDEFGHIJKLMNOP"
)
func init() {
rand.Seed(time.Now().Unix())
}
func Benchmark_httpHandler(b *testing.B) {
ctrl := gomock.NewController(b)
defer ctrl.Finish()
listenPort := randomHighPort()
_, handler := setupHandler(b, ctrl, listenPort)
b.ResetTimer()
for i := 0; i < b.N; i++ {
extension := availableExtensions[rand.Intn(len(availableExtensions))]
if resp, err := http.Get(fmt.Sprintf("http://localhost:%d/%s.%s", listenPort, randomString(15), extension)); err != nil {
b.Error(err)
} else if resp.StatusCode != 200 {
b.Error("")
}
}
defer handler.Shutdown(context.Background())
}
func randomString(length int) (result string) {
buffer := strings.Builder{}
for i := 0; i < length; i++ {
buffer.WriteByte(charSet[rand.Intn(len(charSet))])
}
return buffer.String()
}
func setupHandler(b *testing.B, ctrl *gomock.Controller, listenPort uint16) (api.HandlerRegistry, api.ProtocolHandler) {
b.Helper()
registry := api.NewHandlerRegistry()
if err := http_mock.AddHTTPMock(registry); err != nil {
b.Errorf("AddHTTPMock() error = %v", err)
}
handler, ok := registry.HandlerForName("http_mock")
if !ok {
b.Error("handler not registered")
}
mockApp := api_mock.NewMockPluginContext(ctrl)
mockApp.EXPECT().
Logger().
Return(testLogger)
v := viper.New()
v.Set("rules", []map[string]string{
{
"pattern": ".*\\.(?i)gif",
"response": "./../../assets/fakeFiles/default.gif",
},
{
"pattern": ".*\\.(?i)html",
"response": "./../../assets/fakeFiles/default.html",
},
{
"pattern": ".*\\.(?i)ico",
"response": "./../../assets/fakeFiles/default.ico",
},
{
"pattern": ".*\\.(?i)jpg",
"response": "./../../assets/fakeFiles/default.jpg",
},
{
"pattern": ".*\\.(?i)png",
"response": "./../../assets/fakeFiles/default.png",
},
{
"pattern": ".*\\.(?i)txt",
"response": "./../../assets/fakeFiles/default.txt",
},
})
handlerConfig := config.HandlerConfig{
HandlerName: "http_test",
Port: listenPort,
ListenAddress: "localhost",
Options: v,
}
if err := handler.Start(mockApp, handlerConfig); err != nil {
b.Error(err)
b.FailNow()
}
return registry, handler
}
func randomHighPort() uint16 {
var err error
var listener net.Listener
defer func() {
if listener != nil {
_ = listener.Close()
}
}()
for {
if listener, err = net.Listen("tcp", ":0"); err == nil {
parts := strings.Split(listener.Addr().String(), ":")
port, _ := strconv.Atoi(parts[len(parts)-1])
return uint16(port)
}
}
}

View file

@ -1,48 +0,0 @@
package http_mock
import (
"github.com/baez90/inetmock/pkg/api"
"github.com/baez90/inetmock/pkg/logging"
"github.com/baez90/inetmock/pkg/metrics"
"github.com/prometheus/client_golang/prometheus"
"go.uber.org/zap"
)
var (
totalRequestCounter *prometheus.CounterVec
requestDurationHistogram *prometheus.HistogramVec
)
func init() {
var err error
var logger logging.Logger
if logger, err = logging.CreateLogger(); err != nil {
panic(err)
}
logger = logger.With(
zap.String("protocol_handler", name),
)
if totalRequestCounter, err = metrics.Counter(
name,
"total_requests",
"",
handlerNameLblName,
ruleMatchedLblName,
); err != nil {
panic(err)
}
if requestDurationHistogram, err = metrics.Histogram(
name,
"request_duration",
"",
nil,
handlerNameLblName,
); err != nil {
panic(err)
}
api.Registry().RegisterHandler(name, func() api.ProtocolHandler {
return &httpHandler{
logger: logger,
}
})
}

View file

@ -1,19 +1,44 @@
//go:generate go-enum -f $GOFILE --lower --marshal --names
package http_mock package http_mock
import ( import (
"github.com/spf13/viper" "net/http"
"path/filepath"
"regexp" "regexp"
"github.com/spf13/viper"
) )
const ( var (
rulesConfigKey = "rules" ruleValueSelectors = map[RequestMatchTarget]ruleValueSelector{
patternConfigKey = "pattern" RequestMatchTargetHeader: func(req *http.Request, targetKey string) string {
responseConfigKey = "response" return req.Header.Get(targetKey)
},
RequestMatchTargetPath: func(req *http.Request, _ string) string {
return req.URL.Path
},
}
) )
/* ENUM(
Path,
Header
)
*/
type RequestMatchTarget int
func (x RequestMatchTarget) Matches(req *http.Request, targetKey string, regex *regexp.Regexp) bool {
val := ruleValueSelectors[x](req, targetKey)
return regex.MatchString(val)
}
type ruleValueSelector func(req *http.Request, targetKey string) string
type targetRule struct { type targetRule struct {
pattern *regexp.Regexp pattern *regexp.Regexp
response string response string
requestMatchTarget RequestMatchTarget
targetKey string
} }
func (tr targetRule) Pattern() *regexp.Regexp { func (tr targetRule) Pattern() *regexp.Regexp {
@ -28,20 +53,44 @@ type httpOptions struct {
Rules []targetRule Rules []targetRule
} }
func loadFromConfig(config *viper.Viper) (options httpOptions) { func loadFromConfig(config *viper.Viper) (options httpOptions, err error) {
anonRules := config.Get(rulesConfigKey).([]interface{}) type tmpCfg struct {
Pattern string
Response string
Matcher string
Target string
}
for _, i := range anonRules { tmpRules := struct {
innerData := i.(map[interface{}]interface{}) Rules []tmpCfg
}{}
if rulePattern, err := regexp.Compile(innerData[patternConfigKey].(string)); err == nil { if err = config.Unmarshal(&tmpRules); err != nil {
options.Rules = append(options.Rules, targetRule{ return
pattern: rulePattern, }
response: innerData[responseConfigKey].(string),
}) for _, i := range tmpRules.Rules {
} else { var rulePattern *regexp.Regexp
panic(err) var matchTargetValue RequestMatchTarget
var absoluteResponsePath string
var parseErr error
if rulePattern, parseErr = regexp.Compile(i.Pattern); parseErr != nil {
continue
} }
if matchTargetValue, parseErr = ParseRequestMatchTarget(i.Matcher); parseErr != nil {
matchTargetValue = RequestMatchTargetPath
}
if absoluteResponsePath, parseErr = filepath.Abs(i.Response); parseErr != nil {
}
options.Rules = append(options.Rules, targetRule{
pattern: rulePattern,
response: absoluteResponsePath,
requestMatchTarget: matchTargetValue,
targetKey: i.Target,
})
} }
return return

View file

@ -0,0 +1,114 @@
package http_mock
import (
"path/filepath"
"reflect"
"regexp"
"strings"
"testing"
"github.com/spf13/viper"
)
func Test_loadFromConfig(t *testing.T) {
type args struct {
config string
}
tests := []struct {
name string
args args
wantOptions httpOptions
wantErr bool
}{
{
name: "Parse default config",
args: args{
config: `
rules:
- pattern: ".*\\.(?i)exe"
response: ./assets/fakeFiles/sample.exe
`,
},
wantOptions: httpOptions{
Rules: []targetRule{
{
pattern: regexp.MustCompile(".*\\.(?i)exe"),
response: func() string {
p, _ := filepath.Abs("./assets/fakeFiles/sample.exe")
return p
}(),
requestMatchTarget: RequestMatchTargetPath,
targetKey: "",
},
},
},
wantErr: false,
},
{
name: "Parse config with path matcher",
args: args{
config: `
rules:
- pattern: ".*\\.(?i)exe"
matcher: Path
response: ./assets/fakeFiles/sample.exe
`,
},
wantOptions: httpOptions{
Rules: []targetRule{
{
pattern: regexp.MustCompile(".*\\.(?i)exe"),
response: func() string {
p, _ := filepath.Abs("./assets/fakeFiles/sample.exe")
return p
}(),
requestMatchTarget: RequestMatchTargetPath,
targetKey: "",
},
},
},
wantErr: false,
},
{
name: "Parse config with header matcher",
args: args{
config: `
rules:
- pattern: "^application/octet-stream$"
target: Content-Type
matcher: Header
response: ./assets/fakeFiles/sample.exe
`,
},
wantOptions: httpOptions{
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) {
v := viper.New()
v.SetConfigType("yaml")
_ = v.ReadConfig(strings.NewReader(tt.args.config))
gotOptions, err := loadFromConfig(v)
if (err != nil) != tt.wantErr {
t.Errorf("loadFromConfig() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(gotOptions, tt.wantOptions) {
t.Errorf("loadFromConfig() gotOptions = %v, want %v", gotOptions, tt.wantOptions)
}
})
}
}

View file

@ -2,16 +2,16 @@ package http_mock
import ( import (
"bytes" "bytes"
"github.com/baez90/inetmock/pkg/logging"
"github.com/prometheus/client_golang/prometheus"
"go.uber.org/zap"
"net/http" "net/http"
"regexp"
"strconv" "strconv"
"github.com/prometheus/client_golang/prometheus"
"gitlab.com/inetmock/inetmock/pkg/logging"
"go.uber.org/zap"
) )
type route struct { type route struct {
pattern *regexp.Regexp rule targetRule
handler http.Handler handler http.Handler
} }
@ -21,22 +21,23 @@ type RegexpHandler struct {
routes []*route routes []*route
} }
func (h *RegexpHandler) Handler(pattern *regexp.Regexp, handler http.Handler) { func (h *RegexpHandler) Handler(rule targetRule, handler http.Handler) {
h.routes = append(h.routes, &route{pattern, handler}) h.routes = append(h.routes, &route{rule, handler})
} }
func (h *RegexpHandler) HandleFunc(pattern *regexp.Regexp, handler func(http.ResponseWriter, *http.Request)) { func (h *RegexpHandler) HandleFunc(rule targetRule, handler func(http.ResponseWriter, *http.Request)) {
h.routes = append(h.routes, &route{pattern, http.HandlerFunc(handler)}) h.routes = append(h.routes, &route{rule, http.HandlerFunc(handler)})
} }
func (h *RegexpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h *RegexpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
timer := prometheus.NewTimer(requestDurationHistogram.WithLabelValues(h.handlerName)) timer := prometheus.NewTimer(requestDurationHistogram.WithLabelValues(h.handlerName))
defer timer.ObserveDuration() defer timer.ObserveDuration()
for _, route := range h.routes { for idx := range h.routes {
if route.pattern.MatchString(r.URL.Path) { rule := h.routes[idx].rule
if h.routes[idx].rule.requestMatchTarget.Matches(r, rule.targetKey, rule.pattern) {
totalRequestCounter.WithLabelValues(h.handlerName, strconv.FormatBool(true)).Inc() totalRequestCounter.WithLabelValues(h.handlerName, strconv.FormatBool(true)).Inc()
route.handler.ServeHTTP(w, r) h.routes[idx].handler.ServeHTTP(w, r)
return return
} }
} }
@ -52,7 +53,7 @@ func (h *RegexpHandler) setupRoute(rule targetRule) {
zap.String("response", rule.Response()), zap.String("response", rule.Response()),
) )
h.Handler(rule.Pattern(), createHandlerForTarget(h.logger, rule.response)) h.Handler(rule, createHandlerForTarget(h.logger, rule.response))
} }
func createHandlerForTarget(logger logging.Logger, targetPath string) http.Handler { func createHandlerForTarget(logger logging.Logger, targetPath string) http.Handler {

View file

@ -0,0 +1,44 @@
package http_mock
import (
"github.com/prometheus/client_golang/prometheus"
"gitlab.com/inetmock/inetmock/pkg/api"
"gitlab.com/inetmock/inetmock/pkg/metrics"
)
var (
totalRequestCounter *prometheus.CounterVec
requestDurationHistogram *prometheus.HistogramVec
)
func AddHTTPMock(registry api.HandlerRegistry) (err error) {
if totalRequestCounter == nil {
if totalRequestCounter, err = metrics.Counter(
name,
"total_requests",
"",
handlerNameLblName,
ruleMatchedLblName,
); err != nil {
return
}
}
if requestDurationHistogram == nil {
if requestDurationHistogram, err = metrics.Histogram(
name,
"request_duration",
"",
nil,
handlerNameLblName,
); err != nil {
return
}
}
registry.RegisterHandler(name, func() api.ProtocolHandler {
return &httpHandler{}
})
return
}

View file

@ -4,12 +4,13 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"github.com/baez90/inetmock/pkg/api" "net/http"
"github.com/baez90/inetmock/pkg/config"
"github.com/baez90/inetmock/pkg/logging" "gitlab.com/inetmock/inetmock/pkg/api"
"gitlab.com/inetmock/inetmock/pkg/config"
"gitlab.com/inetmock/inetmock/pkg/logging"
"go.uber.org/zap" "go.uber.org/zap"
"gopkg.in/elazarl/goproxy.v1" "gopkg.in/elazarl/goproxy.v1"
"net/http"
) )
const ( const (
@ -22,28 +23,28 @@ type httpProxy struct {
server *http.Server server *http.Server
} }
func (h *httpProxy) Start(config config.HandlerConfig) (err error) { func (h *httpProxy) Start(ctx api.PluginContext, cfg config.HandlerConfig) (err error) {
var opts httpProxyOptions var opts httpProxyOptions
if err = config.Options.Unmarshal(&opts); err != nil { if err = cfg.Options.Unmarshal(&opts); err != nil {
return return
} }
listenAddr := config.ListenAddr() listenAddr := cfg.ListenAddr()
h.server = &http.Server{Addr: listenAddr, Handler: h.proxy} h.server = &http.Server{Addr: listenAddr, Handler: h.proxy}
h.logger = h.logger.With( h.logger = h.logger.With(
zap.String("handler_name", config.HandlerName), zap.String("handler_name", cfg.HandlerName),
zap.String("address", listenAddr), zap.String("address", listenAddr),
) )
tlsConfig := api.ServicesInstance().CertStore().TLSConfig() tlsConfig := ctx.CertStore().TLSConfig()
proxyHandler := &proxyHttpHandler{ proxyHandler := &proxyHttpHandler{
handlerName: config.HandlerName, handlerName: cfg.HandlerName,
options: opts, options: opts,
logger: h.logger, logger: h.logger,
} }
proxyHttpsHandler := &proxyHttpsHandler{ proxyHttpsHandler := &proxyHttpsHandler{
handlerName: config.HandlerName, handlerName: cfg.HandlerName,
tlsConfig: tlsConfig, tlsConfig: tlsConfig,
logger: h.logger, logger: h.logger,
} }

View file

@ -3,12 +3,13 @@ package http_proxy
import ( import (
"context" "context"
"crypto/tls" "crypto/tls"
"github.com/baez90/inetmock/pkg/logging"
"github.com/prometheus/client_golang/prometheus"
"go.uber.org/zap"
"gopkg.in/elazarl/goproxy.v1"
"net/http" "net/http"
"net/url" "net/url"
"github.com/prometheus/client_golang/prometheus"
"gitlab.com/inetmock/inetmock/pkg/logging"
"go.uber.org/zap"
"gopkg.in/elazarl/goproxy.v1"
) )
type proxyHttpHandler struct { type proxyHttpHandler struct {

View file

@ -1,10 +1,10 @@
package http_proxy package http_proxy
import ( import (
"github.com/baez90/inetmock/pkg/api"
"github.com/baez90/inetmock/pkg/logging"
"github.com/baez90/inetmock/pkg/metrics"
"github.com/prometheus/client_golang/prometheus" "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" "go.uber.org/zap"
"gopkg.in/elazarl/goproxy.v1" "gopkg.in/elazarl/goproxy.v1"
) )
@ -16,32 +16,33 @@ var (
requestDurationHistogram *prometheus.HistogramVec requestDurationHistogram *prometheus.HistogramVec
) )
func init() { func AddHTTPProxy(registry api.HandlerRegistry) (err error) {
var err error
var logger logging.Logger var logger logging.Logger
if logger, err = logging.CreateLogger(); err != nil { if logger, err = logging.CreateLogger(); err != nil {
panic(err) return
} }
logger = logger.With( logger = logger.With(
zap.String("protocol_handler", name), zap.String("protocol_handler", name),
) )
if totalRequestCounter, err = metrics.Counter(name, "total_requests", "", handlerNameLblName); err != nil { if totalRequestCounter, err = metrics.Counter(name, "total_requests", "", handlerNameLblName); err != nil {
panic(err) return
} }
if requestDurationHistogram, err = metrics.Histogram(name, "request_duration", "", nil, handlerNameLblName); err != nil { if requestDurationHistogram, err = metrics.Histogram(name, "request_duration", "", nil, handlerNameLblName); err != nil {
panic(err) return
} }
if totalHttpsRequestCounter, err = metrics.Counter(name, "total_https_requests", "", handlerNameLblName); err != nil { if totalHttpsRequestCounter, err = metrics.Counter(name, "total_https_requests", "", handlerNameLblName); err != nil {
panic(err) return
} }
api.Registry().RegisterHandler(name, func() api.ProtocolHandler { registry.RegisterHandler(name, func() api.ProtocolHandler {
return &httpProxy{ return &httpProxy{
logger: logger, logger: logger,
proxy: goproxy.NewProxyHttpServer(), proxy: goproxy.NewProxyHttpServer(),
} }
}) })
return
} }

View file

@ -3,11 +3,13 @@ package metrics_exporter
import ( import (
"context" "context"
"errors" "errors"
"github.com/baez90/inetmock/pkg/config"
"github.com/baez90/inetmock/pkg/logging"
"github.com/prometheus/client_golang/prometheus/promhttp"
"go.uber.org/zap"
"net/http" "net/http"
"github.com/prometheus/client_golang/prometheus/promhttp"
"gitlab.com/inetmock/inetmock/pkg/api"
"gitlab.com/inetmock/inetmock/pkg/config"
"gitlab.com/inetmock/inetmock/pkg/logging"
"go.uber.org/zap"
) )
const ( const (
@ -19,7 +21,7 @@ type metricsExporter struct {
server *http.Server server *http.Server
} }
func (m *metricsExporter) Start(config config.HandlerConfig) (err error) { func (m *metricsExporter) Start(_ api.PluginContext, config config.HandlerConfig) (err error) {
exporterOptions := metricsExporterOptions{} exporterOptions := metricsExporterOptions{}
if err = config.Options.Unmarshal(&exporterOptions); err != nil { if err = config.Options.Unmarshal(&exporterOptions); err != nil {
return return

View file

@ -1,20 +0,0 @@
package metrics_exporter
import (
"github.com/baez90/inetmock/pkg/api"
"github.com/baez90/inetmock/pkg/logging"
"go.uber.org/zap"
)
func init() {
logger, _ := logging.CreateLogger()
logger = logger.With(
zap.String("protocol_handler", name),
)
api.Registry().RegisterHandler(name, func() api.ProtocolHandler {
return &metricsExporter{
logger: logger,
}
})
}

View file

@ -0,0 +1,24 @@
package metrics_exporter
import (
"gitlab.com/inetmock/inetmock/pkg/api"
"gitlab.com/inetmock/inetmock/pkg/logging"
"go.uber.org/zap"
)
func AddMetricsExporter(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),
)
registry.RegisterHandler(name, func() api.ProtocolHandler {
return &metricsExporter{
logger: logger,
}
})
return
}

View file

@ -4,15 +4,15 @@ import (
"context" "context"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"github.com/baez90/inetmock/pkg/api"
"github.com/baez90/inetmock/pkg/cert"
"github.com/baez90/inetmock/pkg/config"
"github.com/baez90/inetmock/pkg/logging"
"github.com/google/uuid"
"github.com/prometheus/client_golang/prometheus"
"go.uber.org/zap"
"net" "net"
"sync" "sync"
"github.com/google/uuid"
"github.com/prometheus/client_golang/prometheus"
"gitlab.com/inetmock/inetmock/pkg/api"
"gitlab.com/inetmock/inetmock/pkg/config"
"gitlab.com/inetmock/inetmock/pkg/logging"
"go.uber.org/zap"
) )
const ( const (
@ -24,16 +24,16 @@ type tlsInterceptor struct {
options tlsOptions options tlsOptions
logger logging.Logger logger logging.Logger
listener net.Listener listener net.Listener
certStore cert.Store
shutdownRequested bool shutdownRequested bool
currentConnectionsCount *sync.WaitGroup currentConnectionsCount *sync.WaitGroup
currentConnections map[uuid.UUID]*proxyConn currentConnections map[uuid.UUID]*proxyConn
connectionsMutex *sync.Mutex connectionsMutex *sync.Mutex
} }
func (t *tlsInterceptor) Start(config config.HandlerConfig) (err error) { func (t *tlsInterceptor) Start(ctx api.PluginContext, config config.HandlerConfig) (err error) {
t.name = config.HandlerName t.name = config.HandlerName
if err = config.Options.Unmarshal(&config.Options); err != nil {
if err = config.Options.Unmarshal(&t.options); err != nil {
return return
} }
@ -43,7 +43,7 @@ func (t *tlsInterceptor) Start(config config.HandlerConfig) (err error) {
zap.String("Target", t.options.Target.address()), zap.String("Target", t.options.Target.address()),
) )
if t.listener, err = tls.Listen("tcp", config.ListenAddr(), api.ServicesInstance().CertStore().TLSConfig()); err != nil { if t.listener, err = tls.Listen("tcp", config.ListenAddr(), ctx.CertStore().TLSConfig()); err != nil {
t.logger.Fatal( t.logger.Fatal(
"failed to create tls listener", "failed to create tls listener",
zap.Error(err), zap.Error(err),

View file

@ -21,10 +21,10 @@ func chanFromConn(conn net.Conn) chan []byte {
for { for {
n, err := conn.Read(b) n, err := conn.Read(b)
if n > 0 { if n > 0 {
res := make([]byte, n) res := bufferPool.Get().([]byte)
// Copy the buffer so it doesn't get changed while read by the recipient. // Copy the buffer so it doesn't get changed while read by the recipient.
copy(res, b[:n]) copy(res, b[:n])
c <- res c <- res[:n]
} }
if err != nil { if err != nil {
c <- nil c <- nil

View file

@ -1,13 +1,14 @@
package tls_interceptor package tls_interceptor
import ( import (
"github.com/baez90/inetmock/pkg/api" "sync"
"github.com/baez90/inetmock/pkg/logging"
"github.com/baez90/inetmock/pkg/metrics"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/prometheus/client_golang/prometheus" "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" "go.uber.org/zap"
"sync"
) )
var ( var (
@ -17,8 +18,7 @@ var (
requestDurationHistogram *prometheus.HistogramVec requestDurationHistogram *prometheus.HistogramVec
) )
func init() { func AddTLSInterceptor(registry api.HandlerRegistry) (err error) {
var err error
var logger logging.Logger var logger logging.Logger
if logger, err = logging.CreateLogger(); err != nil { if logger, err = logging.CreateLogger(); err != nil {
panic(err) panic(err)
@ -28,16 +28,16 @@ func init() {
) )
if handledRequestCounter, err = metrics.Counter(name, "handled_requests", "", labelNames...); err != nil { if handledRequestCounter, err = metrics.Counter(name, "handled_requests", "", labelNames...); err != nil {
panic(err) return
} }
if openConnectionsGauge, err = metrics.Gauge(name, "open_connections", "", labelNames...); err != nil { if openConnectionsGauge, err = metrics.Gauge(name, "open_connections", "", labelNames...); err != nil {
panic(err) return
} }
if requestDurationHistogram, err = metrics.Histogram(name, "request_duration", "", nil, labelNames...); err != nil { if requestDurationHistogram, err = metrics.Histogram(name, "request_duration", "", nil, labelNames...); err != nil {
panic(err)
} }
api.Registry().RegisterHandler(name, func() api.ProtocolHandler { registry.RegisterHandler(name, func() api.ProtocolHandler {
return &tlsInterceptor{ return &tlsInterceptor{
logger: logger, logger: logger,
currentConnectionsCount: &sync.WaitGroup{}, currentConnectionsCount: &sync.WaitGroup{},
@ -45,4 +45,5 @@ func init() {
connectionsMutex: &sync.Mutex{}, connectionsMutex: &sync.Mutex{},
} }
}) })
return
} }