Merge commit '9a3c410ee8dfe2a380dc1ab36a5778e2c0f7049b' as 'api'
This commit is contained in:
commit
80fa8f45b9
154 changed files with 223 additions and 10159 deletions
|
@ -1,28 +0,0 @@
|
||||||
###############
|
|
||||||
# Directories #
|
|
||||||
###############
|
|
||||||
|
|
||||||
.git/
|
|
||||||
.github/
|
|
||||||
.idea/
|
|
||||||
.vscode/
|
|
||||||
deploy/
|
|
||||||
dist/
|
|
||||||
doc/
|
|
||||||
|
|
||||||
#########
|
|
||||||
# Files #
|
|
||||||
#########
|
|
||||||
|
|
||||||
.gitattributes
|
|
||||||
.gitignore
|
|
||||||
.dockerignore
|
|
||||||
.goreleaser.yml
|
|
||||||
*.out
|
|
||||||
main
|
|
||||||
inetmock
|
|
||||||
imctl
|
|
||||||
README.md
|
|
||||||
LICENSE
|
|
||||||
Dockerfile
|
|
||||||
config.yaml
|
|
|
@ -1,23 +1,10 @@
|
||||||
root = true
|
root = true
|
||||||
|
|
||||||
[*]
|
[*]
|
||||||
charset = utf-8
|
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
indent_size = 4
|
insert_final_newline = true
|
||||||
|
charset = utf-8
|
||||||
|
|
||||||
|
[*.proto]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
insert_final_newline = false
|
indent_size = 4
|
||||||
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
|
|
||||||
|
|
1
.gitattributes
vendored
1
.gitattributes
vendored
|
@ -1 +0,0 @@
|
||||||
assets/fakeFiles/* filter=lfs diff=lfs merge=lfs -text
|
|
57
.github/workflows/docker-image.yml
vendored
57
.github/workflows/docker-image.yml
vendored
|
@ -1,57 +0,0 @@
|
||||||
name: Docker Image CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ master ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ master ]
|
|
||||||
|
|
||||||
env:
|
|
||||||
IMAGE_NAME: server
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
lfs: true
|
|
||||||
|
|
||||||
- name: Login to GitHub Docker registry
|
|
||||||
run: echo ${{ secrets.GITHUB_TOKEN }} | docker login docker.pkg.github.com -u baez90 --password-stdin
|
|
||||||
|
|
||||||
- name: Build the Docker image
|
|
||||||
run: docker build . --file inetmock.dockerfile --tag $IMAGE_NAME
|
|
||||||
|
|
||||||
- name: Push image to GitHub packages
|
|
||||||
run: |
|
|
||||||
IMAGE_ID=docker.pkg.github.com/${{ github.repository }}/$IMAGE_NAME
|
|
||||||
|
|
||||||
# Change all uppercase to lowercase
|
|
||||||
IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')
|
|
||||||
|
|
||||||
# Strip git ref prefix from version
|
|
||||||
VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
|
|
||||||
|
|
||||||
# Strip "v" prefix from tag name
|
|
||||||
[[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//')
|
|
||||||
|
|
||||||
# Use Docker `latest` tag convention
|
|
||||||
[ "$VERSION" == "master" ] && VERSION=latest
|
|
||||||
|
|
||||||
echo IMAGE_ID=$IMAGE_ID
|
|
||||||
echo VERSION=$VERSION
|
|
||||||
|
|
||||||
docker tag $IMAGE_NAME $IMAGE_ID:$VERSION
|
|
||||||
docker push $IMAGE_ID:$VERSION
|
|
||||||
|
|
||||||
- name: Tag image for Docker Hub
|
|
||||||
run: docker tag $IMAGE_NAME ${GITHUB_REPOSITORY}:latest
|
|
||||||
|
|
||||||
- name: Push latest tag to Docker Hub
|
|
||||||
uses: docker/build-push-action@v1
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
|
||||||
repository: baez90/inetmock
|
|
||||||
tags: latest
|
|
49
.github/workflows/go-build.yml
vendored
49
.github/workflows/go-build.yml
vendored
|
@ -1,49 +0,0 @@
|
||||||
name: Go
|
|
||||||
|
|
||||||
on: [ push, pull_request ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
build:
|
|
||||||
name: Build
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
|
|
||||||
- name: Set up Go 1.15
|
|
||||||
uses: actions/setup-go@v2
|
|
||||||
with:
|
|
||||||
go-version: '^1.15'
|
|
||||||
id: go
|
|
||||||
|
|
||||||
- name: Install Protoc
|
|
||||||
uses: arduino/setup-protoc@master
|
|
||||||
with:
|
|
||||||
version: '3.x'
|
|
||||||
|
|
||||||
- name: Check out code into the Go module directory
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
lfs: true
|
|
||||||
|
|
||||||
- name: Install mockgen
|
|
||||||
run: go get -u github.com/golang/mock/mockgen@latest
|
|
||||||
|
|
||||||
- name: Install go-enuum
|
|
||||||
run: go get -u github.com/abice/go-enum
|
|
||||||
|
|
||||||
- name: Install protoc-gen-go
|
|
||||||
run: go install github.com/golang/protobuf/protoc-gen-go
|
|
||||||
|
|
||||||
- name: Unshallow
|
|
||||||
run: git fetch --prune --unshallow
|
|
||||||
|
|
||||||
- name: Build & test
|
|
||||||
run: make
|
|
||||||
|
|
||||||
- name: Run GoReleaser
|
|
||||||
uses: goreleaser/goreleaser-action@v2
|
|
||||||
with:
|
|
||||||
version: latest
|
|
||||||
args: release --rm-dist
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
26
.gitignore
vendored
26
.gitignore
vendored
|
@ -1,26 +1,2 @@
|
||||||
#########
|
|
||||||
# files #
|
|
||||||
#########
|
|
||||||
|
|
||||||
**/cov.out
|
|
||||||
**/cov-raw.out
|
|
||||||
**/*.so
|
|
||||||
*.key
|
|
||||||
*.pem
|
|
||||||
/inetmock
|
|
||||||
/imctl
|
|
||||||
./main
|
|
||||||
**/*.mock.go
|
|
||||||
**/*_enum.go
|
|
||||||
**/*.pb.go
|
|
||||||
|
|
||||||
###############
|
|
||||||
# directories #
|
|
||||||
###############
|
|
||||||
|
|
||||||
.idea/
|
|
||||||
.vscode/
|
.vscode/
|
||||||
dist/
|
.idea/
|
||||||
out/
|
|
||||||
.task/
|
|
||||||
public/
|
|
|
@ -1,65 +0,0 @@
|
||||||
image: registry.gitlab.com/inetmock/ci-image/go
|
|
||||||
|
|
||||||
stages:
|
|
||||||
- test
|
|
||||||
- build
|
|
||||||
- release
|
|
||||||
- deploy
|
|
||||||
|
|
||||||
test:
|
|
||||||
stage: test
|
|
||||||
script:
|
|
||||||
- task cli-cover-report
|
|
||||||
artifacts:
|
|
||||||
reports:
|
|
||||||
junit: out/report.xml
|
|
||||||
cobertura: out/coverage.xml
|
|
||||||
|
|
||||||
integration-test:
|
|
||||||
stage: test
|
|
||||||
services:
|
|
||||||
- docker:dind
|
|
||||||
script:
|
|
||||||
- task integration-test
|
|
||||||
|
|
||||||
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:
|
|
||||||
GIT_DEPTH: 0
|
|
||||||
script:
|
|
||||||
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
|
||||||
- goreleaser release --rm-dist
|
|
||||||
|
|
||||||
pages:
|
|
||||||
stage: deploy
|
|
||||||
image: registry.gitlab.com/inetmock/ci-image/mdbook
|
|
||||||
only:
|
|
||||||
refs:
|
|
||||||
- master
|
|
||||||
- tags
|
|
||||||
script:
|
|
||||||
- mdbook build -d ./../public ./docs
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- public
|
|
|
@ -1,3 +0,0 @@
|
||||||
terminal:
|
|
||||||
image: registry.gitlab.com/inetmock/ci-image
|
|
||||||
script: sleep 60
|
|
136
.golangci.yml
136
.golangci.yml
|
@ -1,136 +0,0 @@
|
||||||
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"
|
|
|
@ -1,84 +0,0 @@
|
||||||
# This is an example goreleaser.yaml file with some sane defaults.
|
|
||||||
# Make sure to check the documentation at http://goreleaser.com
|
|
||||||
before:
|
|
||||||
hooks:
|
|
||||||
# You may remove this if you don't use go modules.
|
|
||||||
- task generate
|
|
||||||
builds:
|
|
||||||
- id: "inetmock"
|
|
||||||
binary: inetmock
|
|
||||||
main: ./cmd/inetmock/
|
|
||||||
ldflags:
|
|
||||||
- -w -s
|
|
||||||
env:
|
|
||||||
- CGO_ENABLED=0
|
|
||||||
goos:
|
|
||||||
- linux
|
|
||||||
- darwin
|
|
||||||
goarch:
|
|
||||||
- amd64
|
|
||||||
- id: "imctl"
|
|
||||||
binary: imctl
|
|
||||||
main: ./cmd/imctl/
|
|
||||||
ldflags:
|
|
||||||
- -w -s
|
|
||||||
goos:
|
|
||||||
- linux
|
|
||||||
- freebsd
|
|
||||||
- darwin
|
|
||||||
- windows
|
|
||||||
goarch:
|
|
||||||
- amd64
|
|
||||||
archives:
|
|
||||||
- id: inetmock
|
|
||||||
builds:
|
|
||||||
- inetmock
|
|
||||||
name_template: "{{ .ProjectName }}_server_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
|
|
||||||
replacements:
|
|
||||||
amd64: x86_64
|
|
||||||
wrap_in_directory: true
|
|
||||||
files:
|
|
||||||
- config.yaml
|
|
||||||
- "*.so"
|
|
||||||
- id: imctl
|
|
||||||
builds:
|
|
||||||
- imctl
|
|
||||||
name_template: "{{ .ProjectName }}_cli_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
|
|
||||||
replacements:
|
|
||||||
amd64: x86_64
|
|
||||||
wrap_in_directory: true
|
|
||||||
files: [ ]
|
|
||||||
checksum:
|
|
||||||
name_template: 'checksums.txt'
|
|
||||||
snapshot:
|
|
||||||
name_template: "{{ .Tag }}-next"
|
|
||||||
changelog:
|
|
||||||
sort: asc
|
|
||||||
filters:
|
|
||||||
exclude:
|
|
||||||
- '^docs:'
|
|
||||||
- '^test:'
|
|
||||||
|
|
||||||
release:
|
|
||||||
gitlab:
|
|
||||||
owner: inetmock
|
|
||||||
name: inetmock
|
|
||||||
|
|
||||||
dockers:
|
|
||||||
- ids:
|
|
||||||
- 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/
|
|
21
LICENSE
21
LICENSE
|
@ -1,21 +0,0 @@
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2020 Peter Kurfer
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
31
README.md
31
README.md
|
@ -1,30 +1 @@
|
||||||
# INetMock
|
# INetMock protobuf API
|
||||||
|
|
||||||
|
|
||||||
[![pipeline status](https://gitlab.com/inetmock/inetmock/badges/master/pipeline.svg)](https://gitlab.com/inetmock/inetmock/-/commits/master)
|
|
||||||
[![coverage report](https://gitlab.com/inetmock/inetmock/badges/master/coverage.svg)](https://gitlab.com/inetmock/inetmock/-/commits/master)
|
|
||||||
[![Go Report Card](https://goreportcard.com/badge/gitlab.com/inetmock/inetmock)](https://goreportcard.com/report/gitlab.com/inetmock/inetmock)
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
INetMock right now does **not** implement so many protocols like INetSim. In fact it is only able to respond to HTTP,
|
|
||||||
HTTPS, DNS, DNS-over-TLS (DoT) requests and to act as an HTTP proxy. 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.
|
|
||||||
|
|
||||||
A second advantage is that INetMock is a complete rewrite in Go.
|
|
||||||
It has a way smaller memory footprint and far better startup and shutdown times.
|
|
||||||
It also does not enforce `root` privileges as it is also possible to run the application with the required capabilities to open ports e.g. with SystemD (a sample unit file can be found in the `deploy/` directory).
|
|
||||||
|
|
||||||
_This project is still heavy work-in-progress. There may be breaking changes at any time. There's no guarantee for anything except no kittens will be harmed!_
|
|
||||||
|
|
||||||
## Docs
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
Please create an issue for any proposal, feature requests, found bug,... 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.
|
|
116
Taskfile.yml
116
Taskfile.yml
|
@ -1,116 +0,0 @@
|
||||||
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 "%p "
|
|
||||||
BENCHMARKS:
|
|
||||||
sh: find . -type f -name "*_bench_test.go"
|
|
||||||
|
|
||||||
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/proto/ --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --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 }}
|
|
||||||
- cmd: go test -coverprofile={{ .OUT_DIR }}/cov-raw.out -covermode count -v ./... 2>&1 | tee {{ .OUT_DIR }}/test_output
|
|
||||||
ignore_error: true
|
|
||||||
- cat {{ .OUT_DIR }}/test_output | go-junit-report -set-exit-code > {{ .OUT_DIR }}/report.xml
|
|
||||||
- grep -v "generated" {{ .OUT_DIR }}/cov-raw.out > {{ .OUT_DIR }}/cov.out
|
|
||||||
- gocover-cobertura < {{ .OUT_DIR }}/cov.out > {{ .OUT_DIR }}/coverage.xml
|
|
||||||
- rm -f {{ .OUT_DIR }}/cov-raw.out
|
|
||||||
|
|
||||||
integration-test:
|
|
||||||
deps:
|
|
||||||
- generate
|
|
||||||
cmds:
|
|
||||||
- |
|
|
||||||
{{ range .BENCHMARKS | splitLines -}}
|
|
||||||
go test -bench=. {{ . }}
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
docs:
|
|
||||||
cmds:
|
|
||||||
- mdbook build -d ./../public ./docs
|
|
|
@ -1,5 +0,0 @@
|
||||||
-----BEGIN PRIVATE KEY-----
|
|
||||||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgEk8KFe6Vl3xefH82
|
|
||||||
Na91uPQkBz4D/s1xE+59+kaRM1+hRANCAASnjLNiOc4UNsRBIQN+FOv8CEd6ftDH
|
|
||||||
Egg2dWUgGCFsgE2VaEG+jOwamBzTjCWbr0azIc+Brupd0CAChq8hVeaM
|
|
||||||
-----END PRIVATE KEY-----
|
|
|
@ -1,14 +0,0 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIICGjCCAb+gAwIBAgIQZutydXTVGWPa32GhLwn4tDAKBggqhkjOPQQDAjBcMRAw
|
|
||||||
DgYDVQQGEwdnZXJtYW55MQwwCgYDVQQIEwNOUlcxETAPBgNVBAcTCERvcnRtdW5k
|
|
||||||
MREwDwYDVQQKEwhJTmV0TW9jazEUMBIGA1UEAxMLSU5ldE1vY2sgQ0EwIBcNMDEw
|
|
||||||
MjAxMDgyODA5WhgPMjA1MTAxMjAwODI4MDlaMFwxEDAOBgNVBAYTB2dlcm1hbnkx
|
|
||||||
DDAKBgNVBAgTA05SVzERMA8GA1UEBxMIRG9ydG11bmQxETAPBgNVBAoTCElOZXRN
|
|
||||||
b2NrMRQwEgYDVQQDEwtJTmV0TW9jayBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEH
|
|
||||||
A0IABKeMs2I5zhQ2xEEhA34U6/wIR3p+0McSCDZ1ZSAYIWyATZVoQb6M7BqYHNOM
|
|
||||||
JZuvRrMhz4Gu6l3QIAKGryFV5oyjYTBfMA4GA1UdDwEB/wQEAwIChDAdBgNVHSUE
|
|
||||||
FjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
|
|
||||||
FgQUkD5REm4Z34O8em3l5cXmHgd3S4swCgYIKoZIzj0EAwIDSQAwRgIhAJ5xWTz/
|
|
||||||
/51Jyo7sO4hi3FhyDYJXeC0koRSQCvMggWuPAiEAjpgtX/UFukTAfTFsDp8q3AXZ
|
|
||||||
Kn0ejVWjmkh4K7nEJ5s=
|
|
||||||
-----END CERTIFICATE-----
|
|
BIN
assets/fakeFiles/default.gif
(Stored with Git LFS)
BIN
assets/fakeFiles/default.gif
(Stored with Git LFS)
Binary file not shown.
BIN
assets/fakeFiles/default.html
(Stored with Git LFS)
BIN
assets/fakeFiles/default.html
(Stored with Git LFS)
Binary file not shown.
BIN
assets/fakeFiles/default.ico
(Stored with Git LFS)
BIN
assets/fakeFiles/default.ico
(Stored with Git LFS)
Binary file not shown.
BIN
assets/fakeFiles/default.jpg
(Stored with Git LFS)
BIN
assets/fakeFiles/default.jpg
(Stored with Git LFS)
Binary file not shown.
BIN
assets/fakeFiles/default.png
(Stored with Git LFS)
BIN
assets/fakeFiles/default.png
(Stored with Git LFS)
Binary file not shown.
BIN
assets/fakeFiles/default.txt
(Stored with Git LFS)
BIN
assets/fakeFiles/default.txt
(Stored with Git LFS)
Binary file not shown.
|
@ -1,41 +0,0 @@
|
||||||
# Runtime layer
|
|
||||||
FROM alpine:3.13
|
|
||||||
|
|
||||||
# Create appuser and group.
|
|
||||||
ARG USER=inetmock
|
|
||||||
ARG GROUP=inetmock
|
|
||||||
ARG USER_ID=10001
|
|
||||||
ARG GROUP_ID=10001
|
|
||||||
|
|
||||||
RUN addgroup -S -g "${GROUP_ID}" "${GROUP}" && \
|
|
||||||
adduser \
|
|
||||||
--disabled-password \
|
|
||||||
--gecos "" \
|
|
||||||
--home "/nonexistent" \
|
|
||||||
--shell "/sbin/nologin" \
|
|
||||||
--no-create-home \
|
|
||||||
-G "${GROUP}" \
|
|
||||||
--uid "${USER_ID}" \
|
|
||||||
"${USER}"
|
|
||||||
|
|
||||||
COPY --chown=$USER:$GROUP inetmock imctl /usr/lib/inetmock/bin/
|
|
||||||
COPY --chown=$USER:$GROUP assets/fakeFiles /var/lib/inetmock/fakeFiles/
|
|
||||||
COPY --chown=$USER:$GROUP assets/demoCA /var/lib/inetmock/ca
|
|
||||||
COPY config-container.yaml /etc/inetmock/config.yaml
|
|
||||||
|
|
||||||
RUN mkdir -p /var/run/inetmock /var/lib/inetmock/certs /usr/lib/inetmock && \
|
|
||||||
chown -R $USER:$GROUP /var/run/inetmock /var/lib/inetmock /usr/lib/inetmock && \
|
|
||||||
apk add -U --no-cache libcap
|
|
||||||
|
|
||||||
RUN ln -s /usr/lib/inetmock/bin/inetmock /usr/bin/inetmock && \
|
|
||||||
ln -s /usr/lib/inetmock/bin/imctl /usr/bin/imctl && \
|
|
||||||
setcap 'cap_net_bind_service=+ep' /usr/lib/inetmock/bin/inetmock
|
|
||||||
|
|
||||||
HEALTHCHECK --interval=5s --timeout=1s \
|
|
||||||
CMD imctl --socket-path /var/run/inetmock/inetmock.sock health container
|
|
||||||
|
|
||||||
USER $USER
|
|
||||||
|
|
||||||
VOLUME [ "/var/lib/inetmock/ca", "/var/lib/inetmock/certs" ]
|
|
||||||
|
|
||||||
ENTRYPOINT ["inetmock"]
|
|
|
@ -1,118 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/format"
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/rpc"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
listSinksCmd = &cobra.Command{
|
|
||||||
Use: "list",
|
|
||||||
Aliases: []string{"ls", "dir"},
|
|
||||||
Short: "List all subscribed sinks",
|
|
||||||
RunE: runListSinks,
|
|
||||||
}
|
|
||||||
addFileCmd = &cobra.Command{
|
|
||||||
Use: "add-file",
|
|
||||||
Aliases: []string{"add"},
|
|
||||||
Short: "subscribe events to a file",
|
|
||||||
Args: cobra.ExactArgs(1),
|
|
||||||
RunE: runAddFile,
|
|
||||||
}
|
|
||||||
|
|
||||||
removeFileCmd = &cobra.Command{
|
|
||||||
Use: "remove-file",
|
|
||||||
Aliases: []string{"rm", "del"},
|
|
||||||
Short: "remove file subscription",
|
|
||||||
Args: cobra.ExactArgs(1),
|
|
||||||
RunE: runRemoveFile,
|
|
||||||
}
|
|
||||||
|
|
||||||
readFileCmd = &cobra.Command{
|
|
||||||
Use: "read-file",
|
|
||||||
Aliases: []string{"cat"},
|
|
||||||
Short: "reads an audit file and prints the events",
|
|
||||||
Args: cobra.ExactArgs(1),
|
|
||||||
RunE: runReadFile,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
type printableSink struct {
|
|
||||||
Name string
|
|
||||||
}
|
|
||||||
|
|
||||||
func runListSinks(*cobra.Command, []string) (err error) {
|
|
||||||
auditClient := rpc.NewAuditClient(conn)
|
|
||||||
ctx, cancel := context.WithTimeout(cliApp.Context(), grpcTimeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
var resp *rpc.ListSinksResponse
|
|
||||||
if resp, err = auditClient.ListSinks(ctx, &rpc.ListSinksRequest{}); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var sinks []printableSink
|
|
||||||
for _, s := range resp.Sinks {
|
|
||||||
sinks = append(sinks, printableSink{Name: s})
|
|
||||||
}
|
|
||||||
|
|
||||||
writer := format.Writer(outputFormat, os.Stdout)
|
|
||||||
err = writer.Write(sinks)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func runAddFile(_ *cobra.Command, args []string) (err error) {
|
|
||||||
auditClient := rpc.NewAuditClient(conn)
|
|
||||||
ctx, cancel := context.WithTimeout(cliApp.Context(), grpcTimeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
_, err = auditClient.RegisterFileSink(ctx, &rpc.RegisterFileSinkRequest{TargetPath: args[0]})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func runRemoveFile(_ *cobra.Command, args []string) (err error) {
|
|
||||||
auditClient := rpc.NewAuditClient(conn)
|
|
||||||
ctx, cancel := context.WithTimeout(cliApp.Context(), grpcTimeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
_, err = auditClient.RemoveFileSink(ctx, &rpc.RemoveFileSinkRequest{TargetPath: args[0]})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func runReadFile(_ *cobra.Command, args []string) (err error) {
|
|
||||||
if len(args) != 1 {
|
|
||||||
return errors.New("expected only 1 argument")
|
|
||||||
}
|
|
||||||
|
|
||||||
var reader io.ReadCloser
|
|
||||||
if reader, err = os.Open(args[0]); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
eventReader := audit.NewEventReader(reader)
|
|
||||||
var ev audit.Event
|
|
||||||
|
|
||||||
for err == nil {
|
|
||||||
if ev, err = eventReader.Read(); err == nil {
|
|
||||||
var jsonBytes []byte
|
|
||||||
if jsonBytes, err = json.Marshal(ev); err == nil {
|
|
||||||
fmt.Println(string(jsonBytes))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if errors.Is(err, io.EOF) {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/rpc"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
watchEventsCmd = &cobra.Command{
|
|
||||||
Use: "watch",
|
|
||||||
Short: "Watch all audit events",
|
|
||||||
RunE: watchAuditEvents,
|
|
||||||
}
|
|
||||||
|
|
||||||
auditCmd = &cobra.Command{
|
|
||||||
Use: "audit",
|
|
||||||
Short: "Interact with the audit stream",
|
|
||||||
}
|
|
||||||
|
|
||||||
listenerName string
|
|
||||||
)
|
|
||||||
|
|
||||||
func watchAuditEvents(_ *cobra.Command, _ []string) (err error) {
|
|
||||||
auditClient := rpc.NewAuditClient(conn)
|
|
||||||
|
|
||||||
var watchClient rpc.Audit_WatchEventsClient
|
|
||||||
if watchClient, err = auditClient.WatchEvents(cliApp.Context(), &rpc.WatchEventsRequest{WatcherName: listenerName}); err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
var protoEv *audit.EventEntity
|
|
||||||
for protoEv, err = watchClient.Recv(); err == nil; protoEv, err = watchClient.Recv() {
|
|
||||||
ev := audit.NewEventFromProto(protoEv)
|
|
||||||
var out []byte
|
|
||||||
out, err = json.Marshal(ev)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fmt.Println(string(out))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
<-cliApp.Context().Done()
|
|
||||||
err = watchClient.CloseSend()
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,89 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/format"
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/rpc"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
healthCmd = &cobra.Command{
|
|
||||||
Use: "health",
|
|
||||||
Short: "health is the entry point for all health check related commands",
|
|
||||||
}
|
|
||||||
|
|
||||||
generalHealthCmd = &cobra.Command{
|
|
||||||
Use: "general",
|
|
||||||
Short: "get the health in a more general way i.e. exit code 0 if healthy, exit codes unequal 0 if somethings wrong",
|
|
||||||
Long: `
|
|
||||||
Exit code 1 means the server is still initializing
|
|
||||||
Exit code 2 means any component is unhealthy
|
|
||||||
Exit code 10 means an error occurred while opening a connection to the API socket
|
|
||||||
|
|
||||||
The output contains information about each component and it's health state.
|
|
||||||
`,
|
|
||||||
Run: runGeneralHealth,
|
|
||||||
}
|
|
||||||
|
|
||||||
containerHealthCmd = &cobra.Command{
|
|
||||||
Use: "container",
|
|
||||||
Short: "get the health in a container compatible way i.e. exit code 0 if okay otherwise exit code 1",
|
|
||||||
Run: runContainerHealth,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
type printableHealthInfo struct {
|
|
||||||
Component string
|
|
||||||
State string
|
|
||||||
Message string
|
|
||||||
}
|
|
||||||
|
|
||||||
func fromComponentsHealth(componentsHealth map[string]*rpc.ComponentHealth) (componentsInfo []printableHealthInfo) {
|
|
||||||
for componentName, component := range componentsHealth {
|
|
||||||
componentsInfo = append(componentsInfo, printableHealthInfo{
|
|
||||||
Component: componentName,
|
|
||||||
State: component.State.String(),
|
|
||||||
Message: component.Message,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getHealthResult() (healthResp *rpc.HealthResponse, err error) {
|
|
||||||
var healthClient = rpc.NewHealthClient(conn)
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), grpcTimeout)
|
|
||||||
healthResp, err = healthClient.GetHealth(ctx, &rpc.HealthRequest{})
|
|
||||||
cancel()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func runGeneralHealth(_ *cobra.Command, _ []string) {
|
|
||||||
var healthResp *rpc.HealthResponse
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if healthResp, err = getHealthResult(); err != nil {
|
|
||||||
fmt.Printf("Failed to get health information: %v", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
printable := fromComponentsHealth(healthResp.ComponentsHealth)
|
|
||||||
|
|
||||||
writer := format.Writer(outputFormat, os.Stdout)
|
|
||||||
if err = writer.Write(printable); err != nil {
|
|
||||||
fmt.Printf("Error occurred during writing response values: %v\n", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func runContainerHealth(_ *cobra.Command, _ []string) {
|
|
||||||
if healthResp, err := getHealthResult(); err != nil {
|
|
||||||
fmt.Printf("Failed to get health information: %v", err)
|
|
||||||
os.Exit(1)
|
|
||||||
} else if healthResp.OverallHealthState != rpc.HealthState_HEALTHY {
|
|
||||||
fmt.Println("Overall health state is not healthy")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,62 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/user"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/app"
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
inetMockSocketPath string
|
|
||||||
outputFormat string
|
|
||||||
grpcTimeout time.Duration
|
|
||||||
cliApp app.App
|
|
||||||
conn *grpc.ClientConn
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
healthCmd.AddCommand(generalHealthCmd, containerHealthCmd)
|
|
||||||
|
|
||||||
cliApp = app.NewApp("imctl", "IMCTL is the CLI app to interact with an INetMock server").
|
|
||||||
WithCommands(healthCmd, auditCmd).
|
|
||||||
WithInitTasks(func(_ *cobra.Command, _ []string) (err error) {
|
|
||||||
return initGRPCConnection()
|
|
||||||
}).
|
|
||||||
WithLogger()
|
|
||||||
|
|
||||||
cliApp.RootCommand().PersistentFlags().StringVar(&inetMockSocketPath, "socket-path", "unix:///var/run/inetmock.sock", "Path to the INetMock socket file")
|
|
||||||
cliApp.RootCommand().PersistentFlags().StringVarP(&outputFormat, "format", "f", "table", "Output format to use. Possible values: table, json, yaml")
|
|
||||||
cliApp.RootCommand().PersistentFlags().DurationVar(&grpcTimeout, "grpc-timeout", 5*time.Second, "Timeout to connect to the gRPC API")
|
|
||||||
|
|
||||||
currentUser := ""
|
|
||||||
if usr, err := user.Current(); err == nil {
|
|
||||||
currentUser = usr.Username
|
|
||||||
} else {
|
|
||||||
currentUser = uuid.New().String()
|
|
||||||
}
|
|
||||||
|
|
||||||
hostname := "."
|
|
||||||
if hn, err := os.Hostname(); err == nil {
|
|
||||||
hostname = hn
|
|
||||||
}
|
|
||||||
|
|
||||||
watchEventsCmd.PersistentFlags().StringVar(&listenerName, "listener-name", fmt.Sprintf("%s\\%s is watching", hostname, currentUser), "set listener name - defaults to the current username, if the user cannot be determined a random UUID will be used")
|
|
||||||
auditCmd.AddCommand(listSinksCmd, watchEventsCmd, addFileCmd, removeFileCmd, readFileCmd)
|
|
||||||
|
|
||||||
cliApp.MustRun()
|
|
||||||
}
|
|
||||||
|
|
||||||
func initGRPCConnection() (err error) {
|
|
||||||
dialCtx, cancel := context.WithTimeout(cliApp.Context(), grpcTimeout)
|
|
||||||
conn, err = grpc.DialContext(dialCtx, inetMockSocketPath, grpc.WithInsecure())
|
|
||||||
cancel()
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,109 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"crypto/x509"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/cert"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
generateCACommonName = "cn"
|
|
||||||
generateCaOrganizationName = "o"
|
|
||||||
generateCaOrganizationalUnitName = "ou"
|
|
||||||
generateCaCountryName = "c"
|
|
||||||
generateCaLocalityName = "l"
|
|
||||||
generateCaStateName = "st"
|
|
||||||
generateCaStreetAddressName = "street-address"
|
|
||||||
generateCaPostalCodeName = "postal-code"
|
|
||||||
generateCACertOutPath = "out-dir"
|
|
||||||
generateCACurveName = "curve"
|
|
||||||
generateCANotBeforeRelative = "not-before"
|
|
||||||
generateCANotAfterRelative = "not-after"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
generateCaCmd *cobra.Command
|
|
||||||
caCertOptions cert.GenerationOptions
|
|
||||||
notBefore, notAfter time.Duration
|
|
||||||
certOutPath, curveName string
|
|
||||||
)
|
|
||||||
|
|
||||||
//nolint:lll
|
|
||||||
func init() {
|
|
||||||
generateCaCmd = &cobra.Command{
|
|
||||||
Use: "generate-ca",
|
|
||||||
Short: "Generate a new CA certificate and corresponding key",
|
|
||||||
Long: ``,
|
|
||||||
Run: runGenerateCA,
|
|
||||||
}
|
|
||||||
|
|
||||||
generateCaCmd.Flags().StringVar(&caCertOptions.CommonName, generateCACommonName, "INetMock", "Certificate Common Name that will also be used as file name during generation.")
|
|
||||||
generateCaCmd.Flags().StringSliceVar(&caCertOptions.Organization, generateCaOrganizationName, nil, "Organization information to append to certificate")
|
|
||||||
generateCaCmd.Flags().StringSliceVar(&caCertOptions.OrganizationalUnit, generateCaOrganizationalUnitName, nil, "Organizational unit information to append to certificate")
|
|
||||||
generateCaCmd.Flags().StringSliceVar(&caCertOptions.Country, generateCaCountryName, nil, "Country information to append to certificate")
|
|
||||||
generateCaCmd.Flags().StringSliceVar(&caCertOptions.Province, generateCaStateName, nil, "State information to append to certificate")
|
|
||||||
generateCaCmd.Flags().StringSliceVar(&caCertOptions.Locality, generateCaLocalityName, nil, "Locality information to append to certificate")
|
|
||||||
generateCaCmd.Flags().StringSliceVar(&caCertOptions.StreetAddress, generateCaStreetAddressName, nil, "Street address information to append to certificate")
|
|
||||||
generateCaCmd.Flags().StringSliceVar(&caCertOptions.PostalCode, generateCaPostalCodeName, nil, "Postal code information to append to certificate")
|
|
||||||
generateCaCmd.Flags().StringVar(&certOutPath, generateCACertOutPath, "", "Path where CA files should be stored")
|
|
||||||
generateCaCmd.Flags().StringVar(&curveName, generateCACurveName, "", "Name of the curve to use, if empty ED25519 is used, other valid values are [P224, P256,P384,P521]")
|
|
||||||
generateCaCmd.Flags().DurationVar(¬Before, generateCANotBeforeRelative, 17520*time.Hour, "Relative time value since when in the past the CA certificate should be valid. The value has a time unit, the greatest time unit is h for hour.")
|
|
||||||
generateCaCmd.Flags().DurationVar(¬After, generateCANotAfterRelative, 17520*time.Hour, "Relative time value until when in the future the CA certificate should be valid. The value has a time unit, the greatest time unit is h for hour.")
|
|
||||||
}
|
|
||||||
|
|
||||||
func runGenerateCA(_ *cobra.Command, _ []string) {
|
|
||||||
logger := serverApp.Logger().Named("generate-ca")
|
|
||||||
|
|
||||||
logger = logger.With(
|
|
||||||
zap.String(generateCACurveName, curveName),
|
|
||||||
zap.String(generateCACertOutPath, certOutPath),
|
|
||||||
)
|
|
||||||
|
|
||||||
generator := cert.NewDefaultGenerator(cert.CertOptions{
|
|
||||||
CertCachePath: certOutPath,
|
|
||||||
Curve: cert.CurveType(curveName),
|
|
||||||
Validity: cert.ValidityByPurpose{
|
|
||||||
CA: cert.ValidityDuration{
|
|
||||||
NotAfterRelative: notAfter,
|
|
||||||
NotBeforeRelative: notBefore,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
var err error
|
|
||||||
var caCrt *tls.Certificate
|
|
||||||
if caCrt, err = generator.CACert(caCertOptions); err != nil {
|
|
||||||
logger.Error(
|
|
||||||
"failed to generate CA certificate",
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(caCrt.Certificate) < 1 {
|
|
||||||
logger.Error("no public key given for generated CA certificate")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var pubKey *x509.Certificate
|
|
||||||
if pubKey, err = x509.ParseCertificate(caCrt.Certificate[0]); err != nil {
|
|
||||||
logger.Error(
|
|
||||||
"failed to parse public key from generated CA",
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
pemCrt := cert.NewPEM(caCrt)
|
|
||||||
if err = pemCrt.Write(pubKey.Subject.CommonName, certOutPath); err != nil {
|
|
||||||
logger.Error(
|
|
||||||
"failed to write Ca files",
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
logger.Info("completed certificate generation")
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/app"
|
|
||||||
dns "gitlab.com/inetmock/inetmock/internal/endpoint/handler/dns/mock"
|
|
||||||
http "gitlab.com/inetmock/inetmock/internal/endpoint/handler/http/mock"
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/endpoint/handler/http/proxy"
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/endpoint/handler/metrics"
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/endpoint/handler/tls/interceptor"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
serverApp app.App
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
serverApp = app.NewApp("inetmock", "INetMock is lightweight internet mock").
|
|
||||||
WithHandlerRegistry(
|
|
||||||
http.AddHTTPMock,
|
|
||||||
dns.AddDNSMock,
|
|
||||||
interceptor.AddTLSInterceptor,
|
|
||||||
proxy.AddHTTPProxy,
|
|
||||||
metrics.AddMetricsExporter).
|
|
||||||
WithCommands(serveCmd, generateCaCmd).
|
|
||||||
WithConfig().
|
|
||||||
WithLogger().
|
|
||||||
WithHealthChecker().
|
|
||||||
WithCertStore().
|
|
||||||
WithEventStream().
|
|
||||||
WithEndpointManager()
|
|
||||||
|
|
||||||
serverApp.MustRun()
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/rpc"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
serveCmd = &cobra.Command{
|
|
||||||
Use: "serve",
|
|
||||||
Short: "Starts the INetMock server",
|
|
||||||
Long: ``,
|
|
||||||
RunE: startINetMock,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func startINetMock(_ *cobra.Command, _ []string) (err error) {
|
|
||||||
rpcAPI := rpc.NewINetMockAPI(serverApp)
|
|
||||||
logger := serverApp.Logger()
|
|
||||||
|
|
||||||
cfg := serverApp.Config()
|
|
||||||
endpointOrchestrator := serverApp.EndpointManager()
|
|
||||||
|
|
||||||
for name, spec := range cfg.ListenerSpecs() {
|
|
||||||
if spec.Name == "" {
|
|
||||||
spec.Name = name
|
|
||||||
}
|
|
||||||
if err = endpointOrchestrator.RegisterListener(spec); err != nil {
|
|
||||||
logger.Error("Failed to register listener", zap.Error(err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
errChan := serverApp.EndpointManager().StartEndpoints()
|
|
||||||
if err = rpcAPI.StartServer(); err != nil {
|
|
||||||
serverApp.Shutdown()
|
|
||||||
logger.Error(
|
|
||||||
"failed to start gRPC API",
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
loop:
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case err := <-errChan:
|
|
||||||
logger.Error("got error from endpoint", zap.Error(err))
|
|
||||||
case <-serverApp.Context().Done():
|
|
||||||
break loop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Info("App context canceled - shutting down")
|
|
||||||
|
|
||||||
rpcAPI.StopServer()
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,166 +0,0 @@
|
||||||
x-response-rules: &httpResponseRules
|
|
||||||
rules:
|
|
||||||
- pattern: ".*\\.(?i)exe"
|
|
||||||
matcher: Path
|
|
||||||
- pattern: "^application/octet-stream$"
|
|
||||||
target: Accept
|
|
||||||
matcher: Header
|
|
||||||
response: /var/lib/inetmock/fakeFiles/sample.exe
|
|
||||||
- pattern: "^image/jpeg$"
|
|
||||||
target: Accept
|
|
||||||
matcher: Header
|
|
||||||
response: /var/lib/inetmock/fakeFiles/default.jpg
|
|
||||||
- pattern: ".*\\.(?i)(jpg|jpeg)"
|
|
||||||
matcher: Path
|
|
||||||
response: /var/lib/inetmock/fakeFiles/default.jpg
|
|
||||||
- pattern: "^image/png$"
|
|
||||||
target: Accept
|
|
||||||
matcher: Header
|
|
||||||
response: /var/lib/inetmock/fakeFiles/default.png
|
|
||||||
- pattern: ".*\\.(?i)png"
|
|
||||||
matcher: Path
|
|
||||||
response: /var/lib/inetmock/fakeFiles/default.png
|
|
||||||
- pattern: ".*\\.(?i)gif"
|
|
||||||
matcher: Path
|
|
||||||
response: /var/lib/inetmock/fakeFiles/default.gif
|
|
||||||
- pattern: ".*\\.(?i)ico"
|
|
||||||
matcher: Path
|
|
||||||
response: /var/lib/inetmock/fakeFiles/default.ico
|
|
||||||
- pattern: "^text/plain$"
|
|
||||||
target: Accept
|
|
||||||
matcher: Header
|
|
||||||
response: /var/lib/inetmock/fakeFiles/default.txt
|
|
||||||
- pattern: ".*\\.(?i)txt"
|
|
||||||
matcher: Path
|
|
||||||
response: /var/lib/inetmock/fakeFiles/default.txt
|
|
||||||
- pattern: "^text/html$"
|
|
||||||
target: Accept
|
|
||||||
matcher: Header
|
|
||||||
response: /var/lib/inetmock/fakeFiles/default.html
|
|
||||||
- pattern: ".*"
|
|
||||||
matcher: Path
|
|
||||||
response: /var/lib/inetmock/fakeFiles/default.html
|
|
||||||
|
|
||||||
x-http-handlers: &httpHandlers
|
|
||||||
endpoints:
|
|
||||||
plainHttp:
|
|
||||||
handler: http_mock
|
|
||||||
tls: false
|
|
||||||
options:
|
|
||||||
<<: *httpResponseRules
|
|
||||||
https:
|
|
||||||
handler: http_mock
|
|
||||||
tls: true
|
|
||||||
options:
|
|
||||||
<<: *httpResponseRules
|
|
||||||
|
|
||||||
api:
|
|
||||||
listen: unix:///var/run/inetmock/inetmock.sock
|
|
||||||
|
|
||||||
tls:
|
|
||||||
curve: P256
|
|
||||||
minTLSVersion: SSL3
|
|
||||||
includeInsecureCipherSuites: false
|
|
||||||
validity:
|
|
||||||
ca:
|
|
||||||
notBeforeRelative: 17520h
|
|
||||||
notAfterRelative: 17520h
|
|
||||||
server:
|
|
||||||
NotBeforeRelative: 168h
|
|
||||||
NotAfterRelative: 168h
|
|
||||||
rootCaCert:
|
|
||||||
publicKeyPath: /var/lib/inetmock/ca/ca.pem
|
|
||||||
privateKeyPath: /var/lib/inetmock/ca/ca.key
|
|
||||||
certCachePath: /var/lib/inetmock/certs
|
|
||||||
|
|
||||||
listeners:
|
|
||||||
udp_53:
|
|
||||||
name: ''
|
|
||||||
protocol: udp
|
|
||||||
listenAddress: ''
|
|
||||||
port: 53
|
|
||||||
endpoints:
|
|
||||||
plainDns:
|
|
||||||
handler: dns_mock
|
|
||||||
options:
|
|
||||||
rules:
|
|
||||||
- pattern: ".*\\.google\\.com"
|
|
||||||
response: 1.1.1.1
|
|
||||||
- pattern: ".*\\.reddit\\.com"
|
|
||||||
response: 2.2.2.2
|
|
||||||
fallback:
|
|
||||||
strategy: incremental
|
|
||||||
args:
|
|
||||||
startIP: 10.0.10.0
|
|
||||||
tcp_80:
|
|
||||||
name: ''
|
|
||||||
protocol: tcp
|
|
||||||
listenAddress: ''
|
|
||||||
port: 80
|
|
||||||
<<: *httpHandlers
|
|
||||||
tcp_443:
|
|
||||||
name: ''
|
|
||||||
protocol: tcp
|
|
||||||
listenAddress: ''
|
|
||||||
port: 443
|
|
||||||
<<: *httpHandlers
|
|
||||||
tcp_853:
|
|
||||||
name: ''
|
|
||||||
protocol: tcp
|
|
||||||
listenAddress: ''
|
|
||||||
port: 853
|
|
||||||
endpoints:
|
|
||||||
DoT:
|
|
||||||
handler: dns_mock
|
|
||||||
tls: true
|
|
||||||
options:
|
|
||||||
rules:
|
|
||||||
- pattern: ".*\\.google\\.com"
|
|
||||||
response: 1.1.1.1
|
|
||||||
- pattern: ".*\\.reddit\\.com"
|
|
||||||
response: 2.2.2.2
|
|
||||||
fallback:
|
|
||||||
strategy: incremental
|
|
||||||
args:
|
|
||||||
startIP: 10.0.10.0
|
|
||||||
tcp_3128:
|
|
||||||
name: ''
|
|
||||||
protocol: tcp
|
|
||||||
listenAddress: ''
|
|
||||||
port: 3128
|
|
||||||
endpoints:
|
|
||||||
proxyPlain:
|
|
||||||
handler: http_proxy
|
|
||||||
options:
|
|
||||||
target:
|
|
||||||
ipAddress: 127.0.0.1
|
|
||||||
port: 80
|
|
||||||
proxyTls:
|
|
||||||
handler: http_proxy
|
|
||||||
tls: true
|
|
||||||
options:
|
|
||||||
target:
|
|
||||||
ipAddress: 127.0.0.1
|
|
||||||
port: 443
|
|
||||||
tcp_8080:
|
|
||||||
name: ''
|
|
||||||
protocol: tcp
|
|
||||||
listenAddress: ''
|
|
||||||
port: 8080
|
|
||||||
<<: *httpHandlers
|
|
||||||
tcp_8443:
|
|
||||||
name: ''
|
|
||||||
protocol: tcp
|
|
||||||
listenAddress: ''
|
|
||||||
port: 8443
|
|
||||||
<<: *httpHandlers
|
|
||||||
tcp_9110:
|
|
||||||
name: ''
|
|
||||||
protocol: tcp
|
|
||||||
listenAddress: ''
|
|
||||||
port: 9110
|
|
||||||
endpoints:
|
|
||||||
metrics:
|
|
||||||
handler: metrics_exporter
|
|
||||||
options:
|
|
||||||
route: /metrics
|
|
166
config.yaml
166
config.yaml
|
@ -1,166 +0,0 @@
|
||||||
x-response-rules: &httpResponseRules
|
|
||||||
rules:
|
|
||||||
- pattern: ".*\\.(?i)exe"
|
|
||||||
matcher: Path
|
|
||||||
- pattern: "^application/octet-stream$"
|
|
||||||
target: Accept
|
|
||||||
matcher: Header
|
|
||||||
response: ./assets/fakeFiles/sample.exe
|
|
||||||
- pattern: "^image/jpeg$"
|
|
||||||
target: Accept
|
|
||||||
matcher: Header
|
|
||||||
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"
|
|
||||||
matcher: Path
|
|
||||||
response: ./assets/fakeFiles/default.png
|
|
||||||
- pattern: ".*\\.(?i)gif"
|
|
||||||
matcher: Path
|
|
||||||
response: ./assets/fakeFiles/default.gif
|
|
||||||
- pattern: ".*\\.(?i)ico"
|
|
||||||
matcher: Path
|
|
||||||
response: ./assets/fakeFiles/default.ico
|
|
||||||
- pattern: "^text/plain$"
|
|
||||||
target: Accept
|
|
||||||
matcher: Header
|
|
||||||
response: ./assets/fakeFiles/default.txt
|
|
||||||
- pattern: ".*\\.(?i)txt"
|
|
||||||
matcher: Path
|
|
||||||
response: ./assets/fakeFiles/default.txt
|
|
||||||
- pattern: "^text/html$"
|
|
||||||
target: Accept
|
|
||||||
matcher: Header
|
|
||||||
response: ./assets/fakeFiles/default.html
|
|
||||||
- pattern: ".*"
|
|
||||||
matcher: Path
|
|
||||||
response: ./assets/fakeFiles/default.html
|
|
||||||
|
|
||||||
x-http-handlers: &httpHandlers
|
|
||||||
endpoints:
|
|
||||||
plainHttp:
|
|
||||||
handler: http_mock
|
|
||||||
tls: false
|
|
||||||
options:
|
|
||||||
<<: *httpResponseRules
|
|
||||||
https:
|
|
||||||
handler: http_mock
|
|
||||||
tls: true
|
|
||||||
options:
|
|
||||||
<<: *httpResponseRules
|
|
||||||
|
|
||||||
api:
|
|
||||||
listen: unix:///var/run/inetmock.sock
|
|
||||||
|
|
||||||
tls:
|
|
||||||
curve: P256
|
|
||||||
minTLSVersion: SSL3
|
|
||||||
includeInsecureCipherSuites: false
|
|
||||||
validity:
|
|
||||||
ca:
|
|
||||||
notBeforeRelative: 17520h
|
|
||||||
notAfterRelative: 17520h
|
|
||||||
server:
|
|
||||||
NotBeforeRelative: 168h
|
|
||||||
NotAfterRelative: 168h
|
|
||||||
rootCaCert:
|
|
||||||
publicKeyPath: ./assets/demoCA/ca.pem
|
|
||||||
privateKeyPath: ./assets/demoCA/ca.key
|
|
||||||
certCachePath: /tmp/inetmock/
|
|
||||||
|
|
||||||
listeners:
|
|
||||||
udp_53:
|
|
||||||
name: ''
|
|
||||||
protocol: udp
|
|
||||||
listenAddress: ''
|
|
||||||
port: 1053
|
|
||||||
endpoints:
|
|
||||||
plainDns:
|
|
||||||
handler: dns_mock
|
|
||||||
options:
|
|
||||||
rules:
|
|
||||||
- pattern: ".*\\.google\\.com"
|
|
||||||
response: 1.1.1.1
|
|
||||||
- pattern: ".*\\.reddit\\.com"
|
|
||||||
response: 2.2.2.2
|
|
||||||
fallback:
|
|
||||||
strategy: incremental
|
|
||||||
args:
|
|
||||||
startIP: 10.0.10.0
|
|
||||||
tcp_80:
|
|
||||||
name: ''
|
|
||||||
protocol: tcp
|
|
||||||
listenAddress: ''
|
|
||||||
port: 80
|
|
||||||
<<: *httpHandlers
|
|
||||||
tcp_443:
|
|
||||||
name: ''
|
|
||||||
protocol: tcp
|
|
||||||
listenAddress: ''
|
|
||||||
port: 443
|
|
||||||
<<: *httpHandlers
|
|
||||||
tcp_853:
|
|
||||||
name: ''
|
|
||||||
protocol: tcp
|
|
||||||
listenAddress: ''
|
|
||||||
port: 853
|
|
||||||
endpoints:
|
|
||||||
DoT:
|
|
||||||
handler: dns_mock
|
|
||||||
tls: true
|
|
||||||
options:
|
|
||||||
rules:
|
|
||||||
- pattern: ".*\\.google\\.com"
|
|
||||||
response: 1.1.1.1
|
|
||||||
- pattern: ".*\\.reddit\\.com"
|
|
||||||
response: 2.2.2.2
|
|
||||||
fallback:
|
|
||||||
strategy: incremental
|
|
||||||
args:
|
|
||||||
startIP: 10.0.10.0
|
|
||||||
tcp_3128:
|
|
||||||
name: ''
|
|
||||||
protocol: tcp
|
|
||||||
listenAddress: ''
|
|
||||||
port: 3128
|
|
||||||
endpoints:
|
|
||||||
proxyPlain:
|
|
||||||
handler: http_proxy
|
|
||||||
options:
|
|
||||||
target:
|
|
||||||
ipAddress: 127.0.0.1
|
|
||||||
port: 80
|
|
||||||
proxyTls:
|
|
||||||
handler: http_proxy
|
|
||||||
tls: true
|
|
||||||
options:
|
|
||||||
target:
|
|
||||||
ipAddress: 127.0.0.1
|
|
||||||
port: 443
|
|
||||||
tcp_8080:
|
|
||||||
name: ''
|
|
||||||
protocol: tcp
|
|
||||||
listenAddress: ''
|
|
||||||
port: 8080
|
|
||||||
<<: *httpHandlers
|
|
||||||
tcp_8443:
|
|
||||||
name: ''
|
|
||||||
protocol: tcp
|
|
||||||
listenAddress: ''
|
|
||||||
port: 8443
|
|
||||||
<<: *httpHandlers
|
|
||||||
tcp_9110:
|
|
||||||
name: ''
|
|
||||||
protocol: tcp
|
|
||||||
listenAddress: ''
|
|
||||||
port: 9110
|
|
||||||
endpoints:
|
|
||||||
metrics:
|
|
||||||
handler: metrics_exporter
|
|
||||||
options:
|
|
||||||
route: /metrics
|
|
|
@ -1 +0,0 @@
|
||||||
OPTIONS="--config=/etc/inetmock/config.yaml"
|
|
|
@ -1,15 +0,0 @@
|
||||||
[Unit]
|
|
||||||
Description=INetMock is a simple service to simulate a valid internet connection
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
User=inetmock
|
|
||||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
|
||||||
MemoryMax=50M
|
|
||||||
CPUQuota=20%
|
|
||||||
EnvironmentFile=/etc/default/inetmock
|
|
||||||
ExecStart=/usr/bin/inetmock $OPTIONS
|
|
||||||
WorkingDirectory=/var/lib/inetmock
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
1
docs/.gitignore
vendored
1
docs/.gitignore
vendored
|
@ -1 +0,0 @@
|
||||||
book
|
|
|
@ -1,15 +0,0 @@
|
||||||
.PHONY: book deploy
|
|
||||||
|
|
||||||
book:
|
|
||||||
@mdbook build
|
|
||||||
|
|
||||||
deploy: book
|
|
||||||
@rm -rf /tmp/book
|
|
||||||
@git worktree remove -f /tmp/book
|
|
||||||
@git worktree add /tmp/book gh-pages
|
|
||||||
@rm -rf /tmp/book/*
|
|
||||||
@cp -rp book/* /tmp/book/
|
|
||||||
@cd /tmp/book && \
|
|
||||||
git add -A && \
|
|
||||||
git commit -m "deployed on $(shell date) by ${USER}" && \
|
|
||||||
git push origin gh-pages
|
|
|
@ -1,10 +0,0 @@
|
||||||
[book]
|
|
||||||
authors = ["Peter Kurfer"]
|
|
||||||
language = "en"
|
|
||||||
multilingual = false
|
|
||||||
src = "src"
|
|
||||||
title = "INetMock docs"
|
|
||||||
|
|
||||||
[build]
|
|
||||||
build-dir = "book"
|
|
||||||
create-missing = true
|
|
|
@ -1,13 +0,0 @@
|
||||||
# Summary
|
|
||||||
|
|
||||||
- [Building & Installation](build_install.md)
|
|
||||||
- [Configuration](config.md)
|
|
||||||
- [`config.yaml`](config/yaml-config.md)
|
|
||||||
- [`http_mock`](config/http_mock.md)
|
|
||||||
- [`dns_mock`](config/dns_mock.md)
|
|
||||||
- [`tls_interceptor`](config/tls_interceptor.md)
|
|
||||||
- [Deployment](deploy.md)
|
|
||||||
- [API](api.md)
|
|
||||||
- [Custom handler](dev/custom_handler.md)
|
|
||||||
- [Plugin command](dev/plugin_command.md)
|
|
||||||
- [Logging](dev/logging.md)
|
|
|
@ -1 +0,0 @@
|
||||||
# API
|
|
|
@ -1,29 +0,0 @@
|
||||||
# Installation
|
|
||||||
|
|
||||||
## Building from source
|
|
||||||
|
|
||||||
### Requirements
|
|
||||||
|
|
||||||
* go 1.14
|
|
||||||
* make
|
|
||||||
* gcc
|
|
||||||
|
|
||||||
### Binary
|
|
||||||
|
|
||||||
To get the binary and all plugins just run
|
|
||||||
|
|
||||||
```bash
|
|
||||||
make
|
|
||||||
```
|
|
||||||
|
|
||||||
and you'll get a `inetmock` binary and a `plugins` directory containing all default plugins.
|
|
||||||
|
|
||||||
The default plugins are:
|
|
||||||
|
|
||||||
* `http_mock`
|
|
||||||
* `dns_mock`
|
|
||||||
* `tls_interceptor`
|
|
||||||
|
|
||||||
## Docker/Podman
|
|
||||||
|
|
||||||
## Getting a pre-built binary (coming soon)
|
|
|
@ -1,21 +0,0 @@
|
||||||
# Configuration
|
|
||||||
|
|
||||||
## Plugins & handlers
|
|
||||||
|
|
||||||
_INetMock_ is based on plugins that ship one or more __protocol handlers__. Examples for protocol handlers are HTTP or
|
|
||||||
DNS but also TLS.
|
|
||||||
|
|
||||||
The application ships with the following handlers:
|
|
||||||
|
|
||||||
* `http_mock`
|
|
||||||
* `dns_mock`
|
|
||||||
* `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`. This way the whole system is very flexible and can be configured
|
|
||||||
for various individual scenarios.
|
|
||||||
|
|
||||||
## 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.
|
|
|
@ -1,142 +0,0 @@
|
||||||
# `dns_mock`
|
|
||||||
|
|
||||||
## Intro
|
|
||||||
|
|
||||||
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. 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.
|
|
||||||
|
|
||||||
The rules for the `dns_mock` handler are equivalent to the `http_mock` rules. 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. 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.
|
|
||||||
|
|
||||||
Right now the following fallback strategies are available:
|
|
||||||
|
|
||||||
* _random_
|
|
||||||
* _incremental_
|
|
||||||
|
|
||||||
Just like the handler is configured via the `options` object the fallback strategies are configured via an `args`
|
|
||||||
object.
|
|
||||||
|
|
||||||
### _random_ fallback
|
|
||||||
|
|
||||||
The _random_ fallback strategy is the easier one of the both. 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.
|
|
||||||
|
|
||||||
### _incremental_ fallback
|
|
||||||
|
|
||||||
The _incremental_ fallback is little bit more intelligent. It takes a `startIP` as an argument which defines from which
|
|
||||||
IP address the strategy starts counting up to respond to DNS queries. Just like the _incremental_ strategy it is _
|
|
||||||
stateless_ and does not store any already given response for later reuse (at least for now).
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
### Matching an explicit host
|
|
||||||
|
|
||||||
The easiest possible pattern is to match a single host:
|
|
||||||
|
|
||||||
```yml
|
|
||||||
endpoints:
|
|
||||||
plainDns:
|
|
||||||
handler: dns_mock
|
|
||||||
listenAddress: 0.0.0.0
|
|
||||||
port: 53
|
|
||||||
options:
|
|
||||||
rules:
|
|
||||||
- pattern: "github\\.com"
|
|
||||||
response: 1.1.1.1
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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. But it's also possible to match a whole domain no matter
|
|
||||||
what subdomain or sub-subdomain or whatever is requested like this:
|
|
||||||
|
|
||||||
```yml
|
|
||||||
endpoints:
|
|
||||||
plainDns:
|
|
||||||
handler: dns_mock
|
|
||||||
listenAddress: 0.0.0.0
|
|
||||||
port: 53
|
|
||||||
options:
|
|
||||||
rules:
|
|
||||||
- pattern: ".*\\.google\\.com"
|
|
||||||
response: 2.2.2.2
|
|
||||||
```
|
|
||||||
|
|
||||||
### Matching a whole TLD
|
|
||||||
|
|
||||||
In some cases it might also be interesting to distinguish between different requested TLDs. Therefore it might be
|
|
||||||
interesting to define one IP address to resolve to for every TLD that should be distinguishable.
|
|
||||||
|
|
||||||
```yml
|
|
||||||
endpoints:
|
|
||||||
plainDns:
|
|
||||||
handler: dns_mock
|
|
||||||
listenAddress: 0.0.0.0
|
|
||||||
port: 53
|
|
||||||
options:
|
|
||||||
rules:
|
|
||||||
- pattern: ".*\\.com"
|
|
||||||
response: 2.2.2.2
|
|
||||||
```
|
|
||||||
|
|
||||||
### Matching any query
|
|
||||||
|
|
||||||
Last but not least it is obvously also possible to match any query. 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.
|
|
||||||
|
|
||||||
```yml
|
|
||||||
endpoints:
|
|
||||||
plainDns:
|
|
||||||
handler: dns_mock
|
|
||||||
listenAddress: 0.0.0.0
|
|
||||||
port: 53
|
|
||||||
options:
|
|
||||||
rules:
|
|
||||||
- pattern: ".*"
|
|
||||||
response: 10.0.10.1
|
|
||||||
```
|
|
||||||
|
|
||||||
### Fallback strategies
|
|
||||||
|
|
||||||
#### _random_
|
|
||||||
|
|
||||||
Like previously mentioned the _random_ strategy is easy as it can be. It just takes a random unsigned integer of 32
|
|
||||||
bits, converts it to an IP address and returns this address as response. Therefore no further configuration is necessary
|
|
||||||
for now.
|
|
||||||
|
|
||||||
```yml
|
|
||||||
endpoints:
|
|
||||||
plainDns:
|
|
||||||
handler: dns_mock
|
|
||||||
listenAddress: 0.0.0.0
|
|
||||||
port: 53
|
|
||||||
options:
|
|
||||||
rules: []
|
|
||||||
fallback:
|
|
||||||
strategy: random
|
|
||||||
```
|
|
||||||
|
|
||||||
#### _incremental_
|
|
||||||
|
|
||||||
Also like previously mentioned the _incremental_ fallback strategy is fairly easy to setup. It just takes a `startIP` as
|
|
||||||
argument which is used to count upwards. It does __not__ check for an interval or something like this right now so a
|
|
||||||
overflow might occur.
|
|
||||||
|
|
||||||
```yml
|
|
||||||
endpoints:
|
|
||||||
plainDns:
|
|
||||||
handler: dns_mock
|
|
||||||
listenAddress: 0.0.0.0
|
|
||||||
port: 53
|
|
||||||
options:
|
|
||||||
rules: []
|
|
||||||
fallback:
|
|
||||||
strategy: incremental
|
|
||||||
args:
|
|
||||||
startIP: 10.0.0.0
|
|
||||||
```
|
|
|
@ -1,88 +0,0 @@
|
||||||
# `http_mock`
|
|
||||||
|
|
||||||
## Intro
|
|
||||||
|
|
||||||
The `http_mock` handler expects an array of rules how it should respond to different request paths. 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"_.
|
|
||||||
|
|
||||||
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`.
|
|
||||||
|
|
||||||
Every rule consists of a regex `pattern` (__re2__ compatible) and a `response` path to the file it should return.
|
|
||||||
|
|
||||||
In the future more advanced rules might be possible e.g. to match not on the request path but on some header values.
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
### Matching a specific path
|
|
||||||
|
|
||||||
The easiest possible pattern is to match a static request path:
|
|
||||||
|
|
||||||
```yml
|
|
||||||
endpoints:
|
|
||||||
plainHttp:
|
|
||||||
handler: http_mock
|
|
||||||
listenAddress: 0.0.0.0
|
|
||||||
port: 80
|
|
||||||
options:
|
|
||||||
rules:
|
|
||||||
- pattern: "/static/http/path/sample.exe"
|
|
||||||
response: ./assets/fakeFiles/sample.exe
|
|
||||||
```
|
|
||||||
|
|
||||||
### Matching a file extensions
|
|
||||||
|
|
||||||
While matching a static path might be nice as an example it's not very useful. Returning a given file for all kinds of
|
|
||||||
of request paths based on the requested file extension is way more handy:
|
|
||||||
|
|
||||||
```yml
|
|
||||||
endpoints:
|
|
||||||
plainHttp:
|
|
||||||
handler: http_mock
|
|
||||||
listenAddress: 0.0.0.0
|
|
||||||
port: 80
|
|
||||||
options:
|
|
||||||
rules:
|
|
||||||
- pattern: ".*\\.png"
|
|
||||||
response: ./assets/fakeFiles/default.png
|
|
||||||
```
|
|
||||||
|
|
||||||
So this is already way more flexible but we can do even better:
|
|
||||||
|
|
||||||
```yml
|
|
||||||
endpoints:
|
|
||||||
plainHttp:
|
|
||||||
handler: http_mock
|
|
||||||
listenAddress: 0.0.0.0
|
|
||||||
port: 80
|
|
||||||
options:
|
|
||||||
rules:
|
|
||||||
- pattern: ".*\\.(?i)(jpg|jpeg)"
|
|
||||||
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).
|
|
||||||
|
|
||||||
The default `config.yaml` already ships with some basic rules to handle the most common file extensions.
|
|
||||||
|
|
||||||
### Defining a fallback
|
|
||||||
|
|
||||||
Last but not least a default case might be necessary to get at least any response but a 404.
|
|
||||||
|
|
||||||
This can be achieved with a `.*` pattern that literally matches everything:
|
|
||||||
|
|
||||||
```yml
|
|
||||||
endpoints:
|
|
||||||
plainHttp:
|
|
||||||
handler: http_mock
|
|
||||||
listenAddress: 0.0.0.0
|
|
||||||
port: 80
|
|
||||||
options:
|
|
||||||
rules:
|
|
||||||
- pattern: ".*"
|
|
||||||
response: ./assets/fakeFiles/default.html
|
|
||||||
```
|
|
|
@ -1 +0,0 @@
|
||||||
# `tls_interceptor`
|
|
|
@ -1,31 +0,0 @@
|
||||||
# `config.yaml`
|
|
||||||
|
|
||||||
## Intro
|
|
||||||
|
|
||||||
The configuration of _INetMock_ is mostly done in the `config.yaml`. It defines which endpoints should be started with
|
|
||||||
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.
|
|
||||||
|
|
||||||
INetMock comes with _"Batteries included"_ and ships with a basic `config.yaml` that defines a basic set of endpoints
|
|
||||||
for:
|
|
||||||
|
|
||||||
* HTTP
|
|
||||||
* HTTPS
|
|
||||||
* DNS
|
|
||||||
* DNS-over-TLS
|
|
||||||
|
|
||||||
## Sample
|
|
||||||
|
|
||||||
```yml
|
|
||||||
endpoints:
|
|
||||||
myHttpEndpoint:
|
|
||||||
handler: http_mock
|
|
||||||
listenAddress: 127.0.0.1
|
|
||||||
port: 8080
|
|
||||||
options:
|
|
||||||
rules:
|
|
||||||
- pattern: ".*"
|
|
||||||
target: ./assets/fakeFiles/default.html
|
|
||||||
```
|
|
|
@ -1 +0,0 @@
|
||||||
# Deployment
|
|
|
@ -1 +0,0 @@
|
||||||
# Custom handler
|
|
|
@ -1 +0,0 @@
|
||||||
# Logging
|
|
|
@ -1 +0,0 @@
|
||||||
# Plugin command
|
|
27
go.mod
27
go.mod
|
@ -1,27 +0,0 @@
|
||||||
module gitlab.com/inetmock/inetmock
|
|
||||||
|
|
||||||
go 1.15
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/bwmarrin/snowflake v0.3.0
|
|
||||||
github.com/docker/go-connections v0.4.0
|
|
||||||
github.com/golang/mock v1.4.4
|
|
||||||
github.com/golang/protobuf v1.4.3
|
|
||||||
github.com/google/uuid v1.2.0
|
|
||||||
github.com/imdario/mergo v0.3.11
|
|
||||||
github.com/jinzhu/copier v0.2.4
|
|
||||||
github.com/miekg/dns v1.1.38
|
|
||||||
github.com/mitchellh/mapstructure v1.4.1
|
|
||||||
github.com/olekukonko/tablewriter v0.0.5
|
|
||||||
github.com/prometheus/client_golang v1.9.0
|
|
||||||
github.com/soheilhy/cmux v0.1.4
|
|
||||||
github.com/spf13/cobra v1.1.2
|
|
||||||
github.com/spf13/viper v1.7.1
|
|
||||||
github.com/testcontainers/testcontainers-go v0.9.0
|
|
||||||
go.uber.org/multierr v1.6.0
|
|
||||||
go.uber.org/zap v1.16.0
|
|
||||||
google.golang.org/grpc v1.35.0
|
|
||||||
google.golang.org/protobuf v1.25.0
|
|
||||||
gopkg.in/elazarl/goproxy.v1 v1.0.0-20180725130230-947c36da3153
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
|
||||||
)
|
|
710
go.sum
710
go.sum
|
@ -1,710 +0,0 @@
|
||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
|
||||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
|
||||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
|
||||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
|
||||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
|
||||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
|
||||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
|
||||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
|
||||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
|
||||||
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
|
||||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
|
||||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
|
||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
|
||||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
|
||||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
|
||||||
github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q=
|
|
||||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
|
||||||
github.com/Microsoft/hcsshim v0.8.6 h1:ZfF0+zZeYdzMIVMZHKtDKJvLHj76XCuVae/jNkjj0IA=
|
|
||||||
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
|
||||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
|
||||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
|
||||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
|
||||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
|
||||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
|
||||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
|
||||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
|
||||||
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
|
||||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
|
||||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
|
||||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
|
||||||
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
|
||||||
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
|
|
||||||
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
|
||||||
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
|
||||||
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
|
|
||||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
|
||||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
|
||||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
|
||||||
github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0=
|
|
||||||
github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=
|
|
||||||
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
|
||||||
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
|
|
||||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
|
||||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
|
||||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
|
||||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
|
||||||
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
|
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
|
||||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
|
||||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
|
||||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
|
||||||
github.com/containerd/containerd v1.4.1 h1:pASeJT3R3YyVn+94qEPk0SnU1OQ20Jd/T+SPKy9xehY=
|
|
||||||
github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
|
||||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc h1:TP+534wVlf61smEIq1nwLLAjQVEK2EADoW3CX9AuT+8=
|
|
||||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
|
||||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
|
||||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
|
||||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
|
||||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
|
||||||
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
|
||||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
|
||||||
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
|
||||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
|
||||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
|
||||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
|
||||||
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible h1:dvc1KSkIYTVjZgHf/CTC2diTYC8PzhaA5sFISRfNVrE=
|
|
||||||
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
|
||||||
github.com/docker/docker v17.12.0-ce-rc1.0.20200916142827-bd33bbf0497b+incompatible h1:SiUATuP//KecDjpOK2tvZJgeScYAklvyjfK8JZlU6fo=
|
|
||||||
github.com/docker/docker v17.12.0-ce-rc1.0.20200916142827-bd33bbf0497b+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
|
||||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
|
||||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
|
||||||
github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk=
|
|
||||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
|
||||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
|
||||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
|
||||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
|
||||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
|
||||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
|
||||||
github.com/elazarl/goproxy v0.0.0-20210110162100-a92cc753f88e h1:/cwV7t2xezilMljIftb7WlFtzGANRCnoOhPjtl2ifcs=
|
|
||||||
github.com/elazarl/goproxy v0.0.0-20210110162100-a92cc753f88e/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
|
|
||||||
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
|
|
||||||
github.com/elazarl/goproxy/ext v0.0.0-20210110162100-a92cc753f88e h1:CQn2/8fi3kmpT9BTiHEELgdxAOQNVZc9GoPA4qnQzrs=
|
|
||||||
github.com/elazarl/goproxy/ext v0.0.0-20210110162100-a92cc753f88e/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
|
||||||
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
|
|
||||||
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
|
||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
|
||||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
|
||||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
|
||||||
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
|
|
||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
|
||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
|
||||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
|
||||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
|
||||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
|
||||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
|
||||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
|
||||||
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
|
|
||||||
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
|
||||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
|
||||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
|
||||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
|
||||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
|
||||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
|
||||||
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
|
|
||||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
|
||||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I=
|
|
||||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
|
||||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
|
||||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
|
||||||
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/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
|
||||||
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.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
|
||||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
|
||||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
|
||||||
github.com/google/go-cmp v0.3.0/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/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/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-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
|
||||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
|
||||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
|
||||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
|
||||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
|
||||||
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
|
|
||||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
|
||||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
|
||||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
|
||||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
|
||||||
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
|
|
||||||
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
|
||||||
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
|
||||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
|
||||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
|
||||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
|
||||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
|
||||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
|
||||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
|
||||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
|
||||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
|
||||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
|
||||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
|
||||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
|
||||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
|
||||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
|
||||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
|
||||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
|
||||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
|
||||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
|
||||||
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
|
||||||
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
|
|
||||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
|
||||||
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
|
||||||
github.com/jinzhu/copier v0.2.4 h1:dT3tI+8GzU8DjJFCj9mLYtjfRtUmK7edauduQdcZCpI=
|
|
||||||
github.com/jinzhu/copier v0.2.4/go.mod h1:24xnZezI2Yqac9J61UC6/dG/k76ttpq0DdJI3QmUvro=
|
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
|
||||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
|
||||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
|
||||||
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
|
||||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
|
||||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
|
||||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
|
||||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
|
||||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
|
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|
||||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
|
||||||
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
|
||||||
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
|
||||||
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
|
|
||||||
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
|
||||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
|
||||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
|
||||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
|
||||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
|
||||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
|
||||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
|
||||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
|
||||||
github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA=
|
|
||||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
|
||||||
github.com/miekg/dns v1.1.38 h1:MtIY+fmHUVVgv1AXzmKMWcwdCYxTRPG1EDjpqF4RCEw=
|
|
||||||
github.com/miekg/dns v1.1.38/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
|
||||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
|
||||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
|
||||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
|
||||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
|
||||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
|
||||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
|
||||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
|
||||||
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
|
|
||||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
|
||||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE=
|
|
||||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
|
||||||
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
|
|
||||||
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
|
|
||||||
github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
|
|
||||||
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
|
|
||||||
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
|
||||||
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
|
||||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
|
||||||
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
|
|
||||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
|
||||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
|
||||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
|
||||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
|
||||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
|
||||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
|
||||||
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
|
|
||||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
|
||||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
|
||||||
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
|
|
||||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
|
||||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
|
||||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
|
||||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
|
||||||
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
|
|
||||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
|
||||||
github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=
|
|
||||||
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
|
||||||
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
|
|
||||||
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
|
||||||
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
|
||||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
|
||||||
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
|
|
||||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
|
||||||
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
|
||||||
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
|
||||||
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
|
|
||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
|
||||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
|
||||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
|
||||||
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
|
|
||||||
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
|
||||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
||||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
|
||||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
|
||||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
|
||||||
github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
|
|
||||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
|
||||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
|
||||||
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
|
|
||||||
github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA=
|
|
||||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
|
||||||
github.com/prometheus/client_golang v1.9.0 h1:Rrch9mh17XcxvEu9D9DEpb4isxjGBtcevQjKvxPRQIU=
|
|
||||||
github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU=
|
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
|
||||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
|
||||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
|
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
|
||||||
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
|
||||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
|
||||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
|
||||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
|
||||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
|
||||||
github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
|
|
||||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
|
||||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
|
||||||
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
|
|
||||||
github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
|
|
||||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
|
||||||
github.com/prometheus/common v0.15.0 h1:4fgOnadei3EZvgRwxJ7RMpG1k1pOZth5Pc13tyspaKM=
|
|
||||||
github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
|
|
||||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
|
||||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
|
||||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=
|
|
||||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
|
||||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
|
||||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
|
||||||
github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8=
|
|
||||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
|
||||||
github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4=
|
|
||||||
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
|
||||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
|
||||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
|
||||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
|
||||||
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
|
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
|
||||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
|
||||||
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
|
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
|
||||||
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
|
|
||||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
|
||||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
|
||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
|
||||||
github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=
|
|
||||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
|
||||||
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
|
||||||
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
|
|
||||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
|
||||||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
|
||||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
|
||||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
|
||||||
github.com/spf13/cobra v1.1.2 h1:frHO75w/dH7kEc+e2KYZZKY4+PLrp39OqI77oB8m0KQ=
|
|
||||||
github.com/spf13/cobra v1.1.2/go.mod h1:ZjwqWkCg0LnXvLRIfTLdB4Y/MCO3gMHHJ2KFxQZy4xE=
|
|
||||||
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
|
||||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
|
||||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
|
||||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
|
||||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
|
||||||
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
|
|
||||||
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
|
||||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
|
||||||
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
|
||||||
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
|
||||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
|
||||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
|
||||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
|
||||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
|
||||||
github.com/testcontainers/testcontainers-go v0.9.0 h1:ZyftCfROjGrKlxk3MOUn2DAzWrUtzY/mj17iAkdUIvI=
|
|
||||||
github.com/testcontainers/testcontainers-go v0.9.0/go.mod h1:b22BFXhRbg4PJmeMVWh6ftqjyZHgiIl3w274e9r3C2E=
|
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
|
||||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
|
||||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
|
||||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
|
||||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
|
||||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
|
||||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
|
||||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
|
||||||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
|
||||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
|
||||||
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
|
||||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
|
||||||
go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs=
|
|
||||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
|
||||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
|
||||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
|
||||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
|
||||||
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
|
|
||||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
|
||||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
|
||||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
|
||||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
|
||||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
|
||||||
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
|
|
||||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
|
||||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
|
||||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
|
||||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
|
|
||||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
|
||||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
|
||||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
|
||||||
go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM=
|
|
||||||
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
|
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
|
||||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU=
|
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
|
||||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
|
||||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
|
||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
|
||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
|
||||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
|
||||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
|
||||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
|
||||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
|
||||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
|
||||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
|
||||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
|
||||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
|
||||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
|
||||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g=
|
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
|
|
||||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc=
|
|
||||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80=
|
|
||||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e h1:AyodaIpKjppX+cBfTASF2E1US3H2JFBj920Ot3rtDjs=
|
|
||||||
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
|
||||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180810170437-e96c4e24768d/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
|
||||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
|
||||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
|
||||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/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/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA=
|
|
||||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114 h1:DnSr2mCsxyCE6ZgIkmcWUQY2R5cH/6wL7eIxEmQOMSE=
|
|
||||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
|
||||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
|
||||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
|
||||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
|
||||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
|
||||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
|
||||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
|
||||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
|
||||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
|
||||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
|
||||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
|
||||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
|
||||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
|
||||||
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
|
|
||||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
|
||||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
|
||||||
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-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-20200806141610-86f49bd18e98 h1:LCO0fg4kb6WwkXQXRQQgUYsFeFb5taTX5WAx5O/Vt28=
|
|
||||||
google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
|
||||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
|
||||||
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
|
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
|
||||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
|
||||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
|
||||||
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
|
||||||
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
|
||||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
|
||||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
|
||||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
|
||||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
|
||||||
google.golang.org/grpc v1.35.0 h1:TwIQcH3es+MojMVojxxfQ3l3OF2KzlRxML2xZq0kRo8=
|
|
||||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
|
||||||
google.golang.org/grpc/examples v0.0.0-20210210171350-ce29c77c5fa1 h1:dtHfS+TF/MZg8X+DaxrDGiq+5z8OrIJJc1yX+8cQ0cU=
|
|
||||||
google.golang.org/grpc/examples v0.0.0-20210210171350-ce29c77c5fa1/go.mod h1:Ly7ZA/ARzg8fnPU9TyZIxoz33sEUuWX7txiqs8lPTgE=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
|
||||||
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.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/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/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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
|
||||||
gopkg.in/elazarl/goproxy.v1 v1.0.0-20180725130230-947c36da3153 h1:i2sumy6EgvN2dbX7HPhoDc7hLyoym3OYdU5HlvUUrpE=
|
|
||||||
gopkg.in/elazarl/goproxy.v1 v1.0.0-20180725130230-947c36da3153/go.mod h1:xzjpkyedLMz3EXUTBbkRuuGPsxfsBX3Sy7J6kC9Gvoc=
|
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
|
||||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
|
||||||
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
|
||||||
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
|
|
||||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
|
||||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
|
||||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
|
||||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c=
|
|
||||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gotest.tools v0.0.0-20181223230014-1083505acf35 h1:zpdCK+REwbk+rqjJmHhiCN6iBIigrZ39glqSF0P3KF0=
|
|
||||||
gotest.tools v0.0.0-20181223230014-1083505acf35/go.mod h1:R//lfYlUuTOTfblYI3lGoAAAebUdzjvbmQsuB7Ykd90=
|
|
||||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
|
||||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
|
||||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
|
||||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
|
||||||
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
|
|
|
@ -1,366 +0,0 @@
|
||||||
//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/endpoint"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/audit/sink"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/cert"
|
|
||||||
"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 contextKey string
|
|
||||||
|
|
||||||
const (
|
|
||||||
loggerKey contextKey = "gitlab.com/inetmock/inetmock/app/context/logger"
|
|
||||||
configKey contextKey = "gitlab.com/inetmock/inetmock/app/context/config"
|
|
||||||
handlerRegistryKey contextKey = "gitlab.com/inetmock/inetmock/app/context/handlerRegistry"
|
|
||||||
healthCheckerKey contextKey = "gitlab.com/inetmock/inetmock/app/context/healthChecker"
|
|
||||||
endpointManagerKey contextKey = "gitlab.com/inetmock/inetmock/app/context/endpointManager"
|
|
||||||
certStoreKey contextKey = "gitlab.com/inetmock/inetmock/app/context/certStore"
|
|
||||||
eventStreamKey contextKey = "gitlab.com/inetmock/inetmock/app/context/eventStream"
|
|
||||||
)
|
|
||||||
|
|
||||||
type App interface {
|
|
||||||
EventStream() audit.EventStream
|
|
||||||
Config() Config
|
|
||||||
Checker() health.Checker
|
|
||||||
Logger() logging.Logger
|
|
||||||
EndpointManager() endpoint.Orchestrator
|
|
||||||
HandlerRegistry() endpoint.HandlerRegistry
|
|
||||||
Context() context.Context
|
|
||||||
RootCommand() *cobra.Command
|
|
||||||
MustRun()
|
|
||||||
Shutdown()
|
|
||||||
|
|
||||||
// WithCommands adds subcommands to the root command
|
|
||||||
// requires nothing
|
|
||||||
WithCommands(cmds ...*cobra.Command) App
|
|
||||||
|
|
||||||
// WithHandlerRegistry builds up the handler registry
|
|
||||||
// requires nothing
|
|
||||||
WithHandlerRegistry(registrations ...endpoint.Registration) App
|
|
||||||
|
|
||||||
// WithHealthChecker adds the health checker mechanism
|
|
||||||
// requires nothing
|
|
||||||
WithHealthChecker() App
|
|
||||||
|
|
||||||
// WithLogger configures the logging system
|
|
||||||
// requires nothing
|
|
||||||
WithLogger() App
|
|
||||||
|
|
||||||
// WithEndpointManager creates an endpoint manager instance and adds it to the context
|
|
||||||
// requires WithHandlerRegistry, WithHealthChecker and WithLogger
|
|
||||||
WithEndpointManager() App
|
|
||||||
|
|
||||||
// WithCertStore initializes the cert store
|
|
||||||
// requires WithLogger and WithConfig
|
|
||||||
WithCertStore() App
|
|
||||||
|
|
||||||
// WithEventStream adds the audit event stream
|
|
||||||
// requires WithLogger
|
|
||||||
WithEventStream() App
|
|
||||||
|
|
||||||
// WithConfig loads the config
|
|
||||||
// requires nothing
|
|
||||||
WithConfig() App
|
|
||||||
|
|
||||||
WithInitTasks(task ...func(cmd *cobra.Command, args []string) (err error)) App
|
|
||||||
}
|
|
||||||
|
|
||||||
type app struct {
|
|
||||||
rootCmd *cobra.Command
|
|
||||||
ctx context.Context
|
|
||||||
cancel context.CancelFunc
|
|
||||||
lateInitTasks []func(cmd *cobra.Command, args []string) (err error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *app) MustRun() {
|
|
||||||
if err := a.rootCmd.Execute(); err != nil {
|
|
||||||
if a.Logger() != nil {
|
|
||||||
a.Logger().Error(
|
|
||||||
"Failed to run inetmock",
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *app) Logger() logging.Logger {
|
|
||||||
val := a.ctx.Value(loggerKey)
|
|
||||||
if val == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return val.(logging.Logger)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *app) Config() Config {
|
|
||||||
val := a.ctx.Value(configKey)
|
|
||||||
if val == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return val.(Config)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *app) CertStore() cert.Store {
|
|
||||||
val := a.ctx.Value(certStoreKey)
|
|
||||||
if val == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return val.(cert.Store)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *app) Checker() health.Checker {
|
|
||||||
val := a.ctx.Value(healthCheckerKey)
|
|
||||||
if val == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return val.(health.Checker)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *app) EndpointManager() endpoint.Orchestrator {
|
|
||||||
val := a.ctx.Value(endpointManagerKey)
|
|
||||||
if val == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return val.(endpoint.Orchestrator)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *app) Audit() audit.Emitter {
|
|
||||||
val := a.ctx.Value(eventStreamKey)
|
|
||||||
if val == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return val.(audit.Emitter)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *app) EventStream() audit.EventStream {
|
|
||||||
val := a.ctx.Value(eventStreamKey)
|
|
||||||
if val == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return val.(audit.EventStream)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *app) HandlerRegistry() endpoint.HandlerRegistry {
|
|
||||||
val := a.ctx.Value(handlerRegistryKey)
|
|
||||||
if val == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return val.(endpoint.HandlerRegistry)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *app) Context() context.Context {
|
|
||||||
return a.ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *app) RootCommand() *cobra.Command {
|
|
||||||
return a.rootCmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *app) Shutdown() {
|
|
||||||
a.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithCommands adds subcommands to the root command
|
|
||||||
// requires nothing
|
|
||||||
func (a *app) WithCommands(cmds ...*cobra.Command) App {
|
|
||||||
a.rootCmd.AddCommand(cmds...)
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithHandlerRegistry builds up the handler registry
|
|
||||||
// requires nothing
|
|
||||||
func (a *app) WithHandlerRegistry(registrations ...endpoint.Registration) App {
|
|
||||||
registry := endpoint.NewHandlerRegistry()
|
|
||||||
|
|
||||||
for _, registration := range registrations {
|
|
||||||
if err := registration(registry); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
a.ctx = context.WithValue(a.ctx, handlerRegistryKey, registry)
|
|
||||||
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithHealthChecker adds the health checker mechanism
|
|
||||||
// requires nothing
|
|
||||||
func (a *app) WithHealthChecker() App {
|
|
||||||
checker := health.New()
|
|
||||||
a.ctx = context.WithValue(a.ctx, healthCheckerKey, checker)
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithLogger configures the logging system
|
|
||||||
// requires nothing
|
|
||||||
func (a *app) WithLogger() App {
|
|
||||||
a.lateInitTasks = append(a.lateInitTasks, func(cmd *cobra.Command, args []string) (err error) {
|
|
||||||
logging.ConfigureLogging(
|
|
||||||
logging.ParseLevel(logLevel),
|
|
||||||
developmentLogs,
|
|
||||||
map[string]interface{}{
|
|
||||||
"cwd": path.WorkingDirectory(),
|
|
||||||
"cmd": cmd.Name(),
|
|
||||||
"args": args,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
var logger logging.Logger
|
|
||||||
if logger, err = logging.CreateLogger(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
a.ctx = context.WithValue(a.ctx, loggerKey, logger)
|
|
||||||
return
|
|
||||||
})
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithEndpointManager creates an endpoint manager instance and adds it to the context
|
|
||||||
// requires WithHandlerRegistry, WithHealthChecker and WithLogger
|
|
||||||
func (a *app) WithEndpointManager() App {
|
|
||||||
a.lateInitTasks = append(a.lateInitTasks, func(_ *cobra.Command, _ []string) (err error) {
|
|
||||||
epMgr := endpoint.NewOrchestrator(
|
|
||||||
a.Context(),
|
|
||||||
a.CertStore(),
|
|
||||||
a.HandlerRegistry(),
|
|
||||||
a.Audit(),
|
|
||||||
a.Logger().Named("Orchestrator"),
|
|
||||||
)
|
|
||||||
|
|
||||||
a.ctx = context.WithValue(a.ctx, endpointManagerKey, epMgr)
|
|
||||||
return
|
|
||||||
})
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithCertStore initializes the cert store
|
|
||||||
// requires WithLogger and WithConfig
|
|
||||||
func (a *app) WithCertStore() App {
|
|
||||||
a.lateInitTasks = append(a.lateInitTasks, func(cmd *cobra.Command, args []string) (err error) {
|
|
||||||
var certStore cert.Store
|
|
||||||
if certStore, err = cert.NewDefaultStore(
|
|
||||||
a.Config().TLSConfig(),
|
|
||||||
a.Logger().Named("CertStore"),
|
|
||||||
); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
a.ctx = context.WithValue(a.ctx, certStoreKey, certStore)
|
|
||||||
return
|
|
||||||
})
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithEventStream adds the audit event stream
|
|
||||||
// requires WithLogger
|
|
||||||
func (a *app) WithEventStream() App {
|
|
||||||
a.lateInitTasks = append(a.lateInitTasks, func(_ *cobra.Command, _ []string) (err error) {
|
|
||||||
var eventStream audit.EventStream
|
|
||||||
eventStream, err = audit.NewEventStream(
|
|
||||||
a.Logger().Named("EventStream"),
|
|
||||||
audit.WithSinkBufferSize(10),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = eventStream.RegisterSink(a.ctx, sink.NewLogSink(a.Logger().Named("LogSink"))); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var metricSink audit.Sink
|
|
||||||
if metricSink, err = sink.NewMetricSink(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = eventStream.RegisterSink(a.ctx, metricSink); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
a.ctx = context.WithValue(a.ctx, eventStreamKey, eventStream)
|
|
||||||
return
|
|
||||||
})
|
|
||||||
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithConfig loads the config
|
|
||||||
// requires nothing
|
|
||||||
func (a *app) WithConfig() App {
|
|
||||||
a.lateInitTasks = append(a.lateInitTasks, func(cmd *cobra.Command, _ []string) (err error) {
|
|
||||||
cfg := CreateConfig()
|
|
||||||
if err = cfg.ReadConfig(configFilePath); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
a.ctx = context.WithValue(a.ctx, configKey, cfg)
|
|
||||||
return
|
|
||||||
})
|
|
||||||
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *app) WithInitTasks(task ...func(cmd *cobra.Command, args []string) (err error)) App {
|
|
||||||
a.lateInitTasks = append(a.lateInitTasks, task...)
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewApp(name, short string) App {
|
|
||||||
ctx, cancel := initAppContext()
|
|
||||||
a := &app{
|
|
||||||
rootCmd: &cobra.Command{
|
|
||||||
Use: name,
|
|
||||||
Short: short,
|
|
||||||
},
|
|
||||||
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) {
|
|
||||||
for _, initTask := range a.lateInitTasks {
|
|
||||||
if err = initTask(cmd, args); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
|
@ -1,89 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/cert"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/path"
|
|
||||||
)
|
|
||||||
|
|
||||||
func CreateConfig() Config {
|
|
||||||
configInstance := &config{
|
|
||||||
cfg: viper.New(),
|
|
||||||
}
|
|
||||||
|
|
||||||
configInstance.cfg.SetConfigName("config")
|
|
||||||
configInstance.cfg.SetConfigType("yaml")
|
|
||||||
configInstance.cfg.AddConfigPath("/etc/inetmock/")
|
|
||||||
configInstance.cfg.AddConfigPath("$HOME/.inetmock")
|
|
||||||
configInstance.cfg.AddConfigPath(".")
|
|
||||||
configInstance.cfg.SetEnvPrefix("INetMock")
|
|
||||||
configInstance.cfg.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
|
|
||||||
configInstance.cfg.AutomaticEnv()
|
|
||||||
|
|
||||||
for k, v := range registeredDefaults {
|
|
||||||
configInstance.cfg.SetDefault(k, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range registeredAliases {
|
|
||||||
configInstance.cfg.RegisterAlias(k, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
return configInstance
|
|
||||||
}
|
|
||||||
|
|
||||||
type Config interface {
|
|
||||||
ReadConfig(configFilePath string) error
|
|
||||||
ReadConfigString(config, format string) error
|
|
||||||
TLSConfig() cert.CertOptions
|
|
||||||
APIConfig() RPC
|
|
||||||
ListenerSpecs() map[string]endpoint.ListenerSpec
|
|
||||||
}
|
|
||||||
|
|
||||||
type config struct {
|
|
||||||
cfg *viper.Viper
|
|
||||||
TLS cert.CertOptions
|
|
||||||
Listeners map[string]endpoint.ListenerSpec
|
|
||||||
API RPC
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *config) APIConfig() RPC {
|
|
||||||
return c.API
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *config) ReadConfigString(config, format string) (err error) {
|
|
||||||
c.cfg.SetConfigType(format)
|
|
||||||
if err = c.cfg.ReadConfig(strings.NewReader(config)); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = c.cfg.Unmarshal(c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c config) ListenerSpecs() map[string]endpoint.ListenerSpec {
|
|
||||||
return c.Listeners
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c config) TLSConfig() cert.CertOptions {
|
|
||||||
return c.TLS
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *config) ReadConfig(configFilePath string) (err error) {
|
|
||||||
if configFilePath != "" && path.FileExists(configFilePath) {
|
|
||||||
c.cfg.SetConfigFile(configFilePath)
|
|
||||||
}
|
|
||||||
if err = c.cfg.ReadInConfig(); err != nil {
|
|
||||||
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
|
||||||
err = nil
|
|
||||||
} else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = c.cfg.Unmarshal(c)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_config_ReadConfig(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
config string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
wantListeners map[string]endpoint.ListenerSpec
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "Test endpoints config",
|
|
||||||
args: args{
|
|
||||||
//language=yaml
|
|
||||||
config: `
|
|
||||||
listeners:
|
|
||||||
tcp_80:
|
|
||||||
name: ''
|
|
||||||
protocol: tcp
|
|
||||||
listenAddress: ''
|
|
||||||
port: 80
|
|
||||||
tcp_443:
|
|
||||||
name: ''
|
|
||||||
protocol: tcp
|
|
||||||
listenAddress: ''
|
|
||||||
port: 443
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
wantListeners: map[string]endpoint.ListenerSpec{
|
|
||||||
"tcp_80": {
|
|
||||||
Name: "",
|
|
||||||
Protocol: "tcp",
|
|
||||||
Address: "",
|
|
||||||
Port: 80,
|
|
||||||
Endpoints: nil,
|
|
||||||
},
|
|
||||||
"tcp_443": {
|
|
||||||
Name: "",
|
|
||||||
Protocol: "tcp",
|
|
||||||
Address: "",
|
|
||||||
Port: 443,
|
|
||||||
Endpoints: nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
cfg := CreateConfig()
|
|
||||||
if err := cfg.ReadConfigString(tt.args.config, "yaml"); (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("ReadConfig() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(tt.wantListeners, cfg.ListenerSpecs()) {
|
|
||||||
t.Errorf("want = %v, got = %v", tt.wantListeners, cfg.ListenerSpecs())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
const (
|
|
||||||
EndpointsKey = "endpoints"
|
|
||||||
OptionsKey = "options"
|
|
||||||
)
|
|
|
@ -1,15 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
var (
|
|
||||||
registeredDefaults = make(map[string]interface{})
|
|
||||||
// default aliases
|
|
||||||
registeredAliases = map[string]string{}
|
|
||||||
)
|
|
||||||
|
|
||||||
func AddDefaultValue(key string, val interface{}) {
|
|
||||||
registeredDefaults[key] = val
|
|
||||||
}
|
|
||||||
|
|
||||||
func AddAlias(alias, orig string) {
|
|
||||||
registeredAliases[alias] = orig
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import "net/url"
|
|
||||||
|
|
||||||
type RPC struct {
|
|
||||||
Listen string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r RPC) ListenURL() (u *url.URL) {
|
|
||||||
var err error
|
|
||||||
if u, err = url.Parse(r.Listen); err != nil {
|
|
||||||
u, _ = url.Parse("tcp://:0")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,71 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/url"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestRPC_ListenURL(t *testing.T) {
|
|
||||||
type fields struct {
|
|
||||||
Listen string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
wantU *url.URL
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "Parse valid TCP URL",
|
|
||||||
fields: fields{
|
|
||||||
Listen: "tcp://localhost:8080",
|
|
||||||
},
|
|
||||||
wantU: func() *url.URL {
|
|
||||||
if u, e := url.Parse("tcp://localhost:8080"); e != nil {
|
|
||||||
t.Errorf("Error during URL parsing: %v", e)
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
}(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Parse valid unix socket url",
|
|
||||||
fields: fields{
|
|
||||||
Listen: "unix:///var/run/inetmock.sock",
|
|
||||||
},
|
|
||||||
wantU: func() *url.URL {
|
|
||||||
if u, e := url.Parse("unix:///var/run/inetmock.sock"); e != nil {
|
|
||||||
t.Errorf("Error during URL parsing: %v", e)
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
}(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Expect fallback value due to parse error",
|
|
||||||
fields: fields{
|
|
||||||
Listen: `"tcp;\\asdf:234sedf`,
|
|
||||||
},
|
|
||||||
wantU: func() *url.URL {
|
|
||||||
if u, e := url.Parse("tcp://:0"); e != nil {
|
|
||||||
t.Errorf("Error during URL parsing: %v", e)
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
}(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
r := RPC{
|
|
||||||
Listen: tt.fields.Listen,
|
|
||||||
}
|
|
||||||
if gotU := r.ListenURL(); !reflect.DeepEqual(gotU, tt.wantU) {
|
|
||||||
t.Errorf("ListenURL() = %v, want %v", gotU, tt.wantU)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
//go:generate mockgen -source=$GOFILE -destination=./../../internal/mock/endpoint/protocol_handler.mock.go -package=endpoint_mock
|
|
||||||
|
|
||||||
package endpoint
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/soheilhy/cmux"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/cert"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Lifecycle interface {
|
|
||||||
Name() string
|
|
||||||
Logger() logging.Logger
|
|
||||||
CertStore() cert.Store
|
|
||||||
Audit() audit.Emitter
|
|
||||||
Context() context.Context
|
|
||||||
Uplink() Uplink
|
|
||||||
UnmarshalOptions(cfg interface{}) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type ProtocolHandler interface {
|
|
||||||
Start(ctx Lifecycle) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type MultiplexHandler interface {
|
|
||||||
ProtocolHandler
|
|
||||||
Matchers() []cmux.Matcher
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
package endpoint
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
const (
|
|
||||||
shutdownTimeout = 5 * time.Second
|
|
||||||
)
|
|
|
@ -1,42 +0,0 @@
|
||||||
package endpoint
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
startupTimeoutDuration = 100 * time.Millisecond
|
|
||||||
)
|
|
||||||
|
|
||||||
type Endpoint struct {
|
|
||||||
Spec
|
|
||||||
name string
|
|
||||||
uplink Uplink
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e Endpoint) Start(lifecycle Lifecycle) (err error) {
|
|
||||||
startupResult := make(chan error)
|
|
||||||
ctx, cancel := context.WithTimeout(lifecycle.Context(), startupTimeoutDuration)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
lifecycle.Logger().Fatal("Startup error recovered", zap.Any("recovered", r))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
startupResult <- e.Handler.Start(lifecycle)
|
|
||||||
}()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case err = <-startupResult:
|
|
||||||
case <-ctx.Done():
|
|
||||||
err = ErrStartupTimeout
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,83 +0,0 @@
|
||||||
package mock
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"math"
|
|
||||||
"math/rand"
|
|
||||||
"net"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"github.com/mitchellh/mapstructure"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
randomIPStrategyName = "random"
|
|
||||||
incrementalIPStrategyName = "incremental"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
defaultStartIPIncrementalStrategy = net.ParseIP("10.10.0.1")
|
|
||||||
fallbackStrategies = map[string]ResolverFactory{
|
|
||||||
incrementalIPStrategyName: func(args map[string]interface{}) ResolverFallback {
|
|
||||||
tmp := struct {
|
|
||||||
StartIP string
|
|
||||||
}{}
|
|
||||||
var startIp net.IP
|
|
||||||
if err := mapstructure.Decode(args, &tmp); err == nil {
|
|
||||||
startIp = net.ParseIP(tmp.StartIP)
|
|
||||||
}
|
|
||||||
if startIp == nil || len(startIp) == 0 {
|
|
||||||
startIp = defaultStartIPIncrementalStrategy
|
|
||||||
}
|
|
||||||
return &incrementalIPFallback{
|
|
||||||
latestIp: ipToInt32(startIp),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
randomIPStrategyName: func(map[string]interface{}) ResolverFallback {
|
|
||||||
return &randomIPFallback{}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
type ResolverFactory func(args map[string]interface{}) ResolverFallback
|
|
||||||
|
|
||||||
func CreateResolverFallback(name string, args map[string]interface{}) ResolverFallback {
|
|
||||||
if factory, ok := fallbackStrategies[name]; ok {
|
|
||||||
return factory(args)
|
|
||||||
} else {
|
|
||||||
return fallbackStrategies[randomIPStrategyName](args)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type ResolverFallback interface {
|
|
||||||
GetIP() net.IP
|
|
||||||
}
|
|
||||||
|
|
||||||
type incrementalIPFallback struct {
|
|
||||||
latestIp uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *incrementalIPFallback) GetIP() net.IP {
|
|
||||||
if i.latestIp < math.MaxInt32 {
|
|
||||||
i.latestIp += 1
|
|
||||||
}
|
|
||||||
return uint32ToIP(i.latestIp)
|
|
||||||
}
|
|
||||||
|
|
||||||
type randomIPFallback struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (randomIPFallback) GetIP() net.IP {
|
|
||||||
return uint32ToIP(uint32(rand.Int31()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func uint32ToIP(i uint32) net.IP {
|
|
||||||
bytes := (*[4]byte)(unsafe.Pointer(&i))[:]
|
|
||||||
return net.IPv4(bytes[3], bytes[2], bytes[1], bytes[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
func ipToInt32(ip net.IP) uint32 {
|
|
||||||
v4 := ip.To4()
|
|
||||||
result := binary.BigEndian.Uint32(v4)
|
|
||||||
return result
|
|
||||||
}
|
|
|
@ -1,106 +0,0 @@
|
||||||
package mock
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_randomIPFallback_GetIP(t *testing.T) {
|
|
||||||
ra := randomIPFallback{}
|
|
||||||
for i := 0; i < 1000; i++ {
|
|
||||||
if got := ra.GetIP(); reflect.DeepEqual(got, net.IP{}) {
|
|
||||||
t.Errorf("GetIP() = %v", got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_incrementalIPFallback_GetIP(t *testing.T) {
|
|
||||||
type fields struct {
|
|
||||||
latestIp uint32
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fields fields
|
|
||||||
want []net.IP
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "Expect the next icremental IP",
|
|
||||||
fields: fields{
|
|
||||||
latestIp: 167772160,
|
|
||||||
},
|
|
||||||
want: []net.IP{
|
|
||||||
net.IPv4(10, 0, 0, 1),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Expect a sequence of 5",
|
|
||||||
fields: fields{
|
|
||||||
latestIp: 167772160,
|
|
||||||
},
|
|
||||||
want: []net.IP{
|
|
||||||
net.IPv4(10, 0, 0, 1),
|
|
||||||
net.IPv4(10, 0, 0, 2),
|
|
||||||
net.IPv4(10, 0, 0, 3),
|
|
||||||
net.IPv4(10, 0, 0, 4),
|
|
||||||
net.IPv4(10, 0, 0, 5),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Expect next block to be incremented",
|
|
||||||
fields: fields{
|
|
||||||
latestIp: 167772413,
|
|
||||||
},
|
|
||||||
want: []net.IP{
|
|
||||||
net.IPv4(10, 0, 0, 254),
|
|
||||||
net.IPv4(10, 0, 0, 255),
|
|
||||||
net.IPv4(10, 0, 1, 0),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
i := &incrementalIPFallback{
|
|
||||||
latestIp: tt.fields.latestIp,
|
|
||||||
}
|
|
||||||
for k := 0; k < len(tt.want); k++ {
|
|
||||||
if got := i.GetIP(); !reflect.DeepEqual(got, tt.want[k]) {
|
|
||||||
t.Errorf("GetIP() = %v, want %v", got, tt.want[k])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_ipToInt32(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
ip net.IP
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want uint32
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "Convert 188.193.106.113 to int",
|
|
||||||
args: args{
|
|
||||||
ip: net.ParseIP("188.193.106.113"),
|
|
||||||
},
|
|
||||||
want: 3166792305,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Convert 192.168.178.10 to int",
|
|
||||||
args: args{
|
|
||||||
ip: net.ParseIP("192.168.178.10"),
|
|
||||||
},
|
|
||||||
want: 3232281098,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := ipToInt32(tt.args.ip); got != tt.want {
|
|
||||||
t.Errorf("ipToInt32() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,77 +0,0 @@
|
||||||
package mock
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
|
||||||
|
|
||||||
type dnsHandler struct {
|
|
||||||
logger logging.Logger
|
|
||||||
dnsServer *dns.Server
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dnsHandler) Start(lifecycle endpoint.Lifecycle) (err error) {
|
|
||||||
var options dnsOptions
|
|
||||||
if options, err = loadFromConfig(lifecycle); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
d.logger = lifecycle.Logger().With(
|
|
||||||
zap.String("handler_name", lifecycle.Name()),
|
|
||||||
zap.String("address", lifecycle.Uplink().Addr().String()),
|
|
||||||
)
|
|
||||||
|
|
||||||
handler := ®exHandler{
|
|
||||||
handlerName: lifecycle.Name(),
|
|
||||||
fallback: options.Fallback,
|
|
||||||
logger: lifecycle.Logger(),
|
|
||||||
auditEmitter: lifecycle.Audit(),
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, rule := range options.Rules {
|
|
||||||
d.logger.Info(
|
|
||||||
"register DNS rule",
|
|
||||||
zap.String("pattern", rule.pattern.String()),
|
|
||||||
zap.String("response", rule.response.String()),
|
|
||||||
)
|
|
||||||
handler.AddRule(rule)
|
|
||||||
}
|
|
||||||
|
|
||||||
if lifecycle.Uplink().Listener != nil {
|
|
||||||
d.dnsServer = &dns.Server{
|
|
||||||
Listener: lifecycle.Uplink().Listener,
|
|
||||||
Handler: handler,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
d.dnsServer = &dns.Server{
|
|
||||||
PacketConn: lifecycle.Uplink().PacketConn,
|
|
||||||
Handler: handler,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
go d.startServer()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dnsHandler) startServer() {
|
|
||||||
if err := d.dnsServer.ActivateAndServe(); err != nil {
|
|
||||||
d.logger.Error(
|
|
||||||
"failed to start DNS server listener",
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dnsHandler) shutdownOnEnd(ctx context.Context) {
|
|
||||||
<-ctx.Done()
|
|
||||||
shutdownCtx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
|
||||||
defer cancel()
|
|
||||||
if err := d.dnsServer.ShutdownContext(shutdownCtx); err != nil {
|
|
||||||
d.logger.Error("failed to shutdown DNS server", zap.Error(err))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,77 +0,0 @@
|
||||||
package mock
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/docker/go-connections/nat"
|
|
||||||
"github.com/testcontainers/testcontainers-go"
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/test/integration"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
charSet = "abcdedfghijklmnopqrstABCDEFGHIJKLMNOP"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
rand.Seed(time.Now().Unix())
|
|
||||||
}
|
|
||||||
|
|
||||||
func Benchmark_dnsHandler(b *testing.B) {
|
|
||||||
var err error
|
|
||||||
var endpoint string
|
|
||||||
if endpoint, err = setupContainer(b, "53/udp"); err != nil {
|
|
||||||
b.Errorf("setupContainer() error = %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
|
||||||
resolv := resolver(endpoint)
|
|
||||||
for pb.Next() {
|
|
||||||
lookupCtx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
|
||||||
_, err := resolv.LookupHost(lookupCtx, fmt.Sprintf("www.%s.com", randomString(8)))
|
|
||||||
cancel()
|
|
||||||
if err != nil {
|
|
||||||
b.Errorf("LookupHost() error = %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func randomString(length int) (result string) {
|
|
||||||
buffer := strings.Builder{}
|
|
||||||
for i := 0; i < length; i++ {
|
|
||||||
buffer.WriteByte(charSet[rand.Intn(len(charSet))])
|
|
||||||
}
|
|
||||||
return buffer.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupContainer(b *testing.B, port string) (httpEndpoint string, err error) {
|
|
||||||
b.Helper()
|
|
||||||
|
|
||||||
startupCtx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
|
||||||
defer cancel()
|
|
||||||
var inetMockContainer testcontainers.Container
|
|
||||||
if inetMockContainer, err = integration.SetupINetMockContainer(startupCtx, b, port); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
httpEndpoint, err = inetMockContainer.PortEndpoint(startupCtx, nat.Port(port), "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolver(endpoint string) net.Resolver {
|
|
||||||
return net.Resolver{
|
|
||||||
PreferGo: true,
|
|
||||||
Dial: func(ctx context.Context, network, address string) (conn net.Conn, err error) {
|
|
||||||
dialer := net.Dialer{}
|
|
||||||
return dialer.DialContext(ctx, "udp", endpoint)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
package mock
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"regexp"
|
|
||||||
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
|
||||||
)
|
|
||||||
|
|
||||||
type resolverRule struct {
|
|
||||||
pattern *regexp.Regexp
|
|
||||||
response net.IP
|
|
||||||
}
|
|
||||||
|
|
||||||
type dnsOptions struct {
|
|
||||||
Rules []resolverRule
|
|
||||||
Fallback ResolverFallback
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadFromConfig(lifecycle endpoint.Lifecycle) (options dnsOptions, err error) {
|
|
||||||
type rule struct {
|
|
||||||
Pattern string
|
|
||||||
Response string
|
|
||||||
}
|
|
||||||
|
|
||||||
type fallback struct {
|
|
||||||
Strategy string
|
|
||||||
Args map[string]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
opts := struct {
|
|
||||||
Rules []rule
|
|
||||||
Fallback fallback
|
|
||||||
}{}
|
|
||||||
|
|
||||||
err = lifecycle.UnmarshalOptions(&opts)
|
|
||||||
|
|
||||||
for _, rule := range opts.Rules {
|
|
||||||
var err error
|
|
||||||
var rr resolverRule
|
|
||||||
if rr.pattern, err = regexp.Compile(rule.Pattern); err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if rr.response = net.ParseIP(rule.Response); rr.response == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
options.Rules = append(options.Rules, rr)
|
|
||||||
}
|
|
||||||
|
|
||||||
options.Fallback = CreateResolverFallback(
|
|
||||||
opts.Fallback.Strategy,
|
|
||||||
opts.Fallback.Args,
|
|
||||||
)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,139 +0,0 @@
|
||||||
package mock
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/audit/details"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
|
||||||
|
|
||||||
type regexHandler struct {
|
|
||||||
handlerName string
|
|
||||||
routes []resolverRule
|
|
||||||
fallback ResolverFallback
|
|
||||||
auditEmitter audit.Emitter
|
|
||||||
logger logging.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rh *regexHandler) AddRule(rule resolverRule) {
|
|
||||||
rh.routes = append(rh.routes, rule)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rh *regexHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
|
|
||||||
timer := prometheus.NewTimer(requestDurationHistogram.WithLabelValues(rh.handlerName))
|
|
||||||
defer func() {
|
|
||||||
timer.ObserveDuration()
|
|
||||||
}()
|
|
||||||
|
|
||||||
rh.recordRequest(r, w.LocalAddr(), w.RemoteAddr())
|
|
||||||
|
|
||||||
m := new(dns.Msg)
|
|
||||||
m.Compress = false
|
|
||||||
m.SetReply(r)
|
|
||||||
|
|
||||||
if r.Opcode == dns.OpcodeQuery {
|
|
||||||
rh.handleQuery(m)
|
|
||||||
}
|
|
||||||
if err := w.WriteMsg(m); err != nil {
|
|
||||||
rh.logger.Error(
|
|
||||||
"Failed to write DNS response message",
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rh *regexHandler) handleQuery(m *dns.Msg) {
|
|
||||||
for _, q := range m.Question {
|
|
||||||
switch q.Qtype {
|
|
||||||
case dns.TypeA:
|
|
||||||
totalHandledRequestsCounter.WithLabelValues(rh.handlerName).Inc()
|
|
||||||
for _, rule := range rh.routes {
|
|
||||||
if !rule.pattern.MatchString(q.Name) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
m.Authoritative = true
|
|
||||||
answer := &dns.A{
|
|
||||||
Hdr: dns.RR_Header{
|
|
||||||
Name: q.Name,
|
|
||||||
Rrtype: dns.TypeA,
|
|
||||||
Class: dns.ClassINET,
|
|
||||||
Ttl: 60,
|
|
||||||
},
|
|
||||||
A: rule.response,
|
|
||||||
}
|
|
||||||
m.Answer = append(m.Answer, answer)
|
|
||||||
rh.logger.Info(
|
|
||||||
"matched DNS rule",
|
|
||||||
zap.String("pattern", rule.pattern.String()),
|
|
||||||
zap.String("response", rule.response.String()),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rh.handleFallbackForMessage(m, q)
|
|
||||||
default:
|
|
||||||
unhandledRequestsCounter.WithLabelValues(rh.handlerName).Inc()
|
|
||||||
rh.logger.Warn(
|
|
||||||
"Unhandled DNS question type - no response will be sent",
|
|
||||||
zap.Uint16("question_type", q.Qtype),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rh *regexHandler) handleFallbackForMessage(m *dns.Msg, q dns.Question) {
|
|
||||||
fallbackIP := rh.fallback.GetIP()
|
|
||||||
answer := &dns.A{
|
|
||||||
Hdr: dns.RR_Header{
|
|
||||||
Name: q.Name,
|
|
||||||
Rrtype: dns.TypeA,
|
|
||||||
Class: dns.ClassINET,
|
|
||||||
Ttl: 60,
|
|
||||||
},
|
|
||||||
A: fallbackIP,
|
|
||||||
}
|
|
||||||
rh.logger.Info(
|
|
||||||
"Falling back to generated IP",
|
|
||||||
zap.String("response", fallbackIP.String()),
|
|
||||||
)
|
|
||||||
m.Authoritative = true
|
|
||||||
m.Answer = append(m.Answer, answer)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rh *regexHandler) recordRequest(m *dns.Msg, localAddr, remoteAddr net.Addr) {
|
|
||||||
dnsDetails := &details.DNS{
|
|
||||||
OPCode: details.DNSOpCode(m.Opcode),
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, q := range m.Question {
|
|
||||||
dnsDetails.Questions = append(dnsDetails.Questions, details.DNSQuestion{
|
|
||||||
RRType: details.ResourceRecordType(q.Qtype),
|
|
||||||
Name: q.Name,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
ev := audit.Event{
|
|
||||||
Transport: guessTransportFromAddr(localAddr),
|
|
||||||
Application: audit.AppProtocol_DNS,
|
|
||||||
ProtocolDetails: dnsDetails,
|
|
||||||
}
|
|
||||||
|
|
||||||
ev.SetSourceIPFromAddr(remoteAddr)
|
|
||||||
ev.SetDestinationIPFromAddr(localAddr)
|
|
||||||
|
|
||||||
rh.auditEmitter.Emit(ev)
|
|
||||||
}
|
|
||||||
|
|
||||||
func guessTransportFromAddr(addr net.Addr) audit.TransportProtocol {
|
|
||||||
switch addr.(type) {
|
|
||||||
case *net.TCPAddr:
|
|
||||||
return audit.TransportProtocol_TCP
|
|
||||||
case *net.UDPAddr:
|
|
||||||
return audit.TransportProtocol_UDP
|
|
||||||
default:
|
|
||||||
return audit.TransportProtocol_UNKNOWN_TRANSPORT
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
package mock
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/metrics"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
name = "dns_mock"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
handlerNameLblName = "handler_name"
|
|
||||||
totalHandledRequestsCounter *prometheus.CounterVec
|
|
||||||
unhandledRequestsCounter *prometheus.CounterVec
|
|
||||||
requestDurationHistogram *prometheus.HistogramVec
|
|
||||||
)
|
|
||||||
|
|
||||||
func AddDNSMock(registry endpoint.HandlerRegistry) (err error) {
|
|
||||||
if totalHandledRequestsCounter, err = metrics.Counter(
|
|
||||||
name,
|
|
||||||
"handled_requests_total",
|
|
||||||
"",
|
|
||||||
handlerNameLblName,
|
|
||||||
); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if unhandledRequestsCounter, err = metrics.Counter(
|
|
||||||
name,
|
|
||||||
"unhandled_requests_total",
|
|
||||||
"",
|
|
||||||
handlerNameLblName,
|
|
||||||
); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if requestDurationHistogram, err = metrics.Histogram(
|
|
||||||
name,
|
|
||||||
"request_duration",
|
|
||||||
"",
|
|
||||||
nil,
|
|
||||||
handlerNameLblName,
|
|
||||||
); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
registry.RegisterHandler(name, func() endpoint.ProtocolHandler {
|
|
||||||
return &dnsHandler{}
|
|
||||||
})
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
package http
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/audit/details"
|
|
||||||
)
|
|
||||||
|
|
||||||
func EventFromRequest(request *http.Request, app audit.AppProtocol) audit.Event {
|
|
||||||
httpDetails := details.HTTP{
|
|
||||||
Method: request.Method,
|
|
||||||
Host: request.Host,
|
|
||||||
URI: request.RequestURI,
|
|
||||||
Proto: request.Proto,
|
|
||||||
Headers: request.Header,
|
|
||||||
}
|
|
||||||
|
|
||||||
ev := audit.Event{
|
|
||||||
Transport: audit.TransportProtocol_TCP,
|
|
||||||
Application: app,
|
|
||||||
ProtocolDetails: httpDetails,
|
|
||||||
}
|
|
||||||
|
|
||||||
if state, ok := tlsConnectionState(request.Context()); ok {
|
|
||||||
ev.TLS = &audit.TLSDetails{
|
|
||||||
Version: audit.TLSVersionToEntity(state.Version).String(),
|
|
||||||
CipherSuite: tls.CipherSuiteName(state.CipherSuite),
|
|
||||||
ServerName: state.ServerName,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ev.SetDestinationIPFromAddr(localAddr(request.Context()))
|
|
||||||
ev.SetSourceIPFromAddr(remoteAddr(request.Context()))
|
|
||||||
|
|
||||||
return ev
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
package http
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto/tls"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/soheilhy/cmux"
|
|
||||||
)
|
|
||||||
|
|
||||||
type httpContextKey string
|
|
||||||
|
|
||||||
const (
|
|
||||||
remoteAddrKey httpContextKey = "gitlab.com/inetmock/inetmock/internal/endpoint/handler/http/context/remoteAddr"
|
|
||||||
localAddrKey httpContextKey = "gitlab.com/inetmock/inetmock/internal/endpoint/handler/http/context/localAddr"
|
|
||||||
tlsStateKey httpContextKey = "gitlab.com/inetmock/inetmock/internal/endpoint/handler/http/context/tlsState"
|
|
||||||
)
|
|
||||||
|
|
||||||
func StoreConnPropertiesInContext(ctx context.Context, c net.Conn) context.Context {
|
|
||||||
ctx = context.WithValue(ctx, remoteAddrKey, c.RemoteAddr())
|
|
||||||
ctx = context.WithValue(ctx, localAddrKey, c.LocalAddr())
|
|
||||||
ctx = addTLSConnectionStateToContext(ctx, c)
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
func addTLSConnectionStateToContext(ctx context.Context, c net.Conn) context.Context {
|
|
||||||
switch subConn := c.(type) {
|
|
||||||
case *tls.Conn:
|
|
||||||
return context.WithValue(ctx, tlsStateKey, subConn.ConnectionState())
|
|
||||||
case *cmux.MuxConn:
|
|
||||||
return addTLSConnectionStateToContext(ctx, subConn.Conn)
|
|
||||||
default:
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func tlsConnectionState(ctx context.Context) (tls.ConnectionState, bool) {
|
|
||||||
val := ctx.Value(tlsStateKey)
|
|
||||||
if val == nil {
|
|
||||||
return tls.ConnectionState{}, false
|
|
||||||
}
|
|
||||||
return val.(tls.ConnectionState), true
|
|
||||||
}
|
|
||||||
|
|
||||||
func localAddr(ctx context.Context) net.Addr {
|
|
||||||
val := ctx.Value(localAddrKey)
|
|
||||||
if val == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return val.(net.Addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func remoteAddr(ctx context.Context) net.Addr {
|
|
||||||
val := ctx.Value(remoteAddrKey)
|
|
||||||
if val == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return val.(net.Addr)
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
package mock
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/soheilhy/cmux"
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
|
||||||
imHttp "gitlab.com/inetmock/inetmock/internal/endpoint/handler/http"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
name = "http_mock"
|
|
||||||
handlerNameLblName = "handler_name"
|
|
||||||
ruleMatchedLblName = "rule_matched"
|
|
||||||
)
|
|
||||||
|
|
||||||
type httpHandler struct {
|
|
||||||
logger logging.Logger
|
|
||||||
server *http.Server
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *httpHandler) Matchers() []cmux.Matcher {
|
|
||||||
return []cmux.Matcher{cmux.HTTP1()}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *httpHandler) Start(lifecycle endpoint.Lifecycle) (err error) {
|
|
||||||
p.logger = lifecycle.Logger().With(
|
|
||||||
zap.String("protocol_handler", name),
|
|
||||||
)
|
|
||||||
|
|
||||||
var options httpOptions
|
|
||||||
if options, err = loadFromConfig(lifecycle); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
p.logger = p.logger.With(
|
|
||||||
zap.String("address", lifecycle.Uplink().Addr().String()),
|
|
||||||
)
|
|
||||||
|
|
||||||
router := &RegexpHandler{
|
|
||||||
logger: p.logger,
|
|
||||||
emitter: lifecycle.Audit(),
|
|
||||||
handlerName: lifecycle.Name(),
|
|
||||||
}
|
|
||||||
p.server = &http.Server{
|
|
||||||
Handler: router,
|
|
||||||
ConnContext: imHttp.StoreConnPropertiesInContext,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, rule := range options.Rules {
|
|
||||||
router.setupRoute(rule)
|
|
||||||
}
|
|
||||||
|
|
||||||
go p.startServer(lifecycle.Uplink().Listener)
|
|
||||||
go p.shutdownOnCancel(lifecycle.Context())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *httpHandler) shutdownOnCancel(ctx context.Context) {
|
|
||||||
<-ctx.Done()
|
|
||||||
p.logger.Info("Shutting down HTTP mock")
|
|
||||||
if err := p.server.Close(); err != nil {
|
|
||||||
p.logger.Error(
|
|
||||||
"failed to shutdown HTTP server",
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *httpHandler) startServer(listener net.Listener) {
|
|
||||||
defer func() {
|
|
||||||
if err := listener.Close(); err != nil {
|
|
||||||
p.logger.Warn("failed to close listener", zap.Error(err))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
if err := p.server.Serve(listener); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
||||||
p.logger.Error(
|
|
||||||
"failed to start http listener",
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,155 +0,0 @@
|
||||||
package mock_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto/tls"
|
|
||||||
"crypto/x509"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"math/rand"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/docker/go-connections/nat"
|
|
||||||
"github.com/testcontainers/testcontainers-go"
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/test/integration"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
charSet = "abcdedfghijklmnopqrstABCDEFGHIJKLMNOP"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
availableExtensions = []string{"gif", "html", "ico", "jpg", "png", "txt"}
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
rand.Seed(time.Now().Unix())
|
|
||||||
}
|
|
||||||
|
|
||||||
func Benchmark_httpHandler(b *testing.B) {
|
|
||||||
type benchmark struct {
|
|
||||||
name string
|
|
||||||
port string
|
|
||||||
scheme string
|
|
||||||
}
|
|
||||||
benchmarks := []benchmark{
|
|
||||||
{
|
|
||||||
name: "HTTP",
|
|
||||||
port: "80/tcp",
|
|
||||||
scheme: "http",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "HTTPS",
|
|
||||||
port: "443/tcp",
|
|
||||||
scheme: "https",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
scenario := func(bm benchmark) func(bm *testing.B) {
|
|
||||||
return func(b *testing.B) {
|
|
||||||
var err error
|
|
||||||
var endpoint string
|
|
||||||
if endpoint, err = setupContainer(b, bm.scheme, bm.port); err != nil {
|
|
||||||
b.Errorf("setupContainer() error = %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var httpClient *http.Client
|
|
||||||
if httpClient, err = setupHTTPClient(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
|
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
|
||||||
for pb.Next() {
|
|
||||||
extension := availableExtensions[rand.Intn(len(availableExtensions))]
|
|
||||||
|
|
||||||
reqUrl, _ := url.Parse(fmt.Sprintf("%s/%s.%s", endpoint, randomString(15), extension))
|
|
||||||
|
|
||||||
req := &http.Request{
|
|
||||||
Method: http.MethodGet,
|
|
||||||
URL: reqUrl,
|
|
||||||
Close: false,
|
|
||||||
Host: "www.inetmock.com",
|
|
||||||
}
|
|
||||||
if resp, err := httpClient.Do(req); err != nil {
|
|
||||||
b.Error(err)
|
|
||||||
} else if resp.StatusCode != 200 {
|
|
||||||
b.Errorf("Got status code %d", resp.StatusCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, bm := range benchmarks {
|
|
||||||
b.Run(bm.name, scenario(bm))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func randomString(length int) (result string) {
|
|
||||||
buffer := strings.Builder{}
|
|
||||||
for i := 0; i < length; i++ {
|
|
||||||
buffer.WriteByte(charSet[rand.Intn(len(charSet))])
|
|
||||||
}
|
|
||||||
return buffer.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupContainer(b *testing.B, scheme, port string) (httpEndpoint string, err error) {
|
|
||||||
b.Helper()
|
|
||||||
|
|
||||||
startupCtx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
|
||||||
defer cancel()
|
|
||||||
var inetMockContainer testcontainers.Container
|
|
||||||
if inetMockContainer, err = integration.SetupINetMockContainer(startupCtx, b, port); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
httpEndpoint, err = inetMockContainer.PortEndpoint(startupCtx, nat.Port(port), scheme)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupHTTPClient() (client *http.Client, err error) {
|
|
||||||
_, fileName, _, _ := runtime.Caller(0)
|
|
||||||
|
|
||||||
var repoRoot string
|
|
||||||
if repoRoot, err = filepath.Abs(filepath.Join(filepath.Dir(fileName), "..", "..", "..", "..", "..")); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var demoCABytes []byte
|
|
||||||
if demoCABytes, err = ioutil.ReadFile(filepath.Join(repoRoot, "assets", "demoCA", "ca.pem")); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
rootCaPool := x509.NewCertPool()
|
|
||||||
if !rootCaPool.AppendCertsFromPEM(demoCABytes) {
|
|
||||||
err = errors.New("failed to add CA key")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
client = &http.Client{
|
|
||||||
Transport: &http.Transport{
|
|
||||||
DialContext: (&net.Dialer{
|
|
||||||
Timeout: 30 * time.Second,
|
|
||||||
KeepAlive: 30 * time.Second,
|
|
||||||
}).DialContext,
|
|
||||||
TLSClientConfig: &tls.Config{
|
|
||||||
RootCAs: rootCaPool,
|
|
||||||
},
|
|
||||||
ForceAttemptHTTP2: true,
|
|
||||||
MaxIdleConns: 100,
|
|
||||||
IdleConnTimeout: 90 * time.Second,
|
|
||||||
TLSHandshakeTimeout: 10 * time.Second,
|
|
||||||
ExpectContinueTimeout: 1 * time.Second,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,97 +0,0 @@
|
||||||
//go:generate go-enum -f $GOFILE --lower --marshal --names
|
|
||||||
package mock
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
|
||||||
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ruleValueSelectors = map[RequestMatchTarget]ruleValueSelector{
|
|
||||||
RequestMatchTargetHeader: func(req *http.Request, targetKey string) string {
|
|
||||||
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 {
|
|
||||||
pattern *regexp.Regexp
|
|
||||||
response string
|
|
||||||
requestMatchTarget RequestMatchTarget
|
|
||||||
targetKey string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr targetRule) Pattern() *regexp.Regexp {
|
|
||||||
return tr.pattern
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr targetRule) Response() string {
|
|
||||||
return tr.response
|
|
||||||
}
|
|
||||||
|
|
||||||
type httpOptions struct {
|
|
||||||
Rules []targetRule
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadFromConfig(lifecycle endpoint.Lifecycle) (options httpOptions, err error) {
|
|
||||||
type tmpCfg struct {
|
|
||||||
Pattern string
|
|
||||||
Response string
|
|
||||||
Matcher string
|
|
||||||
Target string
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpRules := struct {
|
|
||||||
Rules []tmpCfg
|
|
||||||
}{}
|
|
||||||
|
|
||||||
if err = lifecycle.UnmarshalOptions(&tmpRules); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, i := range tmpRules.Rules {
|
|
||||||
var rulePattern *regexp.Regexp
|
|
||||||
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 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
options.Rules = append(options.Rules, targetRule{
|
|
||||||
pattern: rulePattern,
|
|
||||||
response: absoluteResponsePath,
|
|
||||||
requestMatchTarget: matchTargetValue,
|
|
||||||
targetKey: i.Target,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,177 +0,0 @@
|
||||||
package mock
|
|
||||||
|
|
||||||
import (
|
|
||||||
"path/filepath"
|
|
||||||
"reflect"
|
|
||||||
"regexp"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/golang/mock/gomock"
|
|
||||||
"github.com/mitchellh/mapstructure"
|
|
||||||
endpoint_mock "gitlab.com/inetmock/inetmock/internal/mock/endpoint"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_loadFromConfig(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
config map[string]interface{}
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
wantOptions httpOptions
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "Parse default config",
|
|
||||||
args: args{
|
|
||||||
config: map[string]interface{}{
|
|
||||||
"rules": []struct {
|
|
||||||
Pattern string
|
|
||||||
Matcher string
|
|
||||||
Response string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
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: map[string]interface{}{
|
|
||||||
"rules": []struct {
|
|
||||||
Pattern string
|
|
||||||
Matcher string
|
|
||||||
Response string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
Pattern: ".*\\.(?i)exe",
|
|
||||||
Response: "./assets/fakeFiles/sample.exe",
|
|
||||||
Matcher: "Path",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantOptions: httpOptions{
|
|
||||||
Rules: []targetRule{
|
|
||||||
{
|
|
||||||
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: map[string]interface{}{
|
|
||||||
"rules": []struct {
|
|
||||||
Pattern string
|
|
||||||
Matcher string
|
|
||||||
Target string
|
|
||||||
Response string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
Pattern: "^application/octet-stream$",
|
|
||||||
Response: "./assets/fakeFiles/sample.exe",
|
|
||||||
Target: "Content-Type",
|
|
||||||
Matcher: "Header",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantOptions: httpOptions{
|
|
||||||
Rules: []targetRule{
|
|
||||||
{
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Parse config with header matcher and TLS true",
|
|
||||||
args: args{
|
|
||||||
config: map[string]interface{}{
|
|
||||||
"tls": true,
|
|
||||||
"rules": []struct {
|
|
||||||
Pattern string
|
|
||||||
Matcher string
|
|
||||||
Target string
|
|
||||||
Response string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
Pattern: "^application/octet-stream$",
|
|
||||||
Response: "./assets/fakeFiles/sample.exe",
|
|
||||||
Target: "Content-Type",
|
|
||||||
Matcher: "Header",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantOptions: httpOptions{
|
|
||||||
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) {
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
t.Cleanup(ctrl.Finish)
|
|
||||||
lcMock := endpoint_mock.NewMockLifecycle(ctrl)
|
|
||||||
|
|
||||||
lcMock.EXPECT().UnmarshalOptions(gomock.Any()).Do(func(cfg interface{}) {
|
|
||||||
_ = mapstructure.Decode(tt.args.config, cfg)
|
|
||||||
})
|
|
||||||
|
|
||||||
gotOptions, err := loadFromConfig(lcMock)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("loadFromConfig() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(gotOptions, tt.wantOptions) {
|
|
||||||
t.Errorf("loadFromConfig() gotOptions = %v, want %v", gotOptions, tt.wantOptions)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
package mock
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
|
||||||
imHttp "gitlab.com/inetmock/inetmock/internal/endpoint/handler/http"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
|
||||||
|
|
||||||
type route struct {
|
|
||||||
rule targetRule
|
|
||||||
handler http.Handler
|
|
||||||
}
|
|
||||||
|
|
||||||
type RegexpHandler struct {
|
|
||||||
handlerName string
|
|
||||||
logger logging.Logger
|
|
||||||
routes []*route
|
|
||||||
emitter audit.Emitter
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *RegexpHandler) Handler(rule targetRule, handler http.Handler) {
|
|
||||||
h.routes = append(h.routes, &route{rule, handler})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *RegexpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
timer := prometheus.NewTimer(requestDurationHistogram.WithLabelValues(h.handlerName))
|
|
||||||
defer timer.ObserveDuration()
|
|
||||||
|
|
||||||
for idx := range h.routes {
|
|
||||||
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()
|
|
||||||
h.routes[idx].handler.ServeHTTP(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// no pattern matched; send 404 response
|
|
||||||
totalRequestCounter.WithLabelValues(h.handlerName, strconv.FormatBool(false)).Inc()
|
|
||||||
http.NotFound(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *RegexpHandler) setupRoute(rule targetRule) {
|
|
||||||
h.logger.Info(
|
|
||||||
"setup routing",
|
|
||||||
zap.String("route", rule.Pattern().String()),
|
|
||||||
zap.String("response", rule.Response()),
|
|
||||||
)
|
|
||||||
|
|
||||||
h.Handler(rule, emittingFileHandler{
|
|
||||||
emitter: h.emitter,
|
|
||||||
targetPath: rule.response,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type emittingFileHandler struct {
|
|
||||||
emitter audit.Emitter
|
|
||||||
targetPath string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f emittingFileHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
|
||||||
f.emitter.Emit(imHttp.EventFromRequest(request, audit.AppProtocol_HTTP))
|
|
||||||
http.ServeFile(writer, request, f.targetPath)
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
package mock
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/metrics"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
totalRequestCounter *prometheus.CounterVec
|
|
||||||
requestDurationHistogram *prometheus.HistogramVec
|
|
||||||
)
|
|
||||||
|
|
||||||
func AddHTTPMock(registry endpoint.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() endpoint.ProtocolHandler {
|
|
||||||
return &httpHandler{}
|
|
||||||
})
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
package proxy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/soheilhy/cmux"
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
|
||||||
imHttp "gitlab.com/inetmock/inetmock/internal/endpoint/handler/http"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
"gopkg.in/elazarl/goproxy.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
name = "http_proxy"
|
|
||||||
)
|
|
||||||
|
|
||||||
type httpProxy struct {
|
|
||||||
logger logging.Logger
|
|
||||||
proxy *goproxy.ProxyHttpServer
|
|
||||||
server *http.Server
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *httpProxy) Matchers() []cmux.Matcher {
|
|
||||||
return []cmux.Matcher{cmux.HTTP1()}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *httpProxy) Start(lifecycle endpoint.Lifecycle) (err error) {
|
|
||||||
var opts httpProxyOptions
|
|
||||||
if err = lifecycle.UnmarshalOptions(&opts); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
h.server = &http.Server{
|
|
||||||
Handler: h.proxy,
|
|
||||||
ConnContext: imHttp.StoreConnPropertiesInContext,
|
|
||||||
}
|
|
||||||
h.logger = h.logger.With(
|
|
||||||
zap.String("handler_name", lifecycle.Name()),
|
|
||||||
zap.String("address", lifecycle.Uplink().Addr().String()),
|
|
||||||
)
|
|
||||||
|
|
||||||
tlsConfig := lifecycle.CertStore().TLSConfig()
|
|
||||||
|
|
||||||
proxyHandler := &proxyHttpHandler{
|
|
||||||
handlerName: lifecycle.Name(),
|
|
||||||
options: opts,
|
|
||||||
logger: h.logger,
|
|
||||||
emitter: lifecycle.Audit(),
|
|
||||||
}
|
|
||||||
|
|
||||||
proxyHTTPSHandler := &proxyHttpsHandler{
|
|
||||||
options: opts,
|
|
||||||
tlsConfig: tlsConfig,
|
|
||||||
emitter: lifecycle.Audit(),
|
|
||||||
}
|
|
||||||
|
|
||||||
h.proxy.OnRequest().Do(proxyHandler)
|
|
||||||
h.proxy.OnRequest().HandleConnect(proxyHTTPSHandler)
|
|
||||||
go h.startProxy(lifecycle.Uplink().Listener)
|
|
||||||
go h.shutdownOnContextDone(lifecycle.Context())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *httpProxy) startProxy(listener net.Listener) {
|
|
||||||
if err := h.server.Serve(listener); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
||||||
h.logger.Error(
|
|
||||||
"failed to start proxy server",
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *httpProxy) shutdownOnContextDone(ctx context.Context) {
|
|
||||||
<-ctx.Done()
|
|
||||||
var err error
|
|
||||||
h.logger.Info("Shutting down HTTP proxy")
|
|
||||||
if err = h.server.Close(); err != nil {
|
|
||||||
h.logger.Error(
|
|
||||||
"failed to shutdown proxy endpoint",
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,167 +0,0 @@
|
||||||
package proxy_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto/tls"
|
|
||||||
"crypto/x509"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"math/rand"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/docker/go-connections/nat"
|
|
||||||
"github.com/testcontainers/testcontainers-go"
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/test/integration"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
charSet = "abcdedfghijklmnopqrstABCDEFGHIJKLMNOP"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
availableExtensions = []string{"gif", "html", "ico", "jpg", "png", "txt"}
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
rand.Seed(time.Now().Unix())
|
|
||||||
}
|
|
||||||
|
|
||||||
func Benchmark_httpProxy(b *testing.B) {
|
|
||||||
type benchmark struct {
|
|
||||||
name string
|
|
||||||
port string
|
|
||||||
scheme string
|
|
||||||
}
|
|
||||||
benchmarks := []benchmark{
|
|
||||||
{
|
|
||||||
name: "HTTP",
|
|
||||||
port: "3128/tcp",
|
|
||||||
scheme: "http",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "HTTPS",
|
|
||||||
port: "3128/tcp",
|
|
||||||
scheme: "https",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
scenario := func(bm benchmark) func(bm *testing.B) {
|
|
||||||
return func(b *testing.B) {
|
|
||||||
var err error
|
|
||||||
var endpoint string
|
|
||||||
if endpoint, err = setupContainer(b, bm.port); err != nil {
|
|
||||||
b.Errorf("setupContainer() error = %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var httpClient *http.Client
|
|
||||||
if httpClient, err = setupHTTPClient(fmt.Sprintf("http://%s", endpoint), fmt.Sprintf("https://%s", endpoint)); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(500 * time.Millisecond)
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
|
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
|
||||||
for pb.Next() {
|
|
||||||
extension := availableExtensions[rand.Intn(len(availableExtensions))]
|
|
||||||
|
|
||||||
reqUrl, _ := url.Parse(fmt.Sprintf("%s://%s/%s.%s", bm.scheme, endpoint, randomString(15), extension))
|
|
||||||
|
|
||||||
req := &http.Request{
|
|
||||||
Method: http.MethodGet,
|
|
||||||
URL: reqUrl,
|
|
||||||
Close: false,
|
|
||||||
Host: "www.inetmock.com",
|
|
||||||
}
|
|
||||||
if resp, err := httpClient.Do(req); err != nil {
|
|
||||||
b.Error(err)
|
|
||||||
} else if resp.StatusCode != 200 {
|
|
||||||
b.Errorf("Got status code %d", resp.StatusCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, bm := range benchmarks {
|
|
||||||
b.Run(bm.name, scenario(bm))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func randomString(length int) (result string) {
|
|
||||||
buffer := strings.Builder{}
|
|
||||||
for i := 0; i < length; i++ {
|
|
||||||
buffer.WriteByte(charSet[rand.Intn(len(charSet))])
|
|
||||||
}
|
|
||||||
return buffer.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupContainer(b *testing.B, port string) (httpEndpoint string, err error) {
|
|
||||||
b.Helper()
|
|
||||||
|
|
||||||
startupCtx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
|
||||||
defer cancel()
|
|
||||||
var inetMockContainer testcontainers.Container
|
|
||||||
if inetMockContainer, err = integration.SetupINetMockContainer(startupCtx, b, port); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
httpEndpoint, err = inetMockContainer.PortEndpoint(startupCtx, nat.Port(port), "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupHTTPClient(httpEndpoint, httpsEndpoint string) (client *http.Client, err error) {
|
|
||||||
_, fileName, _, _ := runtime.Caller(0)
|
|
||||||
|
|
||||||
var repoRoot string
|
|
||||||
if repoRoot, err = filepath.Abs(filepath.Join(filepath.Dir(fileName), "..", "..", "..", "..", "..")); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var demoCABytes []byte
|
|
||||||
if demoCABytes, err = ioutil.ReadFile(filepath.Join(repoRoot, "assets", "demoCA", "ca.pem")); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
rootCaPool := x509.NewCertPool()
|
|
||||||
if !rootCaPool.AppendCertsFromPEM(demoCABytes) {
|
|
||||||
err = errors.New("failed to add CA key")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
client = &http.Client{
|
|
||||||
Transport: &http.Transport{
|
|
||||||
Proxy: func(req *http.Request) (*url.URL, error) {
|
|
||||||
switch req.URL.Scheme {
|
|
||||||
case "http":
|
|
||||||
return url.Parse(httpEndpoint)
|
|
||||||
case "https":
|
|
||||||
return url.Parse(httpsEndpoint)
|
|
||||||
default:
|
|
||||||
return nil, errors.New("unknown scheme")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
DialContext: (&net.Dialer{
|
|
||||||
Timeout: 30 * time.Second,
|
|
||||||
KeepAlive: 30 * time.Second,
|
|
||||||
}).DialContext,
|
|
||||||
TLSClientConfig: &tls.Config{
|
|
||||||
RootCAs: rootCaPool,
|
|
||||||
},
|
|
||||||
ForceAttemptHTTP2: true,
|
|
||||||
MaxIdleConns: 100,
|
|
||||||
IdleConnTimeout: 90 * time.Second,
|
|
||||||
TLSHandshakeTimeout: 10 * time.Second,
|
|
||||||
ExpectContinueTimeout: 1 * time.Second,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
package proxy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
type redirectionTarget struct {
|
|
||||||
IPAddress string
|
|
||||||
Port uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rt redirectionTarget) host() string {
|
|
||||||
return fmt.Sprintf("%s:%d", rt.IPAddress, rt.Port)
|
|
||||||
}
|
|
||||||
|
|
||||||
type httpProxyOptions struct {
|
|
||||||
Target redirectionTarget
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
package proxy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/jinzhu/copier"
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
|
||||||
imHttp "gitlab.com/inetmock/inetmock/internal/endpoint/handler/http"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
"gopkg.in/elazarl/goproxy.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
type proxyHttpsHandler struct {
|
|
||||||
options httpProxyOptions
|
|
||||||
tlsConfig *tls.Config
|
|
||||||
emitter audit.Emitter
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *proxyHttpsHandler) HandleConnect(_ string, ctx *goproxy.ProxyCtx) (*goproxy.ConnectAction, string) {
|
|
||||||
p.emitter.Emit(imHttp.EventFromRequest(ctx.Req, audit.AppProtocol_HTTP_PROXY))
|
|
||||||
|
|
||||||
return &goproxy.ConnectAction{
|
|
||||||
Action: goproxy.ConnectAccept,
|
|
||||||
TLSConfig: func(host string, ctx *goproxy.ProxyCtx) (*tls.Config, error) {
|
|
||||||
return p.tlsConfig, nil
|
|
||||||
},
|
|
||||||
}, p.options.Target.host()
|
|
||||||
}
|
|
||||||
|
|
||||||
type proxyHttpHandler struct {
|
|
||||||
handlerName string
|
|
||||||
options httpProxyOptions
|
|
||||||
logger logging.Logger
|
|
||||||
emitter audit.Emitter
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *proxyHttpHandler) Handle(req *http.Request, ctx *goproxy.ProxyCtx) (retReq *http.Request, resp *http.Response) {
|
|
||||||
timer := prometheus.NewTimer(requestDurationHistogram.WithLabelValues(p.handlerName))
|
|
||||||
defer timer.ObserveDuration()
|
|
||||||
|
|
||||||
retReq = req
|
|
||||||
p.emitter.Emit(imHttp.EventFromRequest(req, audit.AppProtocol_HTTP_PROXY))
|
|
||||||
|
|
||||||
var err error
|
|
||||||
var redirectReq *http.Request
|
|
||||||
if redirectReq, err = redirectHTTPRequest(p.options.Target.host(), req); err != nil {
|
|
||||||
return req, nil
|
|
||||||
}
|
|
||||||
if resp, err = ctx.RoundTrip(redirectReq); err != nil {
|
|
||||||
p.logger.Error(
|
|
||||||
"error while doing roundtrip",
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
return req, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func redirectHTTPRequest(targetHost string, originalRequest *http.Request) (redirectReq *http.Request, err error) {
|
|
||||||
redirectReq = new(http.Request)
|
|
||||||
if err = copier.Copy(redirectReq, originalRequest); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
originalRequest.URL.Host = targetHost
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
package proxy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/metrics"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
"gopkg.in/elazarl/goproxy.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
handlerNameLblName = "handler_name"
|
|
||||||
requestDurationHistogram *prometheus.HistogramVec
|
|
||||||
)
|
|
||||||
|
|
||||||
func AddHTTPProxy(registry endpoint.HandlerRegistry) (err error) {
|
|
||||||
var logger logging.Logger
|
|
||||||
if logger, err = logging.CreateLogger(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logger = logger.With(
|
|
||||||
zap.String("protocol_handler", name),
|
|
||||||
)
|
|
||||||
|
|
||||||
if requestDurationHistogram, err = metrics.Histogram(name, "request_duration", "", nil, handlerNameLblName); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
registry.RegisterHandler(name, func() endpoint.ProtocolHandler {
|
|
||||||
return &httpProxy{
|
|
||||||
logger: logger,
|
|
||||||
proxy: goproxy.NewProxyHttpServer(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
package metrics
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
name = "metrics_exporter"
|
|
||||||
)
|
|
||||||
|
|
||||||
type metricsExporter struct {
|
|
||||||
logger logging.Logger
|
|
||||||
server *http.Server
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *metricsExporter) Start(lifecycle endpoint.Lifecycle) (err error) {
|
|
||||||
var exporterOptions metricsExporterOptions
|
|
||||||
if err = lifecycle.UnmarshalOptions(&exporterOptions); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
m.logger = m.logger.With(
|
|
||||||
zap.String("handler_name", lifecycle.Name()),
|
|
||||||
zap.String("address", lifecycle.Uplink().Addr().String()),
|
|
||||||
)
|
|
||||||
|
|
||||||
mux := http.NewServeMux()
|
|
||||||
mux.Handle(exporterOptions.Route, promhttp.Handler())
|
|
||||||
m.server = &http.Server{
|
|
||||||
Handler: mux,
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
if err := m.server.Serve(lifecycle.Uplink().Listener); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
||||||
m.logger.Error(
|
|
||||||
"Error occurred while serving metrics",
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
<-lifecycle.Context().Done()
|
|
||||||
if err := m.server.Close(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
||||||
m.logger.Error("failed to stop metrics server", zap.Error(err))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
package metrics
|
|
||||||
|
|
||||||
type metricsExporterOptions struct {
|
|
||||||
Route string
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
package metrics
|
|
||||||
|
|
||||||
import (
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
|
||||||
|
|
||||||
func AddMetricsExporter(registry endpoint.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() endpoint.ProtocolHandler {
|
|
||||||
return &metricsExporter{
|
|
||||||
logger: logger,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,161 +0,0 @@
|
||||||
package interceptor
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto/tls"
|
|
||||||
"net"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
name = "tls_interceptor"
|
|
||||||
)
|
|
||||||
|
|
||||||
type tlsInterceptor struct {
|
|
||||||
name string
|
|
||||||
options tlsOptions
|
|
||||||
logger logging.Logger
|
|
||||||
listener net.Listener
|
|
||||||
shutdownRequested bool
|
|
||||||
currentConnectionsCount *sync.WaitGroup
|
|
||||||
currentConnections map[uuid.UUID]*proxyConn
|
|
||||||
connectionsMutex *sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tlsInterceptor) Start(ctx endpoint.Lifecycle) (err error) {
|
|
||||||
t.name = ctx.Name()
|
|
||||||
|
|
||||||
if err = ctx.UnmarshalOptions(&t.options); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
t.logger = t.logger.With(
|
|
||||||
zap.String("handler_name", ctx.Name()),
|
|
||||||
zap.String("address", ctx.Uplink().Addr().String()),
|
|
||||||
zap.String("Target", t.options.Target.address()),
|
|
||||||
)
|
|
||||||
|
|
||||||
t.listener = tls.NewListener(ctx.Uplink().Listener, ctx.CertStore().TLSConfig())
|
|
||||||
|
|
||||||
go t.startListener()
|
|
||||||
go t.shutdownOnContextDone(ctx.Context())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tlsInterceptor) shutdownOnContextDone(ctx context.Context) {
|
|
||||||
<-ctx.Done()
|
|
||||||
t.logger.Info("Shutting down TLS interceptor")
|
|
||||||
t.shutdownRequested = true
|
|
||||||
done := make(chan struct{})
|
|
||||||
go func() {
|
|
||||||
t.currentConnectionsCount.Wait()
|
|
||||||
close(done)
|
|
||||||
}()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-done:
|
|
||||||
return
|
|
||||||
case <-time.After(100 * time.Millisecond):
|
|
||||||
for _, proxyConn := range t.currentConnections {
|
|
||||||
if err := proxyConn.Close(); err != nil {
|
|
||||||
t.logger.Error(
|
|
||||||
"error while closing remaining proxy connections",
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tlsInterceptor) startListener() {
|
|
||||||
for !t.shutdownRequested {
|
|
||||||
conn, err := t.listener.Accept()
|
|
||||||
if err != nil {
|
|
||||||
t.logger.Error(
|
|
||||||
"error during accept",
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
handledRequestCounter.WithLabelValues(t.name).Inc()
|
|
||||||
openConnectionsGauge.WithLabelValues(t.name).Inc()
|
|
||||||
t.currentConnectionsCount.Add(1)
|
|
||||||
go t.proxyConn(conn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tlsInterceptor) proxyConn(conn net.Conn) {
|
|
||||||
timer := prometheus.NewTimer(requestDurationHistogram.WithLabelValues(t.name))
|
|
||||||
defer func() {
|
|
||||||
_ = conn.Close()
|
|
||||||
t.currentConnectionsCount.Done()
|
|
||||||
openConnectionsGauge.WithLabelValues(t.name).Dec()
|
|
||||||
timer.ObserveDuration()
|
|
||||||
}()
|
|
||||||
|
|
||||||
rAddr, err := net.ResolveTCPAddr("tcp", t.options.Target.address())
|
|
||||||
if err != nil {
|
|
||||||
t.logger.Error(
|
|
||||||
"failed to resolve proxy Target",
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
targetConn, err := net.DialTCP("tcp", nil, rAddr)
|
|
||||||
if err != nil {
|
|
||||||
t.logger.Error(
|
|
||||||
"failed to connect to proxy Target",
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer targetConn.Close()
|
|
||||||
|
|
||||||
proxyCon := &proxyConn{
|
|
||||||
source: conn,
|
|
||||||
target: targetConn,
|
|
||||||
}
|
|
||||||
|
|
||||||
conUID := uuid.New()
|
|
||||||
t.storeConnection(conUID, proxyCon)
|
|
||||||
Pipe(conn, targetConn)
|
|
||||||
t.cleanConnection(conUID)
|
|
||||||
|
|
||||||
switch tlsConn := conn.(type) {
|
|
||||||
case *tls.Conn:
|
|
||||||
if tlsConn.Handshake() != nil {
|
|
||||||
t.logger.Error(
|
|
||||||
"error occurred during TLS handshake",
|
|
||||||
zap.Error(tlsConn.Handshake()),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
t.logger.Info(
|
|
||||||
"connection closed",
|
|
||||||
zap.String("remoteAddr", conn.RemoteAddr().String()),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tlsInterceptor) storeConnection(connUUID uuid.UUID, conn *proxyConn) {
|
|
||||||
t.connectionsMutex.Lock()
|
|
||||||
defer t.connectionsMutex.Unlock()
|
|
||||||
t.currentConnections[connUUID] = conn
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tlsInterceptor) cleanConnection(connUUID uuid.UUID) {
|
|
||||||
t.connectionsMutex.Lock()
|
|
||||||
defer t.connectionsMutex.Unlock()
|
|
||||||
if _, ok := t.currentConnections[connUUID]; ok {
|
|
||||||
delete(t.currentConnections, connUUID)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
package interceptor
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
type redirectionTarget struct {
|
|
||||||
IPAddress string
|
|
||||||
Port uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rt redirectionTarget) address() string {
|
|
||||||
return fmt.Sprintf("%s:%d", rt.IPAddress, rt.Port)
|
|
||||||
}
|
|
||||||
|
|
||||||
type tlsOptions struct {
|
|
||||||
Target redirectionTarget
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
package interceptor
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
bufferPool = sync.Pool{
|
|
||||||
New: func() interface{} {
|
|
||||||
return make([]byte, 1024)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func chanFromConn(conn net.Conn) chan []byte {
|
|
||||||
c := make(chan []byte)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
b := bufferPool.Get().([]byte)
|
|
||||||
for {
|
|
||||||
n, err := conn.Read(b)
|
|
||||||
if n > 0 {
|
|
||||||
res := bufferPool.Get().([]byte)
|
|
||||||
// Copy the buffer so it doesn't get changed while read by the recipient.
|
|
||||||
copy(res, b[:n])
|
|
||||||
c <- res[:n]
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
c <- nil
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func Pipe(conn1 net.Conn, conn2 net.Conn) {
|
|
||||||
chan1 := chanFromConn(conn1)
|
|
||||||
chan2 := chanFromConn(conn2)
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case b1 := <-chan1:
|
|
||||||
if b1 == nil {
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
conn2.Write(b1)
|
|
||||||
}
|
|
||||||
case b2 := <-chan2:
|
|
||||||
if b2 == nil {
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
conn1.Write(b2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
package interceptor
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
type proxyConn struct {
|
|
||||||
source net.Conn
|
|
||||||
target net.Conn
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *proxyConn) Close() error {
|
|
||||||
var err error
|
|
||||||
if targetErr := p.target.Close(); targetErr != nil {
|
|
||||||
err = fmt.Errorf("error while closing Target conn: %w", targetErr)
|
|
||||||
}
|
|
||||||
if sourceErr := p.source.Close(); sourceErr != nil {
|
|
||||||
err = fmt.Errorf("error while closing source conn: %w", err)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
package interceptor
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
|
||||||
"gitlab.com/inetmock/inetmock/internal/endpoint"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/metrics"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
labelNames = []string{"handler_name"}
|
|
||||||
handledRequestCounter *prometheus.CounterVec
|
|
||||||
openConnectionsGauge *prometheus.GaugeVec
|
|
||||||
requestDurationHistogram *prometheus.HistogramVec
|
|
||||||
)
|
|
||||||
|
|
||||||
func AddTLSInterceptor(registry endpoint.HandlerRegistry) (err error) {
|
|
||||||
var logger logging.Logger
|
|
||||||
if logger, err = logging.CreateLogger(); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
logger = logger.With(
|
|
||||||
zap.String("protocol_handler", name),
|
|
||||||
)
|
|
||||||
|
|
||||||
if handledRequestCounter, err = metrics.Counter(name, "handled_requests", "", labelNames...); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if openConnectionsGauge, err = metrics.Gauge(name, "open_connections", "", labelNames...); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if requestDurationHistogram, err = metrics.Histogram(name, "request_duration", "", nil, labelNames...); err != nil {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
registry.RegisterHandler(name, func() endpoint.ProtocolHandler {
|
|
||||||
return &tlsInterceptor{
|
|
||||||
logger: logger,
|
|
||||||
currentConnectionsCount: new(sync.WaitGroup),
|
|
||||||
currentConnections: make(map[uuid.UUID]*proxyConn),
|
|
||||||
connectionsMutex: &sync.Mutex{},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
package endpoint
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/mitchellh/mapstructure"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/cert"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
|
||||||
)
|
|
||||||
|
|
||||||
type endpointLifecycle struct {
|
|
||||||
endpointName string
|
|
||||||
ctx context.Context
|
|
||||||
logger logging.Logger
|
|
||||||
certStore cert.Store
|
|
||||||
emitter audit.Emitter
|
|
||||||
uplink Uplink
|
|
||||||
tls bool
|
|
||||||
opts map[string]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewEndpointLifecycleFromContext(
|
|
||||||
endpointName string,
|
|
||||||
ctx context.Context,
|
|
||||||
logger logging.Logger,
|
|
||||||
certStore cert.Store,
|
|
||||||
emitter audit.Emitter,
|
|
||||||
uplink Uplink,
|
|
||||||
opts map[string]interface{},
|
|
||||||
) Lifecycle {
|
|
||||||
return &endpointLifecycle{
|
|
||||||
endpointName: endpointName,
|
|
||||||
ctx: ctx,
|
|
||||||
logger: logger,
|
|
||||||
certStore: certStore,
|
|
||||||
emitter: emitter,
|
|
||||||
uplink: uplink,
|
|
||||||
opts: opts,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e endpointLifecycle) Name() string {
|
|
||||||
return e.endpointName
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e endpointLifecycle) Uplink() Uplink {
|
|
||||||
return e.uplink
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e endpointLifecycle) Logger() logging.Logger {
|
|
||||||
return e.logger
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e endpointLifecycle) CertStore() cert.Store {
|
|
||||||
return e.certStore
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e endpointLifecycle) Audit() audit.Emitter {
|
|
||||||
return e.emitter
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e endpointLifecycle) Context() context.Context {
|
|
||||||
return e.ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e endpointLifecycle) UnmarshalOptions(cfg interface{}) error {
|
|
||||||
return mapstructure.Decode(e.opts, cfg)
|
|
||||||
}
|
|
|
@ -1,143 +0,0 @@
|
||||||
//go:generate go-enum -f $GOFILE --lower --marshal --names
|
|
||||||
|
|
||||||
package endpoint
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/soheilhy/cmux"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrUDPMultiplexer = errors.New("UDP listeners don't support multiplexing")
|
|
||||||
ErrMultiplexingNotSupported = errors.New("not all handlers do support multiplexing")
|
|
||||||
)
|
|
||||||
|
|
||||||
/* ENUM(
|
|
||||||
UDP,
|
|
||||||
TCP
|
|
||||||
)
|
|
||||||
*/
|
|
||||||
type NetProto int
|
|
||||||
|
|
||||||
type HandlerReference string
|
|
||||||
|
|
||||||
func (h HandlerReference) ToLower() HandlerReference {
|
|
||||||
return HandlerReference(strings.ToLower(string(h)))
|
|
||||||
}
|
|
||||||
|
|
||||||
type ListenerSpec struct {
|
|
||||||
Name string
|
|
||||||
Protocol string
|
|
||||||
Address string `mapstructure:"listenAddress"`
|
|
||||||
Port uint16
|
|
||||||
Endpoints map[string]Spec
|
|
||||||
Uplink *Uplink `mapstructure:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Spec struct {
|
|
||||||
HandlerRef HandlerReference `mapstructure:"handler"`
|
|
||||||
TLS bool
|
|
||||||
Handler ProtocolHandler `mapstructure:"-"`
|
|
||||||
Options map[string]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *ListenerSpec) ConfigureMultiplexing(tlsConfig *tls.Config) (endpoints []Endpoint, muxes []cmux.CMux, err error) {
|
|
||||||
if l.Uplink == nil {
|
|
||||||
if err = l.setupUplink(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(l.Endpoints) <= 1 {
|
|
||||||
for name, s := range l.Endpoints {
|
|
||||||
endpoints = append(endpoints, Endpoint{
|
|
||||||
name: fmt.Sprintf("%s:%s", l.Name, name),
|
|
||||||
uplink: *l.Uplink,
|
|
||||||
Spec: s,
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if l.Uplink.Proto == NetProtoUDP {
|
|
||||||
err = ErrUDPMultiplexer
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var epNames []string
|
|
||||||
var multiplexEndpoints = make(map[string]MultiplexHandler)
|
|
||||||
for name, spec := range l.Endpoints {
|
|
||||||
epNames = append(epNames, name)
|
|
||||||
if ep, ok := spec.Handler.(MultiplexHandler); !ok {
|
|
||||||
err = fmt.Errorf("handler %s %w", spec.HandlerRef, ErrMultiplexingNotSupported)
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
multiplexEndpoints[name] = ep
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Strings(epNames)
|
|
||||||
|
|
||||||
plainMux := cmux.New(l.Uplink.Listener)
|
|
||||||
tlsListener := plainMux.Match(cmux.TLS())
|
|
||||||
tlsListener = tls.NewListener(tlsListener, tlsConfig)
|
|
||||||
tlsMux := cmux.New(tlsListener)
|
|
||||||
|
|
||||||
var tlsRequired = false
|
|
||||||
|
|
||||||
for _, epName := range epNames {
|
|
||||||
epSpec := l.Endpoints[epName]
|
|
||||||
var epMux = plainMux
|
|
||||||
if epSpec.TLS {
|
|
||||||
epMux = tlsMux
|
|
||||||
tlsRequired = true
|
|
||||||
}
|
|
||||||
epListener := Endpoint{
|
|
||||||
name: fmt.Sprintf("%s:%s", l.Name, epName),
|
|
||||||
uplink: Uplink{
|
|
||||||
Proto: NetProtoTCP,
|
|
||||||
Listener: epMux.Match(multiplexEndpoints[epName].Matchers()...),
|
|
||||||
},
|
|
||||||
Spec: epSpec,
|
|
||||||
}
|
|
||||||
|
|
||||||
endpoints = append(endpoints, epListener)
|
|
||||||
}
|
|
||||||
|
|
||||||
muxes = append(muxes, plainMux)
|
|
||||||
|
|
||||||
if tlsRequired {
|
|
||||||
muxes = append(muxes, tlsMux)
|
|
||||||
} else {
|
|
||||||
_ = tlsListener.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *ListenerSpec) setupUplink() (err error) {
|
|
||||||
l.Uplink = new(Uplink)
|
|
||||||
switch l.Protocol {
|
|
||||||
case "udp", "udp4", "udp6":
|
|
||||||
l.Uplink.Proto = NetProtoUDP
|
|
||||||
l.Uplink.PacketConn, err = net.ListenUDP(l.Protocol, &net.UDPAddr{
|
|
||||||
IP: net.ParseIP(l.Address),
|
|
||||||
Port: int(l.Port),
|
|
||||||
})
|
|
||||||
case "tcp", "tcp4", "tcp6":
|
|
||||||
l.Uplink.Proto = NetProtoTCP
|
|
||||||
l.Uplink.Listener, err = net.ListenTCP(l.Protocol, &net.TCPAddr{
|
|
||||||
IP: net.ParseIP(l.Address),
|
|
||||||
Port: int(l.Port),
|
|
||||||
})
|
|
||||||
default:
|
|
||||||
err = errors.New("protocol not supported")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,102 +0,0 @@
|
||||||
package endpoint
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/soheilhy/cmux"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/cert"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrStartupTimeout = errors.New("endpoint did not start in time")
|
|
||||||
)
|
|
||||||
|
|
||||||
type Orchestrator interface {
|
|
||||||
RegisterListener(spec ListenerSpec) error
|
|
||||||
StartEndpoints() (errChan chan error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewOrchestrator(appCtx context.Context, certStore cert.Store, registry HandlerRegistry, emitter audit.Emitter, logging logging.Logger) Orchestrator {
|
|
||||||
return &orchestrator{
|
|
||||||
appCtx: appCtx,
|
|
||||||
registry: registry,
|
|
||||||
logger: logging,
|
|
||||||
certStore: certStore,
|
|
||||||
emitter: emitter,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type orchestrator struct {
|
|
||||||
appCtx context.Context
|
|
||||||
registry HandlerRegistry
|
|
||||||
logger logging.Logger
|
|
||||||
certStore cert.Store
|
|
||||||
emitter audit.Emitter
|
|
||||||
|
|
||||||
endpointListeners []Endpoint
|
|
||||||
muxes []cmux.CMux
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *orchestrator) RegisterListener(spec ListenerSpec) (err error) {
|
|
||||||
for name, s := range spec.Endpoints {
|
|
||||||
if handler, registered := e.registry.HandlerForName(s.HandlerRef); registered {
|
|
||||||
s.Handler = handler
|
|
||||||
spec.Endpoints[name] = s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var endpoints []Endpoint
|
|
||||||
var muxes []cmux.CMux
|
|
||||||
if endpoints, muxes, err = spec.ConfigureMultiplexing(e.certStore.TLSConfig()); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
e.endpointListeners = append(e.endpointListeners, endpoints...)
|
|
||||||
e.muxes = append(e.muxes, muxes...)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *orchestrator) StartEndpoints() (errChan chan error) {
|
|
||||||
errChan = make(chan error)
|
|
||||||
for _, epListener := range e.endpointListeners {
|
|
||||||
endpointLogger := e.logger.With(
|
|
||||||
zap.String("epListener", epListener.name),
|
|
||||||
)
|
|
||||||
endpointLogger.Info("Starting epListener")
|
|
||||||
lifecycle := NewEndpointLifecycleFromContext(
|
|
||||||
epListener.name,
|
|
||||||
e.appCtx,
|
|
||||||
e.logger.With(zap.String("epListener", epListener.name)),
|
|
||||||
e.certStore,
|
|
||||||
e.emitter,
|
|
||||||
epListener.uplink,
|
|
||||||
epListener.Options,
|
|
||||||
)
|
|
||||||
|
|
||||||
if err := epListener.Start(lifecycle); err == nil {
|
|
||||||
endpointLogger.Info("successfully started epListener")
|
|
||||||
} else {
|
|
||||||
endpointLogger.Error("error occurred during epListener startup - will be skipped for now")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
e.logger.Info("Startup of all endpoints completed")
|
|
||||||
|
|
||||||
for _, mux := range e.muxes {
|
|
||||||
go func(mux cmux.CMux) {
|
|
||||||
mux.HandleError(func(err error) bool {
|
|
||||||
errChan <- err
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
if err := mux.Serve(); err != nil && !errors.Is(err, cmux.ErrListenerClosed) {
|
|
||||||
errChan <- err
|
|
||||||
}
|
|
||||||
}(mux)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
//go:generate mockgen -source=$GOFILE -destination=./../../internal/mock/endpoint/handler_registry.mock.go -package=endpoint_mock
|
|
||||||
package endpoint
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Registration func(registry HandlerRegistry) error
|
|
||||||
|
|
||||||
type HandlerRegistry interface {
|
|
||||||
RegisterHandler(handlerRef HandlerReference, handlerProvider func() ProtocolHandler)
|
|
||||||
AvailableHandlers() []HandlerReference
|
|
||||||
HandlerForName(handlerRef HandlerReference) (ProtocolHandler, bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewHandlerRegistry() HandlerRegistry {
|
|
||||||
return &handlerRegistry{
|
|
||||||
handlers: make(map[HandlerReference]func() ProtocolHandler),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type handlerRegistry struct {
|
|
||||||
handlers map[HandlerReference]func() ProtocolHandler
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h handlerRegistry) AvailableHandlers() (availableHandlers []HandlerReference) {
|
|
||||||
for s := range h.handlers {
|
|
||||||
availableHandlers = append(availableHandlers, s)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *handlerRegistry) HandlerForName(handlerRef HandlerReference) (instance ProtocolHandler, ok bool) {
|
|
||||||
var provider func() ProtocolHandler
|
|
||||||
if provider, ok = h.handlers[handlerRef.ToLower()]; ok {
|
|
||||||
instance = provider()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *handlerRegistry) RegisterHandler(handlerRef HandlerReference, handlerProvider func() ProtocolHandler) {
|
|
||||||
handlerRef = handlerRef.ToLower()
|
|
||||||
if _, exists := h.handlers[handlerRef]; exists {
|
|
||||||
panic(fmt.Sprintf("handler with name %s is already registered - there's something strange...in the neighborhood", handlerRef))
|
|
||||||
}
|
|
||||||
h.handlers[handlerRef] = handlerProvider
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
package endpoint
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"go.uber.org/multierr"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Uplink struct {
|
|
||||||
Proto NetProto
|
|
||||||
Listener net.Listener
|
|
||||||
PacketConn net.PacketConn
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u Uplink) Addr() net.Addr {
|
|
||||||
if u.Listener != nil {
|
|
||||||
return u.Listener.Addr()
|
|
||||||
}
|
|
||||||
if u.PacketConn != nil {
|
|
||||||
return u.PacketConn.LocalAddr()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u Uplink) Close() (err error) {
|
|
||||||
if u.Listener != nil {
|
|
||||||
err = multierr.Append(err, u.Listener.Close())
|
|
||||||
}
|
|
||||||
if u.PacketConn != nil {
|
|
||||||
err = multierr.Append(err, u.PacketConn.Close())
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
package format
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/olekukonko/tablewriter"
|
|
||||||
"gopkg.in/yaml.v3"
|
|
||||||
)
|
|
||||||
|
|
||||||
type consoleWriterFactory func(io.Writer) ConsoleWriter
|
|
||||||
|
|
||||||
var (
|
|
||||||
writers = map[string]consoleWriterFactory{
|
|
||||||
"table": func(writer io.Writer) ConsoleWriter {
|
|
||||||
tw := tablewriter.NewWriter(writer)
|
|
||||||
tw.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
|
|
||||||
tw.SetCenterSeparator("|")
|
|
||||||
|
|
||||||
return &tblWriter{
|
|
||||||
tableWriter: tw,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"json": func(writer io.Writer) ConsoleWriter {
|
|
||||||
return &jsonWriter{
|
|
||||||
encoder: json.NewEncoder(writer),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"yaml": func(writer io.Writer) ConsoleWriter {
|
|
||||||
return &yamlWriter{
|
|
||||||
encoder: yaml.NewEncoder(writer),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func Writer(format string, writer io.Writer) ConsoleWriter {
|
|
||||||
if cw, ok := writers[strings.ToLower(format)]; ok {
|
|
||||||
return cw(writer)
|
|
||||||
}
|
|
||||||
return writers["table"](writer)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ConsoleWriter interface {
|
|
||||||
Write(in interface{}) (err error)
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
package format
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
)
|
|
||||||
|
|
||||||
type jsonWriter struct {
|
|
||||||
encoder *json.Encoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (j *jsonWriter) Write(in interface{}) error {
|
|
||||||
return j.encoder.Encode(in)
|
|
||||||
}
|
|
|
@ -1,115 +0,0 @@
|
||||||
package format
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/olekukonko/tablewriter"
|
|
||||||
)
|
|
||||||
|
|
||||||
type tblWriter struct {
|
|
||||||
tableWriter *tablewriter.Table
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tblWriter) Write(in interface{}) (err error) {
|
|
||||||
v := reflect.ValueOf(in)
|
|
||||||
|
|
||||||
if v.Kind() == reflect.Ptr {
|
|
||||||
v = v.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
var vt reflect.Type
|
|
||||||
var numberOfFields int
|
|
||||||
|
|
||||||
data := make([][]string, 0)
|
|
||||||
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Interface:
|
|
||||||
return errors.New("interface{} is not supported")
|
|
||||||
case reflect.Slice, reflect.Array:
|
|
||||||
length := v.Len()
|
|
||||||
if length < 1 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
vt = v.Index(0).Type()
|
|
||||||
|
|
||||||
if vt.Kind() == reflect.Ptr {
|
|
||||||
vt = vt.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
if vt.Kind() != reflect.Struct {
|
|
||||||
return fmt.Errorf("element type of array %v is not supported", vt.Kind())
|
|
||||||
}
|
|
||||||
|
|
||||||
numberOfFields = vt.NumField()
|
|
||||||
|
|
||||||
for i := 0; i < length; i++ {
|
|
||||||
data = append(data, t.getData(v.Index(i), numberOfFields))
|
|
||||||
}
|
|
||||||
case reflect.Struct:
|
|
||||||
vt = v.Type()
|
|
||||||
numberOfFields = vt.NumField()
|
|
||||||
data = append(data, t.getData(v, numberOfFields))
|
|
||||||
}
|
|
||||||
|
|
||||||
t.tableWriter.SetHeader(headersForType(vt, numberOfFields))
|
|
||||||
t.tableWriter.AppendBulk(data)
|
|
||||||
t.tableWriter.Render()
|
|
||||||
t.tableWriter.ClearRows()
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tblWriter) getData(val reflect.Value, numberOfFields int) (data []string) {
|
|
||||||
if val.Kind() == reflect.Ptr {
|
|
||||||
val = val.Elem()
|
|
||||||
}
|
|
||||||
for i := 0; i < numberOfFields; i++ {
|
|
||||||
data = append(data, value(val.Field(i)))
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func value(val reflect.Value) string {
|
|
||||||
|
|
||||||
if val.IsZero() {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if stringer, isStringer := val.Interface().(fmt.Stringer); isStringer {
|
|
||||||
return stringer.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
switch val.Kind() {
|
|
||||||
case reflect.Ptr:
|
|
||||||
return value(val.Elem())
|
|
||||||
case reflect.Struct, reflect.Interface:
|
|
||||||
return "<not supported>"
|
|
||||||
case reflect.Bool:
|
|
||||||
return strconv.FormatBool(val.Bool())
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
return strconv.FormatInt(val.Int(), 10)
|
|
||||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
||||||
return strconv.FormatUint(val.Uint(), 10)
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
return strconv.FormatFloat(val.Float(), 'f', 6, 64)
|
|
||||||
default:
|
|
||||||
return val.String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func headersForType(t reflect.Type, numberOfFields int) (headers []string) {
|
|
||||||
for i := 0; i < numberOfFields; i++ {
|
|
||||||
field := t.Field(i)
|
|
||||||
if tableTag, ok := field.Tag.Lookup("table"); ok {
|
|
||||||
headers = append(headers, tableTag)
|
|
||||||
} else {
|
|
||||||
headers = append(headers, field.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,134 +0,0 @@
|
||||||
package format
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_tblWriter_Write(t *testing.T) {
|
|
||||||
type s1 struct {
|
|
||||||
Name string
|
|
||||||
Age int
|
|
||||||
}
|
|
||||||
|
|
||||||
type s2 struct {
|
|
||||||
Name string `table:"Full name"`
|
|
||||||
Age int `table:"Age in years"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type args struct {
|
|
||||||
in interface{}
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
wantErr bool
|
|
||||||
wantResult string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "Test write table without errors",
|
|
||||||
args: args{
|
|
||||||
in: s1{
|
|
||||||
Name: "Ted Tester",
|
|
||||||
Age: 28,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
wantResult: `
|
|
||||||
| NAME | AGE |
|
|
||||||
|------------|-----|
|
|
||||||
| Ted Tester | 28 |
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Test write table without errors with pointer value",
|
|
||||||
args: args{
|
|
||||||
in: &s1{
|
|
||||||
Name: "Ted Tester",
|
|
||||||
Age: 28,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
wantResult: `
|
|
||||||
| NAME | AGE |
|
|
||||||
|------------|-----|
|
|
||||||
| Ted Tester | 28 |
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Test write table without errors with multiple rows",
|
|
||||||
args: args{
|
|
||||||
in: []s1{
|
|
||||||
{
|
|
||||||
Name: "Ted Tester",
|
|
||||||
Age: 28,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "Heinz",
|
|
||||||
Age: 33,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
wantResult: `
|
|
||||||
| NAME | AGE |
|
|
||||||
|------------|-----|
|
|
||||||
| Ted Tester | 28 |
|
|
||||||
| Heinz | 33 |
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Test write table without errors with multiple pointer rows",
|
|
||||||
args: args{
|
|
||||||
in: []*s1{
|
|
||||||
{
|
|
||||||
Name: "Ted Tester",
|
|
||||||
Age: 28,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "Heinz",
|
|
||||||
Age: 33,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
wantResult: `
|
|
||||||
| NAME | AGE |
|
|
||||||
|------------|-----|
|
|
||||||
| Ted Tester | 28 |
|
|
||||||
| Heinz | 33 |
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Test write table without errors and with custom headers",
|
|
||||||
args: args{
|
|
||||||
in: s2{
|
|
||||||
Name: "Ted Tester",
|
|
||||||
Age: 28,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
wantResult: `
|
|
||||||
| FULL NAME | AGE IN YEARS |
|
|
||||||
|------------|--------------|
|
|
||||||
| Ted Tester | 28 |
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
bldr := &strings.Builder{}
|
|
||||||
|
|
||||||
// hack to be able to format expected strings pretty
|
|
||||||
bldr.WriteRune('\n')
|
|
||||||
tw := Writer("table", bldr)
|
|
||||||
if err := tw.Write(tt.args.in); (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("Write() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if bldr.String() != tt.wantResult {
|
|
||||||
t.Errorf("Write() got = %s, want %s", bldr.String(), tt.wantResult)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
package format
|
|
||||||
|
|
||||||
import (
|
|
||||||
"gopkg.in/yaml.v3"
|
|
||||||
)
|
|
||||||
|
|
||||||
type yamlWriter struct {
|
|
||||||
encoder *yaml.Encoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (y *yamlWriter) Write(in interface{}) (err error) {
|
|
||||||
return y.encoder.Encode(in)
|
|
||||||
}
|
|
|
@ -1,74 +0,0 @@
|
||||||
package rpc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/audit"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/audit/sink"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
|
||||||
|
|
||||||
type auditServer struct {
|
|
||||||
UnimplementedAuditServer
|
|
||||||
logger logging.Logger
|
|
||||||
eventStream audit.EventStream
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *auditServer) ListSinks(context.Context, *ListSinksRequest) (*ListSinksResponse, error) {
|
|
||||||
return &ListSinksResponse{
|
|
||||||
Sinks: a.eventStream.Sinks(),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *auditServer) WatchEvents(req *WatchEventsRequest, srv Audit_WatchEventsServer) (err error) {
|
|
||||||
a.logger.Info("watcher attached", zap.String("name", req.WatcherName))
|
|
||||||
err = a.eventStream.RegisterSink(srv.Context(), sink.NewGenericSink(req.WatcherName, func(ev audit.Event) {
|
|
||||||
if err = srv.Send(ev.ProtoMessage()); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
<-srv.Context().Done()
|
|
||||||
a.logger.Info("Watcher detached", zap.String("name", req.WatcherName))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *auditServer) RegisterFileSink(_ context.Context, req *RegisterFileSinkRequest) (resp *RegisterFileSinkResponse, err error) {
|
|
||||||
var writer io.WriteCloser
|
|
||||||
var flags int
|
|
||||||
|
|
||||||
switch req.OpenMode {
|
|
||||||
case FileOpenMode_APPEND:
|
|
||||||
flags = os.O_CREATE | os.O_WRONLY | os.O_APPEND
|
|
||||||
default:
|
|
||||||
flags = os.O_CREATE | os.O_WRONLY | os.O_TRUNC
|
|
||||||
}
|
|
||||||
|
|
||||||
var permissions = os.FileMode(req.Permissions)
|
|
||||||
if permissions == 0 {
|
|
||||||
permissions = 644
|
|
||||||
}
|
|
||||||
|
|
||||||
if writer, err = os.OpenFile(req.TargetPath, flags, permissions); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = a.eventStream.RegisterSink(context.Background(), sink.NewWriterSink(req.TargetPath, audit.NewEventWriter(writer))); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resp = &RegisterFileSinkResponse{}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *auditServer) RemoveFileSink(_ context.Context, req *RemoveFileSinkRequest) (*RemoveFileSinkResponse, error) {
|
|
||||||
gotRemoved := a.eventStream.RemoveSink(req.TargetPath)
|
|
||||||
return &RemoveFileSinkResponse{
|
|
||||||
SinkGotRemoved: gotRemoved,
|
|
||||||
}, nil
|
|
||||||
}
|
|
|
@ -1,105 +0,0 @@
|
||||||
package rpc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
app2 "gitlab.com/inetmock/inetmock/internal/app"
|
|
||||||
"gitlab.com/inetmock/inetmock/pkg/logging"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
"google.golang.org/grpc/reflection"
|
|
||||||
)
|
|
||||||
|
|
||||||
type INetMockAPI interface {
|
|
||||||
StartServer() error
|
|
||||||
StopServer()
|
|
||||||
}
|
|
||||||
|
|
||||||
type inetmockAPI struct {
|
|
||||||
app app2.App
|
|
||||||
url *url.URL
|
|
||||||
server *grpc.Server
|
|
||||||
logger logging.Logger
|
|
||||||
serverRunning bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewINetMockAPI(
|
|
||||||
app app2.App,
|
|
||||||
) INetMockAPI {
|
|
||||||
return &inetmockAPI{
|
|
||||||
app: app,
|
|
||||||
url: app.Config().APIConfig().ListenURL(),
|
|
||||||
logger: app.Logger().Named("api"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *inetmockAPI) StartServer() (err error) {
|
|
||||||
var lis net.Listener
|
|
||||||
if lis, err = createListenerFromURL(i.url); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
i.server = grpc.NewServer()
|
|
||||||
|
|
||||||
RegisterHealthServer(i.server, &healthServer{
|
|
||||||
app: i.app,
|
|
||||||
})
|
|
||||||
|
|
||||||
RegisterAuditServer(i.server, &auditServer{
|
|
||||||
logger: i.app.Logger(),
|
|
||||||
eventStream: i.app.EventStream(),
|
|
||||||
})
|
|
||||||
|
|
||||||
reflection.Register(i.server)
|
|
||||||
|
|
||||||
go i.startServerAsync(lis)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *inetmockAPI) StopServer() {
|
|
||||||
if !i.serverRunning {
|
|
||||||
i.logger.Info(
|
|
||||||
"Skipping API server shutdown because server is not running",
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
gracefulStopChan := make(chan struct{})
|
|
||||||
go func() {
|
|
||||||
i.server.GracefulStop()
|
|
||||||
close(gracefulStopChan)
|
|
||||||
}()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-gracefulStopChan:
|
|
||||||
case <-time.After(5 * time.Second):
|
|
||||||
i.server.Stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *inetmockAPI) startServerAsync(listener net.Listener) {
|
|
||||||
i.serverRunning = true
|
|
||||||
if err := i.server.Serve(listener); err != nil {
|
|
||||||
i.serverRunning = false
|
|
||||||
i.logger.Error(
|
|
||||||
"failed to start INetMock API",
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createListenerFromURL(url *url.URL) (lis net.Listener, err error) {
|
|
||||||
switch url.Scheme {
|
|
||||||
case "unix":
|
|
||||||
if _, err = os.Stat(url.Path); err == nil {
|
|
||||||
if err = os.Remove(url.Path); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lis, err = net.Listen(url.Scheme, url.Path)
|
|
||||||
default:
|
|
||||||
lis, err = net.Listen(url.Scheme, url.Host)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue