Compare commits

..

45 Commits

Author SHA1 Message Date
RamiBerm
76a6a77a14 Refactor ws (#961)
* Separate socket and basenine logic

* WIP

* Update socket_server_handlers.go

* Update socket_data_streamer.go and socket_server_handlers.go

* Update socket_server_handlers.go

* Merge branch 'develop' into refactor_ws
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.

* empty commit for actions

* empty commit for actions

* commit for actions

* Revert "commit for actions"

This reverts commit 8ba2ecf7d3.

Co-authored-by: RoyUP9 <87927115+RoyUP9@users.noreply.github.com>
2022-04-04 17:33:53 +03:00
M. Mert Yıldıran
2bfc523bbc Handle reflect.TypeOf returning nil case (#970) 2022-04-04 16:25:18 +03:00
RoyUP9
66ba778384 Fixed golint modified files (#969) 2022-04-04 15:32:22 +03:00
leon-up9
7adbf7bf1b Ui/TRA-4461_service-map-&-OAS---GUI-changes (#962)
* OpenAPI renamed to Service Catalog

* Docs icon change
Hide Navbar on serviceMap modal open

* PR comments

Co-authored-by: Leon <>
Co-authored-by: RoyUP9 <87927115+RoyUP9@users.noreply.github.com>
2022-04-04 14:49:41 +03:00
Igor Gov
a97b5b3b38 Add conditional Go lint validation to CI (#967) 2022-04-04 14:35:47 +03:00
Nimrod Gilboa Markevich
aa8dcc5f5c Format commit message as code to handle multi line messages (#963)
Co-authored-by: gadotroee <55343099+gadotroee@users.noreply.github.com>
2022-04-03 22:10:43 +03:00
lirazyehezkel
9d08dbdd5d UI performance fix 2022-04-03 21:35:09 +03:00
lirazyehezkel
b47718e094 TRA-4442 Improve UI performance (#960)
* Move ws entry listener to entriesList component

* unused code
2022-04-03 15:51:20 +03:00
Igor Gov
6a7fad430c Adding resolved prop to service map node (#959)
* Adding resolved prop to service map node

* fixing tests
2022-04-03 15:32:21 +03:00
lirazyehezkel
59ad8d8fad TLS icon position (#956)
* TLS icon position

* cr fix
2022-03-31 11:26:58 +03:00
Nimrod Gilboa Markevich
a49443f101 Set the entry namespace to the source namespace if the destination is not resolved (#950)
* Set the entry namespace to the source namespace if the destination is not resolved
* Overwrite src namespace with dst namespace only if dst non-empty
2022-03-30 15:40:21 +03:00
lirazyehezkel
2427955aa4 Avoid overlap only for service map including under 10 services 2022-03-30 15:30:09 +03:00
David Levanon
27a73e21fb Read from service mesh network namespaces upon update (#944) 2022-03-30 13:56:37 +03:00
Igor Gov
8eeb0e54c9 Changing unit tests workflow timeout to 30 minutes 2022-03-30 11:52:47 +03:00
Andrey Pokhilko
97db24aeba OAS: rework data feeding + sampleIDs (#917)
* Call OAS feeder

* Don't call old OAS code

* Rework calls

* Work on it

* Put back rules

* Make it compile

* start thinking of test

* Compiles

* Save

* Fixes

* Save

* Fixing

* Trying to fake conn

* add timeout

* Test timeout

* Fix tests

* Only build OAS for HTTP entries

* Remove some dead code

* Adding SampleIDs

* Cosmetics

* lint

* Revert rename

* Sample ID for content

* Cleanuo

* Add more sample IDs

* Checking hypothesis

* Move assignment place a bit

* Cosmetics

* Update test.yml

Co-authored-by: undera <undera@undera-old-desktop.home>
Co-authored-by: Igor Gov <iggvrv@gmail.com>
2022-03-30 11:14:25 +03:00
RamiBerm
63cf7ac34e Refactor entries controller logic (#949)
* wip

* Update entries_controller.go and entries_provider.go

* Update entries_controller.go

* change entries provider into a struct + interface

* Update entries_provider.go

* Update entries_provider.go
2022-03-29 18:30:19 +03:00
Igor Gov
e867b7d0f1 Build ui-common part of CI (#914)
* Build ui-common always locally
2022-03-29 14:14:52 +03:00
lirazyehezkel
dcd8a64f43 Hotfix/remove token from community (#948) 2022-03-29 13:16:50 +03:00
lirazyehezkel
bf8d5ed069 Support multiple workspaces (TRA-4365) (#945)
* support multiple workspaces

* reopen by websocket url dep

* open websocket only when websocketURL is changed

* upgrade common version

Co-authored-by: gadotroee <55343099+gadotroee@users.noreply.github.com>
2022-03-29 09:56:59 +03:00
Nimrod Gilboa Markevich
1f6e539590 Add commit message and committer to acceptance tests slack alert (#946)
* Add commit message and committer username to slack alerts
* Use name instead of username
* Use name and email
2022-03-29 09:15:42 +03:00
David Levanon
590fa08c81 EBPF error handling 2022-03-28 14:19:06 +03:00
Igor Gov
0a9be1884a Traffic viewer list item ebpf to lock icon (#939)
* Traffic viewer changing ebpf to lock icon
2022-03-28 11:10:54 +03:00
Igor Gov
40c745068e Traffic viewer changing ebpf to lock icon (#938) 2022-03-28 10:45:52 +03:00
AmitUp9
10dffd9331 TRA-4419 remove headers from OAS select (#936) 2022-03-28 10:01:57 +03:00
leon-up9
0a800e8d8a passed containerId in the object config (#935)
* passed containerId in the object config

* pkg version

* tmp: run acceptance tests, revert before merge

* reverte

Co-authored-by: Leon <>
2022-03-27 19:41:51 +03:00
RamiBerm
068a4ff86e TRA-4422 line numbers fix (#934)
* fix line numbering in body syntax highlighter

* Packages
2022-03-27 16:17:34 +03:00
leon-up9
c45a869b75 Fix/regration-fixes-24.3 (#933)
* link open in a new tab

* fixed double toast

* added const

* javiers new icon

* epbg img fix

* pkg update

* changed import

* formated

Co-authored-by: Leon <>
2022-03-27 15:47:46 +03:00
leon-up9
0a793cd9e0 added props (#924) 2022-03-27 11:31:40 +03:00
Adrian Gąsior
8d19080c11 Updated Dockerfile and fixed typo in Makefile (#929)
* Updated Dockerfile and fixed typo in Makefile

* PR request

* added small hotfix to basenine
2022-03-27 01:39:50 +03:00
Nimrod Gilboa Markevich
319c3c7a8d Initialize tapper before ws (#932) 2022-03-26 21:25:18 +03:00
M. Mert Yıldıran
0e7704eb15 Bump the version to 0.6.6 and fix the index shift bug that occurs after database truncation (#931) 2022-03-26 18:25:38 +03:00
gadotroee
dbcb776139 no message (#930) 2022-03-26 13:19:04 +03:00
Igor Gov
a3de34f544 Turn OAS feature ON by default (#927)
* Turn OAS feature ON by default
2022-03-24 20:40:16 +02:00
Nimrod Gilboa Markevich
99667984d6 Update tap targets without reinitializing packet source manager (#925) 2022-03-24 15:39:20 +02:00
David Levanon
763b0e7362 quick tls update pods solution (#918)
Update TLS tappers when tapped pods are updated via WS.
2022-03-24 15:21:56 +02:00
AmitUp9
e07e04377f mizu and ui-common version update (#923) 2022-03-24 12:31:08 +02:00
AmitUp9
3c8f63ed92 Feature/UI/tra 4390 status bar banner demo env (#921)
* no message

* banner styling condition added and button text transform disabled

* added prps to container

* build fix
2022-03-24 12:14:31 +02:00
leon-up9
11a2246cb9 Fix/entry-title-responsive-comments (#922)
* Make `EntryTitle` component responsive

* useRequetTextByWidth added

* renamed

Co-authored-by: M. Mert Yildiran <mehmet@up9.com>
Co-authored-by: Leon <>
2022-03-24 12:09:28 +02:00
leon-up9
a2595afd5e changed icon (#920)
Co-authored-by: Leon <>
2022-03-24 10:43:35 +02:00
AmitUp9
0f4710918f TRA-4122 oas gui grooming (#915)
* oas styling
* oas button text change
* ui-common version update
* font fix
* version updating
Co-authored-by: gadotroee <55343099+gadotroee@users.noreply.github.com>
2022-03-23 22:15:04 +02:00
RoyUP9
4bdda920d5 Fix for check command (#916)
* Fix for check command

* empty commit for checks

Co-authored-by: Roee Gadot <roee.gadot@up9.com>
2022-03-23 22:02:29 +02:00
Adrian Gąsior
59e6268ddd Refactored Dockerfile (#902)
* Refactored Dockerfile

* PR request
2022-03-23 16:16:37 +02:00
leon-up9
2513e9099f UI/tra 4406 add an information icon (#913)
* information icon added

* new icon and position

* icon changed

* new Ver

* space removed

Co-authored-by: Adam Kol <adam@up9.com>
Co-authored-by: Leon <>
2022-03-23 14:54:51 +02:00
Nimrod Gilboa Markevich
a5c35d7d90 Update tap targets over ws (#901)
Update tappers via websocket instead of by env var. This way the DaemonSet doesn't have to be applied just to notify the tappers that the tap targets changed. The number of tapper restarts is reduced. The DaemonSet still gets applied when there is a need to add/remove a tapper from a node.
2022-03-23 13:50:33 +02:00
Igor Gov
41a7587088 Remove redundant Google auth from test workflow (#911)
* Remove google auth for test workflow
2022-03-23 11:51:42 +02:00
103 changed files with 5850 additions and 1944 deletions

View File

@@ -43,7 +43,7 @@ jobs:
with: with:
status: ${{ job.status }} status: ${{ job.status }}
notification_title: 'Mizu {workflow} has {status_message}' notification_title: 'Mizu {workflow} has {status_message}'
message_format: '{emoji} *{workflow}* {status_message} during <{run_url}|run>, after commit: <{commit_url}|{commit_sha}>' message_format: '{emoji} *{workflow}* {status_message} during <{run_url}|run>, after commit <{commit_url}|{commit_sha}> by ${{ github.event.head_commit.committer.name }} <${{ github.event.head_commit.committer.email }}> ```${{ github.event.head_commit.message }}```'
footer: 'Linked Repo <{repo_url}|{repo}>' footer: 'Linked Repo <{repo_url}|{repo}>'
notify_when: 'failure' notify_when: 'failure'
env: env:

View File

@@ -45,7 +45,7 @@ jobs:
- name: Check modified files - name: Check modified files
id: modified_files id: modified_files
run: devops/check_modified_files.sh agent/ shared/ tap/ ui/ Dockerfile run: devops/check_modified_files.sh agent/ shared/ tap/ ui/ ui-common/ Dockerfile
- name: Set up Docker Buildx - name: Set up Docker Buildx
if: steps.modified_files.outputs.matched == 'true' if: steps.modified_files.outputs.matched == 'true'

View File

@@ -15,6 +15,9 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with:
fetch-depth: 2
- uses: actions/setup-go@v2 - uses: actions/setup-go@v2
with: with:
go-version: '^1.17' go-version: '^1.17'
@@ -24,67 +27,117 @@ jobs:
sudo apt update sudo apt update
sudo apt install -y libpcap-dev sudo apt install -y libpcap-dev
- name: Check Agent modified files
id: agent_modified_files
run: devops/check_modified_files.sh agent/
- name: Go lint - agent - name: Go lint - agent
uses: golangci/golangci-lint-action@v2 uses: golangci/golangci-lint-action@v2
if: steps.agent_modified_files.outputs.matched == 'true'
with: with:
version: latest version: latest
working-directory: agent working-directory: agent
args: --timeout=3m args: --timeout=3m
- name: Check shared modified files
id: shared_modified_files
run: devops/check_modified_files.sh shared/
- name: Go lint - shared - name: Go lint - shared
uses: golangci/golangci-lint-action@v2 uses: golangci/golangci-lint-action@v2
if: steps.shared_modified_files.outputs.matched == 'true'
with: with:
version: latest version: latest
working-directory: shared working-directory: shared
args: --timeout=3m args: --timeout=3m
- name: Check tap modified files
id: tap_modified_files
run: devops/check_modified_files.sh tap/
- name: Go lint - tap - name: Go lint - tap
uses: golangci/golangci-lint-action@v2 uses: golangci/golangci-lint-action@v2
if: steps.tap_modified_files.outputs.matched == 'true'
with: with:
version: latest version: latest
working-directory: tap working-directory: tap
args: --timeout=3m args: --timeout=3m
- name: Check cli modified files
id: cli_modified_files
run: devops/check_modified_files.sh cli/
- name: Go lint - CLI - name: Go lint - CLI
uses: golangci/golangci-lint-action@v2 uses: golangci/golangci-lint-action@v2
if: steps.cli_modified_files.outputs.matched == 'true'
with: with:
version: latest version: latest
working-directory: cli working-directory: cli
args: --timeout=3m args: --timeout=3m
- name: Check acceptanceTests modified files
id: acceptanceTests_modified_files
run: devops/check_modified_files.sh acceptanceTests/
- name: Go lint - acceptanceTests - name: Go lint - acceptanceTests
uses: golangci/golangci-lint-action@v2 uses: golangci/golangci-lint-action@v2
if: steps.acceptanceTests_modified_files.outputs.matched == 'true'
with: with:
version: latest version: latest
working-directory: acceptanceTests working-directory: acceptanceTests
args: --timeout=3m args: --timeout=3m
- name: Check tap/api modified files
id: tap_api_modified_files
run: devops/check_modified_files.sh tap/api/
- name: Go lint - tap/api - name: Go lint - tap/api
uses: golangci/golangci-lint-action@v2 uses: golangci/golangci-lint-action@v2
if: steps.tap_api_modified_files.outputs.matched == 'true'
with: with:
version: latest version: latest
working-directory: tap/api working-directory: tap/api
- name: Check tap/extensions/amqp modified files
id: tap_amqp_modified_files
run: devops/check_modified_files.sh tap/extensions/amqp/
- name: Go lint - tap/extensions/amqp - name: Go lint - tap/extensions/amqp
uses: golangci/golangci-lint-action@v2 uses: golangci/golangci-lint-action@v2
if: steps.tap_amqp_modified_files.outputs.matched == 'true'
with: with:
version: latest version: latest
working-directory: tap/extensions/amqp working-directory: tap/extensions/amqp
- name: Check tap/extensions/http modified files
id: tap_http_modified_files
run: devops/check_modified_files.sh tap/extensions/http/
- name: Go lint - tap/extensions/http - name: Go lint - tap/extensions/http
uses: golangci/golangci-lint-action@v2 uses: golangci/golangci-lint-action@v2
if: steps.tap_http_modified_files.outputs.matched == 'true'
with: with:
version: latest version: latest
working-directory: tap/extensions/http working-directory: tap/extensions/http
- name: Check tap/extensions/kafka modified files
id: tap_kafka_modified_files
run: devops/check_modified_files.sh tap/extensions/kafka/
- name: Go lint - tap/extensions/kafka - name: Go lint - tap/extensions/kafka
uses: golangci/golangci-lint-action@v2 uses: golangci/golangci-lint-action@v2
if: steps.tap_kafka_modified_files.outputs.matched == 'true'
with: with:
version: latest version: latest
working-directory: tap/extensions/kafka working-directory: tap/extensions/kafka
- name: Check tap/extensions/redis modified files
id: tap_redis_modified_files
run: devops/check_modified_files.sh tap/extensions/redis/
- name: Go lint - tap/extensions/redis - name: Go lint - tap/extensions/redis
uses: golangci/golangci-lint-action@v2 uses: golangci/golangci-lint-action@v2
if: steps.tap_redis_modified_files.outputs.matched == 'true'
with: with:
version: latest version: latest
working-directory: tap/extensions/redis working-directory: tap/extensions/redis

View File

@@ -18,6 +18,7 @@ jobs:
run-unit-tests: run-unit-tests:
name: Unit Tests name: Unit Tests
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 20
steps: steps:
- 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
@@ -34,14 +35,6 @@ jobs:
run: | run: |
sudo apt-get install libpcap-dev sudo apt-get install libpcap-dev
- 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: Check CLI modified files - name: Check CLI modified files
id: cli_modified_files id: cli_modified_files
run: devops/check_modified_files.sh cli/ run: devops/check_modified_files.sh cli/

View File

@@ -1,13 +1,23 @@
ARG BUILDARCH=amd64 ARG BUILDARCH=amd64
ARG TARGETARCH=amd64 ARG TARGETARCH=amd64
### Front-end common
FROM node:16 AS front-end-common
WORKDIR /app/ui-build
COPY ui-common/package.json .
COPY ui-common/package-lock.json .
RUN npm i
COPY ui-common .
RUN npm pack
### Front-end ### Front-end
FROM node:16 AS front-end FROM node:16 AS front-end
WORKDIR /app/ui-build WORKDIR /app/ui-build
COPY ui/package.json . COPY ui/package.json ui/package-lock.json ./
COPY ui/package-lock.json . COPY --from=front-end-common ["/app/ui-build/up9-mizu-common-0.0.0.tgz", "."]
RUN npm i RUN npm i
COPY ui . COPY ui .
RUN npm run build RUN npm run build
@@ -15,7 +25,7 @@ RUN npm run build
### Base builder image for native builds architecture ### Base builder image for native builds architecture
FROM golang:1.17-alpine AS builder-native-base FROM golang:1.17-alpine AS builder-native-base
ENV CGO_ENABLED=1 GOOS=linux ENV CGO_ENABLED=1 GOOS=linux
RUN apk add libpcap-dev g++ perl-utils RUN apk add --no-cache libpcap-dev g++ perl-utils
### Intermediate builder image for x86-64 to x86-64 native builds ### Intermediate builder image for x86-64 to x86-64 native builds
@@ -77,17 +87,16 @@ RUN go build -ldflags="-extldflags=-static -s -w \
-X 'github.com/up9inc/mizu/agent/pkg/version.Ver=${VER}'" -o mizuagent . -X 'github.com/up9inc/mizu/agent/pkg/version.Ver=${VER}'" -o mizuagent .
# Download Basenine executable, verify the sha1sum # Download Basenine executable, verify the sha1sum
ADD https://github.com/up9inc/basenine/releases/download/v0.6.5/basenine_linux_${GOARCH} ./basenine_linux_${GOARCH} ADD https://github.com/up9inc/basenine/releases/download/v0.6.6/basenine_linux_${GOARCH} ./basenine_linux_${GOARCH}
ADD https://github.com/up9inc/basenine/releases/download/v0.6.5/basenine_linux_${GOARCH}.sha256 ./basenine_linux_${GOARCH}.sha256 ADD https://github.com/up9inc/basenine/releases/download/v0.6.6/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
RUN shasum -a 256 -c basenine_linux_"${GOARCH}".sha256 && \
chmod +x ./basenine_linux_"${GOARCH}" && \
mv ./basenine_linux_"${GOARCH}" ./basenine
### The shipped image ### The shipped image
ARG TARGETARCH=amd64 ARG TARGETARCH=amd64
FROM ${TARGETARCH}/busybox:latest FROM ${TARGETARCH}/busybox:latest
# gin-gonic runs in debug mode without this # gin-gonic runs in debug mode without this
ENV GIN_MODE=release ENV GIN_MODE=release

View File

@@ -73,7 +73,7 @@ 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-docker: ## Run clen docker clean-docker: ## Run clean docker
@(echo "DOCKER cleanup - NOT IMPLEMENTED YET " ) @(echo "DOCKER cleanup - NOT IMPLEMENTED YET " )
test-lint: ## Run lint on all modules test-lint: ## Run lint on all modules

View File

@@ -20,7 +20,7 @@ require (
github.com/orcaman/concurrent-map v1.0.0 github.com/orcaman/concurrent-map v1.0.0
github.com/patrickmn/go-cache v2.1.0+incompatible github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/stretchr/testify v1.7.0 github.com/stretchr/testify v1.7.0
github.com/up9inc/basenine/client/go v0.0.0-20220317230530-8472d80307f6 github.com/up9inc/basenine/client/go v0.0.0-20220326121918-785f3061c8ce
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
@@ -37,53 +37,79 @@ require (
require ( require (
cloud.google.com/go/compute v1.2.0 // indirect cloud.google.com/go/compute v1.2.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.24 // indirect github.com/Azure/go-autorest/autorest v0.11.24 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/MakeNowJust/heredoc v1.0.0 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/beevik/etree v1.1.0 // indirect github.com/beevik/etree v1.1.0 // indirect
github.com/bradleyfalzon/tlsx v0.0.0-20170624122154-28fd0e59bac4 // indirect github.com/bradleyfalzon/tlsx v0.0.0-20170624122154-28fd0e59bac4 // indirect
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect
github.com/chanced/dynamic v0.0.0-20211210164248-f8fadb1d735b // indirect github.com/chanced/dynamic v0.0.0-20211210164248-f8fadb1d735b // indirect
github.com/cilium/ebpf v0.8.0 // indirect github.com/cilium/ebpf v0.8.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
github.com/fatih/camelcase v1.0.0 // indirect github.com/fatih/camelcase v1.0.0 // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/fvbommel/sortorder v1.0.2 // indirect
github.com/ghodss/yaml v1.0.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-errors/errors v1.4.2 // indirect github.com/go-errors/errors v1.4.2 // indirect
github.com/go-logr/logr v1.2.2 // indirect github.com/go-logr/logr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.6 // indirect
github.com/go-openapi/swag v0.21.1 // indirect github.com/go-openapi/swag v0.21.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect github.com/golang/snappy v0.0.4 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/google/go-cmp v0.5.7 // indirect github.com/google/go-cmp v0.5.7 // indirect
github.com/google/gofuzz v1.2.0 // indirect github.com/google/gofuzz v1.2.0 // indirect
github.com/google/gopacket v1.1.19 // indirect github.com/google/gopacket v1.1.19 // indirect
github.com/google/martian v2.1.0+incompatible // indirect github.com/google/martian v2.1.0+incompatible // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/googleapis/gnostic v0.5.5 // indirect github.com/googleapis/gnostic v0.5.5 // indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/josharian/intern v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.14.2 // indirect github.com/klauspost/compress v1.14.2 // indirect
github.com/leodido/go-urn v1.2.1 // indirect github.com/leodido/go-urn v1.2.1 // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/mailru/easyjson v0.7.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/moby/spdystream v0.2.0 // indirect
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/ohler55/ojg v1.12.12 // indirect github.com/ohler55/ojg v1.12.12 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/russross/blackfriday v1.6.0 // indirect
github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 // indirect github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 // indirect
github.com/segmentio/kafka-go v0.4.27 // indirect github.com/segmentio/kafka-go v0.4.27 // indirect
github.com/spf13/cobra v1.3.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/tidwall/gjson v1.14.0 // indirect github.com/tidwall/gjson v1.14.0 // indirect
github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect github.com/tidwall/pretty v1.2.0 // indirect
github.com/tidwall/sjson v1.2.4 // indirect github.com/tidwall/sjson v1.2.4 // indirect
github.com/ugorji/go/codec v1.2.6 // indirect github.com/ugorji/go/codec v1.2.6 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
github.com/xlab/treeprint v1.1.0 // indirect
go.starlark.net v0.0.0-20220203230714-bb14e151c28f // indirect
golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab // indirect golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab // indirect
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
@@ -96,10 +122,15 @@ require (
gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
k8s.io/cli-runtime v0.23.3 // indirect
k8s.io/component-base v0.23.3 // indirect
k8s.io/klog/v2 v2.40.1 // indirect k8s.io/klog/v2 v2.40.1 // indirect
k8s.io/kube-openapi v0.0.0-20220124234850-424119656bbf // indirect k8s.io/kube-openapi v0.0.0-20220124234850-424119656bbf // indirect
k8s.io/kubectl v0.23.3 // indirect
k8s.io/utils v0.0.0-20220127004650-9b3446523e65 // indirect k8s.io/utils v0.0.0-20220127004650-9b3446523e65 // indirect
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect
sigs.k8s.io/kustomize/api v0.11.1 // indirect
sigs.k8s.io/kustomize/kyaml v0.13.3 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect sigs.k8s.io/yaml v1.3.0 // indirect
) )

View File

@@ -53,6 +53,7 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
@@ -74,10 +75,13 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
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.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/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=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -111,6 +115,7 @@ github.com/census-instrumentation/opencensus-proto v0.3.0/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/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 h1:7aWHqerlJ41y6FOsEUvknqgXnGmJyJSbjhAWq5pO4F8=
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 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/cmpjson v0.0.0-20210415035445-da9262c1f20a/go.mod h1:yhcmlFk1hxuZ+5XZbupzT/cEm/eE4ZvWbmsW1+Q/aZE=
@@ -147,6 +152,7 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -163,6 +169,7 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/elastic/go-elasticsearch/v7 v7.17.0 h1:0fcSh4qeC/i1+7QU1KXpmq2iUAdMk4l0/vmbtW1+KJM= github.com/elastic/go-elasticsearch/v7 v7.17.0 h1:0fcSh4qeC/i1+7QU1KXpmq2iUAdMk4l0/vmbtW1+KJM=
github.com/elastic/go-elasticsearch/v7 v7.17.0/go.mod h1:OJ4wdbtDNk5g503kvlHLyErCgQwwzmDtaFC4XyOxXA4= github.com/elastic/go-elasticsearch/v7 v7.17.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=
@@ -184,6 +191,7 @@ github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi
github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= 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/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/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4=
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc= github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc=
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= 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=
@@ -201,6 +209,7 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
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/fvbommel/sortorder v1.0.2 h1:mV4o8B2hKboCdkJm+a7uX/SIpZob4JzUpc5GGnM45eo=
github.com/fvbommel/sortorder v1.0.2/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= github.com/fvbommel/sortorder v1.0.2/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=
github.com/getkin/kin-openapi v0.89.0 h1:p4nagHchUKGn85z/f+pse4aSh50nIBOYjOhMIku2hiA= github.com/getkin/kin-openapi v0.89.0 h1:p4nagHchUKGn85z/f+pse4aSh50nIBOYjOhMIku2hiA=
@@ -237,6 +246,7 @@ github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUe
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
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/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs=
github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
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.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
@@ -303,6 +313,7 @@ github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW
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=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -344,6 +355,7 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -363,6 +375,7 @@ github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= 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/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/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=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
@@ -410,7 +423,9 @@ github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47
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/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
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/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
@@ -451,6 +466,7 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
@@ -486,6 +502,7 @@ github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
@@ -493,8 +510,10 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F
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.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.3/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-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A=
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc=
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
@@ -503,10 +522,12 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
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/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
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/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/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/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/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 h1:PaybPUsScX+Cd3TEa1tYpfwU61deCEhMTlCO2hONm1c=
github.com/nav-inc/datetime v0.1.3/go.mod h1:gKGf5G+cW7qkTo5TC/sieNyz6lYdrA9cf1PNV+pXIOE= github.com/nav-inc/datetime v0.1.3/go.mod h1:gKGf5G+cW7qkTo5TC/sieNyz6lYdrA9cf1PNV+pXIOE=
@@ -538,6 +559,7 @@ github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTK
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.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
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/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.6.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM=
@@ -583,6 +605,7 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
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 v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww=
github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY=
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/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -593,6 +616,7 @@ github.com/santhosh-tekuri/jsonschema/v5 v5.0.0/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H
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 h1:sIhEozeL/TLN2mZ5dkG462vcGEWYKS+u31sXPjKhAM4=
github.com/segmentio/kafka-go v0.4.27/go.mod h1:XzMcoMjSzDGHcIwpWUI7GB43iKZ2fTVmryPSGLf/MPg= github.com/segmentio/kafka-go v0.4.27/go.mod h1:XzMcoMjSzDGHcIwpWUI7GB43iKZ2fTVmryPSGLf/MPg=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
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=
@@ -611,6 +635,7 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
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.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
github.com/spf13/cobra v1.3.0 h1:R7cSvGu+Vv+qX0gW5R/85dx2kmmJT5z5NM8ifdYjdn0=
github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4= github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
@@ -624,6 +649,7 @@ github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
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 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
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.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=
@@ -655,8 +681,8 @@ github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn
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/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ= github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
github.com/up9inc/basenine/client/go v0.0.0-20220317230530-8472d80307f6 h1:c0aVbLKYeFDAg246+NDgie2y484bsc20NaKLo8ODV3E= github.com/up9inc/basenine/client/go v0.0.0-20220326121918-785f3061c8ce h1:vMTCpKItc9OyTLJXocNaq2NcBU5EnurJgTVOYb8W8dw=
github.com/up9inc/basenine/client/go v0.0.0-20220317230530-8472d80307f6/go.mod h1:SvJGPoa/6erhUQV7kvHBwM/0x5LyO6XaG2lUaCaKiUI= github.com/up9inc/basenine/client/go v0.0.0-20220326121918-785f3061c8ce/go.mod h1:SvJGPoa/6erhUQV7kvHBwM/0x5LyO6XaG2lUaCaKiUI=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg= github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/wI2L/jsondiff v0.1.1 h1:r2TkoEet7E4JMO5+s1RCY2R0LrNPNHY6hbDeow2hRHw= github.com/wI2L/jsondiff v0.1.1 h1:r2TkoEet7E4JMO5+s1RCY2R0LrNPNHY6hbDeow2hRHw=
@@ -665,6 +691,7 @@ github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhe
github.com/xdg/stringprep v1.0.0/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/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk=
github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
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=
@@ -701,6 +728,7 @@ go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4
go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=
go.starlark.net v0.0.0-20220203230714-bb14e151c28f h1:aW4TkS39/naJa9wPSbIXtZUQOlvuUh8gxCsLRrJoByU=
go.starlark.net v0.0.0-20220203230714-bb14e151c28f/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0= go.starlark.net v0.0.0-20220203230714-bb14e151c28f/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
@@ -1205,6 +1233,7 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C
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=
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -1217,10 +1246,12 @@ k8s.io/api v0.23.3 h1:KNrME8KHGr12Ozjf8ytOewKzZh6hl/hHUZeHddT3a38=
k8s.io/api v0.23.3/go.mod h1:w258XdGyvCmnBj/vGzQMj6kzdufJZVUwEM1U2fRJwSQ= k8s.io/api v0.23.3/go.mod h1:w258XdGyvCmnBj/vGzQMj6kzdufJZVUwEM1U2fRJwSQ=
k8s.io/apimachinery v0.23.3 h1:7IW6jxNzrXTsP0c8yXz2E5Yx/WTzVPTsHIx/2Vm0cIk= k8s.io/apimachinery v0.23.3 h1:7IW6jxNzrXTsP0c8yXz2E5Yx/WTzVPTsHIx/2Vm0cIk=
k8s.io/apimachinery v0.23.3/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM= k8s.io/apimachinery v0.23.3/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM=
k8s.io/cli-runtime v0.23.3 h1:aJiediw+uUbxkfO6BNulcAMTUoU9Om43g3R7rIkYqcw=
k8s.io/cli-runtime v0.23.3/go.mod h1:yA00O5pDqnjkBh8fkuugBbfIfjB1nOpz+aYLotbnOfc= k8s.io/cli-runtime v0.23.3/go.mod h1:yA00O5pDqnjkBh8fkuugBbfIfjB1nOpz+aYLotbnOfc=
k8s.io/client-go v0.23.3 h1:23QYUmCQ/W6hW78xIwm3XqZrrKZM+LWDqW2zfo+szJs= k8s.io/client-go v0.23.3 h1:23QYUmCQ/W6hW78xIwm3XqZrrKZM+LWDqW2zfo+szJs=
k8s.io/client-go v0.23.3/go.mod h1:47oMd+YvAOqZM7pcQ6neJtBiFH7alOyfunYN48VsmwE= k8s.io/client-go v0.23.3/go.mod h1:47oMd+YvAOqZM7pcQ6neJtBiFH7alOyfunYN48VsmwE=
k8s.io/code-generator v0.23.3/go.mod h1:S0Q1JVA+kSzTI1oUvbKAxZY/DYbA/ZUb4Uknog12ETk= k8s.io/code-generator v0.23.3/go.mod h1:S0Q1JVA+kSzTI1oUvbKAxZY/DYbA/ZUb4Uknog12ETk=
k8s.io/component-base v0.23.3 h1:q+epprVdylgecijVGVdf4MbizEL2feW4ssd7cdo6LVY=
k8s.io/component-base v0.23.3/go.mod h1:1Smc4C60rWG7d3HjSYpIwEbySQ3YWg0uzH5a2AtaTLg= k8s.io/component-base v0.23.3/go.mod h1:1Smc4C60rWG7d3HjSYpIwEbySQ3YWg0uzH5a2AtaTLg=
k8s.io/component-helpers v0.23.3/go.mod h1:SH+W/WPTaTenbWyDEeY7iytAQiMh45aqKxkvlqQ57cg= k8s.io/component-helpers v0.23.3/go.mod h1:SH+W/WPTaTenbWyDEeY7iytAQiMh45aqKxkvlqQ57cg=
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
@@ -1234,6 +1265,7 @@ k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2R
k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk=
k8s.io/kube-openapi v0.0.0-20220124234850-424119656bbf h1:M9XBsiMslw2lb2ZzglC0TOkBPK5NQi0/noUrdnoFwUg= k8s.io/kube-openapi v0.0.0-20220124234850-424119656bbf h1:M9XBsiMslw2lb2ZzglC0TOkBPK5NQi0/noUrdnoFwUg=
k8s.io/kube-openapi v0.0.0-20220124234850-424119656bbf/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= k8s.io/kube-openapi v0.0.0-20220124234850-424119656bbf/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk=
k8s.io/kubectl v0.23.3 h1:gJsF7cahkWDPYlNvYKK+OrBZLAJUBzCym+Zsi+dfi1E=
k8s.io/kubectl v0.23.3/go.mod h1:VBeeXNgLhSabu4/k0O7Q0YujgnA3+CLTUE0RcmF73yY= k8s.io/kubectl v0.23.3/go.mod h1:VBeeXNgLhSabu4/k0O7Q0YujgnA3+CLTUE0RcmF73yY=
k8s.io/metrics v0.23.3/go.mod h1:Ut8TvkbsO4oMVeUzaTArvPrcw9QRFLs2XNzUlORjdYE= k8s.io/metrics v0.23.3/go.mod h1:Ut8TvkbsO4oMVeUzaTArvPrcw9QRFLs2XNzUlORjdYE=
k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
@@ -1247,10 +1279,12 @@ sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNza
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 h1:kDi4JBNAsJWfz1aEXhO8Jg87JJaPNLh5tIzYHgStQ9Y= sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 h1:kDi4JBNAsJWfz1aEXhO8Jg87JJaPNLh5tIzYHgStQ9Y=
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY=
sigs.k8s.io/kustomize/api v0.10.1/go.mod h1:2FigT1QN6xKdcnGS2Ppp1uIWrtWN28Ms8A3OZUZhwr8= sigs.k8s.io/kustomize/api v0.10.1/go.mod h1:2FigT1QN6xKdcnGS2Ppp1uIWrtWN28Ms8A3OZUZhwr8=
sigs.k8s.io/kustomize/api v0.11.1 h1:/Vutu+gAqVo8skw1xCZrsZD39SN4Adg+z7FrSTw9pds=
sigs.k8s.io/kustomize/api v0.11.1/go.mod h1:GZuhith5YcqxIDe0GnRJNx5xxPTjlwaLTt/e+ChUtJA= sigs.k8s.io/kustomize/api v0.11.1/go.mod h1:GZuhith5YcqxIDe0GnRJNx5xxPTjlwaLTt/e+ChUtJA=
sigs.k8s.io/kustomize/cmd/config v0.10.2/go.mod h1:K2aW7nXJ0AaT+VA/eO0/dzFLxmpFcTzudmAgDwPY1HQ= sigs.k8s.io/kustomize/cmd/config v0.10.2/go.mod h1:K2aW7nXJ0AaT+VA/eO0/dzFLxmpFcTzudmAgDwPY1HQ=
sigs.k8s.io/kustomize/kustomize/v4 v4.4.1/go.mod h1:qOKJMMz2mBP+vcS7vK+mNz4HBLjaQSWRY22EF6Tb7Io= sigs.k8s.io/kustomize/kustomize/v4 v4.4.1/go.mod h1:qOKJMMz2mBP+vcS7vK+mNz4HBLjaQSWRY22EF6Tb7Io=
sigs.k8s.io/kustomize/kyaml v0.13.0/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E= sigs.k8s.io/kustomize/kyaml v0.13.0/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
sigs.k8s.io/kustomize/kyaml v0.13.3 h1:tNNQIC+8cc+aXFTVg+RtQAOsjwUdYBZRAgYOVI3RBc4=
sigs.k8s.io/kustomize/kyaml v0.13.3/go.mod h1:/ya3Gk4diiQzlE4mBh7wykyLRFZNvqlbh+JnwQ9Vhrc= sigs.k8s.io/kustomize/kyaml v0.13.3/go.mod h1:/ya3Gk4diiQzlE4mBh7wykyLRFZNvqlbh+JnwQ9Vhrc=
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.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y=

View File

@@ -18,6 +18,7 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/up9inc/mizu/agent/pkg/dependency" "github.com/up9inc/mizu/agent/pkg/dependency"
"github.com/up9inc/mizu/agent/pkg/elastic" "github.com/up9inc/mizu/agent/pkg/elastic"
"github.com/up9inc/mizu/agent/pkg/entries"
"github.com/up9inc/mizu/agent/pkg/middlewares" "github.com/up9inc/mizu/agent/pkg/middlewares"
"github.com/up9inc/mizu/agent/pkg/models" "github.com/up9inc/mizu/agent/pkg/models"
"github.com/up9inc/mizu/agent/pkg/oas" "github.com/up9inc/mizu/agent/pkg/oas"
@@ -30,8 +31,6 @@ import (
"github.com/up9inc/mizu/agent/pkg/app" "github.com/up9inc/mizu/agent/pkg/app"
"github.com/up9inc/mizu/agent/pkg/config" "github.com/up9inc/mizu/agent/pkg/config"
v1 "k8s.io/api/core/v1"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/op/go-logging" "github.com/op/go-logging"
"github.com/up9inc/mizu/shared" "github.com/up9inc/mizu/shared"
@@ -155,11 +154,6 @@ func runInTapperMode() {
hostMode := os.Getenv(shared.HostModeEnvVar) == "1" hostMode := os.Getenv(shared.HostModeEnvVar) == "1"
tapOpts := &tap.TapOpts{HostMode: hostMode} 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) filteredOutputItemsChannel := make(chan *tapApi.OutputChannelItem)
@@ -257,28 +251,6 @@ func setUIFlags(uiIndexPath string) error {
return nil return nil
} }
func parseEnvVar(env string) map[string][]v1.Pod {
var mapOfList map[string][]v1.Pod
val, present := os.LookupEnv(env)
if !present {
return mapOfList
}
err := json.Unmarshal([]byte(val), &mapOfList)
if err != nil {
panic(fmt.Sprintf("env var %s's value of %v is invalid! must be map[string][]v1.Pod %v", env, mapOfList, err))
}
return mapOfList
}
func getTapTargets() []v1.Pod {
nodeName := os.Getenv(shared.NodeNameEnvVar)
tappedAddressesPerNodeDict := parseEnvVar(shared.TappedAddressesPerNodeDictEnvVar)
return tappedAddressesPerNodeDict[nodeName]
}
func getTrafficFilteringOptions() *tapApi.TrafficFilteringOptions { func getTrafficFilteringOptions() *tapApi.TrafficFilteringOptions {
filteringOptionsJson := os.Getenv(shared.MizuFilteringOptionsEnvVar) filteringOptionsJson := os.Getenv(shared.MizuFilteringOptionsEnvVar)
if filteringOptionsJson == "" { if filteringOptionsJson == "" {
@@ -381,6 +353,14 @@ func handleIncomingMessageAsTapper(socketConnection *websocket.Conn) {
} else { } else {
tap.UpdateTapTargets(tapConfigMessage.TapTargets) tap.UpdateTapTargets(tapConfigMessage.TapTargets)
} }
case shared.WebSocketMessageTypeUpdateTappedPods:
var tappedPodsMessage shared.WebSocketTappedPodsMessage
if err := json.Unmarshal(message, &tappedPodsMessage); err != nil {
logger.Log.Infof("Could not unmarshal message of message type %s %v", socketMessageBase.MessageType, err)
return
}
nodeName := os.Getenv(shared.NodeNameEnvVar)
tap.UpdateTapTargets(tappedPodsMessage.NodeToTappedPodMap[nodeName])
default: default:
logger.Log.Warningf("Received socket message of type %s for which no handlers are defined", socketMessageBase.MessageType) logger.Log.Warningf("Received socket message of type %s for which no handlers are defined", socketMessageBase.MessageType)
} }
@@ -391,5 +371,8 @@ func handleIncomingMessageAsTapper(socketConnection *websocket.Conn) {
func initializeDependencies() { func initializeDependencies() {
dependency.RegisterGenerator(dependency.ServiceMapGeneratorDependency, func() interface{} { return servicemap.GetDefaultServiceMapInstance() }) dependency.RegisterGenerator(dependency.ServiceMapGeneratorDependency, func() interface{} { return servicemap.GetDefaultServiceMapInstance() })
dependency.RegisterGenerator(dependency.OasGeneratorDependency, func() interface{} { return oas.GetDefaultOasGeneratorInstance() }) dependency.RegisterGenerator(dependency.OasGeneratorDependency, func() interface{} { return oas.GetDefaultOasGeneratorInstance(nil) })
dependency.RegisterGenerator(dependency.EntriesProvider, func() interface{} { return &entries.BasenineEntriesProvider{} })
dependency.RegisterGenerator(dependency.EntriesSocketStreamer, func() interface{} { return &api.BasenineEntryStreamer{} })
dependency.RegisterGenerator(dependency.EntryStreamerSocketConnector, func() interface{} { return &api.DefaultEntryStreamerSocketConnector{} })
} }

View File

@@ -0,0 +1,57 @@
package api
import (
"fmt"
basenine "github.com/up9inc/basenine/client/go"
"github.com/up9inc/mizu/agent/pkg/models"
"github.com/up9inc/mizu/shared/logger"
tapApi "github.com/up9inc/mizu/tap/api"
)
type EntryStreamerSocketConnector interface {
SendEntry(socketId int, entry *tapApi.Entry, params *WebSocketParams)
SendMetadata(socketId int, metadata *basenine.Metadata)
SendToastError(socketId int, err error)
CleanupSocket(socketId int)
}
type DefaultEntryStreamerSocketConnector struct{}
func (e *DefaultEntryStreamerSocketConnector) SendEntry(socketId int, entry *tapApi.Entry, params *WebSocketParams) {
var message []byte
if params.EnableFullEntries {
message, _ = models.CreateFullEntryWebSocketMessage(entry)
} else {
extension := extensionsMap[entry.Protocol.Name]
base := extension.Dissector.Summarize(entry)
message, _ = models.CreateBaseEntryWebSocketMessage(base)
}
if err := SendToSocket(socketId, message); err != nil {
logger.Log.Error(err)
}
}
func (e *DefaultEntryStreamerSocketConnector) SendMetadata(socketId int, metadata *basenine.Metadata) {
metadataBytes, _ := models.CreateWebsocketQueryMetadataMessage(metadata)
if err := SendToSocket(socketId, metadataBytes); err != nil {
logger.Log.Error(err)
}
}
func (e *DefaultEntryStreamerSocketConnector) SendToastError(socketId int, err error) {
toastBytes, _ := models.CreateWebsocketToastMessage(&models.ToastMessage{
Type: "error",
AutoClose: 5000,
Text: fmt.Sprintf("Syntax error: %s", err.Error()),
})
if err := SendToSocket(socketId, toastBytes); err != nil {
logger.Log.Error(err)
}
}
func (e *DefaultEntryStreamerSocketConnector) CleanupSocket(socketId int) {
socketObj := connectedWebsockets[socketId]
socketCleanup(socketId, socketObj)
}

View File

@@ -5,6 +5,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/up9inc/mizu/agent/pkg/models"
"os" "os"
"path" "path"
"sort" "sort"
@@ -19,8 +20,6 @@ import (
"github.com/up9inc/mizu/agent/pkg/servicemap" "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/resolver"
"github.com/up9inc/mizu/agent/pkg/utils" "github.com/up9inc/mizu/agent/pkg/utils"
@@ -140,20 +139,6 @@ func startReadingChannel(outputItems <-chan *tapApi.OutputChannelItem, extension
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,
Destination: mizuEntry.Destination.Name,
Id: mizuEntry.Id,
}
if entryWSource.Destination == "" {
entryWSource.Destination = mizuEntry.Destination.IP + ":" + mizuEntry.Destination.Port
}
oasGenerator := dependency.GetInstance(dependency.OasGeneratorDependency).(oas.OasGeneratorSink)
oasGenerator.PushEntry(&entryWSource)
} }
data, err := json.Marshal(mizuEntry) data, err := json.Marshal(mizuEntry)
@@ -183,6 +168,7 @@ func resolveIP(connectionInfo *tapApi.ConnectionInfo) (resolvedSource string, re
} }
} else { } else {
resolvedSource = resolvedSourceObject.FullAddress resolvedSource = resolvedSourceObject.FullAddress
namespace = resolvedSourceObject.Namespace
} }
unresolvedDestination := fmt.Sprintf("%s:%s", connectionInfo.ServerIP, connectionInfo.ServerPort) unresolvedDestination := fmt.Sprintf("%s:%s", connectionInfo.ServerIP, connectionInfo.ServerPort)
@@ -194,7 +180,11 @@ func resolveIP(connectionInfo *tapApi.ConnectionInfo) (resolvedSource string, re
} }
} else { } else {
resolvedDestination = resolvedDestinationObject.FullAddress resolvedDestination = resolvedDestinationObject.FullAddress
namespace = resolvedDestinationObject.Namespace // Overwrite namespace (if it was set according to the source)
// Only overwrite if non-empty
if resolvedDestinationObject.Namespace != "" {
namespace = resolvedDestinationObject.Namespace
}
} }
} }
return resolvedSource, resolvedDestination, namespace return resolvedSource, resolvedDestination, namespace

View File

@@ -0,0 +1,92 @@
package api
import (
"context"
"encoding/json"
basenine "github.com/up9inc/basenine/client/go"
"github.com/up9inc/mizu/agent/pkg/dependency"
"github.com/up9inc/mizu/shared"
"github.com/up9inc/mizu/shared/logger"
tapApi "github.com/up9inc/mizu/tap/api"
)
type EntryStreamer interface {
Get(ctx context.Context, socketId int, params *WebSocketParams) error
}
type BasenineEntryStreamer struct{}
func (e *BasenineEntryStreamer) Get(ctx context.Context, socketId int, params *WebSocketParams) error {
var connection *basenine.Connection
entryStreamerSocketConnector := dependency.GetInstance(dependency.EntryStreamerSocketConnector).(EntryStreamerSocketConnector)
connection, err := basenine.NewConnection(shared.BasenineHost, shared.BaseninePort)
if err != nil {
logger.Log.Errorf("failed to establish a connection to Basenine: %v", err)
entryStreamerSocketConnector.CleanupSocket(socketId)
return err
}
data := make(chan []byte)
meta := make(chan []byte)
query := params.Query
err = basenine.Validate(shared.BasenineHost, shared.BaseninePort, query)
if err != nil {
entryStreamerSocketConnector.SendToastError(socketId, err)
}
handleDataChannel := func(c *basenine.Connection, data chan []byte) {
for {
bytes := <-data
if string(bytes) == basenine.CloseChannel {
return
}
var entry *tapApi.Entry
err = json.Unmarshal(bytes, &entry)
if err != nil {
logger.Log.Debugf("error unmarshalling entry: %v", err.Error())
continue
}
entryStreamerSocketConnector.SendEntry(socketId, entry, params)
}
}
handleMetaChannel := func(c *basenine.Connection, meta chan []byte) {
for {
bytes := <-meta
if string(bytes) == basenine.CloseChannel {
return
}
var metadata *basenine.Metadata
err = json.Unmarshal(bytes, &metadata)
if err != nil {
logger.Log.Debugf("Error unmarshalling metadata: %v", err.Error())
continue
}
entryStreamerSocketConnector.SendMetadata(socketId, metadata)
}
}
go handleDataChannel(connection, data)
go handleMetaChannel(connection, meta)
connection.Query(query, data, meta)
go func() {
<-ctx.Done()
data <- []byte(basenine.CloseChannel)
meta <- []byte(basenine.CloseChannel)
connection.Close()
}()
return nil
}

View File

@@ -1,19 +1,15 @@
package api package api
import ( import (
"encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"sync" "sync"
"time" "time"
"github.com/up9inc/mizu/agent/pkg/models"
"github.com/up9inc/mizu/agent/pkg/utils"
"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" "github.com/up9inc/mizu/agent/pkg/models"
"github.com/up9inc/mizu/shared" "github.com/up9inc/mizu/agent/pkg/utils"
"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"
) )
@@ -25,9 +21,9 @@ func InitExtensionsMap(ref map[string]*tapApi.Extension) {
} }
type EventHandlers interface { type EventHandlers interface {
WebSocketConnect(socketId int, isTapper bool) WebSocketConnect(c *gin.Context, socketId int, isTapper bool)
WebSocketDisconnect(socketId int, isTapper bool) WebSocketDisconnect(socketId int, isTapper bool)
WebSocketMessage(socketId int, message []byte) WebSocketMessage(socketId int, isTapper bool, message []byte)
} }
type SocketConnection struct { type SocketConnection struct {
@@ -62,11 +58,11 @@ func init() {
func WebSocketRoutes(app *gin.Engine, eventHandlers EventHandlers) { func WebSocketRoutes(app *gin.Engine, eventHandlers EventHandlers) {
SocketGetBrowserHandler = func(c *gin.Context) { SocketGetBrowserHandler = func(c *gin.Context) {
websocketHandler(c.Writer, c.Request, eventHandlers, false) websocketHandler(c, eventHandlers, false)
} }
SocketGetTapperHandler = func(c *gin.Context) { SocketGetTapperHandler = func(c *gin.Context) {
websocketHandler(c.Writer, c.Request, eventHandlers, true) websocketHandler(c, eventHandlers, true)
} }
app.GET("/ws", func(c *gin.Context) { app.GET("/ws", func(c *gin.Context) {
@@ -78,10 +74,10 @@ func WebSocketRoutes(app *gin.Engine, eventHandlers EventHandlers) {
}) })
} }
func websocketHandler(w http.ResponseWriter, r *http.Request, eventHandlers EventHandlers, isTapper bool) { func websocketHandler(c *gin.Context, eventHandlers EventHandlers, isTapper bool) {
ws, err := websocketUpgrader.Upgrade(w, r, nil) ws, err := websocketUpgrader.Upgrade(c.Writer, c.Request, nil)
if err != nil { if err != nil {
logger.Log.Errorf("Failed to set websocket upgrade: %v", err) logger.Log.Errorf("failed to set websocket upgrade: %v", err)
return return
} }
@@ -93,30 +89,11 @@ func websocketHandler(w http.ResponseWriter, r *http.Request, eventHandlers Even
websocketIdsLock.Unlock() websocketIdsLock.Unlock()
var connection *basenine.Connection
var isQuerySet bool
// `!isTapper` means it's a connection from the web UI
if !isTapper {
connection, err = basenine.NewConnection(shared.BasenineHost, shared.BaseninePort)
if err != nil {
logger.Log.Errorf("Failed to establish a connection to Basenine: %v", err)
socketCleanup(socketId, connectedWebsockets[socketId])
return
}
}
data := make(chan []byte)
meta := make(chan []byte)
defer func() { defer func() {
socketCleanup(socketId, connectedWebsockets[socketId]) socketCleanup(socketId, connectedWebsockets[socketId])
data <- []byte(basenine.CloseChannel)
meta <- []byte(basenine.CloseChannel)
connection.Close()
}() }()
eventHandlers.WebSocketConnect(socketId, isTapper) eventHandlers.WebSocketConnect(c, socketId, isTapper)
startTimeBytes, _ := models.CreateWebsocketStartTimeMessage(utils.StartTime) startTimeBytes, _ := models.CreateWebsocketStartTimeMessage(utils.StartTime)
@@ -124,127 +101,32 @@ func websocketHandler(w http.ResponseWriter, r *http.Request, eventHandlers Even
logger.Log.Error(err) logger.Log.Error(err)
} }
var params WebSocketParams
for { for {
_, msg, err := ws.ReadMessage() _, msg, err := ws.ReadMessage()
if err != nil { if err != nil {
if _, ok := err.(*websocket.CloseError); ok { if _, ok := err.(*websocket.CloseError); ok {
logger.Log.Debugf("Received websocket close message, socket id: %d", socketId) logger.Log.Debugf("received websocket close message, socket id: %d", socketId)
} else { } else {
logger.Log.Errorf("Error reading message, socket id: %d, error: %v", socketId, err) logger.Log.Errorf("error reading message, socket id: %d, error: %v", socketId, err)
} }
break break
} }
if !isTapper && !isQuerySet { eventHandlers.WebSocketMessage(socketId, isTapper, msg)
if err := json.Unmarshal(msg, &params); err != nil {
logger.Log.Errorf("Error unmarshalling parameters: %v", socketId, err)
continue
}
query := params.Query
err = basenine.Validate(shared.BasenineHost, shared.BaseninePort, query)
if err != nil {
toastBytes, _ := models.CreateWebsocketToastMessage(&models.ToastMessage{
Type: "error",
AutoClose: 5000,
Text: fmt.Sprintf("Syntax error: %s", err.Error()),
})
if err := SendToSocket(socketId, toastBytes); err != nil {
logger.Log.Error(err)
}
break
}
isQuerySet = true
handleDataChannel := func(c *basenine.Connection, data chan []byte) {
for {
bytes := <-data
if string(bytes) == basenine.CloseChannel {
return
}
var entry *tapApi.Entry
err = json.Unmarshal(bytes, &entry)
if err != nil {
logger.Log.Debugf("Error unmarshalling entry: %v", err.Error())
continue
}
var message []byte
if params.EnableFullEntries {
message, _ = models.CreateFullEntryWebSocketMessage(entry)
} else {
extension := extensionsMap[entry.Protocol.Name]
base := extension.Dissector.Summarize(entry)
message, _ = models.CreateBaseEntryWebSocketMessage(base)
}
if err := SendToSocket(socketId, message); err != nil {
logger.Log.Error(err)
}
}
}
handleMetaChannel := func(c *basenine.Connection, meta chan []byte) {
for {
bytes := <-meta
if string(bytes) == basenine.CloseChannel {
return
}
var metadata *basenine.Metadata
err = json.Unmarshal(bytes, &metadata)
if err != nil {
logger.Log.Debugf("Error unmarshalling metadata: %v", err.Error())
continue
}
metadataBytes, _ := models.CreateWebsocketQueryMetadataMessage(metadata)
if err := SendToSocket(socketId, metadataBytes); err != nil {
logger.Log.Error(err)
}
}
}
go handleDataChannel(connection, data)
go handleMetaChannel(connection, meta)
connection.Query(query, data, meta)
} else {
eventHandlers.WebSocketMessage(socketId, msg)
}
} }
} }
func socketCleanup(socketId int, socketConnection *SocketConnection) {
err := socketConnection.connection.Close()
if err != nil {
logger.Log.Errorf("Error closing socket connection for socket id %d: %v", socketId, err)
}
websocketIdsLock.Lock()
connectedWebsockets[socketId] = nil
websocketIdsLock.Unlock()
socketConnection.eventHandlers.WebSocketDisconnect(socketId, socketConnection.isTapper)
}
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 fmt.Errorf("Socket %v is disconnected", socketId) return fmt.Errorf("socket %v is disconnected", socketId)
} }
var sent = false var sent = false
time.AfterFunc(time.Second*5, func() { time.AfterFunc(time.Second*5, func() {
if !sent { if !sent {
logger.Log.Error("Socket timed out") logger.Log.Error("socket timed out")
socketCleanup(socketId, socketObj) socketCleanup(socketId, socketObj)
} }
}) })
@@ -255,7 +137,20 @@ func SendToSocket(socketId int, message []byte) error {
sent = true sent = true
if err != nil { if err != nil {
return fmt.Errorf("Failed to write message to socket %v, err: %w", socketId, err) return fmt.Errorf("failed to write message to socket %v, err: %w", socketId, err)
} }
return nil return nil
} }
func socketCleanup(socketId int, socketConnection *SocketConnection) {
err := socketConnection.connection.Close()
if err != nil {
logger.Log.Errorf("error closing socket connection for socket id %d: %v", socketId, err)
}
websocketIdsLock.Lock()
connectedWebsockets[socketId] = nil
websocketIdsLock.Unlock()
socketConnection.eventHandlers.WebSocketDisconnect(socketId, socketConnection.isTapper)
}

View File

@@ -1,12 +1,14 @@
package api package api
import ( import (
"context"
"encoding/json" "encoding/json"
"fmt"
"sync" "sync"
"github.com/gin-gonic/gin"
"github.com/up9inc/mizu/agent/pkg/dependency"
"github.com/up9inc/mizu/agent/pkg/models" "github.com/up9inc/mizu/agent/pkg/models"
"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/providers/tappers"
"github.com/up9inc/mizu/agent/pkg/up9" "github.com/up9inc/mizu/agent/pkg/up9"
@@ -16,7 +18,12 @@ import (
"github.com/up9inc/mizu/shared/logger" "github.com/up9inc/mizu/shared/logger"
) )
var browserClientSocketUUIDs = make([]int, 0) type BrowserClient struct {
dataStreamCancelFunc context.CancelFunc
}
var browserClients = make(map[int]*BrowserClient, 0)
var tapperClientSocketUUIDs = make([]int, 0)
var socketListLock = sync.Mutex{} var socketListLock = sync.Mutex{}
type RoutesEventHandlers struct { type RoutesEventHandlers struct {
@@ -28,15 +35,22 @@ func init() {
go up9.UpdateAnalyzeStatus(BroadcastToBrowserClients) go up9.UpdateAnalyzeStatus(BroadcastToBrowserClients)
} }
func (h *RoutesEventHandlers) WebSocketConnect(socketId int, isTapper bool) { func (h *RoutesEventHandlers) WebSocketConnect(_ *gin.Context, 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)
tappers.Connected() tappers.Connected()
socketListLock.Lock()
tapperClientSocketUUIDs = append(tapperClientSocketUUIDs, socketId)
socketListLock.Unlock()
nodeToTappedPodMap := tappedPods.GetNodeToTappedPodMap()
SendTappedPods(socketId, nodeToTappedPodMap)
} 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()
browserClientSocketUUIDs = append(browserClientSocketUUIDs, socketId) browserClients[socketId] = &BrowserClient{}
socketListLock.Unlock() socketListLock.Unlock()
BroadcastTappedPodsStatus() BroadcastTappedPodsStatus()
@@ -47,16 +61,23 @@ 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)
tappers.Disconnected() tappers.Disconnected()
socketListLock.Lock()
removeSocketUUIDFromTapperSlice(socketId)
socketListLock.Unlock()
} 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()
removeSocketUUIDFromBrowserSlice(socketId) if browserClients[socketId] != nil && browserClients[socketId].dataStreamCancelFunc != nil {
browserClients[socketId].dataStreamCancelFunc()
}
delete(browserClients, socketId)
socketListLock.Unlock() socketListLock.Unlock()
} }
} }
func BroadcastToBrowserClients(message []byte) { func BroadcastToBrowserClients(message []byte) {
for _, socketId := range browserClientSocketUUIDs { for socketId := range browserClients {
go func(socketId int) { go func(socketId int) {
if err := SendToSocket(socketId, message); err != nil { if err := SendToSocket(socketId, message); err != nil {
logger.Log.Error(err) logger.Log.Error(err)
@@ -65,7 +86,43 @@ func BroadcastToBrowserClients(message []byte) {
} }
} }
func (h *RoutesEventHandlers) WebSocketMessage(_ int, message []byte) { func BroadcastToTapperClients(message []byte) {
for _, socketId := range tapperClientSocketUUIDs {
go func(socketId int) {
if err := SendToSocket(socketId, message); err != nil {
logger.Log.Error(err)
}
}(socketId)
}
}
func (h *RoutesEventHandlers) WebSocketMessage(socketId int, isTapper bool, message []byte) {
if isTapper {
HandleTapperIncomingMessage(message, h.SocketOutChannel, BroadcastToBrowserClients)
} else {
// we initiate the basenine stream after the first websocket message we receive (it contains the entry query), we then store a cancelfunc to later cancel this stream
if browserClients[socketId] != nil && browserClients[socketId].dataStreamCancelFunc == nil {
var params WebSocketParams
if err := json.Unmarshal(message, &params); err != nil {
logger.Log.Errorf("Error: %v", socketId, err)
return
}
entriesStreamer := dependency.GetInstance(dependency.EntriesSocketStreamer).(EntryStreamer)
ctx, cancelFunc := context.WithCancel(context.Background())
err := entriesStreamer.Get(ctx, socketId, &params)
if err != nil {
logger.Log.Errorf("error initializing basenine stream for browser socket %d %+v", socketId, err)
cancelFunc()
} else {
browserClients[socketId].dataStreamCancelFunc = cancelFunc
}
}
}
}
func HandleTapperIncomingMessage(message []byte, socketOutChannel chan<- *tapApi.OutputChannelItem, broadcastMessageFunc func([]byte)) {
var socketMessageBase shared.WebSocketMessageMetadata var socketMessageBase shared.WebSocketMessageMetadata
err := json.Unmarshal(message, &socketMessageBase) err := json.Unmarshal(message, &socketMessageBase)
if err != nil { if err != nil {
@@ -79,7 +136,7 @@ func (h *RoutesEventHandlers) WebSocketMessage(_ int, message []byte) {
logger.Log.Infof("Could not unmarshal message of message type %s %v", socketMessageBase.MessageType, err) logger.Log.Infof("Could not unmarshal message of message type %s %v", socketMessageBase.MessageType, err)
} else { } else {
// NOTE: This is where the message comes back from the intermediate WebSocket to code. // NOTE: This is where the message comes back from the intermediate WebSocket to code.
h.SocketOutChannel <- tappedEntryMessage.Data socketOutChannel <- tappedEntryMessage.Data
} }
case shared.WebSocketMessageTypeUpdateStatus: case shared.WebSocketMessageTypeUpdateStatus:
var statusMessage shared.WebSocketStatusMessage var statusMessage shared.WebSocketStatusMessage
@@ -87,15 +144,7 @@ func (h *RoutesEventHandlers) WebSocketMessage(_ int, message []byte) {
if err != nil { if err != nil {
logger.Log.Infof("Could not unmarshal message of message type %s %v", socketMessageBase.MessageType, err) logger.Log.Infof("Could not unmarshal message of message type %s %v", socketMessageBase.MessageType, err)
} else { } else {
BroadcastToBrowserClients(message) broadcastMessageFunc(message)
}
case shared.WebsocketMessageTypeOutboundLink:
var outboundLinkMessage models.WebsocketOutboundLinkMessage
err := json.Unmarshal(message, &outboundLinkMessage)
if err != nil {
logger.Log.Infof("Could not unmarshal message of message type %s %v", socketMessageBase.MessageType, err)
} else {
handleTLSLink(outboundLinkMessage)
} }
default: default:
logger.Log.Infof("Received socket message of type %s for which no handlers are defined", socketMessageBase.MessageType) logger.Log.Infof("Received socket message of type %s for which no handlers are defined", socketMessageBase.MessageType)
@@ -103,35 +152,12 @@ func (h *RoutesEventHandlers) WebSocketMessage(_ int, message []byte) {
} }
} }
func handleTLSLink(outboundLinkMessage models.WebsocketOutboundLinkMessage) { func removeSocketUUIDFromTapperSlice(uuidToRemove int) {
resolvedNameObject := k8sResolver.Resolve(outboundLinkMessage.Data.DstIP) newUUIDSlice := make([]int, 0, len(tapperClientSocketUUIDs))
if resolvedNameObject != nil { for _, uuid := range tapperClientSocketUUIDs {
outboundLinkMessage.Data.DstIP = resolvedNameObject.FullAddress
} else if outboundLinkMessage.Data.SuggestedResolvedName != "" {
outboundLinkMessage.Data.DstIP = outboundLinkMessage.Data.SuggestedResolvedName
}
cacheKey := fmt.Sprintf("%s -> %s:%d", outboundLinkMessage.Data.Src, outboundLinkMessage.Data.DstIP, outboundLinkMessage.Data.DstPort)
_, isInCache := providers.RecentTLSLinks.Get(cacheKey)
if isInCache {
return
} else {
providers.RecentTLSLinks.SetDefault(cacheKey, outboundLinkMessage.Data)
}
marshaledMessage, err := json.Marshal(outboundLinkMessage)
if err != nil {
logger.Log.Errorf("Error marshaling outbound link message for broadcasting: %v", err)
} else {
logger.Log.Errorf("Broadcasting outboundlink message %s", string(marshaledMessage))
BroadcastToBrowserClients(marshaledMessage)
}
}
func removeSocketUUIDFromBrowserSlice(uuidToRemove int) {
newUUIDSlice := make([]int, 0, len(browserClientSocketUUIDs))
for _, uuid := range browserClientSocketUUIDs {
if uuid != uuidToRemove { if uuid != uuidToRemove {
newUUIDSlice = append(newUUIDSlice, uuid) newUUIDSlice = append(newUUIDSlice, uuid)
} }
} }
browserClientSocketUUIDs = newUUIDSlice tapperClientSocketUUIDs = newUUIDSlice
} }

View File

@@ -18,3 +18,23 @@ func BroadcastTappedPodsStatus() {
BroadcastToBrowserClients(jsonBytes) BroadcastToBrowserClients(jsonBytes)
} }
} }
func SendTappedPods(socketId int, nodeToTappedPodMap shared.NodeToPodsMap) {
message := shared.CreateWebSocketTappedPodsMessage(nodeToTappedPodMap)
if jsonBytes, err := json.Marshal(message); err != nil {
logger.Log.Errorf("Could not Marshal message %v", err)
} else {
if err := SendToSocket(socketId, jsonBytes); err != nil {
logger.Log.Error(err)
}
}
}
func BroadcastTappedPodsToTappers(nodeToTappedPodMap shared.NodeToPodsMap) {
message := shared.CreateWebSocketTappedPodsMessage(nodeToTappedPodMap)
if jsonBytes, err := json.Marshal(message); err != nil {
logger.Log.Errorf("Could not Marshal message %v", err)
} else {
BroadcastToTapperClients(jsonBytes)
}
}

View File

@@ -1,25 +1,20 @@
package controllers package controllers
import ( import (
"encoding/json"
"net/http" "net/http"
"strconv" "strconv"
"time"
"github.com/up9inc/mizu/agent/pkg/app" "github.com/up9inc/mizu/agent/pkg/dependency"
"github.com/up9inc/mizu/agent/pkg/har" "github.com/up9inc/mizu/agent/pkg/entries"
"github.com/up9inc/mizu/agent/pkg/models" "github.com/up9inc/mizu/agent/pkg/models"
"github.com/up9inc/mizu/agent/pkg/validation" "github.com/up9inc/mizu/agent/pkg/validation"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
basenine "github.com/up9inc/basenine/client/go"
"github.com/up9inc/mizu/shared"
"github.com/up9inc/mizu/shared/logger" "github.com/up9inc/mizu/shared/logger"
tapApi "github.com/up9inc/mizu/tap/api"
) )
func Error(c *gin.Context, err error) bool { func HandleEntriesError(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)
@@ -49,45 +44,18 @@ func GetEntries(c *gin.Context) {
entriesRequest.TimeoutMs = 3000 entriesRequest.TimeoutMs = 3000
} }
data, meta, err := basenine.Fetch(shared.BasenineHost, shared.BaseninePort, entriesProvider := dependency.GetInstance(dependency.EntriesProvider).(entries.EntriesProvider)
entriesRequest.LeftOff, entriesRequest.Direction, entriesRequest.Query, entries, metadata, err := entriesProvider.GetEntries(entriesRequest)
entriesRequest.Limit, time.Duration(entriesRequest.TimeoutMs)*time.Millisecond) if !HandleEntriesError(c, err) {
if err != nil { baseEntries := make([]interface{}, 0)
c.JSON(http.StatusInternalServerError, validationError) for _, entry := range entries {
} baseEntries = append(baseEntries, entry.Base)
response := &models.EntriesResponse{}
var dataSlice []interface{}
for _, row := range data {
var entry *tapApi.Entry
err = json.Unmarshal(row, &entry)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": true,
"type": "error",
"autoClose": "5000",
"msg": string(row),
})
return // exit
} }
c.JSON(http.StatusOK, models.EntriesResponse{
extension := app.ExtensionsMap[entry.Protocol.Name] Data: baseEntries,
base := extension.Dissector.Summarize(entry) Meta: metadata,
})
dataSlice = append(dataSlice, base)
} }
var metadata *basenine.Metadata
err = json.Unmarshal(meta, &metadata)
if err != nil {
logger.Log.Debugf("Error recieving metadata: %v", err.Error())
}
response.Data = dataSlice
response.Meta = metadata
c.JSON(http.StatusOK, response)
} }
func GetEntry(c *gin.Context) { func GetEntry(c *gin.Context) {
@@ -102,54 +70,11 @@ func GetEntry(c *gin.Context) {
} }
id, _ := strconv.Atoi(c.Param("id")) id, _ := strconv.Atoi(c.Param("id"))
var entry *tapApi.Entry
bytes, err := basenine.Single(shared.BasenineHost, shared.BaseninePort, id, singleEntryRequest.Query)
if Error(c, err) {
return // exit
}
err = json.Unmarshal(bytes, &entry)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{
"error": true,
"type": "error",
"autoClose": "5000",
"msg": string(bytes),
})
return // exit
}
extension := app.ExtensionsMap[entry.Protocol.Name] entriesProvider := dependency.GetInstance(dependency.EntriesProvider).(entries.EntriesProvider)
base := extension.Dissector.Summarize(entry) entry, err := entriesProvider.GetEntry(singleEntryRequest, id)
var representation []byte
representation, err = extension.Dissector.Represent(entry.Request, entry.Response)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{
"error": true,
"type": "error",
"autoClose": "5000",
"msg": err.Error(),
})
return // exit
}
var rules []map[string]interface{} if !HandleEntriesError(c, err) {
var isRulesEnabled bool c.JSON(http.StatusOK, entry)
if entry.Protocol.Name == "http" {
harEntry, _ := har.NewEntry(entry.Request, entry.Response, entry.StartTime, entry.ElapsedTime)
_, rulesMatched, _isRulesEnabled := models.RunValidationRulesState(*harEntry, entry.Destination.Name)
isRulesEnabled = _isRulesEnabled
inrec, _ := json.Marshal(rulesMatched)
if err := json.Unmarshal(inrec, &rules); err != nil {
logger.Log.Error(err)
}
} }
c.JSON(http.StatusOK, tapApi.EntryWrapper{
Protocol: entry.Protocol,
Representation: string(representation),
Data: entry,
Base: base,
Rules: rules,
IsRulesEnabled: isRulesEnabled,
})
} }

View File

@@ -1,8 +1,12 @@
package controllers package controllers
import ( import (
"bytes"
basenine "github.com/up9inc/basenine/client/go"
"net"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
"time"
"github.com/up9inc/mizu/agent/pkg/dependency" "github.com/up9inc/mizu/agent/pkg/dependency"
"github.com/up9inc/mizu/agent/pkg/oas" "github.com/up9inc/mizu/agent/pkg/oas"
@@ -11,39 +15,55 @@ import (
) )
func TestGetOASServers(t *testing.T) { func TestGetOASServers(t *testing.T) {
dependency.RegisterGenerator(dependency.OasGeneratorDependency, func() interface{} { return oas.GetDefaultOasGeneratorInstance() }) recorder, c := getRecorderAndContext()
recorder := httptest.NewRecorder()
c, _ := gin.CreateTestContext(recorder)
oas.GetDefaultOasGeneratorInstance().Start()
oas.GetDefaultOasGeneratorInstance().GetServiceSpecs().Store("some", oas.NewGen("some"))
GetOASServers(c) GetOASServers(c)
t.Logf("Written body: %s", recorder.Body.String()) t.Logf("Written body: %s", recorder.Body.String())
} }
func TestGetOASAllSpecs(t *testing.T) { func TestGetOASAllSpecs(t *testing.T) {
dependency.RegisterGenerator(dependency.OasGeneratorDependency, func() interface{} { return oas.GetDefaultOasGeneratorInstance() }) recorder, c := getRecorderAndContext()
recorder := httptest.NewRecorder()
c, _ := gin.CreateTestContext(recorder)
oas.GetDefaultOasGeneratorInstance().Start()
oas.GetDefaultOasGeneratorInstance().GetServiceSpecs().Store("some", oas.NewGen("some"))
GetOASAllSpecs(c) GetOASAllSpecs(c)
t.Logf("Written body: %s", recorder.Body.String()) t.Logf("Written body: %s", recorder.Body.String())
} }
func TestGetOASSpec(t *testing.T) { func TestGetOASSpec(t *testing.T) {
dependency.RegisterGenerator(dependency.OasGeneratorDependency, func() interface{} { return oas.GetDefaultOasGeneratorInstance() }) recorder, c := getRecorderAndContext()
recorder := httptest.NewRecorder()
c, _ := gin.CreateTestContext(recorder)
oas.GetDefaultOasGeneratorInstance().Start()
oas.GetDefaultOasGeneratorInstance().GetServiceSpecs().Store("some", oas.NewGen("some"))
c.Params = []gin.Param{{Key: "id", Value: "some"}} c.Params = []gin.Param{{Key: "id", Value: "some"}}
GetOASSpec(c) GetOASSpec(c)
t.Logf("Written body: %s", recorder.Body.String()) t.Logf("Written body: %s", recorder.Body.String())
} }
type fakeConn struct {
sendBuffer *bytes.Buffer
receiveBuffer *bytes.Buffer
}
func (f fakeConn) Read(p []byte) (int, error) { return f.sendBuffer.Read(p) }
func (f fakeConn) Write(p []byte) (int, error) { return f.receiveBuffer.Write(p) }
func (fakeConn) Close() error { return nil }
func (fakeConn) LocalAddr() net.Addr { return nil }
func (fakeConn) RemoteAddr() net.Addr { return nil }
func (fakeConn) SetDeadline(t time.Time) error { return nil }
func (fakeConn) SetReadDeadline(t time.Time) error { return nil }
func (fakeConn) SetWriteDeadline(t time.Time) error { return nil }
func getRecorderAndContext() (*httptest.ResponseRecorder, *gin.Context) {
dummyConn := new(basenine.Connection)
dummyConn.Conn = fakeConn{
sendBuffer: bytes.NewBufferString("\n"),
receiveBuffer: bytes.NewBufferString("\n"),
}
dependency.RegisterGenerator(dependency.OasGeneratorDependency, func() interface{} {
return oas.GetDefaultOasGeneratorInstance(dummyConn)
})
recorder := httptest.NewRecorder()
c, _ := gin.CreateTestContext(recorder)
oas.GetDefaultOasGeneratorInstance(dummyConn).Start()
oas.GetDefaultOasGeneratorInstance(dummyConn).GetServiceSpecs().Store("some", oas.NewGen("some"))
return recorder, c
}

View File

@@ -101,16 +101,18 @@ func (s *ServiceMapControllerSuite) TestGet() {
// response nodes // response nodes
aNode := servicemap.ServiceMapNode{ aNode := servicemap.ServiceMapNode{
Id: 1, Id: 1,
Name: TCPEntryA.Name, Name: TCPEntryA.Name,
Entry: TCPEntryA, Entry: TCPEntryA,
Count: 1, Resolved: true,
Count: 1,
} }
bNode := servicemap.ServiceMapNode{ bNode := servicemap.ServiceMapNode{
Id: 2, Id: 2,
Name: TCPEntryB.Name, Name: TCPEntryB.Name,
Entry: TCPEntryB, Entry: TCPEntryB,
Count: 1, Resolved: true,
Count: 1,
} }
assert.Contains(response.Nodes, aNode) assert.Contains(response.Nodes, aNode)
assert.Contains(response.Nodes, bNode) assert.Contains(response.Nodes, bNode)

View File

@@ -2,6 +2,7 @@ package controllers
import ( import (
"net/http" "net/http"
core "k8s.io/api/core/v1"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/up9inc/mizu/agent/pkg/api" "github.com/up9inc/mizu/agent/pkg/api"
@@ -12,6 +13,7 @@ import (
"github.com/up9inc/mizu/agent/pkg/up9" "github.com/up9inc/mizu/agent/pkg/up9"
"github.com/up9inc/mizu/agent/pkg/validation" "github.com/up9inc/mizu/agent/pkg/validation"
"github.com/up9inc/mizu/shared" "github.com/up9inc/mizu/shared"
"github.com/up9inc/mizu/shared/kubernetes"
"github.com/up9inc/mizu/shared/logger" "github.com/up9inc/mizu/shared/logger"
) )
@@ -30,15 +32,21 @@ func HealthCheck(c *gin.Context) {
} }
func PostTappedPods(c *gin.Context) { func PostTappedPods(c *gin.Context) {
var requestTappedPods []*shared.PodInfo var requestTappedPods []core.Pod
if err := c.Bind(&requestTappedPods); err != nil { if err := c.Bind(&requestTappedPods); err != nil {
c.JSON(http.StatusBadRequest, err) c.JSON(http.StatusBadRequest, err)
return return
} }
podInfos := kubernetes.GetPodInfosForPods(requestTappedPods)
logger.Log.Infof("[Status] POST request: %d tapped pods", len(requestTappedPods)) logger.Log.Infof("[Status] POST request: %d tapped pods", len(requestTappedPods))
tappedPods.Set(requestTappedPods) tappedPods.Set(podInfos)
api.BroadcastTappedPodsStatus() api.BroadcastTappedPodsStatus()
nodeToTappedPodMap := kubernetes.GetNodeHostToTappedPodsMap(requestTappedPods)
tappedPods.SetNodeToTappedPodMap(nodeToTappedPodMap)
api.BroadcastTappedPodsToTappers(nodeToTappedPodMap)
} }
func PostTapperStatus(c *gin.Context) { func PostTapperStatus(c *gin.Context) {

View File

@@ -5,4 +5,7 @@ type DependencyContainerType string
const ( const (
ServiceMapGeneratorDependency = "ServiceMapGeneratorDependency" ServiceMapGeneratorDependency = "ServiceMapGeneratorDependency"
OasGeneratorDependency = "OasGeneratorDependency" OasGeneratorDependency = "OasGeneratorDependency"
EntriesProvider = "EntriesProvider"
EntriesSocketStreamer = "EntriesSocketStreamer"
EntryStreamerSocketConnector = "EntryStreamerSocketConnector"
) )

View File

@@ -0,0 +1,98 @@
package entries
import (
"encoding/json"
"time"
basenine "github.com/up9inc/basenine/client/go"
"github.com/up9inc/mizu/agent/pkg/app"
"github.com/up9inc/mizu/agent/pkg/har"
"github.com/up9inc/mizu/agent/pkg/models"
"github.com/up9inc/mizu/shared"
"github.com/up9inc/mizu/shared/logger"
tapApi "github.com/up9inc/mizu/tap/api"
)
type EntriesProvider interface {
GetEntries(entriesRequest *models.EntriesRequest) ([]*tapApi.EntryWrapper, *basenine.Metadata, error)
GetEntry(singleEntryRequest *models.SingleEntryRequest, entryId int) (*tapApi.EntryWrapper, error)
}
type BasenineEntriesProvider struct{}
func (e *BasenineEntriesProvider) GetEntries(entriesRequest *models.EntriesRequest) ([]*tapApi.EntryWrapper, *basenine.Metadata, error) {
data, meta, err := basenine.Fetch(shared.BasenineHost, shared.BaseninePort,
entriesRequest.LeftOff, entriesRequest.Direction, entriesRequest.Query,
entriesRequest.Limit, time.Duration(entriesRequest.TimeoutMs)*time.Millisecond)
if err != nil {
return nil, nil, err
}
var dataSlice []*tapApi.EntryWrapper
for _, row := range data {
var entry *tapApi.Entry
err = json.Unmarshal(row, &entry)
if err != nil {
return nil, nil, err
}
extension := app.ExtensionsMap[entry.Protocol.Name]
base := extension.Dissector.Summarize(entry)
dataSlice = append(dataSlice, &tapApi.EntryWrapper{
Protocol: entry.Protocol,
Data: entry,
Base: base,
})
}
var metadata *basenine.Metadata
err = json.Unmarshal(meta, &metadata)
if err != nil {
logger.Log.Debugf("Error recieving metadata: %v", err.Error())
}
return dataSlice, metadata, nil
}
func (e *BasenineEntriesProvider) GetEntry(singleEntryRequest *models.SingleEntryRequest, entryId int) (*tapApi.EntryWrapper, error) {
var entry *tapApi.Entry
bytes, err := basenine.Single(shared.BasenineHost, shared.BaseninePort, entryId, singleEntryRequest.Query)
if err != nil {
return nil, err
}
err = json.Unmarshal(bytes, &entry)
if err != nil {
return nil, err
}
extension := app.ExtensionsMap[entry.Protocol.Name]
base := extension.Dissector.Summarize(entry)
var representation []byte
representation, err = extension.Dissector.Represent(entry.Request, entry.Response)
if err != nil {
return nil, err
}
var rules []map[string]interface{}
var isRulesEnabled bool
if entry.Protocol.Name == "http" {
harEntry, _ := har.NewEntry(entry.Request, entry.Response, entry.StartTime, entry.ElapsedTime)
_, rulesMatched, _isRulesEnabled := models.RunValidationRulesState(*harEntry, entry.Destination.Name)
isRulesEnabled = _isRulesEnabled
inrec, _ := json.Marshal(rulesMatched)
if err := json.Unmarshal(inrec, &rules); err != nil {
logger.Log.Error(err)
}
}
return &tapApi.EntryWrapper{
Protocol: entry.Protocol,
Representation: string(representation),
Data: entry,
Base: base,
Rules: rules,
IsRulesEnabled: isRulesEnabled,
}, nil
}

View File

@@ -67,23 +67,23 @@ func fileSize(fname string) int64 {
return fi.Size() return fi.Size()
} }
func feedEntries(fromFiles []string, isSync bool) (count int, err error) { func feedEntries(fromFiles []string, isSync bool, gen *defaultOasGenerator) (count uint, err error) {
badFiles := make([]string, 0) badFiles := make([]string, 0)
cnt := 0 cnt := uint(0)
for _, file := range fromFiles { for _, file := range fromFiles {
logger.Log.Info("Processing file: " + file) logger.Log.Info("Processing file: " + file)
ext := strings.ToLower(filepath.Ext(file)) ext := strings.ToLower(filepath.Ext(file))
eCnt := 0 eCnt := uint(0)
switch ext { switch ext {
case ".har": case ".har":
eCnt, err = feedFromHAR(file, isSync) eCnt, err = feedFromHAR(file, isSync, gen)
if err != nil { if err != nil {
logger.Log.Warning("Failed processing file: " + err.Error()) logger.Log.Warning("Failed processing file: " + err.Error())
badFiles = append(badFiles, file) badFiles = append(badFiles, file)
continue continue
} }
case ".ldjson": case ".ldjson":
eCnt, err = feedFromLDJSON(file, isSync) eCnt, err = feedFromLDJSON(file, isSync, gen)
if err != nil { if err != nil {
logger.Log.Warning("Failed processing file: " + err.Error()) logger.Log.Warning("Failed processing file: " + err.Error())
badFiles = append(badFiles, file) badFiles = append(badFiles, file)
@@ -102,7 +102,7 @@ func feedEntries(fromFiles []string, isSync bool) (count int, err error) {
return cnt, nil return cnt, nil
} }
func feedFromHAR(file string, isSync bool) (int, error) { func feedFromHAR(file string, isSync bool, gen *defaultOasGenerator) (uint, error) {
fd, err := os.Open(file) fd, err := os.Open(file)
if err != nil { if err != nil {
panic(err) panic(err)
@@ -121,16 +121,16 @@ func feedFromHAR(file string, isSync bool) (int, error) {
return 0, err return 0, err
} }
cnt := 0 cnt := uint(0)
for _, entry := range harDoc.Log.Entries { for _, entry := range harDoc.Log.Entries {
cnt += 1 cnt += 1
feedEntry(&entry, "", isSync, file) feedEntry(&entry, "", file, gen, cnt)
} }
return cnt, nil return cnt, nil
} }
func feedEntry(entry *har.Entry, source string, isSync bool, file string) { func feedEntry(entry *har.Entry, source string, file string, gen *defaultOasGenerator, cnt uint) {
entry.Comment = file entry.Comment = file
if entry.Response.Status == 302 { if entry.Response.Status == 302 {
logger.Log.Debugf("Dropped traffic entry due to permanent redirect status: %s", entry.StartedDateTime) logger.Log.Debugf("Dropped traffic entry due to permanent redirect status: %s", entry.StartedDateTime)
@@ -145,15 +145,11 @@ func feedEntry(entry *har.Entry, source string, isSync bool, file string) {
logger.Log.Errorf("Failed to parse entry URL: %v, err: %v", entry.Request.URL, err) logger.Log.Errorf("Failed to parse entry URL: %v, err: %v", entry.Request.URL, err)
} }
ews := EntryWithSource{Entry: *entry, Source: source, Destination: u.Host, Id: uint(0)} ews := EntryWithSource{Entry: *entry, Source: source, Destination: u.Host, Id: cnt}
if isSync { gen.handleHARWithSource(&ews)
GetDefaultOasGeneratorInstance().entriesChan <- ews // blocking variant, right?
} else {
GetDefaultOasGeneratorInstance().PushEntry(&ews)
}
} }
func feedFromLDJSON(file string, isSync bool) (int, error) { func feedFromLDJSON(file string, isSync bool, gen *defaultOasGenerator) (uint, error) {
fd, err := os.Open(file) fd, err := os.Open(file)
if err != nil { if err != nil {
panic(err) panic(err)
@@ -165,7 +161,7 @@ func feedFromLDJSON(file string, isSync bool) (int, error) {
var meta map[string]interface{} var meta map[string]interface{}
buf := strings.Builder{} buf := strings.Builder{}
cnt := 0 cnt := uint(0)
source := "" source := ""
for { for {
substr, isPrefix, err := reader.ReadLine() substr, isPrefix, err := reader.ReadLine()
@@ -196,7 +192,7 @@ func feedFromLDJSON(file string, isSync bool) (int, error) {
logger.Log.Warningf("Failed decoding entry: %s", line) logger.Log.Warningf("Failed decoding entry: %s", line)
} else { } else {
cnt += 1 cnt += 1
feedEntry(&entry, source, isSync, file) feedEntry(&entry, source, file, gen, cnt)
} }
} }
} }

View File

@@ -3,10 +3,13 @@ package oas
import ( import (
"context" "context"
"encoding/json" "encoding/json"
basenine "github.com/up9inc/basenine/client/go"
"github.com/up9inc/mizu/agent/pkg/har"
"github.com/up9inc/mizu/shared"
"github.com/up9inc/mizu/tap/api"
"net/url" "net/url"
"sync" "sync"
"github.com/up9inc/mizu/agent/pkg/har"
"github.com/up9inc/mizu/shared/logger" "github.com/up9inc/mizu/shared/logger"
) )
@@ -15,10 +18,6 @@ var (
instance *defaultOasGenerator instance *defaultOasGenerator
) )
type OasGeneratorSink interface {
PushEntry(entryWithSource *EntryWithSource)
}
type OasGenerator interface { type OasGenerator interface {
Start() Start()
Stop() Stop()
@@ -32,12 +31,20 @@ type defaultOasGenerator struct {
ctx context.Context ctx context.Context
cancel context.CancelFunc cancel context.CancelFunc
serviceSpecs *sync.Map serviceSpecs *sync.Map
entriesChan chan EntryWithSource dbConn *basenine.Connection
} }
func GetDefaultOasGeneratorInstance() *defaultOasGenerator { func GetDefaultOasGeneratorInstance(conn *basenine.Connection) *defaultOasGenerator {
syncOnce.Do(func() { syncOnce.Do(func() {
instance = NewDefaultOasGenerator() if conn == nil {
c, err := basenine.NewConnection(shared.BasenineHost, shared.BaseninePort)
if err != nil {
panic(err)
}
conn = c
}
instance = NewDefaultOasGenerator(conn)
logger.Log.Debug("OAS Generator Initialized") logger.Log.Debug("OAS Generator Initialized")
}) })
return instance return instance
@@ -50,7 +57,6 @@ func (g *defaultOasGenerator) Start() {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
g.cancel = cancel g.cancel = cancel
g.ctx = ctx g.ctx = ctx
g.entriesChan = make(chan EntryWithSource, 100) // buffer up to 100 entries for OAS processing
g.serviceSpecs = &sync.Map{} g.serviceSpecs = &sync.Map{}
g.started = true g.started = true
go g.runGenerator() go g.runGenerator()
@@ -70,80 +76,117 @@ func (g *defaultOasGenerator) IsStarted() bool {
} }
func (g *defaultOasGenerator) runGenerator() { func (g *defaultOasGenerator) runGenerator() {
// Make []byte channels to recieve the data and the meta
dataChan := make(chan []byte)
metaChan := make(chan []byte)
g.dbConn.Query("", dataChan, metaChan)
for { for {
select { select {
case <-g.ctx.Done(): case <-g.ctx.Done():
logger.Log.Infof("OAS Generator was canceled") logger.Log.Infof("OAS Generator was canceled")
return return
case entryWithSource, ok := <-g.entriesChan: case metaBytes, ok := <-metaChan:
if !ok {
logger.Log.Infof("OAS Generator - meta channel closed")
break
}
logger.Log.Debugf("Meta: %s", metaBytes)
case dataBytes, ok := <-dataChan:
if !ok { if !ok {
logger.Log.Infof("OAS Generator - entries channel closed") logger.Log.Infof("OAS Generator - entries channel closed")
break break
} }
entry := entryWithSource.Entry
u, err := url.Parse(entry.Request.URL) logger.Log.Debugf("Data: %s", dataBytes)
e := new(api.Entry)
err := json.Unmarshal(dataBytes, e)
if err != nil { if err != nil {
logger.Log.Errorf("Failed to parse entry URL: %v, err: %v", entry.Request.URL, err)
}
val, found := g.serviceSpecs.Load(entryWithSource.Destination)
var gen *SpecGen
if !found {
gen = NewGen(u.Scheme + "://" + entryWithSource.Destination)
g.serviceSpecs.Store(entryWithSource.Destination, 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 continue
} }
g.handleEntry(e)
logger.Log.Debugf("Handled entry %s as opId: %s", entry.Request.URL, opId) // TODO: set opId back to entry?
} }
} }
} }
func (g *defaultOasGenerator) handleEntry(mizuEntry *api.Entry) {
if mizuEntry.Protocol.Name == "http" {
entry, err := har.NewEntry(mizuEntry.Request, mizuEntry.Response, mizuEntry.StartTime, mizuEntry.ElapsedTime)
if err != nil {
logger.Log.Warningf("Failed to turn MizuEntry %d into HAR Entry: %s", mizuEntry.Id, err)
return
}
dest := mizuEntry.Destination.Name
if dest == "" {
dest = mizuEntry.Destination.IP + ":" + mizuEntry.Destination.Port
}
entryWSource := &EntryWithSource{
Entry: *entry,
Source: mizuEntry.Source.Name,
Destination: dest,
Id: mizuEntry.Id,
}
g.handleHARWithSource(entryWSource)
} else {
logger.Log.Debugf("OAS: Unsupported protocol in entry %d: %s", mizuEntry.Id, mizuEntry.Protocol.Name)
}
}
func (g *defaultOasGenerator) handleHARWithSource(entryWSource *EntryWithSource) {
entry := entryWSource.Entry
gen := g.getGen(entryWSource.Destination, entry.Request.URL)
opId, err := gen.feedEntry(entryWSource)
if err != nil {
txt, suberr := json.Marshal(entry)
if suberr == nil {
logger.Log.Debugf("Problematic entry: %s", txt)
}
logger.Log.Warningf("Failed processing entry %d: %s", entryWSource.Id, err)
return
}
logger.Log.Debugf("Handled entry %d as opId: %s", entryWSource.Id, opId) // TODO: set opId back to entry?
}
func (g *defaultOasGenerator) getGen(dest string, urlStr string) *SpecGen {
u, err := url.Parse(urlStr)
if err != nil {
logger.Log.Errorf("Failed to parse entry URL: %v, err: %v", urlStr, err)
}
val, found := g.serviceSpecs.Load(dest)
var gen *SpecGen
if !found {
gen = NewGen(u.Scheme + "://" + dest)
g.serviceSpecs.Store(dest, gen)
} else {
gen = val.(*SpecGen)
}
return gen
}
func (g *defaultOasGenerator) Reset() { func (g *defaultOasGenerator) Reset() {
g.serviceSpecs = &sync.Map{} g.serviceSpecs = &sync.Map{}
} }
func (g *defaultOasGenerator) 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 (g *defaultOasGenerator) GetServiceSpecs() *sync.Map { func (g *defaultOasGenerator) GetServiceSpecs() *sync.Map {
return g.serviceSpecs return g.serviceSpecs
} }
func NewDefaultOasGenerator() *defaultOasGenerator { func NewDefaultOasGenerator(c *basenine.Connection) *defaultOasGenerator {
return &defaultOasGenerator{ return &defaultOasGenerator{
started: false, started: false,
ctx: nil, ctx: nil,
cancel: nil, cancel: nil,
serviceSpecs: nil, serviceSpecs: nil,
entriesChan: nil, dbConn: c,
} }
} }
type EntryWithSource struct {
Source string
Destination string
Entry har.Entry
Id uint
}

View File

@@ -0,0 +1,36 @@
package oas
import (
"encoding/json"
"github.com/up9inc/mizu/agent/pkg/har"
"sync"
"testing"
)
func TestOASGen(t *testing.T) {
gen := new(defaultOasGenerator)
gen.serviceSpecs = &sync.Map{}
e := new(har.Entry)
err := json.Unmarshal([]byte(`{"startedDateTime": "20000101","request": {"url": "https://host/path", "method": "GET"}, "response": {"status": 200}}`), e)
if err != nil {
panic(err)
}
ews := &EntryWithSource{
Destination: "some",
Entry: *e,
}
gen.handleHARWithSource(ews)
g, ok := gen.serviceSpecs.Load("some")
if !ok {
panic("Failed")
}
sg := g.(*SpecGen)
spec, err := sg.GetSpec()
if err != nil {
panic(err)
}
specText, _ := json.Marshal(spec)
t.Log(string(specText))
}

View File

@@ -28,6 +28,13 @@ const CountersTotal = "x-counters-total"
const CountersPerSource = "x-counters-per-source" const CountersPerSource = "x-counters-per-source"
const SampleId = "x-sample-entry" const SampleId = "x-sample-entry"
type EntryWithSource struct {
Source string
Destination string
Entry har.Entry
Id uint
}
type reqResp struct { // hello, generics in Go type reqResp struct { // hello, generics in Go
Req *har.Request Req *har.Request
Resp *har.Response Resp *har.Response
@@ -60,7 +67,7 @@ func (g *SpecGen) StartFromSpec(oas *openapi.OpenAPI) {
g.tree = new(Node) g.tree = new(Node)
for pathStr, pathObj := range oas.Paths.Items { for pathStr, pathObj := range oas.Paths.Items {
pathSplit := strings.Split(string(pathStr), "/") pathSplit := strings.Split(string(pathStr), "/")
g.tree.getOrSet(pathSplit, pathObj) g.tree.getOrSet(pathSplit, pathObj, 0)
// clean "last entry timestamp" markers from the past // clean "last entry timestamp" markers from the past
for _, pathAndOp := range g.tree.listOps() { for _, pathAndOp := range g.tree.listOps() {
@@ -69,11 +76,11 @@ func (g *SpecGen) StartFromSpec(oas *openapi.OpenAPI) {
} }
} }
func (g *SpecGen) feedEntry(entryWithSource EntryWithSource) (string, error) { func (g *SpecGen) feedEntry(entryWithSource *EntryWithSource) (string, error) {
g.lock.Lock() g.lock.Lock()
defer g.lock.Unlock() defer g.lock.Unlock()
opId, err := g.handlePathObj(&entryWithSource) opId, err := g.handlePathObj(entryWithSource)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -219,7 +226,7 @@ func (g *SpecGen) handlePathObj(entryWithSource *EntryWithSource) (string, error
} else { } else {
split = strings.Split(urlParsed.Path, "/") split = strings.Split(urlParsed.Path, "/")
} }
node := g.tree.getOrSet(split, new(openapi.PathObj)) node := g.tree.getOrSet(split, new(openapi.PathObj), entryWithSource.Id)
opObj, err := handleOpObj(entryWithSource, node.pathObj) opObj, err := handleOpObj(entryWithSource, node.pathObj)
if opObj != nil { if opObj != nil {
@@ -242,12 +249,12 @@ func handleOpObj(entryWithSource *EntryWithSource, pathObj *openapi.PathObj) (*o
return nil, nil return nil, nil
} }
err = handleRequest(&entry.Request, opObj, isSuccess) err = handleRequest(&entry.Request, opObj, isSuccess, entryWithSource.Id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = handleResponse(&entry.Response, opObj, isSuccess) err = handleResponse(&entry.Response, opObj, isSuccess, entryWithSource.Id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -257,6 +264,8 @@ func handleOpObj(entryWithSource *EntryWithSource, pathObj *openapi.PathObj) (*o
return nil, err return nil, err
} }
setSampleID(&opObj.Extensions, entryWithSource.Id)
return opObj, nil return opObj, nil
} }
@@ -329,15 +338,10 @@ func handleCounters(opObj *openapi.Operation, success bool, entryWithSource *Ent
return err return err
} }
err = opObj.Extensions.SetExtension(SampleId, entryWithSource.Id)
if err != nil {
return err
}
return nil return nil
} }
func handleRequest(req *har.Request, opObj *openapi.Operation, isSuccess bool) error { func handleRequest(req *har.Request, opObj *openapi.Operation, isSuccess bool, sampleId uint) 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 // 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) urlParsed, err := url.Parse(req.URL)
if err != nil { if err != nil {
@@ -361,7 +365,7 @@ func handleRequest(req *har.Request, opObj *openapi.Operation, isSuccess bool) e
IsIgnored: func(name string) bool { return false }, IsIgnored: func(name string) bool { return false },
GeneralizeName: func(name string) string { return name }, GeneralizeName: func(name string) string { return name },
} }
handleNameVals(qstrGW, &opObj.Parameters, false) handleNameVals(qstrGW, &opObj.Parameters, false, sampleId)
hdrGW := nvParams{ hdrGW := nvParams{
In: openapi.InHeader, In: openapi.InHeader,
@@ -369,7 +373,7 @@ func handleRequest(req *har.Request, opObj *openapi.Operation, isSuccess bool) e
IsIgnored: isHeaderIgnored, IsIgnored: isHeaderIgnored,
GeneralizeName: strings.ToLower, GeneralizeName: strings.ToLower,
} }
handleNameVals(hdrGW, &opObj.Parameters, true) handleNameVals(hdrGW, &opObj.Parameters, true, sampleId)
if isSuccess { if isSuccess {
reqBody, err := getRequestBody(req, opObj) reqBody, err := getRequestBody(req, opObj)
@@ -378,12 +382,14 @@ func handleRequest(req *har.Request, opObj *openapi.Operation, isSuccess bool) e
} }
if reqBody != nil { if reqBody != nil {
setSampleID(&reqBody.Extensions, sampleId)
if req.PostData.Text == "" { if req.PostData.Text == "" {
reqBody.Required = false reqBody.Required = false
} else { } else {
reqCtype, _ := getReqCtype(req) reqCtype, _ := getReqCtype(req)
reqMedia, err := fillContent(reqResp{Req: req}, reqBody.Content, reqCtype) reqMedia, err := fillContent(reqResp{Req: req}, reqBody.Content, reqCtype, sampleId)
if err != nil { if err != nil {
return err return err
} }
@@ -395,18 +401,20 @@ func handleRequest(req *har.Request, opObj *openapi.Operation, isSuccess bool) e
return nil return nil
} }
func handleResponse(resp *har.Response, opObj *openapi.Operation, isSuccess bool) error { func handleResponse(resp *har.Response, opObj *openapi.Operation, isSuccess bool, sampleId uint) error {
// TODO: we don't support "default" response // TODO: we don't support "default" response
respObj, err := getResponseObj(resp, opObj, isSuccess) respObj, err := getResponseObj(resp, opObj, isSuccess)
if err != nil { if err != nil {
return err return err
} }
handleRespHeaders(resp.Headers, respObj) setSampleID(&respObj.Extensions, sampleId)
handleRespHeaders(resp.Headers, respObj, sampleId)
respCtype := getRespCtype(resp) respCtype := getRespCtype(resp)
respContent := respObj.Content respContent := respObj.Content
respMedia, err := fillContent(reqResp{Resp: resp}, respContent, respCtype) respMedia, err := fillContent(reqResp{Resp: resp}, respContent, respCtype, sampleId)
if err != nil { if err != nil {
return err return err
} }
@@ -414,7 +422,7 @@ func handleResponse(resp *har.Response, opObj *openapi.Operation, isSuccess bool
return nil return nil
} }
func handleRespHeaders(reqHeaders []har.Header, respObj *openapi.ResponseObj) { func handleRespHeaders(reqHeaders []har.Header, respObj *openapi.ResponseObj, sampleId uint) {
visited := map[string]*openapi.HeaderObj{} visited := map[string]*openapi.HeaderObj{}
for _, pair := range reqHeaders { for _, pair := range reqHeaders {
if isHeaderIgnored(pair.Name) { if isHeaderIgnored(pair.Name) {
@@ -436,6 +444,8 @@ func handleRespHeaders(reqHeaders []har.Header, respObj *openapi.ResponseObj) {
logger.Log.Warningf("Failed to add example to a parameter: %s", err) logger.Log.Warningf("Failed to add example to a parameter: %s", err)
} }
visited[nameGeneral] = param visited[nameGeneral] = param
setSampleID(&param.Extensions, sampleId)
} }
// maintain "required" flag // maintain "required" flag
@@ -456,13 +466,15 @@ func handleRespHeaders(reqHeaders []har.Header, respObj *openapi.ResponseObj) {
} }
} }
func fillContent(reqResp reqResp, respContent openapi.Content, ctype string) (*openapi.MediaType, error) { func fillContent(reqResp reqResp, respContent openapi.Content, ctype string, sampleId uint) (*openapi.MediaType, error) {
content, found := respContent[ctype] content, found := respContent[ctype]
if !found { if !found {
respContent[ctype] = &openapi.MediaType{} respContent[ctype] = &openapi.MediaType{}
content = respContent[ctype] content = respContent[ctype]
} }
setSampleID(&content.Extensions, sampleId)
var text string var text string
var isBinary bool var isBinary bool
if reqResp.Req != nil { if reqResp.Req != nil {
@@ -474,10 +486,10 @@ func fillContent(reqResp reqResp, respContent openapi.Content, ctype string) (*o
if !isBinary && text != "" { if !isBinary && text != "" {
var exampleMsg []byte var exampleMsg []byte
// try treating it as json // try treating it as json
any, isJSON := anyJSON(text) anyVal, isJSON := anyJSON(text)
if isJSON { if isJSON {
// re-marshal with forced indent // re-marshal with forced indent
if msg, err := json.MarshalIndent(any, "", "\t"); err != nil { if msg, err := json.MarshalIndent(anyVal, "", "\t"); err != nil {
panic("Failed to re-marshal value, super-strange") panic("Failed to re-marshal value, super-strange")
} else { } else {
exampleMsg = msg exampleMsg = msg

View File

@@ -6,6 +6,7 @@ import (
"os" "os"
"regexp" "regexp"
"strings" "strings"
"sync"
"testing" "testing"
"time" "time"
@@ -19,7 +20,7 @@ import (
// if started via env, write file into subdir // if started via env, write file into subdir
func outputSpec(label string, spec *openapi.OpenAPI, t *testing.T) string { func outputSpec(label string, spec *openapi.OpenAPI, t *testing.T) string {
content, err := json.MarshalIndent(spec, "", "\t") content, err := json.MarshalIndent(spec, "", " ")
if err != nil { if err != nil {
panic(err) panic(err)
} }
@@ -48,14 +49,16 @@ func TestEntries(t *testing.T) {
t.Log(err) t.Log(err)
t.FailNow() t.FailNow()
} }
GetDefaultOasGeneratorInstance().Start()
loadStartingOAS("test_artifacts/catalogue.json", "catalogue") gen := NewDefaultOasGenerator(nil)
loadStartingOAS("test_artifacts/trcc.json", "trcc-api-service") gen.serviceSpecs = new(sync.Map)
loadStartingOAS("test_artifacts/catalogue.json", "catalogue", gen.serviceSpecs)
loadStartingOAS("test_artifacts/trcc.json", "trcc-api-service", gen.serviceSpecs)
go func() { go func() {
for { for {
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
GetDefaultOasGeneratorInstance().GetServiceSpecs().Range(func(key, val interface{}) bool { gen.serviceSpecs.Range(func(key, val interface{}) bool {
svc := key.(string) svc := key.(string)
t.Logf("Getting spec for %s", svc) t.Logf("Getting spec for %s", svc)
gen := val.(*SpecGen) gen := val.(*SpecGen)
@@ -68,16 +71,14 @@ func TestEntries(t *testing.T) {
} }
}() }()
cnt, err := feedEntries(files, true) cnt, err := feedEntries(files, true, gen)
if err != nil { if err != nil {
t.Log(err) t.Log(err)
t.Fail() t.Fail()
} }
waitQueueProcessed()
svcs := strings.Builder{} svcs := strings.Builder{}
GetDefaultOasGeneratorInstance().GetServiceSpecs().Range(func(key, val interface{}) bool { gen.serviceSpecs.Range(func(key, val interface{}) bool {
gen := val.(*SpecGen) gen := val.(*SpecGen)
svc := key.(string) svc := key.(string)
svcs.WriteString(svc + ",") svcs.WriteString(svc + ",")
@@ -99,7 +100,7 @@ func TestEntries(t *testing.T) {
return true return true
}) })
GetDefaultOasGeneratorInstance().GetServiceSpecs().Range(func(key, val interface{}) bool { gen.serviceSpecs.Range(func(key, val interface{}) bool {
svc := key.(string) svc := key.(string)
gen := val.(*SpecGen) gen := val.(*SpecGen)
spec, err := gen.GetSpec() spec, err := gen.GetSpec()
@@ -123,20 +124,18 @@ func TestEntries(t *testing.T) {
} }
func TestFileSingle(t *testing.T) { func TestFileSingle(t *testing.T) {
GetDefaultOasGeneratorInstance().Start() gen := NewDefaultOasGenerator(nil)
GetDefaultOasGeneratorInstance().Reset() gen.serviceSpecs = new(sync.Map)
// loadStartingOAS() // loadStartingOAS()
file := "test_artifacts/params.har" file := "test_artifacts/params.har"
files := []string{file} files := []string{file}
cnt, err := feedEntries(files, true) cnt, err := feedEntries(files, true, gen)
if err != nil { if err != nil {
logger.Log.Warning("Failed processing file: " + err.Error()) logger.Log.Warning("Failed processing file: " + err.Error())
t.Fail() t.Fail()
} }
waitQueueProcessed() gen.serviceSpecs.Range(func(key, val interface{}) bool {
GetDefaultOasGeneratorInstance().GetServiceSpecs().Range(func(key, val interface{}) bool {
svc := key.(string) svc := key.(string)
gen := val.(*SpecGen) gen := val.(*SpecGen)
spec, err := gen.GetSpec() spec, err := gen.GetSpec()
@@ -189,18 +188,7 @@ func TestFileSingle(t *testing.T) {
logger.Log.Infof("Processed entries: %d", cnt) logger.Log.Infof("Processed entries: %d", cnt)
} }
func waitQueueProcessed() { func loadStartingOAS(file string, label string, specs *sync.Map) {
for {
time.Sleep(100 * time.Millisecond)
queue := len(GetDefaultOasGeneratorInstance().entriesChan)
logger.Log.Infof("Queue: %d", queue)
if queue < 1 {
break
}
}
}
func loadStartingOAS(file string, label string) {
fd, err := os.Open(file) fd, err := os.Open(file)
if err != nil { if err != nil {
panic(err) panic(err)
@@ -222,12 +210,14 @@ func loadStartingOAS(file string, label string) {
gen := NewGen(label) gen := NewGen(label)
gen.StartFromSpec(doc) gen.StartFromSpec(doc)
GetDefaultOasGeneratorInstance().GetServiceSpecs().Store(label, gen) specs.Store(label, gen)
} }
func TestEntriesNegative(t *testing.T) { func TestEntriesNegative(t *testing.T) {
gen := NewDefaultOasGenerator(nil)
gen.serviceSpecs = new(sync.Map)
files := []string{"invalid"} files := []string{"invalid"}
_, err := feedEntries(files, false) _, err := feedEntries(files, false, gen)
if err == nil { if err == nil {
t.Logf("Should have failed") t.Logf("Should have failed")
t.Fail() t.Fail()
@@ -235,8 +225,10 @@ func TestEntriesNegative(t *testing.T) {
} }
func TestEntriesPositive(t *testing.T) { func TestEntriesPositive(t *testing.T) {
gen := NewDefaultOasGenerator(nil)
gen.serviceSpecs = new(sync.Map)
files := []string{"test_artifacts/params.har"} files := []string{"test_artifacts/params.har"}
_, err := feedEntries(files, false) _, err := feedEntries(files, false, gen)
if err != nil { if err != nil {
t.Logf("Failed") t.Logf("Failed")
t.Fail() t.Fail()

View File

@@ -21,9 +21,11 @@
"description": "Successful call with status 200", "description": "Successful call with status 200",
"content": { "content": {
"application/json": { "application/json": {
"example": null "example": null,
"x-sample-entry": 4
} }
} },
"x-sample-entry": 4
} }
}, },
"x-counters-per-source": { "x-counters-per-source": {
@@ -45,7 +47,7 @@
"sumDuration": 0 "sumDuration": 0
}, },
"x-last-seen-ts": 1567750580.04, "x-last-seen-ts": 1567750580.04,
"x-sample-entry": 0 "x-sample-entry": 4
} }
}, },
"/appears-twice": { "/appears-twice": {
@@ -58,9 +60,11 @@
"description": "Successful call with status 200", "description": "Successful call with status 200",
"content": { "content": {
"application/json": { "application/json": {
"example": null "example": null,
"x-sample-entry": 6
} }
} },
"x-sample-entry": 6
} }
}, },
"x-counters-per-source": { "x-counters-per-source": {
@@ -82,7 +86,7 @@
"sumDuration": 1 "sumDuration": 1
}, },
"x-last-seen-ts": 1567750581.74, "x-last-seen-ts": 1567750581.74,
"x-sample-entry": 0 "x-sample-entry": 6
} }
}, },
"/body-optional": { "/body-optional": {
@@ -94,8 +98,11 @@
"200": { "200": {
"description": "Successful call with status 200", "description": "Successful call with status 200",
"content": { "content": {
"": {} "": {
} "x-sample-entry": 12
}
},
"x-sample-entry": 12
} }
}, },
"x-counters-per-source": { "x-counters-per-source": {
@@ -117,14 +124,16 @@
"sumDuration": 0.01 "sumDuration": 0.01
}, },
"x-last-seen-ts": 1567750581.75, "x-last-seen-ts": 1567750581.75,
"x-sample-entry": 0, "x-sample-entry": 12,
"requestBody": { "requestBody": {
"description": "Generic request body", "description": "Generic request body",
"content": { "content": {
"application/json": { "application/json": {
"example": "{\"key\", \"val\"}" "example": "{\"key\", \"val\"}",
"x-sample-entry": 11
} }
} },
"x-sample-entry": 12
} }
} }
}, },
@@ -137,8 +146,11 @@
"200": { "200": {
"description": "Successful call with status 200", "description": "Successful call with status 200",
"content": { "content": {
"": {} "": {
} "x-sample-entry": 13
}
},
"x-sample-entry": 13
} }
}, },
"x-counters-per-source": { "x-counters-per-source": {
@@ -160,15 +172,17 @@
"sumDuration": 0 "sumDuration": 0
}, },
"x-last-seen-ts": 1567750581.75, "x-last-seen-ts": 1567750581.75,
"x-sample-entry": 0, "x-sample-entry": 13,
"requestBody": { "requestBody": {
"description": "Generic request body", "description": "Generic request body",
"content": { "content": {
"": { "": {
"example": "body exists" "example": "body exists",
"x-sample-entry": 13
} }
}, },
"required": true "required": true,
"x-sample-entry": 13
} }
} }
}, },
@@ -182,9 +196,11 @@
"description": "Successful call with status 200", "description": "Successful call with status 200",
"content": { "content": {
"": { "": {
"example": {} "example": {},
"x-sample-entry": 9
} }
} },
"x-sample-entry": 9
} }
}, },
"x-counters-per-source": { "x-counters-per-source": {
@@ -206,7 +222,7 @@
"sumDuration": 0 "sumDuration": 0
}, },
"x-last-seen-ts": 1567750582.74, "x-last-seen-ts": 1567750582.74,
"x-sample-entry": 0, "x-sample-entry": 9,
"requestBody": { "requestBody": {
"description": "Generic request body", "description": "Generic request body",
"content": { "content": {
@@ -233,10 +249,12 @@
} }
} }
}, },
"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" "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",
"x-sample-entry": 9
} }
}, },
"required": true "required": true,
"x-sample-entry": 9
} }
} }
}, },
@@ -249,8 +267,11 @@
"200": { "200": {
"description": "Successful call with status 200", "description": "Successful call with status 200",
"content": { "content": {
"": {} "": {
} "x-sample-entry": 8
}
},
"x-sample-entry": 8
} }
}, },
"x-counters-per-source": { "x-counters-per-source": {
@@ -272,7 +293,7 @@
"sumDuration": 1 "sumDuration": 1
}, },
"x-last-seen-ts": 1567750581.74, "x-last-seen-ts": 1567750581.74,
"x-sample-entry": 0, "x-sample-entry": 8,
"requestBody": { "requestBody": {
"description": "Generic request body", "description": "Generic request body",
"content": { "content": {
@@ -312,10 +333,12 @@
} }
} }
}, },
"example": "agent-id=ade\u0026callback-url=\u0026token=sometoken" "example": "agent-id=ade\u0026callback-url=\u0026token=sometoken",
"x-sample-entry": 8
} }
}, },
"required": true "required": true,
"x-sample-entry": 8
} }
} }
}, },
@@ -331,8 +354,11 @@
"200": { "200": {
"description": "Successful call with status 200", "description": "Successful call with status 200",
"content": { "content": {
"": {} "": {
} "x-sample-entry": 14
}
},
"x-sample-entry": 14
} }
}, },
"x-counters-per-source": { "x-counters-per-source": {
@@ -354,7 +380,7 @@
"sumDuration": 0 "sumDuration": 0
}, },
"x-last-seen-ts": 1567750582, "x-last-seen-ts": 1567750582,
"x-sample-entry": 0 "x-sample-entry": 14
}, },
"parameters": [ "parameters": [
{ {
@@ -369,7 +395,8 @@
"example #0": { "example #0": {
"value": "234324" "value": "234324"
} }
} },
"x-sample-entry": 14
} }
] ]
}, },
@@ -385,8 +412,11 @@
"200": { "200": {
"description": "Successful call with status 200", "description": "Successful call with status 200",
"content": { "content": {
"": {} "": {
} "x-sample-entry": 18
}
},
"x-sample-entry": 18
} }
}, },
"x-counters-per-source": { "x-counters-per-source": {
@@ -408,7 +438,7 @@
"sumDuration": 9.53e-7 "sumDuration": 9.53e-7
}, },
"x-last-seen-ts": 1567750582.00, "x-last-seen-ts": 1567750582.00,
"x-sample-entry": 0 "x-sample-entry": 18
}, },
"parameters": [ "parameters": [
{ {
@@ -436,7 +466,8 @@
"example #4": { "example #4": {
"value": "prefix-gibberish-afterwards" "value": "prefix-gibberish-afterwards"
} }
} },
"x-sample-entry": 19
} }
] ]
}, },
@@ -452,8 +483,11 @@
"200": { "200": {
"description": "Successful call with status 200", "description": "Successful call with status 200",
"content": { "content": {
"": {} "": {
} "x-sample-entry": 15
}
},
"x-sample-entry": 15
} }
}, },
"x-counters-per-source": { "x-counters-per-source": {
@@ -475,7 +509,7 @@
"sumDuration": 0 "sumDuration": 0
}, },
"x-last-seen-ts": 1567750582.00, "x-last-seen-ts": 1567750582.00,
"x-sample-entry": 0 "x-sample-entry": 15
}, },
"parameters": [ "parameters": [
{ {
@@ -503,7 +537,8 @@
"example #4": { "example #4": {
"value": "prefix-gibberish-afterwards" "value": "prefix-gibberish-afterwards"
} }
} },
"x-sample-entry": 19
} }
] ]
}, },
@@ -519,8 +554,11 @@
"200": { "200": {
"description": "Successful call with status 200", "description": "Successful call with status 200",
"content": { "content": {
"": {} "": {
} "x-sample-entry": 16
}
},
"x-sample-entry": 16
} }
}, },
"x-counters-per-source": { "x-counters-per-source": {
@@ -542,7 +580,7 @@
"sumDuration": 0 "sumDuration": 0
}, },
"x-last-seen-ts": 1567750582.00, "x-last-seen-ts": 1567750582.00,
"x-sample-entry": 0 "x-sample-entry": 16
}, },
"parameters": [ "parameters": [
{ {
@@ -570,7 +608,8 @@
"example #4": { "example #4": {
"value": "prefix-gibberish-afterwards" "value": "prefix-gibberish-afterwards"
} }
} },
"x-sample-entry": 19
} }
] ]
}, },
@@ -586,8 +625,11 @@
"200": { "200": {
"description": "Successful call with status 200", "description": "Successful call with status 200",
"content": { "content": {
"": {} "": {
} "x-sample-entry": 19
}
},
"x-sample-entry": 19
} }
}, },
"x-counters-per-source": { "x-counters-per-source": {
@@ -609,7 +651,7 @@
"sumDuration": 0 "sumDuration": 0
}, },
"x-last-seen-ts": 1567750582.00, "x-last-seen-ts": 1567750582.00,
"x-sample-entry": 0 "x-sample-entry": 19
}, },
"parameters": [ "parameters": [
{ {
@@ -624,7 +666,8 @@
"example #0": { "example #0": {
"value": "23421" "value": "23421"
} }
} },
"x-sample-entry": 19
}, },
{ {
"name": "parampatternId", "name": "parampatternId",
@@ -651,7 +694,8 @@
"example #4": { "example #4": {
"value": "prefix-gibberish-afterwards" "value": "prefix-gibberish-afterwards"
} }
} },
"x-sample-entry": 19
} }
] ]
}, },
@@ -665,9 +709,11 @@
"description": "Successful call with status 200", "description": "Successful call with status 200",
"content": { "content": {
"application/json": { "application/json": {
"example": null "example": null,
"x-sample-entry": 3
} }
} },
"x-sample-entry": 3
} }
}, },
"x-counters-per-source": { "x-counters-per-source": {
@@ -689,7 +735,7 @@
"sumDuration": 0 "sumDuration": 0
}, },
"x-last-seen-ts": 1567750579.74, "x-last-seen-ts": 1567750579.74,
"x-sample-entry": 0 "x-sample-entry": 3
}, },
"parameters": [ "parameters": [
{ {
@@ -707,7 +753,8 @@
"example #1": { "example #1": {
"value": "<UUID4>" "value": "<UUID4>"
} }
} },
"x-sample-entry": 3
} }
] ]
}, },
@@ -720,8 +767,11 @@
"200": { "200": {
"description": "Successful call with status 200", "description": "Successful call with status 200",
"content": { "content": {
"text/html": {} "text/html": {
} "x-sample-entry": 1
}
},
"x-sample-entry": 1
} }
}, },
"x-counters-per-source": { "x-counters-per-source": {
@@ -743,7 +793,7 @@
"sumDuration": 0 "sumDuration": 0
}, },
"x-last-seen-ts": 1567750483.86, "x-last-seen-ts": 1567750483.86,
"x-sample-entry": 0 "x-sample-entry": 1
}, },
"parameters": [ "parameters": [
{ {
@@ -761,7 +811,8 @@
"example #1": { "example #1": {
"value": "<UUID4>" "value": "<UUID4>"
} }
} },
"x-sample-entry": 3
} }
] ]
}, },
@@ -775,9 +826,11 @@
"description": "Successful call with status 200", "description": "Successful call with status 200",
"content": { "content": {
"application/json": { "application/json": {
"example": null "example": null,
"x-sample-entry": 2
} }
} },
"x-sample-entry": 2
} }
}, },
"x-counters-per-source": { "x-counters-per-source": {
@@ -799,7 +852,7 @@
"sumDuration": 0 "sumDuration": 0
}, },
"x-last-seen-ts": 1567750578.74, "x-last-seen-ts": 1567750578.74,
"x-sample-entry": 0 "x-sample-entry": 2
}, },
"parameters": [ "parameters": [
{ {
@@ -817,7 +870,8 @@
"example #1": { "example #1": {
"value": "<UUID4>" "value": "<UUID4>"
} }
} },
"x-sample-entry": 3
} }
] ]
} }

View File

@@ -20,7 +20,7 @@ type Node struct {
children []*Node children []*Node
} }
func (n *Node) getOrSet(path NodePath, existingPathObj *openapi.PathObj) (node *Node) { func (n *Node) getOrSet(path NodePath, existingPathObj *openapi.PathObj, sampleId uint) (node *Node) {
if existingPathObj == nil { if existingPathObj == nil {
panic("Invalid function call") panic("Invalid function call")
} }
@@ -70,6 +70,10 @@ func (n *Node) getOrSet(path NodePath, existingPathObj *openapi.PathObj) (node *
} }
} }
if node.pathParam != nil {
setSampleID(&node.pathParam.Extensions, sampleId)
}
// add example if it's a gibberish chunk // add example if it's a gibberish chunk
if node.pathParam != nil && !chunkIsParam { if node.pathParam != nil && !chunkIsParam {
exmp := &node.pathParam.Examples exmp := &node.pathParam.Examples
@@ -85,7 +89,7 @@ func (n *Node) getOrSet(path NodePath, existingPathObj *openapi.PathObj) (node *
// TODO: eat up trailing slash, in a smart way: node.pathObj!=nil && path[1]=="" // TODO: eat up trailing slash, in a smart way: node.pathObj!=nil && path[1]==""
if len(path) > 1 { if len(path) > 1 {
return node.getOrSet(path[1:], existingPathObj) return node.getOrSet(path[1:], existingPathObj, sampleId)
} else if node.pathObj == nil { } else if node.pathObj == nil {
node.pathObj = existingPathObj node.pathObj = existingPathObj
} }

View File

@@ -20,10 +20,10 @@ func TestTree(t *testing.T) {
} }
tree := new(Node) tree := new(Node)
for _, tc := range testCases { for i, tc := range testCases {
split := strings.Split(tc.inp, "/") split := strings.Split(tc.inp, "/")
pathObj := new(openapi.PathObj) pathObj := new(openapi.PathObj)
node := tree.getOrSet(split, pathObj) node := tree.getOrSet(split, pathObj, uint(i))
fillPathParams(node, pathObj) fillPathParams(node, pathObj)

View File

@@ -115,7 +115,7 @@ type nvParams struct {
GeneralizeName func(name string) string GeneralizeName func(name string) string
} }
func handleNameVals(gw nvParams, params **openapi.ParameterList, checkIgnore bool) { func handleNameVals(gw nvParams, params **openapi.ParameterList, checkIgnore bool, sampleId uint) {
visited := map[string]*openapi.ParameterObj{} visited := map[string]*openapi.ParameterObj{}
for _, pair := range gw.Pairs { for _, pair := range gw.Pairs {
if (checkIgnore && gw.IsIgnored(pair.Name)) || pair.Name == "" { if (checkIgnore && gw.IsIgnored(pair.Name)) || pair.Name == "" {
@@ -137,6 +137,8 @@ func handleNameVals(gw nvParams, params **openapi.ParameterList, checkIgnore boo
logger.Log.Warningf("Failed to add example to a parameter: %s", err) logger.Log.Warningf("Failed to add example to a parameter: %s", err)
} }
visited[nameGeneral] = param visited[nameGeneral] = param
setSampleID(&param.Extensions, sampleId)
} }
// maintain "required" flag // maintain "required" flag
@@ -474,3 +476,15 @@ func intersectSliceWithMap(required []string, names map[string]struct{}) []strin
} }
return required return required
} }
func setSampleID(extensions *openapi.Extensions, id uint) {
if id > 0 {
if *extensions == nil {
*extensions = openapi.Extensions{}
}
err := (extensions).SetExtension(SampleId, id)
if err != nil {
logger.Log.Warningf("Failed to set sample ID: %s", err)
}
}
}

View File

@@ -14,9 +14,10 @@ import (
const FilePath = shared.DataDirPath + "tapped-pods.json" const FilePath = shared.DataDirPath + "tapped-pods.json"
var ( var (
lock = &sync.Mutex{} lock = &sync.Mutex{}
syncOnce sync.Once syncOnce sync.Once
tappedPods []*shared.PodInfo tappedPods []*shared.PodInfo
nodeHostToTappedPodsMap shared.NodeToPodsMap
) )
func Get() []*shared.PodInfo { func Get() []*shared.PodInfo {
@@ -55,3 +56,14 @@ func GetTappedPodsStatus() []shared.TappedPodStatus {
return tappedPodsStatus return tappedPodsStatus
} }
func SetNodeToTappedPodMap(nodeToTappedPodsMap shared.NodeToPodsMap) {
summary := nodeToTappedPodsMap.Summary()
logger.Log.Infof("Setting node to tapped pods map to %v", summary)
nodeHostToTappedPodsMap = nodeToTappedPodsMap
}
func GetNodeToTappedPodMap() shared.NodeToPodsMap {
return nodeHostToTappedPodsMap
}

View File

@@ -18,10 +18,11 @@ type ServiceMapResponse struct {
} }
type ServiceMapNode struct { type ServiceMapNode struct {
Id int `json:"id"` Id int `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Entry *tapApi.TCP `json:"entry"` Entry *tapApi.TCP `json:"entry"`
Count int `json:"count"` Count int `json:"count"`
Resolved bool `json:"resolved"`
} }
type ServiceMapEdge struct { type ServiceMapEdge struct {

View File

@@ -227,10 +227,11 @@ func (s *defaultServiceMap) GetNodes() []ServiceMapNode {
var nodes []ServiceMapNode var nodes []ServiceMapNode
for i, n := range s.graph.Nodes { for i, n := range s.graph.Nodes {
nodes = append(nodes, ServiceMapNode{ nodes = append(nodes, ServiceMapNode{
Id: n.id, Id: n.id,
Name: string(i), Name: string(i),
Entry: n.entry, Resolved: n.entry.Name != UnresolvedNodeName,
Count: n.count, Entry: n.entry,
Count: n.count,
}) })
} }
return nodes return nodes
@@ -243,16 +244,18 @@ func (s *defaultServiceMap) GetEdges() []ServiceMapEdge {
for _, p := range s.graph.Edges[u][v].data { for _, p := range s.graph.Edges[u][v].data {
edges = append(edges, ServiceMapEdge{ edges = append(edges, ServiceMapEdge{
Source: ServiceMapNode{ Source: ServiceMapNode{
Id: s.graph.Nodes[u].id, Id: s.graph.Nodes[u].id,
Name: string(u), Name: string(u),
Entry: s.graph.Nodes[u].entry, Entry: s.graph.Nodes[u].entry,
Count: s.graph.Nodes[u].count, Resolved: s.graph.Nodes[u].entry.Name != UnresolvedNodeName,
Count: s.graph.Nodes[u].count,
}, },
Destination: ServiceMapNode{ Destination: ServiceMapNode{
Id: s.graph.Nodes[v].id, Id: s.graph.Nodes[v].id,
Name: string(v), Name: string(v),
Entry: s.graph.Nodes[v].entry, Entry: s.graph.Nodes[v].entry,
Count: s.graph.Nodes[v].count, Resolved: s.graph.Nodes[v].entry.Name != UnresolvedNodeName,
Count: s.graph.Nodes[v].count,
}, },
Count: p.count, Count: p.count,
Protocol: p.protocol, Protocol: p.protocol,

View File

@@ -10,8 +10,6 @@ import (
"net/url" "net/url"
"time" "time"
"github.com/up9inc/mizu/shared/kubernetes"
"github.com/up9inc/mizu/cli/config" "github.com/up9inc/mizu/cli/config"
"github.com/up9inc/mizu/shared" "github.com/up9inc/mizu/shared"
"github.com/up9inc/mizu/shared/logger" "github.com/up9inc/mizu/shared/logger"
@@ -83,15 +81,13 @@ func (provider *Provider) ReportTapperStatus(tapperStatus shared.TapperStatus) e
func (provider *Provider) ReportTappedPods(pods []core.Pod) error { func (provider *Provider) ReportTappedPods(pods []core.Pod) error {
tappedPodsUrl := fmt.Sprintf("%s/status/tappedPods", provider.url) tappedPodsUrl := fmt.Sprintf("%s/status/tappedPods", provider.url)
podInfos := kubernetes.GetPodInfosForPods(pods) if jsonValue, err := json.Marshal(pods); err != nil {
if jsonValue, err := json.Marshal(podInfos); err != nil {
return fmt.Errorf("failed Marshal the tapped pods %w", err) return fmt.Errorf("failed Marshal the tapped pods %w", err)
} else { } else {
if _, err := utils.Post(tappedPodsUrl, "application/json", bytes.NewBuffer(jsonValue), provider.client); err != nil { if _, err := utils.Post(tappedPodsUrl, "application/json", bytes.NewBuffer(jsonValue), provider.client); err != nil {
return fmt.Errorf("failed sending to API server the tapped pods %w", err) return fmt.Errorf("failed sending to API server the tapped pods %w", err)
} else { } else {
logger.Log.Debugf("Reported to server API about %d taped pods successfully", len(podInfos)) logger.Log.Debugf("Reported to server API about %d taped pods successfully", len(pods))
return nil return nil
} }
} }

View File

@@ -111,9 +111,9 @@ func checkRulesPermissions(ctx context.Context, kubernetesProvider *kubernetes.P
func checkPermissionExist(group string, resource string, verb string, namespace string, exist bool, err error) bool { func checkPermissionExist(group string, resource string, verb string, namespace string, exist bool, err error) bool {
var groupAndNamespace string var groupAndNamespace string
if group != "" && namespace != "" { if group != "" && namespace != "" {
groupAndNamespace = fmt.Sprintf("in group '%v' and namespace '%v'", group, namespace) groupAndNamespace = fmt.Sprintf("in api group '%v' and namespace '%v'", group, namespace)
} else if group != "" { } else if group != "" {
groupAndNamespace = fmt.Sprintf("in group '%v'", group) groupAndNamespace = fmt.Sprintf("in api group '%v'", group)
} else if namespace != "" { } else if namespace != "" {
groupAndNamespace = fmt.Sprintf("in namespace '%v'", namespace) groupAndNamespace = fmt.Sprintf("in namespace '%v'", namespace)
} }

View File

@@ -27,13 +27,21 @@ func runMizuCheck() {
checkPassed = check.KubernetesVersion(kubernetesVersion) checkPassed = check.KubernetesVersion(kubernetesVersion)
} }
if config.Config.Check.PreTap { if config.Config.Check.PreTap || config.Config.Check.PreInstall || config.Config.Check.ImagePull {
if checkPassed { if config.Config.Check.PreTap {
checkPassed = check.TapKubernetesPermissions(ctx, embedFS, kubernetesProvider) if checkPassed {
checkPassed = check.TapKubernetesPermissions(ctx, embedFS, kubernetesProvider)
}
} else if config.Config.Check.PreInstall {
if checkPassed {
checkPassed = check.InstallKubernetesPermissions(ctx, kubernetesProvider)
}
} }
} else if config.Config.Check.PreInstall {
if checkPassed { if config.Config.Check.ImagePull {
checkPassed = check.InstallKubernetesPermissions(ctx, kubernetesProvider) if checkPassed {
checkPassed = check.ImagePullInCluster(ctx, kubernetesProvider)
}
} }
} else { } else {
if checkPassed { if checkPassed {
@@ -45,12 +53,6 @@ func runMizuCheck() {
} }
} }
if config.Config.Check.ImagePull {
if checkPassed {
checkPassed = check.ImagePullInCluster(ctx, kubernetesProvider)
}
}
if checkPassed { if checkPassed {
logger.Log.Infof("\nStatus check results are %v", fmt.Sprintf(uiUtils.Green, "√")) logger.Log.Infof("\nStatus check results are %v", fmt.Sprintf(uiUtils.Green, "√"))
} else { } else {

View File

@@ -40,7 +40,7 @@ type ConfigStruct struct {
HeadlessMode bool `yaml:"headless" default:"false"` HeadlessMode bool `yaml:"headless" default:"false"`
LogLevelStr string `yaml:"log-level,omitempty" default:"INFO" readonly:""` LogLevelStr string `yaml:"log-level,omitempty" default:"INFO" readonly:""`
ServiceMap bool `yaml:"service-map" default:"true"` ServiceMap bool `yaml:"service-map" default:"true"`
OAS bool `yaml:"oas,omitempty" default:"false" readonly:""` OAS bool `yaml:"oas" default:"true"`
Elastic shared.ElasticConfig `yaml:"elastic"` Elastic shared.ElasticConfig `yaml:"elastic"`
} }

View File

@@ -11,7 +11,7 @@ require (
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
github.com/spf13/cobra v1.3.0 github.com/spf13/cobra v1.3.0
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/up9inc/basenine/server/lib v0.0.0-20220317230530-8472d80307f6 github.com/up9inc/basenine/server/lib v0.0.0-20220326121918-785f3061c8ce
github.com/up9inc/mizu/shared v0.0.0 github.com/up9inc/mizu/shared v0.0.0
github.com/up9inc/mizu/tap/api v0.0.0 github.com/up9inc/mizu/tap/api v0.0.0
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8

View File

@@ -600,8 +600,8 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69
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/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
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/up9inc/basenine/server/lib v0.0.0-20220317230530-8472d80307f6 h1:+RZTD+HdfIW2SMbc65yWkruTY+g5/1Av074m62A74ls= github.com/up9inc/basenine/server/lib v0.0.0-20220326121918-785f3061c8ce h1:PypqybjmuxftGkX4NmP4JAUyEykZj2r6W4r9lnRZ/kE=
github.com/up9inc/basenine/server/lib v0.0.0-20220317230530-8472d80307f6/go.mod h1:ZIkxWiJm65jYQIso9k+OZKhR7gQ1we2jNyE2kQX9IQI= github.com/up9inc/basenine/server/lib v0.0.0-20220326121918-785f3061c8ce/go.mod h1:ZIkxWiJm65jYQIso9k+OZKhR7gQ1we2jNyE2kQX9IQI=
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/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk=

View File

@@ -1,18 +1,20 @@
FROM dockcross/linux-arm64-musl:latest AS builder-from-amd64-to-arm64v8 FROM dockcross/linux-arm64-musl:latest AS builder-from-amd64-to-arm64v8
# Install Go # Install Go
RUN curl https://go.dev/dl/go1.17.6.linux-amd64.tar.gz -Lo ./go.linux-amd64.tar.gz RUN curl https://go.dev/dl/go1.17.6.linux-amd64.tar.gz -Lo ./go.linux-amd64.tar.gz \
RUN curl https://go.dev/dl/go1.17.6.linux-amd64.tar.gz.asc -Lo ./go.linux-amd64.tar.gz.asc && curl https://go.dev/dl/go1.17.6.linux-amd64.tar.gz.asc -Lo ./go.linux-amd64.tar.gz.asc \
RUN curl https://dl.google.com/dl/linux/linux_signing_key.pub -Lo linux_signing_key.pub && curl https://dl.google.com/dl/linux/linux_signing_key.pub -Lo linux_signing_key.pub \
RUN gpg --import linux_signing_key.pub && gpg --verify ./go.linux-amd64.tar.gz.asc ./go.linux-amd64.tar.gz && gpg --import linux_signing_key.pub && gpg --verify ./go.linux-amd64.tar.gz.asc ./go.linux-amd64.tar.gz \
RUN rm -rf /usr/local/go && tar -C /usr/local -xzf go.linux-amd64.tar.gz && rm -rf /usr/local/go && tar -C /usr/local -xzf go.linux-amd64.tar.gz
ENV PATH "$PATH:/usr/local/go/bin" ENV PATH "$PATH:/usr/local/go/bin"
# Compile libpcap from source # Compile libpcap from source
RUN curl https://www.tcpdump.org/release/libpcap-1.10.1.tar.gz -Lo ./libpcap.tar.gz RUN curl https://www.tcpdump.org/release/libpcap-1.10.1.tar.gz -Lo ./libpcap.tar.gz \
RUN curl https://www.tcpdump.org/release/libpcap-1.10.1.tar.gz.sig -Lo ./libpcap.tar.gz.sig && curl https://www.tcpdump.org/release/libpcap-1.10.1.tar.gz.sig -Lo ./libpcap.tar.gz.sig \
RUN curl https://www.tcpdump.org/release/signing-key.asc -Lo ./signing-key.asc && curl https://www.tcpdump.org/release/signing-key.asc -Lo ./signing-key.asc \
RUN gpg --import signing-key.asc && gpg --verify libpcap.tar.gz.sig libpcap.tar.gz && gpg --import signing-key.asc && gpg --verify libpcap.tar.gz.sig libpcap.tar.gz \
RUN tar -xzf libpcap.tar.gz && mv ./libpcap-* ./libpcap && tar -xzf libpcap.tar.gz && mv ./libpcap-* ./libpcap
RUN cd ./libpcap && ./configure --host=arm && make WORKDIR /work/libpcap
RUN cp /work/libpcap/libpcap.a /usr/xcc/aarch64-linux-musl-cross/lib/gcc/aarch64-linux-musl/*/ RUN ./configure --host=arm && make \
&& cp /work/libpcap/libpcap.a /usr/xcc/aarch64-linux-musl-cross/lib/gcc/aarch64-linux-musl/*/

View File

@@ -5,7 +5,6 @@ const (
SyncEntriesConfigEnvVar = "SYNC_ENTRIES_CONFIG" SyncEntriesConfigEnvVar = "SYNC_ENTRIES_CONFIG"
HostModeEnvVar = "HOST_MODE" HostModeEnvVar = "HOST_MODE"
NodeNameEnvVar = "NODE_NAME" NodeNameEnvVar = "NODE_NAME"
TappedAddressesPerNodeDictEnvVar = "TAPPED_ADDRESSES_PER_HOST"
ConfigDirPath = "/app/config/" ConfigDirPath = "/app/config/"
DataDirPath = "/app/data/" DataDirPath = "/app/data/"
ValidationRulesFileName = "validation-rules.yaml" ValidationRulesFileName = "validation-rules.yaml"

View File

@@ -31,7 +31,8 @@ type MizuTapperSyncer struct {
TapPodChangesOut chan TappedPodChangeEvent TapPodChangesOut chan TappedPodChangeEvent
TapperStatusChangedOut chan shared.TapperStatus TapperStatusChangedOut chan shared.TapperStatus
ErrorOut chan K8sTapManagerError ErrorOut chan K8sTapManagerError
nodeToTappedPodMap map[string][]core.Pod nodeToTappedPodMap shared.NodeToPodsMap
tappedNodes []string
} }
type TapperSyncerConfig struct { type TapperSyncerConfig struct {
@@ -177,7 +178,7 @@ func (tapperSyncer *MizuTapperSyncer) watchPodsForTapping() {
podWatchHelper := NewPodWatchHelper(tapperSyncer.kubernetesProvider, &tapperSyncer.config.PodFilterRegex) podWatchHelper := NewPodWatchHelper(tapperSyncer.kubernetesProvider, &tapperSyncer.config.PodFilterRegex)
eventChan, errorChan := FilteredWatch(tapperSyncer.context, podWatchHelper, tapperSyncer.config.TargetNamespaces, podWatchHelper) eventChan, errorChan := FilteredWatch(tapperSyncer.context, podWatchHelper, tapperSyncer.config.TargetNamespaces, podWatchHelper)
restartTappers := func() { handleChangeInPods := func() {
err, changeFound := tapperSyncer.updateCurrentlyTappedPods() err, changeFound := tapperSyncer.updateCurrentlyTappedPods()
if err != nil { if err != nil {
tapperSyncer.ErrorOut <- K8sTapManagerError{ tapperSyncer.ErrorOut <- K8sTapManagerError{
@@ -197,7 +198,7 @@ func (tapperSyncer *MizuTapperSyncer) watchPodsForTapping() {
} }
} }
} }
restartTappersDebouncer := debounce.NewDebouncer(updateTappersDelay, restartTappers) restartTappersDebouncer := debounce.NewDebouncer(updateTappersDelay, handleChangeInPods)
for { for {
select { select {
@@ -295,6 +296,20 @@ func (tapperSyncer *MizuTapperSyncer) updateCurrentlyTappedPods() (err error, ch
} }
func (tapperSyncer *MizuTapperSyncer) updateMizuTappers() error { func (tapperSyncer *MizuTapperSyncer) updateMizuTappers() error {
nodesToTap := make([]string, len(tapperSyncer.nodeToTappedPodMap))
i := 0
for node := range tapperSyncer.nodeToTappedPodMap {
nodesToTap[i] = node
i++
}
if shared.EqualStringSlices(nodesToTap, tapperSyncer.tappedNodes) {
logger.Log.Debug("Skipping apply, DaemonSet is up to date")
return nil
}
logger.Log.Debugf("Updating DaemonSet to run on nodes: %v", nodesToTap)
if len(tapperSyncer.nodeToTappedPodMap) > 0 { if len(tapperSyncer.nodeToTappedPodMap) > 0 {
var serviceAccountName string var serviceAccountName string
if tapperSyncer.config.MizuServiceAccountExists { if tapperSyncer.config.MizuServiceAccountExists {
@@ -303,6 +318,11 @@ func (tapperSyncer *MizuTapperSyncer) updateMizuTappers() error {
serviceAccountName = "" serviceAccountName = ""
} }
nodeNames := make([]string, 0, len(tapperSyncer.nodeToTappedPodMap))
for nodeName := range tapperSyncer.nodeToTappedPodMap {
nodeNames = append(nodeNames, nodeName)
}
if err := tapperSyncer.kubernetesProvider.ApplyMizuTapperDaemonSet( if err := tapperSyncer.kubernetesProvider.ApplyMizuTapperDaemonSet(
tapperSyncer.context, tapperSyncer.context,
tapperSyncer.config.MizuResourcesNamespace, tapperSyncer.config.MizuResourcesNamespace,
@@ -310,7 +330,7 @@ func (tapperSyncer *MizuTapperSyncer) updateMizuTappers() error {
tapperSyncer.config.AgentImage, tapperSyncer.config.AgentImage,
TapperPodName, TapperPodName,
fmt.Sprintf("%s.%s.svc.cluster.local", ApiServerPodName, tapperSyncer.config.MizuResourcesNamespace), fmt.Sprintf("%s.%s.svc.cluster.local", ApiServerPodName, tapperSyncer.config.MizuResourcesNamespace),
tapperSyncer.nodeToTappedPodMap, nodeNames,
serviceAccountName, serviceAccountName,
tapperSyncer.config.TapperResources, tapperSyncer.config.TapperResources,
tapperSyncer.config.ImagePullPolicy, tapperSyncer.config.ImagePullPolicy,
@@ -335,5 +355,7 @@ func (tapperSyncer *MizuTapperSyncer) updateMizuTappers() error {
logger.Log.Debugf("Successfully reset tapper daemon set") logger.Log.Debugf("Successfully reset tapper daemon set")
} }
tapperSyncer.tappedNodes = nodesToTap
return nil return nil
} }

View File

@@ -708,18 +708,13 @@ func (provider *Provider) CreateConfigMap(ctx context.Context, namespace string,
return nil return nil
} }
func (provider *Provider) ApplyMizuTapperDaemonSet(ctx context.Context, namespace string, daemonSetName string, podImage string, tapperPodName string, apiServerPodIp string, nodeToTappedPodMap map[string][]core.Pod, serviceAccountName string, resources shared.Resources, imagePullPolicy core.PullPolicy, mizuApiFilteringOptions api.TrafficFilteringOptions, logLevel logging.Level, serviceMesh bool, tls bool) error { func (provider *Provider) ApplyMizuTapperDaemonSet(ctx context.Context, namespace string, daemonSetName string, podImage string, tapperPodName string, apiServerPodIp string, nodeNames []string, serviceAccountName string, resources shared.Resources, imagePullPolicy core.PullPolicy, mizuApiFilteringOptions api.TrafficFilteringOptions, logLevel logging.Level, serviceMesh bool, tls bool) error {
logger.Log.Debugf("Applying %d tapper daemon sets, ns: %s, daemonSetName: %s, podImage: %s, tapperPodName: %s", len(nodeToTappedPodMap), namespace, daemonSetName, podImage, tapperPodName) logger.Log.Debugf("Applying %d tapper daemon sets, ns: %s, daemonSetName: %s, podImage: %s, tapperPodName: %s", len(nodeNames), namespace, daemonSetName, podImage, tapperPodName)
if len(nodeToTappedPodMap) == 0 { if len(nodeNames) == 0 {
return fmt.Errorf("daemon set %s must tap at least 1 pod", daemonSetName) return fmt.Errorf("daemon set %s must tap at least 1 pod", daemonSetName)
} }
nodeToTappedPodMapJsonStr, err := json.Marshal(nodeToTappedPodMap)
if err != nil {
return err
}
mizuApiFilteringOptionsJsonStr, err := json.Marshal(mizuApiFilteringOptions) mizuApiFilteringOptionsJsonStr, err := json.Marshal(mizuApiFilteringOptions)
if err != nil { if err != nil {
return err return err
@@ -773,7 +768,6 @@ func (provider *Provider) ApplyMizuTapperDaemonSet(ctx context.Context, namespac
agentContainer.WithEnv( agentContainer.WithEnv(
applyconfcore.EnvVar().WithName(shared.LogLevelEnvVar).WithValue(logLevel.String()), applyconfcore.EnvVar().WithName(shared.LogLevelEnvVar).WithValue(logLevel.String()),
applyconfcore.EnvVar().WithName(shared.HostModeEnvVar).WithValue("1"), applyconfcore.EnvVar().WithName(shared.HostModeEnvVar).WithValue("1"),
applyconfcore.EnvVar().WithName(shared.TappedAddressesPerNodeDictEnvVar).WithValue(string(nodeToTappedPodMapJsonStr)),
applyconfcore.EnvVar().WithName(shared.GoGCEnvVar).WithValue("12800"), applyconfcore.EnvVar().WithName(shared.GoGCEnvVar).WithValue("12800"),
applyconfcore.EnvVar().WithName(shared.MizuFilteringOptionsEnvVar).WithValue(string(mizuApiFilteringOptionsJsonStr)), applyconfcore.EnvVar().WithName(shared.MizuFilteringOptionsEnvVar).WithValue(string(mizuApiFilteringOptionsJsonStr)),
) )
@@ -811,10 +805,6 @@ func (provider *Provider) ApplyMizuTapperDaemonSet(ctx context.Context, namespac
agentResources := applyconfcore.ResourceRequirements().WithRequests(agentResourceRequests).WithLimits(agentResourceLimits) agentResources := applyconfcore.ResourceRequirements().WithRequests(agentResourceRequests).WithLimits(agentResourceLimits)
agentContainer.WithResources(agentResources) agentContainer.WithResources(agentResources)
nodeNames := make([]string, 0, len(nodeToTappedPodMap))
for nodeName := range nodeToTappedPodMap {
nodeNames = append(nodeNames, nodeName)
}
nodeSelectorRequirement := applyconfcore.NodeSelectorRequirement() nodeSelectorRequirement := applyconfcore.NodeSelectorRequirement()
nodeSelectorRequirement.WithKey("kubernetes.io/hostname") nodeSelectorRequirement.WithKey("kubernetes.io/hostname")
nodeSelectorRequirement.WithOperator(core.NodeSelectorOpIn) nodeSelectorRequirement.WithOperator(core.NodeSelectorOpIn)

View File

@@ -5,12 +5,11 @@ import (
"github.com/up9inc/mizu/shared" "github.com/up9inc/mizu/shared"
core "k8s.io/api/core/v1" core "k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
func GetNodeHostToTappedPodsMap(tappedPods []core.Pod) map[string][]core.Pod { func GetNodeHostToTappedPodsMap(tappedPods []core.Pod) shared.NodeToPodsMap {
nodeToTappedPodMap := make(map[string][]core.Pod) nodeToTappedPodMap := make(shared.NodeToPodsMap)
for _, pod := range tappedPods { for _, pod := range tappedPods {
minimizedPod := getMinimizedPod(pod) minimizedPod := getMinimizedPod(pod)
@@ -29,18 +28,18 @@ func getMinimizedPod(fullPod core.Pod) core.Pod {
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: fullPod.Name, Name: fullPod.Name,
}, },
Status: v1.PodStatus{ Status: core.PodStatus{
PodIP: fullPod.Status.PodIP, PodIP: fullPod.Status.PodIP,
ContainerStatuses: getMinimizedContainerStatuses(fullPod), ContainerStatuses: getMinimizedContainerStatuses(fullPod),
}, },
} }
} }
func getMinimizedContainerStatuses(fullPod core.Pod) []v1.ContainerStatus { func getMinimizedContainerStatuses(fullPod core.Pod) []core.ContainerStatus {
result := make([]v1.ContainerStatus, len(fullPod.Status.ContainerStatuses)) result := make([]core.ContainerStatus, len(fullPod.Status.ContainerStatuses))
for i, container := range fullPod.Status.ContainerStatuses { for i, container := range fullPod.Status.ContainerStatuses {
result[i] = v1.ContainerStatus{ result[i] = core.ContainerStatus{
ContainerID: container.ContainerID, ContainerID: container.ContainerID,
} }
} }

View File

@@ -6,24 +6,25 @@ import (
"github.com/op/go-logging" "github.com/op/go-logging"
"github.com/up9inc/mizu/shared/logger" "github.com/up9inc/mizu/shared/logger"
v1 "k8s.io/api/core/v1"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
v1 "k8s.io/api/core/v1"
) )
type WebSocketMessageType string type WebSocketMessageType string
const ( const (
WebSocketMessageTypeEntry WebSocketMessageType = "entry" WebSocketMessageTypeEntry WebSocketMessageType = "entry"
WebSocketMessageTypeFullEntry WebSocketMessageType = "fullEntry" WebSocketMessageTypeFullEntry WebSocketMessageType = "fullEntry"
WebSocketMessageTypeTappedEntry WebSocketMessageType = "tappedEntry" WebSocketMessageTypeTappedEntry WebSocketMessageType = "tappedEntry"
WebSocketMessageTypeUpdateStatus WebSocketMessageType = "status" WebSocketMessageTypeUpdateStatus WebSocketMessageType = "status"
WebSocketMessageTypeAnalyzeStatus WebSocketMessageType = "analyzeStatus" WebSocketMessageTypeUpdateTappedPods WebSocketMessageType = "tappedPods"
WebsocketMessageTypeOutboundLink WebSocketMessageType = "outboundLink" WebSocketMessageTypeAnalyzeStatus WebSocketMessageType = "analyzeStatus"
WebSocketMessageTypeToast WebSocketMessageType = "toast" WebsocketMessageTypeOutboundLink WebSocketMessageType = "outboundLink"
WebSocketMessageTypeQueryMetadata WebSocketMessageType = "queryMetadata" WebSocketMessageTypeToast WebSocketMessageType = "toast"
WebSocketMessageTypeStartTime WebSocketMessageType = "startTime" WebSocketMessageTypeQueryMetadata WebSocketMessageType = "queryMetadata"
WebSocketMessageTypeTapConfig WebSocketMessageType = "tapConfig" WebSocketMessageTypeStartTime WebSocketMessageType = "startTime"
WebSocketMessageTypeTapConfig WebSocketMessageType = "tapConfig"
) )
type Resources struct { type Resources struct {
@@ -75,11 +76,29 @@ type WebSocketStatusMessage struct {
TappingStatus []TappedPodStatus `json:"tappingStatus"` TappingStatus []TappedPodStatus `json:"tappingStatus"`
} }
type WebSocketTappedPodsMessage struct {
*WebSocketMessageMetadata
NodeToTappedPodMap NodeToPodsMap `json:"nodeToTappedPodMap"`
}
type WebSocketTapConfigMessage struct { type WebSocketTapConfigMessage struct {
*WebSocketMessageMetadata *WebSocketMessageMetadata
TapTargets []v1.Pod `json:"pods"` TapTargets []v1.Pod `json:"pods"`
} }
type NodeToPodsMap map[string][]v1.Pod
func (np NodeToPodsMap) Summary() map[string][]string {
summary := make(map[string][]string)
for node, pods := range np {
for _, pod := range pods {
summary[node] = append(summary[node], pod.Namespace + "/" + pod.Name)
}
}
return summary
}
type TapperStatus struct { type TapperStatus struct {
TapperName string `json:"tapperName"` TapperName string `json:"tapperName"`
NodeName string `json:"nodeName"` NodeName string `json:"nodeName"`
@@ -121,6 +140,15 @@ func CreateWebSocketStatusMessage(tappedPodsStatus []TappedPodStatus) WebSocketS
} }
} }
func CreateWebSocketTappedPodsMessage(nodeToTappedPodMap NodeToPodsMap) WebSocketTappedPodsMessage {
return WebSocketTappedPodsMessage{
WebSocketMessageMetadata: &WebSocketMessageMetadata{
MessageType: WebSocketMessageTypeUpdateTappedPods,
},
NodeToTappedPodMap: nodeToTappedPodMap,
}
}
func CreateWebSocketMessageTypeAnalyzeStatus(analyzeStatus AnalyzeStatus) WebSocketAnalyzeStatusMessage { func CreateWebSocketMessageTypeAnalyzeStatus(analyzeStatus AnalyzeStatus) WebSocketAnalyzeStatusMessage {
return WebSocketAnalyzeStatusMessage{ return WebSocketAnalyzeStatusMessage{
WebSocketMessageMetadata: &WebSocketMessageMetadata{ WebSocketMessageMetadata: &WebSocketMessageMetadata{

View File

@@ -32,3 +32,17 @@ func Unique(slice []string) []string {
return list return list
} }
func EqualStringSlices(slice1 []string, slice2 []string) bool {
if len(slice1) != len(slice2) {
return false
}
for _, v := range slice1 {
if !Contains(slice2, v) {
return false
}
}
return true
}

View File

@@ -53,7 +53,16 @@ func representMapSliceAsTable(mapSlice []interface{}, selectorPrefix string) (re
h := item.(map[string]interface{}) h := item.(map[string]interface{})
key := h["name"].(string) key := h["name"].(string)
value := h["value"] value := h["value"]
switch reflect.TypeOf(value).Kind() {
var reflectKind reflect.Kind
reflectType := reflect.TypeOf(value)
if reflectType == nil {
reflectKind = reflect.Interface
} else {
reflectKind = reflect.TypeOf(value).Kind()
}
switch reflectKind {
case reflect.Slice: case reflect.Slice:
fallthrough fallthrough
case reflect.Array: case reflect.Array:

View File

@@ -59,7 +59,6 @@ var memprofile = flag.String("memprofile", "", "Write memory profile")
type TapOpts struct { type TapOpts struct {
HostMode bool HostMode bool
FilterAuthorities []v1.Pod
} }
var extensions []*api.Extension // global var extensions []*api.Extension // global
@@ -67,6 +66,7 @@ var filteringOptions *api.TrafficFilteringOptions // global
var tapTargets []v1.Pod // global var tapTargets []v1.Pod // global
var packetSourceManager *source.PacketSourceManager // global var packetSourceManager *source.PacketSourceManager // global
var mainPacketInputChan chan source.TcpPacketInfo // global var mainPacketInputChan chan source.TcpPacketInfo // global
var tlsTapperInstance *tlstapper.TlsTapper // global
func inArrayInt(arr []int, valueToCheck int) bool { func inArrayInt(arr []int, valueToCheck int) bool {
for _, value := range arr { for _, value := range arr {
@@ -90,16 +90,10 @@ func StartPassiveTapper(opts *TapOpts, outputItems chan *api.OutputChannelItem,
extensions = extensionsRef extensions = extensionsRef
filteringOptions = options filteringOptions = options
if opts.FilterAuthorities == nil {
tapTargets = []v1.Pod{}
} else {
tapTargets = opts.FilterAuthorities
}
if *tls { if *tls {
for _, e := range extensions { for _, e := range extensions {
if e.Protocol.Name == "http" { if e.Protocol.Name == "http" {
startTlsTapper(e, outputItems, options) tlsTapperInstance = startTlsTapper(e, outputItems, options)
break break
} }
} }
@@ -109,24 +103,39 @@ func StartPassiveTapper(opts *TapOpts, outputItems chan *api.OutputChannelItem,
diagnose.StartMemoryProfiler(os.Getenv(MemoryProfilingDumpPath), os.Getenv(MemoryProfilingTimeIntervalSeconds)) diagnose.StartMemoryProfiler(os.Getenv(MemoryProfilingDumpPath), os.Getenv(MemoryProfilingTimeIntervalSeconds))
} }
go startPassiveTapper(opts, outputItems) streamsMap, assembler := initializePassiveTapper(opts, outputItems)
go startPassiveTapper(streamsMap, assembler)
} }
func UpdateTapTargets(newTapTargets []v1.Pod) { func UpdateTapTargets(newTapTargets []v1.Pod) {
success := true
tapTargets = newTapTargets tapTargets = newTapTargets
if err := initializePacketSources(); err != nil {
logger.Log.Fatal(err) packetSourceManager.UpdatePods(tapTargets, !*nodefrag, mainPacketInputChan)
if tlsTapperInstance != nil {
if err := tlstapper.UpdateTapTargets(tlsTapperInstance, &tapTargets, *procfs); err != nil {
tlstapper.LogError(err)
success = false
}
} }
printNewTapTargets()
printNewTapTargets(success)
} }
func printNewTapTargets() { func printNewTapTargets(success bool) {
printStr := "" printStr := ""
for _, tapTarget := range tapTargets { for _, tapTarget := range tapTargets {
printStr += fmt.Sprintf("%s (%s), ", tapTarget.Status.PodIP, tapTarget.Name) printStr += fmt.Sprintf("%s (%s), ", tapTarget.Status.PodIP, tapTarget.Name)
} }
printStr = strings.TrimRight(printStr, ", ") printStr = strings.TrimRight(printStr, ", ")
logger.Log.Infof("Now tapping: %s", printStr)
if success {
logger.Log.Infof("Now tapping: %s", printStr)
} else {
logger.Log.Errorf("Failed to start tapping: %s", printStr)
}
} }
func printPeriodicStats(cleaner *Cleaner) { func printPeriodicStats(cleaner *Cleaner) {
@@ -189,17 +198,12 @@ func initializePacketSources() error {
} }
var err error var err error
if packetSourceManager, err = source.NewPacketSourceManager(*procfs, *fname, *iface, *servicemesh, tapTargets, behaviour); err != nil { packetSourceManager, err = source.NewPacketSourceManager(*procfs, *fname, *iface, *servicemesh, tapTargets, behaviour, !*nodefrag, mainPacketInputChan)
return err return err
} else {
packetSourceManager.ReadPackets(!*nodefrag, mainPacketInputChan)
return nil
}
} }
func startPassiveTapper(opts *TapOpts, outputItems chan *api.OutputChannelItem) { func initializePassiveTapper(opts *TapOpts, outputItems chan *api.OutputChannelItem) (*tcpStreamMap, *tcpAssembler) {
streamsMap := NewTcpStreamMap() streamsMap := NewTcpStreamMap()
go streamsMap.closeTimedoutTcpStreamChannels()
diagnose.InitializeErrorsMap(*debug, *verbose, *quiet) diagnose.InitializeErrorsMap(*debug, *verbose, *quiet)
diagnose.InitializeTapperInternalStats() diagnose.InitializeTapperInternalStats()
@@ -212,6 +216,12 @@ func startPassiveTapper(opts *TapOpts, outputItems chan *api.OutputChannelItem)
assembler := NewTcpAssembler(outputItems, streamsMap, opts) assembler := NewTcpAssembler(outputItems, streamsMap, opts)
return streamsMap, assembler
}
func startPassiveTapper(streamsMap *tcpStreamMap, assembler *tcpAssembler) {
go streamsMap.closeTimedoutTcpStreamChannels()
diagnose.AppStats.SetStartTime(time.Now()) diagnose.AppStats.SetStartTime(time.Now())
staleConnectionTimeout := time.Second * time.Duration(*staleTimeoutSeconds) staleConnectionTimeout := time.Second * time.Duration(*staleTimeoutSeconds)
@@ -243,13 +253,19 @@ func startPassiveTapper(opts *TapOpts, outputItems chan *api.OutputChannelItem)
logger.Log.Infof("AppStats: %v", diagnose.AppStats) logger.Log.Infof("AppStats: %v", diagnose.AppStats)
} }
func startTlsTapper(extension *api.Extension, outputItems chan *api.OutputChannelItem, options *api.TrafficFilteringOptions) { func startTlsTapper(extension *api.Extension, outputItems chan *api.OutputChannelItem, options *api.TrafficFilteringOptions) *tlstapper.TlsTapper {
tls := tlstapper.TlsTapper{} tls := tlstapper.TlsTapper{}
tlsPerfBufferSize := os.Getpagesize() * 100 chunksBufferSize := os.Getpagesize() * 100
logBufferSize := os.Getpagesize()
if err := tls.Init(tlsPerfBufferSize, *procfs, extension); err != nil { if err := tls.Init(chunksBufferSize, logBufferSize, *procfs, extension); err != nil {
tlstapper.LogError(err) tlstapper.LogError(err)
return return nil
}
if err := tlstapper.UpdateTapTargets(&tls, &tapTargets, *procfs); err != nil {
tlstapper.LogError(err)
return nil
} }
// A quick way to instrument libssl.so without PID filtering - used for debuging and troubleshooting // A quick way to instrument libssl.so without PID filtering - used for debuging and troubleshooting
@@ -257,19 +273,17 @@ func startTlsTapper(extension *api.Extension, outputItems chan *api.OutputChanne
if os.Getenv("MIZU_GLOBAL_SSL_LIBRARY") != "" { if os.Getenv("MIZU_GLOBAL_SSL_LIBRARY") != "" {
if err := tls.GlobalTap(os.Getenv("MIZU_GLOBAL_SSL_LIBRARY")); err != nil { if err := tls.GlobalTap(os.Getenv("MIZU_GLOBAL_SSL_LIBRARY")); err != nil {
tlstapper.LogError(err) tlstapper.LogError(err)
return return nil
} }
} }
if err := tlstapper.UpdateTapTargets(&tls, &tapTargets, *procfs); err != nil {
tlstapper.LogError(err)
return
}
var emitter api.Emitter = &api.Emitting{ var emitter api.Emitter = &api.Emitting{
AppStats: &diagnose.AppStats, AppStats: &diagnose.AppStats,
OutputChannel: outputItems, OutputChannel: outputItems,
} }
go tls.PollForLogging()
go tls.Poll(emitter, options) go tls.Poll(emitter, options)
return &tls
} }

View File

@@ -11,12 +11,20 @@ import (
const bpfFilterMaxPods = 150 const bpfFilterMaxPods = 150
const hostSourcePid = "0" const hostSourcePid = "0"
type PacketSourceManagerConfig struct {
mtls bool
procfs string
interfaceName string
behaviour TcpPacketSourceBehaviour
}
type PacketSourceManager struct { type PacketSourceManager struct {
sources map[string]*tcpPacketSource sources map[string]*tcpPacketSource
config PacketSourceManagerConfig
} }
func NewPacketSourceManager(procfs string, filename string, interfaceName string, func NewPacketSourceManager(procfs string, filename string, interfaceName string,
mtls bool, pods []v1.Pod, behaviour TcpPacketSourceBehaviour) (*PacketSourceManager, error) { mtls bool, pods []v1.Pod, behaviour TcpPacketSourceBehaviour, ipdefrag bool, packets chan<- TcpPacketInfo) (*PacketSourceManager, error) {
hostSource, err := newHostPacketSource(filename, interfaceName, behaviour) hostSource, err := newHostPacketSource(filename, interfaceName, behaviour)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -28,7 +36,14 @@ func NewPacketSourceManager(procfs string, filename string, interfaceName string
}, },
} }
sourceManager.UpdatePods(mtls, procfs, pods, interfaceName, behaviour) sourceManager.config = PacketSourceManagerConfig{
mtls: mtls,
procfs: procfs,
interfaceName: interfaceName,
behaviour: behaviour,
}
go hostSource.readPackets(ipdefrag, packets)
return sourceManager, nil return sourceManager, nil
} }
@@ -49,17 +64,16 @@ func newHostPacketSource(filename string, interfaceName string,
return source, nil return source, nil
} }
func (m *PacketSourceManager) UpdatePods(mtls bool, procfs string, pods []v1.Pod, func (m *PacketSourceManager) UpdatePods(pods []v1.Pod, ipdefrag bool, packets chan<- TcpPacketInfo) {
interfaceName string, behaviour TcpPacketSourceBehaviour) { if m.config.mtls {
if mtls { m.updateMtlsPods(m.config.procfs, pods, m.config.interfaceName, m.config.behaviour, ipdefrag, packets)
m.updateMtlsPods(procfs, pods, interfaceName, behaviour)
} }
m.setBPFFilter(pods) m.setBPFFilter(pods)
} }
func (m *PacketSourceManager) updateMtlsPods(procfs string, pods []v1.Pod, func (m *PacketSourceManager) updateMtlsPods(procfs string, pods []v1.Pod,
interfaceName string, behaviour TcpPacketSourceBehaviour) { interfaceName string, behaviour TcpPacketSourceBehaviour, ipdefrag bool, packets chan<- TcpPacketInfo) {
relevantPids := m.getRelevantPids(procfs, pods) relevantPids := m.getRelevantPids(procfs, pods)
logger.Log.Infof("Updating mtls pods (new: %v) (current: %v)", relevantPids, m.sources) logger.Log.Infof("Updating mtls pods (new: %v) (current: %v)", relevantPids, m.sources)
@@ -76,6 +90,7 @@ func (m *PacketSourceManager) updateMtlsPods(procfs string, pods []v1.Pod,
source, err := newNetnsPacketSource(procfs, pid, interfaceName, behaviour) source, err := newNetnsPacketSource(procfs, pid, interfaceName, behaviour)
if err == nil { if err == nil {
go source.readPackets(ipdefrag, packets)
m.sources[pid] = source m.sources[pid] = source
} }
} }
@@ -139,12 +154,6 @@ func (m *PacketSourceManager) setBPFFilter(pods []v1.Pod) {
} }
} }
func (m *PacketSourceManager) ReadPackets(ipdefrag bool, packets chan<- TcpPacketInfo) {
for _, src := range m.sources {
go src.readPackets(ipdefrag, packets)
}
}
func (m *PacketSourceManager) Close() { func (m *PacketSourceManager) Close() {
for _, src := range m.sources { for _, src := range m.sources {
src.close() src.close()

View File

@@ -7,8 +7,12 @@ Copyright (C) UP9 Inc.
#include "include/headers.h" #include "include/headers.h"
#include "include/util.h" #include "include/util.h"
#include "include/maps.h" #include "include/maps.h"
#include "include/log.h"
#include "include/logger_messages.h"
#include "include/pids.h" #include "include/pids.h"
#define IPV4_ADDR_LEN (16)
struct accept_info { struct accept_info {
__u64* sockaddr; __u64* sockaddr;
__u32* addrlen; __u32* addrlen;
@@ -41,9 +45,7 @@ void sys_enter_accept4(struct sys_enter_accept4_ctx *ctx) {
long err = bpf_map_update_elem(&accept_syscall_context, &id, &info, BPF_ANY); long err = bpf_map_update_elem(&accept_syscall_context, &id, &info, BPF_ANY);
if (err != 0) { if (err != 0) {
char msg[] = "Error putting accept info (id: %ld) (err: %ld)"; log_error(ctx, LOG_ERROR_PUTTING_ACCEPT_INFO, id, err, 0l);
bpf_trace_printk(msg, sizeof(msg), id, err);
return;
} }
} }
@@ -70,6 +72,7 @@ void sys_exit_accept4(struct sys_exit_accept4_ctx *ctx) {
struct accept_info *infoPtr = bpf_map_lookup_elem(&accept_syscall_context, &id); struct accept_info *infoPtr = bpf_map_lookup_elem(&accept_syscall_context, &id);
if (infoPtr == NULL) { if (infoPtr == NULL) {
log_error(ctx, LOG_ERROR_GETTING_ACCEPT_INFO, id, 0l, 0l);
return; return;
} }
@@ -79,15 +82,14 @@ void sys_exit_accept4(struct sys_exit_accept4_ctx *ctx) {
bpf_map_delete_elem(&accept_syscall_context, &id); bpf_map_delete_elem(&accept_syscall_context, &id);
if (err != 0) { if (err != 0) {
char msg[] = "Error reading accept info from accept syscall (id: %ld) (err: %ld)"; log_error(ctx, LOG_ERROR_READING_ACCEPT_INFO, id, err, 0l);
bpf_trace_printk(msg, sizeof(msg), id, err);
return; return;
} }
__u32 addrlen; __u32 addrlen;
bpf_probe_read(&addrlen, sizeof(__u32), info.addrlen); bpf_probe_read(&addrlen, sizeof(__u32), info.addrlen);
if (addrlen != 16) { if (addrlen != IPV4_ADDR_LEN) {
// Currently only ipv4 is supported linux-src/include/linux/inet.h // Currently only ipv4 is supported linux-src/include/linux/inet.h
return; return;
} }
@@ -105,9 +107,7 @@ void sys_exit_accept4(struct sys_exit_accept4_ctx *ctx) {
err = bpf_map_update_elem(&file_descriptor_to_ipv4, &key, &fdinfo, BPF_ANY); err = bpf_map_update_elem(&file_descriptor_to_ipv4, &key, &fdinfo, BPF_ANY);
if (err != 0) { if (err != 0) {
char msg[] = "Error putting fd to address mapping from accept (key: %ld) (err: %ld)"; log_error(ctx, LOG_ERROR_PUTTING_FD_MAPPING, id, err, ORIGIN_SYS_EXIT_ACCEPT4_CODE);
bpf_trace_printk(msg, sizeof(msg), key, err);
return;
} }
} }
@@ -145,9 +145,7 @@ void sys_enter_connect(struct sys_enter_connect_ctx *ctx) {
long err = bpf_map_update_elem(&connect_syscall_info, &id, &info, BPF_ANY); long err = bpf_map_update_elem(&connect_syscall_info, &id, &info, BPF_ANY);
if (err != 0) { if (err != 0) {
char msg[] = "Error putting connect info (id: %ld) (err: %ld)"; log_error(ctx, LOG_ERROR_PUTTING_CONNECT_INFO, id, err, 0l);
bpf_trace_printk(msg, sizeof(msg), id, err);
return;
} }
} }
@@ -176,6 +174,7 @@ void sys_exit_connect(struct sys_exit_connect_ctx *ctx) {
struct connect_info *infoPtr = bpf_map_lookup_elem(&connect_syscall_info, &id); struct connect_info *infoPtr = bpf_map_lookup_elem(&connect_syscall_info, &id);
if (infoPtr == NULL) { if (infoPtr == NULL) {
log_error(ctx, LOG_ERROR_GETTING_CONNECT_INFO, id, 0l, 0l);
return; return;
} }
@@ -185,12 +184,11 @@ void sys_exit_connect(struct sys_exit_connect_ctx *ctx) {
bpf_map_delete_elem(&connect_syscall_info, &id); bpf_map_delete_elem(&connect_syscall_info, &id);
if (err != 0) { if (err != 0) {
char msg[] = "Error reading connect info from connect syscall (id: %ld) (err: %ld)"; log_error(ctx, LOG_ERROR_READING_CONNECT_INFO, id, err, 0l);
bpf_trace_printk(msg, sizeof(msg), id, err);
return; return;
} }
if (info.addrlen != 16) { if (info.addrlen != IPV4_ADDR_LEN) {
// Currently only ipv4 is supported linux-src/include/linux/inet.h // Currently only ipv4 is supported linux-src/include/linux/inet.h
return; return;
} }
@@ -208,8 +206,6 @@ void sys_exit_connect(struct sys_exit_connect_ctx *ctx) {
err = bpf_map_update_elem(&file_descriptor_to_ipv4, &key, &fdinfo, BPF_ANY); err = bpf_map_update_elem(&file_descriptor_to_ipv4, &key, &fdinfo, BPF_ANY);
if (err != 0) { if (err != 0) {
char msg[] = "Error putting fd to address mapping from connect (key: %ld) (err: %ld)"; log_error(ctx, LOG_ERROR_PUTTING_FD_MAPPING, id, err, ORIGIN_SYS_EXIT_CONNECT_CODE);
bpf_trace_printk(msg, sizeof(msg), key, err);
return;
} }
} }

View File

@@ -7,6 +7,8 @@ Copyright (C) UP9 Inc.
#include "include/headers.h" #include "include/headers.h"
#include "include/util.h" #include "include/util.h"
#include "include/maps.h" #include "include/maps.h"
#include "include/log.h"
#include "include/logger_messages.h"
#include "include/pids.h" #include "include/pids.h"
struct sys_enter_read_ctx { struct sys_enter_read_ctx {
@@ -36,8 +38,7 @@ void sys_enter_read(struct sys_enter_read_ctx *ctx) {
long err = bpf_probe_read(&info, sizeof(struct ssl_info), infoPtr); long err = bpf_probe_read(&info, sizeof(struct ssl_info), infoPtr);
if (err != 0) { if (err != 0) {
char msg[] = "Error reading read info from read syscall (id: %ld) (err: %ld)"; log_error(ctx, LOG_ERROR_READING_SSL_CONTEXT, id, err, ORIGIN_SYS_ENTER_READ_CODE);
bpf_trace_printk(msg, sizeof(msg), id, err);
return; return;
} }
@@ -46,9 +47,7 @@ void sys_enter_read(struct sys_enter_read_ctx *ctx) {
err = bpf_map_update_elem(&ssl_read_context, &id, &info, BPF_ANY); err = bpf_map_update_elem(&ssl_read_context, &id, &info, BPF_ANY);
if (err != 0) { if (err != 0) {
char msg[] = "Error putting file descriptor from read syscall (id: %ld) (err: %ld)"; log_error(ctx, LOG_ERROR_PUTTING_FILE_DESCRIPTOR, id, err, ORIGIN_SYS_ENTER_READ_CODE);
bpf_trace_printk(msg, sizeof(msg), id, err);
return;
} }
} }
@@ -79,8 +78,7 @@ void sys_enter_write(struct sys_enter_write_ctx *ctx) {
long err = bpf_probe_read(&info, sizeof(struct ssl_info), infoPtr); long err = bpf_probe_read(&info, sizeof(struct ssl_info), infoPtr);
if (err != 0) { if (err != 0) {
char msg[] = "Error reading write context from write syscall (id: %ld) (err: %ld)"; log_error(ctx, LOG_ERROR_READING_SSL_CONTEXT, id, err, ORIGIN_SYS_ENTER_WRITE_CODE);
bpf_trace_printk(msg, sizeof(msg), id, err);
return; return;
} }
@@ -89,8 +87,6 @@ void sys_enter_write(struct sys_enter_write_ctx *ctx) {
err = bpf_map_update_elem(&ssl_write_context, &id, &info, BPF_ANY); err = bpf_map_update_elem(&ssl_write_context, &id, &info, BPF_ANY);
if (err != 0) { if (err != 0) {
char msg[] = "Error putting file descriptor from write syscall (id: %ld) (err: %ld)"; log_error(ctx, LOG_ERROR_PUTTING_FILE_DESCRIPTOR, id, err, ORIGIN_SYS_ENTER_WRITE_CODE);
bpf_trace_printk(msg, sizeof(msg), id, err);
return;
} }
} }

View File

@@ -0,0 +1,79 @@
/*
Note: This file is licenced differently from the rest of the project
SPDX-License-Identifier: GPL-2.0
Copyright (C) UP9 Inc.
*/
#ifndef __LOG__
#define __LOG__
// The same consts defined in bpf_logger.go
//
#define LOG_LEVEL_ERROR (0)
#define LOG_LEVEL_INFO (1)
#define LOG_LEVEL_DEBUG (2)
// The same struct can be found in bpf_logger.go
//
// Be careful when editing, alignment and padding should be exactly the same in go/c.
//
struct log_message {
__u32 level;
__u32 message_code;
__u64 arg1;
__u64 arg2;
__u64 arg3;
};
static __always_inline void log_error(void* ctx, __u16 message_code, __u64 arg1, __u64 arg2, __u64 arg3) {
struct log_message entry = {};
entry.level = LOG_LEVEL_ERROR;
entry.message_code = message_code;
entry.arg1 = arg1;
entry.arg2 = arg2;
entry.arg3 = arg3;
long err = bpf_perf_event_output(ctx, &log_buffer, BPF_F_CURRENT_CPU, &entry, sizeof(struct log_message));
if (err != 0) {
char msg[] = "Error writing log error to perf buffer - %ld";
bpf_trace_printk(msg, sizeof(msg), err);
}
}
static __always_inline void log_info(void* ctx, __u16 message_code, __u64 arg1, __u64 arg2, __u64 arg3) {
struct log_message entry = {};
entry.level = LOG_LEVEL_INFO;
entry.message_code = message_code;
entry.arg1 = arg1;
entry.arg2 = arg2;
entry.arg3 = arg3;
long err = bpf_perf_event_output(ctx, &log_buffer, BPF_F_CURRENT_CPU, &entry, sizeof(struct log_message));
if (err != 0) {
char msg[] = "Error writing log info to perf buffer - %ld";
bpf_trace_printk(msg, sizeof(msg), arg1, err);
}
}
static __always_inline void log_debug(void* ctx, __u16 message_code, __u64 arg1, __u64 arg2, __u64 arg3) {
struct log_message entry = {};
entry.level = LOG_LEVEL_DEBUG;
entry.message_code = message_code;
entry.arg1 = arg1;
entry.arg2 = arg2;
entry.arg3 = arg3;
long err = bpf_perf_event_output(ctx, &log_buffer, BPF_F_CURRENT_CPU, &entry, sizeof(struct log_message));
if (err != 0) {
char msg[] = "Error writing log debug to perf buffer - %ld";
bpf_trace_printk(msg, sizeof(msg), arg1, err);
}
}
#endif /* __LOG__ */

View File

@@ -0,0 +1,42 @@
/*
Note: This file is licenced differently from the rest of the project
SPDX-License-Identifier: GPL-2.0
Copyright (C) UP9 Inc.
*/
#ifndef __LOG_MESSAGES__
#define __LOG_MESSAGES__
// Must be synced with bpf_logger_messages.go
//
#define LOG_ERROR_READING_BYTES_COUNT (0)
#define LOG_ERROR_READING_FD_ADDRESS (1)
#define LOG_ERROR_READING_FROM_SSL_BUFFER (2)
#define LOG_ERROR_BUFFER_TOO_BIG (3)
#define LOG_ERROR_ALLOCATING_CHUNK (4)
#define LOG_ERROR_READING_SSL_CONTEXT (5)
#define LOG_ERROR_PUTTING_SSL_CONTEXT (6)
#define LOG_ERROR_GETTING_SSL_CONTEXT (7)
#define LOG_ERROR_MISSING_FILE_DESCRIPTOR (8)
#define LOG_ERROR_PUTTING_FILE_DESCRIPTOR (9)
#define LOG_ERROR_PUTTING_ACCEPT_INFO (10)
#define LOG_ERROR_GETTING_ACCEPT_INFO (11)
#define LOG_ERROR_READING_ACCEPT_INFO (12)
#define LOG_ERROR_PUTTING_FD_MAPPING (13)
#define LOG_ERROR_PUTTING_CONNECT_INFO (14)
#define LOG_ERROR_GETTING_CONNECT_INFO (15)
#define LOG_ERROR_READING_CONNECT_INFO (16)
// Sometimes we have the same error, happening from different locations.
// in order to be able to distinct between them in the log, we add an
// extra number that identify the location. The number can be anything,
// but do not give the same number to different origins.
//
#define ORIGIN_SSL_UPROBE_CODE (0l)
#define ORIGIN_SSL_URETPROBE_CODE (1l)
#define ORIGIN_SYS_ENTER_READ_CODE (2l)
#define ORIGIN_SYS_ENTER_WRITE_CODE (3l)
#define ORIGIN_SYS_EXIT_ACCEPT4_CODE (4l)
#define ORIGIN_SYS_EXIT_CONNECT_CODE (5l)
#endif /* __LOG_MESSAGES__ */

View File

@@ -70,5 +70,6 @@ BPF_LRU_HASH(ssl_write_context, __u64, struct ssl_info);
BPF_LRU_HASH(ssl_read_context, __u64, struct ssl_info); BPF_LRU_HASH(ssl_read_context, __u64, struct ssl_info);
BPF_HASH(file_descriptor_to_ipv4, __u64, struct fd_info); BPF_HASH(file_descriptor_to_ipv4, __u64, struct fd_info);
BPF_PERF_OUTPUT(chunks_buffer); BPF_PERF_OUTPUT(chunks_buffer);
BPF_PERF_OUTPUT(log_buffer);
#endif /* __MAPS__ */ #endif /* __MAPS__ */

View File

@@ -7,6 +7,8 @@ Copyright (C) UP9 Inc.
#include "include/headers.h" #include "include/headers.h"
#include "include/util.h" #include "include/util.h"
#include "include/maps.h" #include "include/maps.h"
#include "include/log.h"
#include "include/logger_messages.h"
#include "include/pids.h" #include "include/pids.h"
// Heap-like area for eBPF programs - stack size limited to 512 bytes, we must use maps for bigger (chunk) objects. // Heap-like area for eBPF programs - stack size limited to 512 bytes, we must use maps for bigger (chunk) objects.
@@ -39,15 +41,14 @@ static __always_inline int get_count_bytes(struct pt_regs *ctx, struct ssl_info*
long err = bpf_probe_read(&countBytes, sizeof(size_t), (void*) info->count_ptr); long err = bpf_probe_read(&countBytes, sizeof(size_t), (void*) info->count_ptr);
if (err != 0) { if (err != 0) {
char msg[] = "Error reading bytes count of _ex (id: %ld) (err: %ld)"; log_error(ctx, LOG_ERROR_READING_BYTES_COUNT, id, err, 0l);
bpf_trace_printk(msg, sizeof(msg), id, err);
return 0; return 0;
} }
return countBytes; return countBytes;
} }
static __always_inline void add_address_to_chunk(struct tlsChunk* chunk, __u64 id, __u32 fd) { static __always_inline void add_address_to_chunk(struct pt_regs *ctx, struct tlsChunk* chunk, __u64 id, __u32 fd) {
__u32 pid = id >> 32; __u32 pid = id >> 32;
__u64 key = (__u64) pid << 32 | fd; __u64 key = (__u64) pid << 32 | fd;
@@ -61,8 +62,7 @@ static __always_inline void add_address_to_chunk(struct tlsChunk* chunk, __u64 i
chunk->flags |= (fdinfo->flags & FLAGS_IS_CLIENT_BIT); chunk->flags |= (fdinfo->flags & FLAGS_IS_CLIENT_BIT);
if (err != 0) { if (err != 0) {
char msg[] = "Error reading from fd address %ld - %ld"; log_error(ctx, LOG_ERROR_READING_FD_ADDRESS, id, err, 0l);
bpf_trace_printk(msg, sizeof(msg), id, err);
} }
} }
@@ -88,8 +88,7 @@ static __always_inline void send_chunk_part(struct pt_regs *ctx, __u8* buffer, _
} }
if (err != 0) { if (err != 0) {
char msg[] = "Error reading from ssl buffer %ld - %ld"; log_error(ctx, LOG_ERROR_READING_FROM_SSL_BUFFER, id, err, 0l);
bpf_trace_printk(msg, sizeof(msg), id, err);
return; return;
} }
@@ -101,8 +100,9 @@ static __always_inline void send_chunk(struct pt_regs *ctx, __u8* buffer, __u64
// //
// https://lwn.net/Articles/794934/ // https://lwn.net/Articles/794934/
// //
// If we want to compile in kernel older than 5.3, we should add "#pragma unroll" to this loop // However we want to run in kernel older than 5.3, hence we use "#pragma unroll" anyway
// //
#pragma unroll
for (int i = 0; i < MAX_CHUNKS_PER_OPERATION; i++) { for (int i = 0; i < MAX_CHUNKS_PER_OPERATION; i++) {
if (chunk->len <= (CHUNK_SIZE * i)) { if (chunk->len <= (CHUNK_SIZE * i)) {
break; break;
@@ -120,8 +120,7 @@ static __always_inline void output_ssl_chunk(struct pt_regs *ctx, struct ssl_inf
} }
if (countBytes > (CHUNK_SIZE * MAX_CHUNKS_PER_OPERATION)) { if (countBytes > (CHUNK_SIZE * MAX_CHUNKS_PER_OPERATION)) {
char msg[] = "Buffer too big %d (id: %ld)"; log_error(ctx, LOG_ERROR_BUFFER_TOO_BIG, id, countBytes, 0l);
bpf_trace_printk(msg, sizeof(msg), countBytes, id);
return; return;
} }
@@ -134,8 +133,7 @@ static __always_inline void output_ssl_chunk(struct pt_regs *ctx, struct ssl_inf
chunk = bpf_map_lookup_elem(&heap, &zero); chunk = bpf_map_lookup_elem(&heap, &zero);
if (!chunk) { if (!chunk) {
char msg[] = "Unable to allocate chunk (id: %ld)"; log_error(ctx, LOG_ERROR_ALLOCATING_CHUNK, id, 0l, 0l);
bpf_trace_printk(msg, sizeof(msg), id);
return; return;
} }
@@ -145,11 +143,11 @@ static __always_inline void output_ssl_chunk(struct pt_regs *ctx, struct ssl_inf
chunk->len = countBytes; chunk->len = countBytes;
chunk->fd = info->fd; chunk->fd = info->fd;
add_address_to_chunk(chunk, id, chunk->fd); add_address_to_chunk(ctx, chunk, id, chunk->fd);
send_chunk(ctx, info->buffer, id, chunk); send_chunk(ctx, info->buffer, id, chunk);
} }
static __always_inline void ssl_uprobe(void* ssl, void* buffer, int num, struct bpf_map_def* map_fd, size_t *count_ptr) { static __always_inline void ssl_uprobe(struct pt_regs *ctx, void* ssl, void* buffer, int num, struct bpf_map_def* map_fd, size_t *count_ptr) {
__u64 id = bpf_get_current_pid_tgid(); __u64 id = bpf_get_current_pid_tgid();
if (!should_tap(id >> 32)) { if (!should_tap(id >> 32)) {
@@ -166,8 +164,7 @@ static __always_inline void ssl_uprobe(void* ssl, void* buffer, int num, struct
long err = bpf_probe_read(&info, sizeof(struct ssl_info), infoPtr); long err = bpf_probe_read(&info, sizeof(struct ssl_info), infoPtr);
if (err != 0) { if (err != 0) {
char msg[] = "Error reading old ssl context (id: %ld) (err: %ld)"; log_error(ctx, LOG_ERROR_READING_SSL_CONTEXT, id, err, ORIGIN_SSL_UPROBE_CODE);
bpf_trace_printk(msg, sizeof(msg), id, err);
} }
if ((bpf_ktime_get_ns() - info.created_at_nano) > SSL_INFO_MAX_TTL_NANO) { if ((bpf_ktime_get_ns() - info.created_at_nano) > SSL_INFO_MAX_TTL_NANO) {
@@ -184,8 +181,7 @@ static __always_inline void ssl_uprobe(void* ssl, void* buffer, int num, struct
long err = bpf_map_update_elem(map_fd, &id, &info, BPF_ANY); long err = bpf_map_update_elem(map_fd, &id, &info, BPF_ANY);
if (err != 0) { if (err != 0) {
char msg[] = "Error putting ssl context (id: %ld) (err: %ld)"; log_error(ctx, LOG_ERROR_PUTTING_SSL_CONTEXT, id, err, 0l);
bpf_trace_printk(msg, sizeof(msg), id, err);
} }
} }
@@ -199,8 +195,7 @@ static __always_inline void ssl_uretprobe(struct pt_regs *ctx, struct bpf_map_de
struct ssl_info *infoPtr = bpf_map_lookup_elem(map_fd, &id); struct ssl_info *infoPtr = bpf_map_lookup_elem(map_fd, &id);
if (infoPtr == NULL) { if (infoPtr == NULL) {
char msg[] = "Error getting ssl context info (id: %ld)"; log_error(ctx, LOG_ERROR_GETTING_SSL_CONTEXT, id, 0l, 0l);
bpf_trace_printk(msg, sizeof(msg), id);
return; return;
} }
@@ -220,14 +215,12 @@ static __always_inline void ssl_uretprobe(struct pt_regs *ctx, struct bpf_map_de
// bpf_map_delete_elem(map_fd, &id); // bpf_map_delete_elem(map_fd, &id);
if (err != 0) { if (err != 0) {
char msg[] = "Error reading ssl context (id: %ld) (err: %ld)"; log_error(ctx, LOG_ERROR_READING_SSL_CONTEXT, id, err, ORIGIN_SSL_URETPROBE_CODE);
bpf_trace_printk(msg, sizeof(msg), id, err);
return; return;
} }
if (info.fd == -1) { if (info.fd == -1) {
char msg[] = "File descriptor is missing from ssl info (id: %ld)"; log_error(ctx, LOG_ERROR_MISSING_FILE_DESCRIPTOR, id, 0l, 0l);
bpf_trace_printk(msg, sizeof(msg), id);
return; return;
} }
@@ -236,7 +229,7 @@ static __always_inline void ssl_uretprobe(struct pt_regs *ctx, struct bpf_map_de
SEC("uprobe/ssl_write") SEC("uprobe/ssl_write")
void BPF_KPROBE(ssl_write, void* ssl, void* buffer, int num) { void BPF_KPROBE(ssl_write, void* ssl, void* buffer, int num) {
ssl_uprobe(ssl, buffer, num, &ssl_write_context, 0); ssl_uprobe(ctx, ssl, buffer, num, &ssl_write_context, 0);
} }
SEC("uretprobe/ssl_write") SEC("uretprobe/ssl_write")
@@ -246,7 +239,7 @@ void BPF_KPROBE(ssl_ret_write) {
SEC("uprobe/ssl_read") SEC("uprobe/ssl_read")
void BPF_KPROBE(ssl_read, void* ssl, void* buffer, int num) { void BPF_KPROBE(ssl_read, void* ssl, void* buffer, int num) {
ssl_uprobe(ssl, buffer, num, &ssl_read_context, 0); ssl_uprobe(ctx, ssl, buffer, num, &ssl_read_context, 0);
} }
SEC("uretprobe/ssl_read") SEC("uretprobe/ssl_read")
@@ -256,7 +249,7 @@ void BPF_KPROBE(ssl_ret_read) {
SEC("uprobe/ssl_write_ex") SEC("uprobe/ssl_write_ex")
void BPF_KPROBE(ssl_write_ex, void* ssl, void* buffer, size_t num, size_t *written) { void BPF_KPROBE(ssl_write_ex, void* ssl, void* buffer, size_t num, size_t *written) {
ssl_uprobe(ssl, buffer, num, &ssl_write_context, written); ssl_uprobe(ctx, ssl, buffer, num, &ssl_write_context, written);
} }
SEC("uretprobe/ssl_write_ex") SEC("uretprobe/ssl_write_ex")
@@ -266,7 +259,7 @@ void BPF_KPROBE(ssl_ret_write_ex) {
SEC("uprobe/ssl_read_ex") SEC("uprobe/ssl_read_ex")
void BPF_KPROBE(ssl_read_ex, void* ssl, void* buffer, size_t num, size_t *readbytes) { void BPF_KPROBE(ssl_read_ex, void* ssl, void* buffer, size_t num, size_t *readbytes) {
ssl_uprobe(ssl, buffer, num, &ssl_read_context, readbytes); ssl_uprobe(ctx, ssl, buffer, num, &ssl_read_context, readbytes);
} }
SEC("uretprobe/ssl_read_ex") SEC("uretprobe/ssl_read_ex")

View File

@@ -7,6 +7,8 @@ Copyright (C) UP9 Inc.
#include "include/headers.h" #include "include/headers.h"
#include "include/util.h" #include "include/util.h"
#include "include/maps.h" #include "include/maps.h"
#include "include/log.h"
#include "include/logger_messages.h"
#include "include/pids.h" #include "include/pids.h"
// To avoid multiple .o files // To avoid multiple .o files

116
tap/tlstapper/bpf_logger.go Normal file
View File

@@ -0,0 +1,116 @@
package tlstapper
import (
"bytes"
"encoding/binary"
"strings"
"github.com/cilium/ebpf/perf"
"github.com/go-errors/errors"
"github.com/up9inc/mizu/shared/logger"
)
const logPrefix = "[bpf] "
// The same consts defined in log.h
//
const logLevelError = 0
const logLevelInfo = 1
const logLevelDebug = 2
type logMessage struct {
Level uint32
MessageCode uint32
Arg1 uint64
Arg2 uint64
Arg3 uint64
}
type bpfLogger struct {
logReader *perf.Reader
}
func newBpfLogger() *bpfLogger {
return &bpfLogger{
logReader: nil,
}
}
func (p *bpfLogger) init(bpfObjects *tlsTapperObjects, bufferSize int) error {
var err error
p.logReader, err = perf.NewReader(bpfObjects.LogBuffer, bufferSize)
if err != nil {
return errors.Wrap(err, 0)
}
return nil
}
func (p *bpfLogger) close() error {
return p.logReader.Close()
}
func (p *bpfLogger) poll() {
logger.Log.Infof("Start polling for bpf logs")
for {
record, err := p.logReader.Read()
if err != nil {
if errors.Is(err, perf.ErrClosed) {
return
}
LogError(errors.Errorf("Error reading from bpf logger perf buffer, aboring logger! %w", err))
return
}
if record.LostSamples != 0 {
logger.Log.Infof("Log buffer is full, dropped %d logs", record.LostSamples)
continue
}
buffer := bytes.NewReader(record.RawSample)
var log logMessage
if err := binary.Read(buffer, binary.LittleEndian, &log); err != nil {
LogError(errors.Errorf("Error parsing log %v", err))
continue
}
p.log(&log)
}
}
func (p *bpfLogger) log(log *logMessage) {
if int(log.MessageCode) >= len(bpfLogMessages) {
logger.Log.Errorf("Unknown message code from bpf logger %d", log.MessageCode)
return
}
format := bpfLogMessages[log.MessageCode]
tokensCount := strings.Count(format, "%")
if tokensCount == 0 {
p.logLevel(log.Level, format)
} else if tokensCount == 1 {
p.logLevel(log.Level, format, log.Arg1)
} else if tokensCount == 2 {
p.logLevel(log.Level, format, log.Arg1, log.Arg2)
} else if tokensCount == 3 {
p.logLevel(log.Level, format, log.Arg1, log.Arg2, log.Arg3)
}
}
func (p *bpfLogger) logLevel(level uint32, format string, args ...interface{}) {
if level == logLevelError {
logger.Log.Errorf(logPrefix+format, args...)
} else if level == logLevelInfo {
logger.Log.Infof(logPrefix+format, args...)
} else if level == logLevelDebug {
logger.Log.Debugf(logPrefix+format, args...)
}
}

View File

@@ -0,0 +1,25 @@
package tlstapper
// Must be synced with logger_messages.h
//
var bpfLogMessages = []string {
/*0000*/ "[%d] Unable to read bytes count from _ex methods [err: %d]",
/*0001*/ "[%d] Unable to read ipv4 address [err: %d]",
/*0002*/ "[%d] Unable to read ssl buffer [err: %d]",
/*0003*/ "[%d] Buffer is too big [size: %d]",
/*0004*/ "[%d] Unable to allocate chunk in bpf heap",
/*0005*/ "[%d] Unable to read ssl context [err: %d] [origin: %d]",
/*0006*/ "[%d] Unable to put ssl context [err: %d]",
/*0007*/ "[%d] Unable to get ssl context",
/*0008*/ "[%d] File descriptor is missing for tls chunk",
/*0009*/ "[%d] Unable to put file descriptor [err: %d] [origin: %d]",
/*0010*/ "[%d] Unable to put accept info [err: %d]",
/*0011*/ "[%d] Unable to get accept info",
/*0012*/ "[%d] Unable to read accept info [err: %d]",
/*0013*/ "[%d] Unable to put file descriptor to address mapping [err: %d] [origin: %d]",
/*0014*/ "[%d] Unable to put connect info [err: %d]",
/*0015*/ "[%d] Unable to get connect info",
/*0016*/ "[%d] Unable to read connect info [err: %d]",
}

View File

@@ -24,6 +24,8 @@ func UpdateTapTargets(tls *TlsTapper, pods *[]v1.Pod, procfs string) error {
if err != nil { if err != nil {
return err return err
} }
tls.ClearPids()
for _, pid := range containerPids { for _, pid := range containerPids {
if err := tls.AddPid(procfs, pid); err != nil { if err := tls.AddPid(procfs, pid); err != nil {

View File

@@ -5,8 +5,11 @@ import (
"github.com/go-errors/errors" "github.com/go-errors/errors"
"github.com/up9inc/mizu/shared/logger" "github.com/up9inc/mizu/shared/logger"
"github.com/up9inc/mizu/tap/api" "github.com/up9inc/mizu/tap/api"
"sync"
) )
const GLOABL_TAP_PID = 0
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go tlsTapper bpf/tls_tapper.c -- -O2 -g -D__TARGET_ARCH_x86 //go:generate go run github.com/cilium/ebpf/cmd/bpf2go tlsTapper bpf/tls_tapper.c -- -O2 -g -D__TARGET_ARCH_x86
type TlsTapper struct { type TlsTapper struct {
@@ -14,10 +17,12 @@ type TlsTapper struct {
syscallHooks syscallHooks syscallHooks syscallHooks
sslHooksStructs []sslHooks sslHooksStructs []sslHooks
poller *tlsPoller poller *tlsPoller
bpfLogger *bpfLogger
registeredPids sync.Map
} }
func (t *TlsTapper) Init(bufferSize int, procfs string, extension *api.Extension) error { func (t *TlsTapper) Init(chunksBufferSize int, logBufferSize int, procfs string, extension *api.Extension) error {
logger.Log.Infof("Initializing tls tapper (bufferSize: %v)", bufferSize) logger.Log.Infof("Initializing tls tapper (chunksSize: %d) (logSize: %d)", chunksBufferSize, logBufferSize)
if err := setupRLimit(); err != nil { if err := setupRLimit(); err != nil {
return err return err
@@ -35,16 +40,25 @@ func (t *TlsTapper) Init(bufferSize int, procfs string, extension *api.Extension
t.sslHooksStructs = make([]sslHooks, 0) t.sslHooksStructs = make([]sslHooks, 0)
t.bpfLogger = newBpfLogger()
if err := t.bpfLogger.init(&t.bpfObjects, logBufferSize); err != nil {
return err
}
t.poller = newTlsPoller(t, extension, procfs) t.poller = newTlsPoller(t, extension, procfs)
return t.poller.init(&t.bpfObjects, bufferSize) return t.poller.init(&t.bpfObjects, chunksBufferSize)
} }
func (t *TlsTapper) Poll(emitter api.Emitter, options *api.TrafficFilteringOptions) { func (t *TlsTapper) Poll(emitter api.Emitter, options *api.TrafficFilteringOptions) {
t.poller.poll(emitter, options) t.poller.poll(emitter, options)
} }
func (t *TlsTapper) PollForLogging() {
t.bpfLogger.poll()
}
func (t *TlsTapper) GlobalTap(sslLibrary string) error { func (t *TlsTapper) GlobalTap(sslLibrary string) error {
return t.tapPid(0, sslLibrary) return t.tapPid(GLOABL_TAP_PID, sslLibrary)
} }
func (t *TlsTapper) AddPid(procfs string, pid uint32) error { func (t *TlsTapper) AddPid(procfs string, pid uint32) error {
@@ -70,6 +84,21 @@ func (t *TlsTapper) RemovePid(pid uint32) error {
return nil return nil
} }
func (t *TlsTapper) ClearPids() {
t.registeredPids.Range(func(key, v interface{}) bool {
pid := key.(uint32)
if pid == GLOABL_TAP_PID {
return true
}
if err := t.RemovePid(pid); err != nil {
LogError(err)
}
t.registeredPids.Delete(key)
return true
})
}
func (t *TlsTapper) Close() []error { func (t *TlsTapper) Close() []error {
errors := make([]error, 0) errors := make([]error, 0)
@@ -83,6 +112,10 @@ func (t *TlsTapper) Close() []error {
errors = append(errors, sslHooks.close()...) errors = append(errors, sslHooks.close()...)
} }
if err := t.bpfLogger.close(); err != nil {
errors = append(errors, err)
}
if err := t.poller.close(); err != nil { if err := t.poller.close(); err != nil {
errors = append(errors, err) errors = append(errors, err)
} }
@@ -116,6 +149,8 @@ func (t *TlsTapper) tapPid(pid uint32, sslLibrary string) error {
if err := pids.Put(pid, uint32(1)); err != nil { if err := pids.Put(pid, uint32(1)); err != nil {
return errors.Wrap(err, 0) return errors.Wrap(err, 0)
} }
t.registeredPids.Store(pid, true)
return nil return nil
} }

View File

@@ -78,6 +78,7 @@ type tlsTapperMapSpecs struct {
ConnectSyscallInfo *ebpf.MapSpec `ebpf:"connect_syscall_info"` ConnectSyscallInfo *ebpf.MapSpec `ebpf:"connect_syscall_info"`
FileDescriptorToIpv4 *ebpf.MapSpec `ebpf:"file_descriptor_to_ipv4"` FileDescriptorToIpv4 *ebpf.MapSpec `ebpf:"file_descriptor_to_ipv4"`
Heap *ebpf.MapSpec `ebpf:"heap"` Heap *ebpf.MapSpec `ebpf:"heap"`
LogBuffer *ebpf.MapSpec `ebpf:"log_buffer"`
PidsMap *ebpf.MapSpec `ebpf:"pids_map"` PidsMap *ebpf.MapSpec `ebpf:"pids_map"`
SslReadContext *ebpf.MapSpec `ebpf:"ssl_read_context"` SslReadContext *ebpf.MapSpec `ebpf:"ssl_read_context"`
SslWriteContext *ebpf.MapSpec `ebpf:"ssl_write_context"` SslWriteContext *ebpf.MapSpec `ebpf:"ssl_write_context"`
@@ -107,6 +108,7 @@ type tlsTapperMaps struct {
ConnectSyscallInfo *ebpf.Map `ebpf:"connect_syscall_info"` ConnectSyscallInfo *ebpf.Map `ebpf:"connect_syscall_info"`
FileDescriptorToIpv4 *ebpf.Map `ebpf:"file_descriptor_to_ipv4"` FileDescriptorToIpv4 *ebpf.Map `ebpf:"file_descriptor_to_ipv4"`
Heap *ebpf.Map `ebpf:"heap"` Heap *ebpf.Map `ebpf:"heap"`
LogBuffer *ebpf.Map `ebpf:"log_buffer"`
PidsMap *ebpf.Map `ebpf:"pids_map"` PidsMap *ebpf.Map `ebpf:"pids_map"`
SslReadContext *ebpf.Map `ebpf:"ssl_read_context"` SslReadContext *ebpf.Map `ebpf:"ssl_read_context"`
SslWriteContext *ebpf.Map `ebpf:"ssl_write_context"` SslWriteContext *ebpf.Map `ebpf:"ssl_write_context"`
@@ -119,6 +121,7 @@ func (m *tlsTapperMaps) Close() error {
m.ConnectSyscallInfo, m.ConnectSyscallInfo,
m.FileDescriptorToIpv4, m.FileDescriptorToIpv4,
m.Heap, m.Heap,
m.LogBuffer,
m.PidsMap, m.PidsMap,
m.SslReadContext, m.SslReadContext,
m.SslWriteContext, m.SslWriteContext,

Binary file not shown.

View File

@@ -78,6 +78,7 @@ type tlsTapperMapSpecs struct {
ConnectSyscallInfo *ebpf.MapSpec `ebpf:"connect_syscall_info"` ConnectSyscallInfo *ebpf.MapSpec `ebpf:"connect_syscall_info"`
FileDescriptorToIpv4 *ebpf.MapSpec `ebpf:"file_descriptor_to_ipv4"` FileDescriptorToIpv4 *ebpf.MapSpec `ebpf:"file_descriptor_to_ipv4"`
Heap *ebpf.MapSpec `ebpf:"heap"` Heap *ebpf.MapSpec `ebpf:"heap"`
LogBuffer *ebpf.MapSpec `ebpf:"log_buffer"`
PidsMap *ebpf.MapSpec `ebpf:"pids_map"` PidsMap *ebpf.MapSpec `ebpf:"pids_map"`
SslReadContext *ebpf.MapSpec `ebpf:"ssl_read_context"` SslReadContext *ebpf.MapSpec `ebpf:"ssl_read_context"`
SslWriteContext *ebpf.MapSpec `ebpf:"ssl_write_context"` SslWriteContext *ebpf.MapSpec `ebpf:"ssl_write_context"`
@@ -107,6 +108,7 @@ type tlsTapperMaps struct {
ConnectSyscallInfo *ebpf.Map `ebpf:"connect_syscall_info"` ConnectSyscallInfo *ebpf.Map `ebpf:"connect_syscall_info"`
FileDescriptorToIpv4 *ebpf.Map `ebpf:"file_descriptor_to_ipv4"` FileDescriptorToIpv4 *ebpf.Map `ebpf:"file_descriptor_to_ipv4"`
Heap *ebpf.Map `ebpf:"heap"` Heap *ebpf.Map `ebpf:"heap"`
LogBuffer *ebpf.Map `ebpf:"log_buffer"`
PidsMap *ebpf.Map `ebpf:"pids_map"` PidsMap *ebpf.Map `ebpf:"pids_map"`
SslReadContext *ebpf.Map `ebpf:"ssl_read_context"` SslReadContext *ebpf.Map `ebpf:"ssl_read_context"`
SslWriteContext *ebpf.Map `ebpf:"ssl_write_context"` SslWriteContext *ebpf.Map `ebpf:"ssl_write_context"`
@@ -119,6 +121,7 @@ func (m *tlsTapperMaps) Close() error {
m.ConnectSyscallInfo, m.ConnectSyscallInfo,
m.FileDescriptorToIpv4, m.FileDescriptorToIpv4,
m.Heap, m.Heap,
m.LogBuffer,
m.PidsMap, m.PidsMap,
m.SslReadContext, m.SslReadContext,
m.SslWriteContext, m.SslWriteContext,

Binary file not shown.

View File

@@ -1,4 +1,4 @@
import TrafficViewer,{useWS, DEFAULT_QUERY} from '@up9/mizu-common'; import TrafficViewer,{useWS, DEFAULT_QUERY, OasModal} from '@up9/mizu-common';
import "@up9/mizu-common/dist/index.css" import "@up9/mizu-common/dist/index.css"
import {useEffect} from 'react'; import {useEffect} from 'react';
import Api, {getWebsocketUrl} from "./api"; import Api, {getWebsocketUrl} from "./api";
@@ -17,8 +17,7 @@ const App = () => {
},[]) },[])
return <> return <>
<TrafficViewer message={message} error={error} isWebSocketOpen={isOpen}
trafficViewerApiProp={trafficViewerApi} ></TrafficViewer>
</> </>
} }

View File

@@ -1,12 +1,12 @@
{ {
"name": "@up9/mizu-common", "name": "@up9/mizu-common",
"version": "1.0.10", "version": "0.0.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@up9/mizu-common", "name": "@up9/mizu-common",
"version": "1.0.10", "version": "0.0.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@craco/craco": "^6.4.3", "@craco/craco": "^6.4.3",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@up9/mizu-common", "name": "@up9/mizu-common",
"version": "1.0.132", "version": "0.0.0",
"description": "Made with create-react-library", "description": "Made with create-react-library",
"author": "", "author": "",
"license": "MIT", "license": "MIT",
@@ -27,7 +27,7 @@
"@material-ui/icons": "^4.11.2", "@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.60", "@material-ui/lab": "^4.0.0-alpha.60",
"node-sass": "^6.0.0", "node-sass": "^6.0.0",
"react":"^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"recoil": "^0.5.2", "recoil": "^0.5.2",
"react-copy-to-clipboard": "^5.0.3", "react-copy-to-clipboard": "^5.0.3",
@@ -77,7 +77,7 @@
"rollup-plugin-postcss": "^4.0.2", "rollup-plugin-postcss": "^4.0.2",
"rollup-plugin-sass": "^1.2.10", "rollup-plugin-sass": "^1.2.10",
"rollup-plugin-scss": "^3.0.0", "rollup-plugin-scss": "^3.0.0",
"react":"^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"typescript": "^4.2.4" "typescript": "^4.2.4"
}, },

View File

@@ -6,23 +6,32 @@
padding: 10px padding: 10px
.selectHeader .selectHeader
font-family: Source Sans Pro,Lucida Grande,Tahoma,sans-serif
display: flex display: flex
align-items: center align-items: center
font-weight: 900
width: 100% width: 100%
margin-top: -1% margin-top: -1%
.openApilogo .openApilogo
width: 36px width: 43px
.title .title
color:#494677 color:#494677
font-family: Lato font-family: Source Sans Pro,Lucida Grande,Tahoma,sans-serif
font-size: 20px font-size: 28px
font-weight: 600 font-weight: 600
.selectContainer .selectContainer
margin-left: 1% width: 14%
margin-bottom: 1%
margin-top: 1%
.redoc .redoc
height: 98% height: 85%
overflow-y: scroll overflow-y: scroll
.borderLine
border-top: 1px solid #dee6fe
margin-bottom: 1%

View File

@@ -1,4 +1,4 @@
import { Box, Fade, FormControl, MenuItem, Modal, Backdrop, ListSubheader } from "@material-ui/core"; import { Box, Fade, FormControl, MenuItem, Modal, Backdrop } from "@material-ui/core";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { RedocStandalone } from "redoc"; import { RedocStandalone } from "redoc";
import closeIcon from "assets/closeIcon.svg"; import closeIcon from "assets/closeIcon.svg";
@@ -9,6 +9,7 @@ import { redocThemeOptions } from "./redocThemeOptions";
import React from "react"; import React from "react";
import { Select } from "../UI/Select"; import { Select } from "../UI/Select";
const modalStyle = { const modalStyle = {
position: 'absolute', position: 'absolute',
top: '6%', top: '6%',
@@ -23,63 +24,44 @@ const modalStyle = {
color: '#000', color: '#000',
}; };
const ipAddressWithPortRegex = new RegExp('([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}):([0-9]{1,5})');
const OasModal = ({ openModal, handleCloseModal, getOasServices, getOasByService}) => { const OasModal = ({ openModal, handleCloseModal, getOasServices, getOasByService }) => {
const [oasServices, setOasServices] = useState([] as string[]) const [oasServices, setOasServices] = useState([] as string[])
const [selectedServiceName, setSelectedServiceName] = useState(""); const [selectedServiceName, setSelectedServiceName] = useState("");
const [selectedServiceSpec, setSelectedServiceSpec] = useState(null); const [selectedServiceSpec, setSelectedServiceSpec] = useState(null);
const [resolvedServices, setResolvedServices] = useState([]);
const [unResolvedServices, setUnResolvedServices] = useState([]);
const onSelectedOASService = useCallback( async (selectedService) => { const onSelectedOASService = async (selectedService) => {
if (!!selectedService){ if (oasServices.length === 0) {
setSelectedServiceName(selectedService); setSelectedServiceSpec(null);
if(oasServices.length === 0){ setSelectedServiceName("");
return return
}
try {
const data = await getOasByService(selectedService);
setSelectedServiceSpec(data);
} catch (e) {
toast.error("Error occurred while fetching service OAS spec");
console.error(e);
}
} }
},[oasServices.length]) else {
setSelectedServiceName(selectedService ? selectedService : oasServices[0]);
const resolvedArrayBuilder = useCallback(async(services) => { }
const resServices = []; try {
const unResServices = []; const data = await getOasByService(selectedService ? selectedService : oasServices[0]);
services.forEach(s => { setSelectedServiceSpec(data);
if(ipAddressWithPortRegex.test(s)){ } catch (e) {
unResServices.push(s); toast.error("Error occurred while fetching service OAS spec");
} console.error(e);
else { }
resServices.push(s); };
}
});
resServices.sort();
unResServices.sort();
onSelectedOASService(resServices[0]);
setResolvedServices(resServices);
setUnResolvedServices(unResServices);
},[onSelectedOASService])
useEffect(() => { useEffect(() => {
(async () => { (async () => {
try { try {
const services = await getOasServices(); const services = await getOasServices();
resolvedArrayBuilder(services);
setOasServices(services); setOasServices(services);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }
})(); })();
}, [openModal,resolvedArrayBuilder]); }, [openModal]);
useEffect(() => {
onSelectedOASService(null);
}, [oasServices])
return ( return (
<Modal <Modal
@@ -90,48 +72,41 @@ const OasModal = ({ openModal, handleCloseModal, getOasServices, getOasByService
closeAfterTransition closeAfterTransition
BackdropComponent={Backdrop} BackdropComponent={Backdrop}
BackdropProps={{ BackdropProps={{
timeout: 500, timeout: 500,
}} }}
> >
<Fade in={openModal}> <Fade in={openModal}>
<Box sx={modalStyle}> <Box sx={modalStyle}>
<div className={style.boxContainer}> <div className={style.boxContainer}>
<div className={style.selectHeader}> <div className={style.selectHeader}>
<div><img src={openApiLogo} alt="openApi" className={style.openApilogo}/></div> <div><img src={openApiLogo} alt="openAPI" className={style.openApilogo} /></div>
<div className={style.title}>OpenAPI selected service: </div> <div className={style.title}>Service Catalog</div>
<div className={style.selectContainer} >
<FormControl>
<Select
labelId="service-select-label"
id="service-select"
placeholder="Show OAS"
value={selectedServiceName}
onChangeCb={onSelectedOASService}
>
<ListSubheader disableSticky={true}>Resolved</ListSubheader>
{resolvedServices.map((service) => (
<MenuItem key={service} value={service}>
{service}
</MenuItem>
))}
<ListSubheader disableSticky={true}>UnResolved</ListSubheader>
{unResolvedServices.map((service) => (
<MenuItem key={service} value={service}>
{service}
</MenuItem>
))}
</Select>
</FormControl>
</div>
</div> </div>
<div style={{ cursor: "pointer" }}> <div style={{ cursor: "pointer" }}>
<img src={closeIcon} alt="close" onClick={handleCloseModal} /> <img src={closeIcon} alt="close" onClick={handleCloseModal} />
</div> </div>
</div> </div>
<div className={style.selectContainer} >
<FormControl>
<Select
labelId="service-select-label"
id="service-select"
value={selectedServiceName}
onChangeCb={onSelectedOASService}
>
{oasServices.map((service) => (
<MenuItem key={service} value={service}>
{service}
</MenuItem>
))}
</Select>
</FormControl>
</div>
<div className={style.borderLine}></div>
<div className={style.redoc}> <div className={style.redoc}>
{selectedServiceSpec && <RedocStandalone {selectedServiceSpec && <RedocStandalone
spec={selectedServiceSpec} spec={selectedServiceSpec}
options={redocThemeOptions}/>} options={redocThemeOptions} />}
</div> </div>
</Box> </Box>
</Fade> </Fade>

View File

@@ -1,35 +1,52 @@
export const redocThemeOptions = { const fontFamilyVar = "Source Sans Pro, Lucida Grande, Tahoma, sans-serif"
theme:{
codeBlock:{ export const redocThemeOptions = {
backgroundColor:"#11171a", theme: {
}, codeBlock: {
colors:{ backgroundColor: "#14161c",
responses:{ },
error:{ components: {
tabTextColor:"#1b1b29" buttons: {
}, fontFamily: fontFamilyVar,
info:{ },
tabTextColor:"#1b1b29", httpBadges: {
}, fontFamily: fontFamilyVar,
success:{ }
tabTextColor:"#0c0b1a" },
}, colors: {
}, responses: {
text:{ error: {
primary:"#1b1b29", tabTextColor: "#1b1b29"
secondary:"#4d4d4d" },
} info: {
}, tabTextColor: "#1b1b29",
rightPanel:{ },
backgroundColor:"#253237", success: {
}, tabTextColor: "#0c0b1a"
sidebar:{ },
backgroundColor:"#ffffff" },
}, text: {
typography:{ primary: "#1b1b29",
code:{ secondary: "#4d4d4d"
color:"#0c0b1a" }
} },
} rightPanel: {
} backgroundColor: "#0D0B1D",
} },
sidebar: {
backgroundColor: "#ffffff"
},
typography: {
code: {
color: "#0c0b1a",
fontFamily: fontFamilyVar
},
fontFamily: fontFamilyVar,
fontSize: "90%",
fontWieght: "normal",
headings: {
fontFamily: fontFamilyVar
}
}
}
}

View File

@@ -10,6 +10,7 @@ import entriesAtom from "../../recoil/entries";
import queryAtom from "../../recoil/query"; import queryAtom from "../../recoil/query";
import TrafficViewerApiAtom from "../../recoil/TrafficViewerApi"; import TrafficViewerApiAtom from "../../recoil/TrafficViewerApi";
import TrafficViewerApi from "./TrafficViewerApi"; import TrafficViewerApi from "./TrafficViewerApi";
import focusedEntryIdAtom from "../../recoil/focusedEntryId";
interface EntriesListProps { interface EntriesListProps {
listEntryREF: any; listEntryREF: any;
@@ -18,8 +19,6 @@ interface EntriesListProps {
setIsSnappedToBottom: any; setIsSnappedToBottom: any;
queriedCurrent: number; queriedCurrent: number;
setQueriedCurrent: any; setQueriedCurrent: any;
queriedTotal: number;
setQueriedTotal: any;
startTime: number; startTime: number;
noMoreDataTop: boolean; noMoreDataTop: boolean;
setNoMoreDataTop: (flag: boolean) => void; setNoMoreDataTop: (flag: boolean) => void;
@@ -33,16 +32,18 @@ interface EntriesListProps {
ws: any; ws: any;
} }
export const EntriesList: React.FC<EntriesListProps> = ({listEntryREF, onSnapBrokenEvent, isSnappedToBottom, setIsSnappedToBottom, queriedCurrent, setQueriedCurrent, queriedTotal, setQueriedTotal, startTime, noMoreDataTop, setNoMoreDataTop, leftOffTop, setLeftOffTop, openWebSocket, leftOffBottom, truncatedTimestamp, setTruncatedTimestamp, scrollableRef, ws}) => { export const EntriesList: React.FC<EntriesListProps> = ({listEntryREF, onSnapBrokenEvent, isSnappedToBottom, setIsSnappedToBottom, queriedCurrent, setQueriedCurrent, startTime, noMoreDataTop, setNoMoreDataTop, leftOffTop, setLeftOffTop, openWebSocket, leftOffBottom, truncatedTimestamp, setTruncatedTimestamp, scrollableRef, ws}) => {
const [entries, setEntries] = useRecoilState(entriesAtom); const [entries, setEntries] = useRecoilState(entriesAtom);
const query = useRecoilValue(queryAtom); const query = useRecoilValue(queryAtom);
const isWsConnectionClosed = ws?.current?.readyState !== WebSocket.OPEN; const isWsConnectionClosed = ws?.current?.readyState !== WebSocket.OPEN;
const [focusedEntryId, setFocusedEntryId] = useRecoilState(focusedEntryIdAtom);
const trafficViewerApi = useRecoilValue(TrafficViewerApiAtom as RecoilState<TrafficViewerApi>) const trafficViewerApi = useRecoilValue(TrafficViewerApiAtom as RecoilState<TrafficViewerApi>)
const [loadMoreTop, setLoadMoreTop] = useState(false); const [loadMoreTop, setLoadMoreTop] = useState(false);
const [isLoadingTop, setIsLoadingTop] = useState(false); const [isLoadingTop, setIsLoadingTop] = useState(false);
const [queriedTotal, setQueriedTotal] = useState(0);
useEffect(() => { useEffect(() => {
const list = document.getElementById('list').firstElementChild; const list = document.getElementById('list').firstElementChild;
@@ -103,6 +104,29 @@ export const EntriesList: React.FC<EntriesListProps> = ({listEntryREF, onSnapBro
const scrollbarVisible = scrollableRef.current?.childWrapperRef.current.clientHeight > scrollableRef.current?.wrapperRef.current.clientHeight; const scrollbarVisible = scrollableRef.current?.childWrapperRef.current.clientHeight > scrollableRef.current?.wrapperRef.current.clientHeight;
if (ws.current) {
ws.current.addEventListener("message", (e) => {
if (!e?.data) return;
const message = JSON.parse(e.data);
switch (message.messageType) {
case "entry":
const entry = message.data;
if (!focusedEntryId) setFocusedEntryId(entry.id.toString());
const newEntries = [...entries, entry];
if (newEntries.length === 10001) {
setLeftOffTop(newEntries[0].entry.id);
newEntries.shift();
setNoMoreDataTop(false);
}
setEntries(newEntries);
break;
case "queryMetadata":
setQueriedTotal(message.data.total);
break;
};
});
}
return <React.Fragment> return <React.Fragment>
<div className={styles.list}> <div className={styles.list}>
<div id="list" ref={listEntryREF} className={styles.list}> <div id="list" ref={listEntryREF} className={styles.list}>

View File

@@ -1,16 +1,18 @@
import React, {useEffect, useState} from "react"; import React, { useEffect, useState } from "react";
import EntryViewer from "./EntryDetailed/EntryViewer"; import EntryViewer from "./EntryDetailed/EntryViewer";
import {EntryItem} from "./EntryListItem/EntryListItem"; import { EntryItem } from "./EntryListItem/EntryListItem";
import {makeStyles} from "@material-ui/core"; import { makeStyles } from "@material-ui/core";
import Protocol from "../UI/Protocol" import Protocol from "../UI/Protocol"
import Queryable from "../UI/Queryable"; import Queryable from "../UI/Queryable";
import {toast} from "react-toastify"; import { toast } from "react-toastify";
import {RecoilState, useRecoilState, useRecoilValue} from "recoil"; import { RecoilState, useRecoilState, useRecoilValue } from "recoil";
import focusedEntryIdAtom from "../../recoil/focusedEntryId"; import focusedEntryIdAtom from "../../recoil/focusedEntryId";
import trafficViewerApi from "../../recoil/TrafficViewerApi"; import trafficViewerApi from "../../recoil/TrafficViewerApi";
import TrafficViewerApi from "./TrafficViewerApi"; import TrafficViewerApi from "./TrafficViewerApi";
import TrafficViewerApiAtom from "../../recoil/TrafficViewerApi/atom"; import TrafficViewerApiAtom from "../../recoil/TrafficViewerApi/atom";
import queryAtom from "../../recoil/query/atom"; import queryAtom from "../../recoil/query/atom";
import useWindowDimensions, { useRequestTextByWidth } from "../../hooks/WindowDimensionsHook";
import { TOAST_CONTAINER_ID } from "../../configs/Consts";
const useStyles = makeStyles(() => ({ const useStyles = makeStyles(() => ({
entryTitle: { entryTitle: {
@@ -35,56 +37,59 @@ const useStyles = makeStyles(() => ({
})); }));
export const formatSize = (n: number) => n > 1000 ? `${Math.round(n / 1000)}KB` : `${n} B`; export const formatSize = (n: number) => n > 1000 ? `${Math.round(n / 1000)}KB` : `${n} B`;
const minSizeDisplayRequestSize = 880;
const EntryTitle: React.FC<any> = ({protocol, data, elapsedTime}) => { const EntryTitle: React.FC<any> = ({ protocol, data, elapsedTime }) => {
const classes = useStyles(); const classes = useStyles();
const request = data.request; const request = data.request;
const response = data.response; const response = data.response;
const { width } = useWindowDimensions();
const { requestText, responseText, elapsedTimeText } = useRequestTextByWidth(width)
return <div className={classes.entryTitle}> return <div className={classes.entryTitle}>
<Protocol protocol={protocol} horizontal={true}/> <Protocol protocol={protocol} horizontal={true} />
<div style={{right: "30px", position: "absolute", display: "flex"}}> {(width > minSizeDisplayRequestSize) && <div style={{ right: "30px", position: "absolute", display: "flex" }}>
{request && <Queryable {request && <Queryable
query={`requestSize == ${data.requestSize}`} query={`requestSize == ${data.requestSize}`}
style={{margin: "0 18px"}} style={{ margin: "0 18px" }}
displayIconOnMouseOver={true} displayIconOnMouseOver={true}
> >
<div <div
style={{opacity: 0.5}} style={{ opacity: 0.5 }}
id="entryDetailedTitleRequestSize" id="entryDetailedTitleRequestSize"
> >
{`Request: ${formatSize(data.requestSize)}`} {`${requestText}${formatSize(data.requestSize)}`}
</div> </div>
</Queryable>} </Queryable>}
{response && <Queryable {response && <Queryable
query={`responseSize == ${data.responseSize}`} query={`responseSize == ${data.responseSize}`}
style={{margin: "0 18px"}} style={{ margin: "0 18px" }}
displayIconOnMouseOver={true} displayIconOnMouseOver={true}
> >
<div <div
style={{opacity: 0.5}} style={{ opacity: 0.5 }}
id="entryDetailedTitleResponseSize" id="entryDetailedTitleResponseSize"
> >
{`Response: ${formatSize(data.responseSize)}`} {`${responseText}${formatSize(data.responseSize)}`}
</div> </div>
</Queryable>} </Queryable>}
{response && <Queryable {response && <Queryable
query={`elapsedTime >= ${elapsedTime}`} query={`elapsedTime >= ${elapsedTime}`}
style={{marginRight: 18}} style={{ margin: "0 0 0 18px" }}
displayIconOnMouseOver={true} displayIconOnMouseOver={true}
> >
<div <div
style={{opacity: 0.5}} style={{ opacity: 0.5 }}
id="entryDetailedTitleElapsedTime" id="entryDetailedTitleElapsedTime"
> >
{`Elapsed Time: ${Math.round(elapsedTime)}ms`} {`${elapsedTimeText}${Math.round(elapsedTime)}ms`}
</div> </div>
</Queryable>} </Queryable>}
</div> </div>}
</div>; </div>;
}; };
const EntrySummary: React.FC<any> = ({entry}) => { const EntrySummary: React.FC<any> = ({ entry }) => {
return <EntryItem return <EntryItem
key={`entry-${entry.id}`} key={`entry-${entry.id}`}
entry={entry} entry={entry}
@@ -113,14 +118,10 @@ export const EntryDetailed = () => {
} catch (error) { } catch (error) {
if (error.response?.data?.type) { if (error.response?.data?.type) {
toast[error.response.data.type](`Entry[${focusedEntryId}]: ${error.response.data.msg}`, { toast[error.response.data.type](`Entry[${focusedEntryId}]: ${error.response.data.msg}`, {
position: "bottom-right",
theme: "colored", theme: "colored",
autoClose: error.response.data.autoClose, autoClose: error.response.data.autoClose,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined, progress: undefined,
containerId: TOAST_CONTAINER_ID
}); });
} }
console.error(error); console.error(error);
@@ -135,7 +136,7 @@ export const EntryDetailed = () => {
data={entryData.data} data={entryData.data}
elapsedTime={entryData.data.elapsedTime} elapsedTime={entryData.data.elapsedTime}
/>} />}
{entryData && <EntrySummary entry={entryData.base}/>} {entryData && <EntrySummary entry={entryData.base} />}
<React.Fragment> <React.Fragment>
{entryData && <EntryViewer {entryData && <EntryViewer
representation={entryData.representation} representation={entryData.representation}

View File

@@ -66,8 +66,10 @@
margin-top: -60px margin-top: -60px
.capture img .capture img
height: 20px height: 14px
z-index: 1000 z-index: 1000
margin-top: 12px
margin-left: -2px
.endpointServiceContainer .endpointServiceContainer
display: flex display: flex
@@ -76,6 +78,7 @@
padding-right: 10px padding-right: 10px
padding-top: 4px padding-top: 4px
flex-grow: 1 flex-grow: 1
padding-left: 10px
.separatorRight .separatorRight
display: flex display: flex

View File

@@ -4,7 +4,7 @@ import SwapHorizIcon from '@material-ui/icons/SwapHoriz';
import styles from './EntryListItem.module.sass'; import styles from './EntryListItem.module.sass';
import StatusCode, {getClassification, StatusCodeClassification} from "../../UI/StatusCode"; import StatusCode, {getClassification, StatusCodeClassification} from "../../UI/StatusCode";
import Protocol, {ProtocolInterface} from "../../UI/Protocol" import Protocol, {ProtocolInterface} from "../../UI/Protocol"
import eBPFLogo from '../../assets/ebpf.png'; import eBPFLogo from 'assets/lock.svg';
import {Summary} from "../../UI/Summary"; import {Summary} from "../../UI/Summary";
import Queryable from "../../UI/Queryable"; import Queryable from "../../UI/Queryable";
import ingoingIconSuccess from "assets/ingoing-traffic-success.svg" import ingoingIconSuccess from "assets/ingoing-traffic-success.svg"
@@ -140,8 +140,6 @@ export const EntryItem: React.FC<EntryProps> = ({entry, style, headingMode}) =>
const isStatusCodeEnabled = ((entry.proto.name === "http" && "status" in entry) || entry.status !== 0); const isStatusCodeEnabled = ((entry.proto.name === "http" && "status" in entry) || entry.status !== 0);
let endpointServiceContainer = "10px";
if (!isStatusCodeEnabled) endpointServiceContainer = "20px";
return <React.Fragment> return <React.Fragment>
<div <div
@@ -178,7 +176,7 @@ export const EntryItem: React.FC<EntryProps> = ({entry, style, headingMode}) =>
{isStatusCodeEnabled && <div> {isStatusCodeEnabled && <div>
<StatusCode statusCode={entry.status} statusQuery={entry.statusQuery}/> <StatusCode statusCode={entry.status} statusQuery={entry.statusQuery}/>
</div>} </div>}
<div className={styles.endpointServiceContainer} style={{paddingLeft: endpointServiceContainer}}> <div className={styles.endpointServiceContainer}>
<Summary method={entry.method} methodQuery={entry.methodQuery} summary={entry.summary} summaryQuery={entry.summaryQuery}/> <Summary method={entry.method} methodQuery={entry.methodQuery} summary={entry.summary} summaryQuery={entry.summaryQuery}/>
<div className={styles.resolvedName}> <div className={styles.resolvedName}>
<Queryable <Queryable

View File

@@ -0,0 +1,3 @@
<svg width="12" height="14" viewBox="0 0 12 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.33333 6.36364H8.66667V6.36011H10.6667V6.36364H11C11.2778 6.36364 11.5139 6.45644 11.7083 6.64205C11.9028 6.82765 12 7.05303 12 7.31818V13.0455C12 13.3106 11.9028 13.536 11.7083 13.7216C11.5139 13.9072 11.2778 14 11 14H1C0.722222 14 0.486111 13.9072 0.291666 13.7216C0.0972223 13.536 0 13.3106 0 13.0455V7.31818C0 7.05303 0.0972223 6.82765 0.291666 6.64205C0.486111 6.45644 0.722222 6.36364 1 6.36364H1.33333V4.45455C1.33333 3.23485 1.79167 2.1875 2.70833 1.3125C3.625 0.4375 4.72222 0 6 0C7.27778 0 8.375 0.4375 9.29167 1.3125C9.92325 1.91538 10.3373 2.60007 10.5337 3.36658L8.59659 3.85085C8.48731 3.40176 8.25026 3.00309 7.88542 2.65483C7.36458 2.15767 6.73611 1.90909 6 1.90909C5.26389 1.90909 4.63542 2.15767 4.11458 2.65483C3.59375 3.15199 3.33333 3.75189 3.33333 4.45455V6.36364Z" fill="#BCCEFD"/>
</svg>

After

Width:  |  Height:  |  Size: 959 B

View File

@@ -8,8 +8,7 @@ import { EntryDetailed } from "./EntryDetailed";
import playIcon from 'assets/run.svg'; import playIcon from 'assets/run.svg';
import pauseIcon from 'assets/pause.svg'; import pauseIcon from 'assets/pause.svg';
import variables from '../../variables.module.scss'; import variables from '../../variables.module.scss';
import { toast,ToastContainer } from 'react-toastify'; import { toast, ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import { RecoilRoot, RecoilState, useRecoilState, useRecoilValue, useSetRecoilState } from "recoil"; import { RecoilRoot, RecoilState, useRecoilState, useRecoilValue, useSetRecoilState } from "recoil";
import entriesAtom from "../../recoil/entries"; import entriesAtom from "../../recoil/entries";
@@ -20,6 +19,7 @@ import trafficViewerApiAtom from "../../recoil/TrafficViewerApi"
import TrafficViewerApi from "./TrafficViewerApi"; import TrafficViewerApi from "./TrafficViewerApi";
import { StatusBar } from "../UI/StatusBar"; import { StatusBar } from "../UI/StatusBar";
import tappingStatusAtom from "../../recoil/tappingStatus/atom"; import tappingStatusAtom from "../../recoil/tappingStatus/atom";
import { TOAST_CONTAINER_ID } from "../../configs/Consts";
const useLayoutStyles = makeStyles(() => ({ const useLayoutStyles = makeStyles(() => ({
details: { details: {
@@ -47,29 +47,29 @@ interface TrafficViewerProps {
trafficViewerApiProp: TrafficViewerApi, trafficViewerApiProp: TrafficViewerApi,
actionButtons?: JSX.Element, actionButtons?: JSX.Element,
isShowStatusBar?: boolean, isShowStatusBar?: boolean,
webSocketUrl : string, webSocketUrl: string,
isCloseWebSocket : boolean isCloseWebSocket: boolean,
isDemoBannerView: boolean
} }
export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus, trafficViewerApiProp, export const TrafficViewer: React.FC<TrafficViewerProps> = ({ setAnalyzeStatus, trafficViewerApiProp,
actionButtons,isShowStatusBar,webSocketUrl, actionButtons, isShowStatusBar, webSocketUrl,
isCloseWebSocket}) => { isCloseWebSocket, isDemoBannerView }) => {
const classes = useLayoutStyles(); const classes = useLayoutStyles();
const [entries, setEntries] = useRecoilState(entriesAtom); const setEntries = useSetRecoilState(entriesAtom);
const [focusedEntryId, setFocusedEntryId] = useRecoilState(focusedEntryIdAtom); const [focusedEntryId, setFocusedEntryId] = useRecoilState(focusedEntryIdAtom);
const query = useRecoilValue(queryAtom); const query = useRecoilValue(queryAtom);
const setTrafficViewerApiState = useSetRecoilState(trafficViewerApiAtom as RecoilState<TrafficViewerApi>) const setTrafficViewerApiState = useSetRecoilState(trafficViewerApiAtom as RecoilState<TrafficViewerApi>)
const [tappingStatus, setTappingStatus] = useRecoilState(tappingStatusAtom); const [tappingStatus, setTappingStatus] = useRecoilState(tappingStatusAtom);
const [noMoreDataTop, setNoMoreDataTop] = useState(false); const [noMoreDataTop, setNoMoreDataTop] = useState(false);
const [isSnappedToBottom, setIsSnappedToBottom] = useState(true); const [isSnappedToBottom, setIsSnappedToBottom] = useState(true);
const [forceRender, setForceRender] = useState(0); const [wsReadyState, setWsReadyState] = useState(0);
const [queryBackgroundColor, setQueryBackgroundColor] = useState("#f5f5f5"); const [queryBackgroundColor, setQueryBackgroundColor] = useState("#f5f5f5");
const [queriedCurrent, setQueriedCurrent] = useState(0); const [queriedCurrent, setQueriedCurrent] = useState(0);
const [queriedTotal, setQueriedTotal] = useState(0);
const [leftOffBottom, setLeftOffBottom] = useState(0); const [leftOffBottom, setLeftOffBottom] = useState(0);
const [leftOffTop, setLeftOffTop] = useState(null); const [leftOffTop, setLeftOffTop] = useState(null);
const [truncatedTimestamp, setTruncatedTimestamp] = useState(0); const [truncatedTimestamp, setTruncatedTimestamp] = useState(0);
@@ -105,12 +105,31 @@ export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus,
handleQueryChange(query); handleQueryChange(query);
}, [query, handleQueryChange]); }, [query, handleQueryChange]);
useEffect(()=>{ useEffect(() => {
isCloseWebSocket && closeWebSocket() isCloseWebSocket && closeWebSocket()
},[isCloseWebSocket]) }, [isCloseWebSocket])
useEffect(() => {
reopenConnection()
}, [webSocketUrl])
const ws = useRef(null); const ws = useRef(null);
const openEmptyWebSocket = () => {
if (query) {
openWebSocket(`(${query}) and leftOff(-1)`, true);
} else {
openWebSocket(`leftOff(-1)`, true);
}
}
const closeWebSocket = () => {
if(ws?.current?.readyState === WebSocket.OPEN) {
ws.current.close();
return true;
}
}
const listEntry = useRef(null); const listEntry = useRef(null);
const openWebSocket = (query: string, resetEntries: boolean) => { const openWebSocket = (query: string, resetEntries: boolean) => {
if (resetEntries) { if (resetEntries) {
@@ -124,9 +143,12 @@ export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus,
ws.current = new WebSocket(webSocketUrl); ws.current = new WebSocket(webSocketUrl);
sendQueryWhenWsOpen(query); sendQueryWhenWsOpen(query);
ws.current.onopen = () => {
setWsReadyState(ws?.current?.readyState);
}
ws.current.onclose = () => { ws.current.onclose = () => {
if(window.location.pathname === "/") setWsReadyState(ws?.current?.readyState);
setForceRender(forceRender + 1);
} }
ws.current.onerror = (event) => { ws.current.onerror = (event) => {
console.error("WebSocket error:", event); console.error("WebSocket error:", event);
@@ -139,41 +161,24 @@ export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus,
openWebSocket(`leftOff(${leftOffBottom})`, false); openWebSocket(`leftOff(${leftOffBottom})`, false);
} }
} }
} catch (e) {} } catch (e) { }
} }
const sendQueryWhenWsOpen = (query) => { const sendQueryWhenWsOpen = (query) => {
setTimeout(() => { setTimeout(() => {
if (ws?.current?.readyState === WebSocket.OPEN) { if (ws?.current?.readyState === WebSocket.OPEN) {
ws.current.send(JSON.stringify({"query": query, "enableFullEntries": false})); ws.current.send(JSON.stringify({ "query": query, "enableFullEntries": false }));
} else { } else {
sendQueryWhenWsOpen(query); sendQueryWhenWsOpen(query);
} }
}, 500) }, 500)
} }
const closeWebSocket = () => {
if(ws?.current?.readyState === WebSocket.OPEN) {
ws.current.close();
}
}
if (ws.current) { if (ws.current) {
ws.current.onmessage = (e) => { ws.current.addEventListener("message", (e) => {
if (!e?.data) return; if (!e?.data) return;
const message = JSON.parse(e.data); const message = JSON.parse(e.data);
switch (message.messageType) { switch (message.messageType) {
case "entry":
const entry = message.data;
if (!focusedEntryId) setFocusedEntryId(entry.id.toString());
const newEntries = [...entries, entry];
if (newEntries.length === 10001) {
setLeftOffTop(newEntries[0].entry.id);
newEntries.shift();
setNoMoreDataTop(false);
}
setEntries(newEntries);
break;
case "status": case "status":
setTappingStatus(message.tappingStatus); setTappingStatus(message.tappingStatus);
break; break;
@@ -185,19 +190,15 @@ export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus,
break; break;
case "toast": case "toast":
toast[message.data.type](message.data.text, { toast[message.data.type](message.data.text, {
position: "bottom-right",
theme: "colored", theme: "colored",
autoClose: message.data.autoClose, autoClose: message.data.autoClose,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true, pauseOnHover: true,
draggable: true,
progress: undefined, progress: undefined,
containerId: TOAST_CONTAINER_ID
}); });
break; break;
case "queryMetadata": case "queryMetadata":
setQueriedCurrent(queriedCurrent + message.data.current); setQueriedCurrent(queriedCurrent + message.data.current);
setQueriedTotal(message.data.total);
setLeftOffBottom(message.data.leftOff); setLeftOffBottom(message.data.leftOff);
setTruncatedTimestamp(message.data.truncatedTimestamp); setTruncatedTimestamp(message.data.truncatedTimestamp);
if (leftOffTop === null) { if (leftOffTop === null) {
@@ -207,22 +208,17 @@ export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus,
case "startTime": case "startTime":
setStartTime(message.data); setStartTime(message.data);
break; break;
default:
console.error(
`unsupported websocket message type, Got: ${message.messageType}`
);
} }
}; })
} }
useEffect(() => { useEffect(() => {
setTrafficViewerApiState({...trafficViewerApiProp, webSocket : {close : closeWebSocket}}); setTrafficViewerApiState({ ...trafficViewerApiProp, webSocket: { close: closeWebSocket } });
(async () => { (async () => {
openWebSocket("leftOff(-1)", true);
try{ try{
const tapStatusResponse = await trafficViewerApiProp.tapStatus(); const tapStatusResponse = await trafficViewerApiProp.tapStatus();
setTappingStatus(tapStatusResponse); setTappingStatus(tapStatusResponse);
if(setAnalyzeStatus) { if (setAnalyzeStatus) {
const analyzeStatusResponse = await trafficViewerApiProp.analyzeStatus(); const analyzeStatusResponse = await trafficViewerApiProp.analyzeStatus();
setAnalyzeStatus(analyzeStatusResponse); setAnalyzeStatus(analyzeStatusResponse);
} }
@@ -234,19 +230,18 @@ export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus,
}, []); }, []);
const toggleConnection = () => { const toggleConnection = () => {
if(ws?.current?.readyState === WebSocket.OPEN) { if(!closeWebSocket()) {
ws?.current?.close(); openEmptyWebSocket();
} else {
if (query) {
openWebSocket(`(${query}) and leftOff(-1)`, true);
} else {
openWebSocket(`leftOff(-1)`, true);
}
scrollableRef.current.jumpToBottom(); scrollableRef.current.jumpToBottom();
setIsSnappedToBottom(true); setIsSnappedToBottom(true);
} }
} }
const reopenConnection = async () => {
closeWebSocket()
openEmptyWebSocket();
}
useEffect(() => { useEffect(() => {
return () => { return () => {
ws.current.close(); ws.current.close();
@@ -263,7 +258,7 @@ export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus,
}; };
const getConnectionIndicator = () => { const getConnectionIndicator = () => {
switch (ws?.current?.readyState) { switch (wsReadyState) {
case WebSocket.OPEN: case WebSocket.OPEN:
return <div className={`${TrafficViewerStyles.indicatorContainer} ${TrafficViewerStyles.greenIndicatorContainer}`}> return <div className={`${TrafficViewerStyles.indicatorContainer} ${TrafficViewerStyles.greenIndicatorContainer}`}>
<div className={`${TrafficViewerStyles.indicator} ${TrafficViewerStyles.greenIndicator}`} /> <div className={`${TrafficViewerStyles.indicator} ${TrafficViewerStyles.greenIndicator}`} />
@@ -276,7 +271,7 @@ export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus,
} }
const getConnectionTitle = () => { const getConnectionTitle = () => {
switch (ws?.current?.readyState) { switch (wsReadyState) {
case WebSocket.OPEN: case WebSocket.OPEN:
return "streaming live traffic" return "streaming live traffic"
default: default:
@@ -293,12 +288,12 @@ export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus,
return ( return (
<div className={TrafficViewerStyles.TrafficPage}> <div className={TrafficViewerStyles.TrafficPage}>
{tappingStatus && isShowStatusBar && <StatusBar />} {tappingStatus && isShowStatusBar && <StatusBar isDemoBannerView={isDemoBannerView} />}
<div className={TrafficViewerStyles.TrafficPageHeader}> <div className={TrafficViewerStyles.TrafficPageHeader}>
<div className={TrafficViewerStyles.TrafficPageStreamStatus}> <div className={TrafficViewerStyles.TrafficPageStreamStatus}>
<img className={TrafficViewerStyles.playPauseIcon} style={{ visibility: ws?.current?.readyState === WebSocket.OPEN ? "visible" : "hidden" }} alt="pause" <img className={TrafficViewerStyles.playPauseIcon} style={{ visibility: wsReadyState === WebSocket.OPEN ? "visible" : "hidden" }} alt="pause"
src={pauseIcon} onClick={toggleConnection} /> src={pauseIcon} onClick={toggleConnection} />
<img className={TrafficViewerStyles.playPauseIcon} style={{ position: "absolute", visibility: ws?.current?.readyState === WebSocket.OPEN ? "hidden" : "visible" }} alt="play" <img className={TrafficViewerStyles.playPauseIcon} style={{ position: "absolute", visibility: wsReadyState === WebSocket.OPEN ? "hidden" : "visible" }} alt="play"
src={playIcon} onClick={toggleConnection} /> src={playIcon} onClick={toggleConnection} />
<div className={TrafficViewerStyles.connectionText}> <div className={TrafficViewerStyles.connectionText}>
{getConnectionTitle()} {getConnectionTitle()}
@@ -322,8 +317,6 @@ export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus,
setIsSnappedToBottom={setIsSnappedToBottom} setIsSnappedToBottom={setIsSnappedToBottom}
queriedCurrent={queriedCurrent} queriedCurrent={queriedCurrent}
setQueriedCurrent={setQueriedCurrent} setQueriedCurrent={setQueriedCurrent}
queriedTotal={queriedTotal}
setQueriedTotal={setQueriedTotal}
startTime={startTime} startTime={startTime}
noMoreDataTop={noMoreDataTop} noMoreDataTop={noMoreDataTop}
setNoMoreDataTop={setNoMoreDataTop} setNoMoreDataTop={setNoMoreDataTop}
@@ -348,19 +341,28 @@ export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus,
setAddressesWithTLS={setAddressesWithTLS} setAddressesWithTLS={setAddressesWithTLS}
userDismissedTLSWarning={userDismissedTLSWarning} userDismissedTLSWarning={userDismissedTLSWarning}
setUserDismissedTLSWarning={setUserDismissedTLSWarning} /> setUserDismissedTLSWarning={setUserDismissedTLSWarning} />
<ToastContainer/>
</div> </div>
); );
}; };
const MemoiedTrafficViewer = React.memo(TrafficViewer) const MemoiedTrafficViewer = React.memo(TrafficViewer)
const TrafficViewerContainer: React.FC<TrafficViewerProps> = ({ setAnalyzeStatus, trafficViewerApiProp, const TrafficViewerContainer: React.FC<TrafficViewerProps> = ({ setAnalyzeStatus, trafficViewerApiProp,
actionButtons, isShowStatusBar = true , actionButtons, isShowStatusBar = true,
webSocketUrl, isCloseWebSocket}) => { webSocketUrl, isCloseWebSocket, isDemoBannerView }) => {
return <RecoilRoot> return <RecoilRoot>
<MemoiedTrafficViewer actionButtons={actionButtons} isShowStatusBar={isShowStatusBar} webSocketUrl={webSocketUrl} <MemoiedTrafficViewer actionButtons={actionButtons} isShowStatusBar={isShowStatusBar} webSocketUrl={webSocketUrl}
isCloseWebSocket={isCloseWebSocket} trafficViewerApiProp={trafficViewerApiProp} isCloseWebSocket={isCloseWebSocket} trafficViewerApiProp={trafficViewerApiProp}
setAnalyzeStatus={setAnalyzeStatus} /> setAnalyzeStatus={setAnalyzeStatus} isDemoBannerView={isDemoBannerView} />
<ToastContainer enableMultiContainer containerId={TOAST_CONTAINER_ID}
position="bottom-right"
autoClose={5000}
hideProgressBar={false}
newestOnTop={false}
closeOnClick
rtl={false}
pauseOnFocusLoss
draggable
pauseOnHover />
</RecoilRoot> </RecoilRoot>
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -6,11 +6,11 @@ export interface Props {
disabled?: boolean; disabled?: boolean;
} }
const Checkbox: React.FC<Props> = ({checked, onToggle, disabled}) => { const Checkbox: React.FC<Props> = ({checked, onToggle, disabled, ...props}) => {
return ( return (
<div> <div>
<input style={!disabled ? {cursor: "pointer"}: {}} type="checkbox" checked={checked} disabled={disabled} onChange={(event) => onToggle(event.target.checked)}/> <input style={!disabled ? {cursor: "pointer"}: {}} type="checkbox" checked={checked} disabled={disabled} onChange={(event) => onToggle(event.target.checked)} {...props}/>
</div> </div>
); );
}; };

View File

@@ -0,0 +1,19 @@
import React, { CSSProperties } from "react";
import styles from "./style/InformationIcon.module.sass"
const DEFUALT_LINK = "https://getmizu.io/docs"
export interface InformationIconProps {
link?: string,
style?: CSSProperties
}
export const InformationIcon: React.FC<InformationIconProps> = ({ link, style }) => {
return <React.Fragment>
<a href={DEFUALT_LINK ? DEFUALT_LINK : link} style={style} className={styles.linkStyle} title="documentation" target="_blank">
<span>Docs</span>
</a>
</React.Fragment>
}

View File

@@ -54,7 +54,7 @@ const Protocol: React.FC<ProtocolProps> = ({protocol, horizontal}) => {
backgroundColor: protocol.backgroundColor, backgroundColor: protocol.backgroundColor,
color: protocol.foregroundColor, color: protocol.foregroundColor,
fontSize: protocol.fontSize, fontSize: protocol.fontSize,
marginRight: "-20px", marginRight: "-6px",
}} }}
title={protocol.longName} title={protocol.longName}
> >

View File

@@ -19,7 +19,7 @@ const menuProps: any = {
}; };
// icons styles are not overwritten from the Props, only as a separate object // icons styles are not overwritten from the Props, only as a separate object
const classes = {icon: styles.icon, selectMenu: styles.list}; const classes = {icon: styles.icon, selectMenu: styles.list, select: styles.oasSelect, root:styles.root};
const defaultProps = { const defaultProps = {
MenuProps: menuProps, MenuProps: menuProps,

View File

@@ -10,12 +10,16 @@ const pluralize = (noun: string, amount: number) => {
return `${noun}${amount !== 1 ? 's' : ''}` return `${noun}${amount !== 1 ? 's' : ''}`
} }
export const StatusBar = () => { interface StatusBarProps {
isDemoBannerView: boolean;
}
export const StatusBar = ({isDemoBannerView}) => {
const tappingStatus = useRecoilValue(tappingStatusAtom); const tappingStatus = useRecoilValue(tappingStatusAtom);
const [expandedBar, setExpandedBar] = useState(false); const [expandedBar, setExpandedBar] = useState(false);
const {uniqueNamespaces, amountOfPods, amountOfTappedPods, amountOfUntappedPods} = useRecoilValue(tappingStatusDetails); const {uniqueNamespaces, amountOfPods, amountOfTappedPods, amountOfUntappedPods} = useRecoilValue(tappingStatusDetails);
return <div className={`${style.statusBar} ${(expandedBar ? `${style.expandedStatusBar}` : "")}`} onMouseOver={() => setExpandedBar(true)} onMouseLeave={() => setExpandedBar(false)} data-cy="expandedStatusBar"> return <div className={`${isDemoBannerView ? `${style.banner}` : ''} ${style.statusBar} ${(expandedBar ? `${style.expandedStatusBar}` : "")}`} onMouseOver={() => setExpandedBar(true)} onMouseLeave={() => setExpandedBar(false)} data-cy="expandedStatusBar">
<div className={style.podsCount}> <div className={style.podsCount}>
{tappingStatus.some(pod => !pod.isTapped) && <img src={warningIcon} alt="warning"/>} {tappingStatus.some(pod => !pod.isTapped) && <img src={warningIcon} alt="warning"/>}
<span className={style.podsCountText} data-cy="podsCountText"> <span className={style.podsCountText} data-cy="podsCountText">

View File

@@ -0,0 +1,39 @@
.highlighterContainer
&.fitScreen
pre
max-height: 90vh
overflow: auto
pre
code
font-size: 0.75rem
&:first-child
margin-right: 0.75rem
background: #F7F9FC
.react-syntax-highlighter-line-number
color: rgb(98, 126, 247)
&:last-child
display: block
code.hljs
white-space: pre-wrap
code.hljs:before
counter-reset: listing
.hljsMarkerLine
counter-increment: listing
.hljsMarkerLine:before
content: counter(listing) " "
display: inline-block
width: 3rem
padding-left: auto
margin-left: auto
text-align: right
opacity: .5

View File

@@ -1,49 +0,0 @@
.highlighterContainer {
&.fitScreen {
pre {
max-height: 90vh;
overflow: auto;
}
}
pre {
code {
font-size: 0.75rem;
&:first-child {
margin-right: 0.75rem;
background: #F7F9FC;
.react-syntax-highlighter-line-number {
color: rgb(98, 126, 247);
}
}
&:last-child {
display: block;
}
}
}
}
code.hljs {
white-space: pre-wrap;
}
code.hljs:before {
counter-reset: listing;
}
code.hljs .hljs-marker-line {
counter-increment: listing;
}
code.hljs .hljs-marker-line:before {
content: counter(listing) " ";
display: inline-block;
width: 3rem;
padding-left: auto;
margin-left: auto;
text-align: right;
opacity: .5;
}

View File

@@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import Lowlight from 'react-lowlight' import Lowlight from 'react-lowlight'
import 'highlight.js/styles/atom-one-light.css' import 'highlight.js/styles/atom-one-light.css'
import './index.scss'; import styles from './index.module.sass';
import xml from 'highlight.js/lib/languages/xml' import xml from 'highlight.js/lib/languages/xml'
import json from 'highlight.js/lib/languages/json' import json from 'highlight.js/lib/languages/json'
@@ -37,11 +37,11 @@ export const SyntaxHighlighter: React.FC<Props> = ({
const markers = showLineNumbers ? code.split("\n").map((item, i) => { const markers = showLineNumbers ? code.split("\n").map((item, i) => {
return { return {
line: i + 1, line: i + 1,
className: 'hljs-marker-line' className: styles.hljsMarkerLine
} }
}) : []; }) : [];
return <div style={{fontSize: ".75rem"}}><Lowlight language={language ? language : ""} value={code} markers={markers}/></div>; return <div style={{fontSize: ".75rem"}} className={styles.highlighterContainer}><Lowlight language={language ? language : ""} value={code} markers={markers}/></div>;
}; };
export default SyntaxHighlighter; export default SyntaxHighlighter;

View File

@@ -76,6 +76,7 @@ const Tabs: React.FC<Props> = ({classes={}, tabs, currentTab, color, onChange, l
{tabs.map(({tab, disabled, disabledMessage, highlight, badge}, index) => { {tabs.map(({tab, disabled, disabledMessage, highlight, badge}, index) => {
const active = currentTab === tab; const active = currentTab === tab;
const tabLink = <span const tabLink = <span
data-cy={"tab-" + tab}
key={tab} key={tab}
className={`${_classes.tab} ${active ? _classes.active : ''} ${disabled ? _classes.disabled : ''} ${highlight ? _classes.highlight : ''} ${dark ? 'dark' : ''}`} className={`${_classes.tab} ${active ? _classes.active : ''} ${disabled ? _classes.disabled : ''} ${highlight ? _classes.highlight : ''} ${dark ? 'dark' : ''}`}
onClick={() => !disabled && onChange(tab)} onClick={() => !disabled && onChange(tab)}

View File

@@ -5,7 +5,8 @@ import Tooltip from "./Tooltip";
import Checkbox from "./Checkbox" import Checkbox from "./Checkbox"
import { StatusBar } from "./StatusBar"; import { StatusBar } from "./StatusBar";
import CustomModal from "./CustomModal"; import CustomModal from "./CustomModal";
import { InformationIcon } from "./InformationIcon";
export {LoadingOverlay,Select,Tabs,Tooltip,Checkbox,CustomModal} export {LoadingOverlay,Select,Tabs,Tooltip,Checkbox,CustomModal,InformationIcon}
export {StatusBar} export {StatusBar}

View File

@@ -0,0 +1,8 @@
.linkStyle
display: flex
color: #18253d
text-decoration: none
font-family: "Ubuntu", sans-serif
font-style: normal
font-weight: 600
font-size: 14px

View File

@@ -3,3 +3,15 @@
.list .list
margin-top: 8px margin-top: 8px
.oasSelect
font-weight: normal
padding: 8px 4px 8px 12px !important
border: 1px solid #9d9d9d !important
border-radius: 9px !important
font-family: Source Sans Pro, Lucida Grande, Tahoma, sans-serif !important
width: 216px !important
.root
font-family: Source Sans Pro, Lucida Grande, Tahoma, sans-serif !important

View File

@@ -4,7 +4,7 @@
position: absolute position: absolute
transform: translate(-50%, -3px) transform: translate(-50%, -3px)
left: 50% left: 50%
z-index: 9999 z-index: 100
min-width: 200px min-width: 200px
background: $blue-color background: $blue-color
color: rgba(255,255,255,0.75) color: rgba(255,255,255,0.75)
@@ -19,6 +19,9 @@
overflow: hidden overflow: hidden
max-width: clamp(150px,50%,600px) max-width: clamp(150px,50%,600px)
&.banner
top: 53px
.podsCount .podsCount
display: flex display: flex
justify-content: center justify-content: center
@@ -38,7 +41,7 @@
table table
width: 100% width: 100%
margin-top: 20px margin-top: 20px
tbody tbody
max-height: 70vh max-height: 70vh
overflow-y: auto overflow-y: auto

View File

@@ -9,7 +9,7 @@
text-align: center text-align: center
line-height: 22px line-height: 22px
font-weight: 600 font-weight: 600
margin-left: 8px margin-left: 3px
.neutral .neutral
background: gray background: gray

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -0,0 +1 @@
export const TOAST_CONTAINER_ID = "Common";

View File

@@ -0,0 +1,43 @@
import { useState, useEffect } from 'react';
function getWindowDimensions() {
const { innerWidth: width, innerHeight: height } = window;
return {
width,
height
};
}
export function useRequestTextByWidth(windowWidth){
let requestText = "Request: "
let responseText = "Response: "
let elapsedTimeText = "Elapsed Time: "
if (windowWidth < 1078) {
requestText = ""
responseText = ""
elapsedTimeText = ""
} else if (windowWidth < 1356) {
requestText = "Req: "
responseText = "Res: "
elapsedTimeText = "ET: "
}
return {requestText, responseText, elapsedTimeText}
}
export default function useWindowDimensions() {
const [windowDimensions, setWindowDimensions] = useState(getWindowDimensions());
useEffect(() => {
function handleResize() {
setWindowDimensions(getWindowDimensions());
}
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return windowDimensions;
}

4657
ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -13,7 +13,7 @@
"@types/jest": "^26.0.22", "@types/jest": "^26.0.22",
"@types/node": "^12.20.10", "@types/node": "^12.20.10",
"@uiw/react-textarea-code-editor": "^1.4.12", "@uiw/react-textarea-code-editor": "^1.4.12",
"@up9/mizu-common": "1.0.131", "@up9/mizu-common": "file:up9-mizu-common-0.0.0.tgz",
"axios": "^0.25.0", "axios": "^0.25.0",
"core-js": "^3.20.2", "core-js": "^3.20.2",
"craco-babel-loader": "^1.0.3", "craco-babel-loader": "^1.0.3",
@@ -48,7 +48,8 @@
"npm-link-shared": "^0.5.6", "npm-link-shared": "^0.5.6",
"react-app-rewire-alias": "^1.1.7", "react-app-rewire-alias": "^1.1.7",
"react-dev-utils": "^12.0.0", "react-dev-utils": "^12.0.0",
"recoil": "^0.5.2" "recoil": "^0.5.2",
"react-error-overlay": "6.0.9"
}, },
"scripts": { "scripts": {
"start": "craco start", "start": "craco start",

View File

@@ -3,6 +3,7 @@ import {AuthPresentation} from "../AuthPresentation/AuthPresentation";
import {AnalyzeButton} from "@up9/mizu-common" import {AnalyzeButton} from "@up9/mizu-common"
import logo from '../assets/Mizu-logo.svg'; import logo from '../assets/Mizu-logo.svg';
import './Header.sass'; import './Header.sass';
import {UI} from "@up9/mizu-common"
interface HeaderProps { interface HeaderProps {
analyzeStatus: any analyzeStatus: any
@@ -15,6 +16,7 @@ export const Header: React.FC<HeaderProps> = ({analyzeStatus}) => {
</div> </div>
<div style={{display: "flex", alignItems: "center"}}> <div style={{display: "flex", alignItems: "center"}}>
{analyzeStatus?.isAnalyzing && <AnalyzeButton analyzeStatus={analyzeStatus}/>} {analyzeStatus?.isAnalyzing && <AnalyzeButton analyzeStatus={analyzeStatus}/>}
<UI.InformationIcon/>
<AuthPresentation/> <AuthPresentation/>
</div> </div>
</div>; </div>;

View File

@@ -1,9 +1,9 @@
import React, {useEffect, useState} from "react"; import React, { useState } from "react";
import { Button } from "@material-ui/core"; import { Button } from "@material-ui/core";
import Api,{getWebsocketUrl} from "../../../helpers/api"; import Api, { MizuWebsocketURL } from "../../../helpers/api";
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import {useSetRecoilState, useRecoilState} from "recoil"; import { useRecoilState } from "recoil";
import {useCommonStyles} from "../../../helpers/commonStyle" import { useCommonStyles } from "../../../helpers/commonStyle"
import serviceMapModalOpenAtom from "../../../recoil/serviceMapModalOpen"; import serviceMapModalOpenAtom from "../../../recoil/serviceMapModalOpen";
import TrafficViewer from "@up9/mizu-common" import TrafficViewer from "@up9/mizu-common"
import "@up9/mizu-common/dist/index.css" import "@up9/mizu-common/dist/index.css"
@@ -17,13 +17,13 @@ interface TrafficPageProps {
const api = Api.getInstance(); const api = Api.getInstance();
export const TrafficPage: React.FC<TrafficPageProps> = ({setAnalyzeStatus}) => { export const TrafficPage: React.FC<TrafficPageProps> = ({ setAnalyzeStatus }) => {
const commonClasses = useCommonStyles(); const commonClasses = useCommonStyles();
const setServiceMapModalOpen = useSetRecoilState(serviceMapModalOpenAtom); const [serviceMapModalOpen, setServiceMapModalOpen] = useRecoilState(serviceMapModalOpenAtom);
const [openOasModal, setOpenOasModal] = useRecoilState(oasModalOpenAtom); const [openOasModal, setOpenOasModal] = useRecoilState(oasModalOpenAtom);
const [openWebSocket, setOpenWebSocket] = useState(true); const [openWebSocket, setOpenWebSocket] = useState(true);
const trafficViewerApi = {...api} const trafficViewerApi = { ...api }
const handleOpenOasModal = () => { const handleOpenOasModal = () => {
setOpenWebSocket(false) setOpenWebSocket(false)
@@ -36,37 +36,31 @@ const trafficViewerApi = {...api}
}, 500); }, 500);
const actionButtons = (window["isOasEnabled"] || window["isServiceMapEnabled"]) && const actionButtons = (window["isOasEnabled"] || window["isServiceMapEnabled"]) &&
<div style={{ display: 'flex', height: "100%" }}> <div style={{ display: 'flex', height: "100%" }}>
{window["isOasEnabled"] && <Button {window["isOasEnabled"] && <Button
startIcon={<img className="custom" src={services} alt="services"></img>} startIcon={<img className="custom" src={services} alt="services"></img>}
size="large" size="large"
type="submit" variant="contained"
variant="contained" className={commonClasses.outlinedButton + " " + commonClasses.imagedButton}
className={commonClasses.outlinedButton + " " + commonClasses.imagedButton} style={{ marginRight: 25, textTransform: 'unset' }}
style={{ marginRight: 25 }} onClick={handleOpenOasModal}>
onClick={handleOpenOasModal}> Service Catalog
Show OAS </Button>}
</Button>} {window["isServiceMapEnabled"] && <Button
{window["isServiceMapEnabled"] && <Button startIcon={<img src={serviceMap} className="custom" alt="service-map" style={{ marginRight: "8%" }}></img>}
startIcon={<img src={serviceMap} className="custom" alt="service-map" style={{marginRight:"8%"}}></img>} size="large"
size="large" variant="contained"
variant="contained" className={commonClasses.outlinedButton + " " + commonClasses.imagedButton}
className={commonClasses.outlinedButton + " " + commonClasses.imagedButton} onClick={openServiceMapModalDebounce}
onClick={openServiceMapModalDebounce}> style={{ textTransform: 'unset' }}>
Service Map Service Map
</Button>} </Button>}
</div> </div>
useEffect(() => {
return () => {
//closeSocket()
}
},[])
return ( return (
<> <>
<TrafficViewer setAnalyzeStatus={setAnalyzeStatus} webSocketUrl={getWebsocketUrl()} isCloseWebSocket={!openWebSocket} <TrafficViewer setAnalyzeStatus={setAnalyzeStatus} webSocketUrl={MizuWebsocketURL} isCloseWebSocket={!openWebSocket}
trafficViewerApiProp={trafficViewerApi} actionButtons={actionButtons} isShowStatusBar={!openOasModal}/> trafficViewerApiProp={trafficViewerApi} actionButtons={actionButtons} isShowStatusBar={!(openOasModal || serviceMapModalOpen)} isDemoBannerView={false} />
</> </>
); );
}; };

View File

@@ -11,6 +11,7 @@ import ServiceMapOptions from './ServiceMapOptions'
import { useCommonStyles } from "../../helpers/commonStyle"; import { useCommonStyles } from "../../helpers/commonStyle";
import refresh from "../assets/refresh.svg"; import refresh from "../assets/refresh.svg";
import close from "../assets/close.svg"; import close from "../assets/close.svg";
import { TOAST_CONTAINER_ID } from "../../consts";
interface GraphData { interface GraphData {
nodes: Node[]; nodes: Node[];
@@ -99,6 +100,7 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onOpen
const commonClasses = useCommonStyles(); const commonClasses = useCommonStyles();
const [isLoading, setIsLoading] = useState<boolean>(true); const [isLoading, setIsLoading] = useState<boolean>(true);
const [graphData, setGraphData] = useState<GraphData>({ nodes: [], edges: [] }); const [graphData, setGraphData] = useState<GraphData>({ nodes: [], edges: [] });
const [graphOptions, setGraphOptions] = useState(ServiceMapOptions);
const getServiceMapData = useCallback(async () => { const getServiceMapData = useCallback(async () => {
try { try {
@@ -140,7 +142,7 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onOpen
setGraphData(newGraphData) setGraphData(newGraphData)
} catch (ex) { } catch (ex) {
toast.error("An error occurred while loading Mizu Service Map, see console for mode details"); toast.error("An error occurred while loading Mizu Service Map, see console for mode details", { containerId: TOAST_CONTAINER_ID });
console.error(ex); console.error(ex);
} finally { } finally {
setIsLoading(false) setIsLoading(false)
@@ -148,6 +150,14 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onOpen
// eslint-disable-next-line // eslint-disable-next-line
}, [isOpen]) }, [isOpen])
useEffect(() => {
if(graphData?.nodes?.length === 0) return;
let options = {...graphOptions};
options.physics.barnesHut.avoidOverlap = graphData?.nodes?.length > 10 ? 0 : 1;
setGraphOptions(options);
// eslint-disable-next-line
},[graphData?.nodes?.length])
useEffect(() => { useEffect(() => {
getServiceMapData(); getServiceMapData();
return () => setGraphData({ nodes: [], edges: [] }) return () => setGraphData({ nodes: [], edges: [] })
@@ -176,32 +186,32 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onOpen
<img alt="spinner" src={spinnerImg} style={{ height: 50 }} /> <img alt="spinner" src={spinnerImg} style={{ height: 50 }} />
</div>} </div>}
{!isLoading && <div style={{ height: "100%", width: "100%" }}> {!isLoading && <div style={{ height: "100%", width: "100%" }}>
<div style={{ display: "flex", justifyContent: "space-between" }}> <div style={{ display: "flex", justifyContent: "space-between" }}>
<div> <div>
<Button <Button
startIcon={<img src={refresh} className="custom" alt="refresh" style={{ marginRight:"8%"}}></img>} startIcon={<img src={refresh} className="custom" alt="refresh" style={{ marginRight: "8%" }}/>}
size="medium" size="medium"
variant="contained" variant="contained"
className={commonClasses.outlinedButton + " " + commonClasses.imagedButton} className={commonClasses.outlinedButton + " " + commonClasses.imagedButton}
onClick={refreshServiceMap} onClick={refreshServiceMap}
> >
Refresh Refresh
</Button> </Button>
</div>
<img src={close} alt="close" onClick={() => onClose()} style={{ cursor: "pointer" }}/>
</div> </div>
<img src={close} alt="close" onClick={() => onClose()} style={{cursor:"pointer"}}></img>
</div>
<Graph <Graph
graph={graphData} graph={graphData}
options={ServiceMapOptions} options={graphOptions}
/> />
<div className='legend-scale'> <div className='legend-scale'>
<ul className='legend-labels'> <ul className='legend-labels'>
<li><span style={{ background: '#205cf5' }}></span>HTTP</li> <li><span style={{ background: '#205cf5' }}/>HTTP</li>
<li><span style={{ background: '#244c5a' }}></span>HTTP/2</li> <li><span style={{ background: '#244c5a' }}/>HTTP/2</li>
<li><span style={{ background: '#244c5a' }}></span>gRPC</li> <li><span style={{ background: '#244c5a' }}/>gRPC</li>
<li><span style={{ background: '#ff6600' }}></span>AMQP</li> <li><span style={{ background: '#ff6600' }}/>AMQP</li>
<li><span style={{ background: '#000000' }}></span>KAFKA</li> <li><span style={{ background: '#000000' }}/>KAFKA</li>
<li><span style={{ background: '#a41e11' }}></span>REDIS</li> <li><span style={{ background: '#a41e11' }}/>REDIS</li>
</ul> </ul>
</div> </div>
</div>} </div>}
@@ -210,4 +220,4 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onOpen
</Modal> </Modal>
); );
} }

View File

@@ -148,7 +148,7 @@ const ServiceMapOptions = {
springLength: 180, springLength: 180,
springConstant: 0.04, springConstant: 0.04,
damping: 0.2, damping: 0.2,
avoidOverlap: 1 avoidOverlap: 0
}, },
}, },
layout: { layout: {
@@ -171,4 +171,4 @@ const ServiceMapOptions = {
}, },
}; };
export default ServiceMapOptions export default ServiceMapOptions

Some files were not shown because too many files have changed in this diff Show More