mirror of
https://github.com/kubeshark/kubeshark.git
synced 2026-07-05 14:41:03 +00:00
Compare commits
109 Commits
0.21.49
...
26.0-dev11
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a4674ea7c | ||
|
|
f013b0f03c | ||
|
|
2c72c27bc9 | ||
|
|
98ca1b2840 | ||
|
|
0365b8971f | ||
|
|
0f77cf8ef9 | ||
|
|
ea678a4d8c | ||
|
|
5b53252033 | ||
|
|
bfa275d08d | ||
|
|
0af1bc021d | ||
|
|
bd253e1ff7 | ||
|
|
379885845b | ||
|
|
ad6fb844aa | ||
|
|
9cddb0c7e8 | ||
|
|
5934be4da6 | ||
|
|
78be20fe4d | ||
|
|
4c4405885b | ||
|
|
a2118b869e | ||
|
|
c37f6478f3 | ||
|
|
d9ed9098eb | ||
|
|
e09748baff | ||
|
|
20fcc8e163 | ||
|
|
fd64c1bb14 | ||
|
|
3a51ca21eb | ||
|
|
0a2e55f7bc | ||
|
|
f7c200b821 | ||
|
|
0846a98bc1 | ||
|
|
602225bb36 | ||
|
|
c0f6f2a049 | ||
|
|
7846d812c1 | ||
|
|
0f6c56986f | ||
|
|
daf6b3db06 | ||
|
|
fdf552a9ec | ||
|
|
cbff1837c1 | ||
|
|
71425928cc | ||
|
|
82db4acb7d | ||
|
|
9bd82191aa | ||
|
|
83b657472b | ||
|
|
2e5cf13b3f | ||
|
|
4be7164f20 | ||
|
|
0abd7c06ff | ||
|
|
c2739a68c2 | ||
|
|
d0ef6c9f97 | ||
|
|
a4f7e61a6e | ||
|
|
a5fef90781 | ||
|
|
70cef9dc4b | ||
|
|
0f3dd66d2d | ||
|
|
5536e5bb44 | ||
|
|
3bab83754f | ||
|
|
d011478a74 | ||
|
|
7fa1a191a6 | ||
|
|
65bb338ed6 | ||
|
|
c098ff3323 | ||
|
|
19b4810ded | ||
|
|
beb8363722 | ||
|
|
1eb67c69d9 | ||
|
|
be3375f797 | ||
|
|
0c56c0f541 | ||
|
|
843ac722c9 | ||
|
|
a9a61edd50 | ||
|
|
83c9194703 | ||
|
|
86edc91f4c | ||
|
|
e30b52f528 | ||
|
|
80418f1802 | ||
|
|
85edd6e5b0 | ||
|
|
3067bf5eaf | ||
|
|
d216c64154 | ||
|
|
39f0b74897 | ||
|
|
569f8ae143 | ||
|
|
bcea6cdc49 | ||
|
|
1a2697dd0d | ||
|
|
2638672603 | ||
|
|
a702f0f93a | ||
|
|
18d90cdf36 | ||
|
|
9c665e664b | ||
|
|
54ea2afe71 | ||
|
|
50c89e6245 | ||
|
|
676e50b0b1 | ||
|
|
6bab381280 | ||
|
|
27dee4e09b | ||
|
|
b31af7214b | ||
|
|
d5fd2ff1da | ||
|
|
7477f867f9 | ||
|
|
cc4638afe6 | ||
|
|
acf3894824 | ||
|
|
6235217ead | ||
|
|
c8a3033f87 | ||
|
|
6b4bcc8abd | ||
|
|
5ca3107422 | ||
|
|
ce477095fd | ||
|
|
5fed5808d2 | ||
|
|
5c59cd643a | ||
|
|
aae03c52e9 | ||
|
|
dacdb69164 | ||
|
|
e15eb71b77 | ||
|
|
ae1bcf4c0c | ||
|
|
20d69228d3 | ||
|
|
59fbe4c479 | ||
|
|
4db8e8902b | ||
|
|
f5bacbd1ea | ||
|
|
b94098fea6 | ||
|
|
92c7e2b91d | ||
|
|
d97d481392 | ||
|
|
8963630e9e | ||
|
|
610b9efdb0 | ||
|
|
0e5611b7e9 | ||
|
|
26a9c31d1e | ||
|
|
68c4ee9a4f | ||
|
|
bfbbc27e62 |
@@ -2,7 +2,6 @@
|
|||||||
.dockerignore
|
.dockerignore
|
||||||
.editorconfig
|
.editorconfig
|
||||||
.gitignore
|
.gitignore
|
||||||
**/.env*
|
|
||||||
Dockerfile
|
Dockerfile
|
||||||
Makefile
|
Makefile
|
||||||
LICENSE
|
LICENSE
|
||||||
|
|||||||
8
.github/workflows/acceptance_tests.yml
vendored
8
.github/workflows/acceptance_tests.yml
vendored
@@ -8,19 +8,15 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- 'develop'
|
- 'develop'
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: mizu-acceptance-tests-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
run-acceptance-tests:
|
run-acceptance-tests:
|
||||||
name: Run acceptance tests
|
name: Run acceptance tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Go 1.16
|
- name: Set up Go 1.17
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: '^1.16'
|
go-version: '^1.17'
|
||||||
|
|
||||||
- name: Check out code into the Go module directory
|
- name: Check out code into the Go module directory
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
name: PR validation
|
name: Build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
@@ -12,13 +12,13 @@ concurrency:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-cli:
|
build-cli:
|
||||||
name: Build CLI
|
name: CLI executable build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Go 1.16
|
- name: Set up Go 1.17
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: '1.16'
|
go-version: '1.17'
|
||||||
|
|
||||||
- name: Check out code into the Go module directory
|
- name: Check out code into the Go module directory
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
@@ -27,20 +27,11 @@ jobs:
|
|||||||
run: make cli
|
run: make cli
|
||||||
|
|
||||||
build-agent:
|
build-agent:
|
||||||
name: Build Agent
|
name: Agent docker image build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Go 1.16
|
|
||||||
uses: actions/setup-go@v2
|
|
||||||
with:
|
|
||||||
go-version: '1.16'
|
|
||||||
|
|
||||||
- name: Check out code into the Go module directory
|
- name: Check out code into the Go module directory
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- shell: bash
|
|
||||||
run: |
|
|
||||||
sudo apt-get install libpcap-dev
|
|
||||||
|
|
||||||
- name: Build Agent
|
- name: Build Agent
|
||||||
run: make agent
|
run: make agent-docker
|
||||||
90
.github/workflows/publish.yml
vendored
90
.github/workflows/publish.yml
vendored
@@ -1,90 +0,0 @@
|
|||||||
name: publish
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- 'develop'
|
|
||||||
- 'main'
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: mizu-publish-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
docker:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Set up Go 1.16
|
|
||||||
uses: actions/setup-go@v2
|
|
||||||
with:
|
|
||||||
go-version: '1.16'
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Set up Cloud SDK
|
|
||||||
uses: google-github-actions/setup-gcloud@master
|
|
||||||
with:
|
|
||||||
service_account_key: ${{ secrets.GCR_JSON_KEY }}
|
|
||||||
export_default_credentials: true
|
|
||||||
- uses: haya14busa/action-cond@v1
|
|
||||||
id: condval
|
|
||||||
with:
|
|
||||||
cond: ${{ github.ref == 'refs/heads/main' }}
|
|
||||||
if_true: "minor"
|
|
||||||
if_false: "patch"
|
|
||||||
- name: Auto Increment Semver Action
|
|
||||||
uses: MCKanpolat/auto-semver-action@1.0.5
|
|
||||||
id: versioning
|
|
||||||
with:
|
|
||||||
releaseType: ${{ steps.condval.outputs.value }}
|
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- name: Get version parameters
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
echo "##[set-output name=build_timestamp;]$(echo $(date +%s))"
|
|
||||||
echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})"
|
|
||||||
id: version_parameters
|
|
||||||
- name: Get base image name
|
|
||||||
shell: bash
|
|
||||||
run: echo "##[set-output name=image;]$(echo gcr.io/up9-docker-hub/mizu/${GITHUB_REF#refs/heads/})"
|
|
||||||
id: base_image_step
|
|
||||||
- name: Docker meta
|
|
||||||
id: meta
|
|
||||||
uses: crazy-max/ghaction-docker-meta@v2
|
|
||||||
with:
|
|
||||||
images: ${{ steps.base_image_step.outputs.image }}
|
|
||||||
tags: |
|
|
||||||
type=sha
|
|
||||||
type=raw,${{ github.sha }}
|
|
||||||
type=raw,${{ steps.versioning.outputs.version }}
|
|
||||||
- name: Login to DockerHub
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
registry: gcr.io
|
|
||||||
username: _json_key
|
|
||||||
password: ${{ secrets.GCR_JSON_KEY }}
|
|
||||||
- name: Build and push
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
push: true
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
build-args: |
|
|
||||||
SEM_VER=${{ steps.versioning.outputs.version }}
|
|
||||||
BUILD_TIMESTAMP=${{ steps.version_parameters.outputs.build_timestamp }}
|
|
||||||
GIT_BRANCH=${{ steps.version_parameters.outputs.branch }}
|
|
||||||
COMMIT_HASH=${{ github.sha }}
|
|
||||||
- name: Build and Push CLI
|
|
||||||
run: make push-cli SEM_VER='${{ steps.versioning.outputs.version }}' BUILD_TIMESTAMP='${{ steps.version_parameters.outputs.build_timestamp }}'
|
|
||||||
- shell: bash
|
|
||||||
run: |
|
|
||||||
echo '${{ steps.versioning.outputs.version }}' >> cli/bin/version.txt
|
|
||||||
- name: publish
|
|
||||||
uses: ncipollo/release-action@v1
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
artifacts: "cli/bin/*"
|
|
||||||
commit: ${{ steps.version_parameters.outputs.branch }}
|
|
||||||
tag: ${{ steps.versioning.outputs.version }}
|
|
||||||
prerelease: ${{ github.ref != 'refs/heads/main' }}
|
|
||||||
bodyFile: 'cli/bin/README.md'
|
|
||||||
287
.github/workflows/release.yml
vendored
Normal file
287
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'develop'
|
||||||
|
- 'main'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: mizu-publish-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker-registry:
|
||||||
|
name: Push Docker image to Docker Hub
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
max-parallel: 2
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target:
|
||||||
|
- amd64
|
||||||
|
- arm64v8
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out the repo
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Determine versioning strategy
|
||||||
|
uses: haya14busa/action-cond@v1
|
||||||
|
id: condval
|
||||||
|
with:
|
||||||
|
cond: ${{ github.ref == 'refs/heads/main' }}
|
||||||
|
if_true: "stable"
|
||||||
|
if_false: "dev"
|
||||||
|
|
||||||
|
- name: Auto Increment Ver Action
|
||||||
|
uses: docker://igorgov/auto-inc-ver:v2.0.0
|
||||||
|
id: versioning
|
||||||
|
with:
|
||||||
|
mode: ${{ steps.condval.outputs.value }}
|
||||||
|
suffix: 'dev'
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Get version parameters
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "##[set-output name=build_timestamp;]$(echo $(date +%s))"
|
||||||
|
echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})"
|
||||||
|
id: version_parameters
|
||||||
|
|
||||||
|
- name: Docker meta
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v3
|
||||||
|
with:
|
||||||
|
images: |
|
||||||
|
up9inc/mizu
|
||||||
|
tags: |
|
||||||
|
type=raw,${{ steps.versioning.outputs.version }}
|
||||||
|
flavor: |
|
||||||
|
latest=auto
|
||||||
|
prefix=
|
||||||
|
suffix=-${{ matrix.target }},onlatest=true
|
||||||
|
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USER }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_PASS }}
|
||||||
|
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: true
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
build-args: |
|
||||||
|
TARGETARCH=${{ matrix.target }}
|
||||||
|
VER=${{ steps.versioning.outputs.version }}
|
||||||
|
BUILD_TIMESTAMP=${{ steps.version_parameters.outputs.build_timestamp }}
|
||||||
|
GIT_BRANCH=${{ steps.version_parameters.outputs.branch }}
|
||||||
|
COMMIT_HASH=${{ github.sha }}
|
||||||
|
|
||||||
|
gcp-registry:
|
||||||
|
name: Push Docker image to GCR
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
max-parallel: 2
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target:
|
||||||
|
- amd64
|
||||||
|
- arm64v8
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out the repo
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- id: 'auth'
|
||||||
|
uses: 'google-github-actions/auth@v0'
|
||||||
|
with:
|
||||||
|
credentials_json: '${{ secrets.GCR_JSON_KEY }}'
|
||||||
|
|
||||||
|
- name: 'Set up Cloud SDK'
|
||||||
|
uses: 'google-github-actions/setup-gcloud@v0'
|
||||||
|
|
||||||
|
- name: Determine versioning strategy
|
||||||
|
uses: haya14busa/action-cond@v1
|
||||||
|
id: condval
|
||||||
|
with:
|
||||||
|
cond: ${{ github.ref == 'refs/heads/main' }}
|
||||||
|
if_true: "stable"
|
||||||
|
if_false: "dev"
|
||||||
|
|
||||||
|
- name: Auto Increment Ver Action
|
||||||
|
uses: docker://igorgov/auto-inc-ver:v2.0.0
|
||||||
|
id: versioning
|
||||||
|
with:
|
||||||
|
mode: ${{ steps.condval.outputs.value }}
|
||||||
|
suffix: 'dev'
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Get version parameters
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "##[set-output name=build_timestamp;]$(echo $(date +%s))"
|
||||||
|
echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})"
|
||||||
|
id: version_parameters
|
||||||
|
|
||||||
|
- name: Get base image name
|
||||||
|
shell: bash
|
||||||
|
run: echo "##[set-output name=image;]$(echo gcr.io/up9-docker-hub/mizu/${GITHUB_REF#refs/heads/})"
|
||||||
|
id: base_image_step
|
||||||
|
|
||||||
|
- name: Docker meta
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v3
|
||||||
|
with:
|
||||||
|
images: |
|
||||||
|
${{ steps.base_image_step.outputs.image }}
|
||||||
|
tags: |
|
||||||
|
type=raw,${{ steps.versioning.outputs.version }}
|
||||||
|
flavor: |
|
||||||
|
latest=auto
|
||||||
|
prefix=
|
||||||
|
suffix=-${{ matrix.target }},onlatest=true
|
||||||
|
|
||||||
|
- name: Login to GCR
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
registry: gcr.io
|
||||||
|
username: _json_key
|
||||||
|
password: ${{ secrets.GCR_JSON_KEY }}
|
||||||
|
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: true
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
build-args: |
|
||||||
|
TARGETARCH=${{ matrix.target }}
|
||||||
|
VER=${{ steps.versioning.outputs.version }}
|
||||||
|
BUILD_TIMESTAMP=${{ steps.version_parameters.outputs.build_timestamp }}
|
||||||
|
GIT_BRANCH=${{ steps.version_parameters.outputs.branch }}
|
||||||
|
COMMIT_HASH=${{ github.sha }}
|
||||||
|
|
||||||
|
docker-manifest:
|
||||||
|
name: Create and Push a Docker Manifest
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [docker-registry]
|
||||||
|
steps:
|
||||||
|
- name: Determine versioning strategy
|
||||||
|
uses: haya14busa/action-cond@v1
|
||||||
|
id: condval
|
||||||
|
with:
|
||||||
|
cond: ${{ github.ref == 'refs/heads/main' }}
|
||||||
|
if_true: "stable"
|
||||||
|
if_false: "dev"
|
||||||
|
|
||||||
|
- name: Auto Increment Ver Action
|
||||||
|
uses: docker://igorgov/auto-inc-ver:v2.0.0
|
||||||
|
id: versioning
|
||||||
|
with:
|
||||||
|
mode: ${{ steps.condval.outputs.value }}
|
||||||
|
suffix: 'dev'
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Get version parameters
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "##[set-output name=build_timestamp;]$(echo $(date +%s))"
|
||||||
|
echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})"
|
||||||
|
id: version_parameters
|
||||||
|
|
||||||
|
- name: Docker meta
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v3
|
||||||
|
with:
|
||||||
|
images: |
|
||||||
|
up9inc/mizu
|
||||||
|
tags: |
|
||||||
|
type=raw,${{ steps.versioning.outputs.version }}
|
||||||
|
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USER }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_PASS }}
|
||||||
|
|
||||||
|
- name: Create manifest
|
||||||
|
run: |
|
||||||
|
while IFS= read -r line; do
|
||||||
|
docker manifest create $line --amend $line-amd64 --amend $line-arm64v8
|
||||||
|
done <<< "${{ steps.meta.outputs.tags }}"
|
||||||
|
|
||||||
|
- name: Push manifest
|
||||||
|
run: |
|
||||||
|
while IFS= read -r line; do
|
||||||
|
docker manifest push $line
|
||||||
|
done <<< "${{ steps.meta.outputs.tags }}"
|
||||||
|
|
||||||
|
cli:
|
||||||
|
name: Build the CLI and publish
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [docker-manifest, gcp-registry]
|
||||||
|
steps:
|
||||||
|
- name: Set up Go 1.17
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: '1.17'
|
||||||
|
|
||||||
|
- name: Check out the repo
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- id: 'auth'
|
||||||
|
uses: 'google-github-actions/auth@v0'
|
||||||
|
with:
|
||||||
|
credentials_json: '${{ secrets.GCR_JSON_KEY }}'
|
||||||
|
|
||||||
|
- name: 'Set up Cloud SDK'
|
||||||
|
uses: 'google-github-actions/setup-gcloud@v0'
|
||||||
|
|
||||||
|
- name: Determine versioning strategy
|
||||||
|
uses: haya14busa/action-cond@v1
|
||||||
|
id: condval
|
||||||
|
with:
|
||||||
|
cond: ${{ github.ref == 'refs/heads/main' }}
|
||||||
|
if_true: "stable"
|
||||||
|
if_false: "dev"
|
||||||
|
|
||||||
|
- name: Auto Increment Ver Action
|
||||||
|
uses: docker://igorgov/auto-inc-ver:v2.0.0
|
||||||
|
id: versioning
|
||||||
|
with:
|
||||||
|
mode: ${{ steps.condval.outputs.value }}
|
||||||
|
suffix: 'dev'
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Get version parameters
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "##[set-output name=build_timestamp;]$(echo $(date +%s))"
|
||||||
|
echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})"
|
||||||
|
id: version_parameters
|
||||||
|
|
||||||
|
- name: Build and Push CLI
|
||||||
|
run: make push-cli VER='${{ steps.versioning.outputs.version }}' BUILD_TIMESTAMP='${{ steps.version_parameters.outputs.build_timestamp }}'
|
||||||
|
|
||||||
|
- name: Log the version into a .txt file
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo '${{ steps.versioning.outputs.version }}' >> cli/bin/version.txt
|
||||||
|
|
||||||
|
- name: Release
|
||||||
|
uses: ncipollo/release-action@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
artifacts: "cli/bin/*"
|
||||||
|
commit: ${{ steps.version_parameters.outputs.branch }}
|
||||||
|
tag: ${{ steps.versioning.outputs.version }}
|
||||||
|
prerelease: ${{ github.ref != 'refs/heads/main' }}
|
||||||
|
bodyFile: 'cli/bin/README.md'
|
||||||
90
.github/workflows/static_code_analysis.yml
vendored
Normal file
90
.github/workflows/static_code_analysis.yml
vendored
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
name: Static code analysis
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- 'develop'
|
||||||
|
- 'main'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
golangci:
|
||||||
|
name: Go lint
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: '^1.17'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y libpcap-dev
|
||||||
|
|
||||||
|
- name: Go lint - agent
|
||||||
|
uses: golangci/golangci-lint-action@v2
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
working-directory: agent
|
||||||
|
args: --timeout=3m
|
||||||
|
|
||||||
|
- name: Go lint - shared
|
||||||
|
uses: golangci/golangci-lint-action@v2
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
working-directory: shared
|
||||||
|
args: --timeout=3m
|
||||||
|
|
||||||
|
- name: Go lint - tap
|
||||||
|
uses: golangci/golangci-lint-action@v2
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
working-directory: tap
|
||||||
|
args: --timeout=3m
|
||||||
|
|
||||||
|
- name: Go lint - CLI
|
||||||
|
uses: golangci/golangci-lint-action@v2
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
working-directory: cli
|
||||||
|
args: --timeout=3m
|
||||||
|
|
||||||
|
- name: Go lint - acceptanceTests
|
||||||
|
uses: golangci/golangci-lint-action@v2
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
working-directory: acceptanceTests
|
||||||
|
args: --timeout=3m
|
||||||
|
|
||||||
|
- name: Go lint - tap/api
|
||||||
|
uses: golangci/golangci-lint-action@v2
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
working-directory: tap/api
|
||||||
|
|
||||||
|
- name: Go lint - tap/extensions/amqp
|
||||||
|
uses: golangci/golangci-lint-action@v2
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
working-directory: tap/extensions/amqp
|
||||||
|
|
||||||
|
- name: Go lint - tap/extensions/http
|
||||||
|
uses: golangci/golangci-lint-action@v2
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
working-directory: tap/extensions/http
|
||||||
|
|
||||||
|
- name: Go lint - tap/extensions/kafka
|
||||||
|
uses: golangci/golangci-lint-action@v2
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
working-directory: tap/extensions/kafka
|
||||||
|
|
||||||
|
- name: Go lint - tap/extensions/redis
|
||||||
|
uses: golangci/golangci-lint-action@v2
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
working-directory: tap/extensions/redis
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
name: tests validation
|
name: Test
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- 'develop'
|
- 'develop'
|
||||||
- 'main'
|
- 'main'
|
||||||
push:
|
push: # needed to upload test coverage report to codecov
|
||||||
branches:
|
branches:
|
||||||
- 'develop'
|
- 'develop'
|
||||||
- 'main'
|
- 'main'
|
||||||
@@ -15,32 +15,14 @@ concurrency:
|
|||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
run-tests-cli:
|
run-unit-tests:
|
||||||
name: Run CLI tests
|
name: Unit Tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Go 1.16
|
- name: Set up Go 1.17
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: '^1.16'
|
go-version: '^1.17'
|
||||||
|
|
||||||
- name: Check out code into the Go module directory
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
run: make test-cli
|
|
||||||
|
|
||||||
- name: Upload coverage to Codecov
|
|
||||||
uses: codecov/codecov-action@v2
|
|
||||||
|
|
||||||
run-tests-agent:
|
|
||||||
name: Run Agent tests
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Set up Go 1.16
|
|
||||||
uses: actions/setup-go@v2
|
|
||||||
with:
|
|
||||||
go-version: '^1.16'
|
|
||||||
|
|
||||||
- name: Check out code into the Go module directory
|
- name: Check out code into the Go module directory
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
@@ -49,8 +31,15 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
sudo apt-get install libpcap-dev
|
sudo apt-get install libpcap-dev
|
||||||
|
|
||||||
- name: Test
|
- name: CLI Test
|
||||||
|
run: make test-cli
|
||||||
|
|
||||||
|
- name: Agent Test
|
||||||
run: make test-agent
|
run: make test-agent
|
||||||
|
|
||||||
|
- name: Shared Test
|
||||||
|
run: make test-shared
|
||||||
|
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
uses: codecov/codecov-action@v2
|
uses: codecov/codecov-action@v2
|
||||||
|
|
||||||
100
Dockerfile
100
Dockerfile
@@ -1,4 +1,8 @@
|
|||||||
FROM node:14-slim AS site-build
|
ARG BUILDARCH=amd64
|
||||||
|
ARG TARGETARCH=amd64
|
||||||
|
|
||||||
|
### Front-end
|
||||||
|
FROM node:16 AS front-end
|
||||||
|
|
||||||
WORKDIR /app/ui-build
|
WORKDIR /app/ui-build
|
||||||
|
|
||||||
@@ -7,57 +11,95 @@ COPY ui/package-lock.json .
|
|||||||
RUN npm i
|
RUN npm i
|
||||||
COPY ui .
|
COPY ui .
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
RUN npm run build-ent
|
||||||
|
|
||||||
|
### Base builder image for native builds architecture
|
||||||
|
FROM golang:1.17-alpine AS builder-native-base
|
||||||
|
ENV CGO_ENABLED=1 GOOS=linux
|
||||||
|
RUN apk add libpcap-dev g++ perl-utils
|
||||||
|
|
||||||
|
|
||||||
FROM golang:1.16-alpine AS builder
|
### Intermediate builder image for x86-64 to x86-64 native builds
|
||||||
# Set necessary environment variables needed for our image.
|
FROM builder-native-base AS builder-from-amd64-to-amd64
|
||||||
ENV CGO_ENABLED=1 GOOS=linux GOARCH=amd64
|
ENV GOARCH=amd64
|
||||||
|
|
||||||
RUN apk add libpcap-dev gcc g++ make bash perl-utils
|
|
||||||
|
|
||||||
# Move to agent working directory (/agent-build).
|
### Intermediate builder image for AArch64 to AArch64 native builds
|
||||||
|
FROM builder-native-base AS builder-from-arm64v8-to-arm64v8
|
||||||
|
ENV GOARCH=arm64
|
||||||
|
|
||||||
|
|
||||||
|
### Builder image for x86-64 to AArch64 cross-compilation
|
||||||
|
FROM up9inc/linux-arm64-musl-go-libpcap AS builder-from-amd64-to-arm64v8
|
||||||
|
ENV CGO_ENABLED=1 GOOS=linux
|
||||||
|
ENV GOARCH=arm64 CGO_CFLAGS="-I/work/libpcap"
|
||||||
|
|
||||||
|
|
||||||
|
### Final builder image where the build happens
|
||||||
|
# Possible build strategies:
|
||||||
|
# BUILDARCH=amd64 TARGETARCH=amd64
|
||||||
|
# BUILDARCH=arm64v8 TARGETARCH=arm64v8
|
||||||
|
# BUILDARCH=amd64 TARGETARCH=arm64v8
|
||||||
|
ARG BUILDARCH=amd64
|
||||||
|
ARG TARGETARCH=amd64
|
||||||
|
FROM builder-from-${BUILDARCH}-to-${TARGETARCH} AS builder
|
||||||
|
|
||||||
|
# Move to agent working directory (/agent-build)
|
||||||
WORKDIR /app/agent-build
|
WORKDIR /app/agent-build
|
||||||
|
|
||||||
COPY agent/go.mod agent/go.sum ./
|
COPY agent/go.mod agent/go.sum ./
|
||||||
COPY shared/go.mod shared/go.mod ../shared/
|
COPY shared/go.mod shared/go.mod ../shared/
|
||||||
COPY tap/go.mod tap/go.mod ../tap/
|
COPY tap/go.mod tap/go.mod ../tap/
|
||||||
COPY tap/api/go.* ../tap/api/
|
COPY tap/api/go.mod ../tap/api/
|
||||||
|
COPY tap/extensions/amqp/go.mod ../tap/extensions/amqp/
|
||||||
|
COPY tap/extensions/http/go.mod ../tap/extensions/http/
|
||||||
|
COPY tap/extensions/kafka/go.mod ../tap/extensions/kafka/
|
||||||
|
COPY tap/extensions/redis/go.mod ../tap/extensions/redis/
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
# cheap trick to make the build faster (As long as go.mod wasn't changes)
|
# cheap trick to make the build faster (as long as go.mod did not change)
|
||||||
RUN go list -f '{{.Path}}@{{.Version}}' -m all | sed 1d | grep -e 'go-cache' | xargs go get
|
RUN go list -f '{{.Path}}@{{.Version}}' -m all | sed 1d | grep -e 'go-cache' | xargs go get
|
||||||
|
|
||||||
ARG COMMIT_HASH
|
|
||||||
ARG GIT_BRANCH
|
|
||||||
ARG BUILD_TIMESTAMP
|
|
||||||
ARG SEM_VER=0.0.0
|
|
||||||
|
|
||||||
# Copy and build agent code
|
# Copy and build agent code
|
||||||
COPY shared ../shared
|
COPY shared ../shared
|
||||||
COPY tap ../tap
|
COPY tap ../tap
|
||||||
COPY agent .
|
COPY agent .
|
||||||
RUN go build -ldflags="-s -w \
|
|
||||||
-X 'mizuserver/pkg/version.GitCommitHash=${COMMIT_HASH}' \
|
|
||||||
-X 'mizuserver/pkg/version.Branch=${GIT_BRANCH}' \
|
|
||||||
-X 'mizuserver/pkg/version.BuildTimestamp=${BUILD_TIMESTAMP}' \
|
|
||||||
-X 'mizuserver/pkg/version.SemVer=${SEM_VER}'" -o mizuagent .
|
|
||||||
|
|
||||||
COPY devops/build_extensions.sh ..
|
ARG COMMIT_HASH
|
||||||
RUN cd .. && /bin/bash build_extensions.sh
|
ARG GIT_BRANCH
|
||||||
|
ARG BUILD_TIMESTAMP
|
||||||
|
ARG VER=0.0
|
||||||
|
|
||||||
FROM alpine:3.15
|
WORKDIR /app/agent-build
|
||||||
|
|
||||||
RUN apk add bash libpcap-dev
|
RUN go build -ldflags="-extldflags=-static -s -w \
|
||||||
|
-X 'github.com/up9inc/mizu/agent/pkg/version.GitCommitHash=${COMMIT_HASH}' \
|
||||||
|
-X 'github.com/up9inc/mizu/agent/pkg/version.Branch=${GIT_BRANCH}' \
|
||||||
|
-X 'github.com/up9inc/mizu/agent/pkg/version.BuildTimestamp=${BUILD_TIMESTAMP}' \
|
||||||
|
-X 'github.com/up9inc/mizu/agent/pkg/version.Ver=${VER}'" -o mizuagent .
|
||||||
|
|
||||||
WORKDIR /app
|
# Download Basenine executable, verify the sha1sum
|
||||||
|
ADD https://github.com/up9inc/basenine/releases/download/v0.4.13/basenine_linux_${GOARCH} ./basenine_linux_${GOARCH}
|
||||||
|
ADD https://github.com/up9inc/basenine/releases/download/v0.4.13/basenine_linux_${GOARCH}.sha256 ./basenine_linux_${GOARCH}.sha256
|
||||||
|
RUN shasum -a 256 -c basenine_linux_${GOARCH}.sha256
|
||||||
|
RUN chmod +x ./basenine_linux_${GOARCH}
|
||||||
|
RUN mv ./basenine_linux_${GOARCH} ./basenine
|
||||||
|
|
||||||
# Copy binary and config files from /build to root folder of scratch container.
|
|
||||||
COPY --from=builder ["/app/agent-build/mizuagent", "."]
|
### The shipped image
|
||||||
COPY --from=builder ["/app/agent/build/extensions", "extensions"]
|
ARG TARGETARCH=amd64
|
||||||
COPY --from=site-build ["/app/ui-build/build", "site"]
|
FROM ${TARGETARCH}/busybox:latest
|
||||||
RUN mkdir /app/data/
|
|
||||||
|
|
||||||
# gin-gonic runs in debug mode without this
|
# gin-gonic runs in debug mode without this
|
||||||
ENV GIN_MODE=release
|
ENV GIN_MODE=release
|
||||||
|
|
||||||
|
WORKDIR /app/data/
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy binary and config files from /build to root folder of scratch container.
|
||||||
|
COPY --from=builder ["/app/agent-build/mizuagent", "."]
|
||||||
|
COPY --from=builder ["/app/agent-build/basenine", "/usr/local/bin/basenine"]
|
||||||
|
COPY --from=front-end ["/app/ui-build/build", "site"]
|
||||||
|
COPY --from=front-end ["/app/ui-build/build-ent", "site-standalone"]
|
||||||
|
|
||||||
# this script runs both apiserver and passivetapper and exits either if one of them exits, preventing a scenario where the container runs without one process
|
# this script runs both apiserver and passivetapper and exits either if one of them exits, preventing a scenario where the container runs without one process
|
||||||
ENTRYPOINT "/app/mizuagent"
|
ENTRYPOINT ["/app/mizuagent"]
|
||||||
|
|||||||
28
Makefile
28
Makefile
@@ -8,7 +8,7 @@ SHELL=/bin/bash
|
|||||||
# HELP
|
# HELP
|
||||||
# This will output the help for each task
|
# This will output the help for each task
|
||||||
# thanks to https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html
|
# thanks to https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html
|
||||||
.PHONY: help ui extensions extensions-debug agent agent-debug cli tap docker
|
.PHONY: help ui agent agent-debug cli tap docker
|
||||||
|
|
||||||
help: ## This help.
|
help: ## This help.
|
||||||
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
||||||
@@ -19,7 +19,7 @@ help: ## This help.
|
|||||||
TS_SUFFIX="$(shell date '+%s')"
|
TS_SUFFIX="$(shell date '+%s')"
|
||||||
GIT_BRANCH="$(shell git branch | grep \* | cut -d ' ' -f2 | tr '[:upper:]' '[:lower:]' | tr '/' '_')"
|
GIT_BRANCH="$(shell git branch | grep \* | cut -d ' ' -f2 | tr '[:upper:]' '[:lower:]' | tr '/' '_')"
|
||||||
BUCKET_PATH=static.up9.io/mizu/$(GIT_BRANCH)
|
BUCKET_PATH=static.up9.io/mizu/$(GIT_BRANCH)
|
||||||
export SEM_VER?=0.0.0
|
export VER?=0.0
|
||||||
|
|
||||||
ui: ## Build UI.
|
ui: ## Build UI.
|
||||||
@(cd ui; npm i ; npm run build; )
|
@(cd ui; npm i ; npm run build; )
|
||||||
@@ -37,28 +37,26 @@ build-cli-ci: ## Build CLI for CI.
|
|||||||
agent: ## Build agent.
|
agent: ## Build agent.
|
||||||
@(echo "building mizu agent .." )
|
@(echo "building mizu agent .." )
|
||||||
@(cd agent; go build -o build/mizuagent main.go)
|
@(cd agent; go build -o build/mizuagent main.go)
|
||||||
${MAKE} extensions
|
|
||||||
@ls -l agent/build
|
@ls -l agent/build
|
||||||
|
|
||||||
agent-debug: ## Build agent for debug.
|
agent-debug: ## Build agent for debug.
|
||||||
@(echo "building mizu agent for debug.." )
|
@(echo "building mizu agent for debug.." )
|
||||||
@(cd agent; go build -gcflags="all=-N -l" -o build/mizuagent main.go)
|
@(cd agent; go build -gcflags="all=-N -l" -o build/mizuagent main.go)
|
||||||
${MAKE} extensions-debug
|
|
||||||
@ls -l agent/build
|
@ls -l agent/build
|
||||||
|
|
||||||
docker: ## Build and publish agent docker image.
|
docker: ## Build and publish agent docker image.
|
||||||
$(MAKE) push-docker
|
$(MAKE) push-docker
|
||||||
|
|
||||||
|
agent-docker: ## Build agent docker image.
|
||||||
|
@echo "Building agent docker image"
|
||||||
|
@docker build -t up9inc/mizu:devlatest .
|
||||||
|
|
||||||
push: push-docker push-cli ## Build and publish agent docker image & CLI.
|
push: push-docker push-cli ## Build and publish agent docker image & CLI.
|
||||||
|
|
||||||
push-docker: ## Build and publish agent docker image.
|
push-docker: ## Build and publish agent docker image.
|
||||||
@echo "publishing Docker image .. "
|
@echo "publishing Docker image .. "
|
||||||
devops/build-push-featurebranch.sh
|
devops/build-push-featurebranch.sh
|
||||||
|
|
||||||
push-docker-debug:
|
|
||||||
@echo "publishing debug Docker image .. "
|
|
||||||
devops/build-push-featurebranch-debug.sh
|
|
||||||
|
|
||||||
build-docker-ci: ## Build agent docker image for CI.
|
build-docker-ci: ## Build agent docker image for CI.
|
||||||
@echo "building docker image for ci"
|
@echo "building docker image for ci"
|
||||||
devops/build-agent-ci.sh
|
devops/build-agent-ci.sh
|
||||||
@@ -71,7 +69,7 @@ push-cli: ## Build and publish CLI.
|
|||||||
gsutil cp -r ./cli/bin/* gs://${BUCKET_PATH}/
|
gsutil cp -r ./cli/bin/* gs://${BUCKET_PATH}/
|
||||||
gsutil setmeta -r -h "Cache-Control:public, max-age=30" gs://${BUCKET_PATH}/\*
|
gsutil setmeta -r -h "Cache-Control:public, max-age=30" gs://${BUCKET_PATH}/\*
|
||||||
|
|
||||||
clean: clean-ui clean-agent clean-cli clean-docker clean-extensions ## Clean all build artifacts.
|
clean: clean-ui clean-agent clean-cli clean-docker ## Clean all build artifacts.
|
||||||
|
|
||||||
clean-ui: ## Clean UI.
|
clean-ui: ## Clean UI.
|
||||||
@(rm -rf ui/build ; echo "UI cleanup done" )
|
@(rm -rf ui/build ; echo "UI cleanup done" )
|
||||||
@@ -82,23 +80,17 @@ clean-agent: ## Clean agent.
|
|||||||
clean-cli: ## Clean CLI.
|
clean-cli: ## Clean CLI.
|
||||||
@(cd cli; make clean ; echo "CLI cleanup done" )
|
@(cd cli; make clean ; echo "CLI cleanup done" )
|
||||||
|
|
||||||
clean-extensions: ## Clean extensions
|
|
||||||
@(rm -rf tap/extensions/*.so ; echo "Extensions cleanup done" )
|
|
||||||
|
|
||||||
clean-docker:
|
clean-docker:
|
||||||
@(echo "DOCKER cleanup - NOT IMPLEMENTED YET " )
|
@(echo "DOCKER cleanup - NOT IMPLEMENTED YET " )
|
||||||
|
|
||||||
extensions-debug:
|
|
||||||
devops/build_extensions_debug.sh
|
|
||||||
|
|
||||||
extensions:
|
|
||||||
devops/build_extensions.sh
|
|
||||||
|
|
||||||
test-cli:
|
test-cli:
|
||||||
@echo "running cli tests"; cd cli && $(MAKE) test
|
@echo "running cli tests"; cd cli && $(MAKE) test
|
||||||
|
|
||||||
test-agent:
|
test-agent:
|
||||||
@echo "running agent tests"; cd agent && $(MAKE) test
|
@echo "running agent tests"; cd agent && $(MAKE) test
|
||||||
|
|
||||||
|
test-shared:
|
||||||
|
@echo "running shared tests"; cd shared && $(MAKE) test
|
||||||
|
|
||||||
acceptance-test:
|
acceptance-test:
|
||||||
@echo "running acceptance tests"; cd acceptanceTests && $(MAKE) test
|
@echo "running acceptance tests"; cd acceptanceTests && $(MAKE) test
|
||||||
|
|||||||
20
README.md
20
README.md
@@ -1,5 +1,23 @@
|
|||||||

|

|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://github.com/up9inc/mizu/blob/main/LICENSE">
|
||||||
|
<img alt="GitHub License" src="https://img.shields.io/github/license/up9inc/mizu?logo=GitHub&style=flat-square">
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/up9inc/mizu/releases/latest">
|
||||||
|
<img alt="GitHub Latest Release" src="https://img.shields.io/github/v/release/up9inc/mizu?logo=GitHub&style=flat-square">
|
||||||
|
</a>
|
||||||
|
<a href="https://hub.docker.com/r/up9inc/mizu">
|
||||||
|
<img alt="Docker pulls" src="https://img.shields.io/docker/pulls/up9inc/mizu?color=%23099cec">
|
||||||
|
</a>
|
||||||
|
<a href="https://hub.docker.com/r/up9inc/mizu">
|
||||||
|
<img alt="Image size" src="https://img.shields.io/docker/image-size/up9inc/mizu/latest">
|
||||||
|
</a>
|
||||||
|
<a href="https://join.slack.com/t/up9/shared_invite/zt-tfjnduli-QzlR8VV4Z1w3YnPIAJfhlQ">
|
||||||
|
<img alt="Slack" src="https://img.shields.io/badge/slack-join_chat-white.svg?logo=slack&style=social">
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
# The API Traffic Viewer for Kubernetes
|
# The API Traffic Viewer for Kubernetes
|
||||||
|
|
||||||
A simple-yet-powerful API traffic viewer for Kubernetes enabling you to view all API communication between microservices to help your debug and troubleshoot regressions.
|
A simple-yet-powerful API traffic viewer for Kubernetes enabling you to view all API communication between microservices to help your debug and troubleshoot regressions.
|
||||||
@@ -12,7 +30,7 @@ Think TCPDump and Wireshark re-invented for Kubernetes.
|
|||||||
|
|
||||||
- Simple and powerful CLI
|
- Simple and powerful CLI
|
||||||
- Monitoring network traffic in real-time. Supported protocols:
|
- Monitoring network traffic in real-time. Supported protocols:
|
||||||
- [HTTP/1.1](https://datatracker.ietf.org/doc/html/rfc2616) (REST, etc.)
|
- [HTTP/1.x](https://datatracker.ietf.org/doc/html/rfc2616) (REST, GraphQL, SOAP, etc.)
|
||||||
- [HTTP/2](https://datatracker.ietf.org/doc/html/rfc7540) (gRPC)
|
- [HTTP/2](https://datatracker.ietf.org/doc/html/rfc7540) (gRPC)
|
||||||
- [AMQP](https://www.rabbitmq.com/amqp-0-9-1-reference.html) (RabbitMQ, Apache Qpid, etc.)
|
- [AMQP](https://www.rabbitmq.com/amqp-0-9-1-reference.html) (RabbitMQ, Apache Qpid, etc.)
|
||||||
- [Apache Kafka](https://kafka.apache.org/protocol)
|
- [Apache Kafka](https://kafka.apache.org/protocol)
|
||||||
|
|||||||
@@ -4,11 +4,27 @@
|
|||||||
"viewportHeight": 1080,
|
"viewportHeight": 1080,
|
||||||
"video": false,
|
"video": false,
|
||||||
"screenshotOnRunFailure": false,
|
"screenshotOnRunFailure": false,
|
||||||
"testFiles":
|
"defaultCommandTimeout": 6000,
|
||||||
["tests/GuiPort.js",
|
"testFiles": [
|
||||||
"tests/MultipleNamespaces.js",
|
"tests/GuiPort.js",
|
||||||
"tests/Regex.js"],
|
"tests/MultipleNamespaces.js",
|
||||||
|
"tests/Redact.js",
|
||||||
|
"tests/NoRedact.js",
|
||||||
|
"tests/Regex.js",
|
||||||
|
"tests/RegexMasking.js",
|
||||||
|
"tests/IgnoredUserAgents.js",
|
||||||
|
"tests/UiTest.js",
|
||||||
|
"tests/Redis.js"
|
||||||
|
],
|
||||||
|
|
||||||
"env": {
|
"env": {
|
||||||
"testUrl": "http://localhost:8899/"
|
"testUrl": "http://localhost:8899/",
|
||||||
|
"redactHeaderContent": "User-Header[REDACTED]",
|
||||||
|
"redactBodyContent": "{ \"User\": \"[REDACTED]\" }",
|
||||||
|
"regexMaskingBodyContent": "[REDACTED]",
|
||||||
|
"minimumEntries": 25,
|
||||||
|
"greenFilterColor": "rgb(210, 250, 210)",
|
||||||
|
"redFilterColor": "rgb(250, 214, 220)",
|
||||||
|
"bodyJsonClass": ".hljs"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
export function isValueExistsInElement(shouldInclude, content, domPathToContainer){
|
||||||
|
it(`should ${shouldInclude ? '' : 'not'} include '${content}'`, function () {
|
||||||
|
cy.get(domPathToContainer).then(htmlText => {
|
||||||
|
const allTextString = htmlText.text();
|
||||||
|
if (allTextString.includes(content) !== shouldInclude)
|
||||||
|
throw new Error(`One of the containers part contains ${content}`)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resizeToHugeMizu() {
|
||||||
|
cy.viewport(1920, 3500);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resizeToNormalMizu() {
|
||||||
|
cy.viewport(1920, 1080);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function verifyMinimumEntries() {
|
||||||
|
const minimumEntries = Cypress.env('minimumEntries');
|
||||||
|
it(`Making sure that mizu shows at least ${minimumEntries} entries`, async function () {
|
||||||
|
cy.get('#total-entries').then(number => {
|
||||||
|
const getNum = () => {
|
||||||
|
const numOfEntries = number.text();
|
||||||
|
return parseInt(numOfEntries);
|
||||||
|
};
|
||||||
|
cy.wrap({there: getNum}).invoke('there').should('be.gte', minimumEntries);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function leftTextCheck(entryNum, path, expectedText) {
|
||||||
|
cy.get(`#list #entry-${entryNum} ${path}`).invoke('text').should('eq', expectedText);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function leftOnHoverCheck(entryNum, path, filterName) {
|
||||||
|
cy.get(`#list #entry-${entryNum} ${path}`).trigger('mouseover');
|
||||||
|
cy.get(`#list #entry-${entryNum} .Queryable-Tooltip`).invoke('text').should('match', new RegExp(filterName));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function rightTextCheck(path, expectedText) {
|
||||||
|
cy.get(`#rightSideContainer ${path}`).should('have.text', expectedText);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function rightOnHoverCheck(path, expectedText) {
|
||||||
|
cy.get(`#rightSideContainer ${path}`).trigger('mouseover');
|
||||||
|
cy.get(`#rightSideContainer .Queryable-Tooltip`).invoke('text').should('match', new RegExp(expectedText));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function checkThatAllEntriesShown() {
|
||||||
|
cy.get('#entries-length').then(number => {
|
||||||
|
if (number.text() === '1')
|
||||||
|
cy.get('[title="Fetch old records"]').click();
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,8 +1,16 @@
|
|||||||
it('check', function () {
|
import {findLineAndCheck, getExpectedDetailsDict} from "../testHelpers/StatusBarHelper";
|
||||||
cy.visit(`http://localhost:${Cypress.env('port')}/`);
|
import {verifyMinimumEntries} from "../testHelpers/TrafficHelper";
|
||||||
|
|
||||||
cy.get('.header').should('be.visible');
|
it('check', function () {
|
||||||
cy.get('.TrafficPageHeader').should('be.visible');
|
const podName = Cypress.env('name'), namespace = Cypress.env('namespace');
|
||||||
cy.get('.TrafficPage-ListContainer').should('be.visible');
|
const port = Cypress.env('port');
|
||||||
cy.get('.TrafficPage-Container').should('be.visible');
|
cy.intercept('GET', `http://localhost:${port}/status/tap`).as('statusTap');
|
||||||
|
|
||||||
|
cy.visit(`http://localhost:${port}`);
|
||||||
|
cy.wait('@statusTap').its('response.statusCode').should('match', /^2\d{2}/);
|
||||||
|
|
||||||
|
verifyMinimumEntries();
|
||||||
|
|
||||||
|
cy.get('.podsCount').trigger('mouseover');
|
||||||
|
findLineAndCheck(getExpectedDetailsDict(podName, namespace));
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import {
|
||||||
|
checkThatAllEntriesShown,
|
||||||
|
isValueExistsInElement,
|
||||||
|
resizeToHugeMizu,
|
||||||
|
verifyMinimumEntries
|
||||||
|
} from "../testHelpers/TrafficHelper";
|
||||||
|
|
||||||
|
it('Loading Mizu', function () {
|
||||||
|
cy.visit(Cypress.env('testUrl'));
|
||||||
|
});
|
||||||
|
|
||||||
|
verifyMinimumEntries();
|
||||||
|
|
||||||
|
checkEntries();
|
||||||
|
|
||||||
|
function checkEntries() {
|
||||||
|
it('checking all entries', function () {
|
||||||
|
checkThatAllEntriesShown();
|
||||||
|
resizeToHugeMizu();
|
||||||
|
|
||||||
|
cy.get('#total-entries').then(number => {
|
||||||
|
const numOfEntries = parseInt(number.text());
|
||||||
|
[...Array(numOfEntries).keys()].map(checkEntry);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkEntry(entryIndex) {
|
||||||
|
cy.get(`#entry-${entryIndex}`).click();
|
||||||
|
cy.get('#tbody-Headers').should('be.visible');
|
||||||
|
isValueExistsInElement(false, 'Ignored-User-Agent', '#tbody-Headers');
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import {findLineAndCheck, getExpectedDetailsDict} from '../page_objects/StatusBar';
|
import {findLineAndCheck, getExpectedDetailsDict} from '../testHelpers/StatusBarHelper';
|
||||||
|
|
||||||
it('opening', function () {
|
it('opening', function () {
|
||||||
cy.visit(Cypress.env('testUrl'));
|
cy.visit(Cypress.env('testUrl'));
|
||||||
@@ -15,4 +15,3 @@ function doItFunc(number) {
|
|||||||
findLineAndCheck(getExpectedDetailsDict(podName, namespace));
|
findLineAndCheck(getExpectedDetailsDict(podName, namespace));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
10
acceptanceTests/cypress/integration/tests/NoRedact.js
Normal file
10
acceptanceTests/cypress/integration/tests/NoRedact.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import {isValueExistsInElement, verifyMinimumEntries} from '../testHelpers/TrafficHelper';
|
||||||
|
|
||||||
|
it('Loading Mizu', function () {
|
||||||
|
cy.visit(Cypress.env('testUrl'));
|
||||||
|
});
|
||||||
|
|
||||||
|
verifyMinimumEntries();
|
||||||
|
|
||||||
|
isValueExistsInElement(false, Cypress.env('redactHeaderContent'), '#tbody-Headers');
|
||||||
|
isValueExistsInElement(false, Cypress.env('redactBodyContent'), Cypress.env('bodyJsonClass'));
|
||||||
10
acceptanceTests/cypress/integration/tests/Redact.js
Normal file
10
acceptanceTests/cypress/integration/tests/Redact.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import {isValueExistsInElement, verifyMinimumEntries} from '../testHelpers/TrafficHelper';
|
||||||
|
|
||||||
|
it('Loading Mizu', function () {
|
||||||
|
cy.visit(Cypress.env('testUrl'));
|
||||||
|
});
|
||||||
|
|
||||||
|
verifyMinimumEntries();
|
||||||
|
|
||||||
|
isValueExistsInElement(true, Cypress.env('redactHeaderContent'), '#tbody-Headers');
|
||||||
|
isValueExistsInElement(true, Cypress.env('redactBodyContent'), Cypress.env('bodyJsonClass'));
|
||||||
155
acceptanceTests/cypress/integration/tests/Redis.js
Normal file
155
acceptanceTests/cypress/integration/tests/Redis.js
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
import {
|
||||||
|
leftOnHoverCheck,
|
||||||
|
leftTextCheck,
|
||||||
|
rightOnHoverCheck,
|
||||||
|
rightTextCheck,
|
||||||
|
} from "../testHelpers/TrafficHelper";
|
||||||
|
|
||||||
|
const valueTabs = {
|
||||||
|
response: 'RESPONSE',
|
||||||
|
request: 'REQUEST',
|
||||||
|
none: null
|
||||||
|
}
|
||||||
|
|
||||||
|
it('opening mizu', function () {
|
||||||
|
cy.visit(Cypress.env('testUrl'));
|
||||||
|
});
|
||||||
|
|
||||||
|
checkRedisFilterByMethod({
|
||||||
|
method: 'PING',
|
||||||
|
shouldCheckSummary: false,
|
||||||
|
valueTab: valueTabs.none
|
||||||
|
});
|
||||||
|
|
||||||
|
checkRedisFilterByMethod({
|
||||||
|
method: 'SET',
|
||||||
|
shouldCheckSummary: true,
|
||||||
|
valueTab: valueTabs.request,
|
||||||
|
valueRegex: /^\[value, keepttl]$/mg
|
||||||
|
});
|
||||||
|
|
||||||
|
checkRedisFilterByMethod({
|
||||||
|
method: 'EXISTS',
|
||||||
|
shouldCheckSummary: true,
|
||||||
|
valueTab: valueTabs.response,
|
||||||
|
valueRegex: /^1$/mg
|
||||||
|
});
|
||||||
|
|
||||||
|
checkRedisFilterByMethod({
|
||||||
|
method: 'GET',
|
||||||
|
shouldCheckSummary: true,
|
||||||
|
valueTab: valueTabs.response,
|
||||||
|
valueRegex: /^value$/mg
|
||||||
|
});
|
||||||
|
|
||||||
|
checkRedisFilterByMethod({
|
||||||
|
method: 'DEL',
|
||||||
|
shouldCheckSummary: true,
|
||||||
|
valueTab: valueTabs.response,
|
||||||
|
valueRegex: /^1$|^0$/mg
|
||||||
|
});
|
||||||
|
|
||||||
|
function checkRedisFilterByMethod(funcDict) {
|
||||||
|
const {method, shouldCheckSummary} = funcDict
|
||||||
|
const summaryDict = getSummeryDict();
|
||||||
|
const methodDict = getMethodDict(method);
|
||||||
|
const protocolDict = getProtocolDict();
|
||||||
|
|
||||||
|
it(`Testing the method: ${method}`, function () {
|
||||||
|
// applying filter
|
||||||
|
cy.get('.w-tc-editor-text').clear().type(`method == "${method}"`);
|
||||||
|
cy.get('[type="submit"]').click();
|
||||||
|
cy.get('.w-tc-editor').should('have.attr', 'style').and('include', Cypress.env('greenFilterColor'));
|
||||||
|
|
||||||
|
cy.get('#entries-length').then(number => {
|
||||||
|
// if the entries list isn't expanded it expands here
|
||||||
|
if (number.text() === '0' || number.text() === '1') // todo change when TRA-4262 is fixed
|
||||||
|
cy.get('[title="Fetch old records"]').click();
|
||||||
|
|
||||||
|
cy.get('#entries-length').should('not.have.text', '0').and('not.have.text', '1').then(() => {
|
||||||
|
cy.get(`#list [id]`).then(elements => {
|
||||||
|
const listElmWithIdAttr = Object.values(elements);
|
||||||
|
let doneCheckOnFirst = false;
|
||||||
|
|
||||||
|
listElmWithIdAttr.forEach(entry => {
|
||||||
|
if (entry?.id && entry.id.match(RegExp(/entry-(\d{2}|\d{1})$/gm))) {
|
||||||
|
const entryNum = getEntryNumById(entry.id);
|
||||||
|
|
||||||
|
leftTextCheck(entryNum, methodDict.pathLeft, methodDict.expectedText);
|
||||||
|
leftTextCheck(entryNum, protocolDict.pathLeft, protocolDict.expectedTextLeft);
|
||||||
|
if (shouldCheckSummary)
|
||||||
|
leftTextCheck(entryNum, summaryDict.pathLeft, summaryDict.expectedText);
|
||||||
|
|
||||||
|
if (!doneCheckOnFirst) {
|
||||||
|
deepCheck(funcDict, protocolDict, methodDict, summaryDict, entry);
|
||||||
|
doneCheckOnFirst = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function deepCheck(generalDict, protocolDict, methodDict, summaryDict, entry) {
|
||||||
|
const entryNum = getEntryNumById(entry.id);
|
||||||
|
const {shouldCheckSummary, valueTab, valueRegex} = generalDict;
|
||||||
|
|
||||||
|
leftOnHoverCheck(entryNum, methodDict.pathLeft, methodDict.expectedOnHover);
|
||||||
|
leftOnHoverCheck(entryNum, protocolDict.pathLeft, protocolDict.expectedOnHover);
|
||||||
|
if (shouldCheckSummary)
|
||||||
|
leftOnHoverCheck(entryNum, summaryDict.pathLeft, summaryDict.expectedOnHover);
|
||||||
|
|
||||||
|
cy.get(`#${entry.id}`).click();
|
||||||
|
|
||||||
|
rightTextCheck(methodDict.pathRight, methodDict.expectedText);
|
||||||
|
rightTextCheck(protocolDict.pathRight, protocolDict.expectedTextRight);
|
||||||
|
if (shouldCheckSummary)
|
||||||
|
rightTextCheck(summaryDict.pathRight, summaryDict.expectedText);
|
||||||
|
|
||||||
|
rightOnHoverCheck(methodDict.pathRight, methodDict.expectedOnHover);
|
||||||
|
rightOnHoverCheck(protocolDict.pathRight, protocolDict.expectedOnHover);
|
||||||
|
if (shouldCheckSummary)
|
||||||
|
rightOnHoverCheck(summaryDict.pathRight, summaryDict.expectedOnHover);
|
||||||
|
|
||||||
|
if (valueTab) {
|
||||||
|
if (valueTab === valueTabs.response)
|
||||||
|
cy.contains('Response').click();
|
||||||
|
cy.get(Cypress.env('bodyJsonClass')).then(text => {
|
||||||
|
expect(text.text()).to.match(valueRegex)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSummeryDict() {
|
||||||
|
return {
|
||||||
|
pathLeft: '> :nth-child(2) > :nth-child(1) > :nth-child(2) > :nth-child(2)',
|
||||||
|
pathRight: '> :nth-child(2) > :nth-child(1) > :nth-child(1) > :nth-child(2) > :nth-child(2)',
|
||||||
|
expectedText: 'key',
|
||||||
|
expectedOnHover: `summary == "key"`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMethodDict(method) {
|
||||||
|
return {
|
||||||
|
pathLeft: '> :nth-child(2) > :nth-child(1) > :nth-child(1) > :nth-child(2)',
|
||||||
|
pathRight: '> :nth-child(2) > :nth-child(1) > :nth-child(1) > :nth-child(1) > :nth-child(2)',
|
||||||
|
expectedText: method,
|
||||||
|
expectedOnHover: `method == "${method}"`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getProtocolDict() {
|
||||||
|
return {
|
||||||
|
pathLeft: '> :nth-child(1) > :nth-child(1)',
|
||||||
|
pathRight: '> :nth-child(1) > :nth-child(1) > :nth-child(1) > :nth-child(1)',
|
||||||
|
expectedTextLeft: 'REDIS',
|
||||||
|
expectedTextRight: 'Redis Serialization Protocol',
|
||||||
|
expectedOnHover: `redis`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEntryNumById (id) {
|
||||||
|
return parseInt(id.split('-')[1]);
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import {getExpectedDetailsDict, checkLine} from '../page_objects/StatusBar';
|
import {getExpectedDetailsDict, checkLine} from '../testHelpers/StatusBarHelper';
|
||||||
|
|
||||||
|
|
||||||
it('opening', function () {
|
it('opening', function () {
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import {isValueExistsInElement, verifyMinimumEntries} from "../testHelpers/TrafficHelper";
|
||||||
|
|
||||||
|
it('Loading Mizu', function () {
|
||||||
|
cy.visit(Cypress.env('testUrl'));
|
||||||
|
});
|
||||||
|
|
||||||
|
verifyMinimumEntries();
|
||||||
|
|
||||||
|
isValueExistsInElement(true, Cypress.env('regexMaskingBodyContent'), Cypress.env('bodyJsonClass'));
|
||||||
322
acceptanceTests/cypress/integration/tests/UiTest.js
Normal file
322
acceptanceTests/cypress/integration/tests/UiTest.js
Normal file
@@ -0,0 +1,322 @@
|
|||||||
|
import {findLineAndCheck, getExpectedDetailsDict} from "../testHelpers/StatusBarHelper";
|
||||||
|
import {
|
||||||
|
leftTextCheck,
|
||||||
|
resizeToHugeMizu,
|
||||||
|
resizeToNormalMizu,
|
||||||
|
rightOnHoverCheck,
|
||||||
|
leftOnHoverCheck,
|
||||||
|
rightTextCheck,
|
||||||
|
verifyMinimumEntries
|
||||||
|
} from "../testHelpers/TrafficHelper";
|
||||||
|
const refreshWaitTimeout = 10000;
|
||||||
|
|
||||||
|
it('opening mizu', function () {
|
||||||
|
cy.visit(Cypress.env('testUrl'));
|
||||||
|
});
|
||||||
|
|
||||||
|
verifyMinimumEntries();
|
||||||
|
|
||||||
|
it('top bar check', function () {
|
||||||
|
const podName1 = 'httpbin', namespace1 = 'mizu-tests';
|
||||||
|
const podName2 = 'httpbin2', namespace2 = 'mizu-tests';
|
||||||
|
|
||||||
|
cy.get('.podsCount').trigger('mouseover');
|
||||||
|
findLineAndCheck(getExpectedDetailsDict(podName1, namespace1));
|
||||||
|
findLineAndCheck(getExpectedDetailsDict(podName2, namespace2));
|
||||||
|
cy.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('filtering guide check', function () {
|
||||||
|
cy.get('[title="Open Filtering Guide (Cheatsheet)"]').click();
|
||||||
|
cy.get('#modal-modal-title').should('be.visible');
|
||||||
|
cy.get('[lang="en"]').click(0, 0);
|
||||||
|
cy.get('#modal-modal-title').should('not.exist');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('right side sanity test', function () {
|
||||||
|
cy.get('#entryDetailedTitleBodySize').then(sizeTopLine => {
|
||||||
|
const sizeOnTopLine = sizeTopLine.text().replace(' B', '');
|
||||||
|
cy.contains('Response').click();
|
||||||
|
cy.contains('Body Size (bytes)').parent().next().then(size => {
|
||||||
|
const bodySizeByDetails = size.text();
|
||||||
|
expect(sizeOnTopLine).to.equal(bodySizeByDetails, 'The body size in the top line should match the details in the response');
|
||||||
|
|
||||||
|
if (parseInt(bodySizeByDetails) < 0) {
|
||||||
|
throw new Error(`The body size cannot be negative. got the size: ${bodySizeByDetails}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
cy.get('#entryDetailedTitleElapsedTime').then(timeInMs => {
|
||||||
|
const time = timeInMs.text();
|
||||||
|
if (time < '0ms') {
|
||||||
|
throw new Error(`The time in the top line cannot be negative ${time}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
cy.get('#rightSideContainer [title="Status Code"]').then(status => {
|
||||||
|
const statusCode = status.text();
|
||||||
|
cy.contains('Status').parent().next().then(statusInDetails => {
|
||||||
|
const statusCodeInDetails = statusInDetails.text();
|
||||||
|
|
||||||
|
expect(statusCode).to.equal(statusCodeInDetails, 'The status code in the top line should match the status code in details');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
checkIllegalFilter('invalid filter');
|
||||||
|
|
||||||
|
checkFilter({
|
||||||
|
name: 'http',
|
||||||
|
leftSidePath: '> :nth-child(1) > :nth-child(1)',
|
||||||
|
leftSideExpectedText: 'HTTP',
|
||||||
|
rightSidePath: '[title=HTTP]',
|
||||||
|
rightSideExpectedText: 'Hypertext Transfer Protocol -- HTTP/1.1',
|
||||||
|
applyByEnter: true
|
||||||
|
});
|
||||||
|
|
||||||
|
checkFilter({
|
||||||
|
name: 'response.status == 200',
|
||||||
|
leftSidePath: '[title="Status Code"]',
|
||||||
|
leftSideExpectedText: '200',
|
||||||
|
rightSidePath: '> :nth-child(2) [title="Status Code"]',
|
||||||
|
rightSideExpectedText: '200',
|
||||||
|
applyByEnter: false
|
||||||
|
});
|
||||||
|
|
||||||
|
checkFilter({
|
||||||
|
name: 'src.name == ""',
|
||||||
|
leftSidePath: '[title="Source Name"]',
|
||||||
|
leftSideExpectedText: '[Unresolved]',
|
||||||
|
rightSidePath: '> :nth-child(2) [title="Source Name"]',
|
||||||
|
rightSideExpectedText: '[Unresolved]',
|
||||||
|
applyByEnter: false
|
||||||
|
});
|
||||||
|
|
||||||
|
checkFilter({
|
||||||
|
name: 'method == "GET"',
|
||||||
|
leftSidePath: '> :nth-child(3) > :nth-child(1) > :nth-child(1) > :nth-child(2)',
|
||||||
|
leftSideExpectedText: 'GET',
|
||||||
|
rightSidePath: '> :nth-child(2) > :nth-child(2) > :nth-child(1) > :nth-child(1) > :nth-child(2)',
|
||||||
|
rightSideExpectedText: 'GET',
|
||||||
|
applyByEnter: true
|
||||||
|
});
|
||||||
|
|
||||||
|
checkFilter({
|
||||||
|
name: 'summary == "/get"',
|
||||||
|
leftSidePath: '> :nth-child(3) > :nth-child(1) > :nth-child(2) > :nth-child(2)',
|
||||||
|
leftSideExpectedText: '/get',
|
||||||
|
rightSidePath: '> :nth-child(2) > :nth-child(2) > :nth-child(1) > :nth-child(2) > :nth-child(2)',
|
||||||
|
rightSideExpectedText: '/get',
|
||||||
|
applyByEnter: false
|
||||||
|
});
|
||||||
|
|
||||||
|
checkFilter({
|
||||||
|
name: 'dst.name == "httpbin.mizu-tests"',
|
||||||
|
leftSidePath: '> :nth-child(3) > :nth-child(2) > :nth-child(3) > :nth-child(2)',
|
||||||
|
leftSideExpectedText: 'httpbin.mizu-tests',
|
||||||
|
rightSidePath: '> :nth-child(2) > :nth-child(2) > :nth-child(2) > :nth-child(3) > :nth-child(2)',
|
||||||
|
rightSideExpectedText: 'httpbin.mizu-tests',
|
||||||
|
applyByEnter: false
|
||||||
|
});
|
||||||
|
|
||||||
|
checkFilter({
|
||||||
|
name: 'src.ip == "127.0.0.1"',
|
||||||
|
leftSidePath: '[title="Source IP"]',
|
||||||
|
leftSideExpectedText: '127.0.0.1',
|
||||||
|
rightSidePath: '> :nth-child(2) [title="Source IP"]',
|
||||||
|
rightSideExpectedText: '127.0.0.1',
|
||||||
|
applyByEnter: false
|
||||||
|
});
|
||||||
|
|
||||||
|
checkFilterNoResults('method == "POST"');
|
||||||
|
|
||||||
|
function checkFilterNoResults(filterName) {
|
||||||
|
it(`checking the filter: ${filterName}. Expecting no results`, function () {
|
||||||
|
cy.get('#total-entries').then(number => {
|
||||||
|
const totalEntries = number.text();
|
||||||
|
|
||||||
|
// applying the filter
|
||||||
|
cy.get('.w-tc-editor-text').type(filterName);
|
||||||
|
cy.get('.w-tc-editor').should('have.attr', 'style').and('include', Cypress.env('greenFilterColor'));
|
||||||
|
cy.get('[type="submit"]').click();
|
||||||
|
|
||||||
|
// waiting for the entries number to load
|
||||||
|
cy.get('#total-entries', {timeout: refreshWaitTimeout}).should('have.text', totalEntries);
|
||||||
|
|
||||||
|
// the DOM should show 0 entries
|
||||||
|
cy.get('#entries-length').should('have.text', '0');
|
||||||
|
|
||||||
|
// going through every potential entry and verifies that it doesn't exist
|
||||||
|
[...Array(parseInt(totalEntries)).keys()].map(shouldNotExist);
|
||||||
|
|
||||||
|
cy.get('[title="Fetch old records"]').click();
|
||||||
|
cy.get('#noMoreDataTop', {timeout: refreshWaitTimeout}).should('be.visible');
|
||||||
|
cy.get('#entries-length').should('have.text', '0'); // after loading all entries there should still be 0 entries
|
||||||
|
|
||||||
|
// reloading then waiting for the entries number to load
|
||||||
|
cy.reload();
|
||||||
|
cy.get('#total-entries', {timeout: refreshWaitTimeout}).should('have.text', totalEntries);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldNotExist(entryNum) {
|
||||||
|
cy.get(`entry-${entryNum}`).should('not.exist');
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkIllegalFilter(illegalFilterName) {
|
||||||
|
it(`should show red search bar with the input: ${illegalFilterName}`, function () {
|
||||||
|
cy.get('#total-entries').then(number => {
|
||||||
|
const totalEntries = number.text();
|
||||||
|
|
||||||
|
cy.get('.w-tc-editor-text').type(illegalFilterName);
|
||||||
|
cy.get('.w-tc-editor').should('have.attr', 'style').and('include', Cypress.env('redFilterColor'));
|
||||||
|
cy.get('[type="submit"]').click();
|
||||||
|
|
||||||
|
cy.get('[role="alert"]').should('be.visible');
|
||||||
|
cy.get('.w-tc-editor-text').clear();
|
||||||
|
|
||||||
|
// reloading then waiting for the entries number to load
|
||||||
|
cy.reload();
|
||||||
|
cy.get('#total-entries', {timeout: refreshWaitTimeout}).should('have.text', totalEntries);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function checkFilter(filterDetails){
|
||||||
|
const {name, leftSidePath, rightSidePath, rightSideExpectedText, leftSideExpectedText, applyByEnter} = filterDetails;
|
||||||
|
const entriesForDeeperCheck = 5;
|
||||||
|
|
||||||
|
it(`checking the filter: ${name}`, function () {
|
||||||
|
cy.get('#total-entries').then(number => {
|
||||||
|
const totalEntries = number.text();
|
||||||
|
|
||||||
|
// checks the hover on the last entry (the only one in DOM at the beginning)
|
||||||
|
leftOnHoverCheck(totalEntries - 1, leftSidePath, name);
|
||||||
|
|
||||||
|
// applying the filter with alt+enter or with the button
|
||||||
|
cy.get('.w-tc-editor-text').type(`${name}${applyByEnter ? '{alt+enter}' : ''}`);
|
||||||
|
cy.get('.w-tc-editor').should('have.attr', 'style').and('include', Cypress.env('greenFilterColor'));
|
||||||
|
if (!applyByEnter)
|
||||||
|
cy.get('[type="submit"]').click();
|
||||||
|
|
||||||
|
// only one entry in DOM after filtering, checking all checks on it
|
||||||
|
leftTextCheck(totalEntries - 1, leftSidePath, leftSideExpectedText);
|
||||||
|
leftOnHoverCheck(totalEntries - 1, leftSidePath, name);
|
||||||
|
rightTextCheck(rightSidePath, rightSideExpectedText);
|
||||||
|
rightOnHoverCheck(rightSidePath, name);
|
||||||
|
checkRightSideResponseBody();
|
||||||
|
|
||||||
|
cy.get('[title="Fetch old records"]').click();
|
||||||
|
resizeToHugeMizu();
|
||||||
|
|
||||||
|
// waiting for the entries number to load
|
||||||
|
cy.get('#entries-length', {timeout: refreshWaitTimeout}).should('have.text', totalEntries);
|
||||||
|
|
||||||
|
// checking only 'leftTextCheck' on all entries because the rest of the checks require more time
|
||||||
|
[...Array(parseInt(totalEntries)).keys()].forEach(entryNum => {
|
||||||
|
leftTextCheck(entryNum, leftSidePath, leftSideExpectedText);
|
||||||
|
});
|
||||||
|
|
||||||
|
// making the other 3 checks on the first X entries (longer time for each check)
|
||||||
|
deeperChcek(leftSidePath, rightSidePath, name, leftSideExpectedText, rightSideExpectedText, entriesForDeeperCheck);
|
||||||
|
|
||||||
|
// reloading then waiting for the entries number to load
|
||||||
|
resizeToNormalMizu();
|
||||||
|
cy.reload();
|
||||||
|
cy.get('#total-entries', {timeout: refreshWaitTimeout}).should('have.text', totalEntries);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function deeperChcek(leftSidePath, rightSidePath, filterName, leftSideExpectedText, rightSideExpectedText, entriesNumToCheck) {
|
||||||
|
[...Array(entriesNumToCheck).keys()].forEach(entryNum => {
|
||||||
|
leftOnHoverCheck(entryNum, leftSidePath, filterName);
|
||||||
|
|
||||||
|
cy.get(`#list #entry-${entryNum}`).click();
|
||||||
|
rightTextCheck(rightSidePath, rightSideExpectedText);
|
||||||
|
rightOnHoverCheck(rightSidePath, filterName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkRightSideResponseBody() {
|
||||||
|
cy.contains('Response').click();
|
||||||
|
clickCheckbox('Decode Base64');
|
||||||
|
|
||||||
|
cy.get(`${Cypress.env('bodyJsonClass')}`).then(value => {
|
||||||
|
const encodedBody = value.text();
|
||||||
|
const decodedBody = atob(encodedBody);
|
||||||
|
const responseBody = JSON.parse(decodedBody);
|
||||||
|
|
||||||
|
const expectdJsonBody = {
|
||||||
|
args: RegExp({}),
|
||||||
|
url: RegExp('http://.*/get'),
|
||||||
|
headers: {
|
||||||
|
"User-Agent": RegExp('[REDACTED]'),
|
||||||
|
"Accept-Encoding": RegExp('gzip'),
|
||||||
|
"X-Forwarded-Uri": RegExp('/api/v1/namespaces/.*/services/.*/proxy/get')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(responseBody.args).to.match(expectdJsonBody.args);
|
||||||
|
expect(responseBody.url).to.match(expectdJsonBody.url);
|
||||||
|
expect(responseBody.headers['User-Agent']).to.match(expectdJsonBody.headers['User-Agent']);
|
||||||
|
expect(responseBody.headers['Accept-Encoding']).to.match(expectdJsonBody.headers['Accept-Encoding']);
|
||||||
|
expect(responseBody.headers['X-Forwarded-Uri']).to.match(expectdJsonBody.headers['X-Forwarded-Uri']);
|
||||||
|
|
||||||
|
cy.get(`${Cypress.env('bodyJsonClass')}`).should('have.text', encodedBody);
|
||||||
|
clickCheckbox('Decode Base64');
|
||||||
|
|
||||||
|
cy.get(`${Cypress.env('bodyJsonClass')} > `).its('length').should('be.gt', 1).then(linesNum => {
|
||||||
|
cy.get(`${Cypress.env('bodyJsonClass')} > >`).its('length').should('be.gt', linesNum).then(jsonItemsNum => {
|
||||||
|
checkPrettyAndLineNums(jsonItemsNum, decodedBody);
|
||||||
|
|
||||||
|
clickCheckbox('Line numbers');
|
||||||
|
checkPrettyOrNothing(jsonItemsNum, decodedBody);
|
||||||
|
|
||||||
|
clickCheckbox('Pretty');
|
||||||
|
checkPrettyOrNothing(jsonItemsNum, decodedBody);
|
||||||
|
|
||||||
|
clickCheckbox('Line numbers');
|
||||||
|
checkOnlyLineNumberes(jsonItemsNum, decodedBody);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function clickCheckbox(type) {
|
||||||
|
cy.contains(`${type}`).prev().children().click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkPrettyAndLineNums(jsonItemsLen, decodedBody) {
|
||||||
|
decodedBody = decodedBody.replaceAll(' ', '');
|
||||||
|
cy.get(`${Cypress.env('bodyJsonClass')} >`).then(elements => {
|
||||||
|
const lines = Object.values(elements);
|
||||||
|
lines.forEach((line, index) => {
|
||||||
|
if (line.getAttribute) {
|
||||||
|
const cleanLine = getCleanLine(line);
|
||||||
|
const currentLineFromDecodedText = decodedBody.substring(0, cleanLine.length);
|
||||||
|
|
||||||
|
expect(cleanLine).to.equal(currentLineFromDecodedText, `expected the text in line number ${index + 1} to match the text that generated by the base64 decoding`)
|
||||||
|
|
||||||
|
decodedBody = decodedBody.substring(cleanLine.length);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCleanLine(lineElement) {
|
||||||
|
return (lineElement.innerText.substring(0, lineElement.innerText.length - 1)).replaceAll(' ', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkPrettyOrNothing(jsonItems, decodedBody) {
|
||||||
|
cy.get(`${Cypress.env('bodyJsonClass')} > `).should('have.length', jsonItems).then(text => {
|
||||||
|
const json = text.text();
|
||||||
|
expect(json).to.equal(decodedBody);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkOnlyLineNumberes(jsonItems, decodedText) {
|
||||||
|
cy.get(`${Cypress.env('bodyJsonClass')} >`).should('have.length', 1).and('have.text', decodedText);
|
||||||
|
cy.get(`${Cypress.env('bodyJsonClass')} > >`).should('have.length', jsonItems)
|
||||||
|
}
|
||||||
101
acceptanceTests/extensions_test.go
Normal file
101
acceptanceTests/extensions_test.go
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
package acceptanceTests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/go-redis/redis/v8"
|
||||||
|
"os/exec"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRedis(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("ignored acceptance test")
|
||||||
|
}
|
||||||
|
|
||||||
|
cliPath, cliPathErr := getCliPath()
|
||||||
|
if cliPathErr != nil {
|
||||||
|
t.Errorf("failed to get cli path, err: %v", cliPathErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tapCmdArgs := getDefaultTapCommandArgs()
|
||||||
|
|
||||||
|
tapNamespace := getDefaultTapNamespace()
|
||||||
|
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
||||||
|
|
||||||
|
tapCmd := exec.Command(cliPath, tapCmdArgs...)
|
||||||
|
t.Logf("running command: %v", tapCmd.String())
|
||||||
|
|
||||||
|
t.Cleanup(func() {
|
||||||
|
if err := cleanupCommand(tapCmd); err != nil {
|
||||||
|
t.Logf("failed to cleanup tap command, err: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if err := tapCmd.Start(); err != nil {
|
||||||
|
t.Errorf("failed to start tap command, err: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
apiServerUrl := getApiServerUrl(defaultApiServerPort)
|
||||||
|
|
||||||
|
if err := waitTapPodsReady(apiServerUrl); err != nil {
|
||||||
|
t.Errorf("failed to start tap pods on time, err: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
redisExternalIp, err := getServiceExternalIp(ctx, defaultNamespaceName, "redis")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to get redis external ip, err: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rdb := redis.NewClient(&redis.Options{
|
||||||
|
Addr: fmt.Sprintf("%v:6379", redisExternalIp),
|
||||||
|
})
|
||||||
|
|
||||||
|
for i := 0; i < defaultEntriesCount/5; i++ {
|
||||||
|
requestErr := rdb.Ping(ctx).Err()
|
||||||
|
if requestErr != nil {
|
||||||
|
t.Errorf("failed to send redis request, err: %v", requestErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < defaultEntriesCount/5; i++ {
|
||||||
|
requestErr := rdb.Set(ctx, "key", "value", -1).Err()
|
||||||
|
if requestErr != nil {
|
||||||
|
t.Errorf("failed to send redis request, err: %v", requestErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < defaultEntriesCount/5; i++ {
|
||||||
|
requestErr := rdb.Exists(ctx, "key").Err()
|
||||||
|
if requestErr != nil {
|
||||||
|
t.Errorf("failed to send redis request, err: %v", requestErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < defaultEntriesCount/5; i++ {
|
||||||
|
requestErr := rdb.Get(ctx, "key").Err()
|
||||||
|
if requestErr != nil {
|
||||||
|
t.Errorf("failed to send redis request, err: %v", requestErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < defaultEntriesCount/5; i++ {
|
||||||
|
requestErr := rdb.Del(ctx, "key").Err()
|
||||||
|
if requestErr != nil {
|
||||||
|
t.Errorf("failed to send redis request, err: %v", requestErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runCypressTests(t, "npx cypress run --spec \"cypress/integration/tests/Redis.js\"")
|
||||||
|
}
|
||||||
@@ -1,11 +1,47 @@
|
|||||||
module github.com/up9inc/mizu/tests
|
module github.com/up9inc/mizu/tests
|
||||||
|
|
||||||
go 1.16
|
go 1.17
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/go-redis/redis/v8 v8.11.4
|
||||||
github.com/up9inc/mizu/shared v0.0.0
|
github.com/up9inc/mizu/shared v0.0.0
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||||
|
k8s.io/apimachinery v0.21.2
|
||||||
|
k8s.io/client-go v0.21.2
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
|
github.com/go-logr/logr v0.4.0 // indirect
|
||||||
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.1.0 // indirect
|
||||||
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
|
github.com/google/go-cmp v0.5.6 // indirect
|
||||||
|
github.com/google/gofuzz v1.1.0 // indirect
|
||||||
|
github.com/googleapis/gnostic v0.4.1 // indirect
|
||||||
|
github.com/imdario/mergo v0.3.5 // indirect
|
||||||
|
github.com/json-iterator/go v1.1.10 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||||
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 // indirect
|
||||||
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887 // indirect
|
||||||
|
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect
|
||||||
|
golang.org/x/text v0.3.6 // indirect
|
||||||
|
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
||||||
|
google.golang.org/appengine v1.6.5 // indirect
|
||||||
|
google.golang.org/protobuf v1.26.0 // indirect
|
||||||
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
k8s.io/api v0.21.2 // indirect
|
||||||
|
k8s.io/klog/v2 v2.8.0 // indirect
|
||||||
|
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 // indirect
|
||||||
|
sigs.k8s.io/structured-merge-diff/v4 v4.1.0 // indirect
|
||||||
|
sigs.k8s.io/yaml v1.2.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/up9inc/mizu/shared v0.0.0 => ../shared
|
replace github.com/up9inc/mizu/shared v0.0.0 => ../shared
|
||||||
|
|||||||
@@ -57,8 +57,11 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB
|
|||||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||||
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
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 v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
|
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
@@ -79,6 +82,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=
|
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||||
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
@@ -96,6 +101,7 @@ github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwo
|
|||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
|
github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
@@ -154,7 +160,10 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
|
|||||||
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
|
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
|
||||||
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
|
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
|
||||||
github.com/go-openapi/validate v0.19.8/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
|
github.com/go-openapi/validate v0.19.8/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
|
||||||
|
github.com/go-redis/redis/v8 v8.11.4 h1:kHoYkfZP6+pe04aFTnhDH6GDROa5yJdHJVNxV3F46Tg=
|
||||||
|
github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM=
|
github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||||
@@ -185,6 +194,9 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
|
|||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
|
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||||
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk=
|
github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk=
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
@@ -194,8 +206,10 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
|||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
|
|
||||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||||
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
|
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
|
||||||
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
@@ -212,10 +226,10 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
|||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
|
github.com/googleapis/gnostic v0.4.1 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyycI+I=
|
||||||
github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
|
github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||||
@@ -243,6 +257,7 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p
|
|||||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
|
github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q=
|
||||||
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
@@ -304,13 +319,23 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
|
|||||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
|
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||||
|
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||||
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
||||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||||
|
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
||||||
|
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
|
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||||
|
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||||
|
github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c=
|
||||||
|
github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
|
||||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
@@ -375,6 +400,7 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH
|
|||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
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/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||||
@@ -470,14 +496,17 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7 h1:OgUuv8lsRpBibGNbSizVwKWlysjaNzmC9gYMhPVfqFM=
|
|
||||||
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0=
|
||||||
|
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/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-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-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@@ -504,10 +533,12 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -522,21 +553,27 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887 h1:dXfMednGJh/SUUFjTLsWJz3P+TQt9qnR11GgeI3vWKs=
|
||||||
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
|
||||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
|
|
||||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE=
|
||||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
@@ -577,6 +614,7 @@ golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapK
|
|||||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||||
golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@@ -598,6 +636,7 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
|
|||||||
google.golang.org/appengine v1.4.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.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
|
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
|
||||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
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-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
@@ -635,6 +674,9 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
|||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||||
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
@@ -647,6 +689,7 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
|||||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
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/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/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
@@ -655,6 +698,7 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8/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/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
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.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-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
@@ -673,6 +717,7 @@ k8s.io/api v0.21.2/go.mod h1:Lv6UGJZ1rlMI1qusN8ruAp9PUBFyBwpEHAdG24vIsiU=
|
|||||||
k8s.io/apimachinery v0.21.2 h1:vezUc/BHqWlQDnZ+XkrpXSmnANSLbpnlpwo0Lhk0gpc=
|
k8s.io/apimachinery v0.21.2 h1:vezUc/BHqWlQDnZ+XkrpXSmnANSLbpnlpwo0Lhk0gpc=
|
||||||
k8s.io/apimachinery v0.21.2/go.mod h1:CdTY8fU/BlvAbJ2z/8kBwimGki5Zp8/fbVuLY8gJumM=
|
k8s.io/apimachinery v0.21.2/go.mod h1:CdTY8fU/BlvAbJ2z/8kBwimGki5Zp8/fbVuLY8gJumM=
|
||||||
k8s.io/cli-runtime v0.21.2/go.mod h1:8u/jFcM0QpoI28f6sfrAAIslLCXUYKD5SsPPMWiHYrI=
|
k8s.io/cli-runtime v0.21.2/go.mod h1:8u/jFcM0QpoI28f6sfrAAIslLCXUYKD5SsPPMWiHYrI=
|
||||||
|
k8s.io/client-go v0.21.2 h1:Q1j4L/iMN4pTw6Y4DWppBoUxgKO8LbffEMVEV00MUp0=
|
||||||
k8s.io/client-go v0.21.2/go.mod h1:HdJ9iknWpbl3vMGtib6T2PyI/VYxiZfq936WNVHBRrA=
|
k8s.io/client-go v0.21.2/go.mod h1:HdJ9iknWpbl3vMGtib6T2PyI/VYxiZfq936WNVHBRrA=
|
||||||
k8s.io/code-generator v0.21.2/go.mod h1:8mXJDCB7HcRo1xiEQstcguZkbxZaqeUOrO9SsicWs3U=
|
k8s.io/code-generator v0.21.2/go.mod h1:8mXJDCB7HcRo1xiEQstcguZkbxZaqeUOrO9SsicWs3U=
|
||||||
k8s.io/component-base v0.21.2/go.mod h1:9lvmIThzdlrJj5Hp8Z/TOgIkdfsNARQ1pT+3PByuiuc=
|
k8s.io/component-base v0.21.2/go.mod h1:9lvmIThzdlrJj5Hp8Z/TOgIkdfsNARQ1pT+3PByuiuc=
|
||||||
@@ -686,6 +731,7 @@ k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
|
|||||||
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE=
|
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE=
|
||||||
k8s.io/kubectl v0.21.2/go.mod h1:PgeUclpG8VVmmQIl8zpLar3IQEpFc9mrmvlwY3CK1xo=
|
k8s.io/kubectl v0.21.2/go.mod h1:PgeUclpG8VVmmQIl8zpLar3IQEpFc9mrmvlwY3CK1xo=
|
||||||
k8s.io/metrics v0.21.2/go.mod h1:wzlOINZMCtWq8dR9gHlyaOemmYlOpAoldEIXE82gAhI=
|
k8s.io/metrics v0.21.2/go.mod h1:wzlOINZMCtWq8dR9gHlyaOemmYlOpAoldEIXE82gAhI=
|
||||||
|
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw=
|
||||||
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||||
|
|||||||
@@ -36,15 +36,24 @@ kubectl create deployment httpbin2 --image=kennethreitz/httpbin -n mizu-tests
|
|||||||
|
|
||||||
kubectl create deployment httpbin --image=kennethreitz/httpbin -n mizu-tests2
|
kubectl create deployment httpbin --image=kennethreitz/httpbin -n mizu-tests2
|
||||||
|
|
||||||
|
echo "Creating redis deployment"
|
||||||
|
kubectl create deployment redis --image=redis -n mizu-tests
|
||||||
|
|
||||||
echo "Creating httpbin services"
|
echo "Creating httpbin services"
|
||||||
kubectl expose deployment httpbin --type=NodePort --port=80 -n mizu-tests
|
kubectl expose deployment httpbin --type=NodePort --port=80 -n mizu-tests
|
||||||
kubectl expose deployment httpbin2 --type=NodePort --port=80 -n mizu-tests
|
kubectl expose deployment httpbin2 --type=NodePort --port=80 -n mizu-tests
|
||||||
|
|
||||||
kubectl expose deployment httpbin --type=NodePort --port=80 -n mizu-tests2
|
kubectl expose deployment httpbin --type=NodePort --port=80 -n mizu-tests2
|
||||||
|
|
||||||
|
echo "Creating redis service"
|
||||||
|
kubectl expose deployment redis --type=LoadBalancer --port=6379 -n mizu-tests
|
||||||
|
|
||||||
echo "Starting proxy"
|
echo "Starting proxy"
|
||||||
kubectl proxy --port=8080 &
|
kubectl proxy --port=8080 &
|
||||||
|
|
||||||
|
echo "Starting tunnel"
|
||||||
|
minikube tunnel &
|
||||||
|
|
||||||
echo "Setting minikube docker env"
|
echo "Setting minikube docker env"
|
||||||
eval $(minikube docker-env)
|
eval $(minikube docker-env)
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package acceptanceTests
|
|||||||
import (
|
import (
|
||||||
"archive/zip"
|
"archive/zip"
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -63,35 +62,7 @@ func TestTap(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
entriesCheckFunc := func() error {
|
runCypressTests(t, "npx cypress run --spec \"cypress/integration/tests/UiTest.js\"")
|
||||||
timestamp := time.Now().UnixNano() / int64(time.Millisecond)
|
|
||||||
|
|
||||||
entries, err := getDBEntries(timestamp, entriesCount, 1*time.Second)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = checkEntriesAtLeast(entries, 1)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
entry := entries[0]
|
|
||||||
|
|
||||||
entryUrl := fmt.Sprintf("%v/entries/%v", apiServerUrl, entry["id"])
|
|
||||||
requestResult, requestErr := executeHttpGetRequest(entryUrl)
|
|
||||||
if requestErr != nil {
|
|
||||||
return fmt.Errorf("failed to get entry, err: %v", requestErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if requestResult == nil {
|
|
||||||
return fmt.Errorf("unexpected nil entry result")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err := retriesExecute(shortRetriesCount, entriesCheckFunc); err != nil {
|
|
||||||
t.Errorf("%v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,7 +110,16 @@ func TestTapGuiPort(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
runCypressTests(t, fmt.Sprintf("npx cypress run --spec \"cypress/integration/tests/GuiPort.js\" --env port=%d", guiPort))
|
proxyUrl := getProxyUrl(defaultNamespaceName, defaultServiceName)
|
||||||
|
for i := 0; i < defaultEntriesCount; i++ {
|
||||||
|
if _, requestErr := executeHttpGetRequest(fmt.Sprintf("%v/get", proxyUrl)); requestErr != nil {
|
||||||
|
t.Errorf("failed to send proxy request, err: %v", requestErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runCypressTests(t, fmt.Sprintf("npx cypress run --spec \"cypress/integration/tests/GuiPort.js\" --env name=%v,namespace=%v,port=%d",
|
||||||
|
"httpbin", "mizu-tests", guiPort))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -378,59 +358,7 @@ func TestTapRedact(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
redactCheckFunc := func() error {
|
runCypressTests(t, "npx cypress run --spec \"cypress/integration/tests/Redact.js\"")
|
||||||
timestamp := time.Now().UnixNano() / int64(time.Millisecond)
|
|
||||||
|
|
||||||
entries, err := getDBEntries(timestamp, defaultEntriesCount, 1*time.Second)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = checkEntriesAtLeast(entries, 1)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
firstEntry := entries[0]
|
|
||||||
|
|
||||||
entryUrl := fmt.Sprintf("%v/entries/%v", apiServerUrl, firstEntry["id"])
|
|
||||||
requestResult, requestErr := executeHttpGetRequest(entryUrl)
|
|
||||||
if requestErr != nil {
|
|
||||||
return fmt.Errorf("failed to get entry, err: %v", requestErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
entry := requestResult.(map[string]interface{})["data"].(map[string]interface{})
|
|
||||||
request := entry["request"].(map[string]interface{})
|
|
||||||
|
|
||||||
headers := request["_headers"].([]interface{})
|
|
||||||
for _, headerInterface := range headers {
|
|
||||||
header := headerInterface.(map[string]interface{})
|
|
||||||
if header["name"].(string) != "User-Header" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
userHeader := header["value"].(string)
|
|
||||||
if userHeader != "[REDACTED]" {
|
|
||||||
return fmt.Errorf("unexpected result - user agent is not redacted")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
postData := request["postData"].(map[string]interface{})
|
|
||||||
textDataStr := postData["text"].(string)
|
|
||||||
|
|
||||||
var textData map[string]string
|
|
||||||
if parseErr := json.Unmarshal([]byte(textDataStr), &textData); parseErr != nil {
|
|
||||||
return fmt.Errorf("failed to parse text data, err: %v", parseErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if textData["User"] != "[REDACTED]" {
|
|
||||||
return fmt.Errorf("unexpected result - user in body is not redacted")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err := retriesExecute(shortRetriesCount, redactCheckFunc); err != nil {
|
|
||||||
t.Errorf("%v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTapNoRedact(t *testing.T) {
|
func TestTapNoRedact(t *testing.T) {
|
||||||
@@ -482,59 +410,7 @@ func TestTapNoRedact(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
redactCheckFunc := func() error {
|
runCypressTests(t, "npx cypress run --spec \"cypress/integration/tests/NoRedact.js\"")
|
||||||
timestamp := time.Now().UnixNano() / int64(time.Millisecond)
|
|
||||||
|
|
||||||
entries, err := getDBEntries(timestamp, defaultEntriesCount, 1*time.Second)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = checkEntriesAtLeast(entries, 1)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
firstEntry := entries[0]
|
|
||||||
|
|
||||||
entryUrl := fmt.Sprintf("%v/entries/%v", apiServerUrl, firstEntry["id"])
|
|
||||||
requestResult, requestErr := executeHttpGetRequest(entryUrl)
|
|
||||||
if requestErr != nil {
|
|
||||||
return fmt.Errorf("failed to get entry, err: %v", requestErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
entry := requestResult.(map[string]interface{})["data"].(map[string]interface{})
|
|
||||||
request := entry["request"].(map[string]interface{})
|
|
||||||
|
|
||||||
headers := request["_headers"].([]interface{})
|
|
||||||
for _, headerInterface := range headers {
|
|
||||||
header := headerInterface.(map[string]interface{})
|
|
||||||
if header["name"].(string) != "User-Header" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
userHeader := header["value"].(string)
|
|
||||||
if userHeader == "[REDACTED]" {
|
|
||||||
return fmt.Errorf("unexpected result - user agent is redacted")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
postData := request["postData"].(map[string]interface{})
|
|
||||||
textDataStr := postData["text"].(string)
|
|
||||||
|
|
||||||
var textData map[string]string
|
|
||||||
if parseErr := json.Unmarshal([]byte(textDataStr), &textData); parseErr != nil {
|
|
||||||
return fmt.Errorf("failed to parse text data, err: %v", parseErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if textData["User"] == "[REDACTED]" {
|
|
||||||
return fmt.Errorf("unexpected result - user in body is redacted")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err := retriesExecute(shortRetriesCount, redactCheckFunc); err != nil {
|
|
||||||
t.Errorf("%v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTapRegexMasking(t *testing.T) {
|
func TestTapRegexMasking(t *testing.T) {
|
||||||
@@ -585,41 +461,8 @@ func TestTapRegexMasking(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
redactCheckFunc := func() error {
|
runCypressTests(t, "npx cypress run --spec \"cypress/integration/tests/RegexMasking.js\"")
|
||||||
timestamp := time.Now().UnixNano() / int64(time.Millisecond)
|
|
||||||
|
|
||||||
entries, err := getDBEntries(timestamp, defaultEntriesCount, 1*time.Second)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = checkEntriesAtLeast(entries, 1)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
firstEntry := entries[0]
|
|
||||||
|
|
||||||
entryUrl := fmt.Sprintf("%v/entries/%v", apiServerUrl, firstEntry["id"])
|
|
||||||
requestResult, requestErr := executeHttpGetRequest(entryUrl)
|
|
||||||
if requestErr != nil {
|
|
||||||
return fmt.Errorf("failed to get entry, err: %v", requestErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
entry := requestResult.(map[string]interface{})["data"].(map[string]interface{})
|
|
||||||
request := entry["request"].(map[string]interface{})
|
|
||||||
|
|
||||||
postData := request["postData"].(map[string]interface{})
|
|
||||||
textData := postData["text"].(string)
|
|
||||||
|
|
||||||
if textData != "[REDACTED]" {
|
|
||||||
return fmt.Errorf("unexpected result - body is not redacted")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err := retriesExecute(shortRetriesCount, redactCheckFunc); err != nil {
|
|
||||||
t.Errorf("%v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTapIgnoredUserAgents(t *testing.T) {
|
func TestTapIgnoredUserAgents(t *testing.T) {
|
||||||
@@ -680,45 +523,7 @@ func TestTapIgnoredUserAgents(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ignoredUserAgentsCheckFunc := func() error {
|
runCypressTests(t, "npx cypress run --spec \"cypress/integration/tests/IgnoredUserAgents.js\"")
|
||||||
timestamp := time.Now().UnixNano() / int64(time.Millisecond)
|
|
||||||
|
|
||||||
entries, err := getDBEntries(timestamp, defaultEntriesCount, 1*time.Second)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = checkEntriesAtLeast(entries, 1)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, entryInterface := range entries {
|
|
||||||
entryUrl := fmt.Sprintf("%v/entries/%v", apiServerUrl, entryInterface["id"])
|
|
||||||
requestResult, requestErr := executeHttpGetRequest(entryUrl)
|
|
||||||
if requestErr != nil {
|
|
||||||
return fmt.Errorf("failed to get entry, err: %v", requestErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
entry := requestResult.(map[string]interface{})["data"].(map[string]interface{})
|
|
||||||
request := entry["request"].(map[string]interface{})
|
|
||||||
|
|
||||||
headers := request["_headers"].([]interface{})
|
|
||||||
for _, headerInterface := range headers {
|
|
||||||
header := headerInterface.(map[string]interface{})
|
|
||||||
if header["name"].(string) != ignoredUserAgentCustomHeader {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("unexpected result - user agent is not ignored")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err := retriesExecute(shortRetriesCount, ignoredUserAgentsCheckFunc); err != nil {
|
|
||||||
t.Errorf("%v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTapDumpLogs(t *testing.T) {
|
func TestTapDumpLogs(t *testing.T) {
|
||||||
|
|||||||
@@ -2,21 +2,24 @@ package acceptanceTests
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
|
"k8s.io/client-go/util/homedir"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
"github.com/up9inc/mizu/shared"
|
"github.com/up9inc/mizu/shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -28,7 +31,6 @@ const (
|
|||||||
defaultServiceName = "httpbin"
|
defaultServiceName = "httpbin"
|
||||||
defaultEntriesCount = 50
|
defaultEntriesCount = 50
|
||||||
waitAfterTapPodsReady = 3 * time.Second
|
waitAfterTapPodsReady = 3 * time.Second
|
||||||
cleanCommandTimeout = 1 * time.Minute
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type PodDescriptor struct {
|
type PodDescriptor struct {
|
||||||
@@ -36,18 +38,6 @@ type PodDescriptor struct {
|
|||||||
Namespace string
|
Namespace string
|
||||||
}
|
}
|
||||||
|
|
||||||
func isPodDescriptorInPodArray(pods []map[string]interface{}, podDescriptor PodDescriptor) bool {
|
|
||||||
for _, pod := range pods {
|
|
||||||
podNamespace := pod["namespace"].(string)
|
|
||||||
podName := pod["name"].(string)
|
|
||||||
|
|
||||||
if podDescriptor.Namespace == podNamespace && strings.Contains(podName, podDescriptor.Name) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCliPath() (string, error) {
|
func getCliPath() (string, error) {
|
||||||
dir, filePathErr := os.Getwd()
|
dir, filePathErr := os.Getwd()
|
||||||
if filePathErr != nil {
|
if filePathErr != nil {
|
||||||
@@ -84,14 +74,39 @@ func getApiServerUrl(port uint16) string {
|
|||||||
return fmt.Sprintf("http://localhost:%v", port)
|
return fmt.Sprintf("http://localhost:%v", port)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getWebSocketUrl(port uint16) string {
|
func getServiceExternalIp(ctx context.Context, namespace string, service string) (string, error) {
|
||||||
return fmt.Sprintf("ws://localhost:%v/ws", port)
|
home := homedir.HomeDir()
|
||||||
|
configLoadingRules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: filepath.Join(home, ".kube", "config")}
|
||||||
|
clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
|
||||||
|
configLoadingRules,
|
||||||
|
&clientcmd.ConfigOverrides{
|
||||||
|
CurrentContext: "",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
restClientConfig, err := clientConfig.ClientConfig()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
clientSet, err := kubernetes.NewForConfig(restClientConfig)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceObj, err := clientSet.CoreV1().Services(namespace).Get(ctx, service, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
externalIp := serviceObj.Status.LoadBalancer.Ingress[0].IP
|
||||||
|
return externalIp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDefaultCommandArgs() []string {
|
func getDefaultCommandArgs() []string {
|
||||||
setFlag := "--set"
|
setFlag := "--set"
|
||||||
telemetry := "telemetry=false"
|
telemetry := "telemetry=false"
|
||||||
agentImage := "agent-image=gcr.io/up9-docker-hub/mizu/ci:0.0.0"
|
agentImage := "agent-image=gcr.io/up9-docker-hub/mizu/ci:0.0"
|
||||||
imagePullPolicy := "image-pull-policy=IfNotPresent"
|
imagePullPolicy := "image-pull-policy=IfNotPresent"
|
||||||
headless := "headless=true"
|
headless := "headless=true"
|
||||||
|
|
||||||
@@ -130,20 +145,6 @@ func getDefaultConfigCommandArgs() []string {
|
|||||||
return append([]string{configCommand}, defaultCmdArgs...)
|
return append([]string{configCommand}, defaultCmdArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDefaultCleanCommandArgs() []string {
|
|
||||||
cleanCommand := "clean"
|
|
||||||
defaultCmdArgs := getDefaultCommandArgs()
|
|
||||||
|
|
||||||
return append([]string{cleanCommand}, defaultCmdArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDefaultViewCommandArgs() []string {
|
|
||||||
viewCommand := "view"
|
|
||||||
defaultCmdArgs := getDefaultCommandArgs()
|
|
||||||
|
|
||||||
return append([]string{viewCommand}, defaultCmdArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func runCypressTests(t *testing.T, cypressRunCmd string) {
|
func runCypressTests(t *testing.T, cypressRunCmd string) {
|
||||||
cypressCmd := exec.Command("bash", "-c", cypressRunCmd)
|
cypressCmd := exec.Command("bash", "-c", cypressRunCmd)
|
||||||
t.Logf("running command: %v", cypressCmd.String())
|
t.Logf("running command: %v", cypressCmd.String())
|
||||||
@@ -183,16 +184,16 @@ func tryExecuteFunc(executeFunc func() error) (err interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func waitTapPodsReady(apiServerUrl string) error {
|
func waitTapPodsReady(apiServerUrl string) error {
|
||||||
resolvingUrl := fmt.Sprintf("%v/status/tappersCount", apiServerUrl)
|
resolvingUrl := fmt.Sprintf("%v/status/connectedTappersCount", apiServerUrl)
|
||||||
tapPodsReadyFunc := func() error {
|
tapPodsReadyFunc := func() error {
|
||||||
requestResult, requestErr := executeHttpGetRequest(resolvingUrl)
|
requestResult, requestErr := executeHttpGetRequest(resolvingUrl)
|
||||||
if requestErr != nil {
|
if requestErr != nil {
|
||||||
return requestErr
|
return requestErr
|
||||||
}
|
}
|
||||||
|
|
||||||
tappersCount := requestResult.(float64)
|
connectedTappersCount := requestResult.(float64)
|
||||||
if tappersCount == 0 {
|
if connectedTappersCount == 0 {
|
||||||
return fmt.Errorf("no tappers running")
|
return fmt.Errorf("no connected tappers running")
|
||||||
}
|
}
|
||||||
time.Sleep(waitAfterTapPodsReady)
|
time.Sleep(waitAfterTapPodsReady)
|
||||||
return nil
|
return nil
|
||||||
@@ -268,36 +269,6 @@ func executeHttpPostRequestWithHeaders(url string, headers map[string]string, bo
|
|||||||
return executeHttpRequest(response, requestErr)
|
return executeHttpRequest(response, requestErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runMizuClean() error {
|
|
||||||
cliPath, err := getCliPath()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanCmdArgs := getDefaultCleanCommandArgs()
|
|
||||||
|
|
||||||
cleanCmd := exec.Command(cliPath, cleanCmdArgs...)
|
|
||||||
|
|
||||||
commandDone := make(chan error)
|
|
||||||
go func() {
|
|
||||||
if err := cleanCmd.Run(); err != nil {
|
|
||||||
commandDone <- err
|
|
||||||
}
|
|
||||||
commandDone <- nil
|
|
||||||
}()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case err = <-commandDone:
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case <-time.After(cleanCommandTimeout):
|
|
||||||
return errors.New("clean command timed out")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func cleanupCommand(cmd *exec.Cmd) error {
|
func cleanupCommand(cmd *exec.Cmd) error {
|
||||||
if err := cmd.Process.Signal(syscall.SIGQUIT); err != nil {
|
if err := cmd.Process.Signal(syscall.SIGQUIT); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -310,17 +281,6 @@ func cleanupCommand(cmd *exec.Cmd) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPods(tapStatusInterface interface{}) ([]map[string]interface{}, error) {
|
|
||||||
tapPodsInterface := tapStatusInterface.([]interface{})
|
|
||||||
|
|
||||||
var pods []map[string]interface{}
|
|
||||||
for _, podInterface := range tapPodsInterface {
|
|
||||||
pods = append(pods, podInterface.(map[string]interface{}))
|
|
||||||
}
|
|
||||||
|
|
||||||
return pods, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getLogsPath() (string, error) {
|
func getLogsPath() (string, error) {
|
||||||
dir, filePathErr := os.Getwd()
|
dir, filePathErr := os.Getwd()
|
||||||
if filePathErr != nil {
|
if filePathErr != nil {
|
||||||
@@ -331,77 +291,6 @@ func getLogsPath() (string, error) {
|
|||||||
return logsPath, nil
|
return logsPath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// waitTimeout waits for the waitgroup for the specified max timeout.
|
|
||||||
// Returns true if waiting timed out.
|
|
||||||
func waitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {
|
|
||||||
channel := make(chan struct{})
|
|
||||||
go func() {
|
|
||||||
defer close(channel)
|
|
||||||
wg.Wait()
|
|
||||||
}()
|
|
||||||
select {
|
|
||||||
case <-channel:
|
|
||||||
return false // completed normally
|
|
||||||
case <-time.After(timeout):
|
|
||||||
return true // timed out
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkEntriesAtLeast checks whether the number of entries greater than or equal to n
|
|
||||||
func checkEntriesAtLeast(entries []map[string]interface{}, n int) error {
|
|
||||||
if len(entries) < n {
|
|
||||||
return fmt.Errorf("Unexpected entries result - Expected more than %d entries", n-1)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getDBEntries retrieves the entries from the database before the given timestamp.
|
|
||||||
// Also limits the results according to the limit parameter.
|
|
||||||
// Timeout for the WebSocket connection is defined by the timeout parameter.
|
|
||||||
func getDBEntries(timestamp int64, limit int, timeout time.Duration) (entries []map[string]interface{}, err error) {
|
|
||||||
query := fmt.Sprintf("timestamp < %d and limit(%d)", timestamp, limit)
|
|
||||||
webSocketUrl := getWebSocketUrl(defaultApiServerPort)
|
|
||||||
|
|
||||||
var connection *websocket.Conn
|
|
||||||
connection, _, err = websocket.DefaultDialer.Dial(webSocketUrl, nil)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer connection.Close()
|
|
||||||
|
|
||||||
handleWSConnection := func(wg *sync.WaitGroup) {
|
|
||||||
defer wg.Done()
|
|
||||||
for {
|
|
||||||
_, message, err := connection.ReadMessage()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var data map[string]interface{}
|
|
||||||
if err = json.Unmarshal([]byte(message), &data); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if data["messageType"] == "entry" {
|
|
||||||
entries = append(entries, data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = connection.WriteMessage(websocket.TextMessage, []byte(query))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
go handleWSConnection(&wg)
|
|
||||||
wg.Add(1)
|
|
||||||
|
|
||||||
waitTimeout(&wg, timeout)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func Contains(slice []string, containsValue string) bool {
|
func Contains(slice []string, containsValue string) bool {
|
||||||
for _, sliceValue := range slice {
|
for _, sliceValue := range slice {
|
||||||
if sliceValue == containsValue {
|
if sliceValue == containsValue {
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
# mizu agent
|
|
||||||
Agent for MIZU (API server and tapper)
|
|
||||||
Basic APIs:
|
|
||||||
* /stats - retrieve statistics of collected data
|
|
||||||
* /viewer - web ui
|
|
||||||
|
|
||||||
## Remote Debugging
|
|
||||||
### Setup remote debugging
|
|
||||||
1. Run `go get github.com/go-delve/delve/cmd/dlv`
|
|
||||||
2. Create a "Go Remote" run/debug configuration in Intellij, set to localhost:2345
|
|
||||||
3. Build and push a debug image using
|
|
||||||
`docker build . -t gcr.io/up9-docker-hub/mizu/debug:latest -f debug.Dockerfile && docker push gcr.io/up9-docker-hub/mizu/debug:latest`
|
|
||||||
|
|
||||||
### Connecting
|
|
||||||
1. Start mizu using the cli with the debug
|
|
||||||
image `mizu tap --set agent-image=gcr.io/up9-docker-hub/mizu/debug:latest {tapped_pod_name}`
|
|
||||||
2. Forward the debug port using `kubectl port-forward -n default mizu-api-server 2345:2345`
|
|
||||||
3. Run the run/debug configuration you've created earlier in Intellij.
|
|
||||||
|
|
||||||
<small>Do note that dlv won't start the api until a debugger connects to it.</small>
|
|
||||||
114
agent/go.mod
114
agent/go.mod
@@ -1,35 +1,139 @@
|
|||||||
module mizuserver
|
module github.com/up9inc/mizu/agent
|
||||||
|
|
||||||
go 1.16
|
go 1.17
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/antelman107/net-wait-go v0.0.0-20210623112055-cf684aebda7b
|
github.com/antelman107/net-wait-go v0.0.0-20210623112055-cf684aebda7b
|
||||||
|
github.com/chanced/openapi v0.0.7
|
||||||
github.com/djherbis/atime v1.0.0
|
github.com/djherbis/atime v1.0.0
|
||||||
|
github.com/elastic/go-elasticsearch/v7 v7.16.0
|
||||||
github.com/getkin/kin-openapi v0.76.0
|
github.com/getkin/kin-openapi v0.76.0
|
||||||
github.com/gin-contrib/static v0.0.1
|
github.com/gin-contrib/static v0.0.1
|
||||||
github.com/gin-gonic/gin v1.7.7
|
github.com/gin-gonic/gin v1.7.7
|
||||||
github.com/go-playground/locales v0.13.0
|
github.com/go-playground/locales v0.13.0
|
||||||
github.com/go-playground/universal-translator v0.17.0
|
github.com/go-playground/universal-translator v0.17.0
|
||||||
github.com/go-playground/validator/v10 v10.5.0
|
github.com/go-playground/validator/v10 v10.5.0
|
||||||
github.com/google/martian v2.1.0+incompatible
|
github.com/google/uuid v1.1.2
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.4.2
|
||||||
|
github.com/nav-inc/datetime v0.1.3
|
||||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
||||||
github.com/orcaman/concurrent-map v0.0.0-20210106121528-16402b402231
|
github.com/orcaman/concurrent-map v0.0.0-20210106121528-16402b402231
|
||||||
|
github.com/ory/keto-client-go v0.7.0-alpha.1
|
||||||
github.com/ory/kratos-client-go v0.8.2-alpha.1
|
github.com/ory/kratos-client-go v0.8.2-alpha.1
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/up9inc/basenine/client/go v0.0.0-20220110083745-04fbc6c2068d
|
github.com/stretchr/testify v1.7.0
|
||||||
|
github.com/up9inc/basenine/client/go v0.0.0-20220125035724-573fff0d5075
|
||||||
github.com/up9inc/mizu/shared v0.0.0
|
github.com/up9inc/mizu/shared v0.0.0
|
||||||
github.com/up9inc/mizu/tap v0.0.0
|
github.com/up9inc/mizu/tap v0.0.0
|
||||||
github.com/up9inc/mizu/tap/api v0.0.0
|
github.com/up9inc/mizu/tap/api v0.0.0
|
||||||
|
github.com/up9inc/mizu/tap/extensions/amqp v0.0.0
|
||||||
|
github.com/up9inc/mizu/tap/extensions/http v0.0.0
|
||||||
|
github.com/up9inc/mizu/tap/extensions/kafka v0.0.0
|
||||||
|
github.com/up9inc/mizu/tap/extensions/redis v0.0.0
|
||||||
|
github.com/wI2L/jsondiff v0.1.1
|
||||||
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0
|
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0
|
||||||
golang.org/x/text v0.3.5 // indirect
|
|
||||||
k8s.io/api v0.21.2
|
k8s.io/api v0.21.2
|
||||||
k8s.io/apimachinery v0.21.2
|
k8s.io/apimachinery v0.21.2
|
||||||
k8s.io/client-go v0.21.2
|
k8s.io/client-go v0.21.2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
cloud.google.com/go v0.65.0 // indirect
|
||||||
|
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||||
|
github.com/Azure/go-autorest/autorest v0.11.12 // indirect
|
||||||
|
github.com/Azure/go-autorest/autorest/adal v0.9.5 // indirect
|
||||||
|
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
|
||||||
|
github.com/Azure/go-autorest/logger v0.2.0 // indirect
|
||||||
|
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||||
|
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||||
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||||
|
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect
|
||||||
|
github.com/beevik/etree v1.1.0 // indirect
|
||||||
|
github.com/bradleyfalzon/tlsx v0.0.0-20170624122154-28fd0e59bac4 // indirect
|
||||||
|
github.com/chanced/dynamic v0.0.0-20210502140838-c010b5fc3e44 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/fatih/camelcase v1.0.0 // indirect
|
||||||
|
github.com/form3tech-oss/jwt-go v3.2.2+incompatible // indirect
|
||||||
|
github.com/ghodss/yaml v1.0.0 // indirect
|
||||||
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
|
github.com/go-logr/logr v0.4.0 // indirect
|
||||||
|
github.com/go-openapi/analysis v0.20.0 // indirect
|
||||||
|
github.com/go-openapi/errors v0.20.1 // indirect
|
||||||
|
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||||
|
github.com/go-openapi/jsonreference v0.19.5 // indirect
|
||||||
|
github.com/go-openapi/loads v0.20.2 // indirect
|
||||||
|
github.com/go-openapi/runtime v0.20.0 // indirect
|
||||||
|
github.com/go-openapi/spec v0.20.3 // indirect
|
||||||
|
github.com/go-openapi/strfmt v0.20.3 // indirect
|
||||||
|
github.com/go-openapi/swag v0.19.15 // indirect
|
||||||
|
github.com/go-openapi/validate v0.20.3 // indirect
|
||||||
|
github.com/go-stack/stack v1.8.0 // indirect
|
||||||
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.1.0 // indirect
|
||||||
|
github.com/golang/protobuf v1.4.3 // indirect
|
||||||
|
github.com/golang/snappy v0.0.1 // indirect
|
||||||
|
github.com/google/go-cmp v0.5.6 // indirect
|
||||||
|
github.com/google/gofuzz v1.1.0 // indirect
|
||||||
|
github.com/google/gopacket v1.1.19 // indirect
|
||||||
|
github.com/google/martian v2.1.0+incompatible // indirect
|
||||||
|
github.com/googleapis/gnostic v0.4.1 // indirect
|
||||||
|
github.com/hashicorp/golang-lru v0.5.1 // indirect
|
||||||
|
github.com/imdario/mergo v0.3.5 // indirect
|
||||||
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
|
github.com/json-iterator/go v1.1.10 // indirect
|
||||||
|
github.com/klauspost/compress v1.14.1 // indirect
|
||||||
|
github.com/leodido/go-urn v1.2.0 // indirect
|
||||||
|
github.com/mailru/easyjson v0.7.6 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.12 // indirect
|
||||||
|
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
||||||
|
github.com/moby/spdystream v0.2.0 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||||
|
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
|
||||||
|
github.com/ohler55/ojg v1.12.12 // indirect
|
||||||
|
github.com/oklog/ulid v1.3.1 // indirect
|
||||||
|
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||||
|
github.com/pierrec/lz4 v2.6.0+incompatible // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 // indirect
|
||||||
|
github.com/segmentio/kafka-go v0.4.27 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
github.com/tidwall/gjson v1.12.0 // indirect
|
||||||
|
github.com/tidwall/match v1.1.1 // indirect
|
||||||
|
github.com/tidwall/pretty v1.2.0 // indirect
|
||||||
|
github.com/tidwall/sjson v1.2.3 // indirect
|
||||||
|
github.com/ugorji/go/codec v1.1.7 // indirect
|
||||||
|
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect
|
||||||
|
go.mongodb.org/mongo-driver v1.5.1 // indirect
|
||||||
|
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 // indirect
|
||||||
|
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7 // indirect
|
||||||
|
golang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558 // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887 // indirect
|
||||||
|
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect
|
||||||
|
golang.org/x/text v0.3.5 // indirect
|
||||||
|
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
||||||
|
google.golang.org/appengine v1.6.6 // indirect
|
||||||
|
google.golang.org/protobuf v1.25.0 // indirect
|
||||||
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||||
|
k8s.io/klog/v2 v2.8.0 // indirect
|
||||||
|
k8s.io/kubectl v0.21.2 // indirect
|
||||||
|
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 // indirect
|
||||||
|
sigs.k8s.io/structured-merge-diff/v4 v4.1.0 // indirect
|
||||||
|
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||||
|
)
|
||||||
|
|
||||||
replace github.com/up9inc/mizu/shared v0.0.0 => ../shared
|
replace github.com/up9inc/mizu/shared v0.0.0 => ../shared
|
||||||
|
|
||||||
replace github.com/up9inc/mizu/tap v0.0.0 => ../tap
|
replace github.com/up9inc/mizu/tap v0.0.0 => ../tap
|
||||||
|
|
||||||
replace github.com/up9inc/mizu/tap/api v0.0.0 => ../tap/api
|
replace github.com/up9inc/mizu/tap/api v0.0.0 => ../tap/api
|
||||||
|
|
||||||
|
replace github.com/up9inc/mizu/tap/extensions/amqp v0.0.0 => ../tap/extensions/amqp
|
||||||
|
|
||||||
|
replace github.com/up9inc/mizu/tap/extensions/http v0.0.0 => ../tap/extensions/http
|
||||||
|
|
||||||
|
replace github.com/up9inc/mizu/tap/extensions/kafka v0.0.0 => ../tap/extensions/kafka
|
||||||
|
|
||||||
|
replace github.com/up9inc/mizu/tap/extensions/redis v0.0.0 => ../tap/extensions/redis
|
||||||
|
|||||||
215
agent/go.sum
215
agent/go.sum
@@ -54,7 +54,9 @@ github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YH
|
|||||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||||
|
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||||
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||||
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
|
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
@@ -70,6 +72,13 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV
|
|||||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||||
|
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
|
||||||
|
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
|
||||||
|
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef h1:46PFijGLmAjMPwCCCo7Jf0W6f9slllCkkv7vyc1yOSg=
|
||||||
|
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||||
|
github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48=
|
||||||
|
github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs=
|
||||||
|
github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
@@ -82,6 +91,12 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA
|
|||||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
|
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
|
||||||
|
github.com/chanced/cmpjson v0.0.0-20210415035445-da9262c1f20a h1:zG6t+4krPXcCKtLbjFvAh+fKN1d0qfD+RaCj+680OU8=
|
||||||
|
github.com/chanced/cmpjson v0.0.0-20210415035445-da9262c1f20a/go.mod h1:yhcmlFk1hxuZ+5XZbupzT/cEm/eE4ZvWbmsW1+Q/aZE=
|
||||||
|
github.com/chanced/dynamic v0.0.0-20210502140838-c010b5fc3e44 h1:4NOJMtvZaOA6cI2gkIuXk/2b5KTOvm/R4zyPy/yLCM4=
|
||||||
|
github.com/chanced/dynamic v0.0.0-20210502140838-c010b5fc3e44/go.mod h1:XVNfXN5kgZST4PQ0W/oBAHJku2OteCeHxjAbvfd0ARM=
|
||||||
|
github.com/chanced/openapi v0.0.7 h1:OmOBHCg/5ViUg0gaGxXBeEFoVBE8C2pHK4BO/AiD6k8=
|
||||||
|
github.com/chanced/openapi v0.0.7/go.mod h1:SxE2VMLPw+T7Vq8nwbVVhDF2PigvRF4n5XyqsVpRJGU=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
@@ -109,6 +124,11 @@ github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4Kfc
|
|||||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||||
|
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw=
|
||||||
|
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||||
|
github.com/elastic/go-elasticsearch/v7 v7.16.0 h1:GHsxDFXIAlhSleXun4kwA89P7kQFADRChqvgOPeYP5A=
|
||||||
|
github.com/elastic/go-elasticsearch/v7 v7.16.0/go.mod h1:OJ4wdbtDNk5g503kvlHLyErCgQwwzmDtaFC4XyOxXA4=
|
||||||
|
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc=
|
||||||
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||||
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||||
@@ -119,11 +139,16 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
|
|||||||
github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||||
github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses=
|
github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses=
|
||||||
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||||
|
github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww=
|
||||||
|
github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
|
||||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
|
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
|
||||||
|
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
|
||||||
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
|
github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
|
||||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||||
|
github.com/frankban/quicktest v1.11.3 h1:8sXhOn0uLys67V8EsXLc6eszDs8VXWxL3iRvebPhedY=
|
||||||
|
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
|
github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
|
||||||
@@ -156,10 +181,22 @@ github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70t
|
|||||||
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
|
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
|
||||||
github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
|
github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
|
||||||
github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
|
github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
|
||||||
|
github.com/go-openapi/analysis v0.19.4/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
|
||||||
github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU=
|
github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU=
|
||||||
|
github.com/go-openapi/analysis v0.19.10/go.mod h1:qmhS3VNFxBlquFJ0RGoDtylO9y4pgTAUNE9AEEMdlJQ=
|
||||||
|
github.com/go-openapi/analysis v0.19.16/go.mod h1:GLInF007N83Ad3m8a/CbQ5TPzdnGT7workfHwuVjNVk=
|
||||||
|
github.com/go-openapi/analysis v0.20.0 h1:UN09o0kNhleunxW7LR+KnltD0YrJ8FF03pSqvAN3Vro=
|
||||||
|
github.com/go-openapi/analysis v0.20.0/go.mod h1:BMchjvaHDykmRMsK40iPtvyOfFdMMxlOmQr9FBZk+Og=
|
||||||
github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
|
github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
|
||||||
github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
|
github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
|
||||||
github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
|
github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
|
||||||
|
github.com/go-openapi/errors v0.19.3/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
|
||||||
|
github.com/go-openapi/errors v0.19.6/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
|
||||||
|
github.com/go-openapi/errors v0.19.7/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
|
||||||
|
github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
|
||||||
|
github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
|
||||||
|
github.com/go-openapi/errors v0.20.1 h1:j23mMDtRxMwIobkpId7sWh7Ddcx4ivaoqUbfXx5P+a8=
|
||||||
|
github.com/go-openapi/errors v0.20.1/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
|
||||||
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||||
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
||||||
@@ -170,32 +207,73 @@ github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3Hfo
|
|||||||
github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||||
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
|
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
|
||||||
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
|
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
|
||||||
|
github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM=
|
||||||
|
github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
|
||||||
github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||||
github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||||
github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||||
github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
|
github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
|
||||||
|
github.com/go-openapi/loads v0.19.3/go.mod h1:YVfqhUCdahYwR3f3iiwQLhicVRvLlU/WO5WPaZvcvSI=
|
||||||
github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk=
|
github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk=
|
||||||
|
github.com/go-openapi/loads v0.19.5/go.mod h1:dswLCAdonkRufe/gSUC3gN8nTSaB9uaS2es0x5/IbjY=
|
||||||
|
github.com/go-openapi/loads v0.19.6/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc=
|
||||||
|
github.com/go-openapi/loads v0.19.7/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc=
|
||||||
|
github.com/go-openapi/loads v0.20.0/go.mod h1:2LhKquiE513rN5xC6Aan6lYOSddlL8Mp20AW9kpviM4=
|
||||||
|
github.com/go-openapi/loads v0.20.2 h1:z5p5Xf5wujMxS1y8aP+vxwW5qYT2zdJBbXKmQUG3lcc=
|
||||||
|
github.com/go-openapi/loads v0.20.2/go.mod h1:hTVUotJ+UonAMMZsvakEgmWKgtulweO9vYP2bQYKA/o=
|
||||||
github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
|
github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
|
||||||
github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
|
github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
|
||||||
github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
|
github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
|
||||||
|
github.com/go-openapi/runtime v0.19.15/go.mod h1:dhGWCTKRXlAfGnQG0ONViOZpjfg0m2gUt9nTQPQZuoo=
|
||||||
|
github.com/go-openapi/runtime v0.19.16/go.mod h1:5P9104EJgYcizotuXhEuUrzVc+j1RiSjahULvYmlv98=
|
||||||
|
github.com/go-openapi/runtime v0.19.24/go.mod h1:Lm9YGCeecBnUUkFTxPC4s1+lwrkJ0pthx8YvyjCfkgk=
|
||||||
|
github.com/go-openapi/runtime v0.20.0 h1:DEV4oYH28MqakaabtbxH0cjvlzFegi/15kfUVCfiZW0=
|
||||||
|
github.com/go-openapi/runtime v0.20.0/go.mod h1:2WnLRxMiOUWNN0UZskSkxW0+WXdfB1KmqRKCFH+ZWYk=
|
||||||
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||||
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||||
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
|
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
|
||||||
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||||
github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
|
github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
|
||||||
|
github.com/go-openapi/spec v0.19.6/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
|
||||||
|
github.com/go-openapi/spec v0.19.8/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
|
||||||
|
github.com/go-openapi/spec v0.19.15/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU=
|
||||||
|
github.com/go-openapi/spec v0.20.0/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU=
|
||||||
|
github.com/go-openapi/spec v0.20.1/go.mod h1:93x7oh+d+FQsmsieroS4cmR3u0p/ywH649a3qwC9OsQ=
|
||||||
|
github.com/go-openapi/spec v0.20.3 h1:uH9RQ6vdyPSs2pSy9fL8QPspDF2AMIMPtmK5coSSjtQ=
|
||||||
|
github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg=
|
||||||
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||||
github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||||
github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
|
github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
|
||||||
|
github.com/go-openapi/strfmt v0.19.2/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
|
||||||
github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
|
github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
|
||||||
|
github.com/go-openapi/strfmt v0.19.4/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk=
|
||||||
github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk=
|
github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk=
|
||||||
|
github.com/go-openapi/strfmt v0.19.11/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc=
|
||||||
|
github.com/go-openapi/strfmt v0.20.0/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc=
|
||||||
|
github.com/go-openapi/strfmt v0.20.2/go.mod h1:43urheQI9dNtE5lTZQfuFJvjYJKPrxicATpEfZwHUNk=
|
||||||
|
github.com/go-openapi/strfmt v0.20.3 h1:YVG4ZgPZ00km/lRHrIf7c6cKL5/4FAUtG2T9RxWAgDY=
|
||||||
|
github.com/go-openapi/strfmt v0.20.3/go.mod h1:43urheQI9dNtE5lTZQfuFJvjYJKPrxicATpEfZwHUNk=
|
||||||
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||||
github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||||
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||||
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
|
|
||||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||||
|
github.com/go-openapi/swag v0.19.7/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY=
|
||||||
|
github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY=
|
||||||
|
github.com/go-openapi/swag v0.19.12/go.mod h1:eFdyEBkTdoAf/9RXBvj4cr1nH7GD8Kzo5HTt47gr72M=
|
||||||
|
github.com/go-openapi/swag v0.19.13/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||||
|
github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||||
|
github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
|
||||||
|
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||||
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
|
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
|
||||||
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
|
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
|
||||||
|
github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo=
|
||||||
github.com/go-openapi/validate v0.19.8/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
|
github.com/go-openapi/validate v0.19.8/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
|
||||||
|
github.com/go-openapi/validate v0.19.10/go.mod h1:RKEZTUWDkxKQxN2jDT7ZnZi2bhZlbNMAuKvKB+IaGx8=
|
||||||
|
github.com/go-openapi/validate v0.19.12/go.mod h1:Rzou8hA/CBw8donlS6WNEUQupNvUZ0waH08tGe6kAQ4=
|
||||||
|
github.com/go-openapi/validate v0.19.15/go.mod h1:tbn/fdOwYHgrhPBzidZfJC2MIVvs9GA7monOmWBbeCI=
|
||||||
|
github.com/go-openapi/validate v0.20.1/go.mod h1:b60iJT+xNNLfaQJUqLI7946tYiFEOuE9E4k54HpKcJ0=
|
||||||
|
github.com/go-openapi/validate v0.20.3 h1:GZPPhhKSZrE8HjB4eEkoYAZmoWA4+tCemSgINH1/vKw=
|
||||||
|
github.com/go-openapi/validate v0.20.3/go.mod h1:goDdqVGiigM3jChcrYJxD2joalke3ZXeftD16byIjA4=
|
||||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||||
@@ -206,8 +284,34 @@ github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GO
|
|||||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||||
github.com/go-playground/validator/v10 v10.5.0 h1:X9rflw/KmpACwT8zdrm1upefpvdy6ur8d1kWyq6sg3E=
|
github.com/go-playground/validator/v10 v10.5.0 h1:X9rflw/KmpACwT8zdrm1upefpvdy6ur8d1kWyq6sg3E=
|
||||||
github.com/go-playground/validator/v10 v10.5.0/go.mod h1:xm76BBt941f7yWdGnI2DVPFFg1UK3YY04qifoXU3lOk=
|
github.com/go-playground/validator/v10 v10.5.0/go.mod h1:xm76BBt941f7yWdGnI2DVPFFg1UK3YY04qifoXU3lOk=
|
||||||
|
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
|
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
|
||||||
|
github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
|
||||||
|
github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
|
||||||
|
github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
|
||||||
|
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
|
||||||
|
github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs=
|
||||||
|
github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
|
||||||
|
github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
|
||||||
|
github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk=
|
||||||
|
github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28=
|
||||||
|
github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo=
|
||||||
|
github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk=
|
||||||
|
github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw=
|
||||||
|
github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360=
|
||||||
|
github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg=
|
||||||
|
github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE=
|
||||||
github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM=
|
github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM=
|
||||||
|
github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8=
|
||||||
|
github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
|
||||||
|
github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
|
||||||
|
github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
|
||||||
|
github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
|
||||||
|
github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
|
||||||
|
github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
|
||||||
|
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
@@ -242,6 +346,8 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
|
|||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||||
|
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk=
|
github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk=
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
@@ -253,8 +359,10 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
|
|
||||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||||
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
|
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
|
||||||
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
@@ -316,7 +424,13 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:
|
|||||||
github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q=
|
github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q=
|
||||||
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
|
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||||
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||||
|
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
|
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||||
|
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/json-iterator/go v1.1.9/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 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||||
@@ -325,13 +439,23 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
|
|||||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
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.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
|
github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
|
||||||
|
github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
|
||||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
|
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||||
|
github.com/klauspost/compress v1.9.8/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||||
|
github.com/klauspost/compress v1.14.1 h1:hLQYb23E8/fO+1u53d02A97a8UnsddcvYzq4ERRU4ds=
|
||||||
|
github.com/klauspost/compress v1.14.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/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/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||||
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
@@ -347,9 +471,13 @@ github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN
|
|||||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
|
|
||||||
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||||
|
github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||||
|
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
|
||||||
|
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||||
|
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
|
||||||
github.com/markbates/pkger v0.17.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI=
|
github.com/markbates/pkger v0.17.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI=
|
||||||
|
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
|
||||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||||
@@ -367,6 +495,12 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4
|
|||||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
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 v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
|
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
|
github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
|
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
|
||||||
|
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
|
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
|
||||||
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
|
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
|
||||||
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=
|
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
@@ -376,12 +510,18 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
|
|||||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
|
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
|
||||||
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
|
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
|
||||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||||
|
github.com/nav-inc/datetime v0.1.3 h1:PaybPUsScX+Cd3TEa1tYpfwU61deCEhMTlCO2hONm1c=
|
||||||
|
github.com/nav-inc/datetime v0.1.3/go.mod h1:gKGf5G+cW7qkTo5TC/sieNyz6lYdrA9cf1PNV+pXIOE=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
|
github.com/ohler55/ojg v1.12.12 h1:hepbQFn7GHAecTPmwS3j5dCiOLsOpzPLvhiqnlAVAoE=
|
||||||
|
github.com/ohler55/ojg v1.12.12/go.mod h1:LBbIVRAgoFbYBXQhRhuEpaJIqq+goSO63/FQ+nyJU88=
|
||||||
|
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
||||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||||
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
||||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
@@ -392,8 +532,12 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
|
|||||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
|
||||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
|
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
|
||||||
|
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
||||||
github.com/orcaman/concurrent-map v0.0.0-20210106121528-16402b402231 h1:fa50YL1pzKW+1SsBnJDOHppJN9stOEwS+CRWyUtyYGU=
|
github.com/orcaman/concurrent-map v0.0.0-20210106121528-16402b402231 h1:fa50YL1pzKW+1SsBnJDOHppJN9stOEwS+CRWyUtyYGU=
|
||||||
github.com/orcaman/concurrent-map v0.0.0-20210106121528-16402b402231/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI=
|
github.com/orcaman/concurrent-map v0.0.0-20210106121528-16402b402231/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI=
|
||||||
|
github.com/ory/keto-client-go v0.7.0-alpha.1 h1:8iZz37j7YXFvFBxO/lAqFkGyQK7KysToQhAE4aNFzqE=
|
||||||
|
github.com/ory/keto-client-go v0.7.0-alpha.1/go.mod h1:xtlyH3xz9lgzvYnuzFXeGwaui0Swc/hxnmqzY2g8AYs=
|
||||||
github.com/ory/kratos-client-go v0.8.2-alpha.1 h1:YlKhGOSZjounlB9iY4xSWlqHbyLYkeLzlLk8ZL7/nEM=
|
github.com/ory/kratos-client-go v0.8.2-alpha.1 h1:YlKhGOSZjounlB9iY4xSWlqHbyLYkeLzlLk8ZL7/nEM=
|
||||||
github.com/ory/kratos-client-go v0.8.2-alpha.1/go.mod h1:dOQIsar76K07wMPJD/6aMhrWyY+sFGEagLDLso1CpsA=
|
github.com/ory/kratos-client-go v0.8.2-alpha.1/go.mod h1:dOQIsar76K07wMPJD/6aMhrWyY+sFGEagLDLso1CpsA=
|
||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
@@ -401,7 +545,11 @@ github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaR
|
|||||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
|
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
|
||||||
|
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
|
||||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||||
|
github.com/pierrec/lz4 v2.6.0+incompatible h1:Ix9yFKn1nSPBLFl/yZknTp8TU5G4Ps0JDmguYK6iH1A=
|
||||||
|
github.com/pierrec/lz4 v2.6.0+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.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
@@ -428,15 +576,23 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
|
|||||||
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
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/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
|
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
|
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
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/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
|
github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 h1:TToq11gyfNlrMFZiYujSekIsPd9AmsA2Bj/iv+s4JHE=
|
||||||
|
github.com/santhosh-tekuri/jsonschema/v5 v5.0.0/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H/cDPdvJ/SZJQLWWXWGrZ0=
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||||
|
github.com/segmentio/kafka-go v0.4.27 h1:sIhEozeL/TLN2mZ5dkG462vcGEWYKS+u31sXPjKhAM4=
|
||||||
|
github.com/segmentio/kafka-go v0.4.27/go.mod h1:XzMcoMjSzDGHcIwpWUI7GB43iKZ2fTVmryPSGLf/MPg=
|
||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
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.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
|
github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
|
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
@@ -445,6 +601,7 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO
|
|||||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
|
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||||
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
||||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||||
@@ -457,6 +614,7 @@ github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5q
|
|||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
|
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
@@ -465,23 +623,41 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||||
|
github.com/tidwall/gjson v1.10.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
|
github.com/tidwall/gjson v1.12.0 h1:61wEp/qfvFnqKH/WCI3M8HuRut+mHT6Mr82QrFmM2SY=
|
||||||
|
github.com/tidwall/gjson v1.12.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
|
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||||
|
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||||
|
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||||
|
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
|
github.com/tidwall/sjson v1.2.3 h1:5+deguEhHSEjmuICXZ21uSSsXotWMA0orU783+Z7Cp8=
|
||||||
|
github.com/tidwall/sjson v1.2.3/go.mod h1:5WdjKx3AQMvCJ4RG6/2UYT7dLrGvJUV1x4jdTAyGvZs=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||||
github.com/up9inc/basenine/client/go v0.0.0-20220110083745-04fbc6c2068d h1:WTz53dcfqCIWZpZLQoHbIcNc21s0ZHEZH7EqMPp99qQ=
|
github.com/up9inc/basenine/client/go v0.0.0-20220125035724-573fff0d5075 h1:Tp0yckZkvb8BNC+bB4B+fBuQdomTner6bOte1S8SYCo=
|
||||||
github.com/up9inc/basenine/client/go v0.0.0-20220110083745-04fbc6c2068d/go.mod h1:SvJGPoa/6erhUQV7kvHBwM/0x5LyO6XaG2lUaCaKiUI=
|
github.com/up9inc/basenine/client/go v0.0.0-20220125035724-573fff0d5075/go.mod h1:SvJGPoa/6erhUQV7kvHBwM/0x5LyO6XaG2lUaCaKiUI=
|
||||||
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
|
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
|
||||||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA=
|
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA=
|
||||||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||||
|
github.com/wI2L/jsondiff v0.1.1 h1:r2TkoEet7E4JMO5+s1RCY2R0LrNPNHY6hbDeow2hRHw=
|
||||||
|
github.com/wI2L/jsondiff v0.1.1/go.mod h1:bAbJSAJXZtfOCZ5y3v7Mfb6UQa3DGdGFjQj1cNv8EcM=
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
|
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
|
||||||
|
github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
|
||||||
|
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
|
||||||
|
github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
|
||||||
|
github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
|
||||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
|
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
|
||||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||||
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=
|
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=
|
||||||
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
|
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
@@ -490,6 +666,13 @@ go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
|||||||
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||||
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||||
go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||||
|
go.mongodb.org/mongo-driver v1.3.0/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=
|
||||||
|
go.mongodb.org/mongo-driver v1.3.4/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=
|
||||||
|
go.mongodb.org/mongo-driver v1.4.3/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc=
|
||||||
|
go.mongodb.org/mongo-driver v1.4.4/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc=
|
||||||
|
go.mongodb.org/mongo-driver v1.4.6/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc=
|
||||||
|
go.mongodb.org/mongo-driver v1.5.1 h1:9nOVLGDfOaZ9R0tBumx/BcuqkbFpyTCU2r/Po7A2azI=
|
||||||
|
go.mongodb.org/mongo-driver v1.5.1/go.mod h1:gRXCHX4Jo7J0IJ1oDQyUxF7jfy19UfxniMS4xxMmUqw=
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
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.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
@@ -503,11 +686,15 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
|
|||||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||||
|
golang.org/x/crypto v0.0.0-20190506204251-e1dfcc566284/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
|
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
|
||||||
@@ -576,11 +763,15 @@ golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/
|
|||||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7 h1:OgUuv8lsRpBibGNbSizVwKWlysjaNzmC9gYMhPVfqFM=
|
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7 h1:OgUuv8lsRpBibGNbSizVwKWlysjaNzmC9gYMhPVfqFM=
|
||||||
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
@@ -594,6 +785,7 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/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-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-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@@ -609,10 +801,13 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/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-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-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -665,6 +860,7 @@ golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiT
|
|||||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/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-20181030221726-6c7e314b6563/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-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/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-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
@@ -672,9 +868,13 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
|
|||||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/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-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-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
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-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-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
@@ -819,6 +1019,8 @@ 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 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
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-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||||
@@ -863,5 +1065,6 @@ sigs.k8s.io/kustomize/kyaml v0.10.17/go.mod h1:mlQFagmkm1P+W4lZJbJ/yaxMd8PqMRSC4
|
|||||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.1.0 h1:C4r9BgJ98vrKnnVCjwCSXcWjWe0NKcUQkmzDXZXGwH8=
|
sigs.k8s.io/structured-merge-diff/v4 v4.1.0 h1:C4r9BgJ98vrKnnVCjwCSXcWjWe0NKcUQkmzDXZXGwH8=
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
|
||||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||||
|
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||||
|
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||||
|
|||||||
13
agent/keto/Dockerfile
Normal file
13
agent/keto/Dockerfile
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
FROM oryd/keto:v0.7.0-alpha.1-sqlite
|
||||||
|
|
||||||
|
USER root
|
||||||
|
|
||||||
|
RUN apk add sqlite
|
||||||
|
|
||||||
|
RUN mkdir -p /etc/config/keto
|
||||||
|
|
||||||
|
COPY ./keto.yml /etc/config/keto/keto.yml
|
||||||
|
COPY ./start.sh /opt/start.sh
|
||||||
|
RUN chmod +x /opt/start.sh
|
||||||
|
|
||||||
|
ENTRYPOINT ["/opt/start.sh"]
|
||||||
@@ -3,13 +3,13 @@ set -e
|
|||||||
|
|
||||||
GCP_PROJECT=up9-docker-hub
|
GCP_PROJECT=up9-docker-hub
|
||||||
REPOSITORY=gcr.io/$GCP_PROJECT
|
REPOSITORY=gcr.io/$GCP_PROJECT
|
||||||
SERVER_NAME=mizu
|
SERVER_NAME=mizu-keto
|
||||||
GIT_BRANCH=$(git branch | grep \* | cut -d ' ' -f2 | tr '[:upper:]' '[:lower:]')
|
GIT_BRANCH=$(git branch | grep \* | cut -d ' ' -f2 | tr '[:upper:]' '[:lower:]')
|
||||||
|
|
||||||
DOCKER_REPO=$REPOSITORY/$SERVER_NAME/$GIT_BRANCH
|
DOCKER_REPO=$REPOSITORY/$SERVER_NAME/$GIT_BRANCH
|
||||||
SEM_VER=${SEM_VER=0.0.0}
|
VER=${VER=0.0}
|
||||||
|
|
||||||
DOCKER_TAGGED_BUILDS=("$DOCKER_REPO:latest" "$DOCKER_REPO:$SEM_VER")
|
DOCKER_TAGGED_BUILDS=("$DOCKER_REPO:latest" "$DOCKER_REPO:$VER")
|
||||||
|
|
||||||
if [ "$GIT_BRANCH" = 'develop' -o "$GIT_BRANCH" = 'master' -o "$GIT_BRANCH" = 'main' ]
|
if [ "$GIT_BRANCH" = 'develop' -o "$GIT_BRANCH" = 'master' -o "$GIT_BRANCH" = 'main' ]
|
||||||
then
|
then
|
||||||
@@ -19,7 +19,7 @@ fi
|
|||||||
|
|
||||||
echo "building ${DOCKER_TAGGED_BUILDS[@]}"
|
echo "building ${DOCKER_TAGGED_BUILDS[@]}"
|
||||||
DOCKER_TAGS_ARGS=$(echo ${DOCKER_TAGGED_BUILDS[@]/#/-t }) # "-t FIRST_TAG -t SECOND_TAG ..."
|
DOCKER_TAGS_ARGS=$(echo ${DOCKER_TAGGED_BUILDS[@]/#/-t }) # "-t FIRST_TAG -t SECOND_TAG ..."
|
||||||
docker build -f debug.Dockerfile $DOCKER_TAGS_ARGS --build-arg SEM_VER=${SEM_VER} --build-arg BUILD_TIMESTAMP=${BUILD_TIMESTAMP} --build-arg GIT_BRANCH=${GIT_BRANCH} --build-arg COMMIT_HASH=${COMMIT_HASH} .
|
docker build $DOCKER_TAGS_ARGS --build-arg VER=${VER} --build-arg BUILD_TIMESTAMP=${BUILD_TIMESTAMP} --build-arg GIT_BRANCH=${GIT_BRANCH} --build-arg COMMIT_HASH=${COMMIT_HASH} .
|
||||||
|
|
||||||
for DOCKER_TAG in "${DOCKER_TAGGED_BUILDS[@]}"
|
for DOCKER_TAG in "${DOCKER_TAGGED_BUILDS[@]}"
|
||||||
do
|
do
|
||||||
22
agent/keto/keto.yml
Normal file
22
agent/keto/keto.yml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
version: v0.7.0-alpha.1
|
||||||
|
|
||||||
|
dsn: sqlite:///app/data/kratos.sqlite?_fk=true
|
||||||
|
|
||||||
|
log:
|
||||||
|
level: info
|
||||||
|
format: text
|
||||||
|
leak_sensitive_values: false
|
||||||
|
|
||||||
|
serve:
|
||||||
|
read:
|
||||||
|
host: 0.0.0.0
|
||||||
|
port: 4466
|
||||||
|
write:
|
||||||
|
host: 0.0.0.0
|
||||||
|
port: 4467
|
||||||
|
|
||||||
|
namespaces:
|
||||||
|
- id: 0
|
||||||
|
name: system
|
||||||
|
- id: 1
|
||||||
|
name: workspaces
|
||||||
4
agent/keto/start.sh
Normal file
4
agent/keto/start.sh
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
keto migrate up -c /etc/config/keto/keto.yml --yes # this initializes the db
|
||||||
|
keto serve -c /etc/config/keto/keto.yml # start keto
|
||||||
@@ -7,9 +7,9 @@ SERVER_NAME=mizu-kratos
|
|||||||
GIT_BRANCH=$(git branch | grep \* | cut -d ' ' -f2 | tr '[:upper:]' '[:lower:]')
|
GIT_BRANCH=$(git branch | grep \* | cut -d ' ' -f2 | tr '[:upper:]' '[:lower:]')
|
||||||
|
|
||||||
DOCKER_REPO=$REPOSITORY/$SERVER_NAME/$GIT_BRANCH
|
DOCKER_REPO=$REPOSITORY/$SERVER_NAME/$GIT_BRANCH
|
||||||
SEM_VER=${SEM_VER=0.0.0}
|
VER=${VER=0.0.0}
|
||||||
|
|
||||||
DOCKER_TAGGED_BUILDS=("$DOCKER_REPO:latest" "$DOCKER_REPO:$SEM_VER")
|
DOCKER_TAGGED_BUILDS=("$DOCKER_REPO:latest" "$DOCKER_REPO:$VER")
|
||||||
|
|
||||||
if [ "$GIT_BRANCH" = 'develop' -o "$GIT_BRANCH" = 'master' -o "$GIT_BRANCH" = 'main' ]
|
if [ "$GIT_BRANCH" = 'develop' -o "$GIT_BRANCH" = 'master' -o "$GIT_BRANCH" = 'main' ]
|
||||||
then
|
then
|
||||||
@@ -19,7 +19,7 @@ fi
|
|||||||
|
|
||||||
echo "building ${DOCKER_TAGGED_BUILDS[@]}"
|
echo "building ${DOCKER_TAGGED_BUILDS[@]}"
|
||||||
DOCKER_TAGS_ARGS=$(echo ${DOCKER_TAGGED_BUILDS[@]/#/-t }) # "-t FIRST_TAG -t SECOND_TAG ..."
|
DOCKER_TAGS_ARGS=$(echo ${DOCKER_TAGGED_BUILDS[@]/#/-t }) # "-t FIRST_TAG -t SECOND_TAG ..."
|
||||||
docker build $DOCKER_TAGS_ARGS --build-arg SEM_VER=${SEM_VER} --build-arg BUILD_TIMESTAMP=${BUILD_TIMESTAMP} --build-arg GIT_BRANCH=${GIT_BRANCH} --build-arg COMMIT_HASH=${COMMIT_HASH} .
|
docker build $DOCKER_TAGS_ARGS --build-arg VER=${VER} --build-arg BUILD_TIMESTAMP=${BUILD_TIMESTAMP} --build-arg GIT_BRANCH=${GIT_BRANCH} --build-arg COMMIT_HASH=${COMMIT_HASH} .
|
||||||
|
|
||||||
for DOCKER_TAG in "${DOCKER_TAGGED_BUILDS[@]}"
|
for DOCKER_TAG in "${DOCKER_TAGGED_BUILDS[@]}"
|
||||||
do
|
do
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ selfservice:
|
|||||||
log:
|
log:
|
||||||
level: info
|
level: info
|
||||||
format: text
|
format: text
|
||||||
leak_sensitive_values: true
|
leak_sensitive_values: false
|
||||||
|
|
||||||
secrets:
|
secrets:
|
||||||
cookie:
|
cookie:
|
||||||
|
|||||||
284
agent/main.go
284
agent/main.go
@@ -5,35 +5,23 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"mizuserver/pkg/api"
|
|
||||||
"mizuserver/pkg/config"
|
|
||||||
"mizuserver/pkg/controllers"
|
|
||||||
"mizuserver/pkg/middlewares"
|
|
||||||
"mizuserver/pkg/models"
|
|
||||||
"mizuserver/pkg/routes"
|
|
||||||
"mizuserver/pkg/up9"
|
|
||||||
"mizuserver/pkg/utils"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"plugin"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/models"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/utils"
|
||||||
|
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/api"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/app"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/config"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
|
|
||||||
"github.com/antelman107/net-wait-go/wait"
|
|
||||||
"github.com/gin-contrib/static"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"github.com/op/go-logging"
|
"github.com/op/go-logging"
|
||||||
basenine "github.com/up9inc/basenine/client/go"
|
|
||||||
"github.com/up9inc/mizu/shared"
|
"github.com/up9inc/mizu/shared"
|
||||||
"github.com/up9inc/mizu/shared/logger"
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
"github.com/up9inc/mizu/tap"
|
"github.com/up9inc/mizu/tap"
|
||||||
@@ -48,16 +36,10 @@ var namespace = flag.String("namespace", "", "Resolve IPs if they belong to reso
|
|||||||
var harsReaderMode = flag.Bool("hars-read", false, "Run in hars-read mode")
|
var harsReaderMode = flag.Bool("hars-read", false, "Run in hars-read mode")
|
||||||
var harsDir = flag.String("hars-dir", "", "Directory to read hars from")
|
var harsDir = flag.String("hars-dir", "", "Directory to read hars from")
|
||||||
|
|
||||||
var extensions []*tapApi.Extension // global
|
|
||||||
var extensionsMap map[string]*tapApi.Extension // global
|
|
||||||
|
|
||||||
var startTime int64
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
socketConnectionRetries = 30
|
socketConnectionRetries = 30
|
||||||
socketConnectionRetryDelay = time.Second * 2
|
socketConnectionRetryDelay = time.Second * 2
|
||||||
socketHandshakeTimeout = time.Second * 2
|
socketHandshakeTimeout = time.Second * 2
|
||||||
uiIndexPath = "./site/index.html"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -67,78 +49,20 @@ func main() {
|
|||||||
if err := config.LoadConfig(); err != nil {
|
if err := config.LoadConfig(); err != nil {
|
||||||
logger.Log.Fatalf("Error loading config file %v", err)
|
logger.Log.Fatalf("Error loading config file %v", err)
|
||||||
}
|
}
|
||||||
loadExtensions()
|
app.LoadExtensions()
|
||||||
|
|
||||||
if !*tapperMode && !*apiServerMode && !*standaloneMode && !*harsReaderMode {
|
if !*tapperMode && !*apiServerMode && !*standaloneMode && !*harsReaderMode {
|
||||||
panic("One of the flags --tap, --api or --standalone or --hars-read must be provided")
|
panic("One of the flags --tap, --api or --standalone or --hars-read must be provided")
|
||||||
}
|
}
|
||||||
|
|
||||||
if *standaloneMode {
|
if *standaloneMode {
|
||||||
api.StartResolving(*namespace)
|
runInStandaloneMode()
|
||||||
|
|
||||||
outputItemsChannel := make(chan *tapApi.OutputChannelItem)
|
|
||||||
filteredOutputItemsChannel := make(chan *tapApi.OutputChannelItem)
|
|
||||||
|
|
||||||
filteringOptions := getTrafficFilteringOptions()
|
|
||||||
hostMode := os.Getenv(shared.HostModeEnvVar) == "1"
|
|
||||||
tapOpts := &tap.TapOpts{HostMode: hostMode}
|
|
||||||
tap.StartPassiveTapper(tapOpts, outputItemsChannel, extensions, filteringOptions)
|
|
||||||
|
|
||||||
go filterItems(outputItemsChannel, filteredOutputItemsChannel)
|
|
||||||
go api.StartReadingEntries(filteredOutputItemsChannel, nil, extensionsMap)
|
|
||||||
|
|
||||||
hostApi(nil)
|
|
||||||
} else if *tapperMode {
|
} else if *tapperMode {
|
||||||
logger.Log.Infof("Starting tapper, websocket address: %s", *apiServerAddress)
|
runInTapperMode()
|
||||||
if *apiServerAddress == "" {
|
|
||||||
panic("API server address must be provided with --api-server-address when using --tap")
|
|
||||||
}
|
|
||||||
|
|
||||||
hostMode := os.Getenv(shared.HostModeEnvVar) == "1"
|
|
||||||
tapOpts := &tap.TapOpts{HostMode: hostMode}
|
|
||||||
tapTargets := getTapTargets()
|
|
||||||
if tapTargets != nil {
|
|
||||||
tapOpts.FilterAuthorities = tapTargets
|
|
||||||
logger.Log.Infof("Filtering for the following authorities: %v", tapOpts.FilterAuthorities)
|
|
||||||
}
|
|
||||||
|
|
||||||
filteredOutputItemsChannel := make(chan *tapApi.OutputChannelItem)
|
|
||||||
|
|
||||||
filteringOptions := getTrafficFilteringOptions()
|
|
||||||
tap.StartPassiveTapper(tapOpts, filteredOutputItemsChannel, extensions, filteringOptions)
|
|
||||||
socketConnection, err := dialSocketWithRetry(*apiServerAddress, socketConnectionRetries, socketConnectionRetryDelay)
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("Error connecting to socket server at %s %v", *apiServerAddress, err))
|
|
||||||
}
|
|
||||||
logger.Log.Infof("Connected successfully to websocket %s", *apiServerAddress)
|
|
||||||
|
|
||||||
go pipeTapChannelToSocket(socketConnection, filteredOutputItemsChannel)
|
|
||||||
} else if *apiServerMode {
|
} else if *apiServerMode {
|
||||||
configureBasenineServer(shared.BasenineHost, shared.BaseninePort)
|
utils.StartServer(app.RunInApiServerMode(*namespace))
|
||||||
startTime = time.Now().UnixNano() / int64(time.Millisecond)
|
|
||||||
api.StartResolving(*namespace)
|
|
||||||
|
|
||||||
outputItemsChannel := make(chan *tapApi.OutputChannelItem)
|
|
||||||
filteredOutputItemsChannel := make(chan *tapApi.OutputChannelItem)
|
|
||||||
|
|
||||||
go filterItems(outputItemsChannel, filteredOutputItemsChannel)
|
|
||||||
go api.StartReadingEntries(filteredOutputItemsChannel, nil, extensionsMap)
|
|
||||||
|
|
||||||
syncEntriesConfig := getSyncEntriesConfig()
|
|
||||||
if syncEntriesConfig != nil {
|
|
||||||
if err := up9.SyncEntries(syncEntriesConfig); err != nil {
|
|
||||||
panic(fmt.Sprintf("Error syncing entries, err: %v", err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hostApi(outputItemsChannel)
|
|
||||||
} else if *harsReaderMode {
|
} else if *harsReaderMode {
|
||||||
outputItemsChannel := make(chan *tapApi.OutputChannelItem, 1000)
|
runInHarReaderMode()
|
||||||
filteredHarChannel := make(chan *tapApi.OutputChannelItem)
|
|
||||||
|
|
||||||
go filterItems(outputItemsChannel, filteredHarChannel)
|
|
||||||
go api.StartReadingEntries(filteredHarChannel, harsDir, extensionsMap)
|
|
||||||
hostApi(nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
signalChan := make(chan os.Signal, 1)
|
signalChan := make(chan os.Signal, 1)
|
||||||
@@ -148,140 +72,59 @@ func main() {
|
|||||||
logger.Log.Info("Exiting")
|
logger.Log.Info("Exiting")
|
||||||
}
|
}
|
||||||
|
|
||||||
func configureBasenineServer(host string, port string) {
|
func runInTapperMode() {
|
||||||
if !wait.New(
|
logger.Log.Infof("Starting tapper, websocket address: %s", *apiServerAddress)
|
||||||
wait.WithProto("tcp"),
|
if *apiServerAddress == "" {
|
||||||
wait.WithWait(200*time.Millisecond),
|
panic("API server address must be provided with --api-server-address when using --tap")
|
||||||
wait.WithBreak(50*time.Millisecond),
|
|
||||||
wait.WithDeadline(5*time.Second),
|
|
||||||
wait.WithDebug(true),
|
|
||||||
).Do([]string{fmt.Sprintf("%s:%s", host, port)}) {
|
|
||||||
logger.Log.Panicf("Basenine is not available!")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Limit the database size to default 200MB
|
hostMode := os.Getenv(shared.HostModeEnvVar) == "1"
|
||||||
err := basenine.Limit(host, port, config.Config.MaxDBSizeBytes)
|
tapOpts := &tap.TapOpts{HostMode: hostMode}
|
||||||
|
tapTargets := getTapTargets()
|
||||||
|
if tapTargets != nil {
|
||||||
|
tapOpts.FilterAuthorities = tapTargets
|
||||||
|
logger.Log.Infof("Filtering for the following authorities: %v", tapOpts.FilterAuthorities)
|
||||||
|
}
|
||||||
|
|
||||||
|
filteredOutputItemsChannel := make(chan *tapApi.OutputChannelItem)
|
||||||
|
|
||||||
|
filteringOptions := getTrafficFilteringOptions()
|
||||||
|
tap.StartPassiveTapper(tapOpts, filteredOutputItemsChannel, app.Extensions, filteringOptions)
|
||||||
|
socketConnection, err := dialSocketWithRetry(*apiServerAddress, socketConnectionRetries, socketConnectionRetryDelay)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log.Panicf("Error while limiting database size: %v", err)
|
panic(fmt.Sprintf("Error connecting to socket server at %s %v", *apiServerAddress, err))
|
||||||
}
|
}
|
||||||
|
logger.Log.Infof("Connected successfully to websocket %s", *apiServerAddress)
|
||||||
|
|
||||||
// Define the macros
|
go pipeTapChannelToSocket(socketConnection, filteredOutputItemsChannel)
|
||||||
for _, extension := range extensions {
|
|
||||||
macros := extension.Dissector.Macros()
|
|
||||||
for macro, expanded := range macros {
|
|
||||||
err = basenine.Macro(host, port, macro, expanded)
|
|
||||||
if err != nil {
|
|
||||||
logger.Log.Panicf("Error while adding a macro: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadExtensions() {
|
func runInStandaloneMode() {
|
||||||
dir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
|
api.StartResolving(*namespace)
|
||||||
extensionsDir := path.Join(dir, "./extensions/")
|
|
||||||
|
|
||||||
files, err := ioutil.ReadDir(extensionsDir)
|
outputItemsChannel := make(chan *tapApi.OutputChannelItem)
|
||||||
if err != nil {
|
filteredOutputItemsChannel := make(chan *tapApi.OutputChannelItem)
|
||||||
logger.Log.Fatal(err)
|
|
||||||
}
|
|
||||||
extensions = make([]*tapApi.Extension, len(files))
|
|
||||||
extensionsMap = make(map[string]*tapApi.Extension)
|
|
||||||
for i, file := range files {
|
|
||||||
filename := file.Name()
|
|
||||||
logger.Log.Infof("Loading extension: %s", filename)
|
|
||||||
extension := &tapApi.Extension{
|
|
||||||
Path: path.Join(extensionsDir, filename),
|
|
||||||
}
|
|
||||||
plug, _ := plugin.Open(extension.Path)
|
|
||||||
extension.Plug = plug
|
|
||||||
symDissector, err := plug.Lookup("Dissector")
|
|
||||||
|
|
||||||
var dissector tapApi.Dissector
|
filteringOptions := getTrafficFilteringOptions()
|
||||||
var ok bool
|
hostMode := os.Getenv(shared.HostModeEnvVar) == "1"
|
||||||
dissector, ok = symDissector.(tapApi.Dissector)
|
tapOpts := &tap.TapOpts{HostMode: hostMode}
|
||||||
if err != nil || !ok {
|
tap.StartPassiveTapper(tapOpts, outputItemsChannel, app.Extensions, filteringOptions)
|
||||||
panic(fmt.Sprintf("Failed to load the extension: %s", extension.Path))
|
|
||||||
}
|
|
||||||
dissector.Register(extension)
|
|
||||||
extension.Dissector = dissector
|
|
||||||
extensions[i] = extension
|
|
||||||
extensionsMap[extension.Protocol.Name] = extension
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Slice(extensions, func(i, j int) bool {
|
go app.FilterItems(outputItemsChannel, filteredOutputItemsChannel)
|
||||||
return extensions[i].Protocol.Priority < extensions[j].Protocol.Priority
|
go api.StartReadingEntries(filteredOutputItemsChannel, nil, app.ExtensionsMap)
|
||||||
})
|
|
||||||
|
|
||||||
for _, extension := range extensions {
|
ginApp := app.HostApi(nil)
|
||||||
logger.Log.Infof("Extension Properties: %+v", extension)
|
utils.StartServer(ginApp)
|
||||||
}
|
|
||||||
|
|
||||||
controllers.InitExtensionsMap(extensionsMap)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func hostApi(socketHarOutputChannel chan<- *tapApi.OutputChannelItem) {
|
func runInHarReaderMode() {
|
||||||
app := gin.Default()
|
outputItemsChannel := make(chan *tapApi.OutputChannelItem, 1000)
|
||||||
|
filteredHarChannel := make(chan *tapApi.OutputChannelItem)
|
||||||
|
|
||||||
app.GET("/echo", func(c *gin.Context) {
|
go app.FilterItems(outputItemsChannel, filteredHarChannel)
|
||||||
c.String(http.StatusOK, "Here is Mizu agent")
|
go api.StartReadingEntries(filteredHarChannel, harsDir, app.ExtensionsMap)
|
||||||
})
|
ginApp := app.HostApi(nil)
|
||||||
|
utils.StartServer(ginApp)
|
||||||
eventHandlers := api.RoutesEventHandlers{
|
|
||||||
SocketOutChannel: socketHarOutputChannel,
|
|
||||||
}
|
|
||||||
|
|
||||||
app.Use(DisableRootStaticCache())
|
|
||||||
|
|
||||||
if err := setUIMode(); err != nil {
|
|
||||||
logger.Log.Errorf("Error setting ui mode, err: %v", err)
|
|
||||||
}
|
|
||||||
app.Use(static.ServeRoot("/", "./site"))
|
|
||||||
|
|
||||||
app.Use(middlewares.CORSMiddleware()) // This has to be called after the static middleware, does not work if its called before
|
|
||||||
|
|
||||||
api.WebSocketRoutes(app, &eventHandlers, startTime)
|
|
||||||
|
|
||||||
if config.Config.StandaloneMode {
|
|
||||||
routes.ConfigRoutes(app)
|
|
||||||
routes.UserRoutes(app)
|
|
||||||
routes.InstallRoutes(app)
|
|
||||||
}
|
|
||||||
|
|
||||||
routes.QueryRoutes(app)
|
|
||||||
routes.EntriesRoutes(app)
|
|
||||||
routes.MetadataRoutes(app)
|
|
||||||
routes.StatusRoutes(app)
|
|
||||||
routes.NotFoundRoute(app)
|
|
||||||
|
|
||||||
utils.StartServer(app)
|
|
||||||
}
|
|
||||||
|
|
||||||
func DisableRootStaticCache() gin.HandlerFunc {
|
|
||||||
return func(c *gin.Context) {
|
|
||||||
if c.Request.RequestURI == "/" {
|
|
||||||
// Disable cache only for the main static route
|
|
||||||
c.Writer.Header().Set("Cache-Control", "no-store")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func setUIMode() error {
|
|
||||||
read, err := ioutil.ReadFile(uiIndexPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
replacedContent := strings.Replace(string(read), "__IS_STANDALONE__", strconv.FormatBool(config.Config.StandaloneMode), 1)
|
|
||||||
|
|
||||||
err = ioutil.WriteFile(uiIndexPath, []byte(replacedContent), 0)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseEnvVar(env string) map[string][]v1.Pod {
|
func parseEnvVar(env string) map[string][]v1.Pod {
|
||||||
@@ -322,16 +165,6 @@ func getTrafficFilteringOptions() *tapApi.TrafficFilteringOptions {
|
|||||||
return &filteringOptions
|
return &filteringOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
func filterItems(inChannel <-chan *tapApi.OutputChannelItem, outChannel chan *tapApi.OutputChannelItem) {
|
|
||||||
for message := range inChannel {
|
|
||||||
if message.ConnectionInfo.IsOutgoing && api.CheckIsServiceIP(message.ConnectionInfo.ServerIP) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
outChannel <- message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func pipeTapChannelToSocket(connection *websocket.Conn, messageDataChannel <-chan *tapApi.OutputChannelItem) {
|
func pipeTapChannelToSocket(connection *websocket.Conn, messageDataChannel <-chan *tapApi.OutputChannelItem) {
|
||||||
if connection == nil {
|
if connection == nil {
|
||||||
panic("Websocket connection is nil")
|
panic("Websocket connection is nil")
|
||||||
@@ -367,21 +200,6 @@ func pipeTapChannelToSocket(connection *websocket.Conn, messageDataChannel <-cha
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSyncEntriesConfig() *shared.SyncEntriesConfig {
|
|
||||||
syncEntriesConfigJson := os.Getenv(shared.SyncEntriesConfigEnvVar)
|
|
||||||
if syncEntriesConfigJson == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var syncEntriesConfig = &shared.SyncEntriesConfig{}
|
|
||||||
err := json.Unmarshal([]byte(syncEntriesConfigJson), syncEntriesConfig)
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("env var %s's value of %s is invalid! json must match the shared.SyncEntriesConfig struct, err: %v", shared.SyncEntriesConfigEnvVar, syncEntriesConfigJson, err))
|
|
||||||
}
|
|
||||||
|
|
||||||
return syncEntriesConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
func determineLogLevel() (logLevel logging.Level) {
|
func determineLogLevel() (logLevel logging.Level) {
|
||||||
logLevel, err := logging.LogLevel(os.Getenv(shared.LogLevelEnvVar))
|
logLevel, err := logging.LogLevel(os.Getenv(shared.LogLevelEnvVar))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -5,23 +5,28 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"mizuserver/pkg/holder"
|
|
||||||
"mizuserver/pkg/providers"
|
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/martian/har"
|
"github.com/up9inc/mizu/agent/pkg/elastic"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/har"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/holder"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/providers"
|
||||||
|
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/servicemap"
|
||||||
|
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/models"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/oas"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/resolver"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/utils"
|
||||||
|
|
||||||
"github.com/up9inc/mizu/shared"
|
"github.com/up9inc/mizu/shared"
|
||||||
"github.com/up9inc/mizu/shared/logger"
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
tapApi "github.com/up9inc/mizu/tap/api"
|
tapApi "github.com/up9inc/mizu/tap/api"
|
||||||
|
|
||||||
"mizuserver/pkg/models"
|
|
||||||
"mizuserver/pkg/resolver"
|
|
||||||
"mizuserver/pkg/utils"
|
|
||||||
|
|
||||||
basenine "github.com/up9inc/basenine/client/go"
|
basenine "github.com/up9inc/basenine/client/go"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -38,10 +43,8 @@ func StartResolving(namespace string) {
|
|||||||
res.Start(ctx)
|
res.Start(ctx)
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
select {
|
err := <-errOut
|
||||||
case err := <-errOut:
|
logger.Log.Infof("name resolving error %s", err)
|
||||||
logger.Log.Infof("name resolving error %s", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -63,7 +66,7 @@ func startReadingFiles(workingDir string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for true {
|
for {
|
||||||
dir, _ := os.Open(workingDir)
|
dir, _ := os.Open(workingDir)
|
||||||
dirFiles, _ := dir.Readdir(-1)
|
dirFiles, _ := dir.Readdir(-1)
|
||||||
|
|
||||||
@@ -114,15 +117,15 @@ func startReadingChannel(outputItems <-chan *tapApi.OutputChannelItem, extension
|
|||||||
}
|
}
|
||||||
|
|
||||||
for item := range outputItems {
|
for item := range outputItems {
|
||||||
providers.EntryAdded()
|
|
||||||
|
|
||||||
extension := extensionsMap[item.Protocol.Name]
|
extension := extensionsMap[item.Protocol.Name]
|
||||||
resolvedSource, resolvedDestionation := resolveIP(item.ConnectionInfo)
|
resolvedSource, resolvedDestionation := resolveIP(item.ConnectionInfo)
|
||||||
mizuEntry := extension.Dissector.Analyze(item, resolvedSource, resolvedDestionation)
|
mizuEntry := extension.Dissector.Analyze(item, resolvedSource, resolvedDestionation)
|
||||||
if extension.Protocol.Name == "http" {
|
if extension.Protocol.Name == "http" {
|
||||||
if !disableOASValidation {
|
if !disableOASValidation {
|
||||||
var httpPair tapApi.HTTPRequestResponsePair
|
var httpPair tapApi.HTTPRequestResponsePair
|
||||||
json.Unmarshal([]byte(mizuEntry.HTTPPair), &httpPair)
|
if err := json.Unmarshal([]byte(mizuEntry.HTTPPair), &httpPair); err != nil {
|
||||||
|
logger.Log.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
contract := handleOAS(ctx, doc, router, httpPair.Request.Payload.RawRequest, httpPair.Response.Payload.RawResponse, contractContent)
|
contract := handleOAS(ctx, doc, router, httpPair.Request.Payload.RawRequest, httpPair.Response.Payload.RawResponse, contractContent)
|
||||||
mizuEntry.ContractStatus = contract.Status
|
mizuEntry.ContractStatus = contract.Status
|
||||||
@@ -131,18 +134,27 @@ func startReadingChannel(outputItems <-chan *tapApi.OutputChannelItem, extension
|
|||||||
mizuEntry.ContractContent = contract.Content
|
mizuEntry.ContractContent = contract.Content
|
||||||
}
|
}
|
||||||
|
|
||||||
harEntry, err := utils.NewEntry(mizuEntry.Request, mizuEntry.Response, mizuEntry.StartTime, mizuEntry.ElapsedTime)
|
harEntry, err := har.NewEntry(mizuEntry.Request, mizuEntry.Response, mizuEntry.StartTime, mizuEntry.ElapsedTime)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
rules, _, _ := models.RunValidationRulesState(*harEntry, mizuEntry.Destination.Name)
|
rules, _, _ := models.RunValidationRulesState(*harEntry, mizuEntry.Destination.Name)
|
||||||
mizuEntry.Rules = rules
|
mizuEntry.Rules = rules
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entryWSource := oas.EntryWithSource{Entry: *harEntry, Source: mizuEntry.Source.Name, Id: mizuEntry.Id}
|
||||||
|
oas.GetOasGeneratorInstance().PushEntry(&entryWSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := json.Marshal(mizuEntry)
|
data, err := json.Marshal(mizuEntry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
providers.EntryAdded(len(data))
|
||||||
|
|
||||||
connection.SendText(string(data))
|
connection.SendText(string(data))
|
||||||
|
|
||||||
|
servicemap.GetInstance().NewTCPEntry(mizuEntry.Source, mizuEntry.Destination, &item.Protocol)
|
||||||
|
elastic.GetInstance().PushEntry(mizuEntry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,19 +2,17 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"mizuserver/pkg/middlewares"
|
|
||||||
"mizuserver/pkg/models"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/models"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
basenine "github.com/up9inc/basenine/client/go"
|
basenine "github.com/up9inc/basenine/client/go"
|
||||||
"github.com/up9inc/mizu/shared"
|
"github.com/up9inc/mizu/shared"
|
||||||
"github.com/up9inc/mizu/shared/debounce"
|
|
||||||
"github.com/up9inc/mizu/shared/logger"
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
tapApi "github.com/up9inc/mizu/tap/api"
|
tapApi "github.com/up9inc/mizu/tap/api"
|
||||||
)
|
)
|
||||||
@@ -32,27 +30,39 @@ type SocketConnection struct {
|
|||||||
isTapper bool
|
isTapper bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var websocketUpgrader = websocket.Upgrader{
|
var (
|
||||||
ReadBufferSize: 1024,
|
websocketUpgrader = websocket.Upgrader{
|
||||||
WriteBufferSize: 1024,
|
ReadBufferSize: 1024,
|
||||||
}
|
WriteBufferSize: 1024,
|
||||||
|
}
|
||||||
|
|
||||||
var websocketIdsLock = sync.Mutex{}
|
websocketIdsLock = sync.Mutex{}
|
||||||
var connectedWebsockets map[int]*SocketConnection
|
connectedWebsockets map[int]*SocketConnection
|
||||||
var connectedWebsocketIdCounter = 0
|
connectedWebsocketIdCounter = 0
|
||||||
|
SocketGetBrowserHandler gin.HandlerFunc
|
||||||
|
SocketGetTapperHandler gin.HandlerFunc
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
websocketUpgrader.CheckOrigin = func(r *http.Request) bool { return true } // like cors for web socket
|
websocketUpgrader.CheckOrigin = func(r *http.Request) bool { return true } // like cors for web socket
|
||||||
connectedWebsockets = make(map[int]*SocketConnection, 0)
|
connectedWebsockets = make(map[int]*SocketConnection)
|
||||||
}
|
}
|
||||||
|
|
||||||
func WebSocketRoutes(app *gin.Engine, eventHandlers EventHandlers, startTime int64) {
|
func WebSocketRoutes(app *gin.Engine, eventHandlers EventHandlers, startTime int64) {
|
||||||
app.GET("/ws", func(c *gin.Context) {
|
SocketGetBrowserHandler = func(c *gin.Context) {
|
||||||
websocketHandler(c.Writer, c.Request, eventHandlers, false, startTime)
|
websocketHandler(c.Writer, c.Request, eventHandlers, false, startTime)
|
||||||
}, middlewares.RequiresAuth())
|
}
|
||||||
|
|
||||||
|
SocketGetTapperHandler = func(c *gin.Context) {
|
||||||
|
websocketHandler(c.Writer, c.Request, eventHandlers, true, startTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
app.GET("/ws", func(c *gin.Context) {
|
||||||
|
SocketGetBrowserHandler(c)
|
||||||
|
})
|
||||||
|
|
||||||
app.GET("/wsTapper", func(c *gin.Context) { // TODO: add m2m authentication to this route
|
app.GET("/wsTapper", func(c *gin.Context) { // TODO: add m2m authentication to this route
|
||||||
websocketHandler(c.Writer, c.Request, eventHandlers, true, startTime)
|
SocketGetTapperHandler(c)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +105,10 @@ func websocketHandler(w http.ResponseWriter, r *http.Request, eventHandlers Even
|
|||||||
eventHandlers.WebSocketConnect(socketId, isTapper)
|
eventHandlers.WebSocketConnect(socketId, isTapper)
|
||||||
|
|
||||||
startTimeBytes, _ := models.CreateWebsocketStartTimeMessage(startTime)
|
startTimeBytes, _ := models.CreateWebsocketStartTimeMessage(startTime)
|
||||||
SendToSocket(socketId, startTimeBytes)
|
|
||||||
|
if err = SendToSocket(socketId, startTimeBytes); err != nil {
|
||||||
|
logger.Log.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
_, msg, err := ws.ReadMessage()
|
_, msg, err := ws.ReadMessage()
|
||||||
@@ -118,7 +131,9 @@ func websocketHandler(w http.ResponseWriter, r *http.Request, eventHandlers Even
|
|||||||
AutoClose: 5000,
|
AutoClose: 5000,
|
||||||
Text: fmt.Sprintf("Syntax error: %s", err.Error()),
|
Text: fmt.Sprintf("Syntax error: %s", err.Error()),
|
||||||
})
|
})
|
||||||
SendToSocket(socketId, toastBytes)
|
if err := SendToSocket(socketId, toastBytes); err != nil {
|
||||||
|
logger.Log.Error(err)
|
||||||
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +153,9 @@ func websocketHandler(w http.ResponseWriter, r *http.Request, eventHandlers Even
|
|||||||
base := tapApi.Summarize(entry)
|
base := tapApi.Summarize(entry)
|
||||||
|
|
||||||
baseEntryBytes, _ := models.CreateBaseEntryWebSocketMessage(base)
|
baseEntryBytes, _ := models.CreateBaseEntryWebSocketMessage(base)
|
||||||
SendToSocket(socketId, baseEntryBytes)
|
if err := SendToSocket(socketId, baseEntryBytes); err != nil {
|
||||||
|
logger.Log.Error(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +174,9 @@ func websocketHandler(w http.ResponseWriter, r *http.Request, eventHandlers Even
|
|||||||
}
|
}
|
||||||
|
|
||||||
metadataBytes, _ := models.CreateWebsocketQueryMetadataMessage(metadata)
|
metadataBytes, _ := models.CreateWebsocketQueryMetadataMessage(metadata)
|
||||||
SendToSocket(socketId, metadataBytes)
|
if err := SendToSocket(socketId, metadataBytes); err != nil {
|
||||||
|
logger.Log.Error(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,14 +203,10 @@ func socketCleanup(socketId int, socketConnection *SocketConnection) {
|
|||||||
socketConnection.eventHandlers.WebSocketDisconnect(socketId, socketConnection.isTapper)
|
socketConnection.eventHandlers.WebSocketDisconnect(socketId, socketConnection.isTapper)
|
||||||
}
|
}
|
||||||
|
|
||||||
var db = debounce.NewDebouncer(time.Second*5, func() {
|
|
||||||
logger.Log.Error("Successfully sent to socket")
|
|
||||||
})
|
|
||||||
|
|
||||||
func SendToSocket(socketId int, message []byte) error {
|
func SendToSocket(socketId int, message []byte) error {
|
||||||
socketObj := connectedWebsockets[socketId]
|
socketObj := connectedWebsockets[socketId]
|
||||||
if socketObj == nil {
|
if socketObj == nil {
|
||||||
return errors.New("Socket is disconnected")
|
return fmt.Errorf("Socket %v is disconnected", socketId)
|
||||||
}
|
}
|
||||||
|
|
||||||
var sent = false
|
var sent = false
|
||||||
@@ -205,7 +220,10 @@ func SendToSocket(socketId int, message []byte) error {
|
|||||||
socketObj.lock.Lock() // gorilla socket panics from concurrent writes to a single socket
|
socketObj.lock.Lock() // gorilla socket panics from concurrent writes to a single socket
|
||||||
err := socketObj.connection.WriteMessage(1, message)
|
err := socketObj.connection.WriteMessage(1, message)
|
||||||
socketObj.lock.Unlock()
|
socketObj.lock.Unlock()
|
||||||
|
|
||||||
sent = true
|
sent = true
|
||||||
return err
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to write message to socket %v, err: %w", socketId, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,13 @@ package api
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"mizuserver/pkg/models"
|
|
||||||
"mizuserver/pkg/providers"
|
|
||||||
"mizuserver/pkg/providers/tappersCount"
|
|
||||||
"mizuserver/pkg/up9"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/models"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/providers"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/providers/tappers"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/up9"
|
||||||
|
|
||||||
tapApi "github.com/up9inc/mizu/tap/api"
|
tapApi "github.com/up9inc/mizu/tap/api"
|
||||||
|
|
||||||
"github.com/up9inc/mizu/shared"
|
"github.com/up9inc/mizu/shared"
|
||||||
@@ -30,7 +31,7 @@ func init() {
|
|||||||
func (h *RoutesEventHandlers) WebSocketConnect(socketId int, isTapper bool) {
|
func (h *RoutesEventHandlers) WebSocketConnect(socketId int, isTapper bool) {
|
||||||
if isTapper {
|
if isTapper {
|
||||||
logger.Log.Infof("Websocket event - Tapper connected, socket ID: %d", socketId)
|
logger.Log.Infof("Websocket event - Tapper connected, socket ID: %d", socketId)
|
||||||
tappersCount.Add()
|
tappers.Connected()
|
||||||
} else {
|
} else {
|
||||||
logger.Log.Infof("Websocket event - Browser socket connected, socket ID: %d", socketId)
|
logger.Log.Infof("Websocket event - Browser socket connected, socket ID: %d", socketId)
|
||||||
socketListLock.Lock()
|
socketListLock.Lock()
|
||||||
@@ -42,7 +43,7 @@ func (h *RoutesEventHandlers) WebSocketConnect(socketId int, isTapper bool) {
|
|||||||
func (h *RoutesEventHandlers) WebSocketDisconnect(socketId int, isTapper bool) {
|
func (h *RoutesEventHandlers) WebSocketDisconnect(socketId int, isTapper bool) {
|
||||||
if isTapper {
|
if isTapper {
|
||||||
logger.Log.Infof("Websocket event - Tapper disconnected, socket ID: %d", socketId)
|
logger.Log.Infof("Websocket event - Tapper disconnected, socket ID: %d", socketId)
|
||||||
tappersCount.Remove()
|
tappers.Disconnected()
|
||||||
} else {
|
} else {
|
||||||
logger.Log.Infof("Websocket event - Browser socket disconnected, socket ID: %d", socketId)
|
logger.Log.Infof("Websocket event - Browser socket disconnected, socket ID: %d", socketId)
|
||||||
socketListLock.Lock()
|
socketListLock.Lock()
|
||||||
@@ -54,9 +55,8 @@ func (h *RoutesEventHandlers) WebSocketDisconnect(socketId int, isTapper bool) {
|
|||||||
func BroadcastToBrowserClients(message []byte) {
|
func BroadcastToBrowserClients(message []byte) {
|
||||||
for _, socketId := range browserClientSocketUUIDs {
|
for _, socketId := range browserClientSocketUUIDs {
|
||||||
go func(socketId int) {
|
go func(socketId int) {
|
||||||
err := SendToSocket(socketId, message)
|
if err := SendToSocket(socketId, message); err != nil {
|
||||||
if err != nil {
|
logger.Log.Error(err)
|
||||||
logger.Log.Errorf("error sending message to socket ID %d: %v", socketId, err)
|
|
||||||
}
|
}
|
||||||
}(socketId)
|
}(socketId)
|
||||||
}
|
}
|
||||||
|
|||||||
62
agent/pkg/app/extensions.go
Normal file
62
agent/pkg/app/extensions.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/controllers"
|
||||||
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
|
tapApi "github.com/up9inc/mizu/tap/api"
|
||||||
|
|
||||||
|
amqpExt "github.com/up9inc/mizu/tap/extensions/amqp"
|
||||||
|
httpExt "github.com/up9inc/mizu/tap/extensions/http"
|
||||||
|
kafkaExt "github.com/up9inc/mizu/tap/extensions/kafka"
|
||||||
|
redisExt "github.com/up9inc/mizu/tap/extensions/redis"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Extensions []*tapApi.Extension // global
|
||||||
|
ExtensionsMap map[string]*tapApi.Extension // global
|
||||||
|
)
|
||||||
|
|
||||||
|
func LoadExtensions() {
|
||||||
|
Extensions = make([]*tapApi.Extension, 4)
|
||||||
|
ExtensionsMap = make(map[string]*tapApi.Extension)
|
||||||
|
|
||||||
|
extensionAmqp := &tapApi.Extension{}
|
||||||
|
dissectorAmqp := amqpExt.NewDissector()
|
||||||
|
dissectorAmqp.Register(extensionAmqp)
|
||||||
|
extensionAmqp.Dissector = dissectorAmqp
|
||||||
|
Extensions[0] = extensionAmqp
|
||||||
|
ExtensionsMap[extensionAmqp.Protocol.Name] = extensionAmqp
|
||||||
|
|
||||||
|
extensionHttp := &tapApi.Extension{}
|
||||||
|
dissectorHttp := httpExt.NewDissector()
|
||||||
|
dissectorHttp.Register(extensionHttp)
|
||||||
|
extensionHttp.Dissector = dissectorHttp
|
||||||
|
Extensions[1] = extensionHttp
|
||||||
|
ExtensionsMap[extensionHttp.Protocol.Name] = extensionHttp
|
||||||
|
|
||||||
|
extensionKafka := &tapApi.Extension{}
|
||||||
|
dissectorKafka := kafkaExt.NewDissector()
|
||||||
|
dissectorKafka.Register(extensionKafka)
|
||||||
|
extensionKafka.Dissector = dissectorKafka
|
||||||
|
Extensions[2] = extensionKafka
|
||||||
|
ExtensionsMap[extensionKafka.Protocol.Name] = extensionKafka
|
||||||
|
|
||||||
|
extensionRedis := &tapApi.Extension{}
|
||||||
|
dissectorRedis := redisExt.NewDissector()
|
||||||
|
dissectorRedis.Register(extensionRedis)
|
||||||
|
extensionRedis.Dissector = dissectorRedis
|
||||||
|
Extensions[3] = extensionRedis
|
||||||
|
ExtensionsMap[extensionRedis.Protocol.Name] = extensionRedis
|
||||||
|
|
||||||
|
sort.Slice(Extensions, func(i, j int) bool {
|
||||||
|
return Extensions[i].Protocol.Priority < Extensions[j].Protocol.Priority
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, extension := range Extensions {
|
||||||
|
logger.Log.Infof("Extension Properties: %+v", extension)
|
||||||
|
}
|
||||||
|
|
||||||
|
controllers.InitExtensionsMap(ExtensionsMap)
|
||||||
|
}
|
||||||
210
agent/pkg/app/server.go
Normal file
210
agent/pkg/app/server.go
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/antelman107/net-wait-go/wait"
|
||||||
|
"github.com/gin-contrib/static"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/op/go-logging"
|
||||||
|
basenine "github.com/up9inc/basenine/client/go"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/api"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/config"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/elastic"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/middlewares"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/oas"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/routes"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/servicemap"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/up9"
|
||||||
|
"github.com/up9inc/mizu/shared"
|
||||||
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
|
tapApi "github.com/up9inc/mizu/tap/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ConfigRoutes *gin.RouterGroup
|
||||||
|
UserRoutes *gin.RouterGroup
|
||||||
|
InstallRoutes *gin.RouterGroup
|
||||||
|
OASRoutes *gin.RouterGroup
|
||||||
|
ServiceMapRoutes *gin.RouterGroup
|
||||||
|
QueryRoutes *gin.RouterGroup
|
||||||
|
EntriesRoutes *gin.RouterGroup
|
||||||
|
MetadataRoutes *gin.RouterGroup
|
||||||
|
StatusRoutes *gin.RouterGroup
|
||||||
|
|
||||||
|
startTime int64
|
||||||
|
)
|
||||||
|
|
||||||
|
func HostApi(socketHarOutputChannel chan<- *tapApi.OutputChannelItem) *gin.Engine {
|
||||||
|
app := gin.Default()
|
||||||
|
|
||||||
|
app.GET("/echo", func(c *gin.Context) {
|
||||||
|
c.String(http.StatusOK, "Here is Mizu agent")
|
||||||
|
})
|
||||||
|
|
||||||
|
eventHandlers := api.RoutesEventHandlers{
|
||||||
|
SocketOutChannel: socketHarOutputChannel,
|
||||||
|
}
|
||||||
|
|
||||||
|
app.Use(disableRootStaticCache())
|
||||||
|
|
||||||
|
var staticFolder string
|
||||||
|
if config.Config.StandaloneMode {
|
||||||
|
staticFolder = "./site-standalone"
|
||||||
|
} else {
|
||||||
|
staticFolder = "./site"
|
||||||
|
}
|
||||||
|
|
||||||
|
indexStaticFile := staticFolder + "/index.html"
|
||||||
|
if err := setUIFlags(indexStaticFile); err != nil {
|
||||||
|
logger.Log.Errorf("Error setting ui flags, err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
app.Use(static.ServeRoot("/", staticFolder))
|
||||||
|
app.NoRoute(func(c *gin.Context) {
|
||||||
|
c.File(indexStaticFile)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.Use(middlewares.CORSMiddleware()) // This has to be called after the static middleware, does not work if its called before
|
||||||
|
|
||||||
|
api.WebSocketRoutes(app, &eventHandlers, startTime)
|
||||||
|
|
||||||
|
if config.Config.StandaloneMode {
|
||||||
|
ConfigRoutes = routes.ConfigRoutes(app)
|
||||||
|
UserRoutes = routes.UserRoutes(app)
|
||||||
|
InstallRoutes = routes.InstallRoutes(app)
|
||||||
|
}
|
||||||
|
if config.Config.OAS {
|
||||||
|
OASRoutes = routes.OASRoutes(app)
|
||||||
|
}
|
||||||
|
if config.Config.ServiceMap {
|
||||||
|
ServiceMapRoutes = routes.ServiceMapRoutes(app)
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryRoutes = routes.QueryRoutes(app)
|
||||||
|
EntriesRoutes = routes.EntriesRoutes(app)
|
||||||
|
MetadataRoutes = routes.MetadataRoutes(app)
|
||||||
|
StatusRoutes = routes.StatusRoutes(app)
|
||||||
|
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunInApiServerMode(namespace string) *gin.Engine {
|
||||||
|
configureBasenineServer(shared.BasenineHost, shared.BaseninePort)
|
||||||
|
startTime = time.Now().UnixNano() / int64(time.Millisecond)
|
||||||
|
api.StartResolving(namespace)
|
||||||
|
|
||||||
|
outputItemsChannel := make(chan *tapApi.OutputChannelItem)
|
||||||
|
filteredOutputItemsChannel := make(chan *tapApi.OutputChannelItem)
|
||||||
|
enableExpFeatureIfNeeded()
|
||||||
|
go FilterItems(outputItemsChannel, filteredOutputItemsChannel)
|
||||||
|
go api.StartReadingEntries(filteredOutputItemsChannel, nil, ExtensionsMap)
|
||||||
|
|
||||||
|
syncEntriesConfig := getSyncEntriesConfig()
|
||||||
|
if syncEntriesConfig != nil {
|
||||||
|
if err := up9.SyncEntries(syncEntriesConfig); err != nil {
|
||||||
|
logger.Log.Error("Error syncing entries, err: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return HostApi(outputItemsChannel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func configureBasenineServer(host string, port string) {
|
||||||
|
if !wait.New(
|
||||||
|
wait.WithProto("tcp"),
|
||||||
|
wait.WithWait(200*time.Millisecond),
|
||||||
|
wait.WithBreak(50*time.Millisecond),
|
||||||
|
wait.WithDeadline(5*time.Second),
|
||||||
|
wait.WithDebug(config.Config.LogLevel == logging.DEBUG),
|
||||||
|
).Do([]string{fmt.Sprintf("%s:%s", host, port)}) {
|
||||||
|
logger.Log.Panicf("Basenine is not available!")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit the database size to default 200MB
|
||||||
|
err := basenine.Limit(host, port, config.Config.MaxDBSizeBytes)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Panicf("Error while limiting database size: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define the macros
|
||||||
|
for _, extension := range Extensions {
|
||||||
|
macros := extension.Dissector.Macros()
|
||||||
|
for macro, expanded := range macros {
|
||||||
|
err = basenine.Macro(host, port, macro, expanded)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Panicf("Error while adding a macro: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSyncEntriesConfig() *shared.SyncEntriesConfig {
|
||||||
|
syncEntriesConfigJson := os.Getenv(shared.SyncEntriesConfigEnvVar)
|
||||||
|
if syncEntriesConfigJson == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var syncEntriesConfig = &shared.SyncEntriesConfig{}
|
||||||
|
err := json.Unmarshal([]byte(syncEntriesConfigJson), syncEntriesConfig)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("env var %s's value of %s is invalid! json must match the shared.SyncEntriesConfig struct, err: %v", shared.SyncEntriesConfigEnvVar, syncEntriesConfigJson, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return syncEntriesConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func FilterItems(inChannel <-chan *tapApi.OutputChannelItem, outChannel chan *tapApi.OutputChannelItem) {
|
||||||
|
for message := range inChannel {
|
||||||
|
if message.ConnectionInfo.IsOutgoing && api.CheckIsServiceIP(message.ConnectionInfo.ServerIP) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
outChannel <- message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func enableExpFeatureIfNeeded() {
|
||||||
|
if config.Config.OAS {
|
||||||
|
oas.GetOasGeneratorInstance().Start()
|
||||||
|
}
|
||||||
|
if config.Config.ServiceMap {
|
||||||
|
servicemap.GetInstance().SetConfig(config.Config)
|
||||||
|
}
|
||||||
|
elastic.GetInstance().Configure(config.Config.Elastic)
|
||||||
|
}
|
||||||
|
|
||||||
|
func disableRootStaticCache() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
if c.Request.RequestURI == "/" {
|
||||||
|
// Disable cache only for the main static route
|
||||||
|
c.Writer.Header().Set("Cache-Control", "no-store")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setUIFlags(uiIndexPath string) error {
|
||||||
|
read, err := ioutil.ReadFile(uiIndexPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
replacedContent := strings.Replace(string(read), "__IS_OAS_ENABLED__", strconv.FormatBool(config.Config.OAS), 1)
|
||||||
|
replacedContent = strings.Replace(replacedContent, "__IS_SERVICE_MAP_ENABLED__", strconv.FormatBool(config.Config.ServiceMap), 1)
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(uiIndexPath, []byte(replacedContent), 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -2,21 +2,22 @@ package controllers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"regexp"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/config"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/models"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/providers"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/providers/tapConfig"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/providers/tappedPods"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/providers/tappers"
|
||||||
"github.com/up9inc/mizu/shared"
|
"github.com/up9inc/mizu/shared"
|
||||||
"github.com/up9inc/mizu/shared/kubernetes"
|
"github.com/up9inc/mizu/shared/kubernetes"
|
||||||
"github.com/up9inc/mizu/shared/logger"
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
tapApi "github.com/up9inc/mizu/tap/api"
|
tapApi "github.com/up9inc/mizu/tap/api"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"mizuserver/pkg/config"
|
|
||||||
"mizuserver/pkg/models"
|
|
||||||
"mizuserver/pkg/providers"
|
|
||||||
"mizuserver/pkg/providers/tapConfig"
|
|
||||||
"mizuserver/pkg/providers/tappedPods"
|
|
||||||
"mizuserver/pkg/providers/tappersStatus"
|
|
||||||
"net/http"
|
|
||||||
"regexp"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var cancelTapperSyncer context.CancelFunc
|
var cancelTapperSyncer context.CancelFunc
|
||||||
@@ -33,7 +34,7 @@ func PostTapConfig(c *gin.Context) {
|
|||||||
cancelTapperSyncer()
|
cancelTapperSyncer()
|
||||||
|
|
||||||
tappedPods.Set([]*shared.PodInfo{})
|
tappedPods.Set([]*shared.PodInfo{})
|
||||||
tappersStatus.Reset()
|
tappers.ResetStatus()
|
||||||
|
|
||||||
broadcastTappedPodsStatus()
|
broadcastTappedPodsStatus()
|
||||||
}
|
}
|
||||||
@@ -141,7 +142,7 @@ func startMizuTapperSyncer(ctx context.Context, provider *kubernetes.Provider, t
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tappersStatus.Set(&tapperStatus)
|
tappers.SetStatus(&tapperStatus)
|
||||||
broadcastTappedPodsStatus()
|
broadcastTappedPodsStatus()
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
logger.Log.Debug("mizuTapperSyncer event listener loop exiting due to context done")
|
logger.Log.Debug("mizuTapperSyncer event listener loop exiting due to context done")
|
||||||
|
|||||||
@@ -2,13 +2,14 @@ package controllers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"mizuserver/pkg/models"
|
|
||||||
"mizuserver/pkg/utils"
|
|
||||||
"mizuserver/pkg/validation"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/har"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/models"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/validation"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
basenine "github.com/up9inc/basenine/client/go"
|
basenine "github.com/up9inc/basenine/client/go"
|
||||||
@@ -26,7 +27,7 @@ func InitExtensionsMap(ref map[string]*tapApi.Extension) {
|
|||||||
func Error(c *gin.Context, err error) bool {
|
func Error(c *gin.Context, err error) bool {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log.Errorf("Error getting entry: %v", err)
|
logger.Log.Errorf("Error getting entry: %v", err)
|
||||||
c.Error(err)
|
_ = c.Error(err)
|
||||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
|
||||||
"error": true,
|
"error": true,
|
||||||
"type": "error",
|
"type": "error",
|
||||||
@@ -127,11 +128,13 @@ func GetEntry(c *gin.Context) {
|
|||||||
var rules []map[string]interface{}
|
var rules []map[string]interface{}
|
||||||
var isRulesEnabled bool
|
var isRulesEnabled bool
|
||||||
if entry.Protocol.Name == "http" {
|
if entry.Protocol.Name == "http" {
|
||||||
harEntry, _ := utils.NewEntry(entry.Request, entry.Response, entry.StartTime, entry.ElapsedTime)
|
harEntry, _ := har.NewEntry(entry.Request, entry.Response, entry.StartTime, entry.ElapsedTime)
|
||||||
_, rulesMatched, _isRulesEnabled := models.RunValidationRulesState(*harEntry, entry.Destination.Name)
|
_, rulesMatched, _isRulesEnabled := models.RunValidationRulesState(*harEntry, entry.Destination.Name)
|
||||||
isRulesEnabled = _isRulesEnabled
|
isRulesEnabled = _isRulesEnabled
|
||||||
inrec, _ := json.Marshal(rulesMatched)
|
inrec, _ := json.Marshal(rulesMatched)
|
||||||
json.Unmarshal(inrec, &rules)
|
if err := json.Unmarshal(inrec, &rules); err != nil {
|
||||||
|
logger.Log.Error(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, tapApi.EntryWrapper{
|
c.JSON(http.StatusOK, tapApi.EntryWrapper{
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"mizuserver/pkg/providers"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/providers"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/up9inc/mizu/shared/logger"
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
)
|
)
|
||||||
@@ -16,3 +17,8 @@ func IsSetupNecessary(c *gin.Context) {
|
|||||||
c.JSON(http.StatusOK, IsInstallNeeded)
|
c.JSON(http.StatusOK, IsInstallNeeded)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetupAdminUser(c *gin.Context) {
|
||||||
|
token, err, formErrorMessages := providers.CreateAdminUser(c.PostForm("password"), c.Request.Context())
|
||||||
|
handleRegistration(token, err, formErrorMessages, c)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/up9inc/mizu/shared"
|
|
||||||
"mizuserver/pkg/version"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/version"
|
||||||
|
"github.com/up9inc/mizu/shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetVersion(c *gin.Context) {
|
func GetVersion(c *gin.Context) {
|
||||||
resp := shared.VersionResponse{SemVer: version.SemVer}
|
resp := shared.VersionResponse{Ver: version.Ver}
|
||||||
c.JSON(http.StatusOK, resp)
|
c.JSON(http.StatusOK, resp)
|
||||||
}
|
}
|
||||||
|
|||||||
63
agent/pkg/controllers/oas_controller.go
Normal file
63
agent/pkg/controllers/oas_controller.go
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/chanced/openapi"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/oas"
|
||||||
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetOASServers(c *gin.Context) {
|
||||||
|
m := make([]string, 0)
|
||||||
|
oas.GetOasGeneratorInstance().ServiceSpecs.Range(func(key, value interface{}) bool {
|
||||||
|
m = append(m, key.(string))
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetOASSpec(c *gin.Context) {
|
||||||
|
res, ok := oas.GetOasGeneratorInstance().ServiceSpecs.Load(c.Param("id"))
|
||||||
|
if !ok {
|
||||||
|
c.JSON(http.StatusNotFound, gin.H{
|
||||||
|
"error": true,
|
||||||
|
"type": "error",
|
||||||
|
"autoClose": "5000",
|
||||||
|
"msg": "Service not found among specs",
|
||||||
|
})
|
||||||
|
return // exit
|
||||||
|
}
|
||||||
|
|
||||||
|
gen := res.(*oas.SpecGen)
|
||||||
|
spec, err := gen.GetSpec()
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{
|
||||||
|
"error": true,
|
||||||
|
"type": "error",
|
||||||
|
"autoClose": "5000",
|
||||||
|
"msg": err,
|
||||||
|
})
|
||||||
|
return // exit
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, spec)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetOASAllSpecs(c *gin.Context) {
|
||||||
|
res := map[string]*openapi.OpenAPI{}
|
||||||
|
oas.GetOasGeneratorInstance().ServiceSpecs.Range(func(key, value interface{}) bool {
|
||||||
|
svc := key.(string)
|
||||||
|
gen := value.(*oas.SpecGen)
|
||||||
|
spec, err := gen.GetSpec()
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Warningf("Failed to obtain spec for service %s: %s", svc, err)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
res[svc] = spec
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
c.JSON(http.StatusOK, res)
|
||||||
|
}
|
||||||
42
agent/pkg/controllers/oas_controller_test.go
Normal file
42
agent/pkg/controllers/oas_controller_test.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/oas"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetOASServers(t *testing.T) {
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
c, _ := gin.CreateTestContext(recorder)
|
||||||
|
oas.GetOasGeneratorInstance().Start()
|
||||||
|
oas.GetOasGeneratorInstance().ServiceSpecs.Store("some", oas.NewGen("some"))
|
||||||
|
|
||||||
|
GetOASServers(c)
|
||||||
|
t.Logf("Written body: %s", recorder.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetOASAllSpecs(t *testing.T) {
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
c, _ := gin.CreateTestContext(recorder)
|
||||||
|
oas.GetOasGeneratorInstance().Start()
|
||||||
|
oas.GetOasGeneratorInstance().ServiceSpecs.Store("some", oas.NewGen("some"))
|
||||||
|
|
||||||
|
GetOASAllSpecs(c)
|
||||||
|
t.Logf("Written body: %s", recorder.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetOASSpec(t *testing.T) {
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
c, _ := gin.CreateTestContext(recorder)
|
||||||
|
oas.GetOasGeneratorInstance().Start()
|
||||||
|
oas.GetOasGeneratorInstance().ServiceSpecs.Store("some", oas.NewGen("some"))
|
||||||
|
|
||||||
|
c.Params = []gin.Param{{Key: "id", Value: "some"}}
|
||||||
|
|
||||||
|
GetOASSpec(c)
|
||||||
|
t.Logf("Written body: %s", recorder.Body.String())
|
||||||
|
}
|
||||||
37
agent/pkg/controllers/service_map_controller.go
Normal file
37
agent/pkg/controllers/service_map_controller.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/servicemap"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServiceMapController struct {
|
||||||
|
service servicemap.ServiceMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServiceMapController() *ServiceMapController {
|
||||||
|
return &ServiceMapController{
|
||||||
|
service: servicemap.GetInstance(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ServiceMapController) Status(c *gin.Context) {
|
||||||
|
c.JSON(http.StatusOK, s.service.GetStatus())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ServiceMapController) Get(c *gin.Context) {
|
||||||
|
response := &servicemap.ServiceMapResponse{
|
||||||
|
Status: s.service.GetStatus(),
|
||||||
|
Nodes: s.service.GetNodes(),
|
||||||
|
Edges: s.service.GetEdges(),
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ServiceMapController) Reset(c *gin.Context) {
|
||||||
|
s.service.Reset()
|
||||||
|
s.Status(c)
|
||||||
|
}
|
||||||
147
agent/pkg/controllers/service_map_controller_test.go
Normal file
147
agent/pkg/controllers/service_map_controller_test.go
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/servicemap"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
"github.com/up9inc/mizu/shared"
|
||||||
|
tapApi "github.com/up9inc/mizu/tap/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
a = "aService"
|
||||||
|
b = "bService"
|
||||||
|
Ip = "127.0.0.1"
|
||||||
|
Port = "80"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
TCPEntryA = &tapApi.TCP{
|
||||||
|
Name: a,
|
||||||
|
Port: Port,
|
||||||
|
IP: fmt.Sprintf("%s.%s", Ip, a),
|
||||||
|
}
|
||||||
|
TCPEntryB = &tapApi.TCP{
|
||||||
|
Name: b,
|
||||||
|
Port: Port,
|
||||||
|
IP: fmt.Sprintf("%s.%s", Ip, b),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var ProtocolHttp = &tapApi.Protocol{
|
||||||
|
Name: "http",
|
||||||
|
LongName: "Hypertext Transfer Protocol -- HTTP/1.1",
|
||||||
|
Abbreviation: "HTTP",
|
||||||
|
Macro: "http",
|
||||||
|
Version: "1.1",
|
||||||
|
BackgroundColor: "#205cf5",
|
||||||
|
ForegroundColor: "#ffffff",
|
||||||
|
FontSize: 12,
|
||||||
|
ReferenceLink: "https://datatracker.ietf.org/doc/html/rfc2616",
|
||||||
|
Ports: []string{"80", "443", "8080"},
|
||||||
|
Priority: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServiceMapControllerSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
|
||||||
|
c *ServiceMapController
|
||||||
|
w *httptest.ResponseRecorder
|
||||||
|
g *gin.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ServiceMapControllerSuite) SetupTest() {
|
||||||
|
s.c = NewServiceMapController()
|
||||||
|
s.c.service.SetConfig(&shared.MizuAgentConfig{
|
||||||
|
ServiceMap: true,
|
||||||
|
})
|
||||||
|
s.c.service.NewTCPEntry(TCPEntryA, TCPEntryB, ProtocolHttp)
|
||||||
|
|
||||||
|
s.w = httptest.NewRecorder()
|
||||||
|
s.g, _ = gin.CreateTestContext(s.w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ServiceMapControllerSuite) TestGetStatus() {
|
||||||
|
assert := s.Assert()
|
||||||
|
|
||||||
|
s.c.Status(s.g)
|
||||||
|
assert.Equal(http.StatusOK, s.w.Code)
|
||||||
|
|
||||||
|
var status servicemap.ServiceMapStatus
|
||||||
|
err := json.Unmarshal(s.w.Body.Bytes(), &status)
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.Equal("enabled", status.Status)
|
||||||
|
assert.Equal(1, status.EntriesProcessedCount)
|
||||||
|
assert.Equal(2, status.NodeCount)
|
||||||
|
assert.Equal(1, status.EdgeCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ServiceMapControllerSuite) TestGet() {
|
||||||
|
assert := s.Assert()
|
||||||
|
|
||||||
|
s.c.Get(s.g)
|
||||||
|
assert.Equal(http.StatusOK, s.w.Code)
|
||||||
|
|
||||||
|
var response servicemap.ServiceMapResponse
|
||||||
|
err := json.Unmarshal(s.w.Body.Bytes(), &response)
|
||||||
|
assert.NoError(err)
|
||||||
|
|
||||||
|
// response status
|
||||||
|
assert.Equal("enabled", response.Status.Status)
|
||||||
|
assert.Equal(1, response.Status.EntriesProcessedCount)
|
||||||
|
assert.Equal(2, response.Status.NodeCount)
|
||||||
|
assert.Equal(1, response.Status.EdgeCount)
|
||||||
|
|
||||||
|
// response nodes
|
||||||
|
aNode := servicemap.ServiceMapNode{
|
||||||
|
Id: 1,
|
||||||
|
Name: TCPEntryA.IP,
|
||||||
|
Entry: TCPEntryA,
|
||||||
|
Count: 1,
|
||||||
|
}
|
||||||
|
bNode := servicemap.ServiceMapNode{
|
||||||
|
Id: 2,
|
||||||
|
Name: TCPEntryB.IP,
|
||||||
|
Entry: TCPEntryB,
|
||||||
|
Count: 1,
|
||||||
|
}
|
||||||
|
assert.Contains(response.Nodes, aNode)
|
||||||
|
assert.Contains(response.Nodes, bNode)
|
||||||
|
assert.Len(response.Nodes, 2)
|
||||||
|
|
||||||
|
// response edges
|
||||||
|
assert.Equal([]servicemap.ServiceMapEdge{
|
||||||
|
{
|
||||||
|
Source: aNode,
|
||||||
|
Destination: bNode,
|
||||||
|
Protocol: ProtocolHttp,
|
||||||
|
Count: 1,
|
||||||
|
},
|
||||||
|
}, response.Edges)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ServiceMapControllerSuite) TestGetReset() {
|
||||||
|
assert := s.Assert()
|
||||||
|
|
||||||
|
s.c.Reset(s.g)
|
||||||
|
assert.Equal(http.StatusOK, s.w.Code)
|
||||||
|
|
||||||
|
var status servicemap.ServiceMapStatus
|
||||||
|
err := json.Unmarshal(s.w.Body.Bytes(), &status)
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.Equal("enabled", status.Status)
|
||||||
|
assert.Equal(0, status.EntriesProcessedCount)
|
||||||
|
assert.Equal(0, status.NodeCount)
|
||||||
|
assert.Equal(0, status.EdgeCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServiceMapControllerSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(ServiceMapControllerSuite))
|
||||||
|
}
|
||||||
@@ -2,30 +2,30 @@ package controllers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/api"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/holder"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/providers"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/providers/tappedPods"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/providers/tappers"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/up9"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/validation"
|
||||||
"github.com/up9inc/mizu/shared"
|
"github.com/up9inc/mizu/shared"
|
||||||
"github.com/up9inc/mizu/shared/logger"
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
"mizuserver/pkg/api"
|
|
||||||
"mizuserver/pkg/holder"
|
|
||||||
"mizuserver/pkg/providers"
|
|
||||||
"mizuserver/pkg/providers/tappedPods"
|
|
||||||
"mizuserver/pkg/providers/tappersCount"
|
|
||||||
"mizuserver/pkg/providers/tappersStatus"
|
|
||||||
"mizuserver/pkg/up9"
|
|
||||||
"mizuserver/pkg/validation"
|
|
||||||
"net/http"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func HealthCheck(c *gin.Context) {
|
func HealthCheck(c *gin.Context) {
|
||||||
tappers := make([]*shared.TapperStatus, 0)
|
tappersStatus := make([]*shared.TapperStatus, 0)
|
||||||
for _, value := range tappersStatus.Get() {
|
for _, value := range tappers.GetStatus() {
|
||||||
tappers = append(tappers, value)
|
tappersStatus = append(tappersStatus, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
response := shared.HealthResponse{
|
response := shared.HealthResponse{
|
||||||
TappedPods: tappedPods.Get(),
|
TappedPods: tappedPods.Get(),
|
||||||
TappersCount: tappersCount.Get(),
|
ConnectedTappersCount: tappers.GetConnectedCount(),
|
||||||
TappersStatus: tappers,
|
TappersStatus: tappersStatus,
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, response)
|
c.JSON(http.StatusOK, response)
|
||||||
}
|
}
|
||||||
@@ -66,12 +66,12 @@ func PostTapperStatus(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Log.Infof("[Status] POST request, tapper status: %v", tapperStatus)
|
logger.Log.Infof("[Status] POST request, tapper status: %v", tapperStatus)
|
||||||
tappersStatus.Set(tapperStatus)
|
tappers.SetStatus(tapperStatus)
|
||||||
broadcastTappedPodsStatus()
|
broadcastTappedPodsStatus()
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetTappersCount(c *gin.Context) {
|
func GetConnectedTappersCount(c *gin.Context) {
|
||||||
c.JSON(http.StatusOK, tappersCount.Get())
|
c.JSON(http.StatusOK, tappers.GetConnectedCount())
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAuthStatus(c *gin.Context) {
|
func GetAuthStatus(c *gin.Context) {
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"mizuserver/pkg/providers"
|
"github.com/up9inc/mizu/agent/pkg/providers"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/up9inc/mizu/shared/logger"
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
|
|
||||||
|
ory "github.com/ory/kratos-client-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Login(c *gin.Context) {
|
func Login(c *gin.Context) {
|
||||||
@@ -25,7 +27,12 @@ func Logout(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Register(c *gin.Context) {
|
func Register(c *gin.Context) {
|
||||||
if token, _, err, formErrorMessages := providers.RegisterUser(c.PostForm("username"), c.PostForm("password"), c.Request.Context()); err != nil {
|
token, _, err, formErrorMessages := providers.RegisterUser(c.PostForm("username"), c.PostForm("password"), c.Request.Context())
|
||||||
|
handleRegistration(token, err, formErrorMessages, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleRegistration(token *string, err error, formErrorMessages map[string][]ory.UiText, c *gin.Context) {
|
||||||
|
if err != nil {
|
||||||
if formErrorMessages != nil {
|
if formErrorMessages != nil {
|
||||||
logger.Log.Infof("user attempted to register but had form errors %v %v", formErrorMessages, err)
|
logger.Log.Infof("user attempted to register but had form errors %v %v", formErrorMessages, err)
|
||||||
c.AbortWithStatusJSON(400, formErrorMessages)
|
c.AbortWithStatusJSON(400, formErrorMessages)
|
||||||
@@ -34,6 +41,6 @@ func Register(c *gin.Context) {
|
|||||||
c.AbortWithStatusJSON(500, gin.H{"error": "internal error occured while registering"})
|
c.AbortWithStatusJSON(500, gin.H{"error": "internal error occured while registering"})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.JSON(200, gin.H{"token": token})
|
c.JSON(201, gin.H{"token": token})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
120
agent/pkg/elastic/esClient.go
Normal file
120
agent/pkg/elastic/esClient.go
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
package elastic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/elastic/go-elasticsearch/v7"
|
||||||
|
"github.com/up9inc/mizu/shared"
|
||||||
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
|
"github.com/up9inc/mizu/tap/api"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type client struct {
|
||||||
|
es *elasticsearch.Client
|
||||||
|
index string
|
||||||
|
insertedCount int
|
||||||
|
}
|
||||||
|
|
||||||
|
var instance *client
|
||||||
|
var once sync.Once
|
||||||
|
|
||||||
|
func GetInstance() *client {
|
||||||
|
once.Do(func() {
|
||||||
|
instance = newClient()
|
||||||
|
})
|
||||||
|
return instance
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *client) Configure(config shared.ElasticConfig) {
|
||||||
|
if config.Url == "" || config.User == "" || config.Password == "" {
|
||||||
|
logger.Log.Infof("No elastic configuration was supplied, elastic exporter disabled")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
transport := http.DefaultTransport
|
||||||
|
tlsClientConfig := &tls.Config{InsecureSkipVerify: true}
|
||||||
|
transport.(*http.Transport).TLSClientConfig = tlsClientConfig
|
||||||
|
cfg := elasticsearch.Config{
|
||||||
|
Addresses: []string{config.Url},
|
||||||
|
Username: config.User,
|
||||||
|
Password: config.Password,
|
||||||
|
Transport: transport,
|
||||||
|
}
|
||||||
|
|
||||||
|
es, err := elasticsearch.NewClient(cfg)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Fatalf("Failed to initialize elastic client %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Have the client instance return a response
|
||||||
|
res, err := es.Info()
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Fatalf("Elastic client.Info() ERROR: %v", err)
|
||||||
|
} else {
|
||||||
|
client.es = es
|
||||||
|
client.index = "mizu_traffic_http_" + time.Now().Format("2006_01_02_15_04")
|
||||||
|
client.insertedCount = 0
|
||||||
|
logger.Log.Infof("Elastic client configured, index: %s, cluster info: %v", client.index, res)
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func newClient() *client {
|
||||||
|
return &client{
|
||||||
|
es: nil,
|
||||||
|
index: "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type httpEntry struct {
|
||||||
|
Source *api.TCP `json:"src"`
|
||||||
|
Destination *api.TCP `json:"dst"`
|
||||||
|
Outgoing bool `json:"outgoing"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
Request map[string]interface{} `json:"request"`
|
||||||
|
Response map[string]interface{} `json:"response"`
|
||||||
|
Summary string `json:"summary"`
|
||||||
|
Method string `json:"method"`
|
||||||
|
Status int `json:"status"`
|
||||||
|
ElapsedTime int64 `json:"elapsedTime"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *client) PushEntry(entry *api.Entry) {
|
||||||
|
if client.es == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if entry.Protocol.Name != "http" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
entryToPush := httpEntry{
|
||||||
|
Source: entry.Source,
|
||||||
|
Destination: entry.Destination,
|
||||||
|
Outgoing: entry.Outgoing,
|
||||||
|
CreatedAt: entry.StartTime,
|
||||||
|
Request: entry.Request,
|
||||||
|
Response: entry.Response,
|
||||||
|
Summary: entry.Summary,
|
||||||
|
Method: entry.Method,
|
||||||
|
Status: entry.Status,
|
||||||
|
ElapsedTime: entry.ElapsedTime,
|
||||||
|
Path: entry.Path,
|
||||||
|
}
|
||||||
|
|
||||||
|
entryJson, err := json.Marshal(entryToPush)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Errorf("json.Marshal ERROR: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
buffer.WriteString(string(entryJson))
|
||||||
|
res, _ := client.es.Index(client.index, &buffer)
|
||||||
|
if res.StatusCode == 201 {
|
||||||
|
client.insertedCount += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
375
agent/pkg/har/types.go
Normal file
375
agent/pkg/har/types.go
Normal file
@@ -0,0 +1,375 @@
|
|||||||
|
package har
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
|
"time"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
HTTP Archive (HAR) format
|
||||||
|
https://w3c.github.io/web-performance/specs/HAR/Overview.html
|
||||||
|
*/
|
||||||
|
|
||||||
|
// HAR is a container type for deserialization
|
||||||
|
type HAR struct {
|
||||||
|
Log Log `json:"log"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log represents the root of the exported data. This object MUST be present and its name MUST be "log".
|
||||||
|
type Log struct {
|
||||||
|
// The object contains the following name/value pairs:
|
||||||
|
|
||||||
|
// Required. Version number of the format.
|
||||||
|
Version string `json:"version"`
|
||||||
|
// Required. An object of type creator that contains the name and version
|
||||||
|
// information of the log creator application.
|
||||||
|
Creator Creator `json:"creator"`
|
||||||
|
// Optional. An object of type browser that contains the name and version
|
||||||
|
// information of the user agent.
|
||||||
|
Browser Browser `json:"browser"`
|
||||||
|
// Optional. An array of objects of type page, each representing one exported
|
||||||
|
// (tracked) page. Leave out this field if the application does not support
|
||||||
|
// grouping by pages.
|
||||||
|
Pages []Page `json:"pages,omitempty"`
|
||||||
|
// Required. An array of objects of type entry, each representing one
|
||||||
|
// exported (tracked) HTTP request.
|
||||||
|
Entries []Entry `json:"entries"`
|
||||||
|
// Optional. A comment provided by the user or the application. Sorting
|
||||||
|
// entries by startedDateTime (starting from the oldest) is preferred way how
|
||||||
|
// to export data since it can make importing faster. However the reader
|
||||||
|
// application should always make sure the array is sorted (if required for
|
||||||
|
// the import).
|
||||||
|
Comment string `json:"comment"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creator contains information about the log creator application
|
||||||
|
type Creator struct {
|
||||||
|
// Required. The name of the application that created the log.
|
||||||
|
Name string `json:"name"`
|
||||||
|
// Required. The version number of the application that created the log.
|
||||||
|
Version string `json:"version"`
|
||||||
|
// Optional. A comment provided by the user or the application.
|
||||||
|
Comment string `json:"comment,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Browser that created the log
|
||||||
|
type Browser struct {
|
||||||
|
// Required. The name of the browser that created the log.
|
||||||
|
Name string `json:"name"`
|
||||||
|
// Required. The version number of the browser that created the log.
|
||||||
|
Version string `json:"version"`
|
||||||
|
// Optional. A comment provided by the user or the browser.
|
||||||
|
Comment string `json:"comment"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Page object for every exported web page and one <entry> object for every HTTP request.
|
||||||
|
// In case when an HTTP trace tool isn't able to group requests by a page,
|
||||||
|
// the <pages> object is empty and individual requests doesn't have a parent page.
|
||||||
|
type Page struct {
|
||||||
|
/* There is one <page> object for every exported web page and one <entry>
|
||||||
|
object for every HTTP request. In case when an HTTP trace tool isn't able to
|
||||||
|
group requests by a page, the <pages> object is empty and individual
|
||||||
|
requests doesn't have a parent page.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Date and time stamp for the beginning of the page load
|
||||||
|
// (ISO 8601 YYYY-MM-DDThh:mm:ss.sTZD, e.g. 2009-07-24T19:20:30.45+01:00).
|
||||||
|
StartedDateTime string `json:"startedDateTime"`
|
||||||
|
// Unique identifier of a page within the . Entries use it to refer the parent page.
|
||||||
|
ID string `json:"id"`
|
||||||
|
// Page title.
|
||||||
|
Title string `json:"title"`
|
||||||
|
// Detailed timing info about page load.
|
||||||
|
PageTiming PageTiming `json:"pageTiming"`
|
||||||
|
// (new in 1.2) A comment provided by the user or the application.
|
||||||
|
Comment string `json:"comment,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PageTiming describes timings for various events (states) fired during the page load.
|
||||||
|
// All times are specified in milliseconds. If a time info is not available appropriate field is set to -1.
|
||||||
|
type PageTiming struct {
|
||||||
|
// Content of the page loaded. Number of milliseconds since page load started
|
||||||
|
// (page.startedDateTime). Use -1 if the timing does not apply to the current
|
||||||
|
// request.
|
||||||
|
// Depeding on the browser, onContentLoad property represents DOMContentLoad
|
||||||
|
// event or document.readyState == interactive.
|
||||||
|
OnContentLoad int `json:"onContentLoad"`
|
||||||
|
// Page is loaded (onLoad event fired). Number of milliseconds since page
|
||||||
|
// load started (page.startedDateTime). Use -1 if the timing does not apply
|
||||||
|
// to the current request.
|
||||||
|
OnLoad int `json:"onLoad"`
|
||||||
|
// (new in 1.2) A comment provided by the user or the application.
|
||||||
|
Comment string `json:"comment"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entry is a unique, optional Reference to the parent page.
|
||||||
|
// Leave out this field if the application does not support grouping by pages.
|
||||||
|
type Entry struct {
|
||||||
|
Pageref string `json:"pageref,omitempty"`
|
||||||
|
// Date and time stamp of the request start
|
||||||
|
// (ISO 8601 YYYY-MM-DDThh:mm:ss.sTZD).
|
||||||
|
StartedDateTime string `json:"startedDateTime"`
|
||||||
|
// Total elapsed time of the request in milliseconds. This is the sum of all
|
||||||
|
// timings available in the timings object (i.e. not including -1 values) .
|
||||||
|
Time int `json:"time"`
|
||||||
|
// Detailed info about the request.
|
||||||
|
Request Request `json:"request"`
|
||||||
|
// Detailed info about the response.
|
||||||
|
Response Response `json:"response"`
|
||||||
|
// Info about cache usage.
|
||||||
|
Cache Cache `json:"cache"`
|
||||||
|
// Detailed timing info about request/response round trip.
|
||||||
|
PageTimings PageTimings `json:"pageTimings"`
|
||||||
|
// optional (new in 1.2) IP address of the server that was connected
|
||||||
|
// (result of DNS resolution).
|
||||||
|
ServerIPAddress string `json:"serverIPAddress,omitempty"`
|
||||||
|
// optional (new in 1.2) Unique ID of the parent TCP/IP connection, can be
|
||||||
|
// the client port number. Note that a port number doesn't have to be unique
|
||||||
|
// identifier in cases where the port is shared for more connections. If the
|
||||||
|
// port isn't available for the application, any other unique connection ID
|
||||||
|
// can be used instead (e.g. connection index). Leave out this field if the
|
||||||
|
// application doesn't support this info.
|
||||||
|
Connection string `json:"connection,omitempty"`
|
||||||
|
// (new in 1.2) A comment provided by the user or the application.
|
||||||
|
Comment string `json:"comment,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request contains detailed info about performed request.
|
||||||
|
type Request struct {
|
||||||
|
// Request method (GET, POST, ...).
|
||||||
|
Method string `json:"method"`
|
||||||
|
// Absolute URL of the request (fragments are not included).
|
||||||
|
URL string `json:"url"`
|
||||||
|
// Request HTTP Version.
|
||||||
|
HTTPVersion string `json:"httpVersion"`
|
||||||
|
// List of cookie objects.
|
||||||
|
Cookies []Cookie `json:"cookies"`
|
||||||
|
// List of header objects.
|
||||||
|
Headers []NVP `json:"headers"`
|
||||||
|
// List of query parameter objects.
|
||||||
|
QueryString []NVP `json:"queryString"`
|
||||||
|
// Posted data.
|
||||||
|
PostData PostData `json:"postData"`
|
||||||
|
// Total number of bytes from the start of the HTTP request message until
|
||||||
|
// (and including) the double CRLF before the body. Set to -1 if the info
|
||||||
|
// is not available.
|
||||||
|
HeaderSize int `json:"headerSize"`
|
||||||
|
// Size of the request body (POST data payload) in bytes. Set to -1 if the
|
||||||
|
// info is not available.
|
||||||
|
BodySize int `json:"bodySize"`
|
||||||
|
// (new in 1.2) A comment provided by the user or the application.
|
||||||
|
Comment string `json:"comment"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response contains detailed info about the response.
|
||||||
|
type Response struct {
|
||||||
|
// Response status.
|
||||||
|
Status int `json:"status"`
|
||||||
|
// Response status description.
|
||||||
|
StatusText string `json:"statusText"`
|
||||||
|
// Response HTTP Version.
|
||||||
|
HTTPVersion string `json:"httpVersion"`
|
||||||
|
// List of cookie objects.
|
||||||
|
Cookies []Cookie `json:"cookies"`
|
||||||
|
// List of header objects.
|
||||||
|
Headers []NVP `json:"headers"`
|
||||||
|
// Details about the response body.
|
||||||
|
Content Content `json:"content"`
|
||||||
|
// Redirection target URL from the Location response header.
|
||||||
|
RedirectURL string `json:"redirectURL"`
|
||||||
|
// Total number of bytes from the start of the HTTP response message until
|
||||||
|
// (and including) the double CRLF before the body. Set to -1 if the info is
|
||||||
|
// not available.
|
||||||
|
// The size of received response-headers is computed only from headers that
|
||||||
|
// are really received from the server. Additional headers appended by the
|
||||||
|
// browser are not included in this number, but they appear in the list of
|
||||||
|
// header objects.
|
||||||
|
HeadersSize int `json:"headersSize"`
|
||||||
|
// Size of the received response body in bytes. Set to zero in case of
|
||||||
|
// responses coming from the cache (304). Set to -1 if the info is not
|
||||||
|
// available.
|
||||||
|
BodySize int `json:"bodySize"`
|
||||||
|
// optional (new in 1.2) A comment provided by the user or the application.
|
||||||
|
Comment string `json:"comment,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cookie contains list of all cookies (used in <request> and <response> objects).
|
||||||
|
type Cookie struct {
|
||||||
|
// The name of the cookie.
|
||||||
|
Name string `json:"name"`
|
||||||
|
// The cookie value.
|
||||||
|
Value string `json:"value"`
|
||||||
|
// optional The path pertaining to the cookie.
|
||||||
|
Path string `json:"path,omitempty"`
|
||||||
|
// optional The host of the cookie.
|
||||||
|
Domain string `json:"domain,omitempty"`
|
||||||
|
// optional Cookie expiration time.
|
||||||
|
// (ISO 8601 YYYY-MM-DDThh:mm:ss.sTZD, e.g. 2009-07-24T19:20:30.123+02:00).
|
||||||
|
Expires string `json:"expires,omitempty"`
|
||||||
|
// optional Set to true if the cookie is HTTP only, false otherwise.
|
||||||
|
HTTPOnly bool `json:"httpOnly,omitempty"`
|
||||||
|
// optional (new in 1.2) True if the cookie was transmitted over ssl, false
|
||||||
|
// otherwise.
|
||||||
|
Secure bool `json:"secure,omitempty"`
|
||||||
|
// optional (new in 1.2) A comment provided by the user or the application.
|
||||||
|
Comment string `json:"comment,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NVP is simply a name/value pair with a comment
|
||||||
|
type NVP struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
Comment string `json:"comment,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostData describes posted data, if any (embedded in <request> object).
|
||||||
|
type PostData struct {
|
||||||
|
// Mime type of posted data.
|
||||||
|
MimeType string `json:"mimeType"`
|
||||||
|
// List of posted parameters (in case of URL encoded parameters).
|
||||||
|
Params []PostParam `json:"params"`
|
||||||
|
// Plain text posted data
|
||||||
|
Text string `json:"text"`
|
||||||
|
// optional (new in 1.2) A comment provided by the user or the
|
||||||
|
// application.
|
||||||
|
Comment string `json:"comment,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d PostData) B64Decoded() (bool, []byte, string) {
|
||||||
|
// there is a weird gap in HAR spec 1.2, that does not define encoding for binary POST bodies
|
||||||
|
// we have own convention of putting `base64` into comment field to handle it similar to response `Content`
|
||||||
|
return b64Decoded(d.Comment, d.Text)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostParam is a list of posted parameters, if any (embedded in <postData> object).
|
||||||
|
type PostParam struct {
|
||||||
|
// name of a posted parameter.
|
||||||
|
Name string `json:"name"`
|
||||||
|
// optional value of a posted parameter or content of a posted file.
|
||||||
|
Value string `json:"value,omitempty"`
|
||||||
|
// optional name of a posted file.
|
||||||
|
FileName string `json:"fileName,omitempty"`
|
||||||
|
// optional content type of a posted file.
|
||||||
|
ContentType string `json:"contentType,omitempty"`
|
||||||
|
// optional (new in 1.2) A comment provided by the user or the application.
|
||||||
|
Comment string `json:"comment,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Content describes details about response content (embedded in <response> object).
|
||||||
|
type Content struct {
|
||||||
|
// Length of the returned content in bytes. Should be equal to
|
||||||
|
// response.bodySize if there is no compression and bigger when the content
|
||||||
|
// has been compressed.
|
||||||
|
Size int `json:"size"`
|
||||||
|
// optional Number of bytes saved. Leave out this field if the information
|
||||||
|
// is not available.
|
||||||
|
Compression int `json:"compression,omitempty"`
|
||||||
|
// MIME type of the response text (value of the Content-Type response
|
||||||
|
// header). The charset attribute of the MIME type is included (if
|
||||||
|
// available).
|
||||||
|
MimeType string `json:"mimeType"`
|
||||||
|
// optional Response body sent from the server or loaded from the browser
|
||||||
|
// cache. This field is populated with textual content only. The text field
|
||||||
|
// is either HTTP decoded text or a encoded (e.g. "base64") representation of
|
||||||
|
// the response body. Leave out this field if the information is not
|
||||||
|
// available.
|
||||||
|
Text string `json:"text,omitempty"`
|
||||||
|
// optional (new in 1.2) Encoding used for response text field e.g
|
||||||
|
// "base64". Leave out this field if the text field is HTTP decoded
|
||||||
|
// (decompressed & unchunked), than trans-coded from its original character
|
||||||
|
// set into UTF-8.
|
||||||
|
Encoding string `json:"encoding,omitempty"`
|
||||||
|
// optional (new in 1.2) A comment provided by the user or the application.
|
||||||
|
Comment string `json:"comment,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Content) B64Decoded() (bool, []byte, string) {
|
||||||
|
return b64Decoded(c.Encoding, c.Text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func b64Decoded(enc string, text string) (isBinary bool, asBytes []byte, asString string) {
|
||||||
|
if enc == "base64" {
|
||||||
|
decoded, err := base64.StdEncoding.DecodeString(text)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Warningf("Failed to decode content as base64: %s", text)
|
||||||
|
return false, []byte(text), text
|
||||||
|
}
|
||||||
|
valid := utf8.Valid(decoded)
|
||||||
|
return !valid, decoded, string(decoded)
|
||||||
|
} else {
|
||||||
|
return false, nil, text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache contains info about a request coming from browser cache.
|
||||||
|
type Cache struct {
|
||||||
|
// optional State of a cache entry before the request. Leave out this field
|
||||||
|
// if the information is not available.
|
||||||
|
BeforeRequest CacheObject `json:"beforeRequest,omitempty"`
|
||||||
|
// optional State of a cache entry after the request. Leave out this field if
|
||||||
|
// the information is not available.
|
||||||
|
AfterRequest CacheObject `json:"afterRequest,omitempty"`
|
||||||
|
// optional (new in 1.2) A comment provided by the user or the application.
|
||||||
|
Comment string `json:"comment,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CacheObject is used by both beforeRequest and afterRequest
|
||||||
|
type CacheObject struct {
|
||||||
|
// optional - Expiration time of the cache entry.
|
||||||
|
Expires string `json:"expires,omitempty"`
|
||||||
|
// The last time the cache entry was opened.
|
||||||
|
LastAccess string `json:"lastAccess"`
|
||||||
|
// Etag
|
||||||
|
ETag string `json:"eTag"`
|
||||||
|
// The number of times the cache entry has been opened.
|
||||||
|
HitCount int `json:"hitCount"`
|
||||||
|
// optional (new in 1.2) A comment provided by the user or the application.
|
||||||
|
Comment string `json:"comment,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PageTimings describes various phases within request-response round trip.
|
||||||
|
// All times are specified in milliseconds.
|
||||||
|
type PageTimings struct {
|
||||||
|
Blocked int `json:"blocked,omitempty"`
|
||||||
|
// optional - Time spent in a queue waiting for a network connection. Use -1
|
||||||
|
// if the timing does not apply to the current request.
|
||||||
|
DNS int `json:"dns,omitempty"`
|
||||||
|
// optional - DNS resolution time. The time required to resolve a host name.
|
||||||
|
// Use -1 if the timing does not apply to the current request.
|
||||||
|
Connect int `json:"connect,omitempty"`
|
||||||
|
// optional - Time required to create TCP connection. Use -1 if the timing
|
||||||
|
// does not apply to the current request.
|
||||||
|
Send int `json:"send"`
|
||||||
|
// Time required to send HTTP request to the server.
|
||||||
|
Wait int `json:"wait"`
|
||||||
|
// Waiting for a response from the server.
|
||||||
|
Receive int `json:"receive"`
|
||||||
|
// Time required to read entire response from the server (or cache).
|
||||||
|
Ssl int `json:"ssl,omitempty"`
|
||||||
|
// optional (new in 1.2) - Time required for SSL/TLS negotiation. If this
|
||||||
|
// field is defined then the time is also included in the connect field (to
|
||||||
|
// ensure backward compatibility with HAR 1.1). Use -1 if the timing does not
|
||||||
|
// apply to the current request.
|
||||||
|
Comment string `json:"comment,omitempty"`
|
||||||
|
// optional (new in 1.2) - A comment provided by the user or the application.
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestResult contains results for an individual HTTP request
|
||||||
|
type TestResult struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
Status int `json:"status"` // 200, 500, etc.
|
||||||
|
StartTime time.Time `json:"startTime"`
|
||||||
|
EndTime time.Time `json:"endTime"`
|
||||||
|
Latency int `json:"latency"` // milliseconds
|
||||||
|
Method string `json:"method"`
|
||||||
|
HarFile string `json:"harfile"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// aliases for martian lib compatibility
|
||||||
|
|
||||||
|
type Header = NVP
|
||||||
|
type QueryString = NVP
|
||||||
|
type Param = PostParam
|
||||||
|
type Timings = PageTimings
|
||||||
38
agent/pkg/har/types_test.go
Normal file
38
agent/pkg/har/types_test.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package har
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestContentEncoded(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
text string
|
||||||
|
isBinary bool
|
||||||
|
expectedStr string
|
||||||
|
binaryLen int
|
||||||
|
}{
|
||||||
|
{"not-base64", false, "not-base64", 10},
|
||||||
|
{"dGVzdA==", false, "test", 4},
|
||||||
|
{"test", true, "\f@A", 3}, // valid UTF-8 with some non-printable chars
|
||||||
|
{"IsDggPCAgPiAgID8gICAgN/vv/e/v/u/v7/9v7+/vyIKIu+3kO+3ke+3ku+3k++3lO+3le+3lu+3l++3mO+3me+3mu+3m++3nO+3ne+3nu+3n++3oO+3oe+3ou+3o++3pO+3pe+3pu+3p++3qO+3qe+3qu+3q++3rO+3re+3ru+3ryIK", true, "test", 132}, // invalid UTF-8 (thus binary), taken from https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
c := Content{
|
||||||
|
Encoding: "base64",
|
||||||
|
Text: tc.text,
|
||||||
|
}
|
||||||
|
isBinary, asBytes, asString := c.B64Decoded()
|
||||||
|
_ = asBytes
|
||||||
|
|
||||||
|
if tc.isBinary != isBinary {
|
||||||
|
t.Errorf("Binary flag mismatch: %t != %t", tc.isBinary, isBinary)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isBinary && tc.expectedStr != asString {
|
||||||
|
t.Errorf("Decode value mismatch: %s != %s", tc.expectedStr, asString)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.binaryLen != len(asBytes) {
|
||||||
|
t.Errorf("Binary len mismatch: %d != %d", tc.binaryLen, len(asBytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package utils
|
package har
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/martian/har"
|
|
||||||
"github.com/up9inc/mizu/shared/logger"
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -55,14 +54,14 @@ import (
|
|||||||
// return cookies
|
// return cookies
|
||||||
//}
|
//}
|
||||||
|
|
||||||
func BuildHeaders(rawHeaders []interface{}) ([]har.Header, string, string, string, string, string) {
|
func BuildHeaders(rawHeaders []interface{}) ([]Header, string, string, string, string, string) {
|
||||||
var host, scheme, authority, path, status string
|
var host, scheme, authority, path, status string
|
||||||
headers := make([]har.Header, 0, len(rawHeaders))
|
headers := make([]Header, 0, len(rawHeaders))
|
||||||
|
|
||||||
for _, header := range rawHeaders {
|
for _, header := range rawHeaders {
|
||||||
h := header.(map[string]interface{})
|
h := header.(map[string]interface{})
|
||||||
|
|
||||||
headers = append(headers, har.Header{
|
headers = append(headers, Header{
|
||||||
Name: h["name"].(string),
|
Name: h["name"].(string),
|
||||||
Value: h["value"].(string),
|
Value: h["value"].(string),
|
||||||
})
|
})
|
||||||
@@ -87,8 +86,8 @@ func BuildHeaders(rawHeaders []interface{}) ([]har.Header, string, string, strin
|
|||||||
return headers, host, scheme, authority, path, status
|
return headers, host, scheme, authority, path, status
|
||||||
}
|
}
|
||||||
|
|
||||||
func BuildPostParams(rawParams []interface{}) []har.Param {
|
func BuildPostParams(rawParams []interface{}) []Param {
|
||||||
params := make([]har.Param, 0, len(rawParams))
|
params := make([]Param, 0, len(rawParams))
|
||||||
for _, param := range rawParams {
|
for _, param := range rawParams {
|
||||||
p := param.(map[string]interface{})
|
p := param.(map[string]interface{})
|
||||||
name := ""
|
name := ""
|
||||||
@@ -108,10 +107,10 @@ func BuildPostParams(rawParams []interface{}) []har.Param {
|
|||||||
contentType = p["contentType"].(string)
|
contentType = p["contentType"].(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
params = append(params, har.Param{
|
params = append(params, Param{
|
||||||
Name: name,
|
Name: name,
|
||||||
Value: value,
|
Value: value,
|
||||||
Filename: fileName,
|
FileName: fileName,
|
||||||
ContentType: contentType,
|
ContentType: contentType,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -119,25 +118,25 @@ func BuildPostParams(rawParams []interface{}) []har.Param {
|
|||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRequest(request map[string]interface{}) (harRequest *har.Request, err error) {
|
func NewRequest(request map[string]interface{}) (harRequest *Request, err error) {
|
||||||
headers, host, scheme, authority, path, _ := BuildHeaders(request["_headers"].([]interface{}))
|
headers, host, scheme, authority, path, _ := BuildHeaders(request["_headers"].([]interface{}))
|
||||||
cookies := make([]har.Cookie, 0) // BuildCookies(request["_cookies"].([]interface{}))
|
cookies := make([]Cookie, 0) // BuildCookies(request["_cookies"].([]interface{}))
|
||||||
|
|
||||||
postData, _ := request["postData"].(map[string]interface{})
|
postData, _ := request["postData"].(map[string]interface{})
|
||||||
mimeType, _ := postData["mimeType"]
|
mimeType := postData["mimeType"]
|
||||||
if mimeType == nil || len(mimeType.(string)) == 0 {
|
if mimeType == nil || len(mimeType.(string)) == 0 {
|
||||||
mimeType = "text/html"
|
mimeType = "text/html"
|
||||||
}
|
}
|
||||||
text, _ := postData["text"]
|
text := postData["text"]
|
||||||
postDataText := ""
|
postDataText := ""
|
||||||
if text != nil {
|
if text != nil {
|
||||||
postDataText = text.(string)
|
postDataText = text.(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
queryString := make([]har.QueryString, 0)
|
queryString := make([]QueryString, 0)
|
||||||
for _, _qs := range request["_queryString"].([]interface{}) {
|
for _, _qs := range request["_queryString"].([]interface{}) {
|
||||||
qs := _qs.(map[string]interface{})
|
qs := _qs.(map[string]interface{})
|
||||||
queryString = append(queryString, har.QueryString{
|
queryString = append(queryString, QueryString{
|
||||||
Name: qs["name"].(string),
|
Name: qs["name"].(string),
|
||||||
Value: qs["value"].(string),
|
Value: qs["value"].(string),
|
||||||
})
|
})
|
||||||
@@ -148,21 +147,21 @@ func NewRequest(request map[string]interface{}) (harRequest *har.Request, err er
|
|||||||
url = fmt.Sprintf("%s://%s%s", scheme, authority, path)
|
url = fmt.Sprintf("%s://%s%s", scheme, authority, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
harParams := make([]har.Param, 0)
|
harParams := make([]Param, 0)
|
||||||
if postData["params"] != nil {
|
if postData["params"] != nil {
|
||||||
harParams = BuildPostParams(postData["params"].([]interface{}))
|
harParams = BuildPostParams(postData["params"].([]interface{}))
|
||||||
}
|
}
|
||||||
|
|
||||||
harRequest = &har.Request{
|
harRequest = &Request{
|
||||||
Method: request["method"].(string),
|
Method: request["method"].(string),
|
||||||
URL: url,
|
URL: url,
|
||||||
HTTPVersion: request["httpVersion"].(string),
|
HTTPVersion: request["httpVersion"].(string),
|
||||||
HeadersSize: -1,
|
HeaderSize: -1,
|
||||||
BodySize: int64(bytes.NewBufferString(postDataText).Len()),
|
BodySize: bytes.NewBufferString(postDataText).Len(),
|
||||||
QueryString: queryString,
|
QueryString: queryString,
|
||||||
Headers: headers,
|
Headers: headers,
|
||||||
Cookies: cookies,
|
Cookies: cookies,
|
||||||
PostData: &har.PostData{
|
PostData: PostData{
|
||||||
MimeType: mimeType.(string),
|
MimeType: mimeType.(string),
|
||||||
Params: harParams,
|
Params: harParams,
|
||||||
Text: postDataText,
|
Text: postDataText,
|
||||||
@@ -172,27 +171,27 @@ func NewRequest(request map[string]interface{}) (harRequest *har.Request, err er
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewResponse(response map[string]interface{}) (harResponse *har.Response, err error) {
|
func NewResponse(response map[string]interface{}) (harResponse *Response, err error) {
|
||||||
headers, _, _, _, _, _status := BuildHeaders(response["_headers"].([]interface{}))
|
headers, _, _, _, _, _status := BuildHeaders(response["_headers"].([]interface{}))
|
||||||
cookies := make([]har.Cookie, 0) // BuildCookies(response["_cookies"].([]interface{}))
|
cookies := make([]Cookie, 0) // BuildCookies(response["_cookies"].([]interface{}))
|
||||||
|
|
||||||
content, _ := response["content"].(map[string]interface{})
|
content, _ := response["content"].(map[string]interface{})
|
||||||
mimeType, _ := content["mimeType"]
|
mimeType := content["mimeType"]
|
||||||
if mimeType == nil || len(mimeType.(string)) == 0 {
|
if mimeType == nil || len(mimeType.(string)) == 0 {
|
||||||
mimeType = "text/html"
|
mimeType = "text/html"
|
||||||
}
|
}
|
||||||
encoding, _ := content["encoding"]
|
encoding := content["encoding"]
|
||||||
text, _ := content["text"]
|
text := content["text"]
|
||||||
bodyText := ""
|
bodyText := ""
|
||||||
if text != nil {
|
if text != nil {
|
||||||
bodyText = text.(string)
|
bodyText = text.(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
harContent := &har.Content{
|
harContent := &Content{
|
||||||
Encoding: encoding.(string),
|
Encoding: encoding.(string),
|
||||||
MimeType: mimeType.(string),
|
MimeType: mimeType.(string),
|
||||||
Text: []byte(bodyText),
|
Text: bodyText,
|
||||||
Size: int64(len(bodyText)),
|
Size: len(bodyText),
|
||||||
}
|
}
|
||||||
|
|
||||||
status := int(response["status"].(float64))
|
status := int(response["status"].(float64))
|
||||||
@@ -206,20 +205,20 @@ func NewResponse(response map[string]interface{}) (harResponse *har.Response, er
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
harResponse = &har.Response{
|
harResponse = &Response{
|
||||||
HTTPVersion: response["httpVersion"].(string),
|
HTTPVersion: response["httpVersion"].(string),
|
||||||
Status: status,
|
Status: status,
|
||||||
StatusText: response["statusText"].(string),
|
StatusText: response["statusText"].(string),
|
||||||
HeadersSize: -1,
|
HeadersSize: -1,
|
||||||
BodySize: int64(bytes.NewBufferString(bodyText).Len()),
|
BodySize: bytes.NewBufferString(bodyText).Len(),
|
||||||
Headers: headers,
|
Headers: headers,
|
||||||
Cookies: cookies,
|
Cookies: cookies,
|
||||||
Content: harContent,
|
Content: *harContent,
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEntry(request map[string]interface{}, response map[string]interface{}, startTime time.Time, elapsedTime int64) (*har.Entry, error) {
|
func NewEntry(request map[string]interface{}, response map[string]interface{}, startTime time.Time, elapsedTime int64) (*Entry, error) {
|
||||||
harRequest, err := NewRequest(request)
|
harRequest, err := NewRequest(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log.Errorf("Failed converting request to HAR %s (%v,%+v)", err, err, err)
|
logger.Log.Errorf("Failed converting request to HAR %s (%v,%+v)", err, err, err)
|
||||||
@@ -236,16 +235,16 @@ func NewEntry(request map[string]interface{}, response map[string]interface{}, s
|
|||||||
elapsedTime = 1
|
elapsedTime = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
harEntry := har.Entry{
|
harEntry := Entry{
|
||||||
StartedDateTime: startTime,
|
StartedDateTime: startTime.Format(time.RFC3339),
|
||||||
Time: elapsedTime,
|
Time: int(elapsedTime),
|
||||||
Request: harRequest,
|
Request: *harRequest,
|
||||||
Response: harResponse,
|
Response: *harResponse,
|
||||||
Cache: &har.Cache{},
|
Cache: Cache{},
|
||||||
Timings: &har.Timings{
|
PageTimings: PageTimings{
|
||||||
Send: -1,
|
Send: -1,
|
||||||
Wait: -1,
|
Wait: -1,
|
||||||
Receive: elapsedTime,
|
Receive: int(elapsedTime),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package holder
|
package holder
|
||||||
|
|
||||||
import "mizuserver/pkg/resolver"
|
import "github.com/up9inc/mizu/agent/pkg/resolver"
|
||||||
|
|
||||||
var k8sResolver *resolver.Resolver
|
var k8sResolver *resolver.Resolver
|
||||||
|
|
||||||
@@ -11,4 +11,3 @@ func SetResolver(param *resolver.Resolver) {
|
|||||||
func GetResolver() *resolver.Resolver {
|
func GetResolver() *resolver.Resolver {
|
||||||
return k8sResolver
|
return k8sResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
73
agent/pkg/middlewares/auth.go
Normal file
73
agent/pkg/middlewares/auth.go
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
package middlewares
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/config"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/providers"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
ory "github.com/ory/kratos-client-go"
|
||||||
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RequiresAuth() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
// auth is irrelevant for ephermeral mizu
|
||||||
|
if !config.Config.StandaloneMode {
|
||||||
|
c.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyKratosSessionForRequest(c)
|
||||||
|
if !c.IsAborted() {
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func RequiresAdmin() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
// auth is irrelevant for ephermeral mizu
|
||||||
|
if !config.Config.StandaloneMode {
|
||||||
|
c.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
session := verifyKratosSessionForRequest(c)
|
||||||
|
if c.IsAborted() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
traits := session.Identity.Traits.(map[string]interface{})
|
||||||
|
username := traits["username"].(string)
|
||||||
|
|
||||||
|
isAdmin, err := providers.CheckIfUserHasSystemRole(username, providers.AdminRole)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Errorf("error checking user role %v", err)
|
||||||
|
c.AbortWithStatusJSON(403, gin.H{"error": "unknown auth error occured"})
|
||||||
|
} else if !isAdmin {
|
||||||
|
logger.Log.Warningf("user %s attempted to call an admin only endpoint with insufficient privileges", username)
|
||||||
|
c.AbortWithStatusJSON(403, gin.H{"error": "unauthorized"})
|
||||||
|
} else {
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyKratosSessionForRequest(c *gin.Context) *ory.Session {
|
||||||
|
token := c.GetHeader("x-session-token")
|
||||||
|
if token == "" {
|
||||||
|
c.AbortWithStatusJSON(401, gin.H{"error": "token header is empty"})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if session, err := providers.VerifyToken(token, c.Request.Context()); err != nil {
|
||||||
|
logger.Log.Errorf("error verifying token %v", err)
|
||||||
|
c.AbortWithStatusJSON(401, gin.H{"error": "unknown auth error occured"})
|
||||||
|
return nil
|
||||||
|
} else if session == nil {
|
||||||
|
c.AbortWithStatusJSON(401, gin.H{"error": "invalid token"})
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return session
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
package middlewares
|
|
||||||
|
|
||||||
import (
|
|
||||||
"mizuserver/pkg/config"
|
|
||||||
"mizuserver/pkg/providers"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/patrickmn/go-cache"
|
|
||||||
"github.com/up9inc/mizu/shared/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
const cachedValidTokensRetainmentTime = time.Minute * 1
|
|
||||||
|
|
||||||
var cachedValidTokens = cache.New(cachedValidTokensRetainmentTime, cachedValidTokensRetainmentTime)
|
|
||||||
|
|
||||||
func RequiresAuth() gin.HandlerFunc {
|
|
||||||
return func(c *gin.Context) {
|
|
||||||
// auth is irrelevant for ephermeral mizu
|
|
||||||
if !config.Config.StandaloneMode {
|
|
||||||
c.Next()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
token := c.GetHeader("x-session-token")
|
|
||||||
if token == "" {
|
|
||||||
c.AbortWithStatusJSON(401, gin.H{"error": "token header is empty"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, isTokenCached := cachedValidTokens.Get(token); isTokenCached {
|
|
||||||
c.Next()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if isTokenValid, err := providers.VerifyToken(token, c.Request.Context()); err != nil {
|
|
||||||
logger.Log.Errorf("error verifying token %s", err)
|
|
||||||
c.AbortWithStatusJSON(401, gin.H{"error": "unknown auth error occured"})
|
|
||||||
return
|
|
||||||
} else if !isTokenValid {
|
|
||||||
c.AbortWithStatusJSON(401, gin.H{"error": "invalid token"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
cachedValidTokens.Set(token, true, cachedValidTokensRetainmentTime)
|
|
||||||
|
|
||||||
c.Next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,11 +2,11 @@ package models
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"mizuserver/pkg/rules"
|
|
||||||
|
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/har"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/rules"
|
||||||
tapApi "github.com/up9inc/mizu/tap/api"
|
tapApi "github.com/up9inc/mizu/tap/api"
|
||||||
|
|
||||||
"github.com/google/martian/har"
|
|
||||||
basenine "github.com/up9inc/basenine/client/go"
|
basenine "github.com/up9inc/basenine/client/go"
|
||||||
"github.com/up9inc/mizu/shared"
|
"github.com/up9inc/mizu/shared"
|
||||||
"github.com/up9inc/mizu/tap"
|
"github.com/up9inc/mizu/tap"
|
||||||
|
|||||||
119
agent/pkg/oas/counters.go
Normal file
119
agent/pkg/oas/counters.go
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
package oas
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/chanced/openapi"
|
||||||
|
"math"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Counter struct {
|
||||||
|
Entries int `json:"entries"`
|
||||||
|
Failures int `json:"failures"`
|
||||||
|
FirstSeen float64 `json:"firstSeen"`
|
||||||
|
LastSeen float64 `json:"lastSeen"`
|
||||||
|
SumRT float64 `json:"sumRT"`
|
||||||
|
SumDuration float64 `json:"sumDuration"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Counter) addEntry(ts float64, rt float64, succ bool, dur float64) {
|
||||||
|
if dur < 0 {
|
||||||
|
panic("Duration cannot be negative")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Entries += 1
|
||||||
|
c.SumRT += rt
|
||||||
|
c.SumDuration += dur
|
||||||
|
if !succ {
|
||||||
|
c.Failures += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.FirstSeen == 0 {
|
||||||
|
c.FirstSeen = ts
|
||||||
|
} else {
|
||||||
|
c.FirstSeen = math.Min(c.FirstSeen, ts)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.LastSeen = math.Max(c.LastSeen, ts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Counter) addOther(other *Counter) {
|
||||||
|
c.Entries += other.Entries
|
||||||
|
c.SumRT += other.SumRT
|
||||||
|
c.Failures += other.Failures
|
||||||
|
c.SumDuration += other.SumDuration
|
||||||
|
|
||||||
|
if c.FirstSeen == 0 {
|
||||||
|
c.FirstSeen = other.FirstSeen
|
||||||
|
} else {
|
||||||
|
c.FirstSeen = math.Min(c.FirstSeen, other.FirstSeen)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.LastSeen = math.Max(c.LastSeen, other.LastSeen)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CounterMap map[string]*Counter
|
||||||
|
|
||||||
|
func (m *CounterMap) addOther(other *CounterMap) {
|
||||||
|
for src, cmap := range *other {
|
||||||
|
if existing, ok := (*m)[src]; ok {
|
||||||
|
existing.addOther(cmap)
|
||||||
|
} else {
|
||||||
|
copied := *cmap
|
||||||
|
(*m)[src] = &copied
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setCounterMsgIfOk(oldStr string, cnt *Counter) string {
|
||||||
|
tpl := "Mizu observed %d entries (%d failed), at %.3f hits/s, average response time is %.3f seconds"
|
||||||
|
if oldStr == "" || (strings.HasPrefix(oldStr, "Mizu ") && strings.HasSuffix(oldStr, " seconds")) {
|
||||||
|
return fmt.Sprintf(tpl, cnt.Entries, cnt.Failures, cnt.SumDuration/float64(cnt.Entries), cnt.SumRT/float64(cnt.Entries))
|
||||||
|
}
|
||||||
|
return oldStr
|
||||||
|
}
|
||||||
|
|
||||||
|
type CounterMaps struct {
|
||||||
|
counterTotal Counter
|
||||||
|
counterMapTotal CounterMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *CounterMaps) processOp(opObj *openapi.Operation) error {
|
||||||
|
if _, ok := opObj.Extensions.Extension(CountersTotal); ok {
|
||||||
|
counter := new(Counter)
|
||||||
|
err := opObj.Extensions.DecodeExtension(CountersTotal, counter)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.counterTotal.addOther(counter)
|
||||||
|
|
||||||
|
opObj.Description = setCounterMsgIfOk(opObj.Description, counter)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := opObj.Extensions.Extension(CountersPerSource); ok {
|
||||||
|
counterMap := new(CounterMap)
|
||||||
|
err := opObj.Extensions.DecodeExtension(CountersPerSource, counterMap)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.counterMapTotal.addOther(counterMap)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *CounterMaps) processOas(oas *openapi.OpenAPI) error {
|
||||||
|
if oas.Extensions == nil {
|
||||||
|
oas.Extensions = openapi.Extensions{}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := oas.Extensions.SetExtension(CountersTotal, m.counterTotal)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = oas.Extensions.SetExtension(CountersPerSource, m.counterMapTotal)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
209
agent/pkg/oas/feeder_test.go
Normal file
209
agent/pkg/oas/feeder_test.go
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
package oas
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/har"
|
||||||
|
|
||||||
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getFiles(baseDir string) (result []string, err error) {
|
||||||
|
result = make([]string, 0)
|
||||||
|
logger.Log.Infof("Reading files from tree: %s", baseDir)
|
||||||
|
|
||||||
|
inputs := []string{baseDir}
|
||||||
|
|
||||||
|
// https://yourbasic.org/golang/list-files-in-directory/
|
||||||
|
visitor := func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.Mode()&os.ModeSymlink != 0 {
|
||||||
|
path, _ = os.Readlink(path)
|
||||||
|
inputs = append(inputs, path)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ext := strings.ToLower(filepath.Ext(path))
|
||||||
|
if !info.IsDir() && (ext == ".har" || ext == ".ldjson") {
|
||||||
|
result = append(result, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for len(inputs) > 0 {
|
||||||
|
path := inputs[0]
|
||||||
|
inputs = inputs[1:]
|
||||||
|
err = filepath.Walk(path, visitor)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.SliceStable(result, func(i, j int) bool {
|
||||||
|
return fileSize(result[i]) < fileSize(result[j])
|
||||||
|
})
|
||||||
|
|
||||||
|
logger.Log.Infof("Got files: %d", len(result))
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func fileSize(fname string) int64 {
|
||||||
|
fi, err := os.Stat(fname)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fi.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
func feedEntries(fromFiles []string, isSync bool) (count int, err error) {
|
||||||
|
badFiles := make([]string, 0)
|
||||||
|
cnt := 0
|
||||||
|
for _, file := range fromFiles {
|
||||||
|
logger.Log.Info("Processing file: " + file)
|
||||||
|
ext := strings.ToLower(filepath.Ext(file))
|
||||||
|
eCnt := 0
|
||||||
|
switch ext {
|
||||||
|
case ".har":
|
||||||
|
eCnt, err = feedFromHAR(file, isSync)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Warning("Failed processing file: " + err.Error())
|
||||||
|
badFiles = append(badFiles, file)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case ".ldjson":
|
||||||
|
eCnt, err = feedFromLDJSON(file, isSync)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Warning("Failed processing file: " + err.Error())
|
||||||
|
badFiles = append(badFiles, file)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return 0, errors.New("Unsupported file extension: " + ext)
|
||||||
|
}
|
||||||
|
cnt += eCnt
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range badFiles {
|
||||||
|
logger.Log.Infof("Bad file: %s", f)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cnt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func feedFromHAR(file string, isSync bool) (int, error) {
|
||||||
|
fd, err := os.Open(file)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer fd.Close()
|
||||||
|
|
||||||
|
data, err := ioutil.ReadAll(fd)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var harDoc har.HAR
|
||||||
|
err = json.Unmarshal(data, &harDoc)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cnt := 0
|
||||||
|
for _, entry := range harDoc.Log.Entries {
|
||||||
|
cnt += 1
|
||||||
|
feedEntry(&entry, "", isSync, file)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cnt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func feedEntry(entry *har.Entry, source string, isSync bool, file string) {
|
||||||
|
entry.Comment = file
|
||||||
|
if entry.Response.Status == 302 {
|
||||||
|
logger.Log.Debugf("Dropped traffic entry due to permanent redirect status: %s", entry.StartedDateTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(entry.Request.URL, "some") { // for debugging
|
||||||
|
logger.Log.Debugf("Interesting: %s", entry.Request.URL)
|
||||||
|
}
|
||||||
|
|
||||||
|
ews := EntryWithSource{Entry: *entry, Source: source, Id: uint(0)}
|
||||||
|
if isSync {
|
||||||
|
GetOasGeneratorInstance().entriesChan <- ews // blocking variant, right?
|
||||||
|
} else {
|
||||||
|
GetOasGeneratorInstance().PushEntry(&ews)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func feedFromLDJSON(file string, isSync bool) (int, error) {
|
||||||
|
fd, err := os.Open(file)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer fd.Close()
|
||||||
|
|
||||||
|
reader := bufio.NewReader(fd)
|
||||||
|
|
||||||
|
var meta map[string]interface{}
|
||||||
|
buf := strings.Builder{}
|
||||||
|
cnt := 0
|
||||||
|
source := ""
|
||||||
|
for {
|
||||||
|
substr, isPrefix, err := reader.ReadLine()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteString(string(substr))
|
||||||
|
if isPrefix {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
line := buf.String()
|
||||||
|
buf.Reset()
|
||||||
|
|
||||||
|
if meta == nil {
|
||||||
|
err := json.Unmarshal([]byte(line), &meta)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if s, ok := meta["_source"]; ok && s != nil {
|
||||||
|
source = s.(string)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var entry har.Entry
|
||||||
|
err := json.Unmarshal([]byte(line), &entry)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Warningf("Failed decoding entry: %s", line)
|
||||||
|
} else {
|
||||||
|
cnt += 1
|
||||||
|
feedEntry(&entry, source, isSync, file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cnt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilesList(t *testing.T) {
|
||||||
|
res, err := getFiles("./test_artifacts/")
|
||||||
|
t.Log(len(res))
|
||||||
|
t.Log(res)
|
||||||
|
if err != nil || len(res) != 3 {
|
||||||
|
t.Logf("Should return 2 files but returned %d", len(res))
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
185
agent/pkg/oas/gibberish.go
Normal file
185
agent/pkg/oas/gibberish.go
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
package oas
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
patUuid4 = regexp.MustCompile(`(?i)[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}`)
|
||||||
|
patEmail = regexp.MustCompile(`^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$`)
|
||||||
|
patLongNum = regexp.MustCompile(`^\d{3,}$`)
|
||||||
|
patLongNumB = regexp.MustCompile(`[^\d]\d{3,}`)
|
||||||
|
patLongNumA = regexp.MustCompile(`\d{3,}[^\d]`)
|
||||||
|
)
|
||||||
|
|
||||||
|
func IsGibberish(str string) bool {
|
||||||
|
if IsVersionString(str) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if patEmail.MatchString(str) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if patUuid4.MatchString(str) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if patLongNum.MatchString(str) || patLongNumB.MatchString(str) || patLongNumA.MatchString(str) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
//alNum := cleanStr(str, isAlNumRune)
|
||||||
|
//alpha := cleanStr(str, isAlphaRune)
|
||||||
|
// noiseAll := isNoisy(alNum)
|
||||||
|
//triAll := isTrigramBad(strings.ToLower(alpha))
|
||||||
|
// _ = noiseAll
|
||||||
|
|
||||||
|
isNotAlNum := func(r rune) bool { return !isAlNumRune(r) }
|
||||||
|
chunks := strings.FieldsFunc(str, isNotAlNum)
|
||||||
|
noisyLen := 0
|
||||||
|
alnumLen := 0
|
||||||
|
for _, chunk := range chunks {
|
||||||
|
alnumLen += len(chunk)
|
||||||
|
noise := isNoisy(chunk)
|
||||||
|
tri := isTrigramBad(strings.ToLower(chunk))
|
||||||
|
if noise || tri {
|
||||||
|
noisyLen += len(chunk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return float64(noisyLen) > 0
|
||||||
|
|
||||||
|
//if float64(noisyLen) > 0 {
|
||||||
|
// return true
|
||||||
|
//}
|
||||||
|
|
||||||
|
//if len(chunks) > 0 && float64(noisyLen) >= float64(alnumLen)/3.0 {
|
||||||
|
// return true
|
||||||
|
//}
|
||||||
|
|
||||||
|
//if triAll {
|
||||||
|
//return true
|
||||||
|
//}
|
||||||
|
|
||||||
|
// return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func noiseLevel(str string) (score float64) {
|
||||||
|
// opinionated algo of certain char pairs marking the non-human strings
|
||||||
|
prev := *new(rune)
|
||||||
|
cnt := 0.0
|
||||||
|
for _, char := range str {
|
||||||
|
cnt += 1
|
||||||
|
if prev > 0 {
|
||||||
|
switch {
|
||||||
|
// continued class of upper/lower/digit adds no noise
|
||||||
|
case unicode.IsUpper(prev) && unicode.IsUpper(char):
|
||||||
|
case unicode.IsLower(prev) && unicode.IsLower(char):
|
||||||
|
case unicode.IsDigit(prev) && unicode.IsDigit(char):
|
||||||
|
|
||||||
|
// upper =>
|
||||||
|
case unicode.IsUpper(prev) && unicode.IsLower(char):
|
||||||
|
score += 0.10
|
||||||
|
case unicode.IsUpper(prev) && unicode.IsDigit(char):
|
||||||
|
score += 0.5
|
||||||
|
|
||||||
|
// lower =>
|
||||||
|
case unicode.IsLower(prev) && unicode.IsUpper(char):
|
||||||
|
score += 0.75
|
||||||
|
case unicode.IsLower(prev) && unicode.IsDigit(char):
|
||||||
|
score += 0.5
|
||||||
|
|
||||||
|
// digit =>
|
||||||
|
case unicode.IsDigit(prev) && unicode.IsUpper(char):
|
||||||
|
score += 0.75
|
||||||
|
case unicode.IsDigit(prev) && unicode.IsLower(char):
|
||||||
|
score += 1.0
|
||||||
|
|
||||||
|
// the rest is 100% noise
|
||||||
|
default:
|
||||||
|
score += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prev = char
|
||||||
|
}
|
||||||
|
|
||||||
|
return score
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsVersionString(component string) bool {
|
||||||
|
if component == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
hasV := false
|
||||||
|
if strings.HasPrefix(component, "v") {
|
||||||
|
component = component[1:]
|
||||||
|
hasV = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range component {
|
||||||
|
if string(c) != "." && !unicode.IsDigit(c) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasV && !strings.Contains(component, ".") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func trigramScore(str string) (float64, int) {
|
||||||
|
tgScore := 0.0
|
||||||
|
trigrams := ngrams(str, 3)
|
||||||
|
if len(trigrams) > 0 {
|
||||||
|
for _, trigram := range trigrams {
|
||||||
|
score, found := corpus_trigrams[trigram]
|
||||||
|
if found {
|
||||||
|
tgScore += score
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tgScore, len(trigrams)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isTrigramBad(s string) bool {
|
||||||
|
tgScore, cnt := trigramScore(s)
|
||||||
|
|
||||||
|
if cnt > 0 {
|
||||||
|
val := math.Sqrt(tgScore) / float64(cnt)
|
||||||
|
val2 := tgScore / float64(cnt)
|
||||||
|
threshold := 0.005
|
||||||
|
bad := val < threshold
|
||||||
|
threshold2 := math.Log(float64(cnt)-2) * 0.1
|
||||||
|
bad2 := val2 < threshold2
|
||||||
|
return bad && bad2 // TODO: improve this logic to be clearer
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func isNoisy(s string) bool {
|
||||||
|
noise := noiseLevel(s)
|
||||||
|
|
||||||
|
if len(s) > 0 {
|
||||||
|
val := (noise * noise) / float64(len(s))
|
||||||
|
threshold := 0.6
|
||||||
|
bad := val > threshold
|
||||||
|
return bad
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func ngrams(s string, n int) []string {
|
||||||
|
result := make([]string, 0)
|
||||||
|
for i := 0; i < len(s)-n+1; i++ {
|
||||||
|
result = append(result, s[i:i+n])
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
212
agent/pkg/oas/gibberish_test.go
Normal file
212
agent/pkg/oas/gibberish_test.go
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
package oas
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNegative(t *testing.T) {
|
||||||
|
cases := []string{
|
||||||
|
"",
|
||||||
|
"{}",
|
||||||
|
"0.0.29",
|
||||||
|
"0.1",
|
||||||
|
"1.0",
|
||||||
|
"1.0.0",
|
||||||
|
"2.1.73",
|
||||||
|
"abTestV2",
|
||||||
|
"actionText,setName,setAttribute,save,ignore,onEnd,getContext,end,get",
|
||||||
|
"AddUserGroupLink",
|
||||||
|
"advert-management.adBlockerMessage.html",
|
||||||
|
"agents.author.1.json",
|
||||||
|
"animated-gif",
|
||||||
|
"b", // can be valid hexadecimal
|
||||||
|
"big-danger-coronavirus-panic-greater-crisis",
|
||||||
|
"breakout-box",
|
||||||
|
"callback",
|
||||||
|
"core.algorithm_execution.view",
|
||||||
|
"core.devices.view",
|
||||||
|
"data.json",
|
||||||
|
"dialog.overlay.infinity.json",
|
||||||
|
"domain-input",
|
||||||
|
"embeddable", // lul, it's a valid HEX!
|
||||||
|
"embeddable_blip",
|
||||||
|
"E PLURIBUS UNUM",
|
||||||
|
"etc",
|
||||||
|
"eu-central-1a",
|
||||||
|
"fcgi-bin",
|
||||||
|
"footer.include.html",
|
||||||
|
"fullHashes:find",
|
||||||
|
"generate-feed",
|
||||||
|
"GetAds",
|
||||||
|
"GetCart",
|
||||||
|
"GetUniversalVariableUser",
|
||||||
|
"github-audit-exports",
|
||||||
|
"g.js",
|
||||||
|
"g.pixel",
|
||||||
|
".html",
|
||||||
|
"Hugo Michiels",
|
||||||
|
"image.sbix",
|
||||||
|
"index.html",
|
||||||
|
"iPad",
|
||||||
|
"Joanna Mazewski",
|
||||||
|
"LibGit2Sharp",
|
||||||
|
"Michael_Vaughan1.png",
|
||||||
|
"New RSS feed has been generated",
|
||||||
|
"nick-clegg",
|
||||||
|
"opt-out",
|
||||||
|
"pixel_details.html",
|
||||||
|
"post.json",
|
||||||
|
"profile-method-info",
|
||||||
|
"project-id",
|
||||||
|
"publisha.1.json",
|
||||||
|
"publish_and_moderate",
|
||||||
|
"Ronna McDaniel",
|
||||||
|
"rtb-h",
|
||||||
|
"runs",
|
||||||
|
"sign-up",
|
||||||
|
"some-uuid-maybe",
|
||||||
|
"stable-4.0-version.json",
|
||||||
|
"StartUpCheckout",
|
||||||
|
"Steve Flunk",
|
||||||
|
"sync_a9",
|
||||||
|
"Ted Cruz",
|
||||||
|
"test.png",
|
||||||
|
"token",
|
||||||
|
"ToList",
|
||||||
|
"v2.1.3",
|
||||||
|
"VersionCheck.php",
|
||||||
|
"v Rusiji",
|
||||||
|
"Walnut St",
|
||||||
|
"web_widget",
|
||||||
|
"zoom_in.cur",
|
||||||
|
"xray",
|
||||||
|
"web",
|
||||||
|
"vipbets1",
|
||||||
|
"trcc",
|
||||||
|
"fbpixel",
|
||||||
|
|
||||||
|
// TODO below
|
||||||
|
// "tcfv2",
|
||||||
|
// "Matt-cartoon-255x206px-small.png",
|
||||||
|
// "TheTelegraph_portal_white-320-small.png",
|
||||||
|
// "testdata-10kB.js",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, str := range cases {
|
||||||
|
if IsGibberish(str) {
|
||||||
|
t.Errorf("Mistakenly true: %s", str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPositive(t *testing.T) {
|
||||||
|
cases := []string{
|
||||||
|
"0a0d0174-b338-4520-a1c3-24f7e3d5ec50.html",
|
||||||
|
"1024807212418223",
|
||||||
|
"11ca096cbc224a67360493d44a9903",
|
||||||
|
"1553183382779",
|
||||||
|
"1554507871",
|
||||||
|
"19180481",
|
||||||
|
"203ef0f713abcebd8d62c35c0e3f12f87d71e5e4",
|
||||||
|
"456795af-b48f-4a8d-9b37-3e932622c2f0",
|
||||||
|
"601a2bdcc5b69137248ddbbf",
|
||||||
|
"60fe9aaeaefe2400012df94f",
|
||||||
|
"610bc3fd5a77a7fa25033fb0",
|
||||||
|
"610bd0315a77a7fa25034368",
|
||||||
|
"610bd0315a77a7fa25034368zh",
|
||||||
|
"6120c057c7a97b03f6986f1b",
|
||||||
|
"710a462e",
|
||||||
|
"730970532670-compute@developer.gserviceaccount.com",
|
||||||
|
"819db2242a648b305395537022523d65",
|
||||||
|
"952bea17-3776-11ea-9341-42010a84012a",
|
||||||
|
"a3226860758.html",
|
||||||
|
"AAAA028295945",
|
||||||
|
"arn-aws-ecs-eu-west-2-396248696294-cluster-london-01-ECSCluster-27iuIYva8nO4",
|
||||||
|
"arn-aws-ecs-eu-west-2-396248696294-cluster-london-01-ECSCluster-27iuIYva8nO4", // ?
|
||||||
|
"bnjksfd897345nl098asd53412kl98",
|
||||||
|
"c738338322370b47a79251f7510dd", // prefixed hex
|
||||||
|
"ci12NC01YzkyNTEzYzllMDRhLTAtYy5tb25pdG9yaW5nLmpzb24=", // long base64
|
||||||
|
"css/login.0f48c49a34eb53ea4623.min.css",
|
||||||
|
"d_fLLxlhzDilixeBEimaZ5",
|
||||||
|
"e21f7112-3d3b-4632-9da3-a4af2e0e9166",
|
||||||
|
"e8782afc112720300c049ff124434b79",
|
||||||
|
"fb6cjraf9cejut2a",
|
||||||
|
"i-0236530c66ed02200",
|
||||||
|
"JEHJW4BKVFDRTMTUQLHKK5WVAU",
|
||||||
|
"john.dow.1981@protonmail.com",
|
||||||
|
"MDEyOk9yZ2FuaXphdGlvbjU3MzI0Nzk1",
|
||||||
|
"MNUTGVFMGLEMFTBH0XSE5E02F6J2DS",
|
||||||
|
"n63nd45qsj",
|
||||||
|
"n9z9QGNiz",
|
||||||
|
"NC4WTmcy",
|
||||||
|
"proxy.3d2100fd7107262ecb55ce6847f01fa5.html",
|
||||||
|
"QgAAAC6zw0qH2DJtnXe8Z7rUJP0FgAFKkOhcHdFWzL1ZYggtwBgiB3LSoele9o3ZqFh7iCBhHbVLAnMuJ0HF8hEw7UKecE6wd-MBXgeRMdubGydhAMZSmuUjRpqplML40bmrb8VjJKNZswD1Cg",
|
||||||
|
"QgAAAC6zw0qH2DJtnXe8Z7rUJP0rG4sjLa_KVLlww5WEDJ__30J15en-K_6Y68jb_rU93e2TFY6fb0MYiQ1UrLNMQufqODHZUl39Lo6cXAOVOThjAMZSmuVH7n85JOYSCgzpvowMAVueGG0Xxg",
|
||||||
|
"qwerqwerasdfqwer@protonmai.com",
|
||||||
|
"r-ext-5579e00a95c90",
|
||||||
|
"r-ext-5579e8b12f11e",
|
||||||
|
"r-v4-5c92513c9e04a",
|
||||||
|
"r-v4-5c92513c9e04a-0-c.monitoring.json",
|
||||||
|
"segments-1563566437171.639994",
|
||||||
|
"sp_ANQXRpqH_urn$3Auri$3Abase64$3A6698b0a3-97ad-52ce-8fc3-17d99e37a726",
|
||||||
|
"sp_dxJTfx11_576742227280287872",
|
||||||
|
"sp_NnUPB5wj_601a2bdcc5b69137248ddbbf",
|
||||||
|
"sp_NxITuoE4_premiumchron-article-14302157_c_ryGQBs_r_yIWvwP",
|
||||||
|
"t_52d94268-8810-4a7e-ba87-ffd657a6752f",
|
||||||
|
"timeouts-1563566437171.639994",
|
||||||
|
"u_YPF3GsGKMo02",
|
||||||
|
|
||||||
|
"0000000000 65535 f",
|
||||||
|
"0000000178 00000 n",
|
||||||
|
"0-10000",
|
||||||
|
"01526123,",
|
||||||
|
"0,18168,183955,3,4,1151616,5663,731,223,5104,207,3204,10,1051,175,364,1435,4,60,576,241,383,246,5,1102",
|
||||||
|
"05/10/2020",
|
||||||
|
"14336456724940333",
|
||||||
|
"fb6cjraf9cejut2a",
|
||||||
|
"JEHJW4BKVFDRTMTUQLHKK5WVAU",
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
/*
|
||||||
|
"0,20",
|
||||||
|
"0.001",
|
||||||
|
"YISAtiX1",
|
||||||
|
"Fxvd1timk", // questionable
|
||||||
|
"B4GCSkORAJs",
|
||||||
|
"D_4EDAqenHQ",
|
||||||
|
"EICJp29EGOk",
|
||||||
|
"Fxvd1timk",
|
||||||
|
"GTqMZELYfQQ",
|
||||||
|
"GZPTpLPEGmwHGWPC",
|
||||||
|
"_HChnE9NDPY",
|
||||||
|
"NwhjgIWHgGg",
|
||||||
|
"production/tsbqksph4xswqjexfbec",
|
||||||
|
"p/u/bguhrxupr23mw3nwxcrw",
|
||||||
|
"nRSNapbJZnc",
|
||||||
|
"zgfpbtolciznub5egzxk",
|
||||||
|
"zufnu7aimadua9wrgwwo",
|
||||||
|
"zznto1jzch9yjsbtbrul",
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, str := range cases {
|
||||||
|
if !IsGibberish(str) {
|
||||||
|
t.Errorf("Mistakenly false: %s", str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVersionStrings(t *testing.T) {
|
||||||
|
cases := []string{
|
||||||
|
"1.0",
|
||||||
|
"1.0.0",
|
||||||
|
"v2.1.3",
|
||||||
|
"2.1.73",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, str := range cases {
|
||||||
|
if !IsVersionString(str) {
|
||||||
|
t.Errorf("Mistakenly false: %s", str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
77
agent/pkg/oas/ignores.go
Normal file
77
agent/pkg/oas/ignores.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package oas
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
var ignoredExtensions = []string{"gif", "svg", "css", "png", "ico", "js", "woff2", "woff", "jpg", "jpeg", "swf", "ttf", "map", "webp", "otf", "mp3"}
|
||||||
|
|
||||||
|
var ignoredCtypePrefixes = []string{"image/", "font/", "video/", "audio/", "text/javascript"}
|
||||||
|
var ignoredCtypes = []string{"application/javascript", "application/x-javascript", "text/css", "application/font-woff2", "application/font-woff", "application/x-font-woff"}
|
||||||
|
|
||||||
|
var ignoredHeaders = []string{
|
||||||
|
"a-im", "accept",
|
||||||
|
"authorization", "cache-control", "connection", "content-encoding", "content-length", "content-type", "cookie",
|
||||||
|
"date", "dnt", "expect", "forwarded", "from", "front-end-https", "host", "http2-settings",
|
||||||
|
"max-forwards", "origin", "pragma", "proxy-authorization", "proxy-connection", "range", "referer",
|
||||||
|
"save-data", "te", "trailer", "transfer-encoding", "upgrade", "upgrade-insecure-requests", "x-download-options",
|
||||||
|
"server", "user-agent", "via", "warning", "strict-transport-security", "x-permitted-cross-domain-policies",
|
||||||
|
"x-att-deviceid", "x-correlation-id", "correlation-id", "x-client-data", "x-dns-prefetch-control",
|
||||||
|
"x-http-method-override", "x-real-ip", "x-request-id", "x-request-start", "x-requested-with", "x-uidh",
|
||||||
|
"x-same-domain", "x-content-type-options", "x-frame-options", "x-xss-protection",
|
||||||
|
"x-wap-profile", "x-scheme", "status", "x-cache", "x-application-context", "retry-after",
|
||||||
|
"newrelic", "x-cloud-trace-context", "sentry-trace", "x-cache-hits", "x-served-by", "x-span-name",
|
||||||
|
"expires", "set-cookie", "p3p", "content-security-policy", "content-security-policy-report-only",
|
||||||
|
"last-modified", "content-language", "x-varnish", "true-client-ip", "akamai-origin-hop",
|
||||||
|
"keep-alive", "etag", "alt-svc", "x-csrf-token", "x-ua-compatible", "vary", "x-powered-by",
|
||||||
|
"age", "allow", "www-authenticate", "expect-ct", "timing-allow-origin", "referrer-policy",
|
||||||
|
"x-aspnet-version", "x-aspnetmvc-version", "x-timer", "x-abuse-info", "x-mod-pagespeed",
|
||||||
|
"duration_ms", // UP9 custom
|
||||||
|
}
|
||||||
|
|
||||||
|
var ignoredHeaderPrefixes = []string{
|
||||||
|
":", "accept-", "access-control-", "if-", "sec-", "grpc-",
|
||||||
|
"x-forwarded-", "x-original-", "cf-",
|
||||||
|
"x-up9-", "x-envoy-", "x-hasura-", "x-b3-", "x-datadog-", "x-envoy-", "x-amz-", "x-newrelic-", "x-prometheus-",
|
||||||
|
"x-akamai-", "x-spotim-", "x-amzn-", "x-ratelimit-", "x-goog-",
|
||||||
|
}
|
||||||
|
|
||||||
|
func isCtypeIgnored(ctype string) bool {
|
||||||
|
for _, prefix := range ignoredCtypePrefixes {
|
||||||
|
if strings.HasPrefix(ctype, prefix) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, toIgnore := range ignoredCtypes {
|
||||||
|
if ctype == toIgnore {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func isExtIgnored(path string) bool {
|
||||||
|
for _, extIgn := range ignoredExtensions {
|
||||||
|
if strings.HasSuffix(path, "."+extIgn) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func isHeaderIgnored(name string) bool {
|
||||||
|
name = strings.ToLower(name)
|
||||||
|
|
||||||
|
for _, ignore := range ignoredHeaders {
|
||||||
|
if name == ignore {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, prefix := range ignoredHeaderPrefixes {
|
||||||
|
if strings.HasPrefix(name, prefix) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
119
agent/pkg/oas/oas_generator.go
Normal file
119
agent/pkg/oas/oas_generator.go
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
package oas
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"net/url"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/har"
|
||||||
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
syncOnce sync.Once
|
||||||
|
instance *oasGenerator
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetOasGeneratorInstance() *oasGenerator {
|
||||||
|
syncOnce.Do(func() {
|
||||||
|
instance = newOasGenerator()
|
||||||
|
logger.Log.Debug("OAS Generator Initialized")
|
||||||
|
})
|
||||||
|
return instance
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *oasGenerator) Start() {
|
||||||
|
if g.started {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
g.cancel = cancel
|
||||||
|
g.ctx = ctx
|
||||||
|
g.entriesChan = make(chan EntryWithSource, 100) // buffer up to 100 entries for OAS processing
|
||||||
|
g.ServiceSpecs = &sync.Map{}
|
||||||
|
g.started = true
|
||||||
|
go instance.runGeneretor()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *oasGenerator) runGeneretor() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-g.ctx.Done():
|
||||||
|
logger.Log.Infof("OAS Generator was canceled")
|
||||||
|
return
|
||||||
|
|
||||||
|
case entryWithSource, ok := <-g.entriesChan:
|
||||||
|
if !ok {
|
||||||
|
logger.Log.Infof("OAS Generator - entries channel closed")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
entry := entryWithSource.Entry
|
||||||
|
u, err := url.Parse(entry.Request.URL)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Errorf("Failed to parse entry URL: %v, err: %v", entry.Request.URL, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
val, found := g.ServiceSpecs.Load(u.Host)
|
||||||
|
var gen *SpecGen
|
||||||
|
if !found {
|
||||||
|
gen = NewGen(u.Scheme + "://" + u.Host)
|
||||||
|
g.ServiceSpecs.Store(u.Host, gen)
|
||||||
|
} else {
|
||||||
|
gen = val.(*SpecGen)
|
||||||
|
}
|
||||||
|
|
||||||
|
opId, err := gen.feedEntry(entryWithSource)
|
||||||
|
if err != nil {
|
||||||
|
txt, suberr := json.Marshal(entry)
|
||||||
|
if suberr == nil {
|
||||||
|
logger.Log.Debugf("Problematic entry: %s", txt)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Log.Warningf("Failed processing entry: %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Log.Debugf("Handled entry %s as opId: %s", entry.Request.URL, opId) // TODO: set opId back to entry?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *oasGenerator) Reset() {
|
||||||
|
g.ServiceSpecs = &sync.Map{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *oasGenerator) PushEntry(entryWithSource *EntryWithSource) {
|
||||||
|
if !g.started {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case g.entriesChan <- *entryWithSource:
|
||||||
|
default:
|
||||||
|
logger.Log.Warningf("OAS Generator - entry wasn't sent to channel because the channel has no buffer or there is no receiver")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newOasGenerator() *oasGenerator {
|
||||||
|
return &oasGenerator{
|
||||||
|
started: false,
|
||||||
|
ctx: nil,
|
||||||
|
cancel: nil,
|
||||||
|
ServiceSpecs: nil,
|
||||||
|
entriesChan: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type EntryWithSource struct {
|
||||||
|
Source string
|
||||||
|
Entry har.Entry
|
||||||
|
Id uint
|
||||||
|
}
|
||||||
|
|
||||||
|
type oasGenerator struct {
|
||||||
|
started bool
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
ServiceSpecs *sync.Map
|
||||||
|
entriesChan chan EntryWithSource
|
||||||
|
}
|
||||||
729
agent/pkg/oas/specgen.go
Normal file
729
agent/pkg/oas/specgen.go
Normal file
@@ -0,0 +1,729 @@
|
|||||||
|
package oas
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"github.com/chanced/openapi"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/nav-inc/datetime"
|
||||||
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"mime"
|
||||||
|
"mime/multipart"
|
||||||
|
"net/textproto"
|
||||||
|
"net/url"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/har"
|
||||||
|
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const LastSeenTS = "x-last-seen-ts"
|
||||||
|
const CountersTotal = "x-counters-total"
|
||||||
|
const CountersPerSource = "x-counters-per-source"
|
||||||
|
const SampleId = "x-sample-entry"
|
||||||
|
|
||||||
|
type reqResp struct { // hello, generics in Go
|
||||||
|
Req *har.Request
|
||||||
|
Resp *har.Response
|
||||||
|
}
|
||||||
|
|
||||||
|
type SpecGen struct {
|
||||||
|
oas *openapi.OpenAPI
|
||||||
|
tree *Node
|
||||||
|
lock sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGen(server string) *SpecGen {
|
||||||
|
spec := new(openapi.OpenAPI)
|
||||||
|
spec.Version = "3.1.0"
|
||||||
|
|
||||||
|
info := openapi.Info{Title: server}
|
||||||
|
info.Version = "1.0"
|
||||||
|
spec.Info = &info
|
||||||
|
spec.Paths = &openapi.Paths{Items: map[openapi.PathValue]*openapi.PathObj{}}
|
||||||
|
|
||||||
|
spec.Servers = make([]*openapi.Server, 0)
|
||||||
|
spec.Servers = append(spec.Servers, &openapi.Server{URL: server})
|
||||||
|
|
||||||
|
gen := SpecGen{oas: spec, tree: new(Node)}
|
||||||
|
return &gen
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *SpecGen) StartFromSpec(oas *openapi.OpenAPI) {
|
||||||
|
g.oas = oas
|
||||||
|
g.tree = new(Node)
|
||||||
|
for pathStr, pathObj := range oas.Paths.Items {
|
||||||
|
pathSplit := strings.Split(string(pathStr), "/")
|
||||||
|
g.tree.getOrSet(pathSplit, pathObj)
|
||||||
|
|
||||||
|
// clean "last entry timestamp" markers from the past
|
||||||
|
for _, pathAndOp := range g.tree.listOps() {
|
||||||
|
delete(pathAndOp.op.Extensions, LastSeenTS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *SpecGen) feedEntry(entryWithSource EntryWithSource) (string, error) {
|
||||||
|
g.lock.Lock()
|
||||||
|
defer g.lock.Unlock()
|
||||||
|
|
||||||
|
opId, err := g.handlePathObj(&entryWithSource)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: opId can be empty for some failed entries
|
||||||
|
return opId, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *SpecGen) GetSpec() (*openapi.OpenAPI, error) {
|
||||||
|
g.lock.Lock()
|
||||||
|
defer g.lock.Unlock()
|
||||||
|
|
||||||
|
g.tree.compact()
|
||||||
|
|
||||||
|
counters := CounterMaps{counterTotal: Counter{}, counterMapTotal: CounterMap{}}
|
||||||
|
|
||||||
|
for _, pathAndOp := range g.tree.listOps() {
|
||||||
|
opObj := pathAndOp.op
|
||||||
|
if opObj.Summary == "" {
|
||||||
|
opObj.Summary = pathAndOp.path
|
||||||
|
}
|
||||||
|
|
||||||
|
err := counters.processOp(opObj)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := counters.processOas(g.oas)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// put paths back from tree into OAS
|
||||||
|
g.oas.Paths = g.tree.listPaths()
|
||||||
|
|
||||||
|
suggestTags(g.oas)
|
||||||
|
|
||||||
|
g.oas.Info.Description = setCounterMsgIfOk(g.oas.Info.Description, &counters.counterTotal)
|
||||||
|
|
||||||
|
// to make a deep copy, no better idea than marshal+unmarshal
|
||||||
|
specText, err := json.MarshalIndent(g.oas, "", "\t")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
spec := new(openapi.OpenAPI)
|
||||||
|
err = json.Unmarshal(specText, spec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return spec, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func suggestTags(oas *openapi.OpenAPI) {
|
||||||
|
paths := getPathsKeys(oas.Paths.Items)
|
||||||
|
sort.Strings(paths) // make it stable in case of multiple candidates
|
||||||
|
for len(paths) > 0 {
|
||||||
|
group := make([]string, 0)
|
||||||
|
group = append(group, paths[0])
|
||||||
|
paths = paths[1:]
|
||||||
|
|
||||||
|
pathsClone := append(paths[:0:0], paths...)
|
||||||
|
for _, path := range pathsClone {
|
||||||
|
if getSimilarPrefix([]string{group[0], path}) != "" {
|
||||||
|
group = append(group, path)
|
||||||
|
paths = deleteFromSlice(paths, path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
common := getSimilarPrefix(group)
|
||||||
|
|
||||||
|
if len(group) > 1 {
|
||||||
|
for _, path := range group {
|
||||||
|
pathObj := oas.Paths.Items[openapi.PathValue(path)]
|
||||||
|
for _, op := range getOps(pathObj) {
|
||||||
|
if op.Tags == nil {
|
||||||
|
op.Tags = make([]string, 0)
|
||||||
|
}
|
||||||
|
// only add tags if not present
|
||||||
|
if len(op.Tags) == 0 {
|
||||||
|
op.Tags = append(op.Tags, common)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPathsKeys(mymap map[openapi.PathValue]*openapi.PathObj) []string {
|
||||||
|
keys := make([]string, len(mymap))
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for k := range mymap {
|
||||||
|
keys[i] = string(k)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *SpecGen) handlePathObj(entryWithSource *EntryWithSource) (string, error) {
|
||||||
|
entry := entryWithSource.Entry
|
||||||
|
urlParsed, err := url.Parse(entry.Request.URL)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if isExtIgnored(urlParsed.Path) {
|
||||||
|
logger.Log.Debugf("Dropped traffic entry due to ignored extension: %s", urlParsed.Path)
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if entry.Request.Method == "OPTIONS" {
|
||||||
|
logger.Log.Debugf("Dropped traffic entry due to its method: %s", urlParsed.Path)
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ctype := getRespCtype(&entry.Response)
|
||||||
|
if isCtypeIgnored(ctype) {
|
||||||
|
logger.Log.Debugf("Dropped traffic entry due to ignored response ctype: %s", ctype)
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if entry.Response.Status < 100 {
|
||||||
|
logger.Log.Debugf("Dropped traffic entry due to status<100: %s", entry.StartedDateTime)
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if entry.Response.Status == 301 || entry.Response.Status == 308 {
|
||||||
|
logger.Log.Debugf("Dropped traffic entry due to permanent redirect status: %s", entry.StartedDateTime)
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if entry.Response.Status == 502 || entry.Response.Status == 503 || entry.Response.Status == 504 {
|
||||||
|
logger.Log.Debugf("Dropped traffic entry due to temporary server error: %s", entry.StartedDateTime)
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var split []string
|
||||||
|
if urlParsed.RawPath != "" {
|
||||||
|
split = strings.Split(urlParsed.RawPath, "/")
|
||||||
|
} else {
|
||||||
|
split = strings.Split(urlParsed.Path, "/")
|
||||||
|
}
|
||||||
|
node := g.tree.getOrSet(split, new(openapi.PathObj))
|
||||||
|
opObj, err := handleOpObj(entryWithSource, node.pathObj)
|
||||||
|
|
||||||
|
if opObj != nil {
|
||||||
|
return opObj.OperationID, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleOpObj(entryWithSource *EntryWithSource, pathObj *openapi.PathObj) (*openapi.Operation, error) {
|
||||||
|
entry := entryWithSource.Entry
|
||||||
|
isSuccess := 100 <= entry.Response.Status && entry.Response.Status < 400
|
||||||
|
opObj, wasMissing, err := getOpObj(pathObj, entry.Request.Method, isSuccess)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isSuccess && wasMissing {
|
||||||
|
logger.Log.Debugf("Dropped traffic entry due to failed status and no known endpoint at: %s", entry.StartedDateTime)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = handleRequest(&entry.Request, opObj, isSuccess)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = handleResponse(&entry.Response, opObj, isSuccess)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = handleCounters(opObj, isSuccess, entryWithSource)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return opObj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleCounters(opObj *openapi.Operation, success bool, entryWithSource *EntryWithSource) error {
|
||||||
|
// TODO: if performance around DecodeExtension+SetExtension is bad, store counters as separate maps
|
||||||
|
counter := Counter{}
|
||||||
|
counterMap := CounterMap{}
|
||||||
|
prevTs := 0.0
|
||||||
|
if opObj.Extensions == nil {
|
||||||
|
opObj.Extensions = openapi.Extensions{}
|
||||||
|
} else {
|
||||||
|
if _, ok := opObj.Extensions.Extension(CountersTotal); ok {
|
||||||
|
err := opObj.Extensions.DecodeExtension(CountersTotal, &counter)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := opObj.Extensions.Extension(CountersPerSource); ok {
|
||||||
|
err := opObj.Extensions.DecodeExtension(CountersPerSource, &counterMap)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := opObj.Extensions.Extension(LastSeenTS); ok {
|
||||||
|
err := opObj.Extensions.DecodeExtension(LastSeenTS, &prevTs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var counterPerSource *Counter
|
||||||
|
if existing, ok := counterMap[entryWithSource.Source]; ok {
|
||||||
|
counterPerSource = existing
|
||||||
|
} else {
|
||||||
|
counterPerSource = new(Counter)
|
||||||
|
counterMap[entryWithSource.Source] = counterPerSource
|
||||||
|
}
|
||||||
|
|
||||||
|
started, err := datetime.Parse(entryWithSource.Entry.StartedDateTime, time.UTC)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ts := float64(started.UnixNano()) / float64(time.Millisecond) / 1000
|
||||||
|
rt := float64(entryWithSource.Entry.Time) / 1000
|
||||||
|
|
||||||
|
dur := 0.0
|
||||||
|
if prevTs != 0 && ts >= prevTs {
|
||||||
|
dur = ts - prevTs
|
||||||
|
}
|
||||||
|
|
||||||
|
counter.addEntry(ts, rt, success, dur)
|
||||||
|
counterPerSource.addEntry(ts, rt, success, dur)
|
||||||
|
|
||||||
|
err = opObj.Extensions.SetExtension(LastSeenTS, ts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = opObj.Extensions.SetExtension(CountersTotal, counter)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = opObj.Extensions.SetExtension(CountersPerSource, counterMap)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = opObj.Extensions.SetExtension(SampleId, entryWithSource.Id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleRequest(req *har.Request, opObj *openapi.Operation, isSuccess bool) error {
|
||||||
|
// TODO: we don't handle the situation when header/qstr param can be defined on pathObj level. Also the path param defined on opObj
|
||||||
|
urlParsed, err := url.Parse(req.URL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
qs := make([]har.NVP, 0)
|
||||||
|
for name, vals := range urlParsed.Query() {
|
||||||
|
for _, val := range vals {
|
||||||
|
qs = append(qs, har.NVP{Name: name, Value: val})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(qs) != len(req.QueryString) {
|
||||||
|
logger.Log.Warningf("QStr params in HAR do not match URL: %s", req.URL)
|
||||||
|
}
|
||||||
|
|
||||||
|
qstrGW := nvParams{
|
||||||
|
In: openapi.InQuery,
|
||||||
|
Pairs: qs,
|
||||||
|
IsIgnored: func(name string) bool { return false },
|
||||||
|
GeneralizeName: func(name string) string { return name },
|
||||||
|
}
|
||||||
|
handleNameVals(qstrGW, &opObj.Parameters, false)
|
||||||
|
|
||||||
|
hdrGW := nvParams{
|
||||||
|
In: openapi.InHeader,
|
||||||
|
Pairs: req.Headers,
|
||||||
|
IsIgnored: isHeaderIgnored,
|
||||||
|
GeneralizeName: strings.ToLower,
|
||||||
|
}
|
||||||
|
handleNameVals(hdrGW, &opObj.Parameters, true)
|
||||||
|
|
||||||
|
if isSuccess {
|
||||||
|
reqBody, err := getRequestBody(req, opObj)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if reqBody != nil {
|
||||||
|
if req.PostData.Text == "" {
|
||||||
|
reqBody.Required = false
|
||||||
|
} else {
|
||||||
|
|
||||||
|
reqCtype, _ := getReqCtype(req)
|
||||||
|
reqMedia, err := fillContent(reqResp{Req: req}, reqBody.Content, reqCtype)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = reqMedia
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleResponse(resp *har.Response, opObj *openapi.Operation, isSuccess bool) error {
|
||||||
|
// TODO: we don't support "default" response
|
||||||
|
respObj, err := getResponseObj(resp, opObj, isSuccess)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
handleRespHeaders(resp.Headers, respObj)
|
||||||
|
|
||||||
|
respCtype := getRespCtype(resp)
|
||||||
|
respContent := respObj.Content
|
||||||
|
respMedia, err := fillContent(reqResp{Resp: resp}, respContent, respCtype)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_ = respMedia
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleRespHeaders(reqHeaders []har.Header, respObj *openapi.ResponseObj) {
|
||||||
|
visited := map[string]*openapi.HeaderObj{}
|
||||||
|
for _, pair := range reqHeaders {
|
||||||
|
if isHeaderIgnored(pair.Name) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
nameGeneral := strings.ToLower(pair.Name)
|
||||||
|
|
||||||
|
initHeaders(respObj)
|
||||||
|
objHeaders := respObj.Headers
|
||||||
|
param := findHeaderByName(&respObj.Headers, pair.Name)
|
||||||
|
if param == nil {
|
||||||
|
param = createHeader(openapi.TypeString)
|
||||||
|
objHeaders[nameGeneral] = param
|
||||||
|
}
|
||||||
|
exmp := ¶m.Examples
|
||||||
|
err := fillParamExample(&exmp, pair.Value)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Warningf("Failed to add example to a parameter: %s", err)
|
||||||
|
}
|
||||||
|
visited[nameGeneral] = param
|
||||||
|
}
|
||||||
|
|
||||||
|
// maintain "required" flag
|
||||||
|
if respObj.Headers != nil {
|
||||||
|
for name, param := range respObj.Headers {
|
||||||
|
paramObj, err := param.ResolveHeader(headerResolver)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Warningf("Failed to resolve param: %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ok := visited[strings.ToLower(name)]
|
||||||
|
if !ok {
|
||||||
|
flag := false
|
||||||
|
paramObj.Required = &flag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func fillContent(reqResp reqResp, respContent openapi.Content, ctype string) (*openapi.MediaType, error) {
|
||||||
|
content, found := respContent[ctype]
|
||||||
|
if !found {
|
||||||
|
respContent[ctype] = &openapi.MediaType{}
|
||||||
|
content = respContent[ctype]
|
||||||
|
}
|
||||||
|
|
||||||
|
var text string
|
||||||
|
var isBinary bool
|
||||||
|
if reqResp.Req != nil {
|
||||||
|
isBinary, _, text = reqResp.Req.PostData.B64Decoded()
|
||||||
|
} else {
|
||||||
|
isBinary, _, text = reqResp.Resp.Content.B64Decoded()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isBinary && text != "" {
|
||||||
|
var exampleMsg []byte
|
||||||
|
// try treating it as json
|
||||||
|
any, isJSON := anyJSON(text)
|
||||||
|
if isJSON {
|
||||||
|
// re-marshal with forced indent
|
||||||
|
if msg, err := json.MarshalIndent(any, "", "\t"); err != nil {
|
||||||
|
panic("Failed to re-marshal value, super-strange")
|
||||||
|
} else {
|
||||||
|
exampleMsg = msg
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if msg, err := json.Marshal(text); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
exampleMsg = msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctype == "application/x-www-form-urlencoded" && reqResp.Req != nil {
|
||||||
|
handleFormDataUrlencoded(text, content)
|
||||||
|
} else if strings.HasPrefix(ctype, "multipart/form-data") && reqResp.Req != nil {
|
||||||
|
_, params := getReqCtype(reqResp.Req)
|
||||||
|
handleFormDataMultipart(text, content, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
if content.Example == nil && len(exampleMsg) > len(content.Example) {
|
||||||
|
content.Example = exampleMsg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return respContent[ctype], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleFormDataUrlencoded(text string, content *openapi.MediaType) {
|
||||||
|
formData, err := url.ParseQuery(text)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Warningf("Could not decode urlencoded: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := make([]PartWithBody, 0)
|
||||||
|
for name, vals := range formData {
|
||||||
|
for _, val := range vals {
|
||||||
|
part := new(multipart.Part)
|
||||||
|
part.Header = textproto.MIMEHeader{}
|
||||||
|
part.Header.Add("Content-Disposition", "form-data; name=\""+name+"\";")
|
||||||
|
parts = append(parts, PartWithBody{part: part, body: []byte(val)})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handleFormData(content, parts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleFormData(content *openapi.MediaType, parts []PartWithBody) {
|
||||||
|
hadSchema := true
|
||||||
|
if content.Schema == nil {
|
||||||
|
hadSchema = false // will use it for required flags
|
||||||
|
content.Schema = new(openapi.SchemaObj)
|
||||||
|
content.Schema.Type = openapi.Types{openapi.TypeObject}
|
||||||
|
content.Schema.Properties = openapi.Schemas{}
|
||||||
|
}
|
||||||
|
|
||||||
|
props := &content.Schema.Properties
|
||||||
|
seenNames := map[string]struct{}{} // set equivalent in Go, yikes
|
||||||
|
for _, pwb := range parts {
|
||||||
|
name := pwb.part.FormName()
|
||||||
|
seenNames[name] = struct{}{}
|
||||||
|
existing, found := (*props)[name]
|
||||||
|
if !found {
|
||||||
|
existing = new(openapi.SchemaObj)
|
||||||
|
existing.Type = openapi.Types{openapi.TypeString}
|
||||||
|
(*props)[name] = existing
|
||||||
|
|
||||||
|
ctype := pwb.part.Header.Get("content-type")
|
||||||
|
if ctype != "" {
|
||||||
|
if existing.Keywords == nil {
|
||||||
|
existing.Keywords = map[string]json.RawMessage{}
|
||||||
|
}
|
||||||
|
existing.Keywords["contentMediaType"], _ = json.Marshal(ctype)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addSchemaExample(existing, string(pwb.body))
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle required flag
|
||||||
|
if content.Schema.Required == nil {
|
||||||
|
if !hadSchema {
|
||||||
|
content.Schema.Required = make([]string, 0)
|
||||||
|
for name := range seenNames {
|
||||||
|
content.Schema.Required = append(content.Schema.Required, name)
|
||||||
|
}
|
||||||
|
sort.Strings(content.Schema.Required)
|
||||||
|
} // else it's a known schema with no required fields
|
||||||
|
} else {
|
||||||
|
content.Schema.Required = intersectSliceWithMap(content.Schema.Required, seenNames)
|
||||||
|
sort.Strings(content.Schema.Required)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type PartWithBody struct {
|
||||||
|
part *multipart.Part
|
||||||
|
body []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleFormDataMultipart(text string, content *openapi.MediaType, ctypeParams map[string]string) {
|
||||||
|
boundary, ok := ctypeParams["boundary"]
|
||||||
|
if !ok {
|
||||||
|
logger.Log.Errorf("Multipart header has no boundary")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mpr := multipart.NewReader(strings.NewReader(text), boundary)
|
||||||
|
|
||||||
|
parts := make([]PartWithBody, 0)
|
||||||
|
for {
|
||||||
|
part, err := mpr.NextPart()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Errorf("Cannot parse multipart body: %v", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
defer part.Close()
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(part)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Errorf("Error reading multipart Part %s: %v", part.Header, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
parts = append(parts, PartWithBody{part: part, body: body})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleFormData(content, parts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRespCtype(resp *har.Response) string {
|
||||||
|
var ctype string
|
||||||
|
ctype = resp.Content.MimeType
|
||||||
|
for _, hdr := range resp.Headers {
|
||||||
|
if strings.ToLower(hdr.Name) == "content-type" {
|
||||||
|
ctype = hdr.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mediaType, _, err := mime.ParseMediaType(ctype)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return mediaType
|
||||||
|
}
|
||||||
|
|
||||||
|
func getReqCtype(req *har.Request) (ctype string, params map[string]string) {
|
||||||
|
ctype = req.PostData.MimeType
|
||||||
|
for _, hdr := range req.Headers {
|
||||||
|
if strings.ToLower(hdr.Name) == "content-type" {
|
||||||
|
ctype = hdr.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctype == "" {
|
||||||
|
return "", map[string]string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
mediaType, params, err := mime.ParseMediaType(ctype)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Errorf("Cannot parse Content-Type header %q: %v", ctype, err)
|
||||||
|
return "", map[string]string{}
|
||||||
|
}
|
||||||
|
return mediaType, params
|
||||||
|
}
|
||||||
|
|
||||||
|
func getResponseObj(resp *har.Response, opObj *openapi.Operation, isSuccess bool) (*openapi.ResponseObj, error) {
|
||||||
|
statusStr := strconv.Itoa(resp.Status)
|
||||||
|
|
||||||
|
var response openapi.Response
|
||||||
|
response, found := opObj.Responses[statusStr]
|
||||||
|
if !found {
|
||||||
|
if opObj.Responses == nil {
|
||||||
|
opObj.Responses = map[string]openapi.Response{}
|
||||||
|
}
|
||||||
|
|
||||||
|
opObj.Responses[statusStr] = &openapi.ResponseObj{Content: map[string]*openapi.MediaType{}}
|
||||||
|
response = opObj.Responses[statusStr]
|
||||||
|
}
|
||||||
|
|
||||||
|
resResponse, err := response.ResolveResponse(responseResolver)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if isSuccess {
|
||||||
|
resResponse.Description = "Successful call with status " + statusStr
|
||||||
|
} else {
|
||||||
|
resResponse.Description = "Failed call with status " + statusStr
|
||||||
|
}
|
||||||
|
return resResponse, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRequestBody(req *har.Request, opObj *openapi.Operation) (*openapi.RequestBodyObj, error) {
|
||||||
|
if opObj.RequestBody == nil {
|
||||||
|
// create if there is body in request
|
||||||
|
if req.PostData.Text != "" {
|
||||||
|
opObj.RequestBody = &openapi.RequestBodyObj{Description: "Generic request body", Required: true, Content: map[string]*openapi.MediaType{}}
|
||||||
|
} else {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reqBody, err := opObj.RequestBody.ResolveRequestBody(reqBodyResolver)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return reqBody, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getOpObj(pathObj *openapi.PathObj, method string, createIfNone bool) (*openapi.Operation, bool, error) {
|
||||||
|
method = strings.ToLower(method)
|
||||||
|
var op **openapi.Operation
|
||||||
|
|
||||||
|
switch method {
|
||||||
|
case "get":
|
||||||
|
op = &pathObj.Get
|
||||||
|
case "put":
|
||||||
|
op = &pathObj.Put
|
||||||
|
case "post":
|
||||||
|
op = &pathObj.Post
|
||||||
|
case "delete":
|
||||||
|
op = &pathObj.Delete
|
||||||
|
case "options":
|
||||||
|
op = &pathObj.Options
|
||||||
|
case "head":
|
||||||
|
op = &pathObj.Head
|
||||||
|
case "patch":
|
||||||
|
op = &pathObj.Patch
|
||||||
|
case "trace":
|
||||||
|
op = &pathObj.Trace
|
||||||
|
default:
|
||||||
|
return nil, false, errors.New("unsupported HTTP method: " + method)
|
||||||
|
}
|
||||||
|
|
||||||
|
isMissing := false
|
||||||
|
if *op == nil {
|
||||||
|
isMissing = true
|
||||||
|
if createIfNone {
|
||||||
|
*op = &openapi.Operation{Responses: map[string]openapi.Response{}}
|
||||||
|
newUUID := uuid.New().String()
|
||||||
|
(**op).OperationID = newUUID
|
||||||
|
} else {
|
||||||
|
return nil, isMissing, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return *op, isMissing, nil
|
||||||
|
}
|
||||||
253
agent/pkg/oas/specgen_test.go
Normal file
253
agent/pkg/oas/specgen_test.go
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
package oas
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/chanced/openapi"
|
||||||
|
"github.com/op/go-logging"
|
||||||
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
|
"github.com/wI2L/jsondiff"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/har"
|
||||||
|
)
|
||||||
|
|
||||||
|
// if started via env, write file into subdir
|
||||||
|
func outputSpec(label string, spec *openapi.OpenAPI, t *testing.T) string {
|
||||||
|
content, err := json.MarshalIndent(spec, "", "\t")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if os.Getenv("MIZU_OAS_WRITE_FILES") != "" {
|
||||||
|
path := "./oas-samples"
|
||||||
|
err := os.MkdirAll(path, 0o755)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(path+"/"+label+".json", content, 0644)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
t.Logf("Written: %s", label)
|
||||||
|
} else {
|
||||||
|
t.Logf("%s", string(content))
|
||||||
|
}
|
||||||
|
return string(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEntries(t *testing.T) {
|
||||||
|
logger.InitLoggerStderrOnly(logging.INFO)
|
||||||
|
files, err := getFiles("./test_artifacts/")
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
GetOasGeneratorInstance().Start()
|
||||||
|
loadStartingOAS()
|
||||||
|
|
||||||
|
cnt, err := feedEntries(files, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
waitQueueProcessed()
|
||||||
|
|
||||||
|
svcs := strings.Builder{}
|
||||||
|
GetOasGeneratorInstance().ServiceSpecs.Range(func(key, val interface{}) bool {
|
||||||
|
gen := val.(*SpecGen)
|
||||||
|
svc := key.(string)
|
||||||
|
svcs.WriteString(svc + ",")
|
||||||
|
spec, err := gen.GetSpec()
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
t.FailNow()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
err = spec.Validate()
|
||||||
|
if err != nil {
|
||||||
|
specText, _ := json.MarshalIndent(spec, "", "\t")
|
||||||
|
t.Log(string(specText))
|
||||||
|
t.Log(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
GetOasGeneratorInstance().ServiceSpecs.Range(func(key, val interface{}) bool {
|
||||||
|
svc := key.(string)
|
||||||
|
gen := val.(*SpecGen)
|
||||||
|
spec, err := gen.GetSpec()
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
outputSpec(svc, spec, t)
|
||||||
|
|
||||||
|
err = spec.Validate()
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
logger.Log.Infof("Total entries: %d", cnt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFileSingle(t *testing.T) {
|
||||||
|
GetOasGeneratorInstance().Start()
|
||||||
|
GetOasGeneratorInstance().Reset()
|
||||||
|
// loadStartingOAS()
|
||||||
|
file := "test_artifacts/params.har"
|
||||||
|
files := []string{file}
|
||||||
|
cnt, err := feedEntries(files, true)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Warning("Failed processing file: " + err.Error())
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
waitQueueProcessed()
|
||||||
|
|
||||||
|
GetOasGeneratorInstance().ServiceSpecs.Range(func(key, val interface{}) bool {
|
||||||
|
svc := key.(string)
|
||||||
|
gen := val.(*SpecGen)
|
||||||
|
spec, err := gen.GetSpec()
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
specText := outputSpec(svc, spec, t)
|
||||||
|
|
||||||
|
err = spec.Validate()
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
expected, err := ioutil.ReadFile(file + ".spec.json")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
patFloatPrecision := regexp.MustCompile(`(\d+\.\d{1,2})(\d*)`)
|
||||||
|
|
||||||
|
expected = []byte(patUuid4.ReplaceAllString(string(expected), "<UUID4>"))
|
||||||
|
specText = patUuid4.ReplaceAllString(specText, "<UUID4>")
|
||||||
|
expected = []byte(patFloatPrecision.ReplaceAllString(string(expected), "$1"))
|
||||||
|
specText = patFloatPrecision.ReplaceAllString(specText, "$1")
|
||||||
|
|
||||||
|
diff, err := jsondiff.CompareJSON(expected, []byte(specText))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf(err.Error())
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(diff) > 0 {
|
||||||
|
t.Errorf("Generated spec does not match expected:\n%s", diff.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
logger.Log.Infof("Processed entries: %d", cnt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitQueueProcessed() {
|
||||||
|
for {
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
queue := len(GetOasGeneratorInstance().entriesChan)
|
||||||
|
logger.Log.Infof("Queue: %d", queue)
|
||||||
|
if queue < 1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadStartingOAS() {
|
||||||
|
file := "test_artifacts/catalogue.json"
|
||||||
|
fd, err := os.Open(file)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer fd.Close()
|
||||||
|
|
||||||
|
data, err := ioutil.ReadAll(fd)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var doc *openapi.OpenAPI
|
||||||
|
err = json.Unmarshal(data, &doc)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
gen := NewGen("catalogue")
|
||||||
|
gen.StartFromSpec(doc)
|
||||||
|
|
||||||
|
GetOasGeneratorInstance().ServiceSpecs.Store("catalogue", gen)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEntriesNegative(t *testing.T) {
|
||||||
|
files := []string{"invalid"}
|
||||||
|
_, err := feedEntries(files, false)
|
||||||
|
if err == nil {
|
||||||
|
t.Logf("Should have failed")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEntriesPositive(t *testing.T) {
|
||||||
|
files := []string{"test_artifacts/params.har"}
|
||||||
|
_, err := feedEntries(files, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("Failed")
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadValidHAR(t *testing.T) {
|
||||||
|
inp := `{"startedDateTime": "2021-02-03T07:48:12.959000+00:00", "time": 1, "request": {"method": "GET", "url": "http://unresolved_target/1.0.0/health", "httpVersion": "HTTP/1.1", "cookies": [], "headers": [], "queryString": [], "headersSize": -1, "bodySize": -1}, "response": {"status": 200, "statusText": "OK", "httpVersion": "HTTP/1.1", "cookies": [], "headers": [], "content": {"size": 2, "mimeType": "", "text": "OK"}, "redirectURL": "", "headersSize": -1, "bodySize": 2}, "cache": {}, "timings": {"send": -1, "wait": -1, "receive": 1}}`
|
||||||
|
var entry *har.Entry
|
||||||
|
var err = json.Unmarshal([]byte(inp), &entry)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("Failed to decode entry: %s", err)
|
||||||
|
t.FailNow() // demonstrates the problem of `martian` HAR library
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadValid3_1(t *testing.T) {
|
||||||
|
fd, err := os.Open("test_artifacts/catalogue.json")
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
defer fd.Close()
|
||||||
|
|
||||||
|
data, err := ioutil.ReadAll(fd)
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
var oas openapi.OpenAPI
|
||||||
|
err = json.Unmarshal(data, &oas)
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
51
agent/pkg/oas/test_artifacts/catalogue.json
Normal file
51
agent/pkg/oas/test_artifacts/catalogue.json
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.1.0",
|
||||||
|
"info": {
|
||||||
|
"title": "Preloaded",
|
||||||
|
"version": "0.1",
|
||||||
|
"description": "Test file for loading pre-existing OAS"
|
||||||
|
},
|
||||||
|
"paths": {
|
||||||
|
"/catalogue/{id}": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"style": "simple",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"example": "some-uuid-maybe"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"get": {
|
||||||
|
"parameters": [ {
|
||||||
|
"name": "non-required-header",
|
||||||
|
"in": "header",
|
||||||
|
"required": true,
|
||||||
|
"style": "simple",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"example": "some-uuid-maybe"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/catalogue/{id}/details": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"style": "simple",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"example": "some-uuid-maybe"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24969
agent/pkg/oas/test_artifacts/example.har
Normal file
24969
agent/pkg/oas/test_artifacts/example.har
Normal file
File diff suppressed because one or more lines are too long
13
agent/pkg/oas/test_artifacts/example.ldjson
Normal file
13
agent/pkg/oas/test_artifacts/example.ldjson
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{"messageType": "http", "_source": "some-source", ",firstMessageTime": 1627298057.784151, "lastMessageTime": 1627298065.729303, "messageCount": 12}
|
||||||
|
{"_id": "", "startedDateTime": "2021-07-26T11:14:17.78415179Z", "time": 13, "request": {"method": "GET", "url": "http://catalogue/catalogue/size?tags=", "httpVersion": "HTTP/1.1", "cookies": [], "headers": [{"name": "x-some", "value": "demo val"},{"name": "Host", "value": "catalogue"}, {"name": "Connection", "value": "close"}], "queryString": [{"name": "tags", "value": ""}], "headersSize": -1, "bodySize": 0}, "response": {"status": 200, "statusText": "OK", "httpVersion": "HTTP/1.1", "cookies": [], "headers": [{"name": "Content-Type", "value": "application/json; charset=utf-8"}, {"name": "Date", "value": "Mon, 26 Jul 2021 11:14:17 GMT"}, {"name": "Content-Length", "value": "22"}], "content": {"size": 22, "mimeType": "application/json; charset=utf-8", "text": "eyJlcnIiOm51bGwsInNpemUiOjl9", "encoding": "base64"}, "redirectURL": "", "headersSize": -1, "bodySize": 22}, "cache": {}, "timings": {"send": -1, "wait": -1, "receive": 13}}
|
||||||
|
{"_id": "", "startedDateTime": "2021-07-26T11:14:17.784918698Z", "time": 19, "request": {"method": "GET", "url": "http://catalogue/catalogue?page=1&size=6&tags=", "httpVersion": "HTTP/1.1", "cookies": [], "headers": [{"name": "Connection", "value": "close"}, {"name": "Host", "value": "catalogue"}], "queryString": [{"name": "page", "value": "1"}, {"name": "size", "value": "6"}, {"name": "tags", "value": ""}], "headersSize": -1, "bodySize": 0}, "response": {"status": 200, "statusText": "OK", "httpVersion": "HTTP/1.1", "cookies": [], "headers": [{"name": "Content-Type", "value": "application/json; charset=utf-8"}, {"name": "Date", "value": "Mon, 26 Jul 2021 11:14:17 GMT"}, {"name": "Content-Length", "value": "1927"}], "content": {"size": 1927, "mimeType": "application/json; charset=utf-8", "text": "W3siaWQiOiIwM2ZlZjZhYy0xODk2LTRjZTgtYmQ2OS1iNzk4Zjg1YzZlMGIiLCJuYW1lIjoiSG9seSIsImRlc2NyaXB0aW9uIjoiU29ja3MgZml0IGZvciBhIE1lc3NpYWguIFlvdSB0b28gY2FuIGV4cGVyaWVuY2Ugd2Fsa2luZyBpbiB3YXRlciB3aXRoIHRoZXNlIHNwZWNpYWwgZWRpdGlvbiBiZWF1dGllcy4gRWFjaCBob2xlIGlzIGxvdmluZ2x5IHByb2dnbGVkIHRvIGxlYXZlIHNtb290aCBlZGdlcy4gVGhlIG9ubHkgc29jayBhcHByb3ZlZCBieSBhIGhpZ2hlciBwb3dlci4iLCJpbWFnZVVybCI6WyIvY2F0YWxvZ3VlL2ltYWdlcy9ob2x5XzEuanBlZyIsIi9jYXRhbG9ndWUvaW1hZ2VzL2hvbHlfMi5qcGVnIl0sInByaWNlIjo5OS45OSwiY291bnQiOjEsInRhZyI6WyJhY3Rpb24iLCJtYWdpYyJdfSx7ImlkIjoiMzM5NWE0M2UtMmQ4OC00MGRlLWI5NWYtZTAwZTE1MDIwODViIiwibmFtZSI6IkNvbG91cmZ1bCIsImRlc2NyaXB0aW9uIjoicHJvaWRlbnQgb2NjYWVjYXQgaXJ1cmUgZXQgZXhjZXB0ZXVyIGxhYm9yZSBtaW5pbSBuaXNpIGFtZXQgaXJ1cmUiLCJpbWFnZVVybCI6WyIvY2F0YWxvZ3VlL2ltYWdlcy9jb2xvdXJmdWxfc29ja3MuanBnIiwiL2NhdGFsb2d1ZS9pbWFnZXMvY29sb3VyZnVsX3NvY2tzLmpwZyJdLCJwcmljZSI6MTgsImNvdW50Ijo0MzgsInRhZyI6WyJicm93biIsImJsdWUiXX0seyJpZCI6IjUxMGEwZDdlLThlODMtNDE5My1iNDgzLWUyN2UwOWRkYzM0ZCIsIm5hbWUiOiJTdXBlclNwb3J0IFhMIiwiZGVzY3JpcHRpb24iOiJSZWFkeSBmb3IgYWN0aW9uLiBFbmdpbmVlcnM6IGJlIHJlYWR5IHRvIHNtYXNoIHRoYXQgbmV4dCBidWchIEJlIHJlYWR5LCB3aXRoIHRoZXNlIHN1cGVyLWFjdGlvbi1zcG9ydC1tYXN0ZXJwaWVjZXMuIFRoaXMgcGFydGljdWxhciBlbmdpbmVlciB3YXMgY2hhc2VkIGF3YXkgZnJvbSB0aGUgb2ZmaWNlIHdpdGggYSBzdGljay4iLCJpbWFnZVVybCI6WyIvY2F0YWxvZ3VlL2ltYWdlcy9wdW1hXzEuanBlZyIsIi9jYXRhbG9ndWUvaW1hZ2VzL3B1bWFfMi5qcGVnIl0sInByaWNlIjoxNSwiY291bnQiOjgyMCwidGFnIjpbInNwb3J0IiwiZm9ybWFsIiwiYmxhY2siXX0seyJpZCI6IjgwOGEyZGUxLTFhYWEtNGMyNS1hOWI5LTY2MTJlOGYyOWEzOCIsIm5hbWUiOiJDcm9zc2VkIiwiZGVzY3JpcHRpb24iOiJBIG1hdHVyZSBzb2NrLCBjcm9zc2VkLCB3aXRoIGFuIGFpciBvZiBub25jaGFsYW5jZS4iLCJpbWFnZVVybCI6WyIvY2F0YWxvZ3VlL2ltYWdlcy9jcm9zc18xLmpwZWciLCIvY2F0YWxvZ3VlL2ltYWdlcy9jcm9zc18yLmpwZWciXSwicHJpY2UiOjE3LjMyLCJjb3VudCI6NzM4LCJ0YWciOlsiYmx1ZSIsImFjdGlvbiIsInJlZCIsImZvcm1hbCJdfSx7ImlkIjoiODE5ZTFmYmYtOGI3ZS00ZjZkLTgxMWYtNjkzNTM0OTE2YThiIiwibmFtZSI6IkZpZ3Vlcm9hIiwiZGVzY3JpcHRpb24iOiJlbmltIG9mZmljaWEgYWxpcXVhIGV4Y2VwdGV1ciBlc3NlIGRlc2VydW50IHF1aXMgYWxpcXVpcCBub3N0cnVkIGFuaW0iLCJpbWFnZVVybCI6WyIvY2F0YWxvZ3VlL2ltYWdlcy9XQVQuanBnIiwiL2NhdGFsb2d1ZS9pbWFnZXMvV0FUMi5qcGciXSwicHJpY2UiOjE0LCJjb3VudCI6ODA4LCJ0YWciOlsiZ3JlZW4iLCJmb3JtYWwiLCJibHVlIl19LHsiaWQiOiI4MzdhYjE0MS0zOTllLTRjMWYtOWFiYy1iYWNlNDAyOTZiYWMiLCJuYW1lIjoiQ2F0IHNvY2tzIiwiZGVzY3JpcHRpb24iOiJjb25zZXF1YXQgYW1ldCBjdXBpZGF0YXQgbWluaW0gbGFib3J1bSB0ZW1wb3IgZWxpdCBleCBjb25zZXF1YXQgaW4iLCJpbWFnZVVybCI6WyIvY2F0YWxvZ3VlL2ltYWdlcy9jYXRzb2Nrcy5qcGciLCIvY2F0YWxvZ3VlL2ltYWdlcy9jYXRzb2NrczIuanBnIl0sInByaWNlIjoxNSwiY291bnQiOjE3NSwidGFnIjpbImJyb3duIiwiZm9ybWFsIiwiZ3JlZW4iXX1dCg==", "encoding": "base64"}, "redirectURL": "", "headersSize": -1, "bodySize": 1927}, "cache": {}, "timings": {"send": -1, "wait": -1, "receive": 19}}
|
||||||
|
{"_id": "", "startedDateTime": "2021-07-26T11:14:17.78418182Z", "time": 7, "request": {"method": "GET", "url": "http://catalogue/tags", "httpVersion": "HTTP/1.1", "cookies": [], "headers": [{"name": "Connection", "value": "close"}, {"name": "Host", "value": "catalogue"}], "queryString": [], "headersSize": -1, "bodySize": 0}, "response": {"status": 200, "statusText": "OK", "httpVersion": "HTTP/1.1", "cookies": [], "headers": [{"name": "Content-Type", "value": "application/json; charset=utf-8"}, {"name": "Date", "value": "Mon, 26 Jul 2021 11:14:17 GMT"}, {"name": "Content-Length", "value": "107"}], "content": {"size": 107, "mimeType": "application/json; charset=utf-8", "text": "eyJlcnIiOm51bGwsInRhZ3MiOlsiYnJvd24iLCJnZWVrIiwiZm9ybWFsIiwiYmx1ZSIsInNraW4iLCJyZWQiLCJhY3Rpb24iLCJzcG9ydCIsImJsYWNrIiwibWFnaWMiLCJncmVlbiJdfQ==", "encoding": "base64"}, "redirectURL": "", "headersSize": -1, "bodySize": 107}, "cache": {}, "timings": {"send": -1, "wait": -1, "receive": 7}}
|
||||||
|
{"_id": "", "startedDateTime": "2021-07-26T11:14:18.131501482Z", "time": 5, "request": {"method": "GET", "url": "http://catalogue/catalogue/3395a43e-2d88-40de-b95f-e00e1502085b", "httpVersion": "HTTP/1.1", "cookies": [], "headers": [{"name": "Connection", "value": "close"}, {"name": "Host", "value": "catalogue"}, {"name": "x-some", "value": "demoval"}], ",queryString": [], "headersSize": -1, "bodySize": 0}, "response": {"status": 200, "statusText": "OK", "httpVersion": "HTTP/1.1", "cookies": [], "headers": [{"name": "Content-Type", "value": "application/json; charset=utf-8"}, {"name": "Date", "value": "Mon, 26 Jul 2021 11:14:17 GMT"}, {"name": "Content-Length", "value": "286"}], "content": {"size": 286, "mimeType": "application/json; charset=utf-8", "text": "eyJjb3VudCI6NDM4LCJkZXNjcmlwdGlvbiI6InByb2lkZW50IG9jY2FlY2F0IGlydXJlIGV0IGV4Y2VwdGV1ciBsYWJvcmUgbWluaW0gbmlzaSBhbWV0IGlydXJlIiwiaWQiOiIzMzk1YTQzZS0yZDg4LTQwZGUtYjk1Zi1lMDBlMTUwMjA4NWIiLCJpbWFnZVVybCI6WyIvY2F0YWxvZ3VlL2ltYWdlcy9jb2xvdXJmdWxfc29ja3MuanBnIiwiL2NhdGFsb2d1ZS9pbWFnZXMvY29sb3VyZnVsX3NvY2tzLmpwZyJdLCJuYW1lIjoiQ29sb3VyZnVsIiwicHJpY2UiOjE4LCJ0YWciOlsiYnJvd24iLCJibHVlIl19", "encoding": "base64"}, "redirectURL": "", "headersSize": -1, "bodySize": 286}, "cache": {}, "timings": {"send": -1, "wait": -1, "receive": 5}}
|
||||||
|
{"_id": "", "startedDateTime": "2021-07-26T11:14:18.379836908Z", "time": 14, "request": {"method": "GET", "url": "http://carts/carts/mHK0P7zTktmV1zv57iWAvCTd43FFMHap/items", "httpVersion": "HTTP/1.1", "cookies": [], "headers": [{"name": "Connection", "value": "close"}, {"name": "Host", "value": "carts"}], "queryString": [], "headersSize": -1, "bodySize": 0}, "response": {"status": 200, "statusText": "OK", "httpVersion": "HTTP/1.1", "cookies": [], "headers": [{"name": "X-Application-Context", "value": "carts:80"}, {"name": "Content-Type", "value": "application/json;charset=UTF-8"}, {"name": "Date", "value": "Mon, 26 Jul 2021 11:14:17 GMT"}, {"name": "Transfer-Encoding", "value": "chunked"}], "content": {"size": 113, "mimeType": "application/json;charset=UTF-8", "text": "W3siaWQiOiI2MGZlOThmYjg2YzBmYzAwMDg2OWE5MGMiLCJpdGVtSWQiOiIzMzk1YTQzZS0yZDg4LTQwZGUtYjk1Zi1lMDBlMTUwMjA4NWIiLCJxdWFudGl0eSI6MSwidW5pdFByaWNlIjoxOC4wfV0=", "encoding": "base64"}, "redirectURL": "", "headersSize": -1, "bodySize": -1}, "cache": {}, "timings": {"send": -1, "wait": -1, "receive": 14}}
|
||||||
|
{"_id": "", "startedDateTime": "2021-07-26T11:14:22.920540124Z", "time": 3, "request": {"method": "GET", "url": "http://catalogue/catalogue/808a2de1-1aaa-4c25-a9b9-6612e8f29a38", "httpVersion": "HTTP/1.1", "cookies": [], "headers": [{"name": "Connection", "value": "close"}, {"name": "Host", "value": "catalogue"}], "queryString": [], "headersSize": -1, "bodySize": 0}, "response": {"status": 200, "statusText": "OK", "httpVersion": "HTTP/1.1", "cookies": [], "headers": [{"name": "Content-Type", "value": "application/json; charset=utf-8"}, {"name": "Date", "value": "Mon, 26 Jul 2021 11:14:22 GMT"}, {"name": "Content-Length", "value": "275"}], "content": {"size": 275, "mimeType": "application/json; charset=utf-8", "text": "eyJjb3VudCI6NzM4LCJkZXNjcmlwdGlvbiI6IkEgbWF0dXJlIHNvY2ssIGNyb3NzZWQsIHdpdGggYW4gYWlyIG9mIG5vbmNoYWxhbmNlLiIsImlkIjoiODA4YTJkZTEtMWFhYS00YzI1LWE5YjktNjYxMmU4ZjI5YTM4IiwiaW1hZ2VVcmwiOlsiL2NhdGFsb2d1ZS9pbWFnZXMvY3Jvc3NfMS5qcGVnIiwiL2NhdGFsb2d1ZS9pbWFnZXMvY3Jvc3NfMi5qcGVnIl0sIm5hbWUiOiJDcm9zc2VkIiwicHJpY2UiOjE3LjMyLCJ0YWciOlsiYmx1ZSIsInJlZCIsImFjdGlvbiIsImZvcm1hbCJdfQ==", "encoding": "base64"}, "redirectURL": "", "headersSize": -1, "bodySize": 275}, "cache": {}, "timings": {"send": -1, "wait": -1, "receive": 3}}
|
||||||
|
{"_id": "", "startedDateTime": "2021-07-26T11:14:22.921609501Z", "time": 3, "request": {"method": "GET", "url": "http://catalogue/catalogue?sort=id&size=3&tags=blue", "httpVersion": "HTTP/1.1", "cookies": [], "headers": [{"name": "Connection", "value": "close"}, {"name": "Host", "value": "catalogue"}], "queryString": [{"name": "size", "value": "3"}, {"name": "tags", "value": "blue"}, {"name": "sort", "value": "id"}], "headersSize": -1, "bodySize": 0}, "response": {"status": 200, "statusText": "OK", "httpVersion": "HTTP/1.1", "cookies": [], "headers": [{"name": "Content-Length", "value": "789"}, {"name": "Content-Type", "value": "application/json; charset=utf-8"}, {"name": "Date", "value": "Mon, 26 Jul 2021 11:14:22 GMT"}], "content": {"size": 789, "mimeType": "application/json; charset=utf-8", "text": "W3siaWQiOiIzMzk1YTQzZS0yZDg4LTQwZGUtYjk1Zi1lMDBlMTUwMjA4NWIiLCJuYW1lIjoiQ29sb3VyZnVsIiwiZGVzY3JpcHRpb24iOiJwcm9pZGVudCBvY2NhZWNhdCBpcnVyZSBldCBleGNlcHRldXIgbGFib3JlIG1pbmltIG5pc2kgYW1ldCBpcnVyZSIsImltYWdlVXJsIjpbIi9jYXRhbG9ndWUvaW1hZ2VzL2NvbG91cmZ1bF9zb2Nrcy5qcGciLCIvY2F0YWxvZ3VlL2ltYWdlcy9jb2xvdXJmdWxfc29ja3MuanBnIl0sInByaWNlIjoxOCwiY291bnQiOjQzOCwidGFnIjpbImJsdWUiXX0seyJpZCI6IjgwOGEyZGUxLTFhYWEtNGMyNS1hOWI5LTY2MTJlOGYyOWEzOCIsIm5hbWUiOiJDcm9zc2VkIiwiZGVzY3JpcHRpb24iOiJBIG1hdHVyZSBzb2NrLCBjcm9zc2VkLCB3aXRoIGFuIGFpciBvZiBub25jaGFsYW5jZS4iLCJpbWFnZVVybCI6WyIvY2F0YWxvZ3VlL2ltYWdlcy9jcm9zc18xLmpwZWciLCIvY2F0YWxvZ3VlL2ltYWdlcy9jcm9zc18yLmpwZWciXSwicHJpY2UiOjE3LjMyLCJjb3VudCI6NzM4LCJ0YWciOlsiYmx1ZSJdfSx7ImlkIjoiODE5ZTFmYmYtOGI3ZS00ZjZkLTgxMWYtNjkzNTM0OTE2YThiIiwibmFtZSI6IkZpZ3Vlcm9hIiwiZGVzY3JpcHRpb24iOiJlbmltIG9mZmljaWEgYWxpcXVhIGV4Y2VwdGV1ciBlc3NlIGRlc2VydW50IHF1aXMgYWxpcXVpcCBub3N0cnVkIGFuaW0iLCJpbWFnZVVybCI6WyIvY2F0YWxvZ3VlL2ltYWdlcy9XQVQuanBnIiwiL2NhdGFsb2d1ZS9pbWFnZXMvV0FUMi5qcGciXSwicHJpY2UiOjE0LCJjb3VudCI6ODA4LCJ0YWciOlsiYmx1ZSJdfV0K", "encoding": "base64"}, "redirectURL": "", "headersSize": -1, "bodySize": 789}, "cache": {}, "timings": {"send": -1, "wait": -1, "receive": 3}}
|
||||||
|
{"_id": "", "startedDateTime": "2021-07-26T11:14:22.923197848Z", "time": 3, "request": {"method": "GET", "url": "http://catalogue/catalogue/3395a43e-2d88-40de-b95f-e00e1502085b", "httpVersion": "HTTP/1.1", "cookies": [], "headers": [{"name": "Host", "value": "catalogue"}, {"name": "Connection", "value": "close"}], "queryString": [], "headersSize": -1, "bodySize": 0}, "response": {"status": 200, "statusText": "OK", "httpVersion": "HTTP/1.1", "cookies": [], "headers": [{"name": "Content-Type", "value": "application/json; charset=utf-8"}, {"name": "Date", "value": "Mon, 26 Jul 2021 11:14:22 GMT"}, {"name": "Content-Length", "value": "286"}], "content": {"size": 286, "mimeType": "application/json; charset=utf-8", "text": "eyJjb3VudCI6NDM4LCJkZXNjcmlwdGlvbiI6InByb2lkZW50IG9jY2FlY2F0IGlydXJlIGV0IGV4Y2VwdGV1ciBsYWJvcmUgbWluaW0gbmlzaSBhbWV0IGlydXJlIiwiaWQiOiIzMzk1YTQzZS0yZDg4LTQwZGUtYjk1Zi1lMDBlMTUwMjA4NWIiLCJpbWFnZVVybCI6WyIvY2F0YWxvZ3VlL2ltYWdlcy9jb2xvdXJmdWxfc29ja3MuanBnIiwiL2NhdGFsb2d1ZS9pbWFnZXMvY29sb3VyZnVsX3NvY2tzLmpwZyJdLCJuYW1lIjoiQ29sb3VyZnVsIiwicHJpY2UiOjE4LCJ0YWciOlsiYnJvd24iLCJibHVlIl19", "encoding": "base64"}, "redirectURL": "", "headersSize": -1, "bodySize": 286}, "cache": {}, "timings": {"send": -1, "wait": -1, "receive": 3}}
|
||||||
|
{"_id": "", "startedDateTime": "2021-07-26T11:14:23.175549218Z", "time": 26, "request": {"method": "GET", "url": "http://carts/carts/mHK0P7zTktmV1zv57iWAvCTd43FFMHap/items", "httpVersion": "HTTP/1.1", "cookies": [], "headers": [{"name": "Connection", "value": "close"}, {"name": "Host", "value": "carts"}], "queryString": [], "headersSize": -1, "bodySize": 0}, "response": {"status": 200, "statusText": "OK", "httpVersion": "HTTP/1.1", "cookies": [], "headers": [{"name": "X-Application-Context", "value": "carts:80"}, {"name": "Content-Type", "value": "application/json;charset=UTF-8"}, {"name": "Date", "value": "Mon, 26 Jul 2021 11:14:22 GMT"}, {"name": "Transfer-Encoding", "value": "chunked"}], "content": {"size": 113, "mimeType": "application/json;charset=UTF-8", "text": "W3siaWQiOiI2MGZlOThmYjg2YzBmYzAwMDg2OWE5MGMiLCJpdGVtSWQiOiIzMzk1YTQzZS0yZDg4LTQwZGUtYjk1Zi1lMDBlMTUwMjA4NWIiLCJxdWFudGl0eSI6MSwidW5pdFByaWNlIjoxOC4wfV0=", "encoding": "base64"}, "redirectURL": "", "headersSize": -1, "bodySize": -1}, "cache": {}, "timings": {"send": -1, "wait": -1, "receive": 26}}
|
||||||
|
{"_id": "", "startedDateTime": "2021-07-26T11:14:25.239777333Z", "time": 10, "request": {"method": "GET", "url": "http://carts/carts/mHK0P7zTktmV1zv57iWAvCTd43FFMHap/items", "httpVersion": "HTTP/1.1", "cookies": [], "headers": [{"name": "Connection", "value": "close"}, {"name": "Host", "value": "carts"}], "queryString": [], "headersSize": -1, "bodySize": 0}, "response": {"status": 200, "statusText": "OK", "httpVersion": "HTTP/1.1", "cookies": [], "headers": [{"name": "Content-Type", "value": "application/json;charset=UTF-8"}, {"name": "Transfer-Encoding", "value": "chunked"}, {"name": "Date", "value": "Mon, 26 Jul 2021 11:14:25 GMT"}, {"name": "X-Application-Context", "value": "carts:80"}], "content": {"size": 113, "mimeType": "application/json;charset=UTF-8", "text": "W3siaWQiOiI2MGZlOThmYjg2YzBmYzAwMDg2OWE5MGMiLCJpdGVtSWQiOiIzMzk1YTQzZS0yZDg4LTQwZGUtYjk1Zi1lMDBlMTUwMjA4NWIiLCJxdWFudGl0eSI6MSwidW5pdFByaWNlIjoxOC4wfV0=", "encoding": "base64"}, "redirectURL": "", "headersSize": -1, "bodySize": -1}, "cache": {}, "timings": {"send": -1, "wait": -1, "receive": 10}}
|
||||||
|
{"_id": "", "startedDateTime": "2021-07-26T11:14:25.725866772Z", "time": 3, "request": {"method": "GET", "url": "http://catalogue/catalogue?size=5", "httpVersion": "HTTP/1.1", "cookies": [], "headers": [{"name": "Connection", "value": "close"}, {"name": "Host", "value": "catalogue"}], "queryString": [{"name": "size", "value": "5"}], "headersSize": -1, "bodySize": 0}, "response": {"status": 200, "statusText": "OK", "httpVersion": "HTTP/1.1", "cookies": [], "headers": [{"name": "Content-Length", "value": "1643"}, {"name": "Content-Type", "value": "application/json; charset=utf-8"}, {"name": "Date", "value": "Mon, 26 Jul 2021 11:14:25 GMT"}], "content": {"size": 1643, "mimeType": "application/json; charset=utf-8", "text": "W3siaWQiOiIwM2ZlZjZhYy0xODk2LTRjZTgtYmQ2OS1iNzk4Zjg1YzZlMGIiLCJuYW1lIjoiSG9seSIsImRlc2NyaXB0aW9uIjoiU29ja3MgZml0IGZvciBhIE1lc3NpYWguIFlvdSB0b28gY2FuIGV4cGVyaWVuY2Ugd2Fsa2luZyBpbiB3YXRlciB3aXRoIHRoZXNlIHNwZWNpYWwgZWRpdGlvbiBiZWF1dGllcy4gRWFjaCBob2xlIGlzIGxvdmluZ2x5IHByb2dnbGVkIHRvIGxlYXZlIHNtb290aCBlZGdlcy4gVGhlIG9ubHkgc29jayBhcHByb3ZlZCBieSBhIGhpZ2hlciBwb3dlci4iLCJpbWFnZVVybCI6WyIvY2F0YWxvZ3VlL2ltYWdlcy9ob2x5XzEuanBlZyIsIi9jYXRhbG9ndWUvaW1hZ2VzL2hvbHlfMi5qcGVnIl0sInByaWNlIjo5OS45OSwiY291bnQiOjEsInRhZyI6WyJhY3Rpb24iLCJtYWdpYyJdfSx7ImlkIjoiMzM5NWE0M2UtMmQ4OC00MGRlLWI5NWYtZTAwZTE1MDIwODViIiwibmFtZSI6IkNvbG91cmZ1bCIsImRlc2NyaXB0aW9uIjoicHJvaWRlbnQgb2NjYWVjYXQgaXJ1cmUgZXQgZXhjZXB0ZXVyIGxhYm9yZSBtaW5pbSBuaXNpIGFtZXQgaXJ1cmUiLCJpbWFnZVVybCI6WyIvY2F0YWxvZ3VlL2ltYWdlcy9jb2xvdXJmdWxfc29ja3MuanBnIiwiL2NhdGFsb2d1ZS9pbWFnZXMvY29sb3VyZnVsX3NvY2tzLmpwZyJdLCJwcmljZSI6MTgsImNvdW50Ijo0MzgsInRhZyI6WyJicm93biIsImJsdWUiXX0seyJpZCI6IjUxMGEwZDdlLThlODMtNDE5My1iNDgzLWUyN2UwOWRkYzM0ZCIsIm5hbWUiOiJTdXBlclNwb3J0IFhMIiwiZGVzY3JpcHRpb24iOiJSZWFkeSBmb3IgYWN0aW9uLiBFbmdpbmVlcnM6IGJlIHJlYWR5IHRvIHNtYXNoIHRoYXQgbmV4dCBidWchIEJlIHJlYWR5LCB3aXRoIHRoZXNlIHN1cGVyLWFjdGlvbi1zcG9ydC1tYXN0ZXJwaWVjZXMuIFRoaXMgcGFydGljdWxhciBlbmdpbmVlciB3YXMgY2hhc2VkIGF3YXkgZnJvbSB0aGUgb2ZmaWNlIHdpdGggYSBzdGljay4iLCJpbWFnZVVybCI6WyIvY2F0YWxvZ3VlL2ltYWdlcy9wdW1hXzEuanBlZyIsIi9jYXRhbG9ndWUvaW1hZ2VzL3B1bWFfMi5qcGVnIl0sInByaWNlIjoxNSwiY291bnQiOjgyMCwidGFnIjpbInNwb3J0IiwiZm9ybWFsIiwiYmxhY2siXX0seyJpZCI6IjgwOGEyZGUxLTFhYWEtNGMyNS1hOWI5LTY2MTJlOGYyOWEzOCIsIm5hbWUiOiJDcm9zc2VkIiwiZGVzY3JpcHRpb24iOiJBIG1hdHVyZSBzb2NrLCBjcm9zc2VkLCB3aXRoIGFuIGFpciBvZiBub25jaGFsYW5jZS4iLCJpbWFnZVVybCI6WyIvY2F0YWxvZ3VlL2ltYWdlcy9jcm9zc18xLmpwZWciLCIvY2F0YWxvZ3VlL2ltYWdlcy9jcm9zc18yLmpwZWciXSwicHJpY2UiOjE3LjMyLCJjb3VudCI6NzM4LCJ0YWciOlsiYmx1ZSIsImFjdGlvbiIsInJlZCIsImZvcm1hbCJdfSx7ImlkIjoiODE5ZTFmYmYtOGI3ZS00ZjZkLTgxMWYtNjkzNTM0OTE2YThiIiwibmFtZSI6IkZpZ3Vlcm9hIiwiZGVzY3JpcHRpb24iOiJlbmltIG9mZmljaWEgYWxpcXVhIGV4Y2VwdGV1ciBlc3NlIGRlc2VydW50IHF1aXMgYWxpcXVpcCBub3N0cnVkIGFuaW0iLCJpbWFnZVVybCI6WyIvY2F0YWxvZ3VlL2ltYWdlcy9XQVQuanBnIiwiL2NhdGFsb2d1ZS9pbWFnZXMvV0FUMi5qcGciXSwicHJpY2UiOjE0LCJjb3VudCI6ODA4LCJ0YWciOlsiZ3JlZW4iLCJmb3JtYWwiLCJibHVlIl19XQo=", "encoding": "base64"}, "redirectURL": "", "headersSize": -1, "bodySize": 1643}, "cache": {}, "timings": {"send": -1, "wait": -1, "receive": 3}}
|
||||||
|
{"_id": "", "startedDateTime": "2021-07-26T11:14:25.729303217Z", "time": 3, "request": {"method": "GET", "url": "http://catalogue/catalogue/3395a43e-2d88-40de-b95f-e00e1502085b", "httpVersion": "HTTP/1.1", "cookies": [], "headers": [{"name": "Connection", "value": "close"}, {"name": "Host", "value": "catalogue"}], "queryString": [], "headersSize": -1, "bodySize": 0}, "response": {"status": 200, "statusText": "OK", "httpVersion": "HTTP/1.1", "cookies": [], "headers": [{"name": "Content-Type", "value": "application/json; charset=utf-8"}, {"name": "Date", "value": "Mon, 26 Jul 2021 11:14:25 GMT"}, {"name": "Content-Length", "value": "286"}], "content": {"size": 286, "mimeType": "application/json; charset=utf-8", "text": "eyJjb3VudCI6NDM4LCJkZXNjcmlwdGlvbiI6InByb2lkZW50IG9jY2FlY2F0IGlydXJlIGV0IGV4Y2VwdGV1ciBsYWJvcmUgbWluaW0gbmlzaSBhbWV0IGlydXJlIiwiaWQiOiIzMzk1YTQzZS0yZDg4LTQwZGUtYjk1Zi1lMDBlMTUwMjA4NWIiLCJpbWFnZVVybCI6WyIvY2F0YWxvZ3VlL2ltYWdlcy9jb2xvdXJmdWxfc29ja3MuanBnIiwiL2NhdGFsb2d1ZS9pbWFnZXMvY29sb3VyZnVsX3NvY2tzLmpwZyJdLCJuYW1lIjoiQ29sb3VyZnVsIiwicHJpY2UiOjE4LCJ0YWciOlsiYnJvd24iLCJibHVlIl19", "encoding": "base64"}, "redirectURL": "", "headersSize": -1, "bodySize": 286}, "cache": {}, "timings": {"send": -1, "wait": -1, "receive": 3}}
|
||||||
544
agent/pkg/oas/test_artifacts/params.har
Normal file
544
agent/pkg/oas/test_artifacts/params.har
Normal file
@@ -0,0 +1,544 @@
|
|||||||
|
{
|
||||||
|
"log": {
|
||||||
|
"version": "1.2",
|
||||||
|
"creator": {
|
||||||
|
"name": "mitmproxy har_dump",
|
||||||
|
"version": "0.1",
|
||||||
|
"comment": "mitmproxy version mitmproxy 4.0.4"
|
||||||
|
},
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"startedDateTime": "2019-09-06T06:14:43.864529+00:00",
|
||||||
|
"time": 111,
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"url": "https://httpbin.org/e21f7112-3d3b-4632-9da3-a4af2e0e9166/sub1",
|
||||||
|
"httpVersion": "HTTP/1.1",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [],
|
||||||
|
"headersSize": 1542,
|
||||||
|
"bodySize": 0,
|
||||||
|
"queryString": []
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"status": 200,
|
||||||
|
"statusText": "OK",
|
||||||
|
"httpVersion": "HTTP/1.1",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [
|
||||||
|
],
|
||||||
|
"content": {
|
||||||
|
"mimeType": "text/html",
|
||||||
|
"text": "",
|
||||||
|
"size": 0
|
||||||
|
},
|
||||||
|
"redirectURL": "",
|
||||||
|
"headersSize": 245,
|
||||||
|
"bodySize": 39
|
||||||
|
},
|
||||||
|
"cache": {},
|
||||||
|
"timings": {
|
||||||
|
"send": 22,
|
||||||
|
"receive": 2,
|
||||||
|
"wait": 87,
|
||||||
|
"connect": -1,
|
||||||
|
"ssl": -1
|
||||||
|
},
|
||||||
|
"serverIPAddress": "54.210.29.33"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startedDateTime": "2019-09-06T06:16:18.747122+00:00",
|
||||||
|
"time": 630,
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"url": "https://httpbin.org/952bea17-3776-11ea-9341-42010a84012a/sub2",
|
||||||
|
"httpVersion": "HTTP/1.1",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [],
|
||||||
|
"queryString": [],
|
||||||
|
"headersSize": 1542,
|
||||||
|
"bodySize": 0
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"status": 200,
|
||||||
|
"statusText": "OK",
|
||||||
|
"httpVersion": "HTTP/1.1",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [],
|
||||||
|
"content": {
|
||||||
|
"size": 39,
|
||||||
|
"compression": -20,
|
||||||
|
"mimeType": "application/json",
|
||||||
|
"text": "null"
|
||||||
|
},
|
||||||
|
"redirectURL": "",
|
||||||
|
"headersSize": 248,
|
||||||
|
"bodySize": 39
|
||||||
|
},
|
||||||
|
"cache": {},
|
||||||
|
"timings": {
|
||||||
|
"send": 14,
|
||||||
|
"receive": 4,
|
||||||
|
"wait": 350,
|
||||||
|
"connect": 262,
|
||||||
|
"ssl": -1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startedDateTime": "2019-09-06T06:16:19.747122+00:00",
|
||||||
|
"time": 630,
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"url": "https://httpbin.org/952bea17-3776-11ea-9341-42010a84012a;mparam=matrixparam",
|
||||||
|
"httpVersion": "HTTP/1.1",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [],
|
||||||
|
"queryString": [],
|
||||||
|
"headersSize": 1542,
|
||||||
|
"bodySize": 0
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"status": 200,
|
||||||
|
"statusText": "OK",
|
||||||
|
"httpVersion": "HTTP/1.1",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [],
|
||||||
|
"content": {
|
||||||
|
"size": 39,
|
||||||
|
"compression": -20,
|
||||||
|
"mimeType": "application/json",
|
||||||
|
"text": "null"
|
||||||
|
},
|
||||||
|
"redirectURL": "",
|
||||||
|
"headersSize": 248,
|
||||||
|
"bodySize": 39
|
||||||
|
},
|
||||||
|
"cache": {},
|
||||||
|
"timings": {
|
||||||
|
"send": 14,
|
||||||
|
"receive": 4,
|
||||||
|
"wait": 350,
|
||||||
|
"connect": 262,
|
||||||
|
"ssl": -1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startedDateTime": "2019-09-06T06:16:20.047122+00:00",
|
||||||
|
"time": 630,
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"url": "https://httpbin.org/appears-once",
|
||||||
|
"httpVersion": "HTTP/1.1",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [],
|
||||||
|
"queryString": [],
|
||||||
|
"headersSize": 1542,
|
||||||
|
"bodySize": 0
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"status": 200,
|
||||||
|
"statusText": "OK",
|
||||||
|
"httpVersion": "HTTP/1.1",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [],
|
||||||
|
"content": {
|
||||||
|
"size": 39,
|
||||||
|
"compression": -20,
|
||||||
|
"mimeType": "application/json",
|
||||||
|
"text": "null"
|
||||||
|
},
|
||||||
|
"redirectURL": "",
|
||||||
|
"headersSize": 248,
|
||||||
|
"bodySize": 39
|
||||||
|
},
|
||||||
|
"cache": {},
|
||||||
|
"timings": {
|
||||||
|
"send": 14,
|
||||||
|
"receive": 4,
|
||||||
|
"wait": 350,
|
||||||
|
"connect": 262,
|
||||||
|
"ssl": -1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startedDateTime": "2019-09-06T06:16:20.747122+00:00",
|
||||||
|
"time": 630,
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"url": "https://httpbin.org/appears-twice",
|
||||||
|
"httpVersion": "HTTP/1.1",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [],
|
||||||
|
"queryString": [],
|
||||||
|
"headersSize": 1542,
|
||||||
|
"bodySize": 0
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"status": 200,
|
||||||
|
"statusText": "OK",
|
||||||
|
"httpVersion": "HTTP/1.1",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [],
|
||||||
|
"content": {
|
||||||
|
"size": 39,
|
||||||
|
"compression": -20,
|
||||||
|
"mimeType": "application/json",
|
||||||
|
"text": "null"
|
||||||
|
},
|
||||||
|
"redirectURL": "",
|
||||||
|
"headersSize": 248,
|
||||||
|
"bodySize": 39
|
||||||
|
},
|
||||||
|
"cache": {},
|
||||||
|
"timings": {
|
||||||
|
"send": 14,
|
||||||
|
"receive": 4,
|
||||||
|
"wait": 350,
|
||||||
|
"connect": 262,
|
||||||
|
"ssl": -1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startedDateTime": "2019-09-06T06:16:21.747122+00:00",
|
||||||
|
"time": 630,
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"url": "https://httpbin.org/appears-twice",
|
||||||
|
"httpVersion": "HTTP/1.1",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [],
|
||||||
|
"queryString": [],
|
||||||
|
"headersSize": 1542,
|
||||||
|
"bodySize": 0
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"status": 200,
|
||||||
|
"statusText": "OK",
|
||||||
|
"httpVersion": "HTTP/1.1",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [],
|
||||||
|
"content": {
|
||||||
|
"size": 39,
|
||||||
|
"compression": -20,
|
||||||
|
"mimeType": "application/json",
|
||||||
|
"text": "null"
|
||||||
|
},
|
||||||
|
"redirectURL": "",
|
||||||
|
"headersSize": 248,
|
||||||
|
"bodySize": 39
|
||||||
|
},
|
||||||
|
"cache": {},
|
||||||
|
"timings": {
|
||||||
|
"send": 14,
|
||||||
|
"receive": 4,
|
||||||
|
"wait": 350,
|
||||||
|
"connect": 262,
|
||||||
|
"ssl": -1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startedDateTime": "2019-09-06T06:16:20.747122+00:00",
|
||||||
|
"time": 1,
|
||||||
|
"request": {
|
||||||
|
"method": "POST",
|
||||||
|
"url": "https://httpbin.org/form-urlencoded",
|
||||||
|
"httpVersion": "",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [
|
||||||
|
{
|
||||||
|
"name": "Content-Type",
|
||||||
|
"value": "application/x-www-form-urlencoded"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"queryString": [],
|
||||||
|
"headersSize": -1,
|
||||||
|
"bodySize": -1,
|
||||||
|
"postData": {
|
||||||
|
"mimeType": "",
|
||||||
|
"text": "agent-id=ade&callback-url=&token=sometoken"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"status": 200,
|
||||||
|
"statusText": "OK",
|
||||||
|
"httpVersion": "",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [
|
||||||
|
],
|
||||||
|
"content": {
|
||||||
|
"size": 0,
|
||||||
|
"mimeType": "",
|
||||||
|
"text": ""
|
||||||
|
},
|
||||||
|
"redirectURL": "",
|
||||||
|
"headersSize": -1,
|
||||||
|
"bodySize": 0
|
||||||
|
},
|
||||||
|
"cache": {},
|
||||||
|
"timings": {
|
||||||
|
"send": -1,
|
||||||
|
"wait": -1,
|
||||||
|
"receive": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startedDateTime": "2019-09-06T06:16:21.747122+00:00",
|
||||||
|
"time": 1,
|
||||||
|
"request": {
|
||||||
|
"method": "POST",
|
||||||
|
"url": "https://httpbin.org/form-urlencoded",
|
||||||
|
"httpVersion": "",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [
|
||||||
|
{
|
||||||
|
"name": "Content-Type",
|
||||||
|
"value": "application/x-www-form-urlencoded"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"queryString": [],
|
||||||
|
"headersSize": -1,
|
||||||
|
"bodySize": -1,
|
||||||
|
"postData": {
|
||||||
|
"mimeType": "",
|
||||||
|
"text": "agent-id=ade&callback-url=&token=sometoken-second-val&optional=another"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"status": 200,
|
||||||
|
"statusText": "OK",
|
||||||
|
"httpVersion": "",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [
|
||||||
|
],
|
||||||
|
"content": {
|
||||||
|
"size": 0,
|
||||||
|
"mimeType": "",
|
||||||
|
"text": ""
|
||||||
|
},
|
||||||
|
"redirectURL": "",
|
||||||
|
"headersSize": -1,
|
||||||
|
"bodySize": 0
|
||||||
|
},
|
||||||
|
"cache": {},
|
||||||
|
"timings": {
|
||||||
|
"send": -1,
|
||||||
|
"wait": -1,
|
||||||
|
"receive": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startedDateTime": "2019-09-06T06:16:22.747122+00:00",
|
||||||
|
"time": 1,
|
||||||
|
"request": {
|
||||||
|
"method": "POST",
|
||||||
|
"url": "https://httpbin.org/form-multipart",
|
||||||
|
"httpVersion": "",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [
|
||||||
|
{
|
||||||
|
"name": "Content-Type",
|
||||||
|
"value": "multipart/form-data; boundary=BOUNDARY"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"queryString": [],
|
||||||
|
"headersSize": -1,
|
||||||
|
"bodySize": -1,
|
||||||
|
"postData": {
|
||||||
|
"mimeType": "",
|
||||||
|
"text": "--BOUNDARY\r\nContent-Disposition: form-data; name=\"file\"; filename=\"metadata.json\"\r\nContent-Type: application/json\r\n\r\n{\"functions\": 123}\r\n--BOUNDARY\r\nContent-Disposition: form-data; name=\"path\"\r\n\r\n/content/components\r\n--BOUNDARY--\r\n"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"status": 200,
|
||||||
|
"statusText": "OK",
|
||||||
|
"httpVersion": "",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [
|
||||||
|
],
|
||||||
|
"content": {
|
||||||
|
"size": 62,
|
||||||
|
"mimeType": "",
|
||||||
|
"text": "{}"
|
||||||
|
},
|
||||||
|
"redirectURL": "",
|
||||||
|
"headersSize": -1,
|
||||||
|
"bodySize": 62
|
||||||
|
},
|
||||||
|
"cache": {},
|
||||||
|
"timings": {
|
||||||
|
"send": -1,
|
||||||
|
"wait": -1,
|
||||||
|
"receive": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startedDateTime": "2019-09-06T06:16:21.757122+00:00",
|
||||||
|
"time": 1,
|
||||||
|
"request": {
|
||||||
|
"method": "POST",
|
||||||
|
"url": "https://httpbin.org/body-optional",
|
||||||
|
"httpVersion": "",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [
|
||||||
|
],
|
||||||
|
"queryString": [],
|
||||||
|
"headersSize": -1,
|
||||||
|
"bodySize": -1,
|
||||||
|
"postData": {
|
||||||
|
"mimeType": "",
|
||||||
|
"text": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"status": 200,
|
||||||
|
"statusText": "OK",
|
||||||
|
"httpVersion": "",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [
|
||||||
|
],
|
||||||
|
"content": {
|
||||||
|
"size": 0,
|
||||||
|
"mimeType": "",
|
||||||
|
"text": ""
|
||||||
|
},
|
||||||
|
"redirectURL": "",
|
||||||
|
"headersSize": -1,
|
||||||
|
"bodySize": 0
|
||||||
|
},
|
||||||
|
"cache": {},
|
||||||
|
"timings": {
|
||||||
|
"send": -1,
|
||||||
|
"wait": -1,
|
||||||
|
"receive": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startedDateTime": "2019-09-06T06:16:21.747122+00:00",
|
||||||
|
"time": 1,
|
||||||
|
"request": {
|
||||||
|
"method": "POST",
|
||||||
|
"url": "https://httpbin.org/body-optional",
|
||||||
|
"httpVersion": "",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [
|
||||||
|
{
|
||||||
|
"name": "Content-Type",
|
||||||
|
"value": "application/json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"queryString": [],
|
||||||
|
"headersSize": -1,
|
||||||
|
"bodySize": -1,
|
||||||
|
"postData": {
|
||||||
|
"mimeType": "",
|
||||||
|
"text": "{\"key\", \"val\"}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"status": 200,
|
||||||
|
"statusText": "OK",
|
||||||
|
"httpVersion": "",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [
|
||||||
|
],
|
||||||
|
"content": {
|
||||||
|
"size": 0,
|
||||||
|
"mimeType": "",
|
||||||
|
"text": ""
|
||||||
|
},
|
||||||
|
"redirectURL": "",
|
||||||
|
"headersSize": -1,
|
||||||
|
"bodySize": 0
|
||||||
|
},
|
||||||
|
"cache": {},
|
||||||
|
"timings": {
|
||||||
|
"send": -1,
|
||||||
|
"wait": -1,
|
||||||
|
"receive": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startedDateTime": "2019-09-06T06:16:21.757122+00:00",
|
||||||
|
"time": 1,
|
||||||
|
"request": {
|
||||||
|
"method": "POST",
|
||||||
|
"url": "https://httpbin.org/body-optional",
|
||||||
|
"httpVersion": "",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [
|
||||||
|
],
|
||||||
|
"queryString": [],
|
||||||
|
"headersSize": -1,
|
||||||
|
"bodySize": -1,
|
||||||
|
"postData": {
|
||||||
|
"mimeType": "",
|
||||||
|
"text": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"status": 200,
|
||||||
|
"statusText": "OK",
|
||||||
|
"httpVersion": "",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [
|
||||||
|
],
|
||||||
|
"content": {
|
||||||
|
"size": 0,
|
||||||
|
"mimeType": "",
|
||||||
|
"text": ""
|
||||||
|
},
|
||||||
|
"redirectURL": "",
|
||||||
|
"headersSize": -1,
|
||||||
|
"bodySize": 0
|
||||||
|
},
|
||||||
|
"cache": {},
|
||||||
|
"timings": {
|
||||||
|
"send": -1,
|
||||||
|
"wait": -1,
|
||||||
|
"receive": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startedDateTime": "2019-09-06T06:16:21.757122+00:00",
|
||||||
|
"time": 1,
|
||||||
|
"request": {
|
||||||
|
"method": "POST",
|
||||||
|
"url": "https://httpbin.org/body-required",
|
||||||
|
"httpVersion": "",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [
|
||||||
|
],
|
||||||
|
"queryString": [],
|
||||||
|
"headersSize": -1,
|
||||||
|
"bodySize": -1,
|
||||||
|
"postData": {
|
||||||
|
"mimeType": "",
|
||||||
|
"text": "body exists"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"status": 200,
|
||||||
|
"statusText": "OK",
|
||||||
|
"httpVersion": "",
|
||||||
|
"cookies": [],
|
||||||
|
"headers": [
|
||||||
|
],
|
||||||
|
"content": {
|
||||||
|
"size": 0,
|
||||||
|
"mimeType": "",
|
||||||
|
"text": ""
|
||||||
|
},
|
||||||
|
"redirectURL": "",
|
||||||
|
"headersSize": -1,
|
||||||
|
"bodySize": 0
|
||||||
|
},
|
||||||
|
"cache": {},
|
||||||
|
"timings": {
|
||||||
|
"send": -1,
|
||||||
|
"wait": -1,
|
||||||
|
"receive": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
507
agent/pkg/oas/test_artifacts/params.har.spec.json
Normal file
507
agent/pkg/oas/test_artifacts/params.har.spec.json
Normal file
@@ -0,0 +1,507 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.1.0",
|
||||||
|
"info": {
|
||||||
|
"title": "https://httpbin.org",
|
||||||
|
"description": "Mizu observed 13 entries (0 failed), at 0.155 hits/s, average response time is 0.251 seconds",
|
||||||
|
"version": "1.0"
|
||||||
|
},
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"url": "https://httpbin.org"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"/appears-once": {
|
||||||
|
"get": {
|
||||||
|
"summary": "/appears-once",
|
||||||
|
"description": "Mizu observed 1 entries (0 failed), at 0.000 hits/s, average response time is 0.630 seconds",
|
||||||
|
"operationId": "89aa39f6-78d0-411b-b701-a33bd77868b0",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful call with status 200",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"example": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-last-seen-ts": 1567750580.0471218,
|
||||||
|
"x-counters-total": {
|
||||||
|
"entries": 1,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750580.0471218,
|
||||||
|
"lastSeen": 1567750580.0471218,
|
||||||
|
"sumRT": 0.63,
|
||||||
|
"sumDuration": 0
|
||||||
|
},
|
||||||
|
"x-counters-per-source": {
|
||||||
|
"": {
|
||||||
|
"entries": 1,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750580.0471218,
|
||||||
|
"lastSeen": 1567750580.0471218,
|
||||||
|
"sumRT": 0.63,
|
||||||
|
"sumDuration": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-sample-entry": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/appears-twice": {
|
||||||
|
"get": {
|
||||||
|
"summary": "/appears-twice",
|
||||||
|
"description": "Mizu observed 2 entries (0 failed), at 0.500 hits/s, average response time is 0.630 seconds",
|
||||||
|
"operationId": "f5e2b5a2-e01a-45f4-bde1-15a7e7a06d3c",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful call with status 200",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"example": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-counters-total": {
|
||||||
|
"entries": 2,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750580.7471218,
|
||||||
|
"lastSeen": 1567750581.7471218,
|
||||||
|
"sumRT": 1.26,
|
||||||
|
"sumDuration": 1
|
||||||
|
},
|
||||||
|
"x-counters-per-source": {
|
||||||
|
"": {
|
||||||
|
"entries": 2,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750580.7471218,
|
||||||
|
"lastSeen": 1567750581.7471218,
|
||||||
|
"sumRT": 1.26,
|
||||||
|
"sumDuration": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-sample-entry": 0,
|
||||||
|
"x-last-seen-ts": 1567750581.7471218
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/body-optional": {
|
||||||
|
"post": {
|
||||||
|
"summary": "/body-optional",
|
||||||
|
"description": "Mizu observed 3 entries (0 failed), at 0.003 hits/s, average response time is 0.001 seconds",
|
||||||
|
"operationId": "14d5b1c2-dc03-4ee5-baaa-5c7992acc82e",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful call with status 200",
|
||||||
|
"content": {
|
||||||
|
"": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-counters-total": {
|
||||||
|
"entries": 3,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750581.7471218,
|
||||||
|
"lastSeen": 1567750581.757122,
|
||||||
|
"sumRT": 0.003,
|
||||||
|
"sumDuration": 0.010000228881835938
|
||||||
|
},
|
||||||
|
"x-sample-entry": 0,
|
||||||
|
"x-counters-per-source": {
|
||||||
|
"": {
|
||||||
|
"entries": 3,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750581.7471218,
|
||||||
|
"lastSeen": 1567750581.757122,
|
||||||
|
"sumRT": 0.003,
|
||||||
|
"sumDuration": 0.010000228881835938
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-last-seen-ts": 1567750581.757122,
|
||||||
|
"requestBody": {
|
||||||
|
"description": "Generic request body",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"example": "{\"key\", \"val\"}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/body-required": {
|
||||||
|
"post": {
|
||||||
|
"summary": "/body-required",
|
||||||
|
"description": "Mizu observed 1 entries (0 failed), at 0.000 hits/s, average response time is 0.001 seconds",
|
||||||
|
"operationId": "d0958c5a-dce6-4616-99f4-201dbc51457a",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful call with status 200",
|
||||||
|
"content": {
|
||||||
|
"": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-counters-per-source": {
|
||||||
|
"": {
|
||||||
|
"entries": 1,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750581.757122,
|
||||||
|
"lastSeen": 1567750581.757122,
|
||||||
|
"sumRT": 0.001,
|
||||||
|
"sumDuration": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-sample-entry": 0,
|
||||||
|
"x-counters-total": {
|
||||||
|
"entries": 1,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750581.757122,
|
||||||
|
"lastSeen": 1567750581.757122,
|
||||||
|
"sumRT": 0.001,
|
||||||
|
"sumDuration": 0
|
||||||
|
},
|
||||||
|
"x-last-seen-ts": 1567750581.757122,
|
||||||
|
"requestBody": {
|
||||||
|
"description": "Generic request body",
|
||||||
|
"content": {
|
||||||
|
"": {
|
||||||
|
"example": "body exists"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/form-multipart": {
|
||||||
|
"post": {
|
||||||
|
"summary": "/form-multipart",
|
||||||
|
"description": "Mizu observed 1 entries (0 failed), at 0.000 hits/s, average response time is 0.001 seconds",
|
||||||
|
"operationId": "cab5a2f3-c18a-4d5a-8f92-e40da4fd6603",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful call with status 200",
|
||||||
|
"content": {
|
||||||
|
"": {
|
||||||
|
"example": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-sample-entry": 0,
|
||||||
|
"x-counters-per-source": {
|
||||||
|
"": {
|
||||||
|
"entries": 1,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750582.7471218,
|
||||||
|
"lastSeen": 1567750582.7471218,
|
||||||
|
"sumRT": 0.001,
|
||||||
|
"sumDuration": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-last-seen-ts": 1567750582.7471218,
|
||||||
|
"x-counters-total": {
|
||||||
|
"entries": 1,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750582.7471218,
|
||||||
|
"lastSeen": 1567750582.7471218,
|
||||||
|
"sumRT": 0.001,
|
||||||
|
"sumDuration": 0
|
||||||
|
},
|
||||||
|
"requestBody": {
|
||||||
|
"description": "Generic request body",
|
||||||
|
"content": {
|
||||||
|
"multipart/form-data": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"file",
|
||||||
|
"path"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"file": {
|
||||||
|
"type": "string",
|
||||||
|
"contentMediaType": "application/json",
|
||||||
|
"examples": [
|
||||||
|
"{\"functions\": 123}"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"path": {
|
||||||
|
"type": "string",
|
||||||
|
"examples": [
|
||||||
|
"/content/components"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"example": "--BOUNDARY\r\nContent-Disposition: form-data; name=\"file\"; filename=\"metadata.json\"\r\nContent-Type: application/json\r\n\r\n{\"functions\": 123}\r\n--BOUNDARY\r\nContent-Disposition: form-data; name=\"path\"\r\n\r\n/content/components\r\n--BOUNDARY--\r\n"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/form-urlencoded": {
|
||||||
|
"post": {
|
||||||
|
"summary": "/form-urlencoded",
|
||||||
|
"description": "Mizu observed 2 entries (0 failed), at 0.500 hits/s, average response time is 0.001 seconds",
|
||||||
|
"operationId": "7c373ad7-6ab5-422e-971b-1cf56b18a7a2",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful call with status 200",
|
||||||
|
"content": {
|
||||||
|
"": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-last-seen-ts": 1567750581.7471218,
|
||||||
|
"x-counters-total": {
|
||||||
|
"entries": 2,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750580.7471218,
|
||||||
|
"lastSeen": 1567750581.7471218,
|
||||||
|
"sumRT": 0.002,
|
||||||
|
"sumDuration": 1
|
||||||
|
},
|
||||||
|
"x-counters-per-source": {
|
||||||
|
"": {
|
||||||
|
"entries": 2,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750580.7471218,
|
||||||
|
"lastSeen": 1567750581.7471218,
|
||||||
|
"sumRT": 0.002,
|
||||||
|
"sumDuration": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-sample-entry": 0,
|
||||||
|
"requestBody": {
|
||||||
|
"description": "Generic request body",
|
||||||
|
"content": {
|
||||||
|
"application/x-www-form-urlencoded": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"agent-id",
|
||||||
|
"callback-url",
|
||||||
|
"token"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"agent-id": {
|
||||||
|
"type": "string",
|
||||||
|
"examples": [
|
||||||
|
"ade"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"callback-url": {
|
||||||
|
"type": "string",
|
||||||
|
"examples": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"optional": {
|
||||||
|
"type": "string",
|
||||||
|
"examples": [
|
||||||
|
"another"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"token": {
|
||||||
|
"type": "string",
|
||||||
|
"examples": [
|
||||||
|
"sometoken",
|
||||||
|
"sometoken-second-val"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"example": "agent-id=ade\u0026callback-url=\u0026token=sometoken"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/{Id}": {
|
||||||
|
"get": {
|
||||||
|
"summary": "/{Id}",
|
||||||
|
"description": "Mizu observed 1 entries (0 failed), at 0.000 hits/s, average response time is 0.630 seconds",
|
||||||
|
"operationId": "99f1d11f-29c0-48f9-8bf0-9f4b407c7c3f",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful call with status 200",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"example": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-sample-entry": 0,
|
||||||
|
"x-last-seen-ts": 1567750579.7471218,
|
||||||
|
"x-counters-total": {
|
||||||
|
"entries": 1,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750579.7471218,
|
||||||
|
"lastSeen": 1567750579.7471218,
|
||||||
|
"sumRT": 0.63,
|
||||||
|
"sumDuration": 0
|
||||||
|
},
|
||||||
|
"x-counters-per-source": {
|
||||||
|
"": {
|
||||||
|
"entries": 1,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750579.7471218,
|
||||||
|
"lastSeen": 1567750579.7471218,
|
||||||
|
"sumRT": 0.63,
|
||||||
|
"sumDuration": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "Id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"style": "simple",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"examples": {
|
||||||
|
"example #0": {
|
||||||
|
"value": "e21f7112-3d3b-4632-9da3-a4af2e0e9166"
|
||||||
|
},
|
||||||
|
"example #1": {
|
||||||
|
"value": "952bea17-3776-11ea-9341-42010a84012a"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"/{Id}/sub1": {
|
||||||
|
"get": {
|
||||||
|
"summary": "/{Id}/sub1",
|
||||||
|
"description": "Mizu observed 1 entries (0 failed), at 0.000 hits/s, average response time is 0.111 seconds",
|
||||||
|
"operationId": "f7e299d2-253c-4eef-975c-9a5659a7fc50",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful call with status 200",
|
||||||
|
"content": {
|
||||||
|
"text/html": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-last-seen-ts": 1567750483.864529,
|
||||||
|
"x-counters-total": {
|
||||||
|
"entries": 1,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750483.864529,
|
||||||
|
"lastSeen": 1567750483.864529,
|
||||||
|
"sumRT": 0.111,
|
||||||
|
"sumDuration": 0
|
||||||
|
},
|
||||||
|
"x-counters-per-source": {
|
||||||
|
"": {
|
||||||
|
"entries": 1,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750483.864529,
|
||||||
|
"lastSeen": 1567750483.864529,
|
||||||
|
"sumRT": 0.111,
|
||||||
|
"sumDuration": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-sample-entry": 0
|
||||||
|
},
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "Id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"style": "simple",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"examples": {
|
||||||
|
"example #0": {
|
||||||
|
"value": "e21f7112-3d3b-4632-9da3-a4af2e0e9166"
|
||||||
|
},
|
||||||
|
"example #1": {
|
||||||
|
"value": "952bea17-3776-11ea-9341-42010a84012a"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"/{Id}/sub2": {
|
||||||
|
"get": {
|
||||||
|
"summary": "/{Id}/sub2",
|
||||||
|
"description": "Mizu observed 1 entries (0 failed), at 0.000 hits/s, average response time is 0.630 seconds",
|
||||||
|
"operationId": "23a54e06-4298-4ea5-b1f0-09b0354a0598",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful call with status 200",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"example": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-last-seen-ts": 1567750578.7471218,
|
||||||
|
"x-counters-total": {
|
||||||
|
"entries": 1,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750578.7471218,
|
||||||
|
"lastSeen": 1567750578.7471218,
|
||||||
|
"sumRT": 0.63,
|
||||||
|
"sumDuration": 0
|
||||||
|
},
|
||||||
|
"x-counters-per-source": {
|
||||||
|
"": {
|
||||||
|
"entries": 1,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750578.7471218,
|
||||||
|
"lastSeen": 1567750578.7471218,
|
||||||
|
"sumRT": 0.63,
|
||||||
|
"sumDuration": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-sample-entry": 0
|
||||||
|
},
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "Id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"style": "simple",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"examples": {
|
||||||
|
"example #0": {
|
||||||
|
"value": "e21f7112-3d3b-4632-9da3-a4af2e0e9166"
|
||||||
|
},
|
||||||
|
"example #1": {
|
||||||
|
"value": "952bea17-3776-11ea-9341-42010a84012a"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-counters-total": {
|
||||||
|
"entries": 13,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750483.864529,
|
||||||
|
"lastSeen": 1567750582.7471218,
|
||||||
|
"sumRT": 3.268,
|
||||||
|
"sumDuration": 2.010000228881836
|
||||||
|
},
|
||||||
|
"x-counters-per-source": {
|
||||||
|
"": {
|
||||||
|
"entries": 13,
|
||||||
|
"failures": 0,
|
||||||
|
"firstSeen": 1567750483.864529,
|
||||||
|
"lastSeen": 1567750582.7471218,
|
||||||
|
"sumRT": 3.268,
|
||||||
|
"sumDuration": 2.010000228881836
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
245
agent/pkg/oas/tree.go
Normal file
245
agent/pkg/oas/tree.go
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
package oas
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/chanced/openapi"
|
||||||
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NodePath = []string
|
||||||
|
|
||||||
|
type Node struct {
|
||||||
|
constant *string
|
||||||
|
pathParam *openapi.ParameterObj
|
||||||
|
pathObj *openapi.PathObj
|
||||||
|
parent *Node
|
||||||
|
children []*Node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Node) getOrSet(path NodePath, existingPathObj *openapi.PathObj) (node *Node) {
|
||||||
|
if existingPathObj == nil {
|
||||||
|
panic("Invalid function call")
|
||||||
|
}
|
||||||
|
|
||||||
|
pathChunk := path[0]
|
||||||
|
potentialMatrix := strings.SplitN(pathChunk, ";", 2)
|
||||||
|
if len(potentialMatrix) > 1 {
|
||||||
|
pathChunk = potentialMatrix[0]
|
||||||
|
logger.Log.Warningf("URI matrix params are not supported: %s", potentialMatrix[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
chunkIsParam := strings.HasPrefix(pathChunk, "{") && strings.HasSuffix(pathChunk, "}")
|
||||||
|
pathChunk, err := url.PathUnescape(pathChunk)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Warningf("URI segment is not correctly encoded: %s", pathChunk)
|
||||||
|
// any side effects on continuing?
|
||||||
|
}
|
||||||
|
|
||||||
|
chunkIsGibberish := IsGibberish(pathChunk) && !IsVersionString(pathChunk)
|
||||||
|
|
||||||
|
var paramObj *openapi.ParameterObj
|
||||||
|
if chunkIsParam && existingPathObj != nil && existingPathObj.Parameters != nil {
|
||||||
|
_, paramObj = findParamByName(existingPathObj.Parameters, openapi.InPath, pathChunk[1:len(pathChunk)-1])
|
||||||
|
}
|
||||||
|
|
||||||
|
if paramObj == nil {
|
||||||
|
node = n.searchInConstants(pathChunk)
|
||||||
|
}
|
||||||
|
|
||||||
|
if node == nil {
|
||||||
|
node = n.searchInParams(paramObj, chunkIsGibberish)
|
||||||
|
}
|
||||||
|
|
||||||
|
// still no node found, should create it
|
||||||
|
if node == nil {
|
||||||
|
node = new(Node)
|
||||||
|
node.parent = n
|
||||||
|
n.children = append(n.children, node)
|
||||||
|
|
||||||
|
if paramObj != nil {
|
||||||
|
node.pathParam = paramObj
|
||||||
|
} else if chunkIsGibberish {
|
||||||
|
newParam := n.createParam()
|
||||||
|
node.pathParam = newParam
|
||||||
|
} else {
|
||||||
|
node.constant = &pathChunk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add example if it's a gibberish chunk
|
||||||
|
if node.pathParam != nil && !chunkIsParam {
|
||||||
|
exmp := &node.pathParam.Examples
|
||||||
|
err := fillParamExample(&exmp, pathChunk)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Warningf("Failed to add example to a parameter: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: eat up trailing slash, in a smart way: node.pathObj!=nil && path[1]==""
|
||||||
|
if len(path) > 1 {
|
||||||
|
return node.getOrSet(path[1:], existingPathObj)
|
||||||
|
} else if node.pathObj == nil {
|
||||||
|
node.pathObj = existingPathObj
|
||||||
|
}
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Node) createParam() *openapi.ParameterObj {
|
||||||
|
name := "param"
|
||||||
|
|
||||||
|
if n.constant != nil { // the node is already a param
|
||||||
|
// REST assumption, not always correct
|
||||||
|
if strings.HasSuffix(*n.constant, "es") && len(*n.constant) > 4 {
|
||||||
|
name = *n.constant
|
||||||
|
name = name[:len(name)-2] + "Id"
|
||||||
|
} else if strings.HasSuffix(*n.constant, "s") && len(*n.constant) > 3 {
|
||||||
|
name = *n.constant
|
||||||
|
name = name[:len(name)-1] + "Id"
|
||||||
|
} else {
|
||||||
|
name = *n.constant + "Id"
|
||||||
|
}
|
||||||
|
|
||||||
|
name = cleanStr(name, isAlNumRune)
|
||||||
|
if !isAlphaRune(rune(name[0])) {
|
||||||
|
name = "_" + name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newParam := createSimpleParam(name, "path", "string")
|
||||||
|
x := n.countParentParams()
|
||||||
|
if x > 0 {
|
||||||
|
newParam.Name = newParam.Name + strconv.Itoa(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newParam
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Node) searchInParams(paramObj *openapi.ParameterObj, chunkIsGibberish bool) *Node {
|
||||||
|
// look among params
|
||||||
|
if paramObj != nil || chunkIsGibberish {
|
||||||
|
for _, subnode := range n.children {
|
||||||
|
if subnode.constant != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: check the regex pattern of param? for exceptions etc
|
||||||
|
|
||||||
|
if paramObj != nil {
|
||||||
|
// TODO: mergeParam(subnode.pathParam, paramObj)
|
||||||
|
return subnode
|
||||||
|
} else {
|
||||||
|
return subnode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Node) searchInConstants(pathChunk string) *Node {
|
||||||
|
// look among constants
|
||||||
|
for _, subnode := range n.children {
|
||||||
|
if subnode.constant == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if *subnode.constant == pathChunk {
|
||||||
|
return subnode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Node) compact() {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Node) listPaths() *openapi.Paths {
|
||||||
|
paths := &openapi.Paths{Items: map[openapi.PathValue]*openapi.PathObj{}}
|
||||||
|
|
||||||
|
var strChunk string
|
||||||
|
if n.constant != nil {
|
||||||
|
strChunk = *n.constant
|
||||||
|
} else if n.pathParam != nil {
|
||||||
|
strChunk = "{" + n.pathParam.Name + "}"
|
||||||
|
} // else -> this is the root node
|
||||||
|
|
||||||
|
// add self
|
||||||
|
if n.pathObj != nil {
|
||||||
|
fillPathParams(n, n.pathObj)
|
||||||
|
paths.Items[openapi.PathValue(strChunk)] = n.pathObj
|
||||||
|
}
|
||||||
|
|
||||||
|
// recurse into children
|
||||||
|
for _, child := range n.children {
|
||||||
|
subPaths := child.listPaths()
|
||||||
|
for path, pathObj := range subPaths.Items {
|
||||||
|
var concat string
|
||||||
|
if n.parent == nil {
|
||||||
|
concat = string(path)
|
||||||
|
} else {
|
||||||
|
concat = strChunk + "/" + string(path)
|
||||||
|
}
|
||||||
|
paths.Items[openapi.PathValue(concat)] = pathObj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return paths
|
||||||
|
}
|
||||||
|
|
||||||
|
func fillPathParams(n *Node, pathObj *openapi.PathObj) {
|
||||||
|
// collect all path parameters from parent hierarchy
|
||||||
|
node := n
|
||||||
|
for {
|
||||||
|
if node.pathParam != nil {
|
||||||
|
initParams(&pathObj.Parameters)
|
||||||
|
|
||||||
|
idx, paramObj := findParamByName(pathObj.Parameters, openapi.InPath, node.pathParam.Name)
|
||||||
|
if paramObj == nil {
|
||||||
|
appended := append(*pathObj.Parameters, node.pathParam)
|
||||||
|
pathObj.Parameters = &appended
|
||||||
|
} else {
|
||||||
|
(*pathObj.Parameters)[idx] = paramObj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node = node.parent
|
||||||
|
if node == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type PathAndOp struct {
|
||||||
|
path string
|
||||||
|
op *openapi.Operation
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Node) listOps() []PathAndOp {
|
||||||
|
res := make([]PathAndOp, 0)
|
||||||
|
for path, pathObj := range n.listPaths().Items {
|
||||||
|
for _, op := range getOps(pathObj) {
|
||||||
|
res = append(res, PathAndOp{path: string(path), op: op})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Node) countParentParams() int {
|
||||||
|
res := 0
|
||||||
|
node := n
|
||||||
|
for {
|
||||||
|
if node.pathParam != nil {
|
||||||
|
res++
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.parent == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
node = node.parent
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
37
agent/pkg/oas/tree_test.go
Normal file
37
agent/pkg/oas/tree_test.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package oas
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/chanced/openapi"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTree(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
inp string
|
||||||
|
numParams int
|
||||||
|
label string
|
||||||
|
}{
|
||||||
|
{"/", 0, ""},
|
||||||
|
{"/v1.0.0/config/launcher/sp_nKNHCzsN/f34efcae-6583-11eb-908a-00b0fcb9d4f6/vendor,init,conversation", 1, "vendor,init,conversation"},
|
||||||
|
{"/v1.0.0/config/launcher/sp_nKNHCzsN/{f34efcae-6583-11eb-908a-00b0fcb9d4f6}/vendor,init,conversation", 0, "vendor,init,conversation"},
|
||||||
|
{"/getSvgs/size/small/brand/SFLY/layoutId/170943/layoutVersion/1/sizeId/742/surface/0/isLandscape/true/childSkus/%7B%7D", 1, "{}"},
|
||||||
|
}
|
||||||
|
|
||||||
|
tree := new(Node)
|
||||||
|
for _, tc := range testCases {
|
||||||
|
split := strings.Split(tc.inp, "/")
|
||||||
|
pathObj := new(openapi.PathObj)
|
||||||
|
node := tree.getOrSet(split, pathObj)
|
||||||
|
|
||||||
|
fillPathParams(node, pathObj)
|
||||||
|
|
||||||
|
if node.constant != nil && *node.constant != tc.label {
|
||||||
|
t.Errorf("Constant does not match: %s != %s", *node.constant, tc.label)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.numParams > 0 && (pathObj.Parameters == nil || len(*pathObj.Parameters) < tc.numParams) {
|
||||||
|
t.Errorf("Wrong num of params, expected: %d", tc.numParams)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7707
agent/pkg/oas/trigrams.go
Normal file
7707
agent/pkg/oas/trigrams.go
Normal file
File diff suppressed because it is too large
Load Diff
429
agent/pkg/oas/utils.go
Normal file
429
agent/pkg/oas/utils.go
Normal file
@@ -0,0 +1,429 @@
|
|||||||
|
package oas
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/har"
|
||||||
|
|
||||||
|
"github.com/chanced/openapi"
|
||||||
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
func exampleResolver(ref string) (*openapi.ExampleObj, error) {
|
||||||
|
return nil, errors.New("JSON references are not supported at the moment: " + ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
func responseResolver(ref string) (*openapi.ResponseObj, error) {
|
||||||
|
return nil, errors.New("JSON references are not supported at the moment: " + ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
func reqBodyResolver(ref string) (*openapi.RequestBodyObj, error) {
|
||||||
|
return nil, errors.New("JSON references are not supported at the moment: " + ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
func paramResolver(ref string) (*openapi.ParameterObj, error) {
|
||||||
|
return nil, errors.New("JSON references are not supported at the moment: " + ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
func headerResolver(ref string) (*openapi.HeaderObj, error) {
|
||||||
|
return nil, errors.New("JSON references are not supported at the moment: " + ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
func initParams(obj **openapi.ParameterList) {
|
||||||
|
if *obj == nil {
|
||||||
|
var params openapi.ParameterList = make([]openapi.Parameter, 0)
|
||||||
|
*obj = ¶ms
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func initHeaders(respObj *openapi.ResponseObj) {
|
||||||
|
if respObj.Headers == nil {
|
||||||
|
var created openapi.Headers = map[string]openapi.Header{}
|
||||||
|
respObj.Headers = created
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSimpleParam(name string, in openapi.In, ptype openapi.SchemaType) *openapi.ParameterObj {
|
||||||
|
if name == "" {
|
||||||
|
panic("Cannot create parameter with empty name")
|
||||||
|
}
|
||||||
|
required := true // FFS! https://stackoverflow.com/questions/32364027/reference-a-boolean-for-assignment-in-a-struct/32364093
|
||||||
|
schema := new(openapi.SchemaObj)
|
||||||
|
schema.Type = openapi.Types{ptype}
|
||||||
|
|
||||||
|
style := openapi.StyleSimple
|
||||||
|
if in == openapi.InQuery {
|
||||||
|
style = openapi.StyleForm
|
||||||
|
}
|
||||||
|
|
||||||
|
newParam := openapi.ParameterObj{
|
||||||
|
Name: name,
|
||||||
|
In: in,
|
||||||
|
Style: string(style),
|
||||||
|
Examples: map[string]openapi.Example{},
|
||||||
|
Schema: schema,
|
||||||
|
Required: &required,
|
||||||
|
}
|
||||||
|
return &newParam
|
||||||
|
}
|
||||||
|
|
||||||
|
func findParamByName(params *openapi.ParameterList, in openapi.In, name string) (idx int, pathParam *openapi.ParameterObj) {
|
||||||
|
caseInsensitive := in == openapi.InHeader
|
||||||
|
for i, param := range *params {
|
||||||
|
idx = i
|
||||||
|
paramObj, err := param.ResolveParameter(paramResolver)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Warningf("Failed to resolve reference: %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if paramObj.In != in {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if paramObj.Name == name || (caseInsensitive && strings.EqualFold(paramObj.Name, name)) {
|
||||||
|
pathParam = paramObj
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return idx, pathParam
|
||||||
|
}
|
||||||
|
|
||||||
|
func findHeaderByName(headers *openapi.Headers, name string) *openapi.HeaderObj {
|
||||||
|
for hname, param := range *headers {
|
||||||
|
hdrObj, err := param.ResolveHeader(headerResolver)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Warningf("Failed to resolve reference: %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.EqualFold(hname, name) {
|
||||||
|
return hdrObj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type nvParams struct {
|
||||||
|
In openapi.In
|
||||||
|
Pairs []har.NVP
|
||||||
|
IsIgnored func(name string) bool
|
||||||
|
GeneralizeName func(name string) string
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleNameVals(gw nvParams, params **openapi.ParameterList, checkIgnore bool) {
|
||||||
|
visited := map[string]*openapi.ParameterObj{}
|
||||||
|
for _, pair := range gw.Pairs {
|
||||||
|
if (checkIgnore && gw.IsIgnored(pair.Name)) || pair.Name == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
nameGeneral := gw.GeneralizeName(pair.Name)
|
||||||
|
|
||||||
|
initParams(params)
|
||||||
|
_, param := findParamByName(*params, gw.In, pair.Name)
|
||||||
|
if param == nil {
|
||||||
|
param = createSimpleParam(nameGeneral, gw.In, openapi.TypeString)
|
||||||
|
appended := append(**params, param)
|
||||||
|
*params = &appended
|
||||||
|
}
|
||||||
|
exmp := ¶m.Examples
|
||||||
|
err := fillParamExample(&exmp, pair.Value)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Warningf("Failed to add example to a parameter: %s", err)
|
||||||
|
}
|
||||||
|
visited[nameGeneral] = param
|
||||||
|
}
|
||||||
|
|
||||||
|
// maintain "required" flag
|
||||||
|
if *params != nil {
|
||||||
|
for _, param := range **params {
|
||||||
|
paramObj, err := param.ResolveParameter(paramResolver)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Warningf("Failed to resolve param: %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if paramObj.In != gw.In {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ok := visited[strings.ToLower(paramObj.Name)]
|
||||||
|
if !ok {
|
||||||
|
flag := false
|
||||||
|
paramObj.Required = &flag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createHeader(ptype openapi.SchemaType) *openapi.HeaderObj {
|
||||||
|
required := true // FFS! https://stackoverflow.com/questions/32364027/reference-a-boolean-for-assignment-in-a-struct/32364093
|
||||||
|
schema := new(openapi.SchemaObj)
|
||||||
|
schema.Type = make(openapi.Types, 0)
|
||||||
|
schema.Type = append(schema.Type, ptype)
|
||||||
|
|
||||||
|
style := openapi.StyleSimple
|
||||||
|
newParam := openapi.HeaderObj{
|
||||||
|
Style: string(style),
|
||||||
|
Examples: map[string]openapi.Example{},
|
||||||
|
Schema: schema,
|
||||||
|
Required: &required,
|
||||||
|
}
|
||||||
|
return &newParam
|
||||||
|
}
|
||||||
|
|
||||||
|
func fillParamExample(param **openapi.Examples, exampleValue string) error {
|
||||||
|
if **param == nil {
|
||||||
|
**param = map[string]openapi.Example{}
|
||||||
|
}
|
||||||
|
|
||||||
|
cnt := 0
|
||||||
|
for _, example := range **param {
|
||||||
|
cnt++
|
||||||
|
exampleObj, err := example.ResolveExample(exampleResolver)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var value string
|
||||||
|
err = json.Unmarshal(exampleObj.Value, &value)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Warningf("Failed decoding parameter example into string: %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if value == exampleValue || cnt >= 5 { // 5 examples is enough
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
valMsg, err := json.Marshal(exampleValue)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
themap := **param
|
||||||
|
themap["example #"+strconv.Itoa(cnt)] = &openapi.ExampleObj{Value: valMsg}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: somehow generalize the two example setting functions, plus add body example handling
|
||||||
|
|
||||||
|
func addSchemaExample(existing *openapi.SchemaObj, bodyStr string) {
|
||||||
|
if len(existing.Examples) < 5 {
|
||||||
|
found := false
|
||||||
|
for _, eVal := range existing.Examples {
|
||||||
|
existingExample := ""
|
||||||
|
err := json.Unmarshal(eVal, &existingExample)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Debugf("Failed to unmarshal example: %v", eVal)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if existingExample == bodyStr {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
example, err := json.Marshal(bodyStr)
|
||||||
|
if err != nil {
|
||||||
|
logger.Log.Debugf("Failed to marshal example: %v", bodyStr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
existing.Examples = append(existing.Examples, example)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func longestCommonXfix(strs [][]string, pre bool) []string { // https://github.com/jpillora/longestcommon
|
||||||
|
empty := make([]string, 0)
|
||||||
|
//short-circuit empty list
|
||||||
|
if len(strs) == 0 {
|
||||||
|
return empty
|
||||||
|
}
|
||||||
|
xfix := strs[0]
|
||||||
|
//short-circuit single-element list
|
||||||
|
if len(strs) == 1 {
|
||||||
|
return xfix
|
||||||
|
}
|
||||||
|
//compare first to rest
|
||||||
|
for _, str := range strs[1:] {
|
||||||
|
xfixl := len(xfix)
|
||||||
|
strl := len(str)
|
||||||
|
//short-circuit empty strings
|
||||||
|
if xfixl == 0 || strl == 0 {
|
||||||
|
return empty
|
||||||
|
}
|
||||||
|
//maximum possible length
|
||||||
|
maxl := xfixl
|
||||||
|
if strl < maxl {
|
||||||
|
maxl = strl
|
||||||
|
}
|
||||||
|
//compare letters
|
||||||
|
if pre {
|
||||||
|
//prefix, iterate left to right
|
||||||
|
for i := 0; i < maxl; i++ {
|
||||||
|
if xfix[i] != str[i] {
|
||||||
|
xfix = xfix[:i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//suffix, iternate right to left
|
||||||
|
for i := 0; i < maxl; i++ {
|
||||||
|
xi := xfixl - i - 1
|
||||||
|
si := strl - i - 1
|
||||||
|
if xfix[xi] != str[si] {
|
||||||
|
xfix = xfix[xi+1:]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return xfix
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSimilarPrefix(strs []string) string {
|
||||||
|
chunked := make([][]string, 0)
|
||||||
|
for _, item := range strs {
|
||||||
|
chunked = append(chunked, strings.Split(item, "/"))
|
||||||
|
}
|
||||||
|
|
||||||
|
cmn := longestCommonXfix(chunked, true)
|
||||||
|
res := make([]string, 0)
|
||||||
|
for _, chunk := range cmn {
|
||||||
|
if chunk != "api" && !IsVersionString(chunk) && !strings.HasPrefix(chunk, "{") {
|
||||||
|
res = append(res, chunk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.Join(res[1:], ".")
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns all non-nil ops in PathObj
|
||||||
|
func getOps(pathObj *openapi.PathObj) []*openapi.Operation {
|
||||||
|
ops := []**openapi.Operation{&pathObj.Get, &pathObj.Patch, &pathObj.Put, &pathObj.Options, &pathObj.Post, &pathObj.Trace, &pathObj.Head, &pathObj.Delete}
|
||||||
|
res := make([]*openapi.Operation, 0)
|
||||||
|
for _, opp := range ops {
|
||||||
|
if *opp == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
res = append(res, *opp)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// parses JSON into any possible value
|
||||||
|
func anyJSON(text string) (anyVal interface{}, isJSON bool) {
|
||||||
|
isJSON = true
|
||||||
|
asMap := map[string]interface{}{}
|
||||||
|
err := json.Unmarshal([]byte(text), &asMap)
|
||||||
|
if err == nil && asMap != nil {
|
||||||
|
return asMap, isJSON
|
||||||
|
}
|
||||||
|
|
||||||
|
asArray := make([]interface{}, 0)
|
||||||
|
err = json.Unmarshal([]byte(text), &asArray)
|
||||||
|
if err == nil && asArray != nil {
|
||||||
|
return asArray, isJSON
|
||||||
|
}
|
||||||
|
|
||||||
|
asString := ""
|
||||||
|
sPtr := &asString
|
||||||
|
err = json.Unmarshal([]byte(text), &sPtr)
|
||||||
|
if err == nil && sPtr != nil {
|
||||||
|
return asString, isJSON
|
||||||
|
}
|
||||||
|
|
||||||
|
asInt := 0
|
||||||
|
intPtr := &asInt
|
||||||
|
err = json.Unmarshal([]byte(text), &intPtr)
|
||||||
|
if err == nil && intPtr != nil {
|
||||||
|
return asInt, isJSON
|
||||||
|
}
|
||||||
|
|
||||||
|
asFloat := 0.0
|
||||||
|
floatPtr := &asFloat
|
||||||
|
err = json.Unmarshal([]byte(text), &floatPtr)
|
||||||
|
if err == nil && floatPtr != nil {
|
||||||
|
return asFloat, isJSON
|
||||||
|
}
|
||||||
|
|
||||||
|
asBool := false
|
||||||
|
boolPtr := &asBool
|
||||||
|
err = json.Unmarshal([]byte(text), &boolPtr)
|
||||||
|
if err == nil && boolPtr != nil {
|
||||||
|
return asBool, isJSON
|
||||||
|
}
|
||||||
|
|
||||||
|
if text == "null" {
|
||||||
|
return nil, isJSON
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanStr(str string, criterion func(r rune) bool) string {
|
||||||
|
s := []byte(str)
|
||||||
|
j := 0
|
||||||
|
for _, b := range s {
|
||||||
|
if criterion(rune(b)) {
|
||||||
|
s[j] = b
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(s[:j])
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func isAlpha(s string) bool {
|
||||||
|
for _, r := range s {
|
||||||
|
if isAlphaRune(r) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
func isAlphaRune(r rune) bool {
|
||||||
|
return !((r < 'a' || r > 'z') && (r < 'A' || r > 'Z'))
|
||||||
|
}
|
||||||
|
|
||||||
|
func isAlNumRune(b rune) bool {
|
||||||
|
return isAlphaRune(b) || ('0' <= b && b <= '9')
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteFromSlice(s []string, val string) []string {
|
||||||
|
temp := s[:0]
|
||||||
|
for _, x := range s {
|
||||||
|
if x != val {
|
||||||
|
temp = append(temp, x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return temp
|
||||||
|
}
|
||||||
|
|
||||||
|
func sliceContains(s []string, e string) bool {
|
||||||
|
for _, a := range s {
|
||||||
|
if a == e {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func intersectSliceWithMap(required []string, names map[string]struct{}) []string {
|
||||||
|
for name := range names {
|
||||||
|
if !sliceContains(required, name) {
|
||||||
|
required = deleteFromSlice(required, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return required
|
||||||
|
}
|
||||||
59
agent/pkg/oas/utils_test.go
Normal file
59
agent/pkg/oas/utils_test.go
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package oas
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAnyJSON(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
inp string
|
||||||
|
isJSON bool
|
||||||
|
out interface{}
|
||||||
|
}{
|
||||||
|
{`{"key": 1, "keyNull": null}`, true, nil},
|
||||||
|
{`[{"key": "val"}, ["subarray"], "string", 1, 2.2, true, null]`, true, nil},
|
||||||
|
{`"somestring"`, true, "somestring"},
|
||||||
|
{"0", true, 0},
|
||||||
|
{"0.5", true, 0.5},
|
||||||
|
{"true", true, true},
|
||||||
|
{"null", true, nil},
|
||||||
|
{"sabbra cadabra", false, nil},
|
||||||
|
{"0.1.2.3", false, nil},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
any, isJSON := anyJSON(tc.inp)
|
||||||
|
if isJSON != tc.isJSON {
|
||||||
|
t.Errorf("Parse flag mismatch: %t != %t", tc.isJSON, isJSON)
|
||||||
|
} else if isJSON && tc.out != nil && tc.out != any {
|
||||||
|
t.Errorf("%s != %s", any, tc.out)
|
||||||
|
} else if tc.inp == "null" && any != nil {
|
||||||
|
t.Errorf("null has to parse as nil (but got %s)", any)
|
||||||
|
} else {
|
||||||
|
t.Logf("%s => %s", tc.inp, any)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStrRunes(t *testing.T) {
|
||||||
|
if isAlphaRune('5') {
|
||||||
|
t.Logf("Failed")
|
||||||
|
}
|
||||||
|
if !isAlphaRune('a') {
|
||||||
|
t.Logf("Failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isAlNumRune('5') {
|
||||||
|
t.Logf("Failed")
|
||||||
|
}
|
||||||
|
if isAlNumRune(' ') {
|
||||||
|
t.Logf("Failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cleanStr("-abc_567", isAlphaRune) != "abc" {
|
||||||
|
t.Logf("Failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cleanStr("-abc_567", isAlNumRune) != "abc567" {
|
||||||
|
t.Logf("Failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,9 +2,17 @@ package providers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"mizuserver/pkg/config"
|
"errors"
|
||||||
|
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/config"
|
||||||
|
|
||||||
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
|
|
||||||
|
ory "github.com/ory/kratos-client-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const AdminUsername = "admin"
|
||||||
|
|
||||||
func IsInstallNeeded() (bool, error) {
|
func IsInstallNeeded() (bool, error) {
|
||||||
if !config.Config.StandaloneMode { // install not needed in ephermeral mizu
|
if !config.Config.StandaloneMode { // install not needed in ephermeral mizu
|
||||||
return false, nil
|
return false, nil
|
||||||
@@ -16,3 +24,29 @@ func IsInstallNeeded() (bool, error) {
|
|||||||
return !anyUserExists, nil
|
return !anyUserExists, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CreateAdminUser(password string, ctx context.Context) (token *string, err error, formErrorMessages map[string][]ory.UiText) {
|
||||||
|
if isInstallNeeded, err := IsInstallNeeded(); err != nil {
|
||||||
|
return nil, err, nil
|
||||||
|
} else if !isInstallNeeded {
|
||||||
|
return nil, errors.New("The admin user has already been created"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
token, identityId, err, formErrors := RegisterUser(AdminUsername, password, ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err, formErrors
|
||||||
|
}
|
||||||
|
|
||||||
|
err = SetUserSystemRole(AdminUsername, AdminRole)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
//Delete the user to prevent a half-setup situation where admin user is created without admin privileges
|
||||||
|
if err := DeleteUser(identityId, ctx); err != nil {
|
||||||
|
logger.Log.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, err, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return token, nil, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
type GeneralStats struct {
|
type GeneralStats struct {
|
||||||
EntriesCount int
|
EntriesCount int
|
||||||
|
EntriesVolumeInGB float64
|
||||||
FirstEntryTimestamp int
|
FirstEntryTimestamp int
|
||||||
LastEntryTimestamp int
|
LastEntryTimestamp int
|
||||||
}
|
}
|
||||||
@@ -21,8 +22,9 @@ func GetGeneralStats() GeneralStats {
|
|||||||
return generalStats
|
return generalStats
|
||||||
}
|
}
|
||||||
|
|
||||||
func EntryAdded() {
|
func EntryAdded(size int) {
|
||||||
generalStats.EntriesCount++
|
generalStats.EntriesCount++
|
||||||
|
generalStats.EntriesVolumeInGB += float64(size) / (1 << 30)
|
||||||
|
|
||||||
currentTimestamp := int(time.Now().Unix())
|
currentTimestamp := int(time.Now().Unix())
|
||||||
|
|
||||||
@@ -32,5 +34,3 @@ func EntryAdded() {
|
|||||||
|
|
||||||
generalStats.LastEntryTimestamp = currentTimestamp
|
generalStats.LastEntryTimestamp = currentTimestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ package providers_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"mizuserver/pkg/providers"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/providers"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNoEntryAddedCount(t *testing.T) {
|
func TestNoEntryAddedCount(t *testing.T) {
|
||||||
@@ -12,6 +13,10 @@ func TestNoEntryAddedCount(t *testing.T) {
|
|||||||
if entriesStats.EntriesCount != 0 {
|
if entriesStats.EntriesCount != 0 {
|
||||||
t.Errorf("unexpected result - expected: %v, actual: %v", 0, entriesStats.EntriesCount)
|
t.Errorf("unexpected result - expected: %v, actual: %v", 0, entriesStats.EntriesCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if entriesStats.EntriesVolumeInGB != 0 {
|
||||||
|
t.Errorf("unexpected result - expected: %v, actual: %v", 0, entriesStats.EntriesVolumeInGB)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEntryAddedCount(t *testing.T) {
|
func TestEntryAddedCount(t *testing.T) {
|
||||||
@@ -20,7 +25,7 @@ func TestEntryAddedCount(t *testing.T) {
|
|||||||
for _, entriesCount := range tests {
|
for _, entriesCount := range tests {
|
||||||
t.Run(fmt.Sprintf("%d", entriesCount), func(t *testing.T) {
|
t.Run(fmt.Sprintf("%d", entriesCount), func(t *testing.T) {
|
||||||
for i := 0; i < entriesCount; i++ {
|
for i := 0; i < entriesCount; i++ {
|
||||||
providers.EntryAdded()
|
providers.EntryAdded(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
entriesStats := providers.GetGeneralStats()
|
entriesStats := providers.GetGeneralStats()
|
||||||
@@ -29,7 +34,38 @@ func TestEntryAddedCount(t *testing.T) {
|
|||||||
t.Errorf("unexpected result - expected: %v, actual: %v", entriesCount, entriesStats.EntriesCount)
|
t.Errorf("unexpected result - expected: %v, actual: %v", entriesCount, entriesStats.EntriesCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if entriesStats.EntriesVolumeInGB != 0 {
|
||||||
|
t.Errorf("unexpected result - expected: %v, actual: %v", 0, entriesStats.EntriesVolumeInGB)
|
||||||
|
}
|
||||||
|
|
||||||
t.Cleanup(providers.ResetGeneralStats)
|
t.Cleanup(providers.ResetGeneralStats)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEntryAddedVolume(t *testing.T) {
|
||||||
|
// 6 bytes + 4 bytes
|
||||||
|
tests := [][]byte{[]byte("volume"), []byte("test")}
|
||||||
|
var expectedEntriesCount int
|
||||||
|
var expectedVolumeInGB float64
|
||||||
|
|
||||||
|
for _, data := range tests {
|
||||||
|
t.Run(fmt.Sprintf("%d", len(data)), func(t *testing.T) {
|
||||||
|
expectedEntriesCount++
|
||||||
|
expectedVolumeInGB += float64(len(data)) / (1 << 30)
|
||||||
|
|
||||||
|
providers.EntryAdded(len(data))
|
||||||
|
|
||||||
|
entriesStats := providers.GetGeneralStats()
|
||||||
|
|
||||||
|
if entriesStats.EntriesCount != expectedEntriesCount {
|
||||||
|
t.Errorf("unexpected result - expected: %v, actual: %v", expectedEntriesCount, entriesStats.EntriesCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
if entriesStats.EntriesVolumeInGB != expectedVolumeInGB {
|
||||||
|
t.Errorf("unexpected result - expected: %v, actual: %v", expectedVolumeInGB, entriesStats.EntriesVolumeInGB)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,12 +3,13 @@ package providers
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/patrickmn/go-cache"
|
|
||||||
"github.com/up9inc/mizu/shared"
|
|
||||||
"github.com/up9inc/mizu/tap"
|
|
||||||
"mizuserver/pkg/models"
|
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/patrickmn/go-cache"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/models"
|
||||||
|
"github.com/up9inc/mizu/shared"
|
||||||
|
"github.com/up9inc/mizu/tap"
|
||||||
)
|
)
|
||||||
|
|
||||||
const tlsLinkRetainmentTime = time.Minute * 15
|
const tlsLinkRetainmentTime = time.Minute * 15
|
||||||
|
|||||||
@@ -1,40 +1,33 @@
|
|||||||
package tapConfig
|
package tapConfig
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"github.com/up9inc/mizu/shared"
|
|
||||||
"github.com/up9inc/mizu/shared/logger"
|
|
||||||
"io/ioutil"
|
|
||||||
"mizuserver/pkg/models"
|
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/models"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/utils"
|
||||||
|
"github.com/up9inc/mizu/shared"
|
||||||
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
const FilePath = shared.DataDirPath + "tap-config.json"
|
const FilePath = shared.DataDirPath + "tap-config.json"
|
||||||
|
|
||||||
var lock = &sync.Mutex{}
|
var (
|
||||||
|
lock = &sync.Mutex{}
|
||||||
var config *models.TapConfig
|
syncOnce sync.Once
|
||||||
|
config *models.TapConfig
|
||||||
|
)
|
||||||
|
|
||||||
func Get() *models.TapConfig {
|
func Get() *models.TapConfig {
|
||||||
if config == nil {
|
syncOnce.Do(func() {
|
||||||
lock.Lock()
|
if err := utils.ReadJsonFile(FilePath, &config); err != nil {
|
||||||
defer lock.Unlock()
|
config = &models.TapConfig{TappedNamespaces: make(map[string]bool)}
|
||||||
|
|
||||||
if config == nil {
|
if !os.IsNotExist(err) {
|
||||||
if content, err := ioutil.ReadFile(FilePath); err != nil {
|
logger.Log.Errorf("Error reading tap config from file, err: %v", err)
|
||||||
config = &models.TapConfig{TappedNamespaces: make(map[string]bool)}
|
|
||||||
if !os.IsNotExist(err) {
|
|
||||||
logger.Log.Errorf("Error loading tap config from file, err: %v", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err = json.Unmarshal(content, &config); err != nil {
|
|
||||||
config = &models.TapConfig{TappedNamespaces: make(map[string]bool)}
|
|
||||||
logger.Log.Errorf("Error while unmarshal tap config, err: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
@@ -44,11 +37,7 @@ func Save(tapConfigToSave *models.TapConfig) {
|
|||||||
defer lock.Unlock()
|
defer lock.Unlock()
|
||||||
|
|
||||||
config = tapConfigToSave
|
config = tapConfigToSave
|
||||||
if data, err := json.Marshal(config); err != nil {
|
if err := utils.SaveJsonFile(FilePath, config); err != nil {
|
||||||
logger.Log.Errorf("Error while marshal tap config, err: %v", err)
|
logger.Log.Errorf("Error saving tap config, err: %v", err)
|
||||||
} else {
|
|
||||||
if err := ioutil.WriteFile(FilePath, data, 0644); err != nil {
|
|
||||||
logger.Log.Errorf("Error writing tap config to file, err: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,51 @@
|
|||||||
package tappedPods
|
package tappedPods
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/up9inc/mizu/shared"
|
"os"
|
||||||
"mizuserver/pkg/providers/tappersStatus"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/providers/tappers"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/utils"
|
||||||
|
"github.com/up9inc/mizu/shared"
|
||||||
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
var tappedPods []*shared.PodInfo
|
const FilePath = shared.DataDirPath + "tapped-pods.json"
|
||||||
|
|
||||||
|
var (
|
||||||
|
lock = &sync.Mutex{}
|
||||||
|
syncOnce sync.Once
|
||||||
|
tappedPods []*shared.PodInfo
|
||||||
|
)
|
||||||
|
|
||||||
func Get() []*shared.PodInfo {
|
func Get() []*shared.PodInfo {
|
||||||
|
syncOnce.Do(func() {
|
||||||
|
if err := utils.ReadJsonFile(FilePath, &tappedPods); err != nil {
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
logger.Log.Errorf("Error reading tapped pods from file, err: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
return tappedPods
|
return tappedPods
|
||||||
}
|
}
|
||||||
|
|
||||||
func Set(tappedPodsToSet []*shared.PodInfo) {
|
func Set(tappedPodsToSet []*shared.PodInfo) {
|
||||||
|
lock.Lock()
|
||||||
|
defer lock.Unlock()
|
||||||
|
|
||||||
tappedPods = tappedPodsToSet
|
tappedPods = tappedPodsToSet
|
||||||
|
if err := utils.SaveJsonFile(FilePath, tappedPods); err != nil {
|
||||||
|
logger.Log.Errorf("Error saving tapped pods, err: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetTappedPodsStatus() []shared.TappedPodStatus {
|
func GetTappedPodsStatus() []shared.TappedPodStatus {
|
||||||
tappedPodsStatus := make([]shared.TappedPodStatus, 0)
|
tappedPodsStatus := make([]shared.TappedPodStatus, 0)
|
||||||
for _, pod := range Get() {
|
for _, pod := range Get() {
|
||||||
var status string
|
var status string
|
||||||
if tapperStatus, ok := tappersStatus.Get()[pod.NodeName]; ok {
|
if tapperStatus, ok := tappers.GetStatus()[pod.NodeName]; ok {
|
||||||
status = strings.ToLower(tapperStatus.Status)
|
status = strings.ToLower(tapperStatus.Status)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
83
agent/pkg/providers/tappers/tappers_provider.go
Normal file
83
agent/pkg/providers/tappers/tappers_provider.go
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
package tappers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/utils"
|
||||||
|
"github.com/up9inc/mizu/shared"
|
||||||
|
"github.com/up9inc/mizu/shared/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
const FilePath = shared.DataDirPath + "tappers-status.json"
|
||||||
|
|
||||||
|
var (
|
||||||
|
lockStatus = &sync.Mutex{}
|
||||||
|
syncOnce sync.Once
|
||||||
|
status map[string]*shared.TapperStatus
|
||||||
|
|
||||||
|
lockConnectedCount = &sync.Mutex{}
|
||||||
|
connectedCount int
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetStatus() map[string]*shared.TapperStatus {
|
||||||
|
initStatus()
|
||||||
|
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetStatus(tapperStatus *shared.TapperStatus) {
|
||||||
|
initStatus()
|
||||||
|
|
||||||
|
lockStatus.Lock()
|
||||||
|
defer lockStatus.Unlock()
|
||||||
|
|
||||||
|
status[tapperStatus.NodeName] = tapperStatus
|
||||||
|
|
||||||
|
saveStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResetStatus() {
|
||||||
|
lockStatus.Lock()
|
||||||
|
defer lockStatus.Unlock()
|
||||||
|
|
||||||
|
status = make(map[string]*shared.TapperStatus)
|
||||||
|
|
||||||
|
saveStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetConnectedCount() int {
|
||||||
|
return connectedCount
|
||||||
|
}
|
||||||
|
|
||||||
|
func Connected() {
|
||||||
|
lockConnectedCount.Lock()
|
||||||
|
defer lockConnectedCount.Unlock()
|
||||||
|
|
||||||
|
connectedCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
func Disconnected() {
|
||||||
|
lockConnectedCount.Lock()
|
||||||
|
defer lockConnectedCount.Unlock()
|
||||||
|
|
||||||
|
connectedCount--
|
||||||
|
}
|
||||||
|
|
||||||
|
func initStatus() {
|
||||||
|
syncOnce.Do(func() {
|
||||||
|
if err := utils.ReadJsonFile(FilePath, &status); err != nil {
|
||||||
|
status = make(map[string]*shared.TapperStatus)
|
||||||
|
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
logger.Log.Errorf("Error reading tappers status from file, err: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveStatus() {
|
||||||
|
if err := utils.SaveJsonFile(FilePath, status); err != nil {
|
||||||
|
logger.Log.Errorf("Error saving tappers status, err: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
package tappersCount
|
|
||||||
|
|
||||||
import "sync"
|
|
||||||
|
|
||||||
var lock = &sync.Mutex{}
|
|
||||||
|
|
||||||
var tappersCount int
|
|
||||||
|
|
||||||
func Add() {
|
|
||||||
lock.Lock()
|
|
||||||
defer lock.Unlock()
|
|
||||||
|
|
||||||
tappersCount++
|
|
||||||
}
|
|
||||||
|
|
||||||
func Remove() {
|
|
||||||
lock.Lock()
|
|
||||||
defer lock.Unlock()
|
|
||||||
|
|
||||||
tappersCount--
|
|
||||||
}
|
|
||||||
|
|
||||||
func Get() int {
|
|
||||||
return tappersCount
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
package tappersStatus
|
|
||||||
|
|
||||||
import "github.com/up9inc/mizu/shared"
|
|
||||||
|
|
||||||
var tappersStatus map[string]*shared.TapperStatus
|
|
||||||
|
|
||||||
func Get() map[string]*shared.TapperStatus {
|
|
||||||
if tappersStatus == nil {
|
|
||||||
tappersStatus = make(map[string]*shared.TapperStatus)
|
|
||||||
}
|
|
||||||
|
|
||||||
return tappersStatus
|
|
||||||
}
|
|
||||||
|
|
||||||
func Set(tapperStatus *shared.TapperStatus) {
|
|
||||||
if tappersStatus == nil {
|
|
||||||
tappersStatus = make(map[string]*shared.TapperStatus)
|
|
||||||
}
|
|
||||||
|
|
||||||
tappersStatus[tapperStatus.NodeName] = tapperStatus
|
|
||||||
}
|
|
||||||
|
|
||||||
func Reset() {
|
|
||||||
tappersStatus = make(map[string]*shared.TapperStatus)
|
|
||||||
}
|
|
||||||
@@ -66,17 +66,17 @@ func PerformLogin(username string, password string, ctx context.Context) (*strin
|
|||||||
return result.SessionToken, nil
|
return result.SessionToken, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func VerifyToken(token string, ctx context.Context) (bool, error) {
|
func VerifyToken(token string, ctx context.Context) (*ory.Session, error) {
|
||||||
flow, _, err := client.V0alpha2Api.ToSession(ctx).XSessionToken(token).Execute()
|
flow, _, err := client.V0alpha2Api.ToSession(ctx).XSessionToken(token).Execute()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if flow == nil {
|
if flow == nil {
|
||||||
return false, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, nil
|
return flow, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteUser(identityId string, ctx context.Context) error {
|
func DeleteUser(identityId string, ctx context.Context) error {
|
||||||
@@ -85,11 +85,11 @@ func DeleteUser(identityId string, ctx context.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if result == nil {
|
if result == nil {
|
||||||
return errors.New("unknown error occured during user deletion")
|
return fmt.Errorf("unknown error occured during user deletion %v", identityId)
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.StatusCode < 200 || result.StatusCode > 299 {
|
if result.StatusCode < 200 || result.StatusCode > 299 {
|
||||||
return errors.New(fmt.Sprintf("user deletion returned bad status %d", result.StatusCode))
|
return fmt.Errorf("user deletion %v returned bad status %d", identityId, result.StatusCode)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
181
agent/pkg/providers/user_role_provider.go
Normal file
181
agent/pkg/providers/user_role_provider.go
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
package providers
|
||||||
|
|
||||||
|
/*
|
||||||
|
This provider abstracts keto role management down to what we need for mizu
|
||||||
|
|
||||||
|
Keto, in the configuration we use it, is basically a tuple database. Each tuple consists of 4 strings (namespace, object, relation, subjectID) - for example ("workspaces", "sock-shop-workspace", "viewer", "ramiberman")
|
||||||
|
|
||||||
|
namespace - used to organize tuples into groups - we currently use "system" for defining admins and "workspaces" for defining workspace permissions
|
||||||
|
objects - represents something one can have permissions to (files, mizu workspaces etc)
|
||||||
|
relation - represents the permission (viewer, editor, owner etc) - we currently use only viewer and admin
|
||||||
|
subject - represents the user or group that has the permission - we currently use usernames
|
||||||
|
|
||||||
|
more on keto here: https://www.ory.sh/keto/docs/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/utils"
|
||||||
|
|
||||||
|
ketoClient "github.com/ory/keto-client-go/client"
|
||||||
|
ketoRead "github.com/ory/keto-client-go/client/read"
|
||||||
|
ketoWrite "github.com/ory/keto-client-go/client/write"
|
||||||
|
ketoModels "github.com/ory/keto-client-go/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ketoHost = "localhost"
|
||||||
|
ketoReadPort = 4466
|
||||||
|
ketoWritePort = 4467
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
readClient = getKetoClient(fmt.Sprintf("%s:%d", ketoHost, ketoReadPort))
|
||||||
|
writeClient = getKetoClient(fmt.Sprintf("%s:%d", ketoHost, ketoWritePort))
|
||||||
|
systemRoleNamespace = "system"
|
||||||
|
workspacesRoleNamespace = "workspaces"
|
||||||
|
|
||||||
|
systemObject = "system"
|
||||||
|
|
||||||
|
AdminRole = "admin"
|
||||||
|
ViewerRole = "viewer"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetUserSystemRoles(username string) ([]string, error) {
|
||||||
|
return getObjectRelationsForSubjectID(systemRoleNamespace, systemObject, username)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckIfUserHasSystemRole(username string, role string) (bool, error) {
|
||||||
|
systemRoles, err := GetUserSystemRoles(username)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, systemRole := range systemRoles {
|
||||||
|
if systemRole == role {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetUserWorkspaceRole(username string, workspace string) ([]string, error) {
|
||||||
|
return getObjectRelationsForSubjectID(workspacesRoleNamespace, workspace, username)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetUserWorkspaceRole(username string, workspace string, role string) error {
|
||||||
|
return createObjectRelationForSubjectID(workspacesRoleNamespace, workspace, username, role)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetUserSystemRole(username string, role string) error {
|
||||||
|
return createObjectRelationForSubjectID(systemRoleNamespace, systemObject, username, role)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteAllUserWorkspaceRoles(username string) error {
|
||||||
|
return deleteAllNamespacedRelationsForSubjectID(workspacesRoleNamespace, username)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createObjectRelationForSubjectID(namespace string, object string, subjectID string, relation string) error {
|
||||||
|
tuple := ketoModels.RelationQuery{
|
||||||
|
Namespace: &namespace,
|
||||||
|
Object: object,
|
||||||
|
Relation: relation,
|
||||||
|
SubjectID: subjectID,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := writeClient.Write.CreateRelationTuple(ketoWrite.
|
||||||
|
NewCreateRelationTupleParams().
|
||||||
|
WithPayload(&tuple))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getObjectRelationsForSubjectID(namespace string, object string, subjectID string) ([]string, error) {
|
||||||
|
relationTuples, err := queryRelationTuples(&namespace, &object, &subjectID, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
relations := make([]string, 0)
|
||||||
|
|
||||||
|
for _, clientRelation := range relationTuples {
|
||||||
|
relations = append(relations, *clientRelation.Relation)
|
||||||
|
}
|
||||||
|
|
||||||
|
return utils.UniqueStringSlice(relations), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteAllNamespacedRelationsForSubjectID(namespace string, subjectID string) error {
|
||||||
|
relationTuples, err := queryRelationTuples(&namespace, nil, &subjectID, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, clientRelation := range relationTuples {
|
||||||
|
_, err := writeClient.Write.DeleteRelationTuple(ketoWrite.
|
||||||
|
NewDeleteRelationTupleParams().
|
||||||
|
WithNamespace(*clientRelation.Namespace).
|
||||||
|
WithObject(*clientRelation.Object).
|
||||||
|
WithRelation(*clientRelation.Relation).
|
||||||
|
WithSubjectID(&clientRelation.SubjectID))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryRelationTuples(namespace *string, object *string, subjectID *string, role *string) ([]*ketoModels.InternalRelationTuple, error) {
|
||||||
|
relationTuplesQuery := ketoRead.NewGetRelationTuplesParams()
|
||||||
|
if namespace != nil {
|
||||||
|
relationTuplesQuery = relationTuplesQuery.WithNamespace(*namespace)
|
||||||
|
}
|
||||||
|
if object != nil {
|
||||||
|
relationTuplesQuery = relationTuplesQuery.WithObject(object)
|
||||||
|
}
|
||||||
|
if subjectID != nil {
|
||||||
|
relationTuplesQuery = relationTuplesQuery.WithSubjectID(subjectID)
|
||||||
|
}
|
||||||
|
if role != nil {
|
||||||
|
relationTuplesQuery = relationTuplesQuery.WithRelation(role)
|
||||||
|
}
|
||||||
|
|
||||||
|
return recursiveKetoPagingTraverse(relationTuplesQuery, make([]*ketoModels.InternalRelationTuple, 0), "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func recursiveKetoPagingTraverse(queryParams *ketoRead.GetRelationTuplesParams, tuples []*ketoModels.InternalRelationTuple, pagingToken string) ([]*ketoModels.InternalRelationTuple, error) {
|
||||||
|
params := queryParams
|
||||||
|
if pagingToken != "" {
|
||||||
|
params = queryParams.WithPageToken(&pagingToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
clientRelationsResponse, err := readClient.Read.GetRelationTuples(params)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tuples = append(tuples, clientRelationsResponse.Payload.RelationTuples...)
|
||||||
|
|
||||||
|
if clientRelationsResponse.Payload.NextPageToken != "" {
|
||||||
|
return recursiveKetoPagingTraverse(queryParams, tuples, clientRelationsResponse.Payload.NextPageToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tuples, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getKetoClient(url string) *ketoClient.OryKeto {
|
||||||
|
return ketoClient.NewHTTPClientWithConfig(nil,
|
||||||
|
ketoClient.
|
||||||
|
DefaultTransportConfig().
|
||||||
|
WithSchemes([]string{"http"}).
|
||||||
|
WithHost(url))
|
||||||
|
}
|
||||||
@@ -1,15 +1,23 @@
|
|||||||
package routes
|
package routes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/controllers"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/middlewares"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"mizuserver/pkg/controllers"
|
|
||||||
"mizuserver/pkg/middlewares"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ConfigRoutes(ginApp *gin.Engine) {
|
var (
|
||||||
|
ConfigPostTapConfigHandler = controllers.PostTapConfig
|
||||||
|
ConfigGetTapConfigHandler = controllers.GetTapConfig
|
||||||
|
)
|
||||||
|
|
||||||
|
func ConfigRoutes(ginApp *gin.Engine) *gin.RouterGroup {
|
||||||
routeGroup := ginApp.Group("/config")
|
routeGroup := ginApp.Group("/config")
|
||||||
routeGroup.Use(middlewares.RequiresAuth())
|
routeGroup.Use(middlewares.RequiresAuth())
|
||||||
|
|
||||||
routeGroup.POST("/tapConfig", controllers.PostTapConfig)
|
routeGroup.POST("/tap", middlewares.RequiresAdmin(), func(c *gin.Context) { ConfigPostTapConfigHandler(c) })
|
||||||
routeGroup.GET("/tapConfig", controllers.GetTapConfig)
|
routeGroup.GET("/tap", func(c *gin.Context) { ConfigGetTapConfigHandler(c) })
|
||||||
|
|
||||||
|
return routeGroup
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,24 @@
|
|||||||
package routes
|
package routes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"mizuserver/pkg/controllers"
|
"github.com/up9inc/mizu/agent/pkg/controllers"
|
||||||
"mizuserver/pkg/middlewares"
|
"github.com/up9inc/mizu/agent/pkg/middlewares"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
EntriesGetHandler = controllers.GetEntries
|
||||||
|
EntriesGetSingleHandler = controllers.GetEntry
|
||||||
|
)
|
||||||
|
|
||||||
// EntriesRoutes defines the group of har entries routes.
|
// EntriesRoutes defines the group of har entries routes.
|
||||||
func EntriesRoutes(ginApp *gin.Engine) {
|
func EntriesRoutes(ginApp *gin.Engine) *gin.RouterGroup {
|
||||||
routeGroup := ginApp.Group("/entries")
|
routeGroup := ginApp.Group("/entries")
|
||||||
routeGroup.Use(middlewares.RequiresAuth())
|
routeGroup.Use(middlewares.RequiresAuth())
|
||||||
|
|
||||||
routeGroup.GET("/", controllers.GetEntries) // get entries (base/thin entries) and metadata
|
routeGroup.GET("/", func(c *gin.Context) { EntriesGetHandler(c) }) // get entries (base/thin entries) and metadata
|
||||||
routeGroup.GET("/:id", controllers.GetEntry) // get single (full) entry
|
routeGroup.GET("/:id", func(c *gin.Context) { EntriesGetSingleHandler(c) }) // get single (full) entry
|
||||||
|
|
||||||
|
return routeGroup
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,21 @@
|
|||||||
package routes
|
package routes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"mizuserver/pkg/controllers"
|
"github.com/up9inc/mizu/agent/pkg/controllers"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func InstallRoutes(ginApp *gin.Engine) {
|
var (
|
||||||
|
InstallGetIsNeededHandler = controllers.IsSetupNecessary
|
||||||
|
InstallPostAdminHandler = controllers.SetupAdminUser
|
||||||
|
)
|
||||||
|
|
||||||
|
func InstallRoutes(ginApp *gin.Engine) *gin.RouterGroup {
|
||||||
routeGroup := ginApp.Group("/install")
|
routeGroup := ginApp.Group("/install")
|
||||||
|
|
||||||
routeGroup.GET("/isNeeded", controllers.IsSetupNecessary)
|
routeGroup.GET("/isNeeded", func(c *gin.Context) { InstallGetIsNeededHandler(c) })
|
||||||
|
routeGroup.POST("/admin", func(c *gin.Context) { InstallPostAdminHandler(c) })
|
||||||
|
|
||||||
|
return routeGroup
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,20 @@
|
|||||||
package routes
|
package routes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"mizuserver/pkg/controllers"
|
"github.com/up9inc/mizu/agent/pkg/controllers"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
MetadataGetVersionHandler = controllers.GetVersion
|
||||||
|
)
|
||||||
|
|
||||||
// MetadataRoutes defines the group of metadata routes.
|
// MetadataRoutes defines the group of metadata routes.
|
||||||
func MetadataRoutes(app *gin.Engine) {
|
func MetadataRoutes(app *gin.Engine) *gin.RouterGroup {
|
||||||
routeGroup := app.Group("/metadata")
|
routeGroup := app.Group("/metadata")
|
||||||
|
|
||||||
routeGroup.GET("/version", controllers.GetVersion)
|
routeGroup.GET("/version", func(c *gin.Context) { MetadataGetVersionHandler(c) })
|
||||||
|
|
||||||
|
return routeGroup
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
package routes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NotFoundRoute defines the 404 Error route.
|
|
||||||
func NotFoundRoute(app *gin.Engine) {
|
|
||||||
app.Use(
|
|
||||||
func(c *gin.Context) {
|
|
||||||
c.JSON(http.StatusNotFound, map[string]interface{}{
|
|
||||||
"error": true,
|
|
||||||
"msg": "sorry, endpoint is not found",
|
|
||||||
})
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
26
agent/pkg/routes/oas_routes.go
Normal file
26
agent/pkg/routes/oas_routes.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/controllers"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/middlewares"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
OASGetServersHandler = controllers.GetOASServers
|
||||||
|
OASGetAllSpecsHandler = controllers.GetOASAllSpecs
|
||||||
|
OASGetSingleSpecHandler = controllers.GetOASSpec
|
||||||
|
)
|
||||||
|
|
||||||
|
// OASRoutes methods to access OAS spec
|
||||||
|
func OASRoutes(ginApp *gin.Engine) *gin.RouterGroup {
|
||||||
|
routeGroup := ginApp.Group("/oas")
|
||||||
|
routeGroup.Use(middlewares.RequiresAuth())
|
||||||
|
|
||||||
|
routeGroup.GET("/", func(c *gin.Context) { OASGetServersHandler(c) }) // list of servers in OAS map
|
||||||
|
routeGroup.GET("/all", func(c *gin.Context) { OASGetAllSpecsHandler(c) }) // list of servers in OAS map
|
||||||
|
routeGroup.GET("/:id", func(c *gin.Context) { OASGetSingleSpecHandler(c) }) // get OAS spec for given server
|
||||||
|
|
||||||
|
return routeGroup
|
||||||
|
}
|
||||||
@@ -1,15 +1,21 @@
|
|||||||
package routes
|
package routes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"mizuserver/pkg/controllers"
|
"github.com/up9inc/mizu/agent/pkg/controllers"
|
||||||
"mizuserver/pkg/middlewares"
|
"github.com/up9inc/mizu/agent/pkg/middlewares"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func QueryRoutes(ginApp *gin.Engine) {
|
var (
|
||||||
|
QueryPostValidateHandler = controllers.PostValidate
|
||||||
|
)
|
||||||
|
|
||||||
|
func QueryRoutes(ginApp *gin.Engine) *gin.RouterGroup {
|
||||||
routeGroup := ginApp.Group("/query")
|
routeGroup := ginApp.Group("/query")
|
||||||
routeGroup.Use(middlewares.RequiresAuth())
|
routeGroup.Use(middlewares.RequiresAuth())
|
||||||
|
|
||||||
routeGroup.POST("/validate", controllers.PostValidate)
|
routeGroup.POST("/validate", func(c *gin.Context) { QueryPostValidateHandler(c) })
|
||||||
|
|
||||||
|
return routeGroup
|
||||||
}
|
}
|
||||||
|
|||||||
31
agent/pkg/routes/service_map_routes.go
Normal file
31
agent/pkg/routes/service_map_routes.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/controllers"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/middlewares"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ServiceMapGetStatus gin.HandlerFunc
|
||||||
|
ServiceMapGet gin.HandlerFunc
|
||||||
|
ServiceMapReset gin.HandlerFunc
|
||||||
|
)
|
||||||
|
|
||||||
|
func ServiceMapRoutes(ginApp *gin.Engine) *gin.RouterGroup {
|
||||||
|
routeGroup := ginApp.Group("/servicemap")
|
||||||
|
routeGroup.Use(middlewares.RequiresAuth())
|
||||||
|
|
||||||
|
controller := controllers.NewServiceMapController()
|
||||||
|
|
||||||
|
ServiceMapGetStatus = controller.Status
|
||||||
|
ServiceMapGet = controller.Get
|
||||||
|
ServiceMapReset = controller.Reset
|
||||||
|
|
||||||
|
routeGroup.GET("/status", func(c *gin.Context) { ServiceMapGetStatus(c) })
|
||||||
|
routeGroup.GET("/get", func(c *gin.Context) { ServiceMapGet(c) })
|
||||||
|
routeGroup.GET("/reset", func(c *gin.Context) { ServiceMapReset(c) })
|
||||||
|
|
||||||
|
return routeGroup
|
||||||
|
}
|
||||||
@@ -1,30 +1,45 @@
|
|||||||
package routes
|
package routes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"mizuserver/pkg/controllers"
|
"github.com/up9inc/mizu/agent/pkg/controllers"
|
||||||
"mizuserver/pkg/middlewares"
|
"github.com/up9inc/mizu/agent/pkg/middlewares"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func StatusRoutes(ginApp *gin.Engine) {
|
var (
|
||||||
|
StatusGetHealthCheck = controllers.HealthCheck
|
||||||
|
StatusPostTappedPods = controllers.PostTappedPods
|
||||||
|
StatusPostTapperStatus = controllers.PostTapperStatus
|
||||||
|
StatusGetConnectedTappersCount = controllers.GetConnectedTappersCount
|
||||||
|
StatusGetTappingStatus = controllers.GetTappingStatus
|
||||||
|
StatusGetAuthStatus = controllers.GetAuthStatus
|
||||||
|
StatusGetAnalyzeInformation = controllers.AnalyzeInformation
|
||||||
|
StatusGetGeneralStats = controllers.GetGeneralStats
|
||||||
|
StatusGetRecentTLSLinks = controllers.GetRecentTLSLinks
|
||||||
|
StatusGetCurrentResolvingInformation = controllers.GetCurrentResolvingInformation
|
||||||
|
)
|
||||||
|
|
||||||
|
func StatusRoutes(ginApp *gin.Engine) *gin.RouterGroup {
|
||||||
routeGroup := ginApp.Group("/status")
|
routeGroup := ginApp.Group("/status")
|
||||||
routeGroup.Use(middlewares.RequiresAuth())
|
routeGroup.Use(middlewares.RequiresAuth())
|
||||||
|
|
||||||
routeGroup.GET("/health", controllers.HealthCheck)
|
routeGroup.GET("/health", func(c *gin.Context) { StatusGetHealthCheck(c) })
|
||||||
|
|
||||||
routeGroup.POST("/tappedPods", controllers.PostTappedPods)
|
routeGroup.POST("/tappedPods", func(c *gin.Context) { StatusPostTappedPods(c) })
|
||||||
routeGroup.POST("/tapperStatus", controllers.PostTapperStatus)
|
routeGroup.POST("/tapperStatus", func(c *gin.Context) { StatusPostTapperStatus(c) })
|
||||||
routeGroup.GET("/tappersCount", controllers.GetTappersCount)
|
routeGroup.GET("/connectedTappersCount", func(c *gin.Context) { StatusGetConnectedTappersCount(c) })
|
||||||
routeGroup.GET("/tap", controllers.GetTappingStatus)
|
routeGroup.GET("/tap", func(c *gin.Context) { StatusGetTappingStatus(c) })
|
||||||
|
|
||||||
routeGroup.GET("/auth", controllers.GetAuthStatus)
|
routeGroup.GET("/auth", func(c *gin.Context) { StatusGetAuthStatus(c) })
|
||||||
|
|
||||||
routeGroup.GET("/analyze", controllers.AnalyzeInformation)
|
routeGroup.GET("/analyze", func(c *gin.Context) { StatusGetAnalyzeInformation(c) })
|
||||||
|
|
||||||
routeGroup.GET("/general", controllers.GetGeneralStats) // get general stats about entries in DB
|
routeGroup.GET("/general", func(c *gin.Context) { StatusGetGeneralStats(c) }) // get general stats about entries in DB
|
||||||
|
|
||||||
routeGroup.GET("/recentTLSLinks", controllers.GetRecentTLSLinks)
|
routeGroup.GET("/recentTLSLinks", func(c *gin.Context) { StatusGetRecentTLSLinks(c) })
|
||||||
|
|
||||||
routeGroup.GET("/resolving", controllers.GetCurrentResolvingInformation)
|
routeGroup.GET("/resolving", func(c *gin.Context) { StatusGetCurrentResolvingInformation(c) })
|
||||||
|
|
||||||
|
return routeGroup
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,23 @@
|
|||||||
package routes
|
package routes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"mizuserver/pkg/controllers"
|
"github.com/up9inc/mizu/agent/pkg/controllers"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func UserRoutes(ginApp *gin.Engine) {
|
var (
|
||||||
|
UserPostLogin = controllers.Login
|
||||||
|
UserPostLogout = controllers.Logout
|
||||||
|
UserPostRegister = controllers.Register
|
||||||
|
)
|
||||||
|
|
||||||
|
func UserRoutes(ginApp *gin.Engine) *gin.RouterGroup {
|
||||||
routeGroup := ginApp.Group("/user")
|
routeGroup := ginApp.Group("/user")
|
||||||
|
|
||||||
routeGroup.POST("/login", controllers.Login)
|
routeGroup.POST("/login", func(c *gin.Context) { UserPostLogin(c) })
|
||||||
routeGroup.POST("/logout", controllers.Logout)
|
routeGroup.POST("/logout", func(c *gin.Context) { UserPostLogout(c) })
|
||||||
routeGroup.POST("/register", controllers.Register)
|
routeGroup.POST("/register", func(c *gin.Context) { UserPostRegister(c) })
|
||||||
|
|
||||||
|
return routeGroup
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user