diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 8da094d..0000000 --- a/.dockerignore +++ /dev/null @@ -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 \ No newline at end of file diff --git a/.editorconfig b/.editorconfig index c3daa0a..698c63d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,23 +1,10 @@ root = true [*] -charset = utf-8 end_of_line = lf -indent_size = 4 +insert_final_newline = true +charset = utf-8 + +[*.proto] indent_style = space -insert_final_newline = false -max_line_length = 120 -tab_width = 4 - -[{*.bash, *.sh, *.zsh}] -indent_size = 2 -tab_width = 2 - -[{*.go, *.go2}] -indent_style = tab - -[{*.har, *.jsb2, *.jsb3, *.json, .babelrc, .eslintrc, .stylelintrc, bowerrc, jest.config}] -indent_size = 2 - -[{*.yaml, *.yml}] -indent_size = 2 +indent_size = 4 diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 8defa90..0000000 --- a/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -assets/fakeFiles/* filter=lfs diff=lfs merge=lfs -text diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml deleted file mode 100644 index 82cf8db..0000000 --- a/.github/workflows/docker-image.yml +++ /dev/null @@ -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 \ No newline at end of file diff --git a/.github/workflows/go-build.yml b/.github/workflows/go-build.yml deleted file mode 100644 index 7a862c4..0000000 --- a/.github/workflows/go-build.yml +++ /dev/null @@ -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 }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 9d05b35..1c0b8d4 100644 --- a/.gitignore +++ b/.gitignore @@ -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/ -dist/ -out/ -.task/ -public/ \ No newline at end of file +.idea/ \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 2c2d3fa..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -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 diff --git a/.gitlab/.gitlab-webide.yml b/.gitlab/.gitlab-webide.yml deleted file mode 100644 index d8bf3f6..0000000 --- a/.gitlab/.gitlab-webide.yml +++ /dev/null @@ -1,3 +0,0 @@ -terminal: - image: registry.gitlab.com/inetmock/ci-image - script: sleep 60 diff --git a/.golangci.yml b/.golangci.yml deleted file mode 100644 index 26cb98b..0000000 --- a/.golangci.yml +++ /dev/null @@ -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" diff --git a/.goreleaser.yml b/.goreleaser.yml deleted file mode 100644 index be5c7a6..0000000 --- a/.goreleaser.yml +++ /dev/null @@ -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/ diff --git a/LICENSE b/LICENSE deleted file mode 100644 index d13e69b..0000000 --- a/LICENSE +++ /dev/null @@ -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. diff --git a/README.md b/README.md index 6051412..19fee22 100644 --- a/README.md +++ b/README.md @@ -1,30 +1 @@ -# INetMock - - -[![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. \ No newline at end of file +# INetMock protobuf API \ No newline at end of file diff --git a/Taskfile.yml b/Taskfile.yml deleted file mode 100644 index c2d70ed..0000000 --- a/Taskfile.yml +++ /dev/null @@ -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 \ No newline at end of file diff --git a/assets/demoCA/ca.key b/assets/demoCA/ca.key deleted file mode 100644 index c77e1ea..0000000 --- a/assets/demoCA/ca.key +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgEk8KFe6Vl3xefH82 -Na91uPQkBz4D/s1xE+59+kaRM1+hRANCAASnjLNiOc4UNsRBIQN+FOv8CEd6ftDH -Egg2dWUgGCFsgE2VaEG+jOwamBzTjCWbr0azIc+Brupd0CAChq8hVeaM ------END PRIVATE KEY----- diff --git a/assets/demoCA/ca.pem b/assets/demoCA/ca.pem deleted file mode 100644 index a815780..0000000 --- a/assets/demoCA/ca.pem +++ /dev/null @@ -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----- diff --git a/assets/fakeFiles/default.gif b/assets/fakeFiles/default.gif deleted file mode 100644 index 299f6f0..0000000 --- a/assets/fakeFiles/default.gif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4951b3d218405caac8b3698658ad8680df908884d0b9d5a17654b1e0c240844b -size 25053 diff --git a/assets/fakeFiles/default.html b/assets/fakeFiles/default.html deleted file mode 100644 index 1a64e75..0000000 --- a/assets/fakeFiles/default.html +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c2a3f8995831dd1e79cb753619a55752692168f6cf846b07405f2070492f481c -size 235 diff --git a/assets/fakeFiles/default.ico b/assets/fakeFiles/default.ico deleted file mode 100644 index 272443c..0000000 --- a/assets/fakeFiles/default.ico +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cb3f33cb0c7bae881d13be647cb928aa7fec9e9fcd6ec758751374bf7436d41a -size 70614 diff --git a/assets/fakeFiles/default.jpg b/assets/fakeFiles/default.jpg deleted file mode 100644 index 4e77e32..0000000 --- a/assets/fakeFiles/default.jpg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:511560ec3481a9089bb5fe0854b41dd9c0413fb8b7694805f50bff4b8fedbb13 -size 36275 diff --git a/assets/fakeFiles/default.png b/assets/fakeFiles/default.png deleted file mode 100644 index 25baa0a..0000000 --- a/assets/fakeFiles/default.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b5ab03ea66bd52ddae299f172a3aef5c8b0b8d84a1c777c22e62358cbb634741 -size 200147 diff --git a/assets/fakeFiles/default.txt b/assets/fakeFiles/default.txt deleted file mode 100644 index 674c898..0000000 --- a/assets/fakeFiles/default.txt +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:136c41a07ec1ee2e3437a636bf48ced65e46e8bbc9c759fee820b5c3bf5b772e -size 92 diff --git a/build/docker/inetmock.dockerfile b/build/docker/inetmock.dockerfile deleted file mode 100644 index 0fc09fe..0000000 --- a/build/docker/inetmock.dockerfile +++ /dev/null @@ -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"] diff --git a/cmd/imctl/audit_sinks.go b/cmd/imctl/audit_sinks.go deleted file mode 100644 index 75aefb5..0000000 --- a/cmd/imctl/audit_sinks.go +++ /dev/null @@ -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 -} diff --git a/cmd/imctl/audit_watch.go b/cmd/imctl/audit_watch.go deleted file mode 100644 index 42f4345..0000000 --- a/cmd/imctl/audit_watch.go +++ /dev/null @@ -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 -} diff --git a/cmd/imctl/health.go b/cmd/imctl/health.go deleted file mode 100644 index e19b1c8..0000000 --- a/cmd/imctl/health.go +++ /dev/null @@ -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) - } -} diff --git a/cmd/imctl/main.go b/cmd/imctl/main.go deleted file mode 100644 index 70ab252..0000000 --- a/cmd/imctl/main.go +++ /dev/null @@ -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 -} diff --git a/cmd/inetmock/ca.go b/cmd/inetmock/ca.go deleted file mode 100644 index 9b7062c..0000000 --- a/cmd/inetmock/ca.go +++ /dev/null @@ -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") -} diff --git a/cmd/inetmock/main.go b/cmd/inetmock/main.go deleted file mode 100644 index cab14d1..0000000 --- a/cmd/inetmock/main.go +++ /dev/null @@ -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() -} diff --git a/cmd/inetmock/serve.go b/cmd/inetmock/serve.go deleted file mode 100644 index 71485b5..0000000 --- a/cmd/inetmock/serve.go +++ /dev/null @@ -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 -} diff --git a/config-container.yaml b/config-container.yaml deleted file mode 100644 index 19ce48a..0000000 --- a/config-container.yaml +++ /dev/null @@ -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 \ No newline at end of file diff --git a/config.yaml b/config.yaml deleted file mode 100644 index f9e88d8..0000000 --- a/config.yaml +++ /dev/null @@ -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 \ No newline at end of file diff --git a/deploy/inetmock.default b/deploy/inetmock.default deleted file mode 100644 index 2fafb68..0000000 --- a/deploy/inetmock.default +++ /dev/null @@ -1 +0,0 @@ -OPTIONS="--config=/etc/inetmock/config.yaml" \ No newline at end of file diff --git a/deploy/inetmock.service b/deploy/inetmock.service deleted file mode 100644 index 4a964ec..0000000 --- a/deploy/inetmock.service +++ /dev/null @@ -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 \ No newline at end of file diff --git a/docs/.gitignore b/docs/.gitignore deleted file mode 100644 index 7585238..0000000 --- a/docs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -book diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index d9ad9be..0000000 --- a/docs/Makefile +++ /dev/null @@ -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 \ No newline at end of file diff --git a/docs/book.toml b/docs/book.toml deleted file mode 100644 index e1367ec..0000000 --- a/docs/book.toml +++ /dev/null @@ -1,10 +0,0 @@ -[book] -authors = ["Peter Kurfer"] -language = "en" -multilingual = false -src = "src" -title = "INetMock docs" - -[build] -build-dir = "book" -create-missing = true diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md deleted file mode 100644 index 9e85f47..0000000 --- a/docs/src/SUMMARY.md +++ /dev/null @@ -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) \ No newline at end of file diff --git a/docs/src/api.md b/docs/src/api.md deleted file mode 100644 index 5932792..0000000 --- a/docs/src/api.md +++ /dev/null @@ -1 +0,0 @@ -# API diff --git a/docs/src/build_install.md b/docs/src/build_install.md deleted file mode 100644 index bf3698b..0000000 --- a/docs/src/build_install.md +++ /dev/null @@ -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) \ No newline at end of file diff --git a/docs/src/config.md b/docs/src/config.md deleted file mode 100644 index bd91014..0000000 --- a/docs/src/config.md +++ /dev/null @@ -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. \ No newline at end of file diff --git a/docs/src/config/dns_mock.md b/docs/src/config/dns_mock.md deleted file mode 100644 index 7026c3a..0000000 --- a/docs/src/config/dns_mock.md +++ /dev/null @@ -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 -``` \ No newline at end of file diff --git a/docs/src/config/http_mock.md b/docs/src/config/http_mock.md deleted file mode 100644 index f70841f..0000000 --- a/docs/src/config/http_mock.md +++ /dev/null @@ -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 -``` \ No newline at end of file diff --git a/docs/src/config/tls_interceptor.md b/docs/src/config/tls_interceptor.md deleted file mode 100644 index bd836bb..0000000 --- a/docs/src/config/tls_interceptor.md +++ /dev/null @@ -1 +0,0 @@ -# `tls_interceptor` \ No newline at end of file diff --git a/docs/src/config/yaml-config.md b/docs/src/config/yaml-config.md deleted file mode 100644 index 3f0af33..0000000 --- a/docs/src/config/yaml-config.md +++ /dev/null @@ -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 -``` \ No newline at end of file diff --git a/docs/src/deploy.md b/docs/src/deploy.md deleted file mode 100644 index d36c65e..0000000 --- a/docs/src/deploy.md +++ /dev/null @@ -1 +0,0 @@ -# Deployment diff --git a/docs/src/dev/custom_handler.md b/docs/src/dev/custom_handler.md deleted file mode 100644 index d9e4fc5..0000000 --- a/docs/src/dev/custom_handler.md +++ /dev/null @@ -1 +0,0 @@ -# Custom handler diff --git a/docs/src/dev/logging.md b/docs/src/dev/logging.md deleted file mode 100644 index b921bbe..0000000 --- a/docs/src/dev/logging.md +++ /dev/null @@ -1 +0,0 @@ -# Logging diff --git a/docs/src/dev/plugin_command.md b/docs/src/dev/plugin_command.md deleted file mode 100644 index 45aa921..0000000 --- a/docs/src/dev/plugin_command.md +++ /dev/null @@ -1 +0,0 @@ -# Plugin command diff --git a/go.mod b/go.mod deleted file mode 100644 index 5a22ac5..0000000 --- a/go.mod +++ /dev/null @@ -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 -) diff --git a/go.sum b/go.sum deleted file mode 100644 index bbd9a6e..0000000 --- a/go.sum +++ /dev/null @@ -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= diff --git a/internal/app/app.go b/internal/app/app.go deleted file mode 100644 index 6a16601..0000000 --- a/internal/app/app.go +++ /dev/null @@ -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 -} diff --git a/internal/app/config.go b/internal/app/config.go deleted file mode 100644 index e0e828d..0000000 --- a/internal/app/config.go +++ /dev/null @@ -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 -} diff --git a/internal/app/config_test.go b/internal/app/config_test.go deleted file mode 100644 index ca491c2..0000000 --- a/internal/app/config_test.go +++ /dev/null @@ -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()) - } - }) - } -} diff --git a/internal/app/constants.go b/internal/app/constants.go deleted file mode 100644 index 6e55d7d..0000000 --- a/internal/app/constants.go +++ /dev/null @@ -1,6 +0,0 @@ -package app - -const ( - EndpointsKey = "endpoints" - OptionsKey = "options" -) diff --git a/internal/app/defaults.go b/internal/app/defaults.go deleted file mode 100644 index 0346ece..0000000 --- a/internal/app/defaults.go +++ /dev/null @@ -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 -} diff --git a/internal/app/rpc.go b/internal/app/rpc.go deleted file mode 100644 index 538efe0..0000000 --- a/internal/app/rpc.go +++ /dev/null @@ -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 -} diff --git a/internal/app/rpc_test.go b/internal/app/rpc_test.go deleted file mode 100644 index a5fea12..0000000 --- a/internal/app/rpc_test.go +++ /dev/null @@ -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) - } - }) - } -} diff --git a/internal/endpoint/api.go b/internal/endpoint/api.go deleted file mode 100644 index 895c302..0000000 --- a/internal/endpoint/api.go +++ /dev/null @@ -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 -} diff --git a/internal/endpoint/constants.go b/internal/endpoint/constants.go deleted file mode 100644 index b7379eb..0000000 --- a/internal/endpoint/constants.go +++ /dev/null @@ -1,7 +0,0 @@ -package endpoint - -import "time" - -const ( - shutdownTimeout = 5 * time.Second -) diff --git a/internal/endpoint/endpoint.go b/internal/endpoint/endpoint.go deleted file mode 100644 index 73405ea..0000000 --- a/internal/endpoint/endpoint.go +++ /dev/null @@ -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 -} diff --git a/internal/endpoint/handler/dns/mock/fallback.go b/internal/endpoint/handler/dns/mock/fallback.go deleted file mode 100644 index 3aa790a..0000000 --- a/internal/endpoint/handler/dns/mock/fallback.go +++ /dev/null @@ -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 -} diff --git a/internal/endpoint/handler/dns/mock/fallback_test.go b/internal/endpoint/handler/dns/mock/fallback_test.go deleted file mode 100644 index 33622be..0000000 --- a/internal/endpoint/handler/dns/mock/fallback_test.go +++ /dev/null @@ -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) - } - }) - } -} diff --git a/internal/endpoint/handler/dns/mock/handler.go b/internal/endpoint/handler/dns/mock/handler.go deleted file mode 100644 index 0958ae3..0000000 --- a/internal/endpoint/handler/dns/mock/handler.go +++ /dev/null @@ -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)) - } -} diff --git a/internal/endpoint/handler/dns/mock/handler_bench_test.go b/internal/endpoint/handler/dns/mock/handler_bench_test.go deleted file mode 100644 index bb10ab0..0000000 --- a/internal/endpoint/handler/dns/mock/handler_bench_test.go +++ /dev/null @@ -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) - }, - } -} diff --git a/internal/endpoint/handler/dns/mock/protocol_options.go b/internal/endpoint/handler/dns/mock/protocol_options.go deleted file mode 100644 index 754602a..0000000 --- a/internal/endpoint/handler/dns/mock/protocol_options.go +++ /dev/null @@ -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 -} diff --git a/internal/endpoint/handler/dns/mock/regex_handler.go b/internal/endpoint/handler/dns/mock/regex_handler.go deleted file mode 100644 index 64905b0..0000000 --- a/internal/endpoint/handler/dns/mock/regex_handler.go +++ /dev/null @@ -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 - } -} diff --git a/internal/endpoint/handler/dns/mock/register.go b/internal/endpoint/handler/dns/mock/register.go deleted file mode 100644 index a7f3e22..0000000 --- a/internal/endpoint/handler/dns/mock/register.go +++ /dev/null @@ -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 -} diff --git a/internal/endpoint/handler/http/audit.go b/internal/endpoint/handler/http/audit.go deleted file mode 100644 index deb8bcf..0000000 --- a/internal/endpoint/handler/http/audit.go +++ /dev/null @@ -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 -} diff --git a/internal/endpoint/handler/http/conn_context.go b/internal/endpoint/handler/http/conn_context.go deleted file mode 100644 index 85c29be..0000000 --- a/internal/endpoint/handler/http/conn_context.go +++ /dev/null @@ -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) -} diff --git a/internal/endpoint/handler/http/mock/handler.go b/internal/endpoint/handler/http/mock/handler.go deleted file mode 100644 index 5a1467b..0000000 --- a/internal/endpoint/handler/http/mock/handler.go +++ /dev/null @@ -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), - ) - } -} diff --git a/internal/endpoint/handler/http/mock/handler_bench_test.go b/internal/endpoint/handler/http/mock/handler_bench_test.go deleted file mode 100644 index 9fc285c..0000000 --- a/internal/endpoint/handler/http/mock/handler_bench_test.go +++ /dev/null @@ -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 -} diff --git a/internal/endpoint/handler/http/mock/protocol_options.go b/internal/endpoint/handler/http/mock/protocol_options.go deleted file mode 100644 index 0d688a6..0000000 --- a/internal/endpoint/handler/http/mock/protocol_options.go +++ /dev/null @@ -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 -} diff --git a/internal/endpoint/handler/http/mock/protocol_options_test.go b/internal/endpoint/handler/http/mock/protocol_options_test.go deleted file mode 100644 index 31510fc..0000000 --- a/internal/endpoint/handler/http/mock/protocol_options_test.go +++ /dev/null @@ -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) - } - }) - } -} diff --git a/internal/endpoint/handler/http/mock/regex_router.go b/internal/endpoint/handler/http/mock/regex_router.go deleted file mode 100644 index 28f9d8b..0000000 --- a/internal/endpoint/handler/http/mock/regex_router.go +++ /dev/null @@ -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) -} diff --git a/internal/endpoint/handler/http/mock/register.go b/internal/endpoint/handler/http/mock/register.go deleted file mode 100644 index ff00eb0..0000000 --- a/internal/endpoint/handler/http/mock/register.go +++ /dev/null @@ -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 -} diff --git a/internal/endpoint/handler/http/proxy/handler.go b/internal/endpoint/handler/http/proxy/handler.go deleted file mode 100644 index 360ee0f..0000000 --- a/internal/endpoint/handler/http/proxy/handler.go +++ /dev/null @@ -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 -} diff --git a/internal/endpoint/handler/http/proxy/handler_bench_test.go b/internal/endpoint/handler/http/proxy/handler_bench_test.go deleted file mode 100644 index d256f13..0000000 --- a/internal/endpoint/handler/http/proxy/handler_bench_test.go +++ /dev/null @@ -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 -} diff --git a/internal/endpoint/handler/http/proxy/protocol_options.go b/internal/endpoint/handler/http/proxy/protocol_options.go deleted file mode 100644 index 3dbf34c..0000000 --- a/internal/endpoint/handler/http/proxy/protocol_options.go +++ /dev/null @@ -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 -} diff --git a/internal/endpoint/handler/http/proxy/proxy_handler.go b/internal/endpoint/handler/http/proxy/proxy_handler.go deleted file mode 100644 index 3b94ae0..0000000 --- a/internal/endpoint/handler/http/proxy/proxy_handler.go +++ /dev/null @@ -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 -} diff --git a/internal/endpoint/handler/http/proxy/register.go b/internal/endpoint/handler/http/proxy/register.go deleted file mode 100644 index 2814e19..0000000 --- a/internal/endpoint/handler/http/proxy/register.go +++ /dev/null @@ -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 -} diff --git a/internal/endpoint/handler/metrics/handler.go b/internal/endpoint/handler/metrics/handler.go deleted file mode 100644 index f0120de..0000000 --- a/internal/endpoint/handler/metrics/handler.go +++ /dev/null @@ -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 -} diff --git a/internal/endpoint/handler/metrics/protocol_options.go b/internal/endpoint/handler/metrics/protocol_options.go deleted file mode 100644 index b7b1aed..0000000 --- a/internal/endpoint/handler/metrics/protocol_options.go +++ /dev/null @@ -1,5 +0,0 @@ -package metrics - -type metricsExporterOptions struct { - Route string -} diff --git a/internal/endpoint/handler/metrics/register.go b/internal/endpoint/handler/metrics/register.go deleted file mode 100644 index 6991111..0000000 --- a/internal/endpoint/handler/metrics/register.go +++ /dev/null @@ -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 -} diff --git a/internal/endpoint/handler/tls/interceptor/handler.go b/internal/endpoint/handler/tls/interceptor/handler.go deleted file mode 100644 index 268ba30..0000000 --- a/internal/endpoint/handler/tls/interceptor/handler.go +++ /dev/null @@ -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) - } -} diff --git a/internal/endpoint/handler/tls/interceptor/protocol_options.go b/internal/endpoint/handler/tls/interceptor/protocol_options.go deleted file mode 100644 index c0de849..0000000 --- a/internal/endpoint/handler/tls/interceptor/protocol_options.go +++ /dev/null @@ -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 -} diff --git a/internal/endpoint/handler/tls/interceptor/proxy.go b/internal/endpoint/handler/tls/interceptor/proxy.go deleted file mode 100644 index be5af4c..0000000 --- a/internal/endpoint/handler/tls/interceptor/proxy.go +++ /dev/null @@ -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) - } - } - } -} diff --git a/internal/endpoint/handler/tls/interceptor/proxy_conn.go b/internal/endpoint/handler/tls/interceptor/proxy_conn.go deleted file mode 100644 index 396d80f..0000000 --- a/internal/endpoint/handler/tls/interceptor/proxy_conn.go +++ /dev/null @@ -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 -} diff --git a/internal/endpoint/handler/tls/interceptor/register.go b/internal/endpoint/handler/tls/interceptor/register.go deleted file mode 100644 index 9d61045..0000000 --- a/internal/endpoint/handler/tls/interceptor/register.go +++ /dev/null @@ -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 -} diff --git a/internal/endpoint/lifecycle.go b/internal/endpoint/lifecycle.go deleted file mode 100644 index af1fba3..0000000 --- a/internal/endpoint/lifecycle.go +++ /dev/null @@ -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) -} diff --git a/internal/endpoint/listener.go b/internal/endpoint/listener.go deleted file mode 100644 index 115d5a7..0000000 --- a/internal/endpoint/listener.go +++ /dev/null @@ -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 -} diff --git a/internal/endpoint/orchestrator.go b/internal/endpoint/orchestrator.go deleted file mode 100644 index da2473d..0000000 --- a/internal/endpoint/orchestrator.go +++ /dev/null @@ -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 -} diff --git a/internal/endpoint/registration.go b/internal/endpoint/registration.go deleted file mode 100644 index d2cf3b5..0000000 --- a/internal/endpoint/registration.go +++ /dev/null @@ -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 -} diff --git a/internal/endpoint/uplink.go b/internal/endpoint/uplink.go deleted file mode 100644 index 177ec79..0000000 --- a/internal/endpoint/uplink.go +++ /dev/null @@ -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 -} diff --git a/internal/format/console_writer.go b/internal/format/console_writer.go deleted file mode 100644 index 6551534..0000000 --- a/internal/format/console_writer.go +++ /dev/null @@ -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) -} diff --git a/internal/format/json_writer.go b/internal/format/json_writer.go deleted file mode 100644 index fe0922a..0000000 --- a/internal/format/json_writer.go +++ /dev/null @@ -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) -} diff --git a/internal/format/table_writer.go b/internal/format/table_writer.go deleted file mode 100644 index da75f16..0000000 --- a/internal/format/table_writer.go +++ /dev/null @@ -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 "" - 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 -} diff --git a/internal/format/table_writer_test.go b/internal/format/table_writer_test.go deleted file mode 100644 index 2c35d40..0000000 --- a/internal/format/table_writer_test.go +++ /dev/null @@ -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) - } - }) - } -} diff --git a/internal/format/yaml_writer.go b/internal/format/yaml_writer.go deleted file mode 100644 index 0814fda..0000000 --- a/internal/format/yaml_writer.go +++ /dev/null @@ -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) -} diff --git a/internal/rpc/audit_server.go b/internal/rpc/audit_server.go deleted file mode 100644 index a593179..0000000 --- a/internal/rpc/audit_server.go +++ /dev/null @@ -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 -} diff --git a/internal/rpc/grpc_api.go b/internal/rpc/grpc_api.go deleted file mode 100644 index a666dc5..0000000 --- a/internal/rpc/grpc_api.go +++ /dev/null @@ -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 -} diff --git a/internal/rpc/health_server.go b/internal/rpc/health_server.go deleted file mode 100644 index 17b0af7..0000000 --- a/internal/rpc/health_server.go +++ /dev/null @@ -1,30 +0,0 @@ -package rpc - -import ( - "context" - - "gitlab.com/inetmock/inetmock/internal/app" -) - -type healthServer struct { - UnimplementedHealthServer - app app.App -} - -func (h healthServer) GetHealth(_ context.Context, _ *HealthRequest) (resp *HealthResponse, err error) { - checker := h.app.Checker() - result := checker.IsHealthy() - - resp = &HealthResponse{ - OverallHealthState: HealthState(result.Status), - ComponentsHealth: map[string]*ComponentHealth{}} - - for component, status := range result.Components { - resp.ComponentsHealth[component] = &ComponentHealth{ - State: HealthState(status.Status), - Message: status.Message, - } - } - - return -} diff --git a/internal/test/integration/testcontainer.go b/internal/test/integration/testcontainer.go deleted file mode 100644 index 77ff8b2..0000000 --- a/internal/test/integration/testcontainer.go +++ /dev/null @@ -1,64 +0,0 @@ -package integration - -import ( - "context" - "path/filepath" - "runtime" - "strings" - "testing" - "time" - - "github.com/docker/go-connections/nat" - "github.com/testcontainers/testcontainers-go" - "github.com/testcontainers/testcontainers-go/wait" -) - -func SetupINetMockContainer(ctx context.Context, tb testing.TB, exposedPorts ...string) (imContainer testcontainers.Container, err error) { - _, fileName, _, _ := runtime.Caller(0) - - var repoRoot string - if repoRoot, err = filepath.Abs(filepath.Join(filepath.Dir(fileName), "..", "..", "..")); err != nil { - return - } - - var waitStrategies []wait.Strategy - - var tcpPortPresent = false - for _, port := range exposedPorts { - if strings.Contains(port, "tcp") { - tcpPortPresent = true - waitStrategies = append(waitStrategies, wait.ForListeningPort(nat.Port(port))) - } - } - - if !tcpPortPresent { - exposedPorts = append(exposedPorts, "80/tcp") - waitStrategies = append(waitStrategies, wait.ForListeningPort("80/tcp")) - } - - req := testcontainers.ContainerRequest{ - FromDockerfile: testcontainers.FromDockerfile{ - Context: repoRoot, - Dockerfile: filepath.Join("./", "testdata", "integration.dockerfile"), - }, - ExposedPorts: exposedPorts, - WaitingFor: wait.ForAll(waitStrategies...), - } - - imContainer, err = testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ - ContainerRequest: req, - Started: true, - }) - - if err != nil { - return - } - - tb.Cleanup(func() { - shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - _ = imContainer.Terminate(shutdownCtx) - }) - - return -} diff --git a/pkg/audit/api.go b/pkg/audit/api.go deleted file mode 100644 index b3a789f..0000000 --- a/pkg/audit/api.go +++ /dev/null @@ -1,30 +0,0 @@ -//go:generate mockgen -source=$GOFILE -destination=./../../internal/mock/audit/audit.mock.go -package=audit_mock - -package audit - -import ( - "context" - "errors" - "io" -) - -var ( - ErrSinkAlreadyRegistered = errors.New("sink with same name already registered") -) - -type Emitter interface { - Emit(ev Event) -} - -type Sink interface { - Name() string - OnSubscribe(evs <-chan Event) -} - -type EventStream interface { - io.Closer - Emitter - RegisterSink(ctx context.Context, s Sink) error - Sinks() []string - RemoveSink(name string) (exists bool) -} diff --git a/pkg/audit/details/dns_details.go b/pkg/audit/details/dns_details.go deleted file mode 100644 index 91397dd..0000000 --- a/pkg/audit/details/dns_details.go +++ /dev/null @@ -1,47 +0,0 @@ -package details - -import ( - "google.golang.org/protobuf/types/known/anypb" -) - -func NewDNSFromWireFormat(entity *DNSDetailsEntity) DNS { - d := DNS{ - OPCode: entity.Opcode, - } - - for _, q := range entity.Questions { - d.Questions = append(d.Questions, DNSQuestion{ - RRType: q.Type, - Name: q.Name, - }) - } - - return d -} - -type DNSQuestion struct { - RRType ResourceRecordType - Name string -} - -type DNS struct { - OPCode DNSOpCode - Questions []DNSQuestion -} - -func (d DNS) MarshalToWireFormat() (any *anypb.Any, err error) { - detailsEntity := &DNSDetailsEntity{ - Opcode: d.OPCode, - } - - for _, q := range d.Questions { - detailsEntity.Questions = append(detailsEntity.Questions, &DNSQuestionEntity{ - Type: q.RRType, - Name: q.Name, - }) - } - - any, err = anypb.New(detailsEntity) - - return -} diff --git a/pkg/audit/details/http_details.go b/pkg/audit/details/http_details.go deleted file mode 100644 index bee3928..0000000 --- a/pkg/audit/details/http_details.go +++ /dev/null @@ -1,59 +0,0 @@ -package details - -import ( - "net/http" - "strings" - - "google.golang.org/protobuf/types/known/anypb" -) - -type HTTP struct { - Method string - Host string - URI string - Proto string - Headers http.Header -} - -func NewHTTPFromWireFormat(entity *HTTPDetailsEntity) HTTP { - headers := http.Header{} - for name, values := range entity.Headers { - for idx := range values.Values { - headers.Add(name, values.Values[idx]) - } - } - - return HTTP{ - Method: entity.Method.String(), - Host: entity.Host, - URI: entity.Uri, - Proto: entity.Proto, - Headers: headers, - } -} - -func (d HTTP) MarshalToWireFormat() (any *anypb.Any, err error) { - var method = HTTPMethod_GET - if methodValue, known := HTTPMethod_value[strings.ToUpper(d.Method)]; known { - method = HTTPMethod(methodValue) - } - - headers := make(map[string]*HTTPHeaderValue) - - for k, v := range d.Headers { - headers[k] = &HTTPHeaderValue{ - Values: v, - } - } - - protoDetails := &HTTPDetailsEntity{ - Method: method, - Host: d.Host, - Uri: d.URI, - Proto: d.Proto, - Headers: headers, - } - - any, err = anypb.New(protoDetails) - return -} diff --git a/pkg/audit/event.go b/pkg/audit/event.go deleted file mode 100644 index 969ba57..0000000 --- a/pkg/audit/event.go +++ /dev/null @@ -1,134 +0,0 @@ -package audit - -import ( - "net" - "strconv" - "strings" - "time" - - "gitlab.com/inetmock/inetmock/pkg/audit/details" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/known/anypb" - "google.golang.org/protobuf/types/known/timestamppb" -) - -type Details interface { - MarshalToWireFormat() (*anypb.Any, error) -} - -type Event struct { - ID int64 - Timestamp time.Time - Transport TransportProtocol - Application AppProtocol - SourceIP net.IP - DestinationIP net.IP - SourcePort uint16 - DestinationPort uint16 - ProtocolDetails Details - TLS *TLSDetails -} - -func (e Event) ProtoMessage() *EventEntity { - var tlsDetails *TLSDetailsEntity = nil - if e.TLS != nil { - tlsDetails = e.TLS.ProtoMessage() - } - - var detailsEntity *anypb.Any = nil - if e.ProtocolDetails != nil { - if any, err := e.ProtocolDetails.MarshalToWireFormat(); err == nil { - detailsEntity = any - } - } - - return &EventEntity{ - Id: e.ID, - Timestamp: timestamppb.New(e.Timestamp), - Transport: e.Transport, - Application: e.Application, - SourceIP: e.SourceIP, - DestinationIP: e.DestinationIP, - SourcePort: uint32(e.SourcePort), - DestinationPort: uint32(e.DestinationPort), - Tls: tlsDetails, - ProtocolDetails: detailsEntity, - } -} - -func (e *Event) ApplyDefaults(id int64) { - e.ID = id - emptyTime := time.Time{} - if e.Timestamp == emptyTime { - e.Timestamp = time.Now().UTC() - } -} - -func (e *Event) SetSourceIPFromAddr(remoteAddr net.Addr) { - ip, port := parseIPPortFromAddr(remoteAddr) - e.SourceIP = ip - e.SourcePort = port -} - -func (e *Event) SetDestinationIPFromAddr(localAddr net.Addr) { - ip, port := parseIPPortFromAddr(localAddr) - e.DestinationIP = ip - e.DestinationPort = port -} - -func NewEventFromProto(msg *EventEntity) (ev Event) { - ev = Event{ - ID: msg.GetId(), - Timestamp: msg.GetTimestamp().AsTime(), - Transport: msg.GetTransport(), - Application: msg.GetApplication(), - SourceIP: msg.SourceIP, - DestinationIP: msg.DestinationIP, - SourcePort: uint16(msg.GetSourcePort()), - DestinationPort: uint16(msg.GetDestinationPort()), - ProtocolDetails: guessDetailsFromApp(msg.GetProtocolDetails()), - TLS: NewTLSDetailsFromProto(msg.GetTls()), - } - return -} - -func parseIPPortFromAddr(addr net.Addr) (ip net.IP, port uint16) { - if addr == nil { - return - } - switch a := addr.(type) { - case *net.TCPAddr: - return a.IP, uint16(a.Port) - case *net.UDPAddr: - return a.IP, uint16(a.Port) - case *net.UnixAddr: - return - default: - ipPortSplit := strings.Split(addr.String(), ":") - if len(ipPortSplit) != 2 { - return - } - - ip = net.ParseIP(ipPortSplit[0]) - if p, err := strconv.Atoi(ipPortSplit[1]); err == nil { - port = uint16(p) - } - return - } -} - -func guessDetailsFromApp(any *anypb.Any) Details { - var detailsProto proto.Message - var err error - if detailsProto, err = any.UnmarshalNew(); err != nil { - return nil - } - switch any.TypeUrl { - case "type.googleapis.com/inetmock.audit.HTTPDetailsEntity": - return details.NewHTTPFromWireFormat(detailsProto.(*details.HTTPDetailsEntity)) - case "type.googleapis.com/inetmock.audit.DNSDetailsEntity": - return details.NewDNSFromWireFormat(detailsProto.(*details.DNSDetailsEntity)) - default: - return nil - } -} diff --git a/pkg/audit/event_stream.go b/pkg/audit/event_stream.go deleted file mode 100644 index 262a29d..0000000 --- a/pkg/audit/event_stream.go +++ /dev/null @@ -1,145 +0,0 @@ -package audit - -import ( - "context" - "sync" - "time" - - "github.com/bwmarrin/snowflake" - "gitlab.com/inetmock/inetmock/pkg/logging" - "go.uber.org/zap" -) - -func init() { - snowflake.Epoch = time.Unix(0, 0).Unix() -} - -type eventStream struct { - logger logging.Logger - buffer chan *Event - sinks map[string]*registeredSink - lock sync.Locker - idGenerator *snowflake.Node - sinkBufferSize int - sinkConsumptionTimeout time.Duration -} - -type registeredSink struct { - downstream chan Event - lock sync.Locker -} - -func NewEventStream(logger logging.Logger, options ...EventStreamOption) (es EventStream, err error) { - cfg := newEventStreamCfg() - - for _, opt := range options { - opt(&cfg) - } - - var node *snowflake.Node - node, err = snowflake.NewNode(cfg.generatorIndex) - if err != nil { - return - } - - generatorIdx++ - underlying := &eventStream{ - logger: logger, - sinks: make(map[string]*registeredSink), - buffer: make(chan *Event, cfg.bufferSize), - sinkBufferSize: cfg.sinkBuffersize, - sinkConsumptionTimeout: cfg.sinkConsumptionTimeout, - idGenerator: node, - lock: &sync.Mutex{}, - } - - // start distribute workers - for i := 0; i < cfg.distributeParallelization; i++ { - go underlying.distribute() - } - - es = underlying - - return -} - -func (e *eventStream) Emit(ev Event) { - ev.ApplyDefaults(e.idGenerator.Generate().Int64()) - select { - case e.buffer <- &ev: - e.logger.Debug("pushed event to distribute loop") - default: - e.logger.Warn("buffer is full") - } -} - -func (e *eventStream) RemoveSink(name string) (exists bool) { - e.lock.Lock() - defer e.lock.Unlock() - - var sink *registeredSink - sink, exists = e.sinks[name] - if !exists { - return - } - sink.lock.Lock() - defer sink.lock.Unlock() - delete(e.sinks, name) - close(sink.downstream) - - return -} - -func (e *eventStream) RegisterSink(ctx context.Context, s Sink) error { - name := s.Name() - - if _, exists := e.sinks[name]; exists { - return ErrSinkAlreadyRegistered - } - - rs := ®isteredSink{ - downstream: make(chan Event, e.sinkBufferSize), - lock: new(sync.Mutex), - } - - s.OnSubscribe(rs.downstream) - - go func() { - <-ctx.Done() - e.RemoveSink(name) - }() - - e.sinks[name] = rs - return nil -} - -func (e eventStream) Sinks() (sinks []string) { - for name := range e.sinks { - sinks = append(sinks, name) - } - return -} - -func (e *eventStream) Close() error { - close(e.buffer) - for _, rs := range e.sinks { - close(rs.downstream) - } - return nil -} - -func (e *eventStream) distribute() { - for ev := range e.buffer { - for name, rs := range e.sinks { - rs.lock.Lock() - e.logger.Debug("notify sink", zap.String("sink", name)) - select { - case rs.downstream <- *ev: - e.logger.Debug("pushed event to sink channel") - case <-time.After(e.sinkConsumptionTimeout): - e.logger.Warn("sink consummation timed out") - } - rs.lock.Unlock() - } - } -} diff --git a/pkg/audit/event_stream_test.go b/pkg/audit/event_stream_test.go deleted file mode 100644 index bd15ea8..0000000 --- a/pkg/audit/event_stream_test.go +++ /dev/null @@ -1,216 +0,0 @@ -package audit_test - -import ( - "context" - "crypto/tls" - "net" - "net/http" - "sync" - "testing" - "time" - - "gitlab.com/inetmock/inetmock/pkg/audit" - "gitlab.com/inetmock/inetmock/pkg/audit/details" - "gitlab.com/inetmock/inetmock/pkg/audit/sink" - "gitlab.com/inetmock/inetmock/pkg/logging" - "gitlab.com/inetmock/inetmock/pkg/wait" -) - -var ( - noOpSink = sink.NewNoOpSink("test defaultSink") - testEvents = []*audit.Event{ - { - Transport: audit.TransportProtocol_TCP, - Application: audit.AppProtocol_HTTP, - SourceIP: net.ParseIP("127.0.0.1").To4(), - DestinationIP: net.ParseIP("127.0.0.1").To4(), - SourcePort: 32344, - DestinationPort: 80, - TLS: &audit.TLSDetails{ - Version: audit.TLSVersionToEntity(tls.VersionTLS13).String(), - CipherSuite: tls.CipherSuiteName(tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA), - ServerName: "localhost", - }, - ProtocolDetails: details.HTTP{ - Method: "GET", - Host: "localhost", - URI: "http://localhost/asdf", - Proto: "HTTP 1.1", - Headers: http.Header{ - "Accept": []string{"application/json"}, - }, - }, - }, - { - Transport: audit.TransportProtocol_TCP, - Application: audit.AppProtocol_DNS, - SourceIP: net.ParseIP("::1").To16(), - DestinationIP: net.ParseIP("::1").To16(), - SourcePort: 32344, - DestinationPort: 80, - }, - } -) - -func wgMockSink(t testing.TB, wg *sync.WaitGroup) audit.Sink { - return sink.NewGenericSink( - "WG mock sink", - func(event audit.Event) { - t.Logf("Got event = %v", event) - wg.Done() - }, - ) -} - -func Test_eventStream_RegisterSink(t *testing.T) { - type args struct { - s audit.Sink - } - type testCase struct { - name string - args args - setup func(e audit.EventStream) - wantErr bool - } - tests := []testCase{ - { - name: "Register test defaultSink", - args: args{ - s: noOpSink, - }, - wantErr: false, - }, - { - name: "Fail due to already registered defaultSink", - args: args{ - s: noOpSink, - }, - setup: func(e audit.EventStream) { - _ = e.RegisterSink(context.Background(), noOpSink) - }, - wantErr: true, - }, - } - scenario := func(tt testCase) func(t *testing.T) { - return func(t *testing.T) { - var err error - var e audit.EventStream - if e, err = audit.NewEventStream(logging.CreateTestLogger(t)); err != nil { - t.Errorf("NewEventStream() error = %v", err) - } - - t.Cleanup(func() { - _ = e.Close() - }) - - if tt.setup != nil { - tt.setup(e) - } - - if err := e.RegisterSink(context.Background(), tt.args.s); (err != nil) != tt.wantErr { - t.Errorf("RegisterSink() error = %v, wantErr %v", err, tt.wantErr) - } - - found := false - for _, s := range e.Sinks() { - if found = s == tt.args.s.Name(); found { - break - } - } - if !found { - t.Errorf("expected defaultSink name %s not found in registered sinks %v", tt.args.s.Name(), e.Sinks()) - } - } - } - - for _, tt := range tests { - t.Run(tt.name, scenario(tt)) - } -} - -func Test_eventStream_Emit(t *testing.T) { - type args struct { - evs []*audit.Event - opts []audit.EventStreamOption - } - type testCase struct { - name string - args args - subscribe bool - } - tests := []testCase{ - { - name: "Expect to get a single event", - subscribe: true, - args: args{ - opts: []audit.EventStreamOption{}, - evs: testEvents[:1], - }, - }, - { - name: "Expect to get multiple events", - subscribe: true, - args: args{ - opts: []audit.EventStreamOption{}, - evs: testEvents, - }, - }, - { - name: "Emit without subscribe sink", - args: args{ - opts: []audit.EventStreamOption{audit.WithBufferSize(0)}, - evs: testEvents[:1], - }, - subscribe: false, - }, - } - - scenario := func(tt testCase) func(t *testing.T) { - return func(t *testing.T) { - var err error - var e audit.EventStream - if e, err = audit.NewEventStream(logging.CreateTestLogger(t), tt.args.opts...); err != nil { - t.Errorf("NewEventStream() error = %v", err) - } - - t.Cleanup(func() { - _ = e.Close() - }) - - emittedWaitGroup := new(sync.WaitGroup) - receivedWaitGroup := new(sync.WaitGroup) - - emittedWaitGroup.Add(len(tt.args.evs)) - - if tt.subscribe { - receivedWaitGroup.Add(len(tt.args.evs)) - if err := e.RegisterSink(context.Background(), wgMockSink(t, receivedWaitGroup)); err != nil { - t.Errorf("RegisterSink() error = %v", err) - } - } - - go func(evs []*audit.Event, wg *sync.WaitGroup) { - for _, ev := range evs { - e.Emit(*ev) - wg.Done() - } - }(tt.args.evs, emittedWaitGroup) - - select { - case <-wait.ForWaitGroupDone(emittedWaitGroup): - case <-time.After(100 * time.Millisecond): - t.Errorf("not all events emitted in time") - } - - select { - case <-wait.ForWaitGroupDone(receivedWaitGroup): - case <-time.After(5 * time.Second): - t.Errorf("did not get all expected events in time") - } - } - } - - for _, tt := range tests { - t.Run(tt.name, scenario(tt)) - } -} diff --git a/pkg/audit/event_test.go b/pkg/audit/event_test.go deleted file mode 100644 index b1aaebc..0000000 --- a/pkg/audit/event_test.go +++ /dev/null @@ -1,61 +0,0 @@ -package audit - -import ( - "net/http" - "reflect" - "testing" - - "gitlab.com/inetmock/inetmock/pkg/audit/details" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/known/anypb" -) - -func Test_guessDetailsFromApp(t *testing.T) { - - mustAny := func(msg proto.Message) *anypb.Any { - a, err := anypb.New(msg) - if err != nil { - panic(a) - } - return a - } - - type args struct { - any *anypb.Any - } - type testCase struct { - name string - args args - want Details - } - tests := []testCase{ - { - name: "HTTP etails", - args: args{ - any: mustAny(&details.HTTPDetailsEntity{ - Method: details.HTTPMethod_GET, - Host: "localhost", - Uri: "http://localhost/asdf", - Proto: "HTTP", - }), - }, - want: details.HTTP{ - Method: "GET", - Host: "localhost", - URI: "http://localhost/asdf", - Proto: "HTTP", - Headers: http.Header{}, - }, - }, - } - scenario := func(tt testCase) func(t *testing.T) { - return func(t *testing.T) { - if got := guessDetailsFromApp(tt.args.any); !reflect.DeepEqual(got, tt.want) { - t.Errorf("guessDetailsFromApp() = %v, want %v", got, tt.want) - } - } - } - for _, tt := range tests { - t.Run(tt.name, scenario(tt)) - } -} diff --git a/pkg/audit/options.go b/pkg/audit/options.go deleted file mode 100644 index 46868e9..0000000 --- a/pkg/audit/options.go +++ /dev/null @@ -1,74 +0,0 @@ -package audit - -import ( - "runtime" - "time" -) - -const ( - defaultEventStreamBufferSize = 100 - defaultSinkBufferSize = 0 - defaultSinkConsumptionTimeout = 50 * time.Millisecond -) - -var ( - defaultDistributeParallelization int - generatorIdx int64 = 1 - WithBufferSize = func(bufferSize int) EventStreamOption { - return func(cfg *eventStreamCfg) { - cfg.bufferSize = bufferSize - } - } - WithGeneratorIndex = func(generatorIndex int64) EventStreamOption { - return func(cfg *eventStreamCfg) { - cfg.generatorIndex = generatorIndex - } - } - WithSinkBufferSize = func(bufferSize int) EventStreamOption { - return func(cfg *eventStreamCfg) { - cfg.sinkBuffersize = bufferSize - } - } - WithSinkConsumptionTimeout = func(timeout time.Duration) EventStreamOption { - return func(cfg *eventStreamCfg) { - cfg.sinkConsumptionTimeout = timeout - } - } - WithDistributeParallelization = func(parallelization int) EventStreamOption { - return func(cfg *eventStreamCfg) { - if parallelization <= 0 || parallelization > runtime.NumCPU() { - return - } - cfg.distributeParallelization = parallelization - } - } -) - -func init() { - if defaultDistributeParallelization = runtime.NumCPU() / 2; defaultDistributeParallelization < 2 { - defaultDistributeParallelization = 2 - } -} - -type EventStreamOption func(cfg *eventStreamCfg) - -type eventStreamCfg struct { - bufferSize int - sinkBuffersize int - generatorIndex int64 - distributeParallelization int - sinkConsumptionTimeout time.Duration -} - -func newEventStreamCfg() eventStreamCfg { - cfg := eventStreamCfg{ - generatorIndex: generatorIdx, - sinkBuffersize: defaultSinkBufferSize, - bufferSize: defaultEventStreamBufferSize, - sinkConsumptionTimeout: defaultSinkConsumptionTimeout, - distributeParallelization: defaultDistributeParallelization, - } - generatorIdx++ - - return cfg -} diff --git a/pkg/audit/reader.go b/pkg/audit/reader.go deleted file mode 100644 index d679565..0000000 --- a/pkg/audit/reader.go +++ /dev/null @@ -1,87 +0,0 @@ -package audit - -import ( - "encoding/binary" - "io" - "sync" - - "google.golang.org/protobuf/proto" -) - -const ( - lengthBufferSize = 4 - defaultPayloadBufferSize = 4096 -) - -type Reader interface { - Read() (Event, error) -} - -type EventReaderOption func(reader *eventReader) - -var ( - WithReaderByteOrder = func(order binary.ByteOrder) EventReaderOption { - return func(reader *eventReader) { - reader.byteOrder = order - } - } -) - -func NewEventReader(source io.Reader, opts ...EventReaderOption) Reader { - reader := &eventReader{ - source: source, - byteOrder: defaultOrder, - lengthPool: &sync.Pool{ - New: func() interface{} { - return make([]byte, lengthBufferSize) - }, - }, - payloadPool: &sync.Pool{ - New: func() interface{} { - return make([]byte, defaultPayloadBufferSize) - }, - }, - } - - for _, opt := range opts { - opt(reader) - } - - return reader -} - -type eventReader struct { - lengthPool *sync.Pool - payloadPool *sync.Pool - byteOrder binary.ByteOrder - source io.Reader -} - -func (e eventReader) Read() (ev Event, err error) { - lengthBuf := e.lengthPool.Get().([]byte) - if _, err = e.source.Read(lengthBuf); err != nil { - return - } - - length := e.byteOrder.Uint32(lengthBuf) - var msgBuf []byte - if length <= defaultPayloadBufferSize { - msgBuf = e.payloadPool.Get().([]byte)[:length] - } else { - msgBuf = make([]byte, length) - } - - if _, err = e.source.Read(msgBuf); err != nil { - return - } - - protoEv := new(EventEntity) - - if err = proto.Unmarshal(msgBuf, protoEv); err != nil { - return - } - - ev = NewEventFromProto(protoEv) - - return -} diff --git a/pkg/audit/reader_test.go b/pkg/audit/reader_test.go deleted file mode 100644 index 94c2e32..0000000 --- a/pkg/audit/reader_test.go +++ /dev/null @@ -1,99 +0,0 @@ -package audit_test - -import ( - "bytes" - "encoding/binary" - "encoding/hex" - "io" - "reflect" - "testing" - - "gitlab.com/inetmock/inetmock/pkg/audit" -) - -var ( - //nolint:lll - httpPayloadBytesLittleEndian = `dd000000120b088092b8c398feffffff01180120022a047f00000132047f00000138d8fc0140504a3308041224544c535f45434448455f45434453415f574954485f4145535f3235365f4342435f5348411a096c6f63616c686f73745282010a34747970652e676f6f676c65617069732e636f6d2f696e65746d6f636b2e61756469742e4854545044657461696c73456e74697479124a12096c6f63616c686f73741a15687474703a2f2f6c6f63616c686f73742f6173646622084854545020312e312a1c0a0641636365707412120a106170706c69636174696f6e2f6a736f6e` - //nolint:lll - httpPayloadBytesBigEndian = `000000dd120b088092b8c398feffffff01180120022a047f00000132047f00000138d8fc0140504a3308041224544c535f45434448455f45434453415f574954485f4145535f3235365f4342435f5348411a096c6f63616c686f73745282010a34747970652e676f6f676c65617069732e636f6d2f696e65746d6f636b2e61756469742e4854545044657461696c73456e74697479124a12096c6f63616c686f73741a15687474703a2f2f6c6f63616c686f73742f6173646622084854545020312e312a1c0a0641636365707412120a106170706c69636174696f6e2f6a736f6e` - //nolint:lll - dnsPayloadBytesLittleEndian = `3b000000120b088092b8c398feffffff01180120012a100000000000000000000000000000000132100000000000000000000000000000000138d8fc014050` - //nolint:lll - dnsPayloadBytesBigEndian = `0000003b120b088092b8c398feffffff01180120012a100000000000000000000000000000000132100000000000000000000000000000000138d8fc014050` -) - -func mustDecodeHex(hexBytes string) io.Reader { - b, err := hex.DecodeString(hexBytes) - if err != nil { - panic(err) - } - return bytes.NewReader(b) -} - -func Test_eventReader_Read(t *testing.T) { - type fields struct { - source io.Reader - order binary.ByteOrder - } - type testCase struct { - name string - fields fields - wantEv *audit.Event - wantErr bool - } - tests := []testCase{ - { - name: "Read HTTP payload - little endian", - fields: fields{ - source: mustDecodeHex(httpPayloadBytesLittleEndian), - order: binary.LittleEndian, - }, - wantEv: testEvents[0], - wantErr: false, - }, - { - name: "Read HTTP payload - big endian", - fields: fields{ - source: mustDecodeHex(httpPayloadBytesBigEndian), - order: binary.BigEndian, - }, - wantEv: testEvents[0], - wantErr: false, - }, - { - name: "Read DNS payload - little endian", - fields: fields{ - source: mustDecodeHex(dnsPayloadBytesLittleEndian), - order: binary.LittleEndian, - }, - wantEv: testEvents[1], - wantErr: false, - }, - { - name: "Read DNS payload - big endian", - fields: fields{ - source: mustDecodeHex(dnsPayloadBytesBigEndian), - order: binary.BigEndian, - }, - wantEv: testEvents[1], - wantErr: false, - }, - } - scenario := func(tt testCase) func(t *testing.T) { - return func(t *testing.T) { - e := audit.NewEventReader(tt.fields.source, audit.WithReaderByteOrder(tt.fields.order)) - gotEv, err := e.Read() - if (err != nil) != tt.wantErr { - t.Errorf("Read() error = %v, wantErr %v", err, tt.wantErr) - return - } - - if err == nil && !reflect.DeepEqual(gotEv, *tt.wantEv) { - t.Errorf("Read() gotEv = %v, want %v", gotEv, tt.wantEv) - } - } - } - for _, tt := range tests { - t.Run(tt.name, scenario(tt)) - } -} diff --git a/pkg/audit/sink/generic_sink.go b/pkg/audit/sink/generic_sink.go deleted file mode 100644 index 4c10e84..0000000 --- a/pkg/audit/sink/generic_sink.go +++ /dev/null @@ -1,33 +0,0 @@ -package sink - -import ( - "gitlab.com/inetmock/inetmock/pkg/audit" -) - -func NewNoOpSink(name string) audit.Sink { - return NewGenericSink(name, func(_ audit.Event) {}) -} - -func NewGenericSink(name string, consumer func(ev audit.Event)) audit.Sink { - return &genericSink{ - name: name, - consumer: consumer, - } -} - -type genericSink struct { - name string - consumer func(ev audit.Event) -} - -func (g genericSink) Name() string { - return g.name -} - -func (g genericSink) OnSubscribe(evs <-chan audit.Event) { - go func(consumer func(ev audit.Event), evs <-chan audit.Event) { - for ev := range evs { - consumer(ev) - } - }(g.consumer, evs) -} diff --git a/pkg/audit/sink/generic_sink_test.go b/pkg/audit/sink/generic_sink_test.go deleted file mode 100644 index 122f105..0000000 --- a/pkg/audit/sink/generic_sink_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package sink_test - -import ( - "context" - "sync" - "testing" - "time" - - "gitlab.com/inetmock/inetmock/pkg/audit" - "gitlab.com/inetmock/inetmock/pkg/audit/sink" - "gitlab.com/inetmock/inetmock/pkg/logging" - "gitlab.com/inetmock/inetmock/pkg/wait" -) - -func Test_genericSink_OnSubscribe(t *testing.T) { - type testCase struct { - name string - events []*audit.Event - } - tests := []testCase{ - { - name: "Get a single log line", - events: testEvents[:1], - }, - { - name: "Get multiple events", - events: testEvents, - }, - } - scenario := func(tt testCase) func(t *testing.T) { - return func(t *testing.T) { - wg := new(sync.WaitGroup) - wg.Add(len(tt.events)) - - genericSink := sink.NewGenericSink(t.Name(), func(ev audit.Event) { - wg.Done() - }) - - var evs audit.EventStream - var err error - if evs, err = audit.NewEventStream(logging.CreateTestLogger(t)); err != nil { - t.Errorf("NewEventStream() error = %v", err) - } - - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - if err = evs.RegisterSink(ctx, genericSink); err != nil { - t.Errorf("RegisterSink() error = %v", err) - } - - for _, ev := range tt.events { - evs.Emit(*ev) - } - - select { - case <-time.After(100 * time.Millisecond): - t.Errorf("not all events recorded in time") - case <-wait.ForWaitGroupDone(wg): - } - } - } - for _, tt := range tests { - t.Run(tt.name, scenario(tt)) - } -} diff --git a/pkg/audit/sink/log_sink.go b/pkg/audit/sink/log_sink.go deleted file mode 100644 index efebc0b..0000000 --- a/pkg/audit/sink/log_sink.go +++ /dev/null @@ -1,53 +0,0 @@ -package sink - -import ( - "gitlab.com/inetmock/inetmock/pkg/audit" - "gitlab.com/inetmock/inetmock/pkg/logging" - "go.uber.org/zap" -) - -const ( - logSinkName = "logging" -) - -func NewLogSink(logger logging.Logger) audit.Sink { - return &logSink{ - logger: logger, - } -} - -type logSink struct { - logger logging.Logger -} - -func (logSink) Name() string { - return logSinkName -} - -func (l logSink) OnSubscribe(evs <-chan audit.Event) { - go func(logger logging.Logger, evs <-chan audit.Event) { - for ev := range evs { - eventLogger := logger - - if ev.TLS != nil { - eventLogger = eventLogger.With( - zap.String("tls_server_name", ev.TLS.ServerName), - zap.String("tls_cipher_suite", ev.TLS.CipherSuite), - zap.String("tls_version", ev.TLS.Version), - ) - } - - eventLogger.Info( - "handled request", - zap.Time("timestamp", ev.Timestamp), - zap.String("application", ev.Application.String()), - zap.String("transport", ev.Transport.String()), - zap.String("source_ip", ev.SourceIP.String()), - zap.Uint16("source_port", ev.SourcePort), - zap.String("destination_ip", ev.DestinationIP.String()), - zap.Uint16("destination_port", ev.DestinationPort), - zap.Any("details", ev.ProtocolDetails), - ) - } - }(l.logger, evs) -} diff --git a/pkg/audit/sink/log_sink_test.go b/pkg/audit/sink/log_sink_test.go deleted file mode 100644 index cc8deca..0000000 --- a/pkg/audit/sink/log_sink_test.go +++ /dev/null @@ -1,115 +0,0 @@ -package sink_test - -import ( - "context" - "sync" - "testing" - "time" - - "github.com/golang/mock/gomock" - logging_mock "gitlab.com/inetmock/inetmock/internal/mock/logging" - "gitlab.com/inetmock/inetmock/pkg/audit" - "gitlab.com/inetmock/inetmock/pkg/audit/sink" - "gitlab.com/inetmock/inetmock/pkg/logging" - "gitlab.com/inetmock/inetmock/pkg/wait" - "go.uber.org/zap" -) - -//nolint:dupl -func Test_logSink_OnSubscribe(t *testing.T) { - type fields struct { - loggerSetup func(t *testing.T, wg *sync.WaitGroup) logging.Logger - } - type testCase struct { - name string - fields fields - events []*audit.Event - } - tests := []testCase{ - { - name: "Get a single log line", - fields: fields{ - loggerSetup: func(t *testing.T, wg *sync.WaitGroup) logging.Logger { - ctrl := gomock.NewController(t) - t.Cleanup(ctrl.Finish) - loggerMock := logging_mock.NewMockLogger(ctrl) - - loggerMock. - EXPECT(). - With(gomock.Any()). - Return(loggerMock) - - loggerMock. - EXPECT(). - Info("handled request", gomock.Any()). - Do(func(_ string, _ ...zap.Field) { - wg.Done() - }). - Times(1) - - return loggerMock - }, - }, - events: testEvents[:1], - }, - { - name: "Get multiple events", - fields: fields{ - loggerSetup: func(t *testing.T, wg *sync.WaitGroup) logging.Logger { - ctrl := gomock.NewController(t) - t.Cleanup(ctrl.Finish) - loggerMock := logging_mock.NewMockLogger(ctrl) - - loggerMock. - EXPECT(). - With(gomock.Any()). - Return(loggerMock) - - loggerMock. - EXPECT(). - Info("handled request", gomock.Any()). - Do(func(_ string, _ ...zap.Field) { - wg.Done() - }). - Times(2) - - return loggerMock - }, - }, - events: testEvents, - }, - } - scenario := func(tt testCase) func(t *testing.T) { - return func(t *testing.T) { - wg := new(sync.WaitGroup) - wg.Add(len(tt.events)) - - logSink := sink.NewLogSink(tt.fields.loggerSetup(t, wg)) - var evs audit.EventStream - var err error - if evs, err = audit.NewEventStream(logging.CreateTestLogger(t)); err != nil { - t.Errorf("NewEventStream() error = %v", err) - } - - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - if err = evs.RegisterSink(ctx, logSink); err != nil { - t.Errorf("RegisterSink() error = %v", err) - } - - for _, ev := range tt.events { - evs.Emit(*ev) - } - - select { - case <-time.After(100 * time.Millisecond): - t.Errorf("not all events recorded in time") - case <-wait.ForWaitGroupDone(wg): - } - } - } - - for _, tt := range tests { - t.Run(tt.name, scenario(tt)) - } -} diff --git a/pkg/audit/sink/metric_sink.go b/pkg/audit/sink/metric_sink.go deleted file mode 100644 index 2090dcb..0000000 --- a/pkg/audit/sink/metric_sink.go +++ /dev/null @@ -1,34 +0,0 @@ -package sink - -import ( - "github.com/prometheus/client_golang/prometheus" - "gitlab.com/inetmock/inetmock/pkg/audit" - "gitlab.com/inetmock/inetmock/pkg/metrics" -) - -func NewMetricSink() (sink audit.Sink, err error) { - var totalEventsCounter *prometheus.CounterVec - if totalEventsCounter, err = metrics.Counter("audit", "events_total", "", "application", "transport"); err != nil { - return - } - sink = &metricSink{ - eventCounter: totalEventsCounter, - } - return -} - -type metricSink struct { - eventCounter *prometheus.CounterVec -} - -func (metricSink) Name() string { - return "metrics" -} - -func (m metricSink) OnSubscribe(evs <-chan audit.Event) { - go func(evs <-chan audit.Event) { - for ev := range evs { - m.eventCounter.WithLabelValues(ev.Application.String(), ev.Transport.String()).Inc() - } - }(evs) -} diff --git a/pkg/audit/sink/writer_sink.go b/pkg/audit/sink/writer_sink.go deleted file mode 100644 index e0e2ba5..0000000 --- a/pkg/audit/sink/writer_sink.go +++ /dev/null @@ -1,45 +0,0 @@ -package sink - -import "gitlab.com/inetmock/inetmock/pkg/audit" - -type WriterSinkOption func(sink *writerCloserSink) - -var ( - WithCloseOnExit WriterSinkOption = func(sink *writerCloserSink) { - sink.closeOnExit = true - } -) - -func NewWriterSink(name string, target audit.Writer, opts ...WriterSinkOption) audit.Sink { - sink := &writerCloserSink{ - name: name, - target: target, - } - - for _, opt := range opts { - opt(sink) - } - - return sink -} - -type writerCloserSink struct { - name string - target audit.Writer - closeOnExit bool -} - -func (f writerCloserSink) Name() string { - return f.name -} - -func (f writerCloserSink) OnSubscribe(evs <-chan audit.Event) { - go func(target audit.Writer, closeOnExit bool, evs <-chan audit.Event) { - for ev := range evs { - _ = target.Write(&ev) - } - if closeOnExit { - _ = target.Close() - } - }(f.target, f.closeOnExit, evs) -} diff --git a/pkg/audit/sink/writer_sink_test.go b/pkg/audit/sink/writer_sink_test.go deleted file mode 100644 index 6d59c32..0000000 --- a/pkg/audit/sink/writer_sink_test.go +++ /dev/null @@ -1,118 +0,0 @@ -package sink_test - -import ( - "context" - "crypto/tls" - "net" - "net/http" - "sync" - "testing" - "time" - - "github.com/golang/mock/gomock" - audit_mock "gitlab.com/inetmock/inetmock/internal/mock/audit" - "gitlab.com/inetmock/inetmock/pkg/audit" - "gitlab.com/inetmock/inetmock/pkg/audit/details" - "gitlab.com/inetmock/inetmock/pkg/audit/sink" - "gitlab.com/inetmock/inetmock/pkg/logging" - "gitlab.com/inetmock/inetmock/pkg/wait" -) - -var ( - testEvents = []*audit.Event{ - { - Transport: audit.TransportProtocol_TCP, - Application: audit.AppProtocol_HTTP, - SourceIP: net.ParseIP("127.0.0.1").To4(), - DestinationIP: net.ParseIP("127.0.0.1").To4(), - SourcePort: 32344, - DestinationPort: 80, - TLS: &audit.TLSDetails{ - Version: audit.TLSVersionToEntity(tls.VersionTLS13).String(), - CipherSuite: tls.CipherSuiteName(tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA), - ServerName: "localhost", - }, - ProtocolDetails: details.HTTP{ - Method: "GET", - Host: "localhost", - URI: "http://localhost/asdf", - Proto: "HTTP 1.1", - Headers: http.Header{ - "Accept": []string{"application/json"}, - }, - }, - }, - { - Transport: audit.TransportProtocol_TCP, - Application: audit.AppProtocol_DNS, - SourceIP: net.ParseIP("::1").To16(), - DestinationIP: net.ParseIP("::1").To16(), - SourcePort: 32344, - DestinationPort: 80, - }, - } -) - -func Test_writerCloserSink_OnSubscribe(t *testing.T) { - type testCase struct { - name string - events []*audit.Event - } - tests := []testCase{ - { - name: "Get a single event", - events: testEvents[:1], - }, - { - name: "Get multiple events", - events: testEvents, - }, - } - scenario := func(tt testCase) func(t *testing.T) { - return func(t *testing.T) { - wg := new(sync.WaitGroup) - wg.Add(len(tt.events)) - - ctrl := gomock.NewController(t) - t.Cleanup(ctrl.Finish) - - writerMock := audit_mock.NewMockWriter(ctrl) - writerMock. - EXPECT(). - Write(gomock.Any()). - Do(func(_ *audit.Event) { - wg.Done() - }). - Times(len(tt.events)) - - writerCloserSink := sink.NewWriterSink("WriterMock", writerMock) - var evs audit.EventStream - var err error - - if evs, err = audit.NewEventStream(logging.CreateTestLogger(t)); err != nil { - t.Errorf("NewEventStream() error = %v", err) - } - - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - - if err = evs.RegisterSink(ctx, writerCloserSink); err != nil { - t.Errorf("RegisterSink() error = %v", err) - } - - for _, ev := range tt.events { - evs.Emit(*ev) - } - - select { - case <-time.After(100 * time.Millisecond): - t.Errorf("not all events recorded in time") - case <-wait.ForWaitGroupDone(wg): - } - } - } - - for _, tt := range tests { - t.Run(tt.name, scenario(tt)) - } -} diff --git a/pkg/audit/tls_details.go b/pkg/audit/tls_details.go deleted file mode 100644 index e4565ff..0000000 --- a/pkg/audit/tls_details.go +++ /dev/null @@ -1,46 +0,0 @@ -package audit - -import "crypto/tls" - -var ( - tlsToEntity = map[uint16]TLSVersion{ - tls.VersionSSL30: TLSVersion_SSLv30, - tls.VersionTLS10: TLSVersion_TLS10, - tls.VersionTLS11: TLSVersion_TLS11, - tls.VersionTLS12: TLSVersion_TLS12, - tls.VersionTLS13: TLSVersion_TLS13, - } -) - -type TLSDetails struct { - Version string - CipherSuite string - ServerName string -} - -func TLSVersionToEntity(version uint16) TLSVersion { - if v, known := tlsToEntity[version]; known { - return v - } - return TLSVersion_SSLv30 -} - -func NewTLSDetailsFromProto(entity *TLSDetailsEntity) *TLSDetails { - if entity == nil { - return nil - } - - return &TLSDetails{ - Version: entity.GetVersion().String(), - CipherSuite: entity.GetCipherSuite(), - ServerName: entity.GetServerName(), - } -} - -func (d TLSDetails) ProtoMessage() *TLSDetailsEntity { - return &TLSDetailsEntity{ - Version: TLSVersion(TLSVersion_value[d.Version]), - CipherSuite: d.CipherSuite, - ServerName: d.ServerName, - } -} diff --git a/pkg/audit/writer.go b/pkg/audit/writer.go deleted file mode 100644 index 15424d7..0000000 --- a/pkg/audit/writer.go +++ /dev/null @@ -1,89 +0,0 @@ -//go:generate mockgen -source=$GOFILE -destination=./../../internal/mock/audit/writer.mock.go -package=audit_mock - -package audit - -import ( - "encoding/binary" - "errors" - "io" - "sync" - - "google.golang.org/protobuf/proto" -) - -var ( - WithWriterByteOrder = func(order binary.ByteOrder) func(writer *eventWriter) { - return func(writer *eventWriter) { - writer.byteOrder = order - } - } - defaultOrder = binary.BigEndian - ErrValueMostNotBeNil = errors.New("event value must not be nil") -) - -type Writer interface { - io.Closer - Write(ev *Event) error -} - -type EventWriterOption func(writer *eventWriter) - -func NewEventWriter(target io.Writer, opts ...EventWriterOption) Writer { - writer := &eventWriter{ - target: target, - byteOrder: defaultOrder, - lengthPool: &sync.Pool{ - New: func() interface{} { - return make([]byte, lengthBufferSize) - }, - }, - } - - for _, opt := range opts { - opt(writer) - } - - return writer -} - -type eventWriter struct { - lengthPool *sync.Pool - target io.Writer - byteOrder binary.ByteOrder -} - -type syncer interface { - Sync() error -} - -func (e eventWriter) Write(ev *Event) (err error) { - if ev == nil { - return ErrValueMostNotBeNil - } - var bytes []byte - - if bytes, err = proto.Marshal(ev.ProtoMessage()); err != nil { - return - } - buf := e.lengthPool.Get().([]byte) - e.byteOrder.PutUint32(buf, uint32(len(bytes))) - - if _, err = e.target.Write(buf); err != nil { - return - } - if _, err = e.target.Write(bytes); err != nil { - return - } - if syncerInst, ok := e.target.(syncer); ok { - err = syncerInst.Sync() - } - - return -} - -func (e eventWriter) Close() error { - if closer, isCloser := e.target.(io.Closer); isCloser { - return closer.Close() - } - return nil -} diff --git a/pkg/audit/writer_test.go b/pkg/audit/writer_test.go deleted file mode 100644 index 17c2e88..0000000 --- a/pkg/audit/writer_test.go +++ /dev/null @@ -1,117 +0,0 @@ -//go:generate mockgen -source=$GOFILE -destination=./../../internal/mock/audit/writercloser.mock.go -package=audit_mock - -package audit_test - -import ( - "encoding/binary" - "encoding/hex" - "io" - "testing" - - "github.com/golang/mock/gomock" - audit_mock "gitlab.com/inetmock/inetmock/internal/mock/audit" - "gitlab.com/inetmock/inetmock/pkg/audit" -) - -type WriterCloserSyncer interface { - io.WriteCloser - Sync() error -} - -func Test_eventWriter_Write(t *testing.T) { - type fields struct { - order binary.ByteOrder - } - type args struct { - evs []*audit.Event - } - type testCase struct { - name string - fields fields - args args - wantErr bool - } - tests := []testCase{ - { - name: "Write a single event - little endian", - fields: fields{ - order: binary.LittleEndian, - }, - args: args{ - evs: testEvents[:1], - }, - wantErr: false, - }, - { - name: "Write a single event - big endian", - fields: fields{ - order: binary.BigEndian, - }, - args: args{ - evs: testEvents[:1], - }, - wantErr: false, - }, - { - name: "Write multiple events - little endian", - fields: fields{ - order: binary.LittleEndian, - }, - args: args{ - evs: testEvents, - }, - wantErr: false, - }, - { - name: "Write multiple events - big endian", - fields: fields{ - order: binary.BigEndian, - }, - args: args{ - evs: testEvents, - }, - wantErr: false, - }, - } - scenario := func(tt testCase) func(t *testing.T) { - return func(t *testing.T) { - ctrl := gomock.NewController(t) - t.Cleanup(ctrl.Finish) - writerMock := audit_mock.NewMockWriterCloserSyncer(ctrl) - calls := make([]*gomock.Call, 0) - for i := 0; i < len(tt.args.evs); i++ { - calls = append(calls, - writerMock. - EXPECT(). - Write(gomock.Any()). - Do(func(data []byte) { - t.Logf("got payload = %s", hex.EncodeToString(data)) - t.Logf("got length %d", tt.fields.order.Uint32(data)) - }), - writerMock. - EXPECT(). - Write(gomock.Any()). - Do(func(data []byte) { - t.Logf("got payload = %s", hex.EncodeToString(data)) - }), - writerMock. - EXPECT(). - Sync(), - ) - } - gomock.InOrder(calls...) - - e := audit.NewEventWriter(writerMock, audit.WithWriterByteOrder(tt.fields.order)) - - for _, ev := range tt.args.evs { - if err := e.Write(ev); (err != nil) != tt.wantErr { - t.Errorf("Write() error = %v, wantErr %v", err, tt.wantErr) - } - } - } - } - - for _, tt := range tests { - t.Run(tt.name, scenario(tt)) - } -} diff --git a/pkg/cert/addr_utils.go b/pkg/cert/addr_utils.go deleted file mode 100644 index adf3211..0000000 --- a/pkg/cert/addr_utils.go +++ /dev/null @@ -1,22 +0,0 @@ -package cert - -import ( - "fmt" - "strings" -) - -func extractIPFromAddress(addr string) (ip string, err error) { - if idx := strings.LastIndex(addr, ":"); idx < 0 { - err = fmt.Errorf("addr %s does not match expected scheme :", addr) - - } else { - /* get IP part of address */ - ip = addr[0:idx] - - /* trim [ ] for IPv6 addresses */ - if ip[0] == '[' { - ip = ip[1 : len(ip)-1] - } - } - return -} diff --git a/pkg/cert/addr_utils_test.go b/pkg/cert/addr_utils_test.go deleted file mode 100644 index a8401e4..0000000 --- a/pkg/cert/addr_utils_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package cert - -import "testing" - -func Test_extractIPFromAddress(t *testing.T) { - type args struct { - addr string - } - tests := []struct { - name string - args args - want string - wantErr bool - }{ - { - name: "Get address for IPv4 address", - want: "127.0.0.1", - wantErr: false, - args: args{ - addr: "127.0.0.1:23492", - }, - }, - { - name: "Get address for IPv6 address", - want: "::1", - wantErr: false, - args: args{ - addr: "[::1]:23492", - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := extractIPFromAddress(tt.args.addr) - if (err != nil) != tt.wantErr { - t.Errorf("extractIPFromAddress() error = %v, wantErr %v", err, tt.wantErr) - return - } - if got != tt.want { - t.Errorf("extractIPFromAddress() got = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/pkg/cert/cache.go b/pkg/cert/cache.go deleted file mode 100644 index 2bf130d..0000000 --- a/pkg/cert/cache.go +++ /dev/null @@ -1,78 +0,0 @@ -//go:generate mockgen -source=$GOFILE -destination=./../../internal/mock/cert/cert_cache.mock.go -package=cert_mock - -package cert - -import ( - "crypto/tls" - "crypto/x509" - "errors" -) - -type Cache interface { - Put(cert *tls.Certificate) error - Get(cn string) (*tls.Certificate, bool) -} - -func NewFileSystemCache(certCachePath string, source TimeSource) Cache { - return &fileSystemCache{ - certCachePath: certCachePath, - inMemCache: make(map[string]*tls.Certificate), - timeSource: source, - } -} - -type fileSystemCache struct { - certCachePath string - inMemCache map[string]*tls.Certificate - timeSource TimeSource -} - -func (f *fileSystemCache) Put(cert *tls.Certificate) (err error) { - if cert == nil { - err = errors.New("cert may not be nil") - return - } - var cn string - if len(cert.Certificate) > 0 { - if pubKey, err := x509.ParseCertificate(cert.Certificate[0]); err != nil { - return err - } else { - cn = pubKey.Subject.CommonName - } - - f.inMemCache[cn] = cert - pemCrt := NewPEM(cert) - err = pemCrt.Write(cn, f.certCachePath) - } else { - err = errors.New("no public key present for certificate") - } - return -} - -func (f *fileSystemCache) Get(cn string) (*tls.Certificate, bool) { - - if crt, ok := f.inMemCache[cn]; ok { - return crt, true - } - - pemCrt := NewPEM(nil) - if err := pemCrt.Read(cn, f.certCachePath); err != nil || pemCrt.Cert() == nil { - return nil, false - } - - x509Cert, err := x509.ParseCertificate(pemCrt.Cert().Certificate[0]) - if err == nil && !certShouldBeRenewed(f.timeSource, x509Cert) { - return pemCrt.Cert(), true - } - - return nil, false -} - -func certShouldBeRenewed(timeSource TimeSource, cert *x509.Certificate) bool { - lifetime := cert.NotAfter.Sub(cert.NotBefore) - // if the cert is closer to the end of the lifetime than lifetime/2 it should be renewed - if cert.NotAfter.Sub(timeSource.UTCNow()) < lifetime/4 { - return true - } - return false -} diff --git a/pkg/cert/cache_test.go b/pkg/cert/cache_test.go deleted file mode 100644 index 71fa798..0000000 --- a/pkg/cert/cache_test.go +++ /dev/null @@ -1,300 +0,0 @@ -package cert - -import ( - "crypto/tls" - "crypto/x509" - "fmt" - "os" - "path" - "reflect" - "testing" - "time" - - "github.com/golang/mock/gomock" - certmock "gitlab.com/inetmock/inetmock/internal/mock/cert" -) - -const ( - cnLocalhost = "localhost" - caCN = "UnitTests" - serverRelativeValidity = 24 * time.Hour - caRelativeValidity = 168 * time.Hour -) - -var ( - serverCN = fmt.Sprintf("%s-%d", cnLocalhost, time.Now().Unix()) -) - -func Test_certShouldBeRenewed(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - type args struct { - timeSource TimeSource - cert *x509.Certificate - } - tests := []struct { - name string - args args - want bool - }{ - { - name: "Expect cert should not be renewed right after creation", - args: args{ - timeSource: func() TimeSource { - tsMock := certmock.NewMockTimeSource(ctrl) - tsMock. - EXPECT(). - UTCNow(). - Return(time.Now().UTC()). - Times(1) - return tsMock - }(), - cert: &x509.Certificate{ - NotAfter: time.Now().UTC().Add(serverRelativeValidity), - NotBefore: time.Now().UTC().Add(-serverRelativeValidity), - }, - }, - want: false, - }, - { - name: "Expect cert should be renewed if the remaining lifetime is less than a quarter of the total lifetime", - args: args{ - timeSource: func() TimeSource { - tsMock := certmock.NewMockTimeSource(ctrl) - tsMock. - EXPECT(). - UTCNow(). - Return(time.Now().UTC().Add(serverRelativeValidity/2 + 1*time.Hour)). - Times(1) - return tsMock - }(), - cert: &x509.Certificate{ - NotAfter: time.Now().UTC().Add(serverRelativeValidity), - NotBefore: time.Now().UTC().Add(-serverRelativeValidity), - }, - }, - want: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := certShouldBeRenewed(tt.args.timeSource, tt.args.cert); got != tt.want { - t.Errorf("certShouldBeRenewed() = %v, want %v", got, tt.want) - } - }) - } -} - -func Test_fileSystemCache_Get(t *testing.T) { - type fields struct { - certCachePath string - inMemCache map[string]*tls.Certificate - timeSource TimeSource - } - type args struct { - cn string - } - tests := []struct { - name string - fields fields - args args - wantOk bool - }{ - { - name: "Get a miss when no cert is present", - fields: fields{ - certCachePath: os.TempDir(), - inMemCache: make(map[string]*tls.Certificate), - timeSource: NewTimeSource(), - }, - args: args{ - cnLocalhost, - }, - wantOk: false, - }, - { - name: "Get a prepared certificate from the memory cache", - fields: func() fields { - certGen := setupCertGen() - - caCrt, _ := certGen.CACert(GenerationOptions{ - CommonName: caCN, - }) - - srvCrt, _ := certGen.ServerCert(GenerationOptions{ - CommonName: cnLocalhost, - }, caCrt) - - return fields{ - certCachePath: os.TempDir(), - inMemCache: map[string]*tls.Certificate{ - cnLocalhost: srvCrt, - }, - timeSource: NewTimeSource(), - } - }(), - args: args{ - cn: cnLocalhost, - }, - wantOk: true, - }, - { - name: "Get a prepared certificate from the file system", - fields: func() fields { - certGen := setupCertGen() - - caCrt, _ := certGen.CACert(GenerationOptions{ - CommonName: "INetMock", - }) - - srvCrt, _ := certGen.ServerCert(GenerationOptions{ - CommonName: serverCN, - }, caCrt) - - pem := NewPEM(srvCrt) - if err := pem.Write(serverCN, os.TempDir()); err != nil { - panic(err) - } - - t.Cleanup(func() { - for _, f := range []string{ - path.Join(os.TempDir(), fmt.Sprintf("%s.pem", serverCN)), - path.Join(os.TempDir(), fmt.Sprintf("%s.key", serverCN)), - } { - _ = os.Remove(f) - } - }) - - return fields{ - certCachePath: os.TempDir(), - inMemCache: make(map[string]*tls.Certificate), - timeSource: NewTimeSource(), - } - }(), - args: args{ - cn: serverCN, - }, - wantOk: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - f := &fileSystemCache{ - certCachePath: tt.fields.certCachePath, - inMemCache: tt.fields.inMemCache, - timeSource: tt.fields.timeSource, - } - gotCrt, gotOk := f.Get(tt.args.cn) - - if gotOk && (gotCrt == nil || !reflect.DeepEqual(reflect.TypeOf(new(tls.Certificate)), reflect.TypeOf(gotCrt))) { - t.Errorf("Wanted propert certificate but got %v", gotCrt) - } - - if gotOk != tt.wantOk { - t.Errorf("Get() gotOk = %v, want %v", gotOk, tt.wantOk) - } - }) - } -} - -func Test_fileSystemCache_Put(t *testing.T) { - type fields struct { - certCachePath string - inMemCache map[string]*tls.Certificate - timeSource TimeSource - } - type args struct { - cert *tls.Certificate - } - tests := []struct { - name string - fields fields - args args - wantErr bool - }{ - { - name: "Want error if nil cert is passed to put", - fields: fields{ - certCachePath: os.TempDir(), - inMemCache: make(map[string]*tls.Certificate), - timeSource: NewTimeSource(), - }, - args: args{ - cert: nil, - }, - wantErr: true, - }, - { - name: "Want error if empty cert is passed to put", - fields: fields{ - certCachePath: os.TempDir(), - inMemCache: make(map[string]*tls.Certificate), - timeSource: NewTimeSource(), - }, - args: args{ - cert: &tls.Certificate{}, - }, - wantErr: true, - }, - { - name: "No error if valid cert is passed", - fields: fields{ - certCachePath: os.TempDir(), - inMemCache: make(map[string]*tls.Certificate), - timeSource: NewTimeSource(), - }, - args: args{ - cert: func() *tls.Certificate { - gen := setupCertGen() - ca, _ := gen.CACert(GenerationOptions{ - CommonName: caCN, - }) - - srvCN := fmt.Sprintf("%s-%d", cnLocalhost, time.Now().Unix()) - - t.Cleanup(func() { - for _, f := range []string{ - path.Join(os.TempDir(), fmt.Sprintf("%s.pem", srvCN)), - path.Join(os.TempDir(), fmt.Sprintf("%s.key", srvCN)), - } { - _ = os.Remove(f) - } - }) - - srvCrt, _ := gen.ServerCert(GenerationOptions{ - CommonName: srvCN, - }, ca) - return srvCrt - }(), - }, - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - f := &fileSystemCache{ - certCachePath: tt.fields.certCachePath, - inMemCache: tt.fields.inMemCache, - timeSource: tt.fields.timeSource, - } - if err := f.Put(tt.args.cert); (err != nil) != tt.wantErr { - t.Errorf("Put() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} - -func setupCertGen() Generator { - return NewDefaultGenerator(CertOptions{ - Validity: ValidityByPurpose{ - Server: ValidityDuration{ - NotBeforeRelative: serverRelativeValidity, - NotAfterRelative: serverRelativeValidity, - }, - CA: ValidityDuration{ - NotBeforeRelative: caRelativeValidity, - NotAfterRelative: caRelativeValidity, - }, - }, - }) -} diff --git a/pkg/cert/certs.go b/pkg/cert/certs.go deleted file mode 100644 index 65e744b..0000000 --- a/pkg/cert/certs.go +++ /dev/null @@ -1,54 +0,0 @@ -package cert - -import ( - "crypto/tls" - "strings" - "time" -) - -type CurveType string - -type TLSVersion string - -func (x TLSVersion) Value() string { - return strings.ToUpper(string(x)) -} - -func (x TLSVersion) TLSVersion() uint16 { - switch TLSVersion(x.Value()) { - case TLSVersionSSL3: - return tls.VersionSSL30 - case TLSVersionTLS10: - return tls.VersionTLS10 - case TLSVersionTLS11: - return tls.VersionTLS11 - case TLSVersionTLS12: - return tls.VersionTLS12 - default: - return tls.VersionTLS13 - } -} - -type File struct { - PublicKeyPath string - PrivateKeyPath string -} - -type ValidityDuration struct { - NotBeforeRelative time.Duration - NotAfterRelative time.Duration -} - -type ValidityByPurpose struct { - CA ValidityDuration - Server ValidityDuration -} - -type CertOptions struct { - RootCACert File - CertCachePath string - Curve CurveType - Validity ValidityByPurpose - IncludeInsecureCipherSuites bool - MinTLSVersion TLSVersion -} diff --git a/pkg/cert/constants.go b/pkg/cert/constants.go deleted file mode 100644 index 204c415..0000000 --- a/pkg/cert/constants.go +++ /dev/null @@ -1,14 +0,0 @@ -package cert - -const ( - CurveTypeP224 CurveType = "P224" - CurveTypeP256 CurveType = "P256" - CurveTypeP384 CurveType = "P384" - CurveTypeP521 CurveType = "P521" - CurveTypeED25519 CurveType = "ED25519" - - TLSVersionSSL3 TLSVersion = "SSL3" - TLSVersionTLS10 TLSVersion = "TLS10" - TLSVersionTLS11 TLSVersion = "TLS11" - TLSVersionTLS12 TLSVersion = "TLS12" -) diff --git a/pkg/cert/defaults.go b/pkg/cert/defaults.go deleted file mode 100644 index b7e0fda..0000000 --- a/pkg/cert/defaults.go +++ /dev/null @@ -1,20 +0,0 @@ -package cert - -import ( - "github.com/imdario/mergo" -) - -var ( - defaultOptions = &GenerationOptions{ - Country: []string{"US"}, - Locality: []string{"San Francisco"}, - Organization: []string{"INetMock"}, - StreetAddress: []string{"Golden Gate Bridge"}, - PostalCode: []string{"94016"}, - Province: []string{""}, - } -) - -func applyDefaultGenerationOptions(opts *GenerationOptions) error { - return mergo.Merge(opts, defaultOptions) -} diff --git a/pkg/cert/defaults_test.go b/pkg/cert/defaults_test.go deleted file mode 100644 index 403db98..0000000 --- a/pkg/cert/defaults_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package cert - -import ( - "reflect" - "testing" -) - -func Test_certOptionsDefaulter(t *testing.T) { - type testCase struct { - name string - arg GenerationOptions - expected GenerationOptions - } - tests := []testCase{ - { - name: "Empty options", - arg: GenerationOptions{ - CommonName: "CA", - }, - expected: GenerationOptions{ - CommonName: "CA", - Country: []string{"US"}, - Locality: []string{"San Francisco"}, - Organization: []string{"INetMock"}, - StreetAddress: []string{"Golden Gate Bridge"}, - PostalCode: []string{"94016"}, - Province: []string{""}, - }, - }, - { - name: "Options with country set", - arg: GenerationOptions{ - CommonName: "CA", - Country: []string{"DE"}, - }, - expected: GenerationOptions{ - CommonName: "CA", - Country: []string{"DE"}, - Locality: []string{"San Francisco"}, - Organization: []string{"INetMock"}, - StreetAddress: []string{"Golden Gate Bridge"}, - PostalCode: []string{"94016"}, - Province: []string{""}, - }, - }, - { - name: "Options with organization set set", - arg: GenerationOptions{ - CommonName: "CA", - Organization: []string{"inetmock"}, - }, - expected: GenerationOptions{ - CommonName: "CA", - Country: []string{"US"}, - Locality: []string{"San Francisco"}, - Organization: []string{"inetmock"}, - StreetAddress: []string{"Golden Gate Bridge"}, - PostalCode: []string{"94016"}, - Province: []string{""}, - }, - }, - } - scenario := func(tt testCase) func(t *testing.T) { - return func(t *testing.T) { - if err := applyDefaultGenerationOptions(&tt.arg); err != nil { - t.Errorf("applyDefaultGenerationOptions() error = %v", err) - } - if !reflect.DeepEqual(tt.expected, tt.arg) { - t.Errorf("Apply defaulter expected=%v got=%v", tt.expected, tt.arg) - } - } - } - for _, tt := range tests { - t.Run(tt.name, scenario(tt)) - } -} diff --git a/pkg/cert/generator.go b/pkg/cert/generator.go deleted file mode 100644 index d57f872..0000000 --- a/pkg/cert/generator.go +++ /dev/null @@ -1,179 +0,0 @@ -package cert - -import ( - "crypto/ecdsa" - "crypto/ed25519" - "crypto/rand" - "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "math/big" - "net" -) - -type GenerationOptions struct { - CommonName string - Organization []string - OrganizationalUnit []string - IPAddresses []net.IP - DNSNames []string - Country []string - Province []string - Locality []string - StreetAddress []string - PostalCode []string -} - -type Generator interface { - CACert(options GenerationOptions) (*tls.Certificate, error) - ServerCert(options GenerationOptions, ca *tls.Certificate) (*tls.Certificate, error) -} - -func NewDefaultGenerator(options CertOptions) Generator { - return NewGenerator(options, NewTimeSource(), defaultKeyProvider(options)) -} - -func NewGenerator(options CertOptions, source TimeSource, provider KeyProvider) Generator { - return &generator{ - options: options, - provider: provider, - timeSource: source, - } -} - -type generator struct { - options CertOptions - provider KeyProvider - timeSource TimeSource - outDir string -} - -func (g *generator) privateKey() (key interface{}, err error) { - if g.provider != nil { - return g.provider() - } else { - return defaultKeyProvider(g.options)() - } -} - -func (g *generator) ServerCert(options GenerationOptions, ca *tls.Certificate) (cert *tls.Certificate, err error) { - applyDefaultGenerationOptions(&options) - var serialNumber *big.Int - if serialNumber, err = generateSerialNumber(); err != nil { - return - } - - var privateKey interface{} - if privateKey, err = g.privateKey(); err != nil { - return - } - - template := x509.Certificate{ - SerialNumber: serialNumber, - Subject: pkix.Name{ - CommonName: options.CommonName, - Organization: options.Organization, - OrganizationalUnit: options.OrganizationalUnit, - Country: options.Country, - Province: options.Province, - Locality: options.Locality, - StreetAddress: options.StreetAddress, - PostalCode: options.PostalCode, - }, - IPAddresses: options.IPAddresses, - DNSNames: options.DNSNames, - NotBefore: g.timeSource.UTCNow().Add(-g.options.Validity.Server.NotBeforeRelative), - NotAfter: g.timeSource.UTCNow().Add(g.options.Validity.Server.NotAfterRelative), - KeyUsage: x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, - } - var caCrt *x509.Certificate - caCrt, err = x509.ParseCertificate(ca.Certificate[0]) - - var derBytes []byte - if derBytes, err = x509.CreateCertificate(rand.Reader, &template, caCrt, publicKey(privateKey), ca.PrivateKey); err != nil { - return - } - - var privateKeyBytes []byte - if privateKeyBytes, err = x509.MarshalPKCS8PrivateKey(privateKey); err != nil { - return - } - - if cert, err = parseCert(derBytes, privateKeyBytes); err != nil { - return - } - - return -} - -func (g generator) CACert(options GenerationOptions) (crt *tls.Certificate, err error) { - applyDefaultGenerationOptions(&options) - - var privateKey interface{} - var serialNumber *big.Int - if serialNumber, err = generateSerialNumber(); err != nil { - return - } - - if privateKey, err = g.privateKey(); err != nil { - return - } - - template := x509.Certificate{ - SerialNumber: serialNumber, - Subject: pkix.Name{ - CommonName: options.CommonName, - Organization: options.Organization, - Country: options.Country, - Province: options.Province, - Locality: options.Locality, - StreetAddress: options.StreetAddress, - PostalCode: options.PostalCode, - }, - IsCA: true, - NotBefore: g.timeSource.UTCNow().Add(-g.options.Validity.CA.NotBeforeRelative), - NotAfter: g.timeSource.UTCNow().Add(g.options.Validity.CA.NotAfterRelative), - KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, - BasicConstraintsValid: true, - } - - var derBytes []byte - if derBytes, err = x509.CreateCertificate(rand.Reader, &template, &template, publicKey(privateKey), privateKey); err != nil { - return - } - - var privateKeyBytes []byte - if privateKeyBytes, err = x509.MarshalPKCS8PrivateKey(privateKey); err != nil { - return - } - - crt, err = parseCert(derBytes, privateKeyBytes) - - return -} - -func generateSerialNumber() (*big.Int, error) { - serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) - return rand.Int(rand.Reader, serialNumberLimit) -} - -func publicKey(privateKey interface{}) interface{} { - switch k := privateKey.(type) { - case *ecdsa.PrivateKey: - return &k.PublicKey - case ed25519.PrivateKey: - return k.Public().(ed25519.PublicKey) - default: - return nil - } -} - -func parseCert(derBytes []byte, privateKeyBytes []byte) (*tls.Certificate, error) { - pemEncodedPublicKey := pem.EncodeToMemory(&pem.Block{Type: certificateBlockType, Bytes: derBytes}) - pemEncodedPrivateKey := pem.EncodeToMemory(&pem.Block{Type: privateKeyBlockType, Bytes: privateKeyBytes}) - cert, err := tls.X509KeyPair(pemEncodedPublicKey, pemEncodedPrivateKey) - return &cert, err -} diff --git a/pkg/cert/options.go b/pkg/cert/options.go deleted file mode 100644 index ad59aa4..0000000 --- a/pkg/cert/options.go +++ /dev/null @@ -1,10 +0,0 @@ -package cert - -/*func init() { - config.AddDefaultValue(certCachePathConfigKey, os.TempDir()) - config.AddDefaultValue(ecdsaCurveConfigKey, string(CurveTypeED25519)) - config.AddDefaultValue(caCertValidityNotBeforeKey, defaultCAValidityDuration) - config.AddDefaultValue(caCertValidityNotAfterKey, defaultCAValidityDuration) - config.AddDefaultValue(serverCertValidityNotBeforeKey, defaultServerValidityDuration) - config.AddDefaultValue(serverCertValidityNotAfterKey, defaultServerValidityDuration) -}*/ diff --git a/pkg/cert/options_test.go b/pkg/cert/options_test.go deleted file mode 100644 index cecc945..0000000 --- a/pkg/cert/options_test.go +++ /dev/null @@ -1,130 +0,0 @@ -package cert - -import ( - "os" - "strings" - "testing" - "time" - - "github.com/spf13/viper" -) - -func readViper(cfg string) *viper.Viper { - vpr := viper.New() - vpr.SetConfigType("yaml") - if err := vpr.ReadConfig(strings.NewReader(cfg)); err != nil { - panic(err) - } - return vpr -} - -func Test_loadFromConfig(t *testing.T) { - type args struct { - config *viper.Viper - } - tests := []struct { - name string - args args - want CertOptions - wantErr bool - }{ - { - name: "Parse valid TLS configuration", - wantErr: false, - args: args{ - config: readViper(` -tls: - ecdsaCurve: P256 - validity: - ca: - notBeforeRelative: 17520h - notAfterRelative: 17520h - server: - NotBeforeRelative: 168h - NotAfterRelative: 168h - rootCaCert: - publicKey: ./ca.pem - privateKey: ./ca.key - certCachePath: /tmp/inetmock/ -`), - }, - want: CertOptions{ - RootCACert: File{ - PublicKeyPath: "./ca.pem", - PrivateKeyPath: "./ca.key", - }, - CertCachePath: "/tmp/inetmock/", - Curve: CurveTypeP256, - Validity: ValidityByPurpose{ - CA: ValidityDuration{ - NotBeforeRelative: 17520 * time.Hour, - NotAfterRelative: 17520 * time.Hour, - }, - Server: ValidityDuration{ - NotBeforeRelative: 168 * time.Hour, - NotAfterRelative: 168 * time.Hour, - }, - }, - }, - }, - { - name: "Get an error if CA public key path is missing", - args: args{ - readViper(` -tls: - rootCaCert: - privateKey: ./ca.key -`), - }, - want: CertOptions{}, - wantErr: true, - }, - { - name: "Get an error if CA private key path is missing", - args: args{ - readViper(` -tls: - rootCaCert: - publicKey: ./ca.pem -`), - }, - want: CertOptions{}, - wantErr: true, - }, - { - name: "Get default options if all required fields are set", - args: args{ - readViper(` -tls: - rootCaCert: - publicKey: ./ca.pem - privateKey: ./ca.key -`), - }, - want: CertOptions{ - RootCACert: File{ - PublicKeyPath: "./ca.pem", - PrivateKeyPath: "./ca.key", - }, - CertCachePath: os.TempDir(), - Curve: CurveTypeED25519, - Validity: ValidityByPurpose{ - CA: ValidityDuration{ - NotBeforeRelative: 17520 * time.Hour, - NotAfterRelative: 17520 * time.Hour, - }, - Server: ValidityDuration{ - NotBeforeRelative: 168 * time.Hour, - NotAfterRelative: 168 * time.Hour, - }, - }, - }, - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - - }) - } -} diff --git a/pkg/cert/pem.go b/pkg/cert/pem.go deleted file mode 100644 index a3058fc..0000000 --- a/pkg/cert/pem.go +++ /dev/null @@ -1,80 +0,0 @@ -package cert - -import ( - "crypto/tls" - "crypto/x509" - "encoding/pem" - "errors" - "fmt" - "os" - "path/filepath" - - "gitlab.com/inetmock/inetmock/pkg/path" -) - -const ( - certificateBlockType = "CERTIFICATE" - privateKeyBlockType = "PRIVATE KEY" -) - -type PEM interface { - Cert() *tls.Certificate - Write(cn string, outDir string) error - Read(cn string, inDir string) error - ReadFrom(pubKeyPath, privateKeyPath string) error -} - -func NewPEM(crt *tls.Certificate) PEM { - return &pemCrt{ - crt: crt, - } -} - -type pemCrt struct { - crt *tls.Certificate -} - -func (p *pemCrt) Cert() *tls.Certificate { - return p.crt -} - -func (p pemCrt) Write(cn string, outDir string) (err error) { - - var certOut *os.File - if certOut, err = os.Create(filepath.Join(outDir, fmt.Sprintf("%s.pem", cn))); err != nil { - return - } - defer certOut.Close() - if err = pem.Encode(certOut, &pem.Block{Type: certificateBlockType, Bytes: p.crt.Certificate[0]}); err != nil { - return - } - - var keyOut *os.File - if keyOut, err = os.OpenFile(filepath.Join(outDir, fmt.Sprintf("%s.key", cn)), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600); err != nil { - return - } - var privKeyBytes []byte - privKeyBytes, err = x509.MarshalPKCS8PrivateKey(p.crt.PrivateKey) - err = pem.Encode(keyOut, &pem.Block{Type: privateKeyBlockType, Bytes: privKeyBytes}) - return -} - -func (p *pemCrt) Read(cn string, inDir string) error { - certPath := filepath.Join(inDir, fmt.Sprintf("%s.pem", cn)) - keyPath := filepath.Join(inDir, fmt.Sprintf("%s.key", cn)) - - return p.ReadFrom(certPath, keyPath) -} - -func (p *pemCrt) ReadFrom(pubKeyPath, privateKeyPath string) (err error) { - var tlsCrt tls.Certificate - if path.FileExists(pubKeyPath) && path.FileExists(privateKeyPath) { - if tlsCrt, err = tls.LoadX509KeyPair(pubKeyPath, privateKeyPath); err == nil { - p.crt = &tlsCrt - } - } else { - err = errors.New("either public or private key file do not exist") - } - - return -} diff --git a/pkg/cert/store.go b/pkg/cert/store.go deleted file mode 100644 index a9a8391..0000000 --- a/pkg/cert/store.go +++ /dev/null @@ -1,164 +0,0 @@ -package cert - -import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/tls" - "crypto/x509" - "net" - - "gitlab.com/inetmock/inetmock/pkg/logging" - "go.uber.org/zap" -) - -const ( - ipv4Loopback = "127.0.0.1" -) - -var ( - defaultKeyProvider = func(options CertOptions) func() (key interface{}, err error) { - return func() (key interface{}, err error) { - return privateKeyForCurve(options) - } - } -) - -type KeyProvider func() (key interface{}, err error) - -type Store interface { - CACert() *tls.Certificate - GetCertificate(serverName string, ip string) (*tls.Certificate, error) - TLSConfig() *tls.Config -} - -func NewDefaultStore( - options CertOptions, - logger logging.Logger, -) (Store, error) { - timeSource := NewTimeSource() - return NewStore( - options, - NewFileSystemCache(options.CertCachePath, timeSource), - NewDefaultGenerator(options), - logger, - ) -} - -func NewStore( - options CertOptions, - cache Cache, - generator Generator, - logger logging.Logger, -) (Store, error) { - store := &store{ - options: options, - cache: cache, - generator: generator, - logger: logger, - } - - if err := store.loadCACert(); err != nil { - return nil, err - } - - return store, nil -} - -type store struct { - options CertOptions - caCert *tls.Certificate - cache Cache - timeSource TimeSource - generator Generator - logger logging.Logger -} - -func (s *store) TLSConfig() *tls.Config { - rootCaPool := x509.NewCertPool() - rootCaPubKey, _ := x509.ParseCertificate(s.caCert.Certificate[0]) - rootCaPool.AddCert(rootCaPubKey) - - suites := make([]uint16, 0) - - for _, suite := range tls.CipherSuites() { - suites = append(suites, suite.ID) - } - - if s.options.IncludeInsecureCipherSuites { - for _, suite := range tls.InsecureCipherSuites() { - suites = append(suites, suite.ID) - } - } - - return &tls.Config{ - CipherSuites: suites, - MinVersion: s.options.MinTLSVersion.TLSVersion(), - CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256}, - PreferServerCipherSuites: true, - GetCertificate: func(info *tls.ClientHelloInfo) (cert *tls.Certificate, err error) { - var localIp string - if localIp, err = extractIPFromAddress(info.Conn.LocalAddr().String()); err != nil { - localIp = ipv4Loopback - } - - if cert, err = s.GetCertificate(info.ServerName, localIp); err != nil { - s.logger.Error( - "error while resolving certificate", - zap.String("serverName", info.ServerName), - zap.String("localAddr", localIp), - zap.Error(err), - ) - } - - return - }, - RootCAs: rootCaPool, - } -} - -func (s *store) loadCACert() (err error) { - pemCrt := NewPEM(nil) - if err = pemCrt.ReadFrom(s.options.RootCACert.PublicKeyPath, s.options.RootCACert.PrivateKeyPath); err != nil { - return - } - s.caCert = pemCrt.Cert() - return -} - -func (s *store) CACert() *tls.Certificate { - return s.caCert -} - -func (s *store) GetCertificate(serverName string, ip string) (cert *tls.Certificate, err error) { - if crt, ok := s.cache.Get(serverName); ok { - return crt, nil - } - - if cert, err = s.generator.ServerCert(GenerationOptions{ - CommonName: serverName, - DNSNames: []string{serverName}, - IPAddresses: []net.IP{net.ParseIP(ip)}, - }, s.caCert); err == nil { - s.cache.Put(cert) - } - - return -} - -func privateKeyForCurve(options CertOptions) (privateKey interface{}, err error) { - switch options.Curve { - case CurveTypeP224: - privateKey, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader) - case CurveTypeP256: - privateKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - case CurveTypeP384: - privateKey, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader) - case CurveTypeP521: - privateKey, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader) - default: - privateKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - } - - return -} diff --git a/pkg/cert/time_source.go b/pkg/cert/time_source.go deleted file mode 100644 index 3b98162..0000000 --- a/pkg/cert/time_source.go +++ /dev/null @@ -1,20 +0,0 @@ -//go:generate mockgen -source=$GOFILE -destination=./../../internal/mock/cert/time_source.mock.go -package=cert_mock - -package cert - -import "time" - -type TimeSource interface { - UTCNow() time.Time -} - -func NewTimeSource() TimeSource { - return &defaultTimeSource{} -} - -type defaultTimeSource struct { -} - -func (d defaultTimeSource) UTCNow() time.Time { - return time.Now().UTC() -} diff --git a/pkg/health/api.go b/pkg/health/api.go deleted file mode 100644 index 9bf882f..0000000 --- a/pkg/health/api.go +++ /dev/null @@ -1,36 +0,0 @@ -package health - -import ( - "errors" -) - -type Status uint8 - -const ( - HEALTHY Status = 0 - INITIALIZING Status = 1 - UNHEALTHY Status = 2 - UNKNOWN Status = 3 -) - -type CheckResult struct { - Status Status - Message string -} - -type Result struct { - Status Status - Components map[string]CheckResult -} - -type Check func() CheckResult - -var ( - ErrCheckForComponentAlreadyRegistered = errors.New("a check for the requested component is already registered") -) - -func New() Checker { - return &checker{ - componentChecks: map[string]Check{}, - } -} diff --git a/pkg/health/checker.go b/pkg/health/checker.go deleted file mode 100644 index e72d5b0..0000000 --- a/pkg/health/checker.go +++ /dev/null @@ -1,45 +0,0 @@ -package health - -import "fmt" - -type checker struct { - componentChecks map[string]Check -} - -type Checker interface { - RegisterCheck(component string, check Check) error - IsHealthy() Result -} - -func (c *checker) RegisterCheck(component string, check Check) error { - if _, exists := c.componentChecks[component]; exists { - return fmt.Errorf("component: %s: %w", component, ErrCheckForComponentAlreadyRegistered) - } - - c.componentChecks[component] = check - return nil -} - -func (c *checker) IsHealthy() (r Result) { - r.Status = HEALTHY - r.Components = make(map[string]CheckResult) - for component, componentCheck := range c.componentChecks { - r.Components[component] = componentCheck() - r.Status = max(r.Components[component].Status, r.Status) - } - return -} - -func max(s1, s2 Status) Status { - var max Status - if s1 > s2 { - max = s1 - } else { - max = s2 - } - - if max > UNHEALTHY { - return UNHEALTHY - } - return max -} diff --git a/pkg/health/checker_test.go b/pkg/health/checker_test.go deleted file mode 100644 index 7e9dd3b..0000000 --- a/pkg/health/checker_test.go +++ /dev/null @@ -1,156 +0,0 @@ -package health - -import ( - "reflect" - "testing" -) - -func Test_checker_IsHealthy(t *testing.T) { - type fields struct { - componentChecks map[string]Check - } - tests := []struct { - name string - fields fields - wantR Result - }{ - { - name: "No checks registered expect HEALTHY", - fields: fields{ - componentChecks: map[string]Check{}, - }, - wantR: Result{ - Status: HEALTHY, - Components: map[string]CheckResult{}, - }, - }, - { - name: "Return only HEALTHY result expect HEALTHY", - fields: fields{ - componentChecks: map[string]Check{ - "asdf": func() CheckResult { - return CheckResult{ - Status: HEALTHY, - Message: "", - } - }, - }, - }, - wantR: Result{ - Status: HEALTHY, - Components: map[string]CheckResult{ - "asdf": { - Status: HEALTHY, - Message: "", - }, - }, - }, - }, - { - name: "Return HEALTHY and INITIALIZING result expect INITIALIZING", - fields: fields{ - componentChecks: map[string]Check{ - "asdf": func() CheckResult { - return CheckResult{ - Status: HEALTHY, - Message: "", - } - }, - "qwert": func() CheckResult { - return CheckResult{ - Status: INITIALIZING, - Message: "", - } - }, - }, - }, - wantR: Result{ - Status: INITIALIZING, - Components: map[string]CheckResult{ - "asdf": { - Status: HEALTHY, - Message: "", - }, - "qwert": { - Status: INITIALIZING, - Message: "", - }, - }, - }, - }, - { - name: "Return HEALTHY AND UNHEALTHY result expect UNHEALTHY", - fields: fields{ - componentChecks: map[string]Check{ - "asdf": func() CheckResult { - return CheckResult{ - Status: UNHEALTHY, - Message: "", - } - }, - "qwert": func() CheckResult { - return CheckResult{ - Status: HEALTHY, - Message: "", - } - }, - }, - }, - wantR: Result{ - Status: UNHEALTHY, - Components: map[string]CheckResult{ - "asdf": { - Status: UNHEALTHY, - Message: "", - }, - "qwert": { - Status: HEALTHY, - Message: "", - }, - }, - }, - }, - { - name: "Return HEALTHY AND UNKNOWN result expect UNHEALTHY", - fields: fields{ - componentChecks: map[string]Check{ - "asdf": func() CheckResult { - return CheckResult{ - Status: UNKNOWN, - Message: "", - } - }, - "qwert": func() CheckResult { - return CheckResult{ - Status: HEALTHY, - Message: "", - } - }, - }, - }, - wantR: Result{ - Status: UNHEALTHY, - Components: map[string]CheckResult{ - "asdf": { - Status: UNKNOWN, - Message: "", - }, - "qwert": { - Status: HEALTHY, - Message: "", - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := &checker{ - componentChecks: tt.fields.componentChecks, - } - if gotR := c.IsHealthy(); !reflect.DeepEqual(gotR, tt.wantR) { - t.Errorf("IsHealthy() = %v, want %v", gotR, tt.wantR) - } - }) - } -} diff --git a/pkg/health/checks.go b/pkg/health/checks.go deleted file mode 100644 index e56d9fd..0000000 --- a/pkg/health/checks.go +++ /dev/null @@ -1,18 +0,0 @@ -package health - -func StaticResultCheck(status Status) Check { - return func() CheckResult { - return CheckResult{ - Status: status, - } - } -} - -func StaticResultCheckWithMessage(status Status, message string) Check { - return func() CheckResult { - return CheckResult{ - Status: status, - Message: message, - } - } -} diff --git a/pkg/logging/factory.go b/pkg/logging/factory.go deleted file mode 100644 index b42a286..0000000 --- a/pkg/logging/factory.go +++ /dev/null @@ -1,57 +0,0 @@ -package logging - -import ( - "strings" - "testing" - - "go.uber.org/zap" - "go.uber.org/zap/zapcore" -) - -var ( - loggingConfig = zap.NewProductionConfig() -) - -func ConfigureLogging( - level zap.AtomicLevel, - developmentLogging bool, - initialFields map[string]interface{}, -) { - loggingConfig.Level = level - loggingConfig.Development = developmentLogging - if initialFields != nil { - loggingConfig.InitialFields = initialFields - } -} - -func ParseLevel(levelString string) zap.AtomicLevel { - switch strings.ToLower(levelString) { - case "debug": - return zap.NewAtomicLevelAt(zapcore.DebugLevel) - case "info": - return zap.NewAtomicLevelAt(zapcore.InfoLevel) - case "warn": - return zap.NewAtomicLevelAt(zapcore.WarnLevel) - case "error": - return zap.NewAtomicLevelAt(zapcore.ErrorLevel) - case "fatal": - return zap.NewAtomicLevelAt(zapcore.FatalLevel) - default: - return zap.NewAtomicLevelAt(zapcore.InfoLevel) - } -} - -func CreateLogger() (Logger, error) { - if zapLogger, err := loggingConfig.Build(zap.AddCallerSkip(2)); err != nil { - return nil, err - } else { - return NewLogger(zapLogger), nil - } -} - -func CreateTestLogger(tb testing.TB) Logger { - return &testLogger{ - tb: tb, - encoder: zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig()), - } -} diff --git a/pkg/logging/factory_test.go b/pkg/logging/factory_test.go deleted file mode 100644 index e5b69a7..0000000 --- a/pkg/logging/factory_test.go +++ /dev/null @@ -1,167 +0,0 @@ -package logging - -import ( - "reflect" - "testing" - - "go.uber.org/zap" -) - -func TestParseLevel(t *testing.T) { - type args struct { - levelString string - } - tests := []struct { - name string - args args - want zap.AtomicLevel - }{ - { - name: "Test parse DEBUG level", - args: args{ - levelString: "DEBUG", - }, - want: zap.NewAtomicLevelAt(zap.DebugLevel), - }, - { - name: "Test parse DeBuG level", - args: args{ - levelString: "DeBuG", - }, - want: zap.NewAtomicLevelAt(zap.DebugLevel), - }, - { - name: "Test parse INFO level", - args: args{ - levelString: "INFO", - }, - want: zap.NewAtomicLevelAt(zap.InfoLevel), - }, - { - name: "Test parse InFo level", - args: args{ - levelString: "InFo", - }, - want: zap.NewAtomicLevelAt(zap.InfoLevel), - }, - { - name: "Test parse WARN level", - args: args{ - levelString: "WARN", - }, - want: zap.NewAtomicLevelAt(zap.WarnLevel), - }, - { - name: "Test parse WaRn level", - args: args{ - levelString: "WaRn", - }, - want: zap.NewAtomicLevelAt(zap.WarnLevel), - }, - { - name: "Test parse ERROR level", - args: args{ - levelString: "ERROR", - }, - want: zap.NewAtomicLevelAt(zap.ErrorLevel), - }, - { - name: "Test parse ErRoR level", - args: args{ - levelString: "ErRoR", - }, - want: zap.NewAtomicLevelAt(zap.ErrorLevel), - }, - { - name: "Test parse FATAL level", - args: args{ - levelString: "FATAL", - }, - want: zap.NewAtomicLevelAt(zap.FatalLevel), - }, - { - name: "Test parse FaTaL level", - args: args{ - levelString: "FaTaL", - }, - want: zap.NewAtomicLevelAt(zap.FatalLevel), - }, - { - name: "Fallback to INFO level if unknown level", - args: args{ - levelString: "asdf23423", - }, - want: zap.NewAtomicLevelAt(zap.InfoLevel), - }, - { - name: "Fallback to INFO level if no level", - args: args{ - levelString: "", - }, - want: zap.NewAtomicLevelAt(zap.InfoLevel), - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := ParseLevel(tt.args.levelString); !reflect.DeepEqual(got, tt.want) { - t.Errorf("ParseLevel() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestConfigureLogging(t *testing.T) { - type args struct { - level zap.AtomicLevel - developmentLogging bool - initialFields map[string]interface{} - } - tests := []struct { - name string - args args - }{ - { - name: "Test configure defaults", - args: args{}, - }, - { - name: "Test configure with initialFields", - args: args{ - initialFields: map[string]interface{}{ - "asdf": "hello, World", - }, - }, - }, - { - name: "Test configure development logging enabled", - args: args{ - developmentLogging: true, - }, - }, - { - name: "Test configure log level", - args: args{ - level: zap.NewAtomicLevelAt(zap.FatalLevel), - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - ConfigureLogging(tt.args.level, tt.args.developmentLogging, tt.args.initialFields) - if loggingConfig.Development != tt.args.developmentLogging { - t.Errorf("loggingConfig.Development = %t, want %t", loggingConfig.Development, tt.args.developmentLogging) - return - } - - if loggingConfig.Level != tt.args.level { - t.Errorf("loggingConfig.Level = %v, want %v", loggingConfig.Level, tt.args.level) - return - } - - if tt.args.initialFields != nil && !reflect.DeepEqual(loggingConfig.InitialFields, tt.args.initialFields) { - t.Errorf("loggingConfig.InitialFields = %v, want %v", loggingConfig.InitialFields, tt.args.initialFields) - return - } - }) - } -} diff --git a/pkg/logging/logger.go b/pkg/logging/logger.go deleted file mode 100644 index c6e045a..0000000 --- a/pkg/logging/logger.go +++ /dev/null @@ -1,60 +0,0 @@ -//go:generate mockgen -source=$GOFILE -destination=./../../internal/mock/logging/logger.mock.go -package=logging_mock -package logging - -import "go.uber.org/zap" - -type Logger interface { - Named(s string) Logger - With(fields ...zap.Field) Logger - Debug(msg string, fields ...zap.Field) - Info(msg string, fields ...zap.Field) - Warn(msg string, fields ...zap.Field) - Error(msg string, fields ...zap.Field) - Panic(msg string, fields ...zap.Field) - Fatal(msg string, fields ...zap.Field) - Sync() error -} - -type logger struct { - underlyingLogger *zap.Logger -} - -func NewLogger(underlyingLogger *zap.Logger) *logger { - return &logger{underlyingLogger: underlyingLogger} -} - -func (l logger) Named(s string) Logger { - return NewLogger(l.underlyingLogger.Named(s)) -} - -func (l logger) With(fields ...zap.Field) Logger { - return NewLogger(l.underlyingLogger.With(fields...)) -} - -func (l logger) Debug(msg string, fields ...zap.Field) { - l.underlyingLogger.Debug(msg, fields...) -} - -func (l logger) Info(msg string, fields ...zap.Field) { - l.underlyingLogger.Info(msg, fields...) -} - -func (l logger) Warn(msg string, fields ...zap.Field) { - l.underlyingLogger.Warn(msg, fields...) -} - -func (l logger) Error(msg string, fields ...zap.Field) { - l.underlyingLogger.Error(msg, fields...) -} - -func (l logger) Panic(msg string, fields ...zap.Field) { - l.underlyingLogger.Panic(msg, fields...) -} - -func (l logger) Fatal(msg string, fields ...zap.Field) { - l.underlyingLogger.Fatal(msg, fields...) -} - -func (l logger) Sync() error { - return l.underlyingLogger.Sync() -} diff --git a/pkg/logging/test_logger.go b/pkg/logging/test_logger.go deleted file mode 100644 index 263f0c5..0000000 --- a/pkg/logging/test_logger.go +++ /dev/null @@ -1,133 +0,0 @@ -package logging - -import ( - "runtime" - "runtime/debug" - "testing" - "time" - - "go.uber.org/zap" - "go.uber.org/zap/zapcore" -) - -type testLogger struct { - name string - fields []zap.Field - tb testing.TB - encoder zapcore.Encoder -} - -func (t testLogger) Named(s string) Logger { - return testLogger{ - encoder: t.encoder, - name: s, - tb: t.tb, - fields: t.fields, - } -} - -func (t testLogger) With(fields ...zap.Field) Logger { - return &testLogger{ - encoder: t.encoder, - name: t.name, - fields: append(t.fields, fields...), - tb: t.tb, - } -} - -func (t testLogger) Debug(msg string, fields ...zap.Field) { - t.tb.Helper() - buf, err := t.encoder.EncodeEntry(zapcore.Entry{ - Level: zapcore.DebugLevel, - Time: time.Now(), - LoggerName: t.name, - Message: msg, - Caller: zapcore.NewEntryCaller(runtime.Caller(2)), - }, append(t.fields, fields...)) - - if err == nil { - t.tb.Log(buf.String()) - } -} - -func (t testLogger) Info(msg string, fields ...zap.Field) { - t.tb.Helper() - buf, err := t.encoder.EncodeEntry(zapcore.Entry{ - Level: zapcore.InfoLevel, - Time: time.Now(), - LoggerName: t.name, - Message: msg, - Caller: zapcore.NewEntryCaller(runtime.Caller(2)), - }, append(t.fields, fields...)) - - if err == nil { - t.tb.Log(buf.String()) - } -} - -func (t testLogger) Warn(msg string, fields ...zap.Field) { - t.tb.Helper() - buf, err := t.encoder.EncodeEntry(zapcore.Entry{ - Level: zapcore.WarnLevel, - Time: time.Now(), - LoggerName: t.name, - Message: msg, - Caller: zapcore.NewEntryCaller(runtime.Caller(2)), - }, append(t.fields, fields...)) - - if err == nil { - t.tb.Log(buf.String()) - } -} - -func (t testLogger) Error(msg string, fields ...zap.Field) { - t.tb.Helper() - buf, err := t.encoder.EncodeEntry(zapcore.Entry{ - Level: zapcore.ErrorLevel, - Time: time.Now(), - LoggerName: t.name, - Message: msg, - Caller: zapcore.NewEntryCaller(runtime.Caller(2)), - Stack: string(debug.Stack()), - }, append(t.fields, fields...)) - - if err == nil { - t.tb.Log(buf.String()) - } -} - -func (t testLogger) Panic(msg string, fields ...zap.Field) { - t.tb.Helper() - buf, err := t.encoder.EncodeEntry(zapcore.Entry{ - Level: zapcore.PanicLevel, - Time: time.Now(), - LoggerName: t.name, - Message: msg, - Caller: zapcore.NewEntryCaller(runtime.Caller(2)), - Stack: string(debug.Stack()), - }, append(t.fields, fields...)) - - if err == nil { - t.tb.Error(buf.String()) - } -} - -func (t testLogger) Fatal(msg string, fields ...zap.Field) { - t.tb.Helper() - buf, err := t.encoder.EncodeEntry(zapcore.Entry{ - Level: zapcore.FatalLevel, - Time: time.Now(), - LoggerName: t.name, - Message: msg, - Caller: zapcore.NewEntryCaller(runtime.Caller(2)), - Stack: string(debug.Stack()), - }, append(t.fields, fields...)) - - if err == nil { - t.tb.Error(buf.String()) - } -} - -func (t testLogger) Sync() error { - return nil -} diff --git a/pkg/metrics/metrics_factory.go b/pkg/metrics/metrics_factory.go deleted file mode 100644 index 28cc117..0000000 --- a/pkg/metrics/metrics_factory.go +++ /dev/null @@ -1,47 +0,0 @@ -package metrics - -import ( - "github.com/prometheus/client_golang/prometheus" -) - -const ( - metricNamespace = "inetmock" -) - -func Gauge(subsystem, name, help string, labelNames ...string) (*prometheus.GaugeVec, error) { - vec := prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Namespace: metricNamespace, - Subsystem: subsystem, - Name: name, - Help: help, - }, labelNames) - return vec, prometheus.Register(vec) -} - -func Histogram(subsystem, name, help string, buckets []float64, labelNames ...string) (*prometheus.HistogramVec, error) { - vec := prometheus.NewHistogramVec( - prometheus.HistogramOpts{ - Namespace: metricNamespace, - Subsystem: subsystem, - Name: name, - Help: help, - Buckets: buckets, - }, - labelNames, - ) - return vec, prometheus.Register(vec) -} - -func Counter(subsystem, name, help string, labelNames ...string) (*prometheus.CounterVec, error) { - vec := prometheus.NewCounterVec( - prometheus.CounterOpts{ - Namespace: metricNamespace, - Subsystem: subsystem, - Name: name, - Help: help, - }, - labelNames, - ) - return vec, prometheus.Register(vec) -} diff --git a/pkg/path/helpers.go b/pkg/path/helpers.go deleted file mode 100644 index c19d57c..0000000 --- a/pkg/path/helpers.go +++ /dev/null @@ -1,26 +0,0 @@ -package path - -import ( - "os" -) - -func WorkingDirectory() (cwd string) { - cwd, _ = os.Getwd() - return -} - -func FileExists(filename string) bool { - info, err := os.Stat(filename) - if os.IsNotExist(err) { - return false - } - return !info.IsDir() -} - -func DirExists(dirPath string) bool { - info, err := os.Stat(dirPath) - if os.IsNotExist(err) { - return false - } - return info.IsDir() -} diff --git a/pkg/path/helpers_test.go b/pkg/path/helpers_test.go deleted file mode 100644 index de27487..0000000 --- a/pkg/path/helpers_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package path - -import ( - "fmt" - "io/ioutil" - "math/rand" - "os" - "path" - "testing" -) - -func TestFileExists(t *testing.T) { - tmpFile, err := ioutil.TempFile("", "inetmock") - - if err != nil { - t.Errorf("failed to create temp file: %v", err) - } - defer func() { - tmpFile.Close() - os.Remove(tmpFile.Name()) - }() - - type args struct { - filename string - } - tests := []struct { - name string - args args - want bool - }{ - { - name: "Ensure temp file exists", - want: true, - args: args{ - filename: tmpFile.Name(), - }, - }, - { - name: "Ensure random file name does not exist", - want: false, - args: args{ - filename: path.Join(os.TempDir(), fmt.Sprintf("asdf-%d", rand.Uint32())), - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := FileExists(tt.args.filename); got != tt.want { - t.Errorf("FileExists() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/pkg/wait/wg.go b/pkg/wait/wg.go deleted file mode 100644 index 11adf3f..0000000 --- a/pkg/wait/wg.go +++ /dev/null @@ -1,14 +0,0 @@ -package wait - -import "sync" - -func ForWaitGroupDone(wg *sync.WaitGroup) <-chan struct{} { - done := make(chan struct{}) - - go func(wg *sync.WaitGroup) { - wg.Wait() - close(done) - }(wg) - - return done -} diff --git a/proto/audit/details/dns_details.proto b/proto/audit/details/dns_details.proto new file mode 100644 index 0000000..f820840 --- /dev/null +++ b/proto/audit/details/dns_details.proto @@ -0,0 +1,43 @@ +syntax = "proto3"; + +option go_package = "gitlab.com/inetmock/inetmock/pkg/audit/details"; +option java_multiple_files = true; +option java_package = "com.github.baez90.inetmock.audit.details"; +option java_outer_classname = "HandlerEventProto"; +option csharp_namespace = "INetMock.Client.Audit.Details"; + +package inetmock.audit.details; + +enum DNSOpCode { + Query = 0; + Status = 2; + Notify = 4; + Update = 5; +} + +enum ResourceRecordType { + UnknownRR = 0; + A = 1; + NS = 2; + CNAME = 5; + SOA = 6; + PTR = 12; + HINFO = 13; + MINFO = 14; + MX = 15; + TXT = 16; + RP = 17; + AAAA = 28; + SRV = 33; + NAPTR = 35; +} + +message DNSQuestionEntity { + ResourceRecordType type = 1; + string name = 2; +} + +message DNSDetailsEntity { + DNSOpCode opcode = 1; + repeated DNSQuestionEntity questions = 2; +} diff --git a/proto/audit/details/http_details.proto b/proto/audit/details/http_details.proto new file mode 100644 index 0000000..e5e5d7a --- /dev/null +++ b/proto/audit/details/http_details.proto @@ -0,0 +1,33 @@ +syntax = "proto3"; + +option go_package = "gitlab.com/inetmock/inetmock/pkg/audit/details"; +option java_multiple_files = true; +option java_package = "com.github.baez90.inetmock.audit.details"; +option java_outer_classname = "HandlerEventProto"; +option csharp_namespace = "INetMock.Client.Audit.Details"; + +package inetmock.audit.details; + +enum HTTPMethod { + GET = 0; + HEAD = 1; + POST = 2; + PUT = 3; + DELETE = 4; + CONNECT = 5; + OPTIONS = 6; + TRACE = 7; + PATCH = 8; +} + +message HTTPHeaderValue { + repeated string values = 1; +} + +message HTTPDetailsEntity { + HTTPMethod method = 1; + string host = 2; + string uri = 3; + string proto = 4; + map headers = 5; +} diff --git a/proto/audit/event_entity.proto b/proto/audit/event_entity.proto new file mode 100644 index 0000000..3b213ee --- /dev/null +++ b/proto/audit/event_entity.proto @@ -0,0 +1,52 @@ +syntax = "proto3"; + +option go_package = "gitlab.com/inetmock/inetmock/pkg/audit"; +option java_multiple_files = true; +option java_package = "com.github.baez90.inetmock.audit"; +option java_outer_classname = "HandlerEventProto"; +option csharp_namespace = "INetMock.Client.Audit"; + +package inetmock.audit; + +import "google/protobuf/any.proto"; +import "google/protobuf/timestamp.proto"; + +enum TransportProtocol { + UNKNOWN_TRANSPORT = 0; + TCP = 1; + UDP = 2; +} + +enum AppProtocol { + UNKNOWN_APPLICATION = 0; + DNS = 1; + HTTP = 2; + HTTP_PROXY = 3; +} + +enum TLSVersion { + SSLv30 = 0; + TLS10 = 1; + TLS11 = 2; + TLS12 = 3; + TLS13 = 4; +} + +message TLSDetailsEntity { + TLSVersion version = 1; + string cipherSuite = 2; + string serverName = 3; +} + +message EventEntity { + int64 id = 1; + google.protobuf.Timestamp timestamp = 2; + TransportProtocol transport = 3; + AppProtocol application = 4; + bytes sourceIP = 5; + bytes destinationIP = 6; + uint32 sourcePort = 7; + uint32 destinationPort = 8; + TLSDetailsEntity tls = 9; + google.protobuf.Any protocolDetails = 10; +} diff --git a/proto/rpc/audit.proto b/proto/rpc/audit.proto new file mode 100644 index 0000000..ca369c1 --- /dev/null +++ b/proto/rpc/audit.proto @@ -0,0 +1,53 @@ +syntax = "proto3"; + +option go_package = "gitlab.com/inetmock/inetmock/pkg/rpc"; +option java_multiple_files = true; +option java_package = "com.github.baez90.inetmock.rpc"; +option java_outer_classname = "AuditProto"; +option csharp_namespace = "INetMock.Client.Rpc"; + +import 'proto/audit/event_entity.proto'; + +package inetmock.rpc; + +enum FileOpenMode { + TRUNCATE = 0; + APPEND = 1; +} + +message WatchEventsRequest { + string watcherName = 1; +} + +message RegisterFileSinkRequest { + string targetPath = 1; + FileOpenMode openMode = 2; + uint32 permissions = 3; +} + +message RegisterFileSinkResponse { + +} + +message RemoveFileSinkRequest { + string targetPath = 1; +} + +message RemoveFileSinkResponse { + bool SinkGotRemoved = 1; +} + +message ListSinksRequest { + +} + +message ListSinksResponse { + repeated string sinks = 1; +} + +service Audit { + rpc WatchEvents (WatchEventsRequest) returns (stream inetmock.audit.EventEntity); + rpc RegisterFileSink (RegisterFileSinkRequest) returns (RegisterFileSinkResponse); + rpc RemoveFileSink (RemoveFileSinkRequest) returns (RemoveFileSinkResponse); + rpc ListSinks(ListSinksRequest) returns (ListSinksResponse); +} diff --git a/proto/rpc/health.proto b/proto/rpc/health.proto new file mode 100644 index 0000000..193bc8c --- /dev/null +++ b/proto/rpc/health.proto @@ -0,0 +1,35 @@ +syntax = "proto3"; + +option go_package = "gitlab.com/inetmock/inetmock/internal/rpc"; +option java_multiple_files = true; +option java_package = "com.github.baez90.inetmock.rpc"; +option java_outer_classname = "HealthProto"; +option csharp_namespace = "INetMock.Client.Rpc"; + +package inetmock.rpc; + +service Health { + rpc GetHealth (HealthRequest) returns (HealthResponse) { + } +} + +enum HealthState { + HEALTHY = 0; + INITIALIZING = 1; + UNHEALTHY = 2; + UNKNOWN = 3; +} + +message HealthRequest { + repeated string components = 1; +} + +message ComponentHealth { + HealthState State = 1; + string message = 2; +} + +message HealthResponse { + HealthState overallHealthState = 1; + map componentsHealth = 2; +} diff --git a/testdata/integration.dockerfile b/testdata/integration.dockerfile deleted file mode 100644 index c058cef..0000000 --- a/testdata/integration.dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM golang:1.15-alpine as build - -WORKDIR /app - -COPY ./ ./ - -RUN go build -o inetmock ./cmd/inetmock - -FROM alpine:3.13 - -WORKDIR /app - -COPY --from=build /app/inetmock ./ -COPY --from=build /app/config-container.yaml /etc/inetmock/config.yaml -COPY --from=build /app/assets/fakeFiles /var/lib/inetmock/fakeFiles -COPY --from=build /app/assets/demoCA /var/lib/inetmock/ca - -RUN mkdir -p /var/run/inetmock - -CMD /app/inetmock serve \ No newline at end of file