mirror of
https://github.com/kubeshark/kubeshark.git
synced 2026-02-15 02:19:54 +00:00
Compare commits
68 Commits
31.0-dev22
...
32.0-dev2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fadd052f02 | ||
|
|
171b72831f | ||
|
|
36910c84ed | ||
|
|
95a37c877e | ||
|
|
7ce74c71b4 | ||
|
|
6a15bf1aed | ||
|
|
2de57a6630 | ||
|
|
b5c5a8934b | ||
|
|
df269fa950 | ||
|
|
80ebd7199d | ||
|
|
9b475a78f9 | ||
|
|
d76f24edb8 | ||
|
|
32ba653d12 | ||
|
|
e03db7b09b | ||
|
|
de533730d3 | ||
|
|
d834fcc3cb | ||
|
|
1624b0d7b9 | ||
|
|
9715bb046b | ||
|
|
65e5ebe23c | ||
|
|
30986c3b22 | ||
|
|
1e167f2757 | ||
|
|
149e86d050 | ||
|
|
1213162b85 | ||
|
|
189c158150 | ||
|
|
c5006e5f57 | ||
|
|
d7fcf273c0 | ||
|
|
eca3267b47 | ||
|
|
a527fc6c51 | ||
|
|
e104128df8 | ||
|
|
5bb904d068 | ||
|
|
1a5378b64b | ||
|
|
7cfe506897 | ||
|
|
ab64046e8e | ||
|
|
62cbcf8857 | ||
|
|
8f3b0e8fee | ||
|
|
32d473ea26 | ||
|
|
97000293fd | ||
|
|
3ed9bc1e0d | ||
|
|
86e5dcea19 | ||
|
|
81fe4af30d | ||
|
|
df1fd2c3a7 | ||
|
|
f8496c0235 | ||
|
|
2de7107c0a | ||
|
|
22e3b3d8b2 | ||
|
|
45611c4c13 | ||
|
|
bb425fa6e2 | ||
|
|
4bc83ebcb5 | ||
|
|
bbb44dae79 | ||
|
|
72a1aba3e5 | ||
|
|
d8fb8ff710 | ||
|
|
f344bd2633 | ||
|
|
6575495fa5 | ||
|
|
cf5c03d45c | ||
|
|
491da24c63 | ||
|
|
dc0365dfb0 | ||
|
|
84f641d8fe | ||
|
|
ac94554d2f | ||
|
|
2ef6afa395 | ||
|
|
697bfef861 | ||
|
|
f8c0ddd69a | ||
|
|
2253737625 | ||
|
|
4a0294c61a | ||
|
|
9053c58146 | ||
|
|
5d5b13fc86 | ||
|
|
af61c69fb6 | ||
|
|
94dfa68858 | ||
|
|
50c0062db4 | ||
|
|
720969bbe6 |
18
.github/workflows/acceptance_tests.yml
vendored
18
.github/workflows/acceptance_tests.yml
vendored
@@ -8,6 +8,9 @@ on:
|
||||
branches:
|
||||
- 'develop'
|
||||
|
||||
env:
|
||||
MIZU_CI_IMAGE: mizu/ci:0.0
|
||||
|
||||
jobs:
|
||||
run-acceptance-tests:
|
||||
name: Run acceptance tests
|
||||
@@ -21,6 +24,19 @@ jobs:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
push: false
|
||||
load: true
|
||||
tags: ${{ env.MIZU_CI_IMAGE }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Setup acceptance test
|
||||
run: ./acceptanceTests/setup.sh
|
||||
|
||||
@@ -43,7 +59,7 @@ jobs:
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
notification_title: 'Mizu {workflow} has {status_message}'
|
||||
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 }}```'
|
||||
message_format: '{emoji} *{workflow}* {status_message} during <{run_url}|run>, after commit <{commit_url}|{commit_sha}> by ${{ github.event.head_commit.author.name }} <${{ github.event.head_commit.author.email }}> ```${{ github.event.head_commit.message }}```'
|
||||
footer: 'Linked Repo <{repo_url}|{repo}>'
|
||||
notify_when: 'failure'
|
||||
env:
|
||||
|
||||
54
.github/workflows/acceptance_tests_on_pr.yml
vendored
Normal file
54
.github/workflows/acceptance_tests_on_pr.yml
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
name: Acceptance tests on PR
|
||||
|
||||
on: push
|
||||
|
||||
env:
|
||||
MIZU_CI_IMAGE: mizu/ci:0.0
|
||||
|
||||
concurrency:
|
||||
group: acceptance-tests-on-pr-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
run-tests:
|
||||
name: Run tests
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ contains(github.event.head_commit.message, '#run_acceptance_tests') }}
|
||||
|
||||
steps:
|
||||
- name: Set up Go 1.17
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.17'
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
push: false
|
||||
load: true
|
||||
tags: ${{ env.MIZU_CI_IMAGE }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Setup acceptance test
|
||||
run: ./acceptanceTests/setup.sh
|
||||
|
||||
- name: Create k8s users and change context
|
||||
env:
|
||||
USERNAME_UNRESTRICTED: user-with-clusterwide-access
|
||||
USERNAME_RESTRICTED: user-with-restricted-access
|
||||
run: |
|
||||
./acceptanceTests/create_user.sh "${USERNAME_UNRESTRICTED}"
|
||||
./acceptanceTests/create_user.sh "${USERNAME_RESTRICTED}"
|
||||
kubectl apply -f cli/cmd/permissionFiles/permissions-all-namespaces-tap.yaml
|
||||
kubectl config use-context ${USERNAME_UNRESTRICTED}
|
||||
|
||||
- name: Test
|
||||
run: make acceptance-test
|
||||
5
.github/workflows/inactive-issues-close.yaml
vendored
5
.github/workflows/inactive-issues-close.yaml
vendored
@@ -13,10 +13,11 @@ jobs:
|
||||
- uses: actions/stale@v3
|
||||
with:
|
||||
days-before-issue-stale: 30
|
||||
days-before-issue-close: 14
|
||||
days-before-issue-close: -1
|
||||
exempt-issue-labels: "enhancement"
|
||||
stale-issue-label: "stale"
|
||||
stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
|
||||
close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
|
||||
close-issue-message: ""
|
||||
days-before-pr-stale: -1
|
||||
days-before-pr-close: -1
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
3
.github/workflows/test.yml
vendored
3
.github/workflows/test.yml
vendored
@@ -56,7 +56,7 @@ jobs:
|
||||
|
||||
- name: Check extensions modified files
|
||||
id: ext_modified_files
|
||||
run: devops/check_modified_files.sh tap/extensions/
|
||||
run: devops/check_modified_files.sh tap/extensions/ tap/api/
|
||||
|
||||
- name: Extensions Test
|
||||
if: github.event_name == 'push' || steps.ext_modified_files.outputs.matched == 'true'
|
||||
@@ -64,4 +64,3 @@ jobs:
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v2
|
||||
|
||||
|
||||
@@ -87,8 +87,8 @@ RUN go build -ldflags="-extldflags=-static -s -w \
|
||||
-X 'github.com/up9inc/mizu/agent/pkg/version.Ver=${VER}'" -o mizuagent .
|
||||
|
||||
# Download Basenine executable, verify the sha1sum
|
||||
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.6/basenine_linux_${GOARCH}.sha256 ./basenine_linux_${GOARCH}.sha256
|
||||
ADD https://github.com/up9inc/basenine/releases/download/v0.7.3/basenine_linux_${GOARCH} ./basenine_linux_${GOARCH}
|
||||
ADD https://github.com/up9inc/basenine/releases/download/v0.7.3/basenine_linux_${GOARCH}.sha256 ./basenine_linux_${GOARCH}.sha256
|
||||
|
||||
RUN shasum -a 256 -c basenine_linux_"${GOARCH}".sha256 && \
|
||||
chmod +x ./basenine_linux_"${GOARCH}" && \
|
||||
|
||||
@@ -39,13 +39,13 @@ export function verifyMinimumEntries() {
|
||||
});
|
||||
}
|
||||
|
||||
export function leftTextCheck(entryNum, path, expectedText) {
|
||||
cy.get(`#list #entry-${entryNum} ${path}`).invoke('text').should('eq', expectedText);
|
||||
export function leftTextCheck(entryId, path, expectedText) {
|
||||
cy.get(`#list #entry-${entryId} ${path}`).invoke('text').should('eq', expectedText);
|
||||
}
|
||||
|
||||
export function leftOnHoverCheck(entryNum, path, filterName) {
|
||||
cy.get(`#list #entry-${entryNum} ${path}`).trigger('mouseover');
|
||||
cy.get(`#list #entry-${entryNum} [data-cy='QueryableTooltip']`).invoke('text').should('match', new RegExp(filterName));
|
||||
export function leftOnHoverCheck(entryId, path, filterName) {
|
||||
cy.get(`#list #entry-${entryId} ${path}`).trigger('mouseover');
|
||||
cy.get(`#list #entry-${entryId} [data-cy='QueryableTooltip']`).invoke('text').should('match', new RegExp(filterName));
|
||||
}
|
||||
|
||||
export function rightTextCheck(path, expectedText) {
|
||||
@@ -89,13 +89,13 @@ export function checkFilterByMethod(funcDict) {
|
||||
cy.get('#entries-length').invoke('text').then(len => {
|
||||
resizeIfNeeded(len);
|
||||
listElmWithIdAttr.forEach(entry => {
|
||||
if (entry?.id && entry.id.match(RegExp(/entry-(\d{2}|\d{1})$/gm))) {
|
||||
const entryNum = getEntryNumById(entry.id);
|
||||
if (entry?.id && entry.id.match(RegExp(/entry-(\d{24})$/gm))) {
|
||||
const entryId = getEntryId(entry.id);
|
||||
|
||||
leftTextCheck(entryNum, methodDict.pathLeft, methodDict.expectedText);
|
||||
leftTextCheck(entryNum, protocolDict.pathLeft, protocolDict.expectedTextLeft);
|
||||
leftTextCheck(entryId, methodDict.pathLeft, methodDict.expectedText);
|
||||
leftTextCheck(entryId, protocolDict.pathLeft, protocolDict.expectedTextLeft);
|
||||
if (summaryDict)
|
||||
leftTextCheck(entryNum, summaryDict.pathLeft, summaryDict.expectedText);
|
||||
leftTextCheck(entryId, summaryDict.pathLeft, summaryDict.expectedText);
|
||||
|
||||
if (!doneCheckOnFirst) {
|
||||
deepCheck(funcDict, protocolDict, methodDict, entry);
|
||||
@@ -111,6 +111,11 @@ export function checkFilterByMethod(funcDict) {
|
||||
});
|
||||
}
|
||||
|
||||
export function getEntryId(id) {
|
||||
// take the second part from the string (entry-<ID>)
|
||||
return id.split('-')[1];
|
||||
}
|
||||
|
||||
function resizeIfNeeded(entriesLen) {
|
||||
if (entriesLen > maxEntriesInDom){
|
||||
Cypress.config().viewportHeight === Cypress.env('normalMizuHeight') ?
|
||||
@@ -119,14 +124,14 @@ function resizeIfNeeded(entriesLen) {
|
||||
}
|
||||
|
||||
function deepCheck(generalDict, protocolDict, methodDict, entry) {
|
||||
const entryNum = getEntryNumById(entry.id);
|
||||
const entryId = getEntryId(entry.id);
|
||||
const {summary, value} = generalDict;
|
||||
const summaryDict = getSummaryDict(summary);
|
||||
|
||||
leftOnHoverCheck(entryNum, methodDict.pathLeft, methodDict.expectedOnHover);
|
||||
leftOnHoverCheck(entryNum, protocolDict.pathLeft, protocolDict.expectedOnHover);
|
||||
leftOnHoverCheck(entryId, methodDict.pathLeft, methodDict.expectedOnHover);
|
||||
leftOnHoverCheck(entryId, protocolDict.pathLeft, protocolDict.expectedOnHover);
|
||||
if (summaryDict)
|
||||
leftOnHoverCheck(entryNum, summaryDict.pathLeft, summaryDict.expectedOnHover);
|
||||
leftOnHoverCheck(entryId, summaryDict.pathLeft, summaryDict.expectedOnHover);
|
||||
|
||||
cy.get(`#${entry.id}`).click();
|
||||
|
||||
@@ -183,7 +188,3 @@ function getProtocolDict(protocol, protocolText) {
|
||||
expectedOnHover: protocol.toLowerCase()
|
||||
};
|
||||
}
|
||||
|
||||
function getEntryNumById (id) {
|
||||
return parseInt(id.split('-')[1]);
|
||||
}
|
||||
|
||||
@@ -15,15 +15,11 @@ function checkEntries() {
|
||||
checkThatAllEntriesShown();
|
||||
resizeToHugeMizu();
|
||||
|
||||
cy.get('#total-entries').then(number => {
|
||||
const numOfEntries = parseInt(number.text());
|
||||
[...Array(numOfEntries).keys()].map(checkEntry);
|
||||
cy.get('#list [id^=entry]').each(entryElement => {
|
||||
entryElement.click();
|
||||
cy.get('#tbody-Headers').should('be.visible');
|
||||
isValueExistsInElement(false, 'Ignored-User-Agent', '#tbody-Headers');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function checkEntry(entryIndex) {
|
||||
cy.get(`#entry-${entryIndex}`).click();
|
||||
cy.get('#tbody-Headers').should('be.visible');
|
||||
isValueExistsInElement(false, 'Ignored-User-Agent', '#tbody-Headers');
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {findLineAndCheck, getExpectedDetailsDict} from "../testHelpers/StatusBarHelper";
|
||||
import {
|
||||
getEntryId,
|
||||
leftOnHoverCheck,
|
||||
leftTextCheck,
|
||||
resizeToHugeMizu,
|
||||
@@ -148,9 +149,6 @@ function checkFilterNoResults(filterName) {
|
||||
// the DOM should show 0 entries
|
||||
cy.get('#entries-length').should('have.text', '0');
|
||||
|
||||
// going through every potential entry and verifies that it doesn't exist
|
||||
[...Array(parseInt(totalEntries)).keys()].map(shouldNotExist);
|
||||
|
||||
cy.get('[title="Fetch old records"]').click();
|
||||
cy.get('#noMoreDataTop', {timeout: refreshWaitTimeout}).should('be.visible');
|
||||
cy.get('#entries-length').should('have.text', '0'); // after loading all entries there should still be 0 entries
|
||||
@@ -162,10 +160,6 @@ function checkFilterNoResults(filterName) {
|
||||
});
|
||||
}
|
||||
|
||||
function shouldNotExist(entryNum) {
|
||||
cy.get(`entry-${entryNum}`).should('not.exist');
|
||||
}
|
||||
|
||||
function checkIllegalFilter(illegalFilterName) {
|
||||
it(`should show red search bar with the input: ${illegalFilterName}`, function () {
|
||||
cy.reload();
|
||||
@@ -185,30 +179,44 @@ function checkIllegalFilter(illegalFilterName) {
|
||||
});
|
||||
});
|
||||
}
|
||||
function checkFilter(filterDetails){
|
||||
const {name, leftSidePath, rightSidePath, rightSideExpectedText, leftSideExpectedText, applyByEnter} = filterDetails;
|
||||
|
||||
function checkFilter(filterDetails) {
|
||||
const {
|
||||
name,
|
||||
leftSidePath,
|
||||
rightSidePath,
|
||||
rightSideExpectedText,
|
||||
leftSideExpectedText,
|
||||
applyByEnter
|
||||
} = filterDetails;
|
||||
|
||||
const entriesForDeeperCheck = 5;
|
||||
|
||||
it(`checking the filter: ${name}`, function () {
|
||||
cy.get('#total-entries').should('not.have.text', '0').then(number => {
|
||||
const totalEntries = number.text();
|
||||
|
||||
// checks the hover on the last entry (the only one in DOM at the beginning)
|
||||
leftOnHoverCheck(totalEntries - 1, leftSidePath, name);
|
||||
cy.get(`#list [id^=entry]`).last().then(elem => {
|
||||
const element = elem[0];
|
||||
const entryId = getEntryId(element.id);
|
||||
// checks the hover on the last entry (the only one in DOM at the beginning)
|
||||
leftOnHoverCheck(entryId, leftSidePath, name);
|
||||
|
||||
cy.get('.w-tc-editor-text').clear();
|
||||
// applying the filter with alt+enter or with the button
|
||||
cy.get('.w-tc-editor-text').type(`${name}${applyByEnter ? '{alt+enter}' : ''}`);
|
||||
cy.get('.w-tc-editor').should('have.attr', 'style').and('include', Cypress.env('greenFilterColor'));
|
||||
if (!applyByEnter)
|
||||
cy.get('[type="submit"]').click();
|
||||
cy.get('.w-tc-editor-text').clear();
|
||||
// applying the filter with alt+enter or with the button
|
||||
cy.get('.w-tc-editor-text').type(`${name}${applyByEnter ? '{alt+enter}' : ''}`);
|
||||
cy.get('.w-tc-editor').should('have.attr', 'style').and('include', Cypress.env('greenFilterColor'));
|
||||
if (!applyByEnter)
|
||||
cy.get('[type="submit"]').click();
|
||||
|
||||
// only one entry in DOM after filtering, checking all checks on it
|
||||
leftTextCheck(totalEntries - 1, leftSidePath, leftSideExpectedText);
|
||||
leftOnHoverCheck(totalEntries - 1, leftSidePath, name);
|
||||
rightTextCheck(rightSidePath, rightSideExpectedText);
|
||||
rightOnHoverCheck(rightSidePath, name);
|
||||
checkRightSideResponseBody();
|
||||
// only one entry in DOM after filtering, checking all checks on it
|
||||
leftTextCheck(entryId, leftSidePath, leftSideExpectedText);
|
||||
leftOnHoverCheck(entryId, leftSidePath, name);
|
||||
|
||||
rightTextCheck(rightSidePath, rightSideExpectedText);
|
||||
rightOnHoverCheck(rightSidePath, name);
|
||||
checkRightSideResponseBody();
|
||||
});
|
||||
|
||||
cy.get('[title="Fetch old records"]').click();
|
||||
resizeToHugeMizu();
|
||||
@@ -217,28 +225,33 @@ function checkFilter(filterDetails){
|
||||
cy.get('#entries-length', {timeout: refreshWaitTimeout}).should('have.text', totalEntries);
|
||||
|
||||
// checking only 'leftTextCheck' on all entries because the rest of the checks require more time
|
||||
[...Array(parseInt(totalEntries)).keys()].forEach(entryNum => {
|
||||
leftTextCheck(entryNum, leftSidePath, leftSideExpectedText);
|
||||
cy.get(`#list [id^=entry]`).each(elem => {
|
||||
const element = elem[0];
|
||||
let entryId = getEntryId(element.id);
|
||||
leftTextCheck(entryId, leftSidePath, leftSideExpectedText);
|
||||
});
|
||||
|
||||
// making the other 3 checks on the first X entries (longer time for each check)
|
||||
deeperChcek(leftSidePath, rightSidePath, name, leftSideExpectedText, rightSideExpectedText, entriesForDeeperCheck);
|
||||
deeperCheck(leftSidePath, rightSidePath, name, leftSideExpectedText, rightSideExpectedText, entriesForDeeperCheck);
|
||||
|
||||
// reloading then waiting for the entries number to load
|
||||
resizeToNormalMizu();
|
||||
cy.reload();
|
||||
cy.get('#total-entries', {timeout: refreshWaitTimeout}).should('have.text', totalEntries);
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function deeperChcek(leftSidePath, rightSidePath, filterName, leftSideExpectedText, rightSideExpectedText, entriesNumToCheck) {
|
||||
[...Array(entriesNumToCheck).keys()].forEach(entryNum => {
|
||||
leftOnHoverCheck(entryNum, leftSidePath, filterName);
|
||||
function deeperCheck(leftSidePath, rightSidePath, filterName, leftSideExpectedText, rightSideExpectedText, entriesNumToCheck) {
|
||||
cy.get(`#list [id^=entry]`).each((element, index) => {
|
||||
if (index < entriesNumToCheck) {
|
||||
const entryId = getEntryId(element[0].id);
|
||||
leftOnHoverCheck(entryId, leftSidePath, filterName);
|
||||
|
||||
cy.get(`#list #entry-${entryNum}`).click();
|
||||
rightTextCheck(rightSidePath, rightSideExpectedText);
|
||||
rightOnHoverCheck(rightSidePath, filterName);
|
||||
cy.get(`#list #entry-${entryId}`).click();
|
||||
rightTextCheck(rightSidePath, rightSideExpectedText);
|
||||
rightOnHoverCheck(rightSidePath, filterName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -57,11 +57,16 @@ kubectl expose deployment rabbitmq --type=LoadBalancer --port=5672 -n mizu-tests
|
||||
echo "Starting proxy"
|
||||
kubectl proxy --port=8080 &
|
||||
|
||||
echo "Setting minikube docker env"
|
||||
eval $(minikube docker-env)
|
||||
if [[ -z "${CI}" ]]; then
|
||||
echo "Setting env var of mizu ci image"
|
||||
export MIZU_CI_IMAGE="mizu/ci:0.0"
|
||||
echo "Build agent image"
|
||||
docker build -t "${MIZU_CI_IMAGE}" .
|
||||
else
|
||||
echo "not building docker image in CI because it is created as separate step"
|
||||
fi
|
||||
|
||||
echo "Build agent image"
|
||||
docker build -t mizu/ci:0.0 .
|
||||
minikube image load "${MIZU_CI_IMAGE}"
|
||||
|
||||
echo "Build cli"
|
||||
cd cli && make build GIT_BRANCH=ci SUFFIX=ci
|
||||
|
||||
@@ -212,9 +212,10 @@ func DeleteKubeFile(kubeContext string, namespace string, filename string) error
|
||||
}
|
||||
|
||||
func getDefaultCommandArgs() []string {
|
||||
agentImageValue := os.Getenv("MIZU_CI_IMAGE")
|
||||
setFlag := "--set"
|
||||
telemetry := "telemetry=false"
|
||||
agentImage := "agent-image=mizu/ci:0.0"
|
||||
agentImage := fmt.Sprintf("agent-image=%s", agentImageValue)
|
||||
imagePullPolicy := "image-pull-policy=IfNotPresent"
|
||||
headless := "headless=true"
|
||||
|
||||
|
||||
@@ -15,12 +15,13 @@ require (
|
||||
github.com/go-playground/validator/v10 v10.10.0
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/jinzhu/copier v0.3.5
|
||||
github.com/nav-inc/datetime v0.1.3
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
||||
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/up9inc/basenine/client/go v0.0.0-20220326121918-785f3061c8ce
|
||||
github.com/up9inc/basenine/client/go v0.0.0-20220419100955-e2ca51087607
|
||||
github.com/up9inc/mizu/shared v0.0.0
|
||||
github.com/up9inc/mizu/tap v0.0.0
|
||||
github.com/up9inc/mizu/tap/api v0.0.0
|
||||
@@ -48,7 +49,6 @@ require (
|
||||
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/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/cilium/ebpf v0.8.0 // indirect
|
||||
|
||||
@@ -108,8 +108,6 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
|
||||
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/bradleyfalzon/tlsx v0.0.0-20170624122154-28fd0e59bac4 h1:NJOOlc6ZJjix0A1rAU+nxruZtR8KboG1848yqpIUo4M=
|
||||
github.com/bradleyfalzon/tlsx v0.0.0-20170624122154-28fd0e59bac4/go.mod h1:DQPxZS994Ld1Y8uwnJT+dRL04XPD0cElP/pHH/zEBHM=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
@@ -428,6 +426,8 @@ github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
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/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg=
|
||||
github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
@@ -681,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.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
|
||||
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
|
||||
github.com/up9inc/basenine/client/go v0.0.0-20220326121918-785f3061c8ce h1:vMTCpKItc9OyTLJXocNaq2NcBU5EnurJgTVOYb8W8dw=
|
||||
github.com/up9inc/basenine/client/go v0.0.0-20220326121918-785f3061c8ce/go.mod h1:SvJGPoa/6erhUQV7kvHBwM/0x5LyO6XaG2lUaCaKiUI=
|
||||
github.com/up9inc/basenine/client/go v0.0.0-20220419100955-e2ca51087607 h1:UqxUSkOYOmsLZWQtMSk02ttnhdRwBRLOLt2aDiS9tEk=
|
||||
github.com/up9inc/basenine/client/go v0.0.0-20220419100955-e2ca51087607/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/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
github.com/wI2L/jsondiff v0.1.1 h1:r2TkoEet7E4JMO5+s1RCY2R0LrNPNHY6hbDeow2hRHw=
|
||||
|
||||
@@ -199,7 +199,7 @@ func runInHarReaderMode() {
|
||||
func enableExpFeatureIfNeeded() {
|
||||
if config.Config.OAS {
|
||||
oasGenerator := dependency.GetInstance(dependency.OasGeneratorDependency).(oas.OasGenerator)
|
||||
oasGenerator.Start()
|
||||
oasGenerator.Start(nil)
|
||||
}
|
||||
if config.Config.ServiceMap {
|
||||
serviceMapGenerator := dependency.GetInstance(dependency.ServiceMapGeneratorDependency).(servicemap.ServiceMap)
|
||||
@@ -320,6 +320,7 @@ func dialSocketWithRetry(socketAddress string, retryAmount int, retryDelay time.
|
||||
for i := 1; i < retryAmount; i++ {
|
||||
socketConnection, _, err := dialer.Dial(socketAddress, nil)
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
if i < retryAmount {
|
||||
logger.Log.Infof("socket connection to %s failed: %v, retrying %d out of %d in %d seconds...", socketAddress, err, i, retryAmount, retryDelay/time.Second)
|
||||
time.Sleep(retryDelay)
|
||||
@@ -371,7 +372,7 @@ func handleIncomingMessageAsTapper(socketConnection *websocket.Conn) {
|
||||
|
||||
func initializeDependencies() {
|
||||
dependency.RegisterGenerator(dependency.ServiceMapGeneratorDependency, func() interface{} { return servicemap.GetDefaultServiceMapInstance() })
|
||||
dependency.RegisterGenerator(dependency.OasGeneratorDependency, func() interface{} { return oas.GetDefaultOasGeneratorInstance(nil) })
|
||||
dependency.RegisterGenerator(dependency.OasGeneratorDependency, func() interface{} { return oas.GetDefaultOasGeneratorInstance() })
|
||||
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{} })
|
||||
|
||||
@@ -5,13 +5,14 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/up9inc/mizu/agent/pkg/models"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/up9inc/mizu/agent/pkg/models"
|
||||
|
||||
"github.com/up9inc/mizu/agent/pkg/dependency"
|
||||
"github.com/up9inc/mizu/agent/pkg/elastic"
|
||||
"github.com/up9inc/mizu/agent/pkg/har"
|
||||
@@ -102,11 +103,19 @@ func startReadingChannel(outputItems <-chan *tapApi.OutputChannelItem, extension
|
||||
panic("Channel of captured messages is nil")
|
||||
}
|
||||
|
||||
BasenineReconnect:
|
||||
connection, err := basenine.NewConnection(shared.BasenineHost, shared.BaseninePort)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
logger.Log.Errorf("Can't establish a new connection to Basenine server: %v", err)
|
||||
time.Sleep(shared.BasenineReconnectInterval * time.Second)
|
||||
goto BasenineReconnect
|
||||
}
|
||||
if err = connection.InsertMode(); err != nil {
|
||||
logger.Log.Errorf("Insert mode call failed: %v", err)
|
||||
connection.Close()
|
||||
time.Sleep(shared.BasenineReconnectInterval * time.Second)
|
||||
goto BasenineReconnect
|
||||
}
|
||||
connection.InsertMode()
|
||||
|
||||
disableOASValidation := false
|
||||
ctx := context.Background()
|
||||
@@ -119,19 +128,24 @@ func startReadingChannel(outputItems <-chan *tapApi.OutputChannelItem, extension
|
||||
for item := range outputItems {
|
||||
extension := extensionsMap[item.Protocol.Name]
|
||||
resolvedSource, resolvedDestionation, namespace := resolveIP(item.ConnectionInfo)
|
||||
|
||||
if namespace == "" && item.Namespace != tapApi.UNKNOWN_NAMESPACE {
|
||||
namespace = item.Namespace
|
||||
}
|
||||
|
||||
mizuEntry := extension.Dissector.Analyze(item, resolvedSource, resolvedDestionation, namespace)
|
||||
if extension.Protocol.Name == "http" {
|
||||
if !disableOASValidation {
|
||||
var httpPair tapApi.HTTPRequestResponsePair
|
||||
if err := json.Unmarshal([]byte(mizuEntry.HTTPPair), &httpPair); err != nil {
|
||||
logger.Log.Error(err)
|
||||
} else {
|
||||
contract := handleOAS(ctx, doc, router, httpPair.Request.Payload.RawRequest, httpPair.Response.Payload.RawResponse, contractContent)
|
||||
mizuEntry.ContractStatus = contract.Status
|
||||
mizuEntry.ContractRequestReason = contract.RequestReason
|
||||
mizuEntry.ContractResponseReason = contract.ResponseReason
|
||||
mizuEntry.ContractContent = contract.Content
|
||||
}
|
||||
|
||||
contract := handleOAS(ctx, doc, router, httpPair.Request.Payload.RawRequest, httpPair.Response.Payload.RawResponse, contractContent)
|
||||
mizuEntry.ContractStatus = contract.Status
|
||||
mizuEntry.ContractRequestReason = contract.RequestReason
|
||||
mizuEntry.ContractResponseReason = contract.ResponseReason
|
||||
mizuEntry.ContractContent = contract.Content
|
||||
}
|
||||
|
||||
harEntry, err := har.NewEntry(mizuEntry.Request, mizuEntry.Response, mizuEntry.StartTime, mizuEntry.ElapsedTime)
|
||||
@@ -143,12 +157,18 @@ func startReadingChannel(outputItems <-chan *tapApi.OutputChannelItem, extension
|
||||
|
||||
data, err := json.Marshal(mizuEntry)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
logger.Log.Errorf("Error while marshaling entry: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
providers.EntryAdded(len(data))
|
||||
|
||||
connection.SendText(string(data))
|
||||
if err = connection.SendText(string(data)); err != nil {
|
||||
logger.Log.Errorf("An error occured while inserting a new record to database: %v", err)
|
||||
connection.Close()
|
||||
time.Sleep(shared.BasenineReconnectInterval * time.Second)
|
||||
goto BasenineReconnect
|
||||
}
|
||||
|
||||
serviceMapGenerator := dependency.GetInstance(dependency.ServiceMapGeneratorDependency).(servicemap.ServiceMapSink)
|
||||
serviceMapGenerator.NewTCPEntry(mizuEntry.Source, mizuEntry.Destination, &item.Protocol)
|
||||
|
||||
@@ -24,7 +24,7 @@ func (e *BasenineEntryStreamer) Get(ctx context.Context, socketId int, params *W
|
||||
|
||||
connection, err := basenine.NewConnection(shared.BasenineHost, shared.BaseninePort)
|
||||
if err != nil {
|
||||
logger.Log.Errorf("failed to establish a connection to Basenine: %v", err)
|
||||
logger.Log.Errorf("Failed to establish a connection to Basenine: %v", err)
|
||||
entryStreamerSocketConnector.CleanupSocket(socketId)
|
||||
return err
|
||||
}
|
||||
@@ -49,7 +49,7 @@ func (e *BasenineEntryStreamer) Get(ctx context.Context, socketId int, params *W
|
||||
var entry *tapApi.Entry
|
||||
err = json.Unmarshal(bytes, &entry)
|
||||
if err != nil {
|
||||
logger.Log.Debugf("error unmarshalling entry: %v", err.Error())
|
||||
logger.Log.Debugf("Error unmarshalling entry: %v", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -79,7 +79,11 @@ func (e *BasenineEntryStreamer) Get(ctx context.Context, socketId int, params *W
|
||||
go handleDataChannel(connection, data)
|
||||
go handleMetaChannel(connection, meta)
|
||||
|
||||
connection.Query(query, data, meta)
|
||||
if err = connection.Query(query, data, meta); err != nil {
|
||||
logger.Log.Errorf("Query mode call failed: %v", err)
|
||||
entryStreamerSocketConnector.CleanupSocket(socketId)
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
|
||||
@@ -123,22 +123,23 @@ func SendToSocket(socketId int, message []byte) error {
|
||||
return fmt.Errorf("socket %v is disconnected", socketId)
|
||||
}
|
||||
|
||||
var sent = false
|
||||
time.AfterFunc(time.Second*5, func() {
|
||||
if !sent {
|
||||
logger.Log.Error("socket timed out")
|
||||
socketCleanup(socketId, socketObj)
|
||||
}
|
||||
})
|
||||
|
||||
socketObj.lock.Lock() // gorilla socket panics from concurrent writes to a single socket
|
||||
err := socketObj.connection.WriteMessage(1, message)
|
||||
socketObj.lock.Unlock()
|
||||
sent = true
|
||||
defer socketObj.lock.Unlock()
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write message to socket %v, err: %w", socketId, err)
|
||||
if connectedWebsockets[socketId] == nil {
|
||||
return fmt.Errorf("socket %v is disconnected", socketId)
|
||||
}
|
||||
|
||||
if err := socketObj.connection.SetWriteDeadline(time.Now().Add(time.Second * 10)); err != nil {
|
||||
socketCleanup(socketId, socketObj)
|
||||
return fmt.Errorf("error setting timeout to socket %v, err: %v", socketId, err)
|
||||
}
|
||||
|
||||
if err := socketObj.connection.WriteMessage(websocket.TextMessage, message); err != nil {
|
||||
socketCleanup(socketId, socketObj)
|
||||
return fmt.Errorf("failed to write message to socket %v, err: %v", socketId, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ func ConfigureBasenineServer(host string, port string, dbSize int64, logLevel lo
|
||||
wait.WithProto("tcp"),
|
||||
wait.WithWait(200*time.Millisecond),
|
||||
wait.WithBreak(50*time.Millisecond),
|
||||
wait.WithDeadline(5*time.Second),
|
||||
wait.WithDeadline(20*time.Second),
|
||||
wait.WithDebug(logLevel == logging.DEBUG),
|
||||
).Do([]string{fmt.Sprintf("%s:%s", host, port)}) {
|
||||
logger.Log.Panicf("Basenine is not available!")
|
||||
|
||||
@@ -2,7 +2,6 @@ package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/up9inc/mizu/agent/pkg/dependency"
|
||||
"github.com/up9inc/mizu/agent/pkg/entries"
|
||||
@@ -69,7 +68,7 @@ func GetEntry(c *gin.Context) {
|
||||
c.JSON(http.StatusBadRequest, validationError)
|
||||
}
|
||||
|
||||
id, _ := strconv.Atoi(c.Param("id"))
|
||||
id := c.Param("id")
|
||||
|
||||
entriesProvider := dependency.GetInstance(dependency.EntriesProvider).(entries.EntriesProvider)
|
||||
entry, err := entriesProvider.GetEntry(singleEntryRequest, id)
|
||||
|
||||
@@ -58,12 +58,12 @@ func getRecorderAndContext() (*httptest.ResponseRecorder, *gin.Context) {
|
||||
receiveBuffer: bytes.NewBufferString("\n"),
|
||||
}
|
||||
dependency.RegisterGenerator(dependency.OasGeneratorDependency, func() interface{} {
|
||||
return oas.GetDefaultOasGeneratorInstance(dummyConn)
|
||||
return oas.GetDefaultOasGeneratorInstance()
|
||||
})
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(recorder)
|
||||
oas.GetDefaultOasGeneratorInstance(dummyConn).Start()
|
||||
oas.GetDefaultOasGeneratorInstance(dummyConn).GetServiceSpecs().Store("some", oas.NewGen("some"))
|
||||
oas.GetDefaultOasGeneratorInstance().Start(dummyConn)
|
||||
oas.GetDefaultOasGeneratorInstance().GetServiceSpecs().Store("some", oas.NewGen("some"))
|
||||
return recorder, c
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
core "k8s.io/api/core/v1"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -93,10 +94,6 @@ func GetGeneralStats(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, providers.GetGeneralStats())
|
||||
}
|
||||
|
||||
func GetRecentTLSLinks(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, providers.GetAllRecentTLSAddresses())
|
||||
}
|
||||
|
||||
func GetCurrentResolvingInformation(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, holder.GetResolver().GetMap())
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package entries
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
basenine "github.com/up9inc/basenine/client/go"
|
||||
@@ -15,7 +16,7 @@ import (
|
||||
|
||||
type EntriesProvider interface {
|
||||
GetEntries(entriesRequest *models.EntriesRequest) ([]*tapApi.EntryWrapper, *basenine.Metadata, error)
|
||||
GetEntry(singleEntryRequest *models.SingleEntryRequest, entryId int) (*tapApi.EntryWrapper, error)
|
||||
GetEntry(singleEntryRequest *models.SingleEntryRequest, entryId string) (*tapApi.EntryWrapper, error)
|
||||
}
|
||||
|
||||
type BasenineEntriesProvider struct{}
|
||||
@@ -56,7 +57,7 @@ func (e *BasenineEntriesProvider) GetEntries(entriesRequest *models.EntriesReque
|
||||
return dataSlice, metadata, nil
|
||||
}
|
||||
|
||||
func (e *BasenineEntriesProvider) GetEntry(singleEntryRequest *models.SingleEntryRequest, entryId int) (*tapApi.EntryWrapper, error) {
|
||||
func (e *BasenineEntriesProvider) GetEntry(singleEntryRequest *models.SingleEntryRequest, entryId string) (*tapApi.EntryWrapper, error) {
|
||||
var entry *tapApi.Entry
|
||||
bytes, err := basenine.Single(shared.BasenineHost, shared.BaseninePort, entryId, singleEntryRequest.Query)
|
||||
if err != nil {
|
||||
@@ -64,7 +65,7 @@ func (e *BasenineEntriesProvider) GetEntry(singleEntryRequest *models.SingleEntr
|
||||
}
|
||||
err = json.Unmarshal(bytes, &entry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.New(string(bytes))
|
||||
}
|
||||
|
||||
extension := app.ExtensionsMap[entry.Protocol.Name]
|
||||
|
||||
@@ -9,11 +9,10 @@ import (
|
||||
|
||||
basenine "github.com/up9inc/basenine/client/go"
|
||||
"github.com/up9inc/mizu/shared"
|
||||
"github.com/up9inc/mizu/tap"
|
||||
)
|
||||
|
||||
type EntriesRequest struct {
|
||||
LeftOff int `form:"leftOff" validate:"required,min=-1"`
|
||||
LeftOff string `form:"leftOff" validate:"required"`
|
||||
Direction int `form:"direction" validate:"required,oneof='1' '-1'"`
|
||||
Query string `form:"query"`
|
||||
Limit int `form:"limit" validate:"required,min=1"`
|
||||
@@ -44,11 +43,6 @@ type WebSocketTappedEntryMessage struct {
|
||||
Data *tapApi.OutputChannelItem
|
||||
}
|
||||
|
||||
type WebsocketOutboundLinkMessage struct {
|
||||
*shared.WebSocketMessageMetadata
|
||||
Data *tap.OutboundLink
|
||||
}
|
||||
|
||||
type AuthStatus struct {
|
||||
Email string `json:"email"`
|
||||
Model string `json:"model"`
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
@@ -124,13 +125,13 @@ func feedFromHAR(file string, isSync bool, gen *defaultOasGenerator) (uint, erro
|
||||
cnt := uint(0)
|
||||
for _, entry := range harDoc.Log.Entries {
|
||||
cnt += 1
|
||||
feedEntry(&entry, "", file, gen, cnt)
|
||||
feedEntry(&entry, "", file, gen, fmt.Sprintf("%024d", cnt))
|
||||
}
|
||||
|
||||
return cnt, nil
|
||||
}
|
||||
|
||||
func feedEntry(entry *har.Entry, source string, file string, gen *defaultOasGenerator, cnt uint) {
|
||||
func feedEntry(entry *har.Entry, source string, file string, gen *defaultOasGenerator, cnt string) {
|
||||
entry.Comment = file
|
||||
if entry.Response.Status == 302 {
|
||||
logger.Log.Debugf("Dropped traffic entry due to permanent redirect status: %s", entry.StartedDateTime)
|
||||
@@ -192,7 +193,7 @@ func feedFromLDJSON(file string, isSync bool, gen *defaultOasGenerator) (uint, e
|
||||
logger.Log.Warningf("Failed decoding entry: %s", line)
|
||||
} else {
|
||||
cnt += 1
|
||||
feedEntry(&entry, source, file, gen, cnt)
|
||||
feedEntry(&entry, source, file, gen, fmt.Sprintf("%024d", cnt))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
96
agent/pkg/oas/oas-samples/carts.json
Normal file
96
agent/pkg/oas/oas-samples/carts.json
Normal file
@@ -0,0 +1,96 @@
|
||||
{
|
||||
"openapi": "3.1.0",
|
||||
"info": {
|
||||
"title": "http://carts",
|
||||
"description": "Mizu observed 3 entries (0 failed), at 2.287 hits/s, average response time is 0.017 seconds",
|
||||
"version": "1.0"
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "http://carts"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/carts/{cartId}/items": {
|
||||
"get": {
|
||||
"summary": "/carts/{cartId}/items",
|
||||
"description": "Mizu observed 3 entries (0 failed), at 2.287 hits/s, average response time is 0.017 seconds",
|
||||
"operationId": "84c9b926-1f73-4ab4-b381-3c124528959f",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful call with status 200",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": [
|
||||
{
|
||||
"id": "60fe98fb86c0fc000869a90c",
|
||||
"itemId": "3395a43e-2d88-40de-b95f-e00e1502085b",
|
||||
"quantity": 1,
|
||||
"unitPrice": 18
|
||||
}
|
||||
],
|
||||
"x-sample-entry": "000000000000000000000010"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000010"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
"some-source": {
|
||||
"entries": 3,
|
||||
"failures": 0,
|
||||
"firstSeen": 1627298058.3798368,
|
||||
"lastSeen": 1627298065.2397773,
|
||||
"sumRT": 0.05,
|
||||
"sumDuration": 6.859940528869629
|
||||
}
|
||||
},
|
||||
"x-counters-total": {
|
||||
"entries": 3,
|
||||
"failures": 0,
|
||||
"firstSeen": 1627298058.3798368,
|
||||
"lastSeen": 1627298065.2397773,
|
||||
"sumRT": 0.05,
|
||||
"sumDuration": 6.859940528869629
|
||||
},
|
||||
"x-last-seen-ts": 1627298065.2397773,
|
||||
"x-sample-entry": "000000000000000000000010"
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "cartId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"style": "simple",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"examples": {
|
||||
"example #0": {
|
||||
"value": "mHK0P7zTktmV1zv57iWAvCTd43FFMHap"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000010"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
"some-source": {
|
||||
"entries": 3,
|
||||
"failures": 0,
|
||||
"firstSeen": 1627298058.3798368,
|
||||
"lastSeen": 1627298065.2397773,
|
||||
"sumRT": 0.05,
|
||||
"sumDuration": 6.859940528869629
|
||||
}
|
||||
},
|
||||
"x-counters-total": {
|
||||
"entries": 3,
|
||||
"failures": 0,
|
||||
"firstSeen": 1627298058.3798368,
|
||||
"lastSeen": 1627298065.2397773,
|
||||
"sumRT": 0.05,
|
||||
"sumDuration": 6.859940528869629
|
||||
}
|
||||
}
|
||||
485
agent/pkg/oas/oas-samples/catalogue.json
Normal file
485
agent/pkg/oas/oas-samples/catalogue.json
Normal file
@@ -0,0 +1,485 @@
|
||||
{
|
||||
"openapi": "3.1.0",
|
||||
"info": {
|
||||
"title": "Preloaded",
|
||||
"description": "Test file for loading pre-existing OAS",
|
||||
"version": "0.1"
|
||||
},
|
||||
"paths": {
|
||||
"/catalogue": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"catalogue"
|
||||
],
|
||||
"summary": "/catalogue",
|
||||
"description": "Mizu observed 3 entries (0 failed), at 2.647 hits/s, average response time is 0.008 seconds",
|
||||
"operationId": "dd6c3dbe-6b6b-4ddd-baed-757e237ddb8a",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "page",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"style": "form",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"examples": {
|
||||
"example #0": {
|
||||
"value": "1"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000002"
|
||||
},
|
||||
{
|
||||
"name": "size",
|
||||
"in": "query",
|
||||
"required": true,
|
||||
"style": "form",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"examples": {
|
||||
"example #0": {
|
||||
"value": "6"
|
||||
},
|
||||
"example #1": {
|
||||
"value": "3"
|
||||
},
|
||||
"example #2": {
|
||||
"value": "5"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000011"
|
||||
},
|
||||
{
|
||||
"name": "tags",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"style": "form",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"examples": {
|
||||
"example #0": {
|
||||
"value": ""
|
||||
},
|
||||
"example #1": {
|
||||
"value": "blue"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000007"
|
||||
},
|
||||
{
|
||||
"name": "sort",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"style": "form",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"examples": {
|
||||
"example #0": {
|
||||
"value": "id"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000007"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful call with status 200",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": [
|
||||
{
|
||||
"count": 1,
|
||||
"description": "Socks fit for a Messiah. You too can experience walking in water with these special edition beauties. Each hole is lovingly proggled to leave smooth edges. The only sock approved by a higher power.",
|
||||
"id": "03fef6ac-1896-4ce8-bd69-b798f85c6e0b",
|
||||
"imageUrl": [
|
||||
"/catalogue/images/holy_1.jpeg",
|
||||
"/catalogue/images/holy_2.jpeg"
|
||||
],
|
||||
"name": "Holy",
|
||||
"price": 99.99,
|
||||
"tag": [
|
||||
"action",
|
||||
"magic"
|
||||
]
|
||||
},
|
||||
{
|
||||
"count": 438,
|
||||
"description": "proident occaecat irure et excepteur labore minim nisi amet irure",
|
||||
"id": "3395a43e-2d88-40de-b95f-e00e1502085b",
|
||||
"imageUrl": [
|
||||
"/catalogue/images/colourful_socks.jpg",
|
||||
"/catalogue/images/colourful_socks.jpg"
|
||||
],
|
||||
"name": "Colourful",
|
||||
"price": 18,
|
||||
"tag": [
|
||||
"brown",
|
||||
"blue"
|
||||
]
|
||||
},
|
||||
{
|
||||
"count": 820,
|
||||
"description": "Ready for action. Engineers: be ready to smash that next bug! Be ready, with these super-action-sport-masterpieces. This particular engineer was chased away from the office with a stick.",
|
||||
"id": "510a0d7e-8e83-4193-b483-e27e09ddc34d",
|
||||
"imageUrl": [
|
||||
"/catalogue/images/puma_1.jpeg",
|
||||
"/catalogue/images/puma_2.jpeg"
|
||||
],
|
||||
"name": "SuperSport XL",
|
||||
"price": 15,
|
||||
"tag": [
|
||||
"sport",
|
||||
"formal",
|
||||
"black"
|
||||
]
|
||||
},
|
||||
{
|
||||
"count": 738,
|
||||
"description": "A mature sock, crossed, with an air of nonchalance.",
|
||||
"id": "808a2de1-1aaa-4c25-a9b9-6612e8f29a38",
|
||||
"imageUrl": [
|
||||
"/catalogue/images/cross_1.jpeg",
|
||||
"/catalogue/images/cross_2.jpeg"
|
||||
],
|
||||
"name": "Crossed",
|
||||
"price": 17.32,
|
||||
"tag": [
|
||||
"blue",
|
||||
"action",
|
||||
"red",
|
||||
"formal"
|
||||
]
|
||||
},
|
||||
{
|
||||
"count": 808,
|
||||
"description": "enim officia aliqua excepteur esse deserunt quis aliquip nostrud anim",
|
||||
"id": "819e1fbf-8b7e-4f6d-811f-693534916a8b",
|
||||
"imageUrl": [
|
||||
"/catalogue/images/WAT.jpg",
|
||||
"/catalogue/images/WAT2.jpg"
|
||||
],
|
||||
"name": "Figueroa",
|
||||
"price": 14,
|
||||
"tag": [
|
||||
"green",
|
||||
"formal",
|
||||
"blue"
|
||||
]
|
||||
},
|
||||
{
|
||||
"count": 175,
|
||||
"description": "consequat amet cupidatat minim laborum tempor elit ex consequat in",
|
||||
"id": "837ab141-399e-4c1f-9abc-bace40296bac",
|
||||
"imageUrl": [
|
||||
"/catalogue/images/catsocks.jpg",
|
||||
"/catalogue/images/catsocks2.jpg"
|
||||
],
|
||||
"name": "Cat socks",
|
||||
"price": 15,
|
||||
"tag": [
|
||||
"brown",
|
||||
"formal",
|
||||
"green"
|
||||
]
|
||||
}
|
||||
],
|
||||
"x-sample-entry": "000000000000000000000011"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000011"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
"some-source": {
|
||||
"entries": 3,
|
||||
"failures": 0,
|
||||
"firstSeen": 1627298057.7849188,
|
||||
"lastSeen": 1627298065.7258668,
|
||||
"sumRT": 0.024999999999999998,
|
||||
"sumDuration": 7.940948009490967
|
||||
}
|
||||
},
|
||||
"x-counters-total": {
|
||||
"entries": 3,
|
||||
"failures": 0,
|
||||
"firstSeen": 1627298057.7849188,
|
||||
"lastSeen": 1627298065.7258668,
|
||||
"sumRT": 0.024999999999999998,
|
||||
"sumDuration": 7.940948009490967
|
||||
},
|
||||
"x-last-seen-ts": 1627298065.7258668,
|
||||
"x-sample-entry": "000000000000000000000011"
|
||||
}
|
||||
},
|
||||
"/catalogue/size": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"catalogue"
|
||||
],
|
||||
"summary": "/catalogue/size",
|
||||
"description": "Mizu observed 1 entries (0 failed), at 0.000 hits/s, average response time is 0.013 seconds",
|
||||
"operationId": "2315e69d-9d66-48cf-b3d3-fec9c30bd28b",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "tags",
|
||||
"in": "query",
|
||||
"required": true,
|
||||
"style": "form",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"examples": {
|
||||
"example #0": {
|
||||
"value": ""
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000001"
|
||||
},
|
||||
{
|
||||
"name": "x-some",
|
||||
"in": "header",
|
||||
"required": true,
|
||||
"style": "simple",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"examples": {
|
||||
"example #0": {
|
||||
"value": "demo val"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000001"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful call with status 200",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": {
|
||||
"err": null,
|
||||
"size": 9
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000001"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000001"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
"some-source": {
|
||||
"entries": 1,
|
||||
"failures": 0,
|
||||
"firstSeen": 1627298057.7841518,
|
||||
"lastSeen": 1627298057.7841518,
|
||||
"sumRT": 0.013,
|
||||
"sumDuration": 0
|
||||
}
|
||||
},
|
||||
"x-counters-total": {
|
||||
"entries": 1,
|
||||
"failures": 0,
|
||||
"firstSeen": 1627298057.7841518,
|
||||
"lastSeen": 1627298057.7841518,
|
||||
"sumRT": 0.013,
|
||||
"sumDuration": 0
|
||||
},
|
||||
"x-last-seen-ts": 1627298057.7841518,
|
||||
"x-sample-entry": "000000000000000000000001"
|
||||
}
|
||||
},
|
||||
"/catalogue/{id}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"catalogue"
|
||||
],
|
||||
"summary": "/catalogue/{id}",
|
||||
"description": "Mizu observed 4 entries (0 failed), at 1.899 hits/s, average response time is 0.003 seconds",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "non-required-header",
|
||||
"in": "header",
|
||||
"required": false,
|
||||
"style": "simple",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": "some-uuid-maybe"
|
||||
},
|
||||
{
|
||||
"name": "x-some",
|
||||
"in": "header",
|
||||
"required": false,
|
||||
"style": "simple",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"examples": {
|
||||
"example #0": {
|
||||
"value": "demoval"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000004"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful call with status 200",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": {
|
||||
"count": 438,
|
||||
"description": "proident occaecat irure et excepteur labore minim nisi amet irure",
|
||||
"id": "3395a43e-2d88-40de-b95f-e00e1502085b",
|
||||
"imageUrl": [
|
||||
"/catalogue/images/colourful_socks.jpg",
|
||||
"/catalogue/images/colourful_socks.jpg"
|
||||
],
|
||||
"name": "Colourful",
|
||||
"price": 18,
|
||||
"tag": [
|
||||
"brown",
|
||||
"blue"
|
||||
]
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000012"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000012"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
"some-source": {
|
||||
"entries": 4,
|
||||
"failures": 0,
|
||||
"firstSeen": 1627298058.1315014,
|
||||
"lastSeen": 1627298065.7293031,
|
||||
"sumRT": 0.013999999999999999,
|
||||
"sumDuration": 7.597801685333252
|
||||
}
|
||||
},
|
||||
"x-counters-total": {
|
||||
"entries": 4,
|
||||
"failures": 0,
|
||||
"firstSeen": 1627298058.1315014,
|
||||
"lastSeen": 1627298065.7293031,
|
||||
"sumRT": 0.013999999999999999,
|
||||
"sumDuration": 7.597801685333252
|
||||
},
|
||||
"x-last-seen-ts": 1627298065.7293031,
|
||||
"x-sample-entry": "000000000000000000000012"
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"style": "simple",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"examples": {
|
||||
"example #0": {
|
||||
"value": "3395a43e-2d88-40de-b95f-e00e1502085b"
|
||||
},
|
||||
"example #1": {
|
||||
"value": "808a2de1-1aaa-4c25-a9b9-6612e8f29a38"
|
||||
}
|
||||
},
|
||||
"example": "some-uuid-maybe",
|
||||
"x-sample-entry": "000000000000000000000012"
|
||||
}
|
||||
]
|
||||
},
|
||||
"/catalogue/{id}/details": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"style": "simple",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": "some-uuid-maybe"
|
||||
}
|
||||
]
|
||||
},
|
||||
"/tags": {
|
||||
"get": {
|
||||
"summary": "/tags",
|
||||
"description": "Mizu observed 1 entries (0 failed), at 0.000 hits/s, average response time is 0.007 seconds",
|
||||
"operationId": "c4d7d0ed-1a78-4370-a049-efe3abc631a6",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful call with status 200",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": {
|
||||
"err": null,
|
||||
"tags": [
|
||||
"brown",
|
||||
"geek",
|
||||
"formal",
|
||||
"blue",
|
||||
"skin",
|
||||
"red",
|
||||
"action",
|
||||
"sport",
|
||||
"black",
|
||||
"magic",
|
||||
"green"
|
||||
]
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000003"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000003"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
"some-source": {
|
||||
"entries": 1,
|
||||
"failures": 0,
|
||||
"firstSeen": 1627298057.7841816,
|
||||
"lastSeen": 1627298057.7841816,
|
||||
"sumRT": 0.007,
|
||||
"sumDuration": 0
|
||||
}
|
||||
},
|
||||
"x-counters-total": {
|
||||
"entries": 1,
|
||||
"failures": 0,
|
||||
"firstSeen": 1627298057.7841816,
|
||||
"lastSeen": 1627298057.7841816,
|
||||
"sumRT": 0.007,
|
||||
"sumDuration": 0
|
||||
},
|
||||
"x-last-seen-ts": 1627298057.7841816,
|
||||
"x-sample-entry": "000000000000000000000003"
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
"some-source": {
|
||||
"entries": 9,
|
||||
"failures": 0,
|
||||
"firstSeen": 1627298057.7841518,
|
||||
"lastSeen": 1627298065.7293031,
|
||||
"sumRT": 0.05899999999999999,
|
||||
"sumDuration": 15.538749694824219
|
||||
}
|
||||
},
|
||||
"x-counters-total": {
|
||||
"entries": 9,
|
||||
"failures": 0,
|
||||
"firstSeen": 1627298057.7841518,
|
||||
"lastSeen": 1627298065.7293031,
|
||||
"sumRT": 0.05899999999999999,
|
||||
"sumDuration": 15.538749694824219
|
||||
}
|
||||
}
|
||||
897
agent/pkg/oas/oas-samples/httpbin.org.json
Normal file
897
agent/pkg/oas/oas-samples/httpbin.org.json
Normal file
@@ -0,0 +1,897 @@
|
||||
{
|
||||
"openapi": "3.1.0",
|
||||
"info": {
|
||||
"title": "https://httpbin.org",
|
||||
"description": "Mizu observed 19 entries (0 failed), at 0.106 hits/s, average response time is 0.172 seconds",
|
||||
"version": "1.0"
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://httpbin.org"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/appears-once": {
|
||||
"get": {
|
||||
"summary": "/appears-once",
|
||||
"description": "Mizu observed 1 entries (0 failed), at 0.000 hits/s, average response time is 0.630 seconds",
|
||||
"operationId": "2d34623e-fde8-4720-8390-9a7439051755",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful call with status 200",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": null,
|
||||
"x-sample-entry": "000000000000000000000004"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000004"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
"": {
|
||||
"entries": 1,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750580.0471218,
|
||||
"lastSeen": 1567750580.0471218,
|
||||
"sumRT": 0.63,
|
||||
"sumDuration": 0
|
||||
}
|
||||
},
|
||||
"x-counters-total": {
|
||||
"entries": 1,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750580.0471218,
|
||||
"lastSeen": 1567750580.0471218,
|
||||
"sumRT": 0.63,
|
||||
"sumDuration": 0
|
||||
},
|
||||
"x-last-seen-ts": 1567750580.0471218,
|
||||
"x-sample-entry": "000000000000000000000004"
|
||||
}
|
||||
},
|
||||
"/appears-twice": {
|
||||
"get": {
|
||||
"summary": "/appears-twice",
|
||||
"description": "Mizu observed 2 entries (0 failed), at 0.500 hits/s, average response time is 0.630 seconds",
|
||||
"operationId": "9c5330f3-8062-468b-b5a3-df1ad82b4846",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful call with status 200",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": null,
|
||||
"x-sample-entry": "000000000000000000000006"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000006"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
"": {
|
||||
"entries": 2,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750580.7471218,
|
||||
"lastSeen": 1567750581.7471218,
|
||||
"sumRT": 1.26,
|
||||
"sumDuration": 1
|
||||
}
|
||||
},
|
||||
"x-counters-total": {
|
||||
"entries": 2,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750580.7471218,
|
||||
"lastSeen": 1567750581.7471218,
|
||||
"sumRT": 1.26,
|
||||
"sumDuration": 1
|
||||
},
|
||||
"x-last-seen-ts": 1567750581.7471218,
|
||||
"x-sample-entry": "000000000000000000000006"
|
||||
}
|
||||
},
|
||||
"/body-optional": {
|
||||
"post": {
|
||||
"summary": "/body-optional",
|
||||
"description": "Mizu observed 3 entries (0 failed), at 0.003 hits/s, average response time is 0.001 seconds",
|
||||
"operationId": "34f3d66c-b1f7-4dca-9cab-987fcc8ae472",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful call with status 200",
|
||||
"content": {
|
||||
"": {
|
||||
"x-sample-entry": "000000000000000000000012"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000012"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
"": {
|
||||
"entries": 3,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750581.7471218,
|
||||
"lastSeen": 1567750581.757122,
|
||||
"sumRT": 0.003,
|
||||
"sumDuration": 0.010000228881835938
|
||||
}
|
||||
},
|
||||
"x-counters-total": {
|
||||
"entries": 3,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750581.7471218,
|
||||
"lastSeen": 1567750581.757122,
|
||||
"sumRT": 0.003,
|
||||
"sumDuration": 0.010000228881835938
|
||||
},
|
||||
"x-last-seen-ts": 1567750581.757122,
|
||||
"x-sample-entry": "000000000000000000000012",
|
||||
"requestBody": {
|
||||
"description": "Generic request body",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": "{\"key\", \"val\"}",
|
||||
"x-sample-entry": "000000000000000000000011"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000012"
|
||||
}
|
||||
}
|
||||
},
|
||||
"/body-required": {
|
||||
"post": {
|
||||
"summary": "/body-required",
|
||||
"description": "Mizu observed 1 entries (0 failed), at 0.000 hits/s, average response time is 0.001 seconds",
|
||||
"operationId": "ff6add53-ab1c-4d4e-b590-0835fa318276",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful call with status 200",
|
||||
"content": {
|
||||
"": {
|
||||
"x-sample-entry": "000000000000000000000013"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000013"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
"": {
|
||||
"entries": 1,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750581.757122,
|
||||
"lastSeen": 1567750581.757122,
|
||||
"sumRT": 0.001,
|
||||
"sumDuration": 0
|
||||
}
|
||||
},
|
||||
"x-counters-total": {
|
||||
"entries": 1,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750581.757122,
|
||||
"lastSeen": 1567750581.757122,
|
||||
"sumRT": 0.001,
|
||||
"sumDuration": 0
|
||||
},
|
||||
"x-last-seen-ts": 1567750581.757122,
|
||||
"x-sample-entry": "000000000000000000000013",
|
||||
"requestBody": {
|
||||
"description": "Generic request body",
|
||||
"content": {
|
||||
"": {
|
||||
"example": "body exists",
|
||||
"x-sample-entry": "000000000000000000000013"
|
||||
}
|
||||
},
|
||||
"required": true,
|
||||
"x-sample-entry": "000000000000000000000013"
|
||||
}
|
||||
}
|
||||
},
|
||||
"/form-multipart": {
|
||||
"post": {
|
||||
"summary": "/form-multipart",
|
||||
"description": "Mizu observed 1 entries (0 failed), at 0.000 hits/s, average response time is 0.001 seconds",
|
||||
"operationId": "153f0925-9fc7-4e9f-9d33-f1470f25f0f7",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful call with status 200",
|
||||
"content": {
|
||||
"": {
|
||||
"example": {},
|
||||
"x-sample-entry": "000000000000000000000009"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000009"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
"": {
|
||||
"entries": 1,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750582.7471218,
|
||||
"lastSeen": 1567750582.7471218,
|
||||
"sumRT": 0.001,
|
||||
"sumDuration": 0
|
||||
}
|
||||
},
|
||||
"x-counters-total": {
|
||||
"entries": 1,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750582.7471218,
|
||||
"lastSeen": 1567750582.7471218,
|
||||
"sumRT": 0.001,
|
||||
"sumDuration": 0
|
||||
},
|
||||
"x-last-seen-ts": 1567750582.7471218,
|
||||
"x-sample-entry": "000000000000000000000009",
|
||||
"requestBody": {
|
||||
"description": "Generic request body",
|
||||
"content": {
|
||||
"multipart/form-data": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"file",
|
||||
"path"
|
||||
],
|
||||
"properties": {
|
||||
"file": {
|
||||
"type": "string",
|
||||
"contentMediaType": "application/json",
|
||||
"examples": [
|
||||
"{\"functions\": 123}"
|
||||
]
|
||||
},
|
||||
"path": {
|
||||
"type": "string",
|
||||
"examples": [
|
||||
"/content/components"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"example": "--BOUNDARY\r\nContent-Disposition: form-data; name=\"file\"; filename=\"metadata.json\"\r\nContent-Type: application/json\r\n\r\n{\"functions\": 123}\r\n--BOUNDARY\r\nContent-Disposition: form-data; name=\"path\"\r\n\r\n/content/components\r\n--BOUNDARY--\r\n",
|
||||
"x-sample-entry": "000000000000000000000009"
|
||||
}
|
||||
},
|
||||
"required": true,
|
||||
"x-sample-entry": "000000000000000000000009"
|
||||
}
|
||||
}
|
||||
},
|
||||
"/form-urlencoded": {
|
||||
"post": {
|
||||
"summary": "/form-urlencoded",
|
||||
"description": "Mizu observed 2 entries (0 failed), at 0.500 hits/s, average response time is 0.001 seconds",
|
||||
"operationId": "c92189f5-5636-46eb-ac71-92b17941a568",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful call with status 200",
|
||||
"content": {
|
||||
"": {
|
||||
"x-sample-entry": "000000000000000000000008"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000008"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
"": {
|
||||
"entries": 2,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750580.7471218,
|
||||
"lastSeen": 1567750581.7471218,
|
||||
"sumRT": 0.002,
|
||||
"sumDuration": 1
|
||||
}
|
||||
},
|
||||
"x-counters-total": {
|
||||
"entries": 2,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750580.7471218,
|
||||
"lastSeen": 1567750581.7471218,
|
||||
"sumRT": 0.002,
|
||||
"sumDuration": 1
|
||||
},
|
||||
"x-last-seen-ts": 1567750581.7471218,
|
||||
"x-sample-entry": "000000000000000000000008",
|
||||
"requestBody": {
|
||||
"description": "Generic request body",
|
||||
"content": {
|
||||
"application/x-www-form-urlencoded": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"agent-id",
|
||||
"callback-url",
|
||||
"token"
|
||||
],
|
||||
"properties": {
|
||||
"agent-id": {
|
||||
"type": "string",
|
||||
"examples": [
|
||||
"ade"
|
||||
]
|
||||
},
|
||||
"callback-url": {
|
||||
"type": "string",
|
||||
"examples": [
|
||||
""
|
||||
]
|
||||
},
|
||||
"optional": {
|
||||
"type": "string",
|
||||
"examples": [
|
||||
"another"
|
||||
]
|
||||
},
|
||||
"token": {
|
||||
"type": "string",
|
||||
"examples": [
|
||||
"sometoken",
|
||||
"sometoken-second-val"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"example": "agent-id=ade\u0026callback-url=\u0026token=sometoken",
|
||||
"x-sample-entry": "000000000000000000000008"
|
||||
}
|
||||
},
|
||||
"required": true,
|
||||
"x-sample-entry": "000000000000000000000008"
|
||||
}
|
||||
}
|
||||
},
|
||||
"/param-patterns/prefix-gibberish-fine/{prefixgibberishfineId}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"param-patterns"
|
||||
],
|
||||
"summary": "/param-patterns/prefix-gibberish-fine/{prefixgibberishfineId}",
|
||||
"description": "Mizu observed 1 entries (0 failed), at 0.000 hits/s, average response time is 0.001 seconds",
|
||||
"operationId": "85270437-7aae-4a5b-b988-3662092463d0",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful call with status 200",
|
||||
"content": {
|
||||
"": {
|
||||
"x-sample-entry": "000000000000000000000014"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000014"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
"": {
|
||||
"entries": 1,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750582,
|
||||
"lastSeen": 1567750582,
|
||||
"sumRT": 0.001,
|
||||
"sumDuration": 0
|
||||
}
|
||||
},
|
||||
"x-counters-total": {
|
||||
"entries": 1,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750582,
|
||||
"lastSeen": 1567750582,
|
||||
"sumRT": 0.001,
|
||||
"sumDuration": 0
|
||||
},
|
||||
"x-last-seen-ts": 1567750582,
|
||||
"x-sample-entry": "000000000000000000000014"
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "prefixgibberishfineId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"style": "simple",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"examples": {
|
||||
"example #0": {
|
||||
"value": "234324"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000014"
|
||||
}
|
||||
]
|
||||
},
|
||||
"/param-patterns/{parampatternId}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"param-patterns"
|
||||
],
|
||||
"summary": "/param-patterns/{parampatternId}",
|
||||
"description": "Mizu observed 2 entries (0 failed), at 0.000 hits/s, average response time is 0.001 seconds",
|
||||
"operationId": "da597734-1cf5-4d3b-917b-6b02dacf7b7b",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful call with status 200",
|
||||
"content": {
|
||||
"": {
|
||||
"x-sample-entry": "000000000000000000000018"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000018"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
"": {
|
||||
"entries": 2,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750582.000003,
|
||||
"lastSeen": 1567750582.000004,
|
||||
"sumRT": 0.002,
|
||||
"sumDuration": 9.5367431640625e-7
|
||||
}
|
||||
},
|
||||
"x-counters-total": {
|
||||
"entries": 2,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750582.000003,
|
||||
"lastSeen": 1567750582.000004,
|
||||
"sumRT": 0.002,
|
||||
"sumDuration": 9.5367431640625e-7
|
||||
},
|
||||
"x-last-seen-ts": 1567750582.000004,
|
||||
"x-sample-entry": "000000000000000000000018"
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "parampatternId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"style": "simple",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"pattern": "^prefix-gibberish-.+"
|
||||
},
|
||||
"examples": {
|
||||
"example #0": {
|
||||
"value": "prefix-gibberish-sfdlasdfkadf87sd93284q24r"
|
||||
},
|
||||
"example #1": {
|
||||
"value": "prefix-gibberish-adslkfasdf89sa7dfasddafa8a98sd7kansdf"
|
||||
},
|
||||
"example #2": {
|
||||
"value": "prefix-gibberish-4jk5l2345h2452l4352435jlk45"
|
||||
},
|
||||
"example #3": {
|
||||
"value": "prefix-gibberish-84395h2j4k35hj243j5h2kl34h54k"
|
||||
},
|
||||
"example #4": {
|
||||
"value": "prefix-gibberish-afterwards"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000019"
|
||||
}
|
||||
]
|
||||
},
|
||||
"/param-patterns/{parampatternId}/1": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"param-patterns"
|
||||
],
|
||||
"summary": "/param-patterns/{parampatternId}/1",
|
||||
"description": "Mizu observed 1 entries (0 failed), at 0.000 hits/s, average response time is 0.001 seconds",
|
||||
"operationId": "e965a245-9cfc-48ed-94e1-f765eadb3960",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful call with status 200",
|
||||
"content": {
|
||||
"": {
|
||||
"x-sample-entry": "000000000000000000000015"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000015"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
"": {
|
||||
"entries": 1,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750582.000001,
|
||||
"lastSeen": 1567750582.000001,
|
||||
"sumRT": 0.001,
|
||||
"sumDuration": 0
|
||||
}
|
||||
},
|
||||
"x-counters-total": {
|
||||
"entries": 1,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750582.000001,
|
||||
"lastSeen": 1567750582.000001,
|
||||
"sumRT": 0.001,
|
||||
"sumDuration": 0
|
||||
},
|
||||
"x-last-seen-ts": 1567750582.000001,
|
||||
"x-sample-entry": "000000000000000000000015"
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "parampatternId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"style": "simple",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"pattern": "^prefix-gibberish-.+"
|
||||
},
|
||||
"examples": {
|
||||
"example #0": {
|
||||
"value": "prefix-gibberish-sfdlasdfkadf87sd93284q24r"
|
||||
},
|
||||
"example #1": {
|
||||
"value": "prefix-gibberish-adslkfasdf89sa7dfasddafa8a98sd7kansdf"
|
||||
},
|
||||
"example #2": {
|
||||
"value": "prefix-gibberish-4jk5l2345h2452l4352435jlk45"
|
||||
},
|
||||
"example #3": {
|
||||
"value": "prefix-gibberish-84395h2j4k35hj243j5h2kl34h54k"
|
||||
},
|
||||
"example #4": {
|
||||
"value": "prefix-gibberish-afterwards"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000019"
|
||||
}
|
||||
]
|
||||
},
|
||||
"/param-patterns/{parampatternId}/static": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"param-patterns"
|
||||
],
|
||||
"summary": "/param-patterns/{parampatternId}/static",
|
||||
"description": "Mizu observed 1 entries (0 failed), at 0.000 hits/s, average response time is 0.001 seconds",
|
||||
"operationId": "7af420dc-f8b7-450f-8f6f-18b039aa3cde",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful call with status 200",
|
||||
"content": {
|
||||
"": {
|
||||
"x-sample-entry": "000000000000000000000016"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000016"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
"": {
|
||||
"entries": 1,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750582.000002,
|
||||
"lastSeen": 1567750582.000002,
|
||||
"sumRT": 0.001,
|
||||
"sumDuration": 0
|
||||
}
|
||||
},
|
||||
"x-counters-total": {
|
||||
"entries": 1,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750582.000002,
|
||||
"lastSeen": 1567750582.000002,
|
||||
"sumRT": 0.001,
|
||||
"sumDuration": 0
|
||||
},
|
||||
"x-last-seen-ts": 1567750582.000002,
|
||||
"x-sample-entry": "000000000000000000000016"
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "parampatternId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"style": "simple",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"pattern": "^prefix-gibberish-.+"
|
||||
},
|
||||
"examples": {
|
||||
"example #0": {
|
||||
"value": "prefix-gibberish-sfdlasdfkadf87sd93284q24r"
|
||||
},
|
||||
"example #1": {
|
||||
"value": "prefix-gibberish-adslkfasdf89sa7dfasddafa8a98sd7kansdf"
|
||||
},
|
||||
"example #2": {
|
||||
"value": "prefix-gibberish-4jk5l2345h2452l4352435jlk45"
|
||||
},
|
||||
"example #3": {
|
||||
"value": "prefix-gibberish-84395h2j4k35hj243j5h2kl34h54k"
|
||||
},
|
||||
"example #4": {
|
||||
"value": "prefix-gibberish-afterwards"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000019"
|
||||
}
|
||||
]
|
||||
},
|
||||
"/param-patterns/{parampatternId}/{param1}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"param-patterns"
|
||||
],
|
||||
"summary": "/param-patterns/{parampatternId}/{param1}",
|
||||
"description": "Mizu observed 1 entries (0 failed), at 0.000 hits/s, average response time is 0.001 seconds",
|
||||
"operationId": "02a1771d-2d50-4a8c-8be2-29c7e59b8435",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful call with status 200",
|
||||
"content": {
|
||||
"": {
|
||||
"x-sample-entry": "000000000000000000000019"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000019"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
"": {
|
||||
"entries": 1,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750582.000002,
|
||||
"lastSeen": 1567750582.000002,
|
||||
"sumRT": 0.001,
|
||||
"sumDuration": 0
|
||||
}
|
||||
},
|
||||
"x-counters-total": {
|
||||
"entries": 1,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750582.000002,
|
||||
"lastSeen": 1567750582.000002,
|
||||
"sumRT": 0.001,
|
||||
"sumDuration": 0
|
||||
},
|
||||
"x-last-seen-ts": 1567750582.000002,
|
||||
"x-sample-entry": "000000000000000000000019"
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "param1",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"style": "simple",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"examples": {
|
||||
"example #0": {
|
||||
"value": "23421"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000019"
|
||||
},
|
||||
{
|
||||
"name": "parampatternId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"style": "simple",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"pattern": "^prefix-gibberish-.+"
|
||||
},
|
||||
"examples": {
|
||||
"example #0": {
|
||||
"value": "prefix-gibberish-sfdlasdfkadf87sd93284q24r"
|
||||
},
|
||||
"example #1": {
|
||||
"value": "prefix-gibberish-adslkfasdf89sa7dfasddafa8a98sd7kansdf"
|
||||
},
|
||||
"example #2": {
|
||||
"value": "prefix-gibberish-4jk5l2345h2452l4352435jlk45"
|
||||
},
|
||||
"example #3": {
|
||||
"value": "prefix-gibberish-84395h2j4k35hj243j5h2kl34h54k"
|
||||
},
|
||||
"example #4": {
|
||||
"value": "prefix-gibberish-afterwards"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000019"
|
||||
}
|
||||
]
|
||||
},
|
||||
"/{Id}": {
|
||||
"get": {
|
||||
"summary": "/{Id}",
|
||||
"description": "Mizu observed 1 entries (0 failed), at 0.000 hits/s, average response time is 0.630 seconds",
|
||||
"operationId": "77ec4910-d47a-46a5-8234-fb80a11034b4",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful call with status 200",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": null,
|
||||
"x-sample-entry": "000000000000000000000003"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000003"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
"": {
|
||||
"entries": 1,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750579.7471218,
|
||||
"lastSeen": 1567750579.7471218,
|
||||
"sumRT": 0.63,
|
||||
"sumDuration": 0
|
||||
}
|
||||
},
|
||||
"x-counters-total": {
|
||||
"entries": 1,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750579.7471218,
|
||||
"lastSeen": 1567750579.7471218,
|
||||
"sumRT": 0.63,
|
||||
"sumDuration": 0
|
||||
},
|
||||
"x-last-seen-ts": 1567750579.7471218,
|
||||
"x-sample-entry": "000000000000000000000003"
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"style": "simple",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"examples": {
|
||||
"example #0": {
|
||||
"value": "e21f7112-3d3b-4632-9da3-a4af2e0e9166"
|
||||
},
|
||||
"example #1": {
|
||||
"value": "952bea17-3776-11ea-9341-42010a84012a"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000003"
|
||||
}
|
||||
]
|
||||
},
|
||||
"/{Id}/sub1": {
|
||||
"get": {
|
||||
"summary": "/{Id}/sub1",
|
||||
"description": "Mizu observed 1 entries (0 failed), at 0.000 hits/s, average response time is 0.111 seconds",
|
||||
"operationId": "198675eb-9faf-407b-83fa-0483a730bbbe",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful call with status 200",
|
||||
"content": {
|
||||
"text/html": {
|
||||
"x-sample-entry": "000000000000000000000001"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000001"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
"": {
|
||||
"entries": 1,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750483.864529,
|
||||
"lastSeen": 1567750483.864529,
|
||||
"sumRT": 0.111,
|
||||
"sumDuration": 0
|
||||
}
|
||||
},
|
||||
"x-counters-total": {
|
||||
"entries": 1,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750483.864529,
|
||||
"lastSeen": 1567750483.864529,
|
||||
"sumRT": 0.111,
|
||||
"sumDuration": 0
|
||||
},
|
||||
"x-last-seen-ts": 1567750483.864529,
|
||||
"x-sample-entry": "000000000000000000000001"
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"style": "simple",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"examples": {
|
||||
"example #0": {
|
||||
"value": "e21f7112-3d3b-4632-9da3-a4af2e0e9166"
|
||||
},
|
||||
"example #1": {
|
||||
"value": "952bea17-3776-11ea-9341-42010a84012a"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000003"
|
||||
}
|
||||
]
|
||||
},
|
||||
"/{Id}/sub2": {
|
||||
"get": {
|
||||
"summary": "/{Id}/sub2",
|
||||
"description": "Mizu observed 1 entries (0 failed), at 0.000 hits/s, average response time is 0.630 seconds",
|
||||
"operationId": "31d880f1-152f-4dd6-84a7-463e13b694a5",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful call with status 200",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": null,
|
||||
"x-sample-entry": "000000000000000000000002"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000002"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
"": {
|
||||
"entries": 1,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750578.7471218,
|
||||
"lastSeen": 1567750578.7471218,
|
||||
"sumRT": 0.63,
|
||||
"sumDuration": 0
|
||||
}
|
||||
},
|
||||
"x-counters-total": {
|
||||
"entries": 1,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750578.7471218,
|
||||
"lastSeen": 1567750578.7471218,
|
||||
"sumRT": 0.63,
|
||||
"sumDuration": 0
|
||||
},
|
||||
"x-last-seen-ts": 1567750578.7471218,
|
||||
"x-sample-entry": "000000000000000000000002"
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"style": "simple",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"examples": {
|
||||
"example #0": {
|
||||
"value": "e21f7112-3d3b-4632-9da3-a4af2e0e9166"
|
||||
},
|
||||
"example #1": {
|
||||
"value": "952bea17-3776-11ea-9341-42010a84012a"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": "000000000000000000000003"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
"": {
|
||||
"entries": 19,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750483.864529,
|
||||
"lastSeen": 1567750582.7471218,
|
||||
"sumRT": 3.273999999999999,
|
||||
"sumDuration": 2.0100011825561523
|
||||
}
|
||||
},
|
||||
"x-counters-total": {
|
||||
"entries": 19,
|
||||
"failures": 0,
|
||||
"firstSeen": 1567750483.864529,
|
||||
"lastSeen": 1567750582.7471218,
|
||||
"sumRT": 3.273999999999999,
|
||||
"sumDuration": 2.0100011825561523
|
||||
}
|
||||
}
|
||||
1787
agent/pkg/oas/oas-samples/trcc-api-service.json
Normal file
1787
agent/pkg/oas/oas-samples/trcc-api-service.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -3,12 +3,13 @@ package oas
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"sync"
|
||||
|
||||
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"
|
||||
"sync"
|
||||
|
||||
"github.com/up9inc/mizu/shared/logger"
|
||||
)
|
||||
@@ -19,11 +20,11 @@ var (
|
||||
)
|
||||
|
||||
type OasGenerator interface {
|
||||
Start()
|
||||
Start(conn *basenine.Connection)
|
||||
Stop()
|
||||
IsStarted() bool
|
||||
Reset()
|
||||
GetServiceSpecs() *sync.Map
|
||||
SetEntriesQuery(query string) bool
|
||||
}
|
||||
|
||||
type defaultOasGenerator struct {
|
||||
@@ -32,33 +33,45 @@ type defaultOasGenerator struct {
|
||||
cancel context.CancelFunc
|
||||
serviceSpecs *sync.Map
|
||||
dbConn *basenine.Connection
|
||||
dbMutex sync.Mutex
|
||||
entriesQuery string
|
||||
}
|
||||
|
||||
func GetDefaultOasGeneratorInstance(conn *basenine.Connection) *defaultOasGenerator {
|
||||
func GetDefaultOasGeneratorInstance() *defaultOasGenerator {
|
||||
syncOnce.Do(func() {
|
||||
if conn == nil {
|
||||
c, err := basenine.NewConnection(shared.BasenineHost, shared.BaseninePort)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
conn = c
|
||||
}
|
||||
|
||||
instance = NewDefaultOasGenerator(conn)
|
||||
instance = NewDefaultOasGenerator()
|
||||
logger.Log.Debug("OAS Generator Initialized")
|
||||
})
|
||||
return instance
|
||||
}
|
||||
|
||||
func (g *defaultOasGenerator) Start() {
|
||||
func (g *defaultOasGenerator) Start(conn *basenine.Connection) {
|
||||
if g.started {
|
||||
return
|
||||
}
|
||||
|
||||
if g.dbConn == nil {
|
||||
if conn == nil {
|
||||
logger.Log.Infof("Creating new DB connection for OAS generator to address %s:%s", shared.BasenineHost, shared.BaseninePort)
|
||||
newConn, err := basenine.NewConnection(shared.BasenineHost, shared.BaseninePort)
|
||||
if err != nil {
|
||||
logger.Log.Error("Error connecting to DB for OAS generator, err: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
conn = newConn
|
||||
}
|
||||
|
||||
g.dbConn = conn
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
g.cancel = cancel
|
||||
g.ctx = ctx
|
||||
g.serviceSpecs = &sync.Map{}
|
||||
|
||||
g.started = true
|
||||
|
||||
go g.runGenerator()
|
||||
}
|
||||
|
||||
@@ -66,9 +79,17 @@ func (g *defaultOasGenerator) Stop() {
|
||||
if !g.started {
|
||||
return
|
||||
}
|
||||
g.cancel()
|
||||
g.Reset()
|
||||
g.started = false
|
||||
|
||||
g.cancel()
|
||||
g.reset()
|
||||
|
||||
g.dbMutex.Lock()
|
||||
defer g.dbMutex.Unlock()
|
||||
if g.dbConn != nil {
|
||||
g.dbConn.Close()
|
||||
g.dbConn = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (g *defaultOasGenerator) IsStarted() bool {
|
||||
@@ -76,16 +97,23 @@ func (g *defaultOasGenerator) IsStarted() bool {
|
||||
}
|
||||
|
||||
func (g *defaultOasGenerator) runGenerator() {
|
||||
// Make []byte channels to recieve the data and the meta
|
||||
// Make []byte channels to receive the data and the meta
|
||||
dataChan := make(chan []byte)
|
||||
metaChan := make(chan []byte)
|
||||
|
||||
g.dbConn.Query("", dataChan, metaChan)
|
||||
g.dbMutex.Lock()
|
||||
defer g.dbMutex.Unlock()
|
||||
logger.Log.Infof("Querying DB for OAS generator with query '%s'", g.entriesQuery)
|
||||
if err := g.dbConn.Query(g.entriesQuery, dataChan, metaChan); err != nil {
|
||||
logger.Log.Errorf("Query mode call failed: %v", err)
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-g.ctx.Done():
|
||||
logger.Log.Infof("OAS Generator was canceled")
|
||||
close(dataChan)
|
||||
close(metaChan)
|
||||
return
|
||||
|
||||
case metaBytes, ok := <-metaChan:
|
||||
@@ -114,17 +142,18 @@ func (g *defaultOasGenerator) runGenerator() {
|
||||
|
||||
func (g *defaultOasGenerator) handleEntry(mizuEntry *api.Entry) {
|
||||
if mizuEntry.Protocol.Name == "http" {
|
||||
dest := mizuEntry.Destination.Name
|
||||
if dest == "" {
|
||||
logger.Log.Debugf("OAS: Unresolved entry %d", mizuEntry.Id)
|
||||
return
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -153,7 +182,7 @@ func (g *defaultOasGenerator) handleHARWithSource(entryWSource *EntryWithSource)
|
||||
return
|
||||
}
|
||||
|
||||
logger.Log.Debugf("Handled entry %d as opId: %s", entryWSource.Id, opId) // TODO: set opId back to entry?
|
||||
logger.Log.Debugf("Handled entry %s as opId: %s", entryWSource.Id, opId) // TODO: set opId back to entry?
|
||||
}
|
||||
|
||||
func (g *defaultOasGenerator) getGen(dest string, urlStr string) *SpecGen {
|
||||
@@ -173,7 +202,7 @@ func (g *defaultOasGenerator) getGen(dest string, urlStr string) *SpecGen {
|
||||
return gen
|
||||
}
|
||||
|
||||
func (g *defaultOasGenerator) Reset() {
|
||||
func (g *defaultOasGenerator) reset() {
|
||||
g.serviceSpecs = &sync.Map{}
|
||||
}
|
||||
|
||||
@@ -181,12 +210,18 @@ func (g *defaultOasGenerator) GetServiceSpecs() *sync.Map {
|
||||
return g.serviceSpecs
|
||||
}
|
||||
|
||||
func NewDefaultOasGenerator(c *basenine.Connection) *defaultOasGenerator {
|
||||
func (g *defaultOasGenerator) SetEntriesQuery(query string) bool {
|
||||
changed := g.entriesQuery != query
|
||||
g.entriesQuery = query
|
||||
return changed
|
||||
}
|
||||
|
||||
func NewDefaultOasGenerator() *defaultOasGenerator {
|
||||
return &defaultOasGenerator{
|
||||
started: false,
|
||||
ctx: nil,
|
||||
cancel: nil,
|
||||
serviceSpecs: nil,
|
||||
dbConn: c,
|
||||
dbConn: nil,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,12 @@ package oas
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/up9inc/mizu/agent/pkg/har"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
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)
|
||||
@@ -21,6 +20,9 @@ func TestOASGen(t *testing.T) {
|
||||
Destination: "some",
|
||||
Entry: *e,
|
||||
}
|
||||
|
||||
dummyConn := GetFakeDBConn(`{"startedDateTime": "20000101","request": {"url": "https://host/path", "method": "GET"}, "response": {"status": 200}}`)
|
||||
gen.Start(dummyConn)
|
||||
gen.handleHARWithSource(ews)
|
||||
g, ok := gen.serviceSpecs.Load("some")
|
||||
if !ok {
|
||||
@@ -33,4 +35,12 @@ func TestOASGen(t *testing.T) {
|
||||
}
|
||||
specText, _ := json.Marshal(spec)
|
||||
t.Log(string(specText))
|
||||
|
||||
if !gen.IsStarted() {
|
||||
t.Errorf("Should be started")
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
gen.Stop()
|
||||
}
|
||||
|
||||
@@ -3,10 +3,6 @@ package oas
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/chanced/openapi"
|
||||
"github.com/google/uuid"
|
||||
"github.com/nav-inc/datetime"
|
||||
"github.com/up9inc/mizu/shared/logger"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime"
|
||||
@@ -18,6 +14,11 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/chanced/openapi"
|
||||
"github.com/google/uuid"
|
||||
"github.com/nav-inc/datetime"
|
||||
"github.com/up9inc/mizu/shared/logger"
|
||||
|
||||
"github.com/up9inc/mizu/agent/pkg/har"
|
||||
|
||||
"time"
|
||||
@@ -32,7 +33,7 @@ type EntryWithSource struct {
|
||||
Source string
|
||||
Destination string
|
||||
Entry har.Entry
|
||||
Id uint
|
||||
Id string
|
||||
}
|
||||
|
||||
type reqResp struct { // hello, generics in Go
|
||||
@@ -67,7 +68,7 @@ func (g *SpecGen) StartFromSpec(oas *openapi.OpenAPI) {
|
||||
g.tree = new(Node)
|
||||
for pathStr, pathObj := range oas.Paths.Items {
|
||||
pathSplit := strings.Split(string(pathStr), "/")
|
||||
g.tree.getOrSet(pathSplit, pathObj, 0)
|
||||
g.tree.getOrSet(pathSplit, pathObj, "")
|
||||
|
||||
// clean "last entry timestamp" markers from the past
|
||||
for _, pathAndOp := range g.tree.listOps() {
|
||||
@@ -341,7 +342,7 @@ func handleCounters(opObj *openapi.Operation, success bool, entryWithSource *Ent
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleRequest(req *har.Request, opObj *openapi.Operation, isSuccess bool, sampleId uint) error {
|
||||
func handleRequest(req *har.Request, opObj *openapi.Operation, isSuccess bool, sampleId string) error {
|
||||
// TODO: we don't handle the situation when header/qstr param can be defined on pathObj level. Also the path param defined on opObj
|
||||
urlParsed, err := url.Parse(req.URL)
|
||||
if err != nil {
|
||||
@@ -401,7 +402,7 @@ func handleRequest(req *har.Request, opObj *openapi.Operation, isSuccess bool, s
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleResponse(resp *har.Response, opObj *openapi.Operation, isSuccess bool, sampleId uint) error {
|
||||
func handleResponse(resp *har.Response, opObj *openapi.Operation, isSuccess bool, sampleId string) error {
|
||||
// TODO: we don't support "default" response
|
||||
respObj, err := getResponseObj(resp, opObj, isSuccess)
|
||||
if err != nil {
|
||||
@@ -422,7 +423,7 @@ func handleResponse(resp *har.Response, opObj *openapi.Operation, isSuccess bool
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleRespHeaders(reqHeaders []har.Header, respObj *openapi.ResponseObj, sampleId uint) {
|
||||
func handleRespHeaders(reqHeaders []har.Header, respObj *openapi.ResponseObj, sampleId string) {
|
||||
visited := map[string]*openapi.HeaderObj{}
|
||||
for _, pair := range reqHeaders {
|
||||
if isHeaderIgnored(pair.Name) {
|
||||
@@ -466,7 +467,7 @@ func handleRespHeaders(reqHeaders []har.Header, respObj *openapi.ResponseObj, sa
|
||||
}
|
||||
}
|
||||
|
||||
func fillContent(reqResp reqResp, respContent openapi.Content, ctype string, sampleId uint) (*openapi.MediaType, error) {
|
||||
func fillContent(reqResp reqResp, respContent openapi.Content, ctype string, sampleId string) (*openapi.MediaType, error) {
|
||||
content, found := respContent[ctype]
|
||||
if !found {
|
||||
respContent[ctype] = &openapi.MediaType{}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package oas
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
@@ -11,13 +13,22 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/chanced/openapi"
|
||||
"github.com/op/go-logging"
|
||||
"github.com/up9inc/mizu/shared/logger"
|
||||
"github.com/wI2L/jsondiff"
|
||||
|
||||
basenine "github.com/up9inc/basenine/client/go"
|
||||
"github.com/up9inc/mizu/agent/pkg/har"
|
||||
)
|
||||
|
||||
func GetFakeDBConn(send string) *basenine.Connection {
|
||||
dummyConn := new(basenine.Connection)
|
||||
dummyConn.Conn = FakeConn{
|
||||
sendBuffer: bytes.NewBufferString(send),
|
||||
receiveBuffer: bytes.NewBufferString(""),
|
||||
}
|
||||
return dummyConn
|
||||
}
|
||||
|
||||
// if started via env, write file into subdir
|
||||
func outputSpec(label string, spec *openapi.OpenAPI, t *testing.T) string {
|
||||
content, err := json.MarshalIndent(spec, "", " ")
|
||||
@@ -43,14 +54,14 @@ func outputSpec(label string, spec *openapi.OpenAPI, t *testing.T) string {
|
||||
}
|
||||
|
||||
func TestEntries(t *testing.T) {
|
||||
logger.InitLoggerStd(logging.INFO)
|
||||
//logger.InitLoggerStd(logging.INFO) causes race condition
|
||||
files, err := getFiles("./test_artifacts/")
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
gen := NewDefaultOasGenerator(nil)
|
||||
gen := NewDefaultOasGenerator()
|
||||
gen.serviceSpecs = new(sync.Map)
|
||||
loadStartingOAS("test_artifacts/catalogue.json", "catalogue", gen.serviceSpecs)
|
||||
loadStartingOAS("test_artifacts/trcc.json", "trcc-api-service", gen.serviceSpecs)
|
||||
@@ -124,7 +135,7 @@ func TestEntries(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFileSingle(t *testing.T) {
|
||||
gen := NewDefaultOasGenerator(nil)
|
||||
gen := NewDefaultOasGenerator()
|
||||
gen.serviceSpecs = new(sync.Map)
|
||||
// loadStartingOAS()
|
||||
file := "test_artifacts/params.har"
|
||||
@@ -214,7 +225,7 @@ func loadStartingOAS(file string, label string, specs *sync.Map) {
|
||||
}
|
||||
|
||||
func TestEntriesNegative(t *testing.T) {
|
||||
gen := NewDefaultOasGenerator(nil)
|
||||
gen := NewDefaultOasGenerator()
|
||||
gen.serviceSpecs = new(sync.Map)
|
||||
files := []string{"invalid"}
|
||||
_, err := feedEntries(files, false, gen)
|
||||
@@ -225,7 +236,7 @@ func TestEntriesNegative(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEntriesPositive(t *testing.T) {
|
||||
gen := NewDefaultOasGenerator(nil)
|
||||
gen := NewDefaultOasGenerator()
|
||||
gen.serviceSpecs = new(sync.Map)
|
||||
files := []string{"test_artifacts/params.har"}
|
||||
_, err := feedEntries(files, false, gen)
|
||||
@@ -267,3 +278,17 @@ func TestLoadValid3_1(t *testing.T) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
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 }
|
||||
|
||||
@@ -22,10 +22,10 @@
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": null,
|
||||
"x-sample-entry": 4
|
||||
"x-sample-entry": "000000000000000000000004"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": 4
|
||||
"x-sample-entry": "000000000000000000000004"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
@@ -47,7 +47,7 @@
|
||||
"sumDuration": 0
|
||||
},
|
||||
"x-last-seen-ts": 1567750580.04,
|
||||
"x-sample-entry": 4
|
||||
"x-sample-entry": "000000000000000000000004"
|
||||
}
|
||||
},
|
||||
"/appears-twice": {
|
||||
@@ -61,10 +61,10 @@
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": null,
|
||||
"x-sample-entry": 6
|
||||
"x-sample-entry": "000000000000000000000006"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": 6
|
||||
"x-sample-entry": "000000000000000000000006"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
@@ -86,7 +86,7 @@
|
||||
"sumDuration": 1
|
||||
},
|
||||
"x-last-seen-ts": 1567750581.74,
|
||||
"x-sample-entry": 6
|
||||
"x-sample-entry": "000000000000000000000006"
|
||||
}
|
||||
},
|
||||
"/body-optional": {
|
||||
@@ -99,10 +99,10 @@
|
||||
"description": "Successful call with status 200",
|
||||
"content": {
|
||||
"": {
|
||||
"x-sample-entry": 12
|
||||
"x-sample-entry": "000000000000000000000012"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": 12
|
||||
"x-sample-entry": "000000000000000000000012"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
@@ -124,16 +124,16 @@
|
||||
"sumDuration": 0.01
|
||||
},
|
||||
"x-last-seen-ts": 1567750581.75,
|
||||
"x-sample-entry": 12,
|
||||
"x-sample-entry": "000000000000000000000012",
|
||||
"requestBody": {
|
||||
"description": "Generic request body",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": "{\"key\", \"val\"}",
|
||||
"x-sample-entry": 11
|
||||
"x-sample-entry": "000000000000000000000011"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": 12
|
||||
"x-sample-entry": "000000000000000000000012"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -147,10 +147,10 @@
|
||||
"description": "Successful call with status 200",
|
||||
"content": {
|
||||
"": {
|
||||
"x-sample-entry": 13
|
||||
"x-sample-entry": "000000000000000000000013"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": 13
|
||||
"x-sample-entry": "000000000000000000000013"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
@@ -172,17 +172,17 @@
|
||||
"sumDuration": 0
|
||||
},
|
||||
"x-last-seen-ts": 1567750581.75,
|
||||
"x-sample-entry": 13,
|
||||
"x-sample-entry": "000000000000000000000013",
|
||||
"requestBody": {
|
||||
"description": "Generic request body",
|
||||
"content": {
|
||||
"": {
|
||||
"example": "body exists",
|
||||
"x-sample-entry": 13
|
||||
"x-sample-entry": "000000000000000000000013"
|
||||
}
|
||||
},
|
||||
"required": true,
|
||||
"x-sample-entry": 13
|
||||
"x-sample-entry": "000000000000000000000013"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -197,10 +197,10 @@
|
||||
"content": {
|
||||
"": {
|
||||
"example": {},
|
||||
"x-sample-entry": 9
|
||||
"x-sample-entry": "000000000000000000000009"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": 9
|
||||
"x-sample-entry": "000000000000000000000009"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
@@ -222,7 +222,7 @@
|
||||
"sumDuration": 0
|
||||
},
|
||||
"x-last-seen-ts": 1567750582.74,
|
||||
"x-sample-entry": 9,
|
||||
"x-sample-entry": "000000000000000000000009",
|
||||
"requestBody": {
|
||||
"description": "Generic request body",
|
||||
"content": {
|
||||
@@ -250,11 +250,11 @@
|
||||
}
|
||||
},
|
||||
"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
|
||||
"x-sample-entry": "000000000000000000000009"
|
||||
}
|
||||
},
|
||||
"required": true,
|
||||
"x-sample-entry": 9
|
||||
"x-sample-entry": "000000000000000000000009"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -268,10 +268,10 @@
|
||||
"description": "Successful call with status 200",
|
||||
"content": {
|
||||
"": {
|
||||
"x-sample-entry": 8
|
||||
"x-sample-entry": "000000000000000000000008"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": 8
|
||||
"x-sample-entry": "000000000000000000000008"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
@@ -293,7 +293,7 @@
|
||||
"sumDuration": 1
|
||||
},
|
||||
"x-last-seen-ts": 1567750581.74,
|
||||
"x-sample-entry": 8,
|
||||
"x-sample-entry": "000000000000000000000008",
|
||||
"requestBody": {
|
||||
"description": "Generic request body",
|
||||
"content": {
|
||||
@@ -334,11 +334,11 @@
|
||||
}
|
||||
},
|
||||
"example": "agent-id=ade\u0026callback-url=\u0026token=sometoken",
|
||||
"x-sample-entry": 8
|
||||
"x-sample-entry": "000000000000000000000008"
|
||||
}
|
||||
},
|
||||
"required": true,
|
||||
"x-sample-entry": 8
|
||||
"x-sample-entry": "000000000000000000000008"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -355,10 +355,10 @@
|
||||
"description": "Successful call with status 200",
|
||||
"content": {
|
||||
"": {
|
||||
"x-sample-entry": 14
|
||||
"x-sample-entry": "000000000000000000000014"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": 14
|
||||
"x-sample-entry": "000000000000000000000014"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
@@ -380,7 +380,7 @@
|
||||
"sumDuration": 0
|
||||
},
|
||||
"x-last-seen-ts": 1567750582,
|
||||
"x-sample-entry": 14
|
||||
"x-sample-entry": "000000000000000000000014"
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
@@ -396,7 +396,7 @@
|
||||
"value": "234324"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": 14
|
||||
"x-sample-entry": "000000000000000000000014"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -413,10 +413,10 @@
|
||||
"description": "Successful call with status 200",
|
||||
"content": {
|
||||
"": {
|
||||
"x-sample-entry": 18
|
||||
"x-sample-entry": "000000000000000000000018"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": 18
|
||||
"x-sample-entry": "000000000000000000000018"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
@@ -438,7 +438,7 @@
|
||||
"sumDuration": 9.53e-7
|
||||
},
|
||||
"x-last-seen-ts": 1567750582.00,
|
||||
"x-sample-entry": 18
|
||||
"x-sample-entry": "000000000000000000000018"
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
@@ -467,7 +467,7 @@
|
||||
"value": "prefix-gibberish-afterwards"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": 19
|
||||
"x-sample-entry": "000000000000000000000019"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -484,10 +484,10 @@
|
||||
"description": "Successful call with status 200",
|
||||
"content": {
|
||||
"": {
|
||||
"x-sample-entry": 15
|
||||
"x-sample-entry": "000000000000000000000015"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": 15
|
||||
"x-sample-entry": "000000000000000000000015"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
@@ -509,7 +509,7 @@
|
||||
"sumDuration": 0
|
||||
},
|
||||
"x-last-seen-ts": 1567750582.00,
|
||||
"x-sample-entry": 15
|
||||
"x-sample-entry": "000000000000000000000015"
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
@@ -538,7 +538,7 @@
|
||||
"value": "prefix-gibberish-afterwards"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": 19
|
||||
"x-sample-entry": "000000000000000000000019"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -555,10 +555,10 @@
|
||||
"description": "Successful call with status 200",
|
||||
"content": {
|
||||
"": {
|
||||
"x-sample-entry": 16
|
||||
"x-sample-entry": "000000000000000000000016"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": 16
|
||||
"x-sample-entry": "000000000000000000000016"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
@@ -580,7 +580,7 @@
|
||||
"sumDuration": 0
|
||||
},
|
||||
"x-last-seen-ts": 1567750582.00,
|
||||
"x-sample-entry": 16
|
||||
"x-sample-entry": "000000000000000000000016"
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
@@ -609,7 +609,7 @@
|
||||
"value": "prefix-gibberish-afterwards"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": 19
|
||||
"x-sample-entry": "000000000000000000000019"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -626,10 +626,10 @@
|
||||
"description": "Successful call with status 200",
|
||||
"content": {
|
||||
"": {
|
||||
"x-sample-entry": 19
|
||||
"x-sample-entry": "000000000000000000000019"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": 19
|
||||
"x-sample-entry": "000000000000000000000019"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
@@ -651,7 +651,7 @@
|
||||
"sumDuration": 0
|
||||
},
|
||||
"x-last-seen-ts": 1567750582.00,
|
||||
"x-sample-entry": 19
|
||||
"x-sample-entry": "000000000000000000000019"
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
@@ -667,7 +667,7 @@
|
||||
"value": "23421"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": 19
|
||||
"x-sample-entry": "000000000000000000000019"
|
||||
},
|
||||
{
|
||||
"name": "parampatternId",
|
||||
@@ -695,7 +695,7 @@
|
||||
"value": "prefix-gibberish-afterwards"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": 19
|
||||
"x-sample-entry": "000000000000000000000019"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -710,10 +710,10 @@
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": null,
|
||||
"x-sample-entry": 3
|
||||
"x-sample-entry": "000000000000000000000003"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": 3
|
||||
"x-sample-entry": "000000000000000000000003"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
@@ -735,7 +735,7 @@
|
||||
"sumDuration": 0
|
||||
},
|
||||
"x-last-seen-ts": 1567750579.74,
|
||||
"x-sample-entry": 3
|
||||
"x-sample-entry": "000000000000000000000003"
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
@@ -754,7 +754,7 @@
|
||||
"value": "<UUID4>"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": 3
|
||||
"x-sample-entry": "000000000000000000000003"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -768,10 +768,10 @@
|
||||
"description": "Successful call with status 200",
|
||||
"content": {
|
||||
"text/html": {
|
||||
"x-sample-entry": 1
|
||||
"x-sample-entry": "000000000000000000000001"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": 1
|
||||
"x-sample-entry": "000000000000000000000001"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
@@ -793,7 +793,7 @@
|
||||
"sumDuration": 0
|
||||
},
|
||||
"x-last-seen-ts": 1567750483.86,
|
||||
"x-sample-entry": 1
|
||||
"x-sample-entry": "000000000000000000000001"
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
@@ -812,7 +812,7 @@
|
||||
"value": "<UUID4>"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": 3
|
||||
"x-sample-entry": "000000000000000000000003"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -827,10 +827,10 @@
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": null,
|
||||
"x-sample-entry": 2
|
||||
"x-sample-entry": "000000000000000000000002"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": 2
|
||||
"x-sample-entry": "000000000000000000000002"
|
||||
}
|
||||
},
|
||||
"x-counters-per-source": {
|
||||
@@ -852,7 +852,7 @@
|
||||
"sumDuration": 0
|
||||
},
|
||||
"x-last-seen-ts": 1567750578.74,
|
||||
"x-sample-entry": 2
|
||||
"x-sample-entry": "000000000000000000000002"
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
@@ -871,7 +871,7 @@
|
||||
"value": "<UUID4>"
|
||||
}
|
||||
},
|
||||
"x-sample-entry": 3
|
||||
"x-sample-entry": "000000000000000000000003"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2,12 +2,13 @@ package oas
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/chanced/openapi"
|
||||
"github.com/up9inc/mizu/shared/logger"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/chanced/openapi"
|
||||
"github.com/up9inc/mizu/shared/logger"
|
||||
)
|
||||
|
||||
type NodePath = []string
|
||||
@@ -20,7 +21,7 @@ type Node struct {
|
||||
children []*Node
|
||||
}
|
||||
|
||||
func (n *Node) getOrSet(path NodePath, existingPathObj *openapi.PathObj, sampleId uint) (node *Node) {
|
||||
func (n *Node) getOrSet(path NodePath, existingPathObj *openapi.PathObj, sampleId string) (node *Node) {
|
||||
if existingPathObj == nil {
|
||||
panic("Invalid function call")
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package oas
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -23,7 +24,7 @@ func TestTree(t *testing.T) {
|
||||
for i, tc := range testCases {
|
||||
split := strings.Split(tc.inp, "/")
|
||||
pathObj := new(openapi.PathObj)
|
||||
node := tree.getOrSet(split, pathObj, uint(i))
|
||||
node := tree.getOrSet(split, pathObj, fmt.Sprintf("%024d", i))
|
||||
|
||||
fillPathParams(node, pathObj)
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ type nvParams struct {
|
||||
GeneralizeName func(name string) string
|
||||
}
|
||||
|
||||
func handleNameVals(gw nvParams, params **openapi.ParameterList, checkIgnore bool, sampleId uint) {
|
||||
func handleNameVals(gw nvParams, params **openapi.ParameterList, checkIgnore bool, sampleId string) {
|
||||
visited := map[string]*openapi.ParameterObj{}
|
||||
for _, pair := range gw.Pairs {
|
||||
if (checkIgnore && gw.IsIgnored(pair.Name)) || pair.Name == "" {
|
||||
@@ -477,8 +477,8 @@ func intersectSliceWithMap(required []string, names map[string]struct{}) []strin
|
||||
return required
|
||||
}
|
||||
|
||||
func setSampleID(extensions *openapi.Extensions, id uint) {
|
||||
if id > 0 {
|
||||
func setSampleID(extensions *openapi.Extensions, id string) {
|
||||
if id != "" {
|
||||
if *extensions == nil {
|
||||
*extensions = openapi.Extensions{}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ func TestAnyJSON(t *testing.T) {
|
||||
} else if tc.inp == "null" && any != nil {
|
||||
t.Errorf("null has to parse as nil (but got %s)", any)
|
||||
} else {
|
||||
t.Logf("%s => %s", tc.inp, any)
|
||||
t.Logf("%s => %v", tc.inp, any)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,19 +4,13 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/patrickmn/go-cache"
|
||||
"github.com/up9inc/mizu/agent/pkg/models"
|
||||
"github.com/up9inc/mizu/shared"
|
||||
"github.com/up9inc/mizu/tap"
|
||||
)
|
||||
|
||||
const tlsLinkRetainmentTime = time.Minute * 15
|
||||
|
||||
var (
|
||||
authStatus *models.AuthStatus
|
||||
RecentTLSLinks = cache.New(tlsLinkRetainmentTime, tlsLinkRetainmentTime)
|
||||
authStatus *models.AuthStatus
|
||||
)
|
||||
|
||||
func GetAuthStatus() (*models.AuthStatus, error) {
|
||||
@@ -51,16 +45,3 @@ func GetAuthStatus() (*models.AuthStatus, error) {
|
||||
|
||||
return authStatus, nil
|
||||
}
|
||||
|
||||
func GetAllRecentTLSAddresses() []string {
|
||||
recentTLSLinks := make([]string, 0)
|
||||
|
||||
for _, outboundLinkItem := range RecentTLSLinks.Items() {
|
||||
outboundLink, castOk := outboundLinkItem.Object.(*tap.OutboundLink)
|
||||
if castOk {
|
||||
recentTLSLinks = append(recentTLSLinks, outboundLink.DstIP)
|
||||
}
|
||||
}
|
||||
|
||||
return recentTLSLinks
|
||||
}
|
||||
|
||||
@@ -21,7 +21,5 @@ func StatusRoutes(ginApp *gin.Engine) {
|
||||
|
||||
routeGroup.GET("/general", controllers.GetGeneralStats) // get general stats about entries in DB
|
||||
|
||||
routeGroup.GET("/recentTLSLinks", controllers.GetRecentTLSLinks)
|
||||
|
||||
routeGroup.GET("/resolving", controllers.GetCurrentResolvingInformation)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package servicemap
|
||||
|
||||
import (
|
||||
"github.com/jinzhu/copier"
|
||||
"sync"
|
||||
|
||||
"github.com/up9inc/mizu/shared/logger"
|
||||
@@ -183,8 +184,12 @@ func (s *defaultServiceMap) NewTCPEntry(src *tapApi.TCP, dst *tapApi.TCP, p *tap
|
||||
if len(src.Name) == 0 {
|
||||
srcEntry = &entryData{
|
||||
key: key(src.IP),
|
||||
entry: src,
|
||||
entry: &tapApi.TCP{},
|
||||
}
|
||||
if err := copier.Copy(srcEntry.entry, src); err != nil {
|
||||
logger.Log.Errorf("Error while copying src entry into src entry data")
|
||||
}
|
||||
|
||||
srcEntry.entry.Name = UnresolvedNodeName
|
||||
} else {
|
||||
srcEntry = &entryData{
|
||||
@@ -196,8 +201,12 @@ func (s *defaultServiceMap) NewTCPEntry(src *tapApi.TCP, dst *tapApi.TCP, p *tap
|
||||
if len(dst.Name) == 0 {
|
||||
dstEntry = &entryData{
|
||||
key: key(dst.IP),
|
||||
entry: dst,
|
||||
entry: &tapApi.TCP{},
|
||||
}
|
||||
if err := copier.Copy(dstEntry.entry, dst); err != nil {
|
||||
logger.Log.Errorf("Error while copying dst entry into dst entry data")
|
||||
}
|
||||
|
||||
dstEntry.entry.Name = UnresolvedNodeName
|
||||
} else {
|
||||
dstEntry = &entryData{
|
||||
@@ -224,7 +233,8 @@ func (s *defaultServiceMap) GetStatus() ServiceMapStatus {
|
||||
}
|
||||
|
||||
func (s *defaultServiceMap) GetNodes() []ServiceMapNode {
|
||||
var nodes []ServiceMapNode
|
||||
nodes := []ServiceMapNode{}
|
||||
|
||||
for i, n := range s.graph.Nodes {
|
||||
nodes = append(nodes, ServiceMapNode{
|
||||
Id: n.id,
|
||||
@@ -234,11 +244,13 @@ func (s *defaultServiceMap) GetNodes() []ServiceMapNode {
|
||||
Count: n.count,
|
||||
})
|
||||
}
|
||||
|
||||
return nodes
|
||||
}
|
||||
|
||||
func (s *defaultServiceMap) GetEdges() []ServiceMapEdge {
|
||||
var edges []ServiceMapEdge
|
||||
edges := []ServiceMapEdge{}
|
||||
|
||||
for u, m := range s.graph.Edges {
|
||||
for v := range m {
|
||||
for _, p := range s.graph.Edges[u][v].data {
|
||||
@@ -263,6 +275,7 @@ func (s *defaultServiceMap) GetEdges() []ServiceMapEdge {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return edges
|
||||
}
|
||||
|
||||
|
||||
@@ -403,10 +403,10 @@ func (s *ServiceMapEnabledSuite) TestServiceMap() {
|
||||
assert.Equal(0, status.EdgeCount)
|
||||
|
||||
// Nodes after reset
|
||||
assert.Equal([]ServiceMapNode(nil), nodes)
|
||||
assert.Equal([]ServiceMapNode{}, nodes)
|
||||
|
||||
// Edges after reset
|
||||
assert.Equal([]ServiceMapEdge(nil), edges)
|
||||
assert.Equal([]ServiceMapEdge{}, edges)
|
||||
}
|
||||
|
||||
func TestServiceMapSuite(t *testing.T) {
|
||||
|
||||
@@ -211,11 +211,15 @@ func syncEntriesImpl(token string, model string, envPrefix string, uploadInterva
|
||||
|
||||
logger.Log.Infof("Getting entries from the database")
|
||||
|
||||
BasenineReconnect:
|
||||
var connection *basenine.Connection
|
||||
var err error
|
||||
connection, err = basenine.NewConnection(shared.BasenineHost, shared.BaseninePort)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
logger.Log.Errorf("Can't establish a new connection to Basenine server: %v", err)
|
||||
connection.Close()
|
||||
time.Sleep(shared.BasenineReconnectInterval * time.Second)
|
||||
goto BasenineReconnect
|
||||
}
|
||||
|
||||
data := make(chan []byte)
|
||||
@@ -323,7 +327,12 @@ func syncEntriesImpl(token string, model string, envPrefix string, uploadInterva
|
||||
go handleMetaChannel(&wg, connection, meta)
|
||||
wg.Add(2)
|
||||
|
||||
connection.Query(query, data, meta)
|
||||
if err = connection.Query(query, data, meta); err != nil {
|
||||
logger.Log.Errorf("Query mode call failed: %v", err)
|
||||
connection.Close()
|
||||
time.Sleep(shared.BasenineReconnectInterval * time.Second)
|
||||
goto BasenineReconnect
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
20
cli/Makefile
20
cli/Makefile
@@ -36,16 +36,16 @@ build-base: ## Build mizu CLI binary (select platform via GOOS / GOARCH env vari
|
||||
(cd bin && shasum -a 256 mizu_${SUFFIX} > mizu_${SUFFIX}.sha256)
|
||||
|
||||
build-all: ## Build for all supported platforms.
|
||||
@echo "Compiling for every OS and Platform"
|
||||
@mkdir -p bin && sed s/_VER_/$(VER)/g README.md.TEMPLATE > bin/README.md
|
||||
@$(MAKE) build GOOS=linux GOARCH=amd64
|
||||
@$(MAKE) build GOOS=linux GOARCH=arm64
|
||||
@$(MAKE) build GOOS=darwin GOARCH=amd64
|
||||
@$(MAKE) build GOOS=darwin GOARCH=arm64
|
||||
@$(MAKE) build GOOS=windows GOARCH=amd64
|
||||
@mv ./bin/mizu_windows_amd64 ./bin/mizu.exe
|
||||
@echo "---------"
|
||||
@find ./bin -ls
|
||||
echo "Compiling for every OS and Platform" && \
|
||||
mkdir -p bin && sed s/_VER_/$(VER)/g README.md.TEMPLATE > bin/README.md && \
|
||||
$(MAKE) build GOOS=linux GOARCH=amd64 && \
|
||||
$(MAKE) build GOOS=linux GOARCH=arm64 && \
|
||||
$(MAKE) build GOOS=darwin GOARCH=amd64 && \
|
||||
$(MAKE) build GOOS=darwin GOARCH=arm64 && \
|
||||
$(MAKE) build GOOS=windows GOARCH=amd64 && \
|
||||
mv ./bin/mizu_windows_amd64 ./bin/mizu.exe && \
|
||||
echo "---------" && \
|
||||
find ./bin -ls
|
||||
|
||||
clean: ## Clean all build artifacts.
|
||||
go clean
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"github.com/up9inc/mizu/cli/uiUtils"
|
||||
"github.com/up9inc/mizu/shared"
|
||||
|
||||
basenine "github.com/up9inc/basenine/server/lib"
|
||||
"github.com/up9inc/mizu/shared/logger"
|
||||
"github.com/up9inc/mizu/shared/units"
|
||||
)
|
||||
@@ -79,10 +78,6 @@ func (config *TapConfig) GetInsertionFilter() string {
|
||||
}
|
||||
}
|
||||
}
|
||||
_, err := basenine.Parse(insertionFilter)
|
||||
if err != nil {
|
||||
logger.Log.Warningf(uiUtils.Warning, fmt.Sprintf("Insertion filter syntax error: %v", err))
|
||||
}
|
||||
return insertionFilter
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ require (
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
||||
github.com/spf13/cobra v1.3.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/up9inc/basenine/server/lib v0.0.0-20220326121918-785f3061c8ce
|
||||
github.com/up9inc/mizu/shared v0.0.0
|
||||
github.com/up9inc/mizu/tap/api v0.0.0
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
|
||||
@@ -33,11 +32,8 @@ require (
|
||||
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/alecthomas/participle/v2 v2.0.0-alpha7 // indirect
|
||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect
|
||||
github.com/clbanning/mxj/v2 v2.5.5 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dlclark/regexp2 v1.4.0 // indirect
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
|
||||
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
|
||||
@@ -72,7 +68,6 @@ require (
|
||||
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.13 // indirect
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
|
||||
12
cli/go.sum
12
cli/go.sum
@@ -83,10 +83,6 @@ github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tN
|
||||
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/alecthomas/participle/v2 v2.0.0-alpha7 h1:cK4vjj0VSgb3lN1nuKA5F7dw+1s1pWBe5bx7nNCnN+c=
|
||||
github.com/alecthomas/participle/v2 v2.0.0-alpha7/go.mod h1:NumScqsC42o9x+dGj8/YqsIfhrIQjFEOFovxotbBirA=
|
||||
github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1 h1:GDQdwm/gAcJcLAKQQZGOJ4knlw+7rfEQQcmwTbt4p5E=
|
||||
github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
@@ -120,8 +116,6 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||
github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E=
|
||||
github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
@@ -155,8 +149,6 @@ github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMS
|
||||
github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
|
||||
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
@@ -487,8 +479,6 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWb
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/ohler55/ojg v1.12.13 h1:FvfVpYzLgMraLcg3rrXiRXaihOP6fnzQNEU9YyZ/AmM=
|
||||
github.com/ohler55/ojg v1.12.13/go.mod h1:LBbIVRAgoFbYBXQhRhuEpaJIqq+goSO63/FQ+nyJU88=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
@@ -600,8 +590,6 @@ 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/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/up9inc/basenine/server/lib v0.0.0-20220326121918-785f3061c8ce h1:PypqybjmuxftGkX4NmP4JAUyEykZj2r6W4r9lnRZ/kE=
|
||||
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/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
|
||||
github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk=
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
package shared
|
||||
|
||||
const (
|
||||
MizuFilteringOptionsEnvVar = "SENSITIVE_DATA_FILTERING_OPTIONS"
|
||||
SyncEntriesConfigEnvVar = "SYNC_ENTRIES_CONFIG"
|
||||
HostModeEnvVar = "HOST_MODE"
|
||||
NodeNameEnvVar = "NODE_NAME"
|
||||
ConfigDirPath = "/app/config/"
|
||||
DataDirPath = "/app/data/"
|
||||
ValidationRulesFileName = "validation-rules.yaml"
|
||||
ContractFileName = "contract-oas.yaml"
|
||||
ConfigFileName = "mizu-config.json"
|
||||
GoGCEnvVar = "GOGC"
|
||||
DefaultApiServerPort = 8899
|
||||
LogLevelEnvVar = "LOG_LEVEL"
|
||||
MizuAgentImageRepo = "docker.io/up9inc/mizu"
|
||||
BasenineHost = "127.0.0.1"
|
||||
BaseninePort = "9099"
|
||||
MizuFilteringOptionsEnvVar = "SENSITIVE_DATA_FILTERING_OPTIONS"
|
||||
SyncEntriesConfigEnvVar = "SYNC_ENTRIES_CONFIG"
|
||||
HostModeEnvVar = "HOST_MODE"
|
||||
NodeNameEnvVar = "NODE_NAME"
|
||||
ConfigDirPath = "/app/config/"
|
||||
DataDirPath = "/app/data/"
|
||||
ValidationRulesFileName = "validation-rules.yaml"
|
||||
ContractFileName = "contract-oas.yaml"
|
||||
ConfigFileName = "mizu-config.json"
|
||||
GoGCEnvVar = "GOGC"
|
||||
DefaultApiServerPort = 8899
|
||||
LogLevelEnvVar = "LOG_LEVEL"
|
||||
MizuAgentImageRepo = "docker.io/up9inc/mizu"
|
||||
BasenineHost = "127.0.0.1"
|
||||
BaseninePort = "9099"
|
||||
BasenineReconnectInterval = 3
|
||||
)
|
||||
|
||||
@@ -329,7 +329,7 @@ func (tapperSyncer *MizuTapperSyncer) updateMizuTappers() error {
|
||||
TapperDaemonSetName,
|
||||
tapperSyncer.config.AgentImage,
|
||||
TapperPodName,
|
||||
fmt.Sprintf("%s.%s.svc.cluster.local", ApiServerPodName, tapperSyncer.config.MizuResourcesNamespace),
|
||||
fmt.Sprintf("%s.%s.svc", ApiServerPodName, tapperSyncer.config.MizuResourcesNamespace),
|
||||
nodeNames,
|
||||
serviceAccountName,
|
||||
tapperSyncer.config.TapperResources,
|
||||
|
||||
@@ -805,14 +805,20 @@ func (provider *Provider) ApplyMizuTapperDaemonSet(ctx context.Context, namespac
|
||||
agentResources := applyconfcore.ResourceRequirements().WithRequests(agentResourceRequests).WithLimits(agentResourceLimits)
|
||||
agentContainer.WithResources(agentResources)
|
||||
|
||||
nodeSelectorRequirement := applyconfcore.NodeSelectorRequirement()
|
||||
nodeSelectorRequirement.WithKey("kubernetes.io/hostname")
|
||||
nodeSelectorRequirement.WithOperator(core.NodeSelectorOpIn)
|
||||
nodeSelectorRequirement.WithValues(nodeNames...)
|
||||
nodeSelectorTerm := applyconfcore.NodeSelectorTerm()
|
||||
nodeSelectorTerm.WithMatchExpressions(nodeSelectorRequirement)
|
||||
matchFields := make([]*applyconfcore.NodeSelectorTermApplyConfiguration, 0)
|
||||
for _, nodeName := range nodeNames {
|
||||
nodeSelectorRequirement := applyconfcore.NodeSelectorRequirement()
|
||||
nodeSelectorRequirement.WithKey("metadata.name")
|
||||
nodeSelectorRequirement.WithOperator(core.NodeSelectorOpIn)
|
||||
nodeSelectorRequirement.WithValues(nodeName)
|
||||
|
||||
nodeSelectorTerm := applyconfcore.NodeSelectorTerm()
|
||||
nodeSelectorTerm.WithMatchFields(nodeSelectorRequirement)
|
||||
matchFields = append(matchFields, nodeSelectorTerm)
|
||||
}
|
||||
|
||||
nodeSelector := applyconfcore.NodeSelector()
|
||||
nodeSelector.WithNodeSelectorTerms(nodeSelectorTerm)
|
||||
nodeSelector.WithNodeSelectorTerms(matchFields...)
|
||||
nodeAffinity := applyconfcore.NodeAffinity()
|
||||
nodeAffinity.WithRequiredDuringSchedulingIgnoredDuringExecution(nodeSelector)
|
||||
affinity := applyconfcore.Affinity()
|
||||
|
||||
@@ -26,7 +26,8 @@ func GetNodeHostToTappedPodsMap(tappedPods []core.Pod) shared.NodeToPodsMap {
|
||||
func getMinimizedPod(fullPod core.Pod) core.Pod {
|
||||
return core.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fullPod.Name,
|
||||
Name: fullPod.Name,
|
||||
Namespace: fullPod.Namespace,
|
||||
},
|
||||
Status: core.PodStatus{
|
||||
PodIP: fullPod.Status.PodIP,
|
||||
|
||||
@@ -20,7 +20,6 @@ const (
|
||||
WebSocketMessageTypeUpdateStatus WebSocketMessageType = "status"
|
||||
WebSocketMessageTypeUpdateTappedPods WebSocketMessageType = "tappedPods"
|
||||
WebSocketMessageTypeAnalyzeStatus WebSocketMessageType = "analyzeStatus"
|
||||
WebsocketMessageTypeOutboundLink WebSocketMessageType = "outboundLink"
|
||||
WebSocketMessageTypeToast WebSocketMessageType = "toast"
|
||||
WebSocketMessageTypeQueryMetadata WebSocketMessageType = "queryMetadata"
|
||||
WebSocketMessageTypeStartTime WebSocketMessageType = "startTime"
|
||||
@@ -92,7 +91,7 @@ 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)
|
||||
summary[node] = append(summary[node], pod.Namespace+"/"+pod.Name)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
)
|
||||
|
||||
const mizuTestEnvVar = "MIZU_TEST"
|
||||
const UNKNOWN_NAMESPACE = ""
|
||||
|
||||
var UnknownIp net.IP = net.IP{0, 0, 0, 0}
|
||||
var UnknownPort uint16 = 0
|
||||
@@ -92,14 +93,15 @@ type RequestResponsePair struct {
|
||||
Response GenericMessage `json:"response"`
|
||||
}
|
||||
|
||||
// `Protocol` is modified in the later stages of data propagation. Therefore it's not a pointer.
|
||||
type OutputChannelItem struct {
|
||||
// `Protocol` is modified in later stages of data propagation. Therefore, it's not a pointer.
|
||||
Protocol Protocol
|
||||
Capture Capture
|
||||
Timestamp int64
|
||||
ConnectionInfo *ConnectionInfo
|
||||
Pair *RequestResponsePair
|
||||
Summary *BaseEntry
|
||||
Namespace string
|
||||
}
|
||||
|
||||
type SuperTimer struct {
|
||||
@@ -156,12 +158,12 @@ func (e *Emitting) Emit(item *OutputChannelItem) {
|
||||
}
|
||||
|
||||
type Entry struct {
|
||||
Id uint `json:"id"`
|
||||
Id string `json:"id"`
|
||||
Protocol Protocol `json:"proto"`
|
||||
Capture Capture `json:"capture"`
|
||||
Source *TCP `json:"src"`
|
||||
Destination *TCP `json:"dst"`
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
Namespace string `json:"namespace"`
|
||||
Outgoing bool `json:"outgoing"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
StartTime time.Time `json:"startTime"`
|
||||
@@ -188,7 +190,7 @@ type EntryWrapper struct {
|
||||
}
|
||||
|
||||
type BaseEntry struct {
|
||||
Id uint `json:"id"`
|
||||
Id string `json:"id"`
|
||||
Protocol Protocol `json:"proto,omitempty"`
|
||||
Capture Capture `json:"capture"`
|
||||
Summary string `json:"summary,omitempty"`
|
||||
|
||||
@@ -13,4 +13,4 @@ test-pull-bin:
|
||||
|
||||
test-pull-expect:
|
||||
@mkdir -p expect
|
||||
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect5/amqp/\* expect
|
||||
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect8/amqp/\* expect
|
||||
|
||||
@@ -13,4 +13,4 @@ test-pull-bin:
|
||||
|
||||
test-pull-expect:
|
||||
@mkdir -p expect
|
||||
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect5/http/\* expect
|
||||
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect8/http/\* expect
|
||||
|
||||
@@ -28,26 +28,6 @@ const protoMinorHTTP2 = 0
|
||||
|
||||
var maxHTTP2DataLen = 1 * 1024 * 1024 // 1MB
|
||||
|
||||
var grpcStatusCodes = []string{
|
||||
"OK",
|
||||
"CANCELLED",
|
||||
"UNKNOWN",
|
||||
"INVALID_ARGUMENT",
|
||||
"DEADLINE_EXCEEDED",
|
||||
"NOT_FOUND",
|
||||
"ALREADY_EXISTS",
|
||||
"PERMISSION_DENIED",
|
||||
"RESOURCE_EXHAUSTED",
|
||||
"FAILED_PRECONDITION",
|
||||
"ABORTED",
|
||||
"OUT_OF_RANGE",
|
||||
"UNIMPLEMENTED",
|
||||
"INTERNAL",
|
||||
"UNAVAILABLE",
|
||||
"DATA_LOSS",
|
||||
"UNAUTHENTICATED",
|
||||
}
|
||||
|
||||
type messageFragment struct {
|
||||
headers []hpack.HeaderField
|
||||
data []byte
|
||||
@@ -142,18 +122,8 @@ func (ga *Http2Assembler) readMessage() (streamID uint32, messageHTTP1 interface
|
||||
|
||||
// gRPC detection
|
||||
grpcStatus := headersHTTP1.Get("Grpc-Status")
|
||||
if grpcStatus != "" {
|
||||
if grpcStatus != "" || strings.Contains(headersHTTP1.Get("Content-Type"), "application/grpc") {
|
||||
isGrpc = true
|
||||
status = grpcStatus
|
||||
}
|
||||
|
||||
if strings.Contains(headersHTTP1.Get("Content-Type"), "application/grpc") {
|
||||
isGrpc = true
|
||||
grpcPath := headersHTTP1.Get(":path")
|
||||
pathSegments := strings.Split(grpcPath, "/")
|
||||
if len(pathSegments) > 0 {
|
||||
method = pathSegments[len(pathSegments)-1]
|
||||
}
|
||||
}
|
||||
|
||||
if method != "" {
|
||||
|
||||
@@ -248,11 +248,6 @@ func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string,
|
||||
reqDetails["_queryStringMerged"] = mapSliceMergeRepeatedKeys(reqDetails["_queryString"].([]interface{}))
|
||||
reqDetails["queryString"] = mapSliceRebuildAsMap(reqDetails["_queryStringMerged"].([]interface{}))
|
||||
|
||||
statusCode := int(resDetails["status"].(float64))
|
||||
if item.Protocol.Abbreviation == "gRPC" {
|
||||
resDetails["statusText"] = grpcStatusCodes[statusCode]
|
||||
}
|
||||
|
||||
elapsedTime := item.Pair.Response.CaptureTime.Sub(item.Pair.Request.CaptureTime).Round(time.Millisecond).Milliseconds()
|
||||
if elapsedTime < 0 {
|
||||
elapsedTime = 0
|
||||
|
||||
@@ -13,4 +13,4 @@ test-pull-bin:
|
||||
|
||||
test-pull-expect:
|
||||
@mkdir -p expect
|
||||
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect5/kafka/\* expect
|
||||
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect8/kafka/\* expect
|
||||
|
||||
@@ -13,4 +13,4 @@ test-pull-bin:
|
||||
|
||||
test-pull-expect:
|
||||
@mkdir -p expect
|
||||
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect5/redis/\* expect
|
||||
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect8/redis/\* expect
|
||||
|
||||
@@ -3,7 +3,6 @@ module github.com/up9inc/mizu/tap
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/bradleyfalzon/tlsx v0.0.0-20170624122154-28fd0e59bac4
|
||||
github.com/cilium/ebpf v0.8.0
|
||||
github.com/go-errors/errors v1.4.2
|
||||
github.com/google/gopacket v1.1.19
|
||||
|
||||
@@ -91,8 +91,6 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
|
||||
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/bradleyfalzon/tlsx v0.0.0-20170624122154-28fd0e59bac4 h1:NJOOlc6ZJjix0A1rAU+nxruZtR8KboG1848yqpIUo4M=
|
||||
github.com/bradleyfalzon/tlsx v0.0.0-20170624122154-28fd0e59bac4/go.mod h1:DQPxZS994Ld1Y8uwnJT+dRL04XPD0cElP/pHH/zEBHM=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
|
||||
@@ -29,21 +29,6 @@ func getLocalhostIPs() ([]string, error) {
|
||||
return myIPs, nil
|
||||
}
|
||||
|
||||
//lint:ignore U1000 will be used in the future
|
||||
func isPrivateIP(ipStr string) bool {
|
||||
ip := net.ParseIP(ipStr)
|
||||
if ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, block := range privateIPBlocks {
|
||||
if block.Contains(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func initPrivateIPBlocks() {
|
||||
for _, cidr := range []string{
|
||||
"127.0.0.0/8", // IPv4 loopback
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
package tap
|
||||
|
||||
type OutboundLinkProtocol string
|
||||
|
||||
const (
|
||||
TLSProtocol OutboundLinkProtocol = "tls"
|
||||
)
|
||||
|
||||
type OutboundLink struct {
|
||||
Src string
|
||||
DstIP string
|
||||
DstPort int
|
||||
SuggestedResolvedName string
|
||||
SuggestedProtocol OutboundLinkProtocol
|
||||
}
|
||||
|
||||
func NewOutboundLinkWriter() *OutboundLinkWriter {
|
||||
return &OutboundLinkWriter{
|
||||
OutChan: make(chan *OutboundLink),
|
||||
}
|
||||
}
|
||||
|
||||
type OutboundLinkWriter struct {
|
||||
OutChan chan *OutboundLink
|
||||
}
|
||||
|
||||
func (olw *OutboundLinkWriter) WriteOutboundLink(src string, DstIP string, DstPort int, SuggestedResolvedName string, SuggestedProtocol OutboundLinkProtocol) {
|
||||
olw.OutChan <- &OutboundLink{
|
||||
Src: src,
|
||||
DstIP: DstIP,
|
||||
DstPort: DstPort,
|
||||
SuggestedResolvedName: SuggestedResolvedName,
|
||||
SuggestedProtocol: SuggestedProtocol,
|
||||
}
|
||||
}
|
||||
|
||||
func (olw *OutboundLinkWriter) Stop() {
|
||||
close(olw.OutChan)
|
||||
}
|
||||
@@ -27,9 +27,6 @@ import (
|
||||
|
||||
const cleanPeriod = time.Second * 10
|
||||
|
||||
//lint:ignore U1000 will be used in the future
|
||||
var remoteOnlyOutboundPorts = []int{80, 443}
|
||||
|
||||
var maxcount = flag.Int64("c", -1, "Only grab this many packets, then exit")
|
||||
var decoder = flag.String("decoder", "", "Name of the decoder to use (default: guess from capture)")
|
||||
var statsevery = flag.Int("stats", 60, "Output statistics every N seconds")
|
||||
@@ -58,7 +55,7 @@ var tls = flag.Bool("tls", false, "Enable TLS tapper")
|
||||
var memprofile = flag.String("memprofile", "", "Write memory profile")
|
||||
|
||||
type TapOpts struct {
|
||||
HostMode bool
|
||||
HostMode bool
|
||||
}
|
||||
|
||||
var extensions []*api.Extension // global
|
||||
@@ -68,24 +65,6 @@ var packetSourceManager *source.PacketSourceManager // global
|
||||
var mainPacketInputChan chan source.TcpPacketInfo // global
|
||||
var tlsTapperInstance *tlstapper.TlsTapper // global
|
||||
|
||||
func inArrayInt(arr []int, valueToCheck int) bool {
|
||||
for _, value := range arr {
|
||||
if value == valueToCheck {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func inArrayString(arr []string, valueToCheck string) bool {
|
||||
for _, value := range arr {
|
||||
if value == valueToCheck {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func StartPassiveTapper(opts *TapOpts, outputItems chan *api.OutputChannelItem, extensionsRef []*api.Extension, options *api.TrafficFilteringOptions) {
|
||||
extensions = extensionsRef
|
||||
filteringOptions = options
|
||||
|
||||
@@ -13,6 +13,7 @@ const (
|
||||
MaxBufferedPagesTotalEnvVarName = "MAX_BUFFERED_PAGES_TOTAL"
|
||||
MaxBufferedPagesPerConnectionEnvVarName = "MAX_BUFFERED_PAGES_PER_CONNECTION"
|
||||
TcpStreamChannelTimeoutMsEnvVarName = "TCP_STREAM_CHANNEL_TIMEOUT_MS"
|
||||
CloseTimedoutTcpChannelsIntervalMsEnvVar = "CLOSE_TIMEDOUT_TCP_STREAM_CHANNELS_INTERVAL_MS"
|
||||
MaxBufferedPagesTotalDefaultValue = 5000
|
||||
MaxBufferedPagesPerConnectionDefaultValue = 5000
|
||||
TcpStreamChannelTimeoutMsDefaultValue = 10000
|
||||
|
||||
@@ -5,11 +5,12 @@ import (
|
||||
"runtime"
|
||||
|
||||
"github.com/up9inc/mizu/shared/logger"
|
||||
"github.com/up9inc/mizu/tap/api"
|
||||
"github.com/vishvananda/netns"
|
||||
)
|
||||
|
||||
func newNetnsPacketSource(procfs string, pid string,
|
||||
interfaceName string, behaviour TcpPacketSourceBehaviour) (*tcpPacketSource, error) {
|
||||
func newNetnsPacketSource(procfs string, pid string, interfaceName string,
|
||||
behaviour TcpPacketSourceBehaviour, origin api.Capture) (*tcpPacketSource, error) {
|
||||
nsh, err := netns.GetFromPath(fmt.Sprintf("%s/%s/ns/net", procfs, pid))
|
||||
|
||||
if err != nil {
|
||||
@@ -17,7 +18,7 @@ func newNetnsPacketSource(procfs string, pid string,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
src, err := newPacketSourceFromNetnsHandle(pid, nsh, interfaceName, behaviour)
|
||||
src, err := newPacketSourceFromNetnsHandle(pid, nsh, interfaceName, behaviour, origin)
|
||||
|
||||
if err != nil {
|
||||
logger.Log.Errorf("Error starting netns packet source for %s - %w", pid, err)
|
||||
@@ -28,7 +29,7 @@ func newNetnsPacketSource(procfs string, pid string,
|
||||
}
|
||||
|
||||
func newPacketSourceFromNetnsHandle(pid string, nsh netns.NsHandle, interfaceName string,
|
||||
behaviour TcpPacketSourceBehaviour) (*tcpPacketSource, error) {
|
||||
behaviour TcpPacketSourceBehaviour, origin api.Capture) (*tcpPacketSource, error) {
|
||||
|
||||
done := make(chan *tcpPacketSource)
|
||||
errors := make(chan error)
|
||||
@@ -57,7 +58,7 @@ func newPacketSourceFromNetnsHandle(pid string, nsh netns.NsHandle, interfaceNam
|
||||
}
|
||||
|
||||
name := fmt.Sprintf("netns-%s-%s", pid, interfaceName)
|
||||
src, err := newTcpPacketSource(name, "", interfaceName, behaviour)
|
||||
src, err := newTcpPacketSource(name, "", interfaceName, behaviour, origin)
|
||||
|
||||
if err != nil {
|
||||
logger.Log.Errorf("Error listening to PID %s - %w", pid, err)
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/up9inc/mizu/shared/logger"
|
||||
"github.com/up9inc/mizu/tap/api"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
@@ -37,10 +38,10 @@ func NewPacketSourceManager(procfs string, filename string, interfaceName string
|
||||
}
|
||||
|
||||
sourceManager.config = PacketSourceManagerConfig{
|
||||
mtls: mtls,
|
||||
procfs: procfs,
|
||||
mtls: mtls,
|
||||
procfs: procfs,
|
||||
interfaceName: interfaceName,
|
||||
behaviour: behaviour,
|
||||
behaviour: behaviour,
|
||||
}
|
||||
|
||||
go hostSource.readPackets(ipdefrag, packets)
|
||||
@@ -56,7 +57,7 @@ func newHostPacketSource(filename string, interfaceName string,
|
||||
name = fmt.Sprintf("file-%s", filename)
|
||||
}
|
||||
|
||||
source, err := newTcpPacketSource(name, filename, interfaceName, behaviour)
|
||||
source, err := newTcpPacketSource(name, filename, interfaceName, behaviour, api.Pcap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -85,9 +86,9 @@ func (m *PacketSourceManager) updateMtlsPods(procfs string, pods []v1.Pod,
|
||||
}
|
||||
}
|
||||
|
||||
for pid := range relevantPids {
|
||||
for pid, origin := range relevantPids {
|
||||
if _, ok := m.sources[pid]; !ok {
|
||||
source, err := newNetnsPacketSource(procfs, pid, interfaceName, behaviour)
|
||||
source, err := newNetnsPacketSource(procfs, pid, interfaceName, behaviour, origin)
|
||||
|
||||
if err == nil {
|
||||
go source.readPackets(ipdefrag, packets)
|
||||
@@ -97,15 +98,15 @@ func (m *PacketSourceManager) updateMtlsPods(procfs string, pods []v1.Pod,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *PacketSourceManager) getRelevantPids(procfs string, pods []v1.Pod) map[string]bool {
|
||||
relevantPids := make(map[string]bool)
|
||||
relevantPids[hostSourcePid] = true
|
||||
func (m *PacketSourceManager) getRelevantPids(procfs string, pods []v1.Pod) map[string]api.Capture {
|
||||
relevantPids := make(map[string]api.Capture)
|
||||
relevantPids[hostSourcePid] = api.Pcap
|
||||
|
||||
if envoyPids, err := discoverRelevantEnvoyPids(procfs, pods); err != nil {
|
||||
logger.Log.Warningf("Unable to discover envoy pids - %w", err)
|
||||
} else {
|
||||
for _, pid := range envoyPids {
|
||||
relevantPids[pid] = true
|
||||
relevantPids[pid] = api.Envoy
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +114,7 @@ func (m *PacketSourceManager) getRelevantPids(procfs string, pods []v1.Pod) map[
|
||||
logger.Log.Warningf("Unable to discover linkerd pids - %w", err)
|
||||
} else {
|
||||
for _, pid := range linkerdPids {
|
||||
relevantPids[pid] = true
|
||||
relevantPids[pid] = api.Linkerd
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/google/gopacket/layers"
|
||||
"github.com/google/gopacket/pcap"
|
||||
"github.com/up9inc/mizu/shared/logger"
|
||||
"github.com/up9inc/mizu/tap/api"
|
||||
"github.com/up9inc/mizu/tap/diagnose"
|
||||
)
|
||||
|
||||
@@ -19,6 +20,7 @@ type tcpPacketSource struct {
|
||||
defragger *ip4defrag.IPv4Defragmenter
|
||||
Behaviour *TcpPacketSourceBehaviour
|
||||
name string
|
||||
Origin api.Capture
|
||||
}
|
||||
|
||||
type TcpPacketSourceBehaviour struct {
|
||||
@@ -36,13 +38,14 @@ type TcpPacketInfo struct {
|
||||
}
|
||||
|
||||
func newTcpPacketSource(name, filename string, interfaceName string,
|
||||
behaviour TcpPacketSourceBehaviour) (*tcpPacketSource, error) {
|
||||
behaviour TcpPacketSourceBehaviour, origin api.Capture) (*tcpPacketSource, error) {
|
||||
var err error
|
||||
|
||||
result := &tcpPacketSource{
|
||||
name: name,
|
||||
defragger: ip4defrag.NewIPv4Defragmenter(),
|
||||
Behaviour: &behaviour,
|
||||
Origin: origin,
|
||||
}
|
||||
|
||||
if filename != "" {
|
||||
|
||||
@@ -29,6 +29,7 @@ type tcpAssembler struct {
|
||||
// The assembler context
|
||||
type context struct {
|
||||
CaptureInfo gopacket.CaptureInfo
|
||||
Origin api.Capture
|
||||
}
|
||||
|
||||
func (c *context) GetCaptureInfo() gopacket.CaptureInfo {
|
||||
@@ -87,8 +88,10 @@ func (a *tcpAssembler) processPackets(dumpPacket bool, packets <-chan source.Tcp
|
||||
logger.Log.Fatalf("Failed to set network layer for checksum: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
c := context{
|
||||
CaptureInfo: packet.Metadata().CaptureInfo,
|
||||
Origin: packetInfo.Source.Origin,
|
||||
}
|
||||
diagnose.InternalStats.Totalsz += len(tcp.Payload)
|
||||
a.assemblerMutex.Lock()
|
||||
|
||||
@@ -7,13 +7,10 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/bradleyfalzon/tlsx"
|
||||
"github.com/up9inc/mizu/shared/logger"
|
||||
"github.com/up9inc/mizu/tap/api"
|
||||
)
|
||||
|
||||
const checkTLSPacketAmount = 100
|
||||
|
||||
type tcpReaderDataMsg struct {
|
||||
bytes []byte
|
||||
timestamp time.Time
|
||||
@@ -33,22 +30,21 @@ type ConnectionInfo struct {
|
||||
* Implements io.Reader interface (Read)
|
||||
*/
|
||||
type tcpReader struct {
|
||||
ident string
|
||||
tcpID *api.TcpID
|
||||
isClosed bool
|
||||
isClient bool
|
||||
isOutgoing bool
|
||||
msgQueue chan tcpReaderDataMsg // Channel of captured reassembled tcp payload
|
||||
data []byte
|
||||
progress *api.ReadProgress
|
||||
superTimer *api.SuperTimer
|
||||
parent *tcpStream
|
||||
packetsSeen uint
|
||||
outboundLinkWriter *OutboundLinkWriter
|
||||
extension *api.Extension
|
||||
emitter api.Emitter
|
||||
counterPair *api.CounterPair
|
||||
reqResMatcher api.RequestResponseMatcher
|
||||
ident string
|
||||
tcpID *api.TcpID
|
||||
isClosed bool
|
||||
isClient bool
|
||||
isOutgoing bool
|
||||
msgQueue chan tcpReaderDataMsg // Channel of captured reassembled tcp payload
|
||||
data []byte
|
||||
progress *api.ReadProgress
|
||||
superTimer *api.SuperTimer
|
||||
parent *tcpStream
|
||||
packetsSeen uint
|
||||
extension *api.Extension
|
||||
emitter api.Emitter
|
||||
counterPair *api.CounterPair
|
||||
reqResMatcher api.RequestResponseMatcher
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
@@ -64,16 +60,6 @@ func (h *tcpReader) Read(p []byte) (int, error) {
|
||||
if len(h.data) > 0 {
|
||||
h.packetsSeen += 1
|
||||
}
|
||||
if h.packetsSeen < checkTLSPacketAmount && len(msg.bytes) > 5 { // packets with less than 5 bytes cause tlsx to panic
|
||||
clientHello := tlsx.ClientHello{}
|
||||
err := clientHello.Unmarshall(msg.bytes)
|
||||
if err == nil {
|
||||
logger.Log.Debugf("Detected TLS client hello with SNI %s", clientHello.SNI)
|
||||
// TODO: Throws `panic: runtime error: invalid memory address or nil pointer dereference` error.
|
||||
// numericPort, _ := strconv.Atoi(h.tcpID.DstPort)
|
||||
// h.outboundLinkWriter.WriteOutboundLink(h.tcpID.SrcIP, h.tcpID.DstIP, numericPort, clientHello.SNI, TLSProtocol)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !ok || len(h.data) == 0 {
|
||||
return 0, io.EOF
|
||||
@@ -98,8 +84,7 @@ func (h *tcpReader) Close() {
|
||||
func (h *tcpReader) run(wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
b := bufio.NewReader(h)
|
||||
// TODO: Add api.Pcap, api.Envoy and api.Linkerd distinction by refactoring NewPacketSourceManager method
|
||||
err := h.extension.Dissector.Dissect(b, h.progress, api.Pcap, h.isClient, h.tcpID, h.counterPair, h.superTimer, h.parent.superIdentifier, h.emitter, filteringOptions, h.reqResMatcher)
|
||||
err := h.extension.Dissector.Dissect(b, h.progress, h.parent.origin, h.isClient, h.tcpID, h.counterPair, h.superTimer, h.parent.superIdentifier, h.emitter, filteringOptions, h.reqResMatcher)
|
||||
if err != nil {
|
||||
_, err = io.Copy(ioutil.Discard, b)
|
||||
if err != nil {
|
||||
|
||||
@@ -29,6 +29,7 @@ type tcpStream struct {
|
||||
clients []tcpReader
|
||||
servers []tcpReader
|
||||
ident string
|
||||
origin api.Capture
|
||||
sync.Mutex
|
||||
streamsMap *tcpStreamMap
|
||||
}
|
||||
@@ -70,6 +71,9 @@ func (t *tcpStream) Accept(tcp *layers.TCP, ci gopacket.CaptureInfo, dir reassem
|
||||
if !accept {
|
||||
diagnose.InternalStats.RejectOpt++
|
||||
}
|
||||
|
||||
*start = true
|
||||
|
||||
return accept
|
||||
}
|
||||
|
||||
|
||||
@@ -20,12 +20,11 @@ import (
|
||||
* Generates a new tcp stream for each new tcp connection. Closes the stream when the connection closes.
|
||||
*/
|
||||
type tcpStreamFactory struct {
|
||||
wg sync.WaitGroup
|
||||
outboundLinkWriter *OutboundLinkWriter
|
||||
Emitter api.Emitter
|
||||
streamsMap *tcpStreamMap
|
||||
ownIps []string
|
||||
opts *TapOpts
|
||||
wg sync.WaitGroup
|
||||
Emitter api.Emitter
|
||||
streamsMap *tcpStreamMap
|
||||
ownIps []string
|
||||
opts *TapOpts
|
||||
}
|
||||
|
||||
type tcpStreamWrapper struct {
|
||||
@@ -63,9 +62,6 @@ func (factory *tcpStreamFactory) New(net, transport gopacket.Flow, tcp *layers.T
|
||||
srcPort := transport.Src().String()
|
||||
dstPort := transport.Dst().String()
|
||||
|
||||
// if factory.shouldNotifyOnOutboundLink(dstIp, dstPort) {
|
||||
// factory.outboundLinkWriter.WriteOutboundLink(net.Src().String(), dstIp, dstPort, "", "")
|
||||
// }
|
||||
props := factory.getStreamProps(srcIp, srcPort, dstIp, dstPort)
|
||||
isTapTarget := props.isTapTarget
|
||||
stream := &tcpStream{
|
||||
@@ -78,6 +74,7 @@ func (factory *tcpStreamFactory) New(net, transport gopacket.Flow, tcp *layers.T
|
||||
optchecker: reassembly.NewTCPOptionCheck(),
|
||||
superIdentifier: &api.SuperIdentifier{},
|
||||
streamsMap: factory.streamsMap,
|
||||
origin: getPacketOrigin(ac),
|
||||
}
|
||||
if stream.isTapTarget {
|
||||
stream.id = factory.streamsMap.nextId()
|
||||
@@ -98,14 +95,13 @@ func (factory *tcpStreamFactory) New(net, transport gopacket.Flow, tcp *layers.T
|
||||
SrcPort: srcPort,
|
||||
DstPort: dstPort,
|
||||
},
|
||||
parent: stream,
|
||||
isClient: true,
|
||||
isOutgoing: props.isOutgoing,
|
||||
outboundLinkWriter: factory.outboundLinkWriter,
|
||||
extension: extension,
|
||||
emitter: factory.Emitter,
|
||||
counterPair: counterPair,
|
||||
reqResMatcher: reqResMatcher,
|
||||
parent: stream,
|
||||
isClient: true,
|
||||
isOutgoing: props.isOutgoing,
|
||||
extension: extension,
|
||||
emitter: factory.Emitter,
|
||||
counterPair: counterPair,
|
||||
reqResMatcher: reqResMatcher,
|
||||
})
|
||||
stream.servers = append(stream.servers, tcpReader{
|
||||
msgQueue: make(chan tcpReaderDataMsg),
|
||||
@@ -118,14 +114,13 @@ func (factory *tcpStreamFactory) New(net, transport gopacket.Flow, tcp *layers.T
|
||||
SrcPort: transport.Dst().String(),
|
||||
DstPort: transport.Src().String(),
|
||||
},
|
||||
parent: stream,
|
||||
isClient: false,
|
||||
isOutgoing: props.isOutgoing,
|
||||
outboundLinkWriter: factory.outboundLinkWriter,
|
||||
extension: extension,
|
||||
emitter: factory.Emitter,
|
||||
counterPair: counterPair,
|
||||
reqResMatcher: reqResMatcher,
|
||||
parent: stream,
|
||||
isClient: false,
|
||||
isOutgoing: props.isOutgoing,
|
||||
extension: extension,
|
||||
emitter: factory.Emitter,
|
||||
counterPair: counterPair,
|
||||
reqResMatcher: reqResMatcher,
|
||||
})
|
||||
|
||||
factory.streamsMap.Store(stream.id, &tcpStreamWrapper{
|
||||
@@ -173,13 +168,15 @@ func (factory *tcpStreamFactory) getStreamProps(srcIP string, srcPort string, ds
|
||||
}
|
||||
}
|
||||
|
||||
//lint:ignore U1000 will be used in the future
|
||||
func (factory *tcpStreamFactory) shouldNotifyOnOutboundLink(dstIP string, dstPort int) bool {
|
||||
if inArrayInt(remoteOnlyOutboundPorts, dstPort) {
|
||||
isDirectedHere := inArrayString(factory.ownIps, dstIP)
|
||||
return !isDirectedHere && !isPrivateIP(dstIP)
|
||||
func getPacketOrigin(ac reassembly.AssemblerContext) api.Capture {
|
||||
c, ok := ac.(*context)
|
||||
|
||||
if !ok {
|
||||
// If ac is not our context, fallback to Pcap
|
||||
return api.Pcap
|
||||
}
|
||||
return true
|
||||
|
||||
return c.Origin
|
||||
}
|
||||
|
||||
type streamProps struct {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package tap
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
_debug "runtime/debug"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -34,10 +36,35 @@ func (streamMap *tcpStreamMap) nextId() int64 {
|
||||
return streamMap.streamId
|
||||
}
|
||||
|
||||
func (streamMap *tcpStreamMap) getCloseTimedoutTcpChannelsInterval() time.Duration {
|
||||
defaultDuration := 1000 * time.Millisecond
|
||||
rangeMin := 10
|
||||
rangeMax := 10000
|
||||
closeTimedoutTcpChannelsIntervalMsStr := os.Getenv(CloseTimedoutTcpChannelsIntervalMsEnvVar)
|
||||
if closeTimedoutTcpChannelsIntervalMsStr == "" {
|
||||
return defaultDuration
|
||||
} else {
|
||||
closeTimedoutTcpChannelsIntervalMs, err := strconv.Atoi(closeTimedoutTcpChannelsIntervalMsStr)
|
||||
if err != nil {
|
||||
logger.Log.Warningf("Error parsing environment variable %s: %v\n", CloseTimedoutTcpChannelsIntervalMsEnvVar, err)
|
||||
return defaultDuration
|
||||
} else {
|
||||
if closeTimedoutTcpChannelsIntervalMs < rangeMin || closeTimedoutTcpChannelsIntervalMs > rangeMax {
|
||||
logger.Log.Warningf("The value of environment variable %s is not in acceptable range: %d - %d\n", CloseTimedoutTcpChannelsIntervalMsEnvVar, rangeMin, rangeMax)
|
||||
return defaultDuration
|
||||
} else {
|
||||
return time.Duration(closeTimedoutTcpChannelsIntervalMs) * time.Millisecond
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (streamMap *tcpStreamMap) closeTimedoutTcpStreamChannels() {
|
||||
tcpStreamChannelTimeout := GetTcpChannelTimeoutMs()
|
||||
closeTimedoutTcpChannelsIntervalMs := streamMap.getCloseTimedoutTcpChannelsInterval()
|
||||
logger.Log.Infof("Using %d ms as the close timedout TCP stream channels interval", closeTimedoutTcpChannelsIntervalMs/time.Millisecond)
|
||||
for {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
time.Sleep(closeTimedoutTcpChannelsIntervalMs)
|
||||
_debug.FreeOSMemory()
|
||||
streamMap.streams.Range(func(key interface{}, value interface{}) bool {
|
||||
streamWrapper := value.(*tcpStreamWrapper)
|
||||
@@ -47,7 +74,7 @@ func (streamMap *tcpStreamMap) closeTimedoutTcpStreamChannels() {
|
||||
stream.Close()
|
||||
diagnose.AppStats.IncDroppedTcpStreams()
|
||||
logger.Log.Debugf("Dropped an unidentified TCP stream because of timeout. Total dropped: %d Total Goroutines: %d Timeout (ms): %d",
|
||||
diagnose.AppStats.DroppedTcpStreams, runtime.NumGoroutine(), tcpStreamChannelTimeout/1000000)
|
||||
diagnose.AppStats.DroppedTcpStreams, runtime.NumGoroutine(), tcpStreamChannelTimeout/time.Millisecond)
|
||||
}
|
||||
} else {
|
||||
if !stream.superIdentifier.IsClosedOthers {
|
||||
|
||||
13
tap/tlstapper/tls_emitter.go
Normal file
13
tap/tlstapper/tls_emitter.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package tlstapper
|
||||
|
||||
import "github.com/up9inc/mizu/tap/api"
|
||||
|
||||
type tlsEmitter struct {
|
||||
delegate api.Emitter
|
||||
namespace string
|
||||
}
|
||||
|
||||
func (e *tlsEmitter) Emit(item *api.OutputChannelItem) {
|
||||
item.Namespace = e.namespace
|
||||
e.delegate.Emit(item)
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
@@ -19,13 +21,14 @@ import (
|
||||
)
|
||||
|
||||
type tlsPoller struct {
|
||||
tls *TlsTapper
|
||||
readers map[string]*tlsReader
|
||||
closedReaders chan string
|
||||
reqResMatcher api.RequestResponseMatcher
|
||||
chunksReader *perf.Reader
|
||||
extension *api.Extension
|
||||
procfs string
|
||||
tls *TlsTapper
|
||||
readers map[string]*tlsReader
|
||||
closedReaders chan string
|
||||
reqResMatcher api.RequestResponseMatcher
|
||||
chunksReader *perf.Reader
|
||||
extension *api.Extension
|
||||
procfs string
|
||||
pidToNamespace sync.Map
|
||||
}
|
||||
|
||||
func newTlsPoller(tls *TlsTapper, extension *api.Extension, procfs string) *tlsPoller {
|
||||
@@ -151,16 +154,25 @@ func (p *tlsPoller) startNewTlsReader(chunk *tlsChunk, ip net.IP, port uint16, k
|
||||
|
||||
tcpid := p.buildTcpId(chunk, ip, port)
|
||||
|
||||
go dissect(extension, reader, chunk.isRequest(), &tcpid, emitter, options, p.reqResMatcher)
|
||||
tlsEmitter := &tlsEmitter{
|
||||
delegate: emitter,
|
||||
namespace: p.getNamespace(chunk.Pid),
|
||||
}
|
||||
|
||||
go dissect(extension, reader, chunk.isRequest(), &tcpid, tlsEmitter, options, p.reqResMatcher)
|
||||
return reader
|
||||
}
|
||||
|
||||
func dissect(extension *api.Extension, reader *tlsReader, isRequest bool, tcpid *api.TcpID,
|
||||
emitter api.Emitter, options *api.TrafficFilteringOptions, reqResMatcher api.RequestResponseMatcher) {
|
||||
tlsEmitter *tlsEmitter, options *api.TrafficFilteringOptions, reqResMatcher api.RequestResponseMatcher) {
|
||||
b := bufio.NewReader(reader)
|
||||
|
||||
timer := api.SuperTimer{
|
||||
CaptureTime: time.Now(),
|
||||
}
|
||||
|
||||
err := extension.Dissector.Dissect(b, reader.progress, api.Ebpf, isRequest, tcpid, &api.CounterPair{},
|
||||
&api.SuperTimer{}, &api.SuperIdentifier{}, emitter, options, reqResMatcher)
|
||||
&timer, &api.SuperIdentifier{}, tlsEmitter, options, reqResMatcher)
|
||||
|
||||
if err != nil {
|
||||
logger.Log.Warningf("Error dissecting TLS %v - %v", tcpid, err)
|
||||
@@ -205,6 +217,33 @@ func (p *tlsPoller) buildTcpId(chunk *tlsChunk, ip net.IP, port uint16) api.TcpI
|
||||
}
|
||||
}
|
||||
|
||||
func (p *tlsPoller) addPid(pid uint32, namespace string) {
|
||||
p.pidToNamespace.Store(pid, namespace)
|
||||
}
|
||||
|
||||
func (p *tlsPoller) getNamespace(pid uint32) string {
|
||||
namespaceIfc, ok := p.pidToNamespace.Load(pid)
|
||||
|
||||
if !ok {
|
||||
return api.UNKNOWN_NAMESPACE
|
||||
}
|
||||
|
||||
namespace, ok := namespaceIfc.(string)
|
||||
|
||||
if !ok {
|
||||
return api.UNKNOWN_NAMESPACE
|
||||
}
|
||||
|
||||
return namespace
|
||||
}
|
||||
|
||||
func (p *tlsPoller) clearPids() {
|
||||
p.pidToNamespace.Range(func(key, v interface{}) bool {
|
||||
p.pidToNamespace.Delete(key)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func (p *tlsPoller) logTls(chunk *tlsChunk, ip net.IP, port uint16) {
|
||||
var flagsStr string
|
||||
|
||||
|
||||
@@ -24,11 +24,11 @@ func UpdateTapTargets(tls *TlsTapper, pods *[]v1.Pod, procfs string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
tls.ClearPids()
|
||||
|
||||
for _, pid := range containerPids {
|
||||
if err := tls.AddPid(procfs, pid); err != nil {
|
||||
for pid, pod := range containerPids {
|
||||
if err := tls.AddPid(procfs, pid, pod.Namespace); err != nil {
|
||||
LogError(err)
|
||||
}
|
||||
}
|
||||
@@ -36,8 +36,8 @@ func UpdateTapTargets(tls *TlsTapper, pods *[]v1.Pod, procfs string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func findContainerPids(procfs string, containerIds map[string]bool) ([]uint32, error) {
|
||||
result := make([]uint32, 0)
|
||||
func findContainerPids(procfs string, containerIds map[string]v1.Pod) (map[uint32]v1.Pod, error) {
|
||||
result := make(map[uint32]v1.Pod)
|
||||
|
||||
pids, err := ioutil.ReadDir(procfs)
|
||||
|
||||
@@ -63,7 +63,9 @@ func findContainerPids(procfs string, containerIds map[string]bool) ([]uint32, e
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := containerIds[cgroup]; !ok {
|
||||
pod, ok := containerIds[cgroup]
|
||||
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -73,14 +75,14 @@ func findContainerPids(procfs string, containerIds map[string]bool) ([]uint32, e
|
||||
continue
|
||||
}
|
||||
|
||||
result = append(result, uint32(pidNumber))
|
||||
result[uint32(pidNumber)] = pod
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func buildContainerIdsMap(pods *[]v1.Pod) map[string]bool {
|
||||
result := make(map[string]bool)
|
||||
func buildContainerIdsMap(pods *[]v1.Pod) map[string]v1.Pod {
|
||||
result := make(map[string]v1.Pod)
|
||||
|
||||
for _, pod := range *pods {
|
||||
for _, container := range pod.Status.ContainerStatuses {
|
||||
@@ -91,7 +93,7 @@ func buildContainerIdsMap(pods *[]v1.Pod) map[string]bool {
|
||||
continue
|
||||
}
|
||||
|
||||
result[url.Host] = true
|
||||
result[url.Host] = pod
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,14 +143,14 @@ func extractCgroup(lines []string) string {
|
||||
// /kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod3beae8e0_164d_4689_a087_efd902d8c2ab.slice/docker-<ID>.scope
|
||||
// /kubepods/besteffort/pod7709c1d5-447c-428f-bed9-8ddec35c93f4/<ID>
|
||||
//
|
||||
// This function extract the <ID> out of the cgroup path, the <ID> should match
|
||||
// This function extract the <ID> out of the cgroup path, the <ID> should match
|
||||
// the "Container ID:" field when running kubectl describe pod <POD>
|
||||
//
|
||||
func normalizeCgroup(cgrouppath string) string {
|
||||
basename := strings.TrimSpace(path.Base(cgrouppath))
|
||||
|
||||
|
||||
if strings.Contains(basename, "-") {
|
||||
basename = basename[strings.Index(basename, "-") + 1:]
|
||||
basename = basename[strings.Index(basename, "-")+1:]
|
||||
}
|
||||
|
||||
if strings.Contains(basename, ".") {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package tlstapper
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/cilium/ebpf/rlimit"
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/up9inc/mizu/shared/logger"
|
||||
"github.com/up9inc/mizu/tap/api"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const GLOABL_TAP_PID = 0
|
||||
@@ -58,10 +59,10 @@ func (t *TlsTapper) PollForLogging() {
|
||||
}
|
||||
|
||||
func (t *TlsTapper) GlobalTap(sslLibrary string) error {
|
||||
return t.tapPid(GLOABL_TAP_PID, sslLibrary)
|
||||
return t.tapPid(GLOABL_TAP_PID, sslLibrary, api.UNKNOWN_NAMESPACE)
|
||||
}
|
||||
|
||||
func (t *TlsTapper) AddPid(procfs string, pid uint32) error {
|
||||
func (t *TlsTapper) AddPid(procfs string, pid uint32, namespace string) error {
|
||||
sslLibrary, err := findSsllib(procfs, pid)
|
||||
|
||||
if err != nil {
|
||||
@@ -69,7 +70,7 @@ func (t *TlsTapper) AddPid(procfs string, pid uint32) error {
|
||||
return nil // hide the error on purpose, its OK for a process to not use libssl.so
|
||||
}
|
||||
|
||||
return t.tapPid(pid, sslLibrary)
|
||||
return t.tapPid(pid, sslLibrary, namespace)
|
||||
}
|
||||
|
||||
func (t *TlsTapper) RemovePid(pid uint32) error {
|
||||
@@ -85,12 +86,13 @@ func (t *TlsTapper) RemovePid(pid uint32) error {
|
||||
}
|
||||
|
||||
func (t *TlsTapper) ClearPids() {
|
||||
t.poller.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)
|
||||
}
|
||||
@@ -133,7 +135,7 @@ func setupRLimit() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TlsTapper) tapPid(pid uint32, sslLibrary string) error {
|
||||
func (t *TlsTapper) tapPid(pid uint32, sslLibrary string, namespace string) error {
|
||||
logger.Log.Infof("Tapping TLS (pid: %v) (sslLibrary: %v)", pid, sslLibrary)
|
||||
|
||||
newSsl := sslHooks{}
|
||||
@@ -144,12 +146,14 @@ func (t *TlsTapper) tapPid(pid uint32, sslLibrary string) error {
|
||||
|
||||
t.sslHooksStructs = append(t.sslHooksStructs, newSsl)
|
||||
|
||||
t.poller.addPid(pid, namespace)
|
||||
|
||||
pids := t.bpfObjects.tlsTapperMaps.PidsMap
|
||||
|
||||
if err := pids.Put(pid, uint32(1)); err != nil {
|
||||
return errors.Wrap(err, 0)
|
||||
}
|
||||
|
||||
|
||||
t.registeredPids.Store(pid, true)
|
||||
|
||||
return nil
|
||||
|
||||
@@ -54,11 +54,6 @@ export default class Api {
|
||||
return response.data;
|
||||
}
|
||||
|
||||
getRecentTLSLinks = async () => {
|
||||
const response = await client.get("/status/recentTLSLinks");
|
||||
return response.data;
|
||||
}
|
||||
|
||||
getOasServices = async () => {
|
||||
const response = await client.get("/oas");
|
||||
return response.data;
|
||||
@@ -121,4 +116,4 @@ export function getWebsocketUrl(){
|
||||
}
|
||||
|
||||
return websocketUrl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,15 +26,16 @@
|
||||
"@material-ui/core": "^4.11.3",
|
||||
"@material-ui/icons": "^4.11.2",
|
||||
"@material-ui/lab": "^4.0.0-alpha.60",
|
||||
"@types/jest": "^26.0.22",
|
||||
"@types/node": "^12.20.10",
|
||||
"node-sass": "^6.0.0",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"recoil": "^0.5.2",
|
||||
"react-copy-to-clipboard": "^5.0.3",
|
||||
"@types/jest": "^26.0.22",
|
||||
"@types/node": "^12.20.10"
|
||||
"react-dom": "^17.0.2",
|
||||
"recoil": "^0.5.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@craco/craco": "^6.4.3",
|
||||
"@types/lodash": "^4.14.179",
|
||||
"@uiw/react-textarea-code-editor": "^1.4.12",
|
||||
"axios": "^0.25.0",
|
||||
@@ -48,8 +49,7 @@
|
||||
"moment": "^2.29.1",
|
||||
"node-fetch": "^3.1.1",
|
||||
"numeral": "^2.0.6",
|
||||
"protobuf-decoder": "^0.1.0",
|
||||
"react-resizable": "^3.0.4",
|
||||
"protobuf-decoder": "^0.1.2",
|
||||
"react-graph-vis": "^1.0.7",
|
||||
"react-lowlight": "^3.0.0",
|
||||
"react-router-dom": "^6.2.1",
|
||||
@@ -59,8 +59,7 @@
|
||||
"redoc": "^2.0.0-rc.59",
|
||||
"styled-components": "^5.3.3",
|
||||
"web-vitals": "^1.1.1",
|
||||
"xml-formatter": "^2.6.0",
|
||||
"@craco/craco": "^6.4.3"
|
||||
"xml-formatter": "^2.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-node-resolve": "^13.1.3",
|
||||
|
||||
@@ -8,6 +8,7 @@ import openApiLogo from 'assets/openApiLogo.png'
|
||||
import { redocThemeOptions } from "./redocThemeOptions";
|
||||
import React from "react";
|
||||
import { Select } from "../UI/Select";
|
||||
import { TOAST_CONTAINER_ID } from "../../configs/Consts";
|
||||
|
||||
|
||||
const modalStyle = {
|
||||
@@ -43,7 +44,7 @@ const OasModal = ({ openModal, handleCloseModal, getOasServices, getOasByService
|
||||
const data = await getOasByService(selectedService ? selectedService : oasServices[0]);
|
||||
setSelectedServiceSpec(data);
|
||||
} catch (e) {
|
||||
toast.error("Error occurred while fetching service OAS spec");
|
||||
toast.error("Error occurred while fetching service OAS spec", { containerId: TOAST_CONTAINER_ID });
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
.modalContainer
|
||||
display: flex
|
||||
flex-wrap: nowrap
|
||||
width: 100%
|
||||
height: 100%
|
||||
|
||||
@@ -11,8 +10,6 @@
|
||||
|
||||
.filterSection
|
||||
flex: 15%
|
||||
border-right: 1px solid $blue-color
|
||||
margin-right: 2%
|
||||
height: 100%
|
||||
|
||||
.filters table
|
||||
@@ -38,6 +35,7 @@
|
||||
display: flex
|
||||
flex-direction: column
|
||||
margin-right: 10px
|
||||
width: 100%
|
||||
|
||||
.servicesFilterSearch
|
||||
width: calc(100% - 10px)
|
||||
@@ -47,13 +45,13 @@
|
||||
margin-bottom: 5px
|
||||
|
||||
.servicesFilter
|
||||
margin-top: clamp(25px,15%,35px)
|
||||
margin-top: 15px
|
||||
height: 100%
|
||||
overflow: hidden
|
||||
|
||||
& .servicesFilterList
|
||||
overflow-y: auto
|
||||
height: 92%
|
||||
height: calc(100% - 30px - 5px)
|
||||
|
||||
.separtorLine
|
||||
margin-top: 10px
|
||||
|
||||
@@ -12,9 +12,9 @@ import closeIcon from "assets/close.svg"
|
||||
import styles from './ServiceMapModal.module.sass'
|
||||
import SelectList from "../UI/SelectList";
|
||||
import { GraphData, ServiceMapGraph } from "./ServiceMapModalTypes"
|
||||
import { ResizableBox } from "react-resizable"
|
||||
import "react-resizable/css/styles.css"
|
||||
import { Utils } from "../../helpers/Utils";
|
||||
import { TOAST_CONTAINER_ID } from "../../configs/Consts";
|
||||
import Resizeable from "../UI/Resizeable"
|
||||
|
||||
const modalStyle = {
|
||||
position: 'absolute',
|
||||
@@ -46,12 +46,12 @@ const LegentLabel: React.FC<LegentLabelProps> = ({ color, name }) => {
|
||||
}
|
||||
|
||||
const protocols = [
|
||||
{ key: "http", value: "HTTP", component: <LegentLabel color="#205cf5" name="HTTP" /> },
|
||||
{ key: "http/2", value: "HTTP/2", component: <LegentLabel color='#244c5a' name="HTTP/2" /> },
|
||||
{ key: "grpc", value: "gRPC", component: <LegentLabel color='#244c5a' name="gRPC" /> },
|
||||
{ key: "amqp", value: "AMQP", component: <LegentLabel color='#ff6600' name="AMQP" /> },
|
||||
{ key: "kafka", value: "KAFKA", component: <LegentLabel color='#000000' name="KAFKA" /> },
|
||||
{ key: "redis", value: "REDIS", component: <LegentLabel color='#a41e11' name="REDIS" /> },]
|
||||
{ key: "HTTP", value: "HTTP", component: <LegentLabel color="#205cf5" name="HTTP" /> },
|
||||
{ key: "HTTP/2", value: "HTTP/2", component: <LegentLabel color='#244c5a' name="HTTP/2" /> },
|
||||
{ key: "gRPC", value: "gRPC", component: <LegentLabel color='#244c5a' name="gRPC" /> },
|
||||
{ key: "AMQP", value: "AMQP", component: <LegentLabel color='#ff6600' name="AMQP" /> },
|
||||
{ key: "KAFKA", value: "KAFKA", component: <LegentLabel color='#000000' name="KAFKA" /> },
|
||||
{ key: "REDIS", value: "REDIS", component: <LegentLabel color='#a41e11' name="REDIS" /> },]
|
||||
|
||||
|
||||
interface ServiceMapModalProps {
|
||||
@@ -65,8 +65,8 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onClos
|
||||
const commonClasses = useCommonStyles();
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
const [graphData, setGraphData] = useState<GraphData>({ nodes: [], edges: [] });
|
||||
const [filteredProtocols, setFilteredProtocols] = useState(protocols.map(x => x.key))
|
||||
const [filteredServices, setFilteredServices] = useState([])
|
||||
const [checkedProtocols, setCheckedProtocols] = useState(protocols.map(x => x.key))
|
||||
const [checkedServices, setCheckedServices] = useState([])
|
||||
const [serviceMapApiData, setServiceMapApiData] = useState<ServiceMapGraph>({ edges: [], nodes: [] })
|
||||
const [servicesSearchVal, setServicesSearchVal] = useState("")
|
||||
const [graphOptions, setGraphOptions] = useState(ServiceMapOptions);
|
||||
@@ -78,18 +78,11 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onClos
|
||||
const serviceMapData: ServiceMapGraph = await getServiceMapDataApi()
|
||||
setServiceMapApiData(serviceMapData)
|
||||
const newGraphData: GraphData = { nodes: [], edges: [] }
|
||||
|
||||
if (serviceMapData.nodes) {
|
||||
newGraphData.nodes = serviceMapData.nodes.map(mapNodesDatatoGraph)
|
||||
}
|
||||
|
||||
if (serviceMapData.edges) {
|
||||
newGraphData.edges = serviceMapData.edges.map(mapEdgesDatatoGraph)
|
||||
}
|
||||
|
||||
newGraphData.nodes = serviceMapData.nodes.map(mapNodesDatatoGraph)
|
||||
newGraphData.edges = serviceMapData.edges.map(mapEdgesDatatoGraph)
|
||||
setGraphData(newGraphData)
|
||||
} 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);
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
@@ -124,28 +117,32 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onClos
|
||||
.sort((a, b) => { return a.key.localeCompare(b.key) });
|
||||
|
||||
const getServicesForFilter = useMemo(() => {
|
||||
|
||||
const resolved = mapToKeyValForFilter(serviceMapApiData.nodes?.filter(x => x.resolved))
|
||||
const unResolved = mapToKeyValForFilter(serviceMapApiData.nodes?.filter(x => !x.resolved))
|
||||
return [...resolved, ...unResolved]
|
||||
}, [serviceMapApiData])
|
||||
|
||||
const filterServiceMap = (newProtocolsFilters?: any[], newServiceFilters?: string[]) => {
|
||||
const filterProt = newProtocolsFilters || filteredProtocols
|
||||
const filterService = newServiceFilters || filteredServices || getServicesForFilter.map(x => x.key)
|
||||
setFilteredProtocols(filterProt)
|
||||
setFilteredServices(filterService)
|
||||
useEffect(() => {
|
||||
const newGraphData: GraphData = {
|
||||
nodes: serviceMapApiData.nodes?.map(mapNodesDatatoGraph).filter(node => filterService.includes(node.label)),
|
||||
edges: serviceMapApiData.edges?.filter(edge => filterProt.includes(edge.protocol.name)).map(mapEdgesDatatoGraph)
|
||||
nodes: serviceMapApiData.nodes?.map(mapNodesDatatoGraph).filter(node => checkedServices.includes(node.label)),
|
||||
edges: serviceMapApiData.edges?.filter(edge => checkedProtocols.includes(edge.protocol.abbr)).map(mapEdgesDatatoGraph)
|
||||
}
|
||||
setGraphData(newGraphData);
|
||||
}, [checkedServices, checkedProtocols, serviceMapApiData])
|
||||
|
||||
const onProtocolsChange = (newProtocolsFiltersnewProt) => {
|
||||
const filterProt = newProtocolsFiltersnewProt || checkedProtocols
|
||||
setCheckedProtocols(filterProt)
|
||||
}
|
||||
|
||||
const onServiceChanges = (newServiceFilters) => {
|
||||
const filterService = newServiceFilters || checkedServices
|
||||
setCheckedServices([...filterService])
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const resolvedServices = getServicesForFilter.map(x => x.key).filter(serviceName => !Utils.isIpAddress(serviceName))
|
||||
setFilteredServices(resolvedServices)
|
||||
filterServiceMap(filteredProtocols, resolvedServices)
|
||||
if (checkedServices.length == 0)
|
||||
setCheckedServices(getServicesForFilter.map(x => x.key).filter(serviceName => !Utils.isIpAddress(serviceName)))
|
||||
}, [getServicesForFilter])
|
||||
|
||||
useEffect(() => {
|
||||
@@ -176,28 +173,27 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onClos
|
||||
<Fade in={isOpen}>
|
||||
<Box sx={modalStyle}>
|
||||
<div className={styles.modalContainer}>
|
||||
{/* TODO: remove error missing height */}
|
||||
<ResizableBox width={200} style={{ height: '100%', minWidth: "200px" }} axis={"x"}>
|
||||
<div className={styles.filterSection}>
|
||||
<div className={styles.filterSection}>
|
||||
<Resizeable minWidth={170} maxWidth={320}>
|
||||
<div className={styles.filterWrapper}>
|
||||
<div className={styles.protocolsFilterList}>
|
||||
<SelectList items={protocols} checkBoxWidth="5%" tableName={"Protocols"} multiSelect={true}
|
||||
checkedValues={filteredProtocols} setCheckedValues={filterServiceMap} tableClassName={styles.filters} />
|
||||
checkedValues={checkedProtocols} setCheckedValues={onProtocolsChange} tableClassName={styles.filters} />
|
||||
</div>
|
||||
<div className={styles.separtorLine}></div>
|
||||
<div className={styles.servicesFilter}>
|
||||
<input className={commonClasses.textField + ` ${styles.servicesFilterSearch}`} placeholder="search service" value={servicesSearchVal} onChange={(event) => setServicesSearchVal(event.target.value)} />
|
||||
<div className={styles.servicesFilterList}>
|
||||
<SelectList items={getServicesForFilter} tableName={"Services"} tableClassName={styles.filters} multiSelect={true} searchValue={servicesSearchVal}
|
||||
checkBoxWidth="5%" checkedValues={filteredServices} setCheckedValues={(newServicesForFilter) => filterServiceMap(null, newServicesForFilter)} />
|
||||
checkBoxWidth="5%" checkedValues={checkedServices} setCheckedValues={onServiceChanges} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ResizableBox>
|
||||
</Resizeable>
|
||||
</div>
|
||||
<div className={styles.graphSection}>
|
||||
<div style={{ display: "flex", justifyContent: "space-between" }}>
|
||||
<Button style={{ marginRight: "3%" }}
|
||||
<Button style={{ marginLeft: "3%" }}
|
||||
startIcon={<img src={refreshIcon} className="custom" alt="refresh" style={{ marginRight: "8%" }}></img>}
|
||||
size="medium"
|
||||
variant="contained"
|
||||
@@ -206,7 +202,7 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onClos
|
||||
>
|
||||
Refresh
|
||||
</Button>
|
||||
<img src={closeIcon} alt="close" onClick={() => onClose()} style={{ cursor: "pointer" }}></img>
|
||||
<img src={closeIcon} alt="close" onClick={() => onClose()} style={{ cursor: "pointer", userSelect: "none" }}></img>
|
||||
</div>
|
||||
{isLoading && <div className={spinnerStyle.spinnerContainer}>
|
||||
<img alt="spinner" src={spinnerImg} style={{ height: 50 }} />
|
||||
|
||||
@@ -67,11 +67,11 @@ const ServiceMapOptions = {
|
||||
color: '#343434',
|
||||
size: 12, // px
|
||||
face: 'arial',
|
||||
background: 'none',
|
||||
background: 'rgba(255,255,255,0.7)',
|
||||
strokeWidth: 2, // px
|
||||
strokeColor: '#ffffff',
|
||||
align: 'horizontal',
|
||||
multi: false,
|
||||
multi: false
|
||||
},
|
||||
labelHighlightBold: true,
|
||||
selectionWidth: 1,
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
.httpsDomains
|
||||
display: none
|
||||
margin: 0
|
||||
padding: 0
|
||||
list-style: none
|
||||
|
||||
.customWarningStyle
|
||||
&:hover
|
||||
overflow-y: scroll
|
||||
height: 85px
|
||||
.httpsDomains
|
||||
display: block
|
||||
@@ -1,44 +0,0 @@
|
||||
import {Snackbar} from "@material-ui/core";
|
||||
import MuiAlert from "@material-ui/lab/Alert";
|
||||
import React, {useEffect} from "react";
|
||||
import { RecoilState, useRecoilValue } from "recoil";
|
||||
import TrafficViewerApiAtom from "../../recoil/TrafficViewerApi/atom";
|
||||
import TrafficViewerApi from "../TrafficViewer/TrafficViewerApi";
|
||||
import './TLSWarning.sass';
|
||||
|
||||
interface TLSWarningProps {
|
||||
showTLSWarning: boolean
|
||||
setShowTLSWarning: (show: boolean) => void
|
||||
addressesWithTLS: Set<string>
|
||||
setAddressesWithTLS: (addresses: Set<string>) => void
|
||||
userDismissedTLSWarning: boolean
|
||||
setUserDismissedTLSWarning: (flag: boolean) => void
|
||||
}
|
||||
|
||||
export const TLSWarning: React.FC<TLSWarningProps> = ({showTLSWarning, setShowTLSWarning, addressesWithTLS, setAddressesWithTLS, userDismissedTLSWarning, setUserDismissedTLSWarning}) => {
|
||||
|
||||
const trafficViewerApi = useRecoilValue(TrafficViewerApiAtom as RecoilState<TrafficViewerApi>)
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
try {
|
||||
const getRecentTLSLinksFunc = trafficViewerApi?.getRecentTLSLinks ? trafficViewerApi?.getRecentTLSLinks : function(){}
|
||||
const recentTLSLinks = await getRecentTLSLinksFunc();
|
||||
if (recentTLSLinks?.length > 0) {
|
||||
setAddressesWithTLS(new Set(recentTLSLinks));
|
||||
setShowTLSWarning(true);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
})();
|
||||
}, [setShowTLSWarning, setAddressesWithTLS,trafficViewerApi]);
|
||||
|
||||
return (<Snackbar open={showTLSWarning && !userDismissedTLSWarning}>
|
||||
<MuiAlert classes={{filledWarning: 'customWarningStyle'}} elevation={6} variant="filled"
|
||||
onClose={() => setUserDismissedTLSWarning(true)} severity="warning">
|
||||
Mizu is detecting TLS traffic, this type of traffic will not be displayed.
|
||||
{addressesWithTLS.size > 0 &&
|
||||
<ul className="httpsDomains"> {Array.from(addressesWithTLS, address => <li>{address}</li>)} </ul>}
|
||||
</MuiAlert>
|
||||
</Snackbar>);
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import TrafficViewerApiAtom from "../../recoil/TrafficViewerApi";
|
||||
import TrafficViewerApi from "./TrafficViewerApi";
|
||||
import focusedEntryIdAtom from "../../recoil/focusedEntryId";
|
||||
import {toast} from "react-toastify";
|
||||
import {TOAST_CONTAINER_ID} from "../../configs/Consts";
|
||||
import {MAX_ENTRIES, TOAST_CONTAINER_ID} from "../../configs/Consts";
|
||||
import tappingStatusAtom from "../../recoil/tappingStatus";
|
||||
import leftOffTopAtom from "../../recoil/leftOffTop";
|
||||
|
||||
@@ -55,7 +55,7 @@ export const EntriesList: React.FC<EntriesListProps> = ({
|
||||
const [startTime, setStartTime] = useState(0);
|
||||
const [truncatedTimestamp, setTruncatedTimestamp] = useState(0);
|
||||
|
||||
const leftOffBottom = entries.length > 0 ? entries[entries.length - 1].id : -1;
|
||||
const leftOffBottom = entries.length > 0 ? entries[entries.length - 1].id : "latest";
|
||||
|
||||
useEffect(() => {
|
||||
const list = document.getElementById('list').firstElementChild;
|
||||
@@ -76,7 +76,7 @@ export const EntriesList: React.FC<EntriesListProps> = ({
|
||||
|
||||
const getOldEntries = useCallback(async () => {
|
||||
setLoadMoreTop(false);
|
||||
if (leftOffTop === null || leftOffTop <= 0) {
|
||||
if (leftOffTop === "") {
|
||||
return;
|
||||
}
|
||||
setIsLoadingTop(true);
|
||||
@@ -89,7 +89,7 @@ export const EntriesList: React.FC<EntriesListProps> = ({
|
||||
setLeftOffTop(data.meta.leftOff);
|
||||
|
||||
let scrollTo: boolean;
|
||||
if (data.meta.leftOff === 0) {
|
||||
if (data.meta.noMoreData) {
|
||||
setNoMoreDataTop(true);
|
||||
scrollTo = false;
|
||||
} else {
|
||||
@@ -98,6 +98,9 @@ export const EntriesList: React.FC<EntriesListProps> = ({
|
||||
setIsLoadingTop(false);
|
||||
|
||||
const newEntries = [...data.data.reverse(), ...entries];
|
||||
if(newEntries.length > MAX_ENTRIES) {
|
||||
newEntries.splice(MAX_ENTRIES, newEntries.length - MAX_ENTRIES)
|
||||
}
|
||||
setEntries(newEntries);
|
||||
|
||||
setQueriedTotal(data.meta.total);
|
||||
@@ -115,23 +118,28 @@ export const EntriesList: React.FC<EntriesListProps> = ({
|
||||
|
||||
const scrollbarVisible = scrollableRef.current?.childWrapperRef.current.clientHeight > scrollableRef.current?.wrapperRef.current.clientHeight;
|
||||
|
||||
useEffect(() => {
|
||||
if (!focusedEntryId && entries.length > 0)
|
||||
setFocusedEntryId(entries[0].id);
|
||||
}, [focusedEntryId, entries])
|
||||
|
||||
useEffect(() => {
|
||||
const newEntries = [...entries];
|
||||
if (newEntries.length > MAX_ENTRIES) {
|
||||
setLeftOffTop(newEntries[0].id);
|
||||
newEntries.splice(0, newEntries.length - MAX_ENTRIES)
|
||||
setNoMoreDataTop(false);
|
||||
setEntries(newEntries);
|
||||
}
|
||||
}, [entries])
|
||||
|
||||
if (ws.current) {
|
||||
if(ws.current && !ws.current.onmessage) {
|
||||
ws.current.onmessage = (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].id);
|
||||
newEntries.shift();
|
||||
setNoMoreDataTop(false);
|
||||
}
|
||||
setEntries(newEntries);
|
||||
setEntries(entriesState => [...entriesState, message.data]);
|
||||
break;
|
||||
case "status":
|
||||
setTappingStatus(message.tappingStatus);
|
||||
@@ -148,14 +156,12 @@ export const EntriesList: React.FC<EntriesListProps> = ({
|
||||
case "queryMetadata":
|
||||
setTruncatedTimestamp(message.data.truncatedTimestamp);
|
||||
setQueriedTotal(message.data.total);
|
||||
if (leftOffTop === null) {
|
||||
setLeftOffTop(message.data.leftOff - 1);
|
||||
}
|
||||
setLeftOffTop(leftOffState => leftOffState === "" ? message.data.leftOff : leftOffState);
|
||||
break;
|
||||
case "startTime":
|
||||
setStartTime(message.data);
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +183,7 @@ export const EntriesList: React.FC<EntriesListProps> = ({
|
||||
</ScrollableFeedVirtualized>
|
||||
<button type="button"
|
||||
title="Fetch old records"
|
||||
className={`${styles.btnOld} ${!scrollbarVisible && leftOffTop > 0 ? styles.showButton : styles.hideButton}`}
|
||||
className={`${styles.btnOld} ${!scrollbarVisible && leftOffTop !== "" ? styles.showButton : styles.hideButton}`}
|
||||
onClick={(_) => {
|
||||
trafficViewerApi.webSocket.close()
|
||||
getOldEntries();
|
||||
@@ -190,9 +196,9 @@ export const EntriesList: React.FC<EntriesListProps> = ({
|
||||
onClick={(_) => {
|
||||
if (isWsConnectionClosed) {
|
||||
if (query) {
|
||||
openWebSocket(`(${query}) and leftOff(${leftOffBottom})`, false);
|
||||
openWebSocket(`(${query}) and leftOff("${leftOffBottom}")`, false);
|
||||
} else {
|
||||
openWebSocket(`leftOff(${leftOffBottom})`, false);
|
||||
openWebSocket(`leftOff("${leftOffBottom}")`, false);
|
||||
}
|
||||
}
|
||||
scrollableRef.current.jumpToBottom();
|
||||
@@ -203,10 +209,10 @@ export const EntriesList: React.FC<EntriesListProps> = ({
|
||||
</div>
|
||||
|
||||
<div className={styles.footer}>
|
||||
<div>Displaying <b id="entries-length">{entries?.length}</b> results out of <b
|
||||
<div>Displaying <b id="entries-length">{entries?.length > MAX_ENTRIES ? MAX_ENTRIES : entries?.length}</b> results out of <b
|
||||
id="total-entries">{queriedTotal}</b> total
|
||||
</div>
|
||||
{startTime !== 0 && <div>Started listening at <span style={{
|
||||
{startTime !== 0 && <div>First traffic entry time <span style={{
|
||||
marginRight: 5,
|
||||
fontWeight: 600,
|
||||
fontSize: 13
|
||||
|
||||
@@ -89,12 +89,13 @@ const EntryTitle: React.FC<any> = ({ protocol, data, elapsedTime }) => {
|
||||
</div>;
|
||||
};
|
||||
|
||||
const EntrySummary: React.FC<any> = ({ entry }) => {
|
||||
const EntrySummary: React.FC<any> = ({ entry, namespace }) => {
|
||||
return <EntryItem
|
||||
key={`entry-${entry.id}`}
|
||||
entry={entry}
|
||||
style={{}}
|
||||
headingMode={true}
|
||||
namespace={namespace}
|
||||
/>;
|
||||
};
|
||||
|
||||
@@ -140,7 +141,7 @@ export const EntryDetailed = () => {
|
||||
data={entryData.data}
|
||||
elapsedTime={entryData.data.elapsedTime}
|
||||
/>}
|
||||
{!isLoading && entryData && <EntrySummary entry={entryData.base} />}
|
||||
{!isLoading && entryData && <EntrySummary entry={entryData.base} namespace={entryData.data.namespace} />}
|
||||
<React.Fragment>
|
||||
{!isLoading && entryData && <EntryViewer
|
||||
representation={entryData.representation}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import styles from "./EntrySections.module.sass";
|
||||
import React, {useState} from "react";
|
||||
import {SyntaxHighlighter} from "../../UI/SyntaxHighlighter/index";
|
||||
import React, { useState } from "react";
|
||||
import { SyntaxHighlighter } from "../../UI/SyntaxHighlighter/index";
|
||||
import CollapsibleContainer from "../../UI/CollapsibleContainer";
|
||||
import FancyTextDisplay from "../../UI/FancyTextDisplay";
|
||||
import Queryable from "../../UI/Queryable";
|
||||
import Checkbox from "../../UI/Checkbox";
|
||||
import ProtobufDecoder from "protobuf-decoder";
|
||||
import {default as jsonBeautify} from "json-beautify";
|
||||
import {default as xmlBeautify} from "xml-formatter";
|
||||
import { default as jsonBeautify } from "json-beautify";
|
||||
import { default as xmlBeautify } from "xml-formatter";
|
||||
|
||||
interface EntryViewLineProps {
|
||||
label: string;
|
||||
@@ -18,40 +18,40 @@ interface EntryViewLineProps {
|
||||
useTooltip?: boolean;
|
||||
}
|
||||
|
||||
const EntryViewLine: React.FC<EntryViewLineProps> = ({label, value, selector = "", overrideQueryValue = "", displayIconOnMouseOver = true, useTooltip = true}) => {
|
||||
const EntryViewLine: React.FC<EntryViewLineProps> = ({ label, value, selector = "", overrideQueryValue = "", displayIconOnMouseOver = true, useTooltip = true }) => {
|
||||
let query: string;
|
||||
if (!selector) {
|
||||
query = "";
|
||||
} else if (overrideQueryValue) {
|
||||
query = `${selector} == ${overrideQueryValue}`;
|
||||
} else if (typeof(value) == "string") {
|
||||
} else if (typeof (value) == "string") {
|
||||
query = `${selector} == "${JSON.stringify(value).slice(1, -1)}"`;
|
||||
} else {
|
||||
query = `${selector} == ${value}`;
|
||||
}
|
||||
return (label && <tr className={styles.dataLine}>
|
||||
<td className={`${styles.dataKey}`}>
|
||||
<Queryable
|
||||
query={query}
|
||||
style={{float: "right", height: "18px"}}
|
||||
iconStyle={{marginRight: "20px"}}
|
||||
flipped={true}
|
||||
useTooltip={useTooltip}
|
||||
displayIconOnMouseOver={displayIconOnMouseOver}
|
||||
>
|
||||
{label}
|
||||
</Queryable>
|
||||
</td>
|
||||
<td>
|
||||
<FancyTextDisplay
|
||||
className={styles.dataValue}
|
||||
text={value}
|
||||
applyTextEllipsis={false}
|
||||
flipped={true}
|
||||
displayIconOnMouseOver={true}
|
||||
/>
|
||||
</td>
|
||||
</tr>) || null;
|
||||
<td className={`${styles.dataKey}`}>
|
||||
<Queryable
|
||||
query={query}
|
||||
style={{ float: "right", height: "18px" }}
|
||||
iconStyle={{ marginRight: "20px" }}
|
||||
flipped={true}
|
||||
useTooltip={useTooltip}
|
||||
displayIconOnMouseOver={displayIconOnMouseOver}
|
||||
>
|
||||
{label}
|
||||
</Queryable>
|
||||
</td>
|
||||
<td>
|
||||
<FancyTextDisplay
|
||||
className={styles.dataValue}
|
||||
text={value}
|
||||
applyTextEllipsis={false}
|
||||
flipped={true}
|
||||
displayIconOnMouseOver={true}
|
||||
/>
|
||||
</td>
|
||||
</tr>) || null;
|
||||
}
|
||||
|
||||
|
||||
@@ -63,11 +63,11 @@ interface EntrySectionCollapsibleTitleProps {
|
||||
query?: string,
|
||||
}
|
||||
|
||||
const EntrySectionCollapsibleTitle: React.FC<EntrySectionCollapsibleTitleProps> = ({title, color, expanded, setExpanded, query = ""}) => {
|
||||
const EntrySectionCollapsibleTitle: React.FC<EntrySectionCollapsibleTitleProps> = ({ title, color, expanded, setExpanded, query = "" }) => {
|
||||
return <div className={styles.title}>
|
||||
<div
|
||||
className={`${styles.button} ${expanded ? styles.expanded : ''}`}
|
||||
style={{backgroundColor: color}}
|
||||
style={{ backgroundColor: color }}
|
||||
onClick={() => {
|
||||
setExpanded(!expanded)
|
||||
}}
|
||||
@@ -90,12 +90,12 @@ interface EntrySectionContainerProps {
|
||||
query?: string,
|
||||
}
|
||||
|
||||
export const EntrySectionContainer: React.FC<EntrySectionContainerProps> = ({title, color, children, query = ""}) => {
|
||||
export const EntrySectionContainer: React.FC<EntrySectionContainerProps> = ({ title, color, children, query = "" }) => {
|
||||
const [expanded, setExpanded] = useState(true);
|
||||
return <CollapsibleContainer
|
||||
className={styles.collapsibleContainer}
|
||||
expanded={expanded}
|
||||
title={<EntrySectionCollapsibleTitle title={title} color={color} expanded={expanded} setExpanded={setExpanded} query={query}/>}
|
||||
title={<EntrySectionCollapsibleTitle title={title} color={color} expanded={expanded} setExpanded={setExpanded} query={query} />}
|
||||
>
|
||||
{children}
|
||||
</CollapsibleContainer>
|
||||
@@ -130,6 +130,7 @@ export const EntryBodySection: React.FC<EntryBodySectionProps> = ({
|
||||
|
||||
const isBase64Encoding = encoding === 'base64';
|
||||
const supportsPrettying = supportedFormats.some(format => contentType?.indexOf(format) > -1);
|
||||
const [isDecodeGrpc, setIsDecodeGrpc] = useState(true);
|
||||
|
||||
const formatTextBody = (body: any): string => {
|
||||
if (!decodeBase64) return body;
|
||||
@@ -141,7 +142,7 @@ export const EntryBodySection: React.FC<EntryBodySectionProps> = ({
|
||||
if (jsonLikeFormats.some(format => contentType?.indexOf(format) > -1)) {
|
||||
if (!isPretty) return bodyBuf;
|
||||
return jsonBeautify(JSON.parse(bodyBuf), null, 2, 80);
|
||||
} else if (xmlLikeFormats.some(format => contentType?.indexOf(format) > -1)) {
|
||||
} else if (xmlLikeFormats.some(format => contentType?.indexOf(format) > -1)) {
|
||||
if (!isPretty) return bodyBuf;
|
||||
return xmlBeautify(bodyBuf, {
|
||||
indentation: ' ',
|
||||
@@ -157,49 +158,55 @@ export const EntryBodySection: React.FC<EntryBodySectionProps> = ({
|
||||
return jsonBeautify(protobufDecoded, null, 2, 80);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
if(isDecodeGrpc)
|
||||
setIsDecodeGrpc(false);
|
||||
if (!String(error).includes("More than one message in")){
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
return bodyBuf;
|
||||
}
|
||||
|
||||
return <React.Fragment>
|
||||
{content && content?.length > 0 && <EntrySectionContainer
|
||||
title={title}
|
||||
color={color}
|
||||
query={`${selector} == r".*"`}
|
||||
>
|
||||
<div style={{display: 'flex', alignItems: 'center', alignContent: 'center', margin: "5px 0"}}>
|
||||
{supportsPrettying && <div style={{paddingTop: 3}}>
|
||||
<Checkbox checked={isPretty} onToggle={() => {setIsPretty(!isPretty)}}/>
|
||||
title={title}
|
||||
color={color}
|
||||
query={`${selector} == r".*"`}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', alignContent: 'center', margin: "5px 0" }}>
|
||||
{supportsPrettying && <div style={{ paddingTop: 3 }}>
|
||||
<Checkbox checked={isPretty} onToggle={() => { setIsPretty(!isPretty) }} />
|
||||
</div>}
|
||||
{supportsPrettying && <span style={{marginLeft: '.2rem'}}>Pretty</span>}
|
||||
{supportsPrettying && <span style={{ marginLeft: '.2rem' }}>Pretty</span>}
|
||||
|
||||
<div style={{paddingTop: 3, paddingLeft: supportsPrettying ? 20 : 0}}>
|
||||
<Checkbox checked={showLineNumbers} onToggle={() => {setShowLineNumbers(!showLineNumbers)}}/>
|
||||
<div style={{ paddingTop: 3, paddingLeft: supportsPrettying ? 20 : 0 }}>
|
||||
<Checkbox checked={showLineNumbers} onToggle={() => { setShowLineNumbers(!showLineNumbers) }} />
|
||||
</div>
|
||||
<span style={{marginLeft: '.2rem'}}>Line numbers</span>
|
||||
|
||||
{isBase64Encoding && <div style={{paddingTop: 3, paddingLeft: 20}}>
|
||||
<Checkbox checked={decodeBase64} onToggle={() => {setDecodeBase64(!decodeBase64)}}/>
|
||||
<span style={{ marginLeft: '.2rem' }}>Line numbers</span>
|
||||
{isBase64Encoding && <div style={{ paddingTop: 3, paddingLeft: 20 }}>
|
||||
<Checkbox checked={decodeBase64} onToggle={() => { setDecodeBase64(!decodeBase64) }} />
|
||||
</div>}
|
||||
{isBase64Encoding && <span style={{marginLeft: '.2rem'}}>Decode Base64</span>}
|
||||
</div>
|
||||
{isBase64Encoding && <span style={{ marginLeft: '.2rem' }}>Decode Base64</span>}
|
||||
{!isDecodeGrpc && <span style={{ fontSize: '12px', color: '#DB2156', marginLeft: '.8rem' }}>More than one message in protobuf payload is not supported</span>}
|
||||
</div>
|
||||
|
||||
<SyntaxHighlighter
|
||||
<SyntaxHighlighter
|
||||
code={formatTextBody(content)}
|
||||
showLineNumbers={showLineNumbers}
|
||||
/>
|
||||
|
||||
</EntrySectionContainer>}
|
||||
</React.Fragment>
|
||||
}
|
||||
|
||||
|
||||
interface EntrySectionProps {
|
||||
title: string,
|
||||
color: string,
|
||||
arrayToIterate: any[],
|
||||
}
|
||||
|
||||
export const EntryTableSection: React.FC<EntrySectionProps> = ({title, color, arrayToIterate}) => {
|
||||
export const EntryTableSection: React.FC<EntrySectionProps> = ({ title, color, arrayToIterate }) => {
|
||||
let arrayToIterateSorted: any[];
|
||||
if (arrayToIterate) {
|
||||
arrayToIterateSorted = arrayToIterate.sort((a, b) => {
|
||||
@@ -220,7 +227,7 @@ export const EntryTableSection: React.FC<EntrySectionProps> = ({title, color, ar
|
||||
<EntrySectionContainer title={title} color={color}>
|
||||
<table>
|
||||
<tbody id={`tbody-${title}`}>
|
||||
{arrayToIterateSorted.map(({name, value, selector}, index) => <EntryViewLine
|
||||
{arrayToIterateSorted.map(({ name, value, selector }, index) => <EntryViewLine
|
||||
key={index}
|
||||
label={name}
|
||||
value={value}
|
||||
@@ -228,7 +235,7 @@ export const EntryTableSection: React.FC<EntrySectionProps> = ({title, color, ar
|
||||
/>)}
|
||||
</tbody>
|
||||
</table>
|
||||
</EntrySectionContainer> : <span/>
|
||||
</EntrySectionContainer> : <span />
|
||||
}
|
||||
</React.Fragment>
|
||||
}
|
||||
@@ -247,7 +254,7 @@ interface EntryPolicySectionCollapsibleTitleProps {
|
||||
setExpanded: any;
|
||||
}
|
||||
|
||||
const EntryPolicySectionCollapsibleTitle: React.FC<EntryPolicySectionCollapsibleTitleProps> = ({label, matched, expanded, setExpanded}) => {
|
||||
const EntryPolicySectionCollapsibleTitle: React.FC<EntryPolicySectionCollapsibleTitleProps> = ({ label, matched, expanded, setExpanded }) => {
|
||||
return <div className={styles.title}>
|
||||
<span
|
||||
className={`${styles.button}
|
||||
@@ -260,8 +267,8 @@ const EntryPolicySectionCollapsibleTitle: React.FC<EntryPolicySectionCollapsible
|
||||
</span>
|
||||
<span>
|
||||
<tr className={styles.dataLine}>
|
||||
<td className={`${styles.dataKey} ${styles.rulesTitleSuccess}`}>{label}</td>
|
||||
<td className={`${styles.dataKey} ${matched === 'Success' ? styles.rulesMatchedSuccess : styles.rulesMatchedFailure}`}>{matched}</td>
|
||||
<td className={`${styles.dataKey} ${styles.rulesTitleSuccess}`}>{label}</td>
|
||||
<td className={`${styles.dataKey} ${matched === 'Success' ? styles.rulesMatchedSuccess : styles.rulesMatchedFailure}`}>{matched}</td>
|
||||
</tr>
|
||||
</span>
|
||||
</div>
|
||||
@@ -273,28 +280,28 @@ interface EntryPolicySectionContainerProps {
|
||||
children?: any;
|
||||
}
|
||||
|
||||
export const EntryPolicySectionContainer: React.FC<EntryPolicySectionContainerProps> = ({label, matched, children}) => {
|
||||
export const EntryPolicySectionContainer: React.FC<EntryPolicySectionContainerProps> = ({ label, matched, children }) => {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
return <CollapsibleContainer
|
||||
className={styles.collapsibleContainer}
|
||||
expanded={expanded}
|
||||
title={<EntryPolicySectionCollapsibleTitle label={label} matched={matched} expanded={expanded} setExpanded={setExpanded}/>}
|
||||
title={<EntryPolicySectionCollapsibleTitle label={label} matched={matched} expanded={expanded} setExpanded={setExpanded} />}
|
||||
>
|
||||
{children}
|
||||
</CollapsibleContainer>
|
||||
}
|
||||
|
||||
export const EntryTablePolicySection: React.FC<EntryPolicySectionProps> = ({title, color, latency, arrayToIterate}) => {
|
||||
export const EntryTablePolicySection: React.FC<EntryPolicySectionProps> = ({ title, color, latency, arrayToIterate }) => {
|
||||
return <React.Fragment>
|
||||
{
|
||||
arrayToIterate && arrayToIterate.length > 0 ?
|
||||
<React.Fragment>
|
||||
<EntrySectionContainer title={title} color={color}>
|
||||
<table>
|
||||
<tbody>
|
||||
{arrayToIterate.map(({rule, matched}, index) => {
|
||||
<EntrySectionContainer title={title} color={color}>
|
||||
<table>
|
||||
<tbody>
|
||||
{arrayToIterate.map(({ rule, matched }, index) => {
|
||||
return (
|
||||
<EntryPolicySectionContainer key={index} label={rule.Name} matched={matched && (rule.Type === 'slo' ? rule.ResponseTime >= latency : true)? "Success" : "Failure"}>
|
||||
<EntryPolicySectionContainer key={index} label={rule.Name} matched={matched && (rule.Type === 'slo' ? rule.ResponseTime >= latency : true) ? "Success" : "Failure"}>
|
||||
{
|
||||
<React.Fragment>
|
||||
{
|
||||
@@ -330,11 +337,11 @@ export const EntryTablePolicySection: React.FC<EntryPolicySectionProps> = ({titl
|
||||
</EntryPolicySectionContainer>
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</EntrySectionContainer>
|
||||
)
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</EntrySectionContainer>
|
||||
</React.Fragment> : <span className={styles.noRules}>No rules could be applied to this request.</span>
|
||||
}
|
||||
</React.Fragment>
|
||||
@@ -347,7 +354,7 @@ interface EntryContractSectionProps {
|
||||
contractContent: string,
|
||||
}
|
||||
|
||||
export const EntryContractSection: React.FC<EntryContractSectionProps> = ({color, requestReason, responseReason, contractContent}) => {
|
||||
export const EntryContractSection: React.FC<EntryContractSectionProps> = ({ color, requestReason, responseReason, contractContent }) => {
|
||||
return <React.Fragment>
|
||||
{requestReason && <EntrySectionContainer title="Request" color={color}>
|
||||
{requestReason}
|
||||
|
||||
@@ -67,7 +67,6 @@
|
||||
|
||||
.capture img
|
||||
height: 14px
|
||||
z-index: 1000
|
||||
margin-top: 12px
|
||||
margin-left: -2px
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ interface EntryProps {
|
||||
entry: Entry;
|
||||
style: object;
|
||||
headingMode: boolean;
|
||||
namespace?: string;
|
||||
}
|
||||
|
||||
enum CaptureTypes {
|
||||
@@ -62,11 +63,11 @@ enum CaptureTypes {
|
||||
Ebpf = "ebpf",
|
||||
}
|
||||
|
||||
export const EntryItem: React.FC<EntryProps> = ({entry, style, headingMode}) => {
|
||||
export const EntryItem: React.FC<EntryProps> = ({entry, style, headingMode, namespace}) => {
|
||||
|
||||
const [focusedEntryId, setFocusedEntryId] = useRecoilState(focusedEntryIdAtom);
|
||||
const [queryState, setQuery] = useRecoilState(queryAtom);
|
||||
const isSelected = focusedEntryId === entry.id.toString();
|
||||
const isSelected = focusedEntryId === entry.id;
|
||||
|
||||
const classification = getClassification(entry.status)
|
||||
const numberOfRules = entry.rules.numberOfRules
|
||||
@@ -143,12 +144,12 @@ export const EntryItem: React.FC<EntryProps> = ({entry, style, headingMode}) =>
|
||||
|
||||
return <React.Fragment>
|
||||
<div
|
||||
id={`entry-${entry.id.toString()}`}
|
||||
id={`entry-${entry.id}`}
|
||||
className={`${styles.row}
|
||||
${isSelected && !rule && !contractEnabled ? styles.rowSelected : additionalRulesProperties}`}
|
||||
onClick={() => {
|
||||
if (!setFocusedEntryId) return;
|
||||
setFocusedEntryId(entry.id.toString());
|
||||
setFocusedEntryId(entry.id);
|
||||
}}
|
||||
style={{
|
||||
border: isSelected && !headingMode ? `1px ${entry.proto.backgroundColor} solid` : "1px transparent solid",
|
||||
@@ -224,6 +225,19 @@ export const EntryItem: React.FC<EntryProps> = ({entry, style, headingMode}) =>
|
||||
: ""
|
||||
}
|
||||
<div className={styles.separatorRight}>
|
||||
{headingMode ? <Queryable
|
||||
query={`namespace == "${namespace}"`}
|
||||
displayIconOnMouseOver={true}
|
||||
flipped={true}
|
||||
iconStyle={{marginRight: "16px"}}
|
||||
>
|
||||
<span
|
||||
className={`${styles.tcpInfo} ${styles.ip}`}
|
||||
title="Namespace"
|
||||
>
|
||||
{namespace}
|
||||
</span>
|
||||
</Queryable> : null}
|
||||
<Queryable
|
||||
query={`src.ip == "${entry.src.ip}"`}
|
||||
displayIconOnMouseOver={true}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
flex-direction: column
|
||||
overflow: hidden
|
||||
flex-grow: 1
|
||||
height: calc(100vh - 70px)
|
||||
height: calc(100% - 70px)
|
||||
|
||||
.TrafficPageHeader
|
||||
padding: 20px 24px
|
||||
@@ -16,9 +16,8 @@
|
||||
justify-content: space-between
|
||||
|
||||
.TrafficPageStreamStatus
|
||||
display: flex
|
||||
align-items: center
|
||||
|
||||
display: flex
|
||||
align-items: center
|
||||
|
||||
.TrafficPageHeaderImage
|
||||
width: 22px
|
||||
@@ -113,4 +112,4 @@
|
||||
.playPauseIcon
|
||||
cursor: pointer
|
||||
margin-right: 15px
|
||||
height: 30px
|
||||
height: 30px
|
||||
|
||||
@@ -14,13 +14,13 @@ import {RecoilRoot, RecoilState, useRecoilState, useRecoilValue, useSetRecoilSta
|
||||
import entriesAtom from "../../recoil/entries";
|
||||
import focusedEntryIdAtom from "../../recoil/focusedEntryId";
|
||||
import queryAtom from "../../recoil/query";
|
||||
import {TLSWarning} from "../TLSWarning/TLSWarning";
|
||||
import trafficViewerApiAtom from "../../recoil/TrafficViewerApi"
|
||||
import TrafficViewerApi from "./TrafficViewerApi";
|
||||
import {StatusBar} from "../UI/StatusBar";
|
||||
import tappingStatusAtom from "../../recoil/tappingStatus/atom";
|
||||
import {TOAST_CONTAINER_ID} from "../../configs/Consts";
|
||||
import leftOffTopAtom from "../../recoil/leftOffTop";
|
||||
import { DEFAULT_QUERY } from '../../hooks/useWS';
|
||||
|
||||
const useLayoutStyles = makeStyles(() => ({
|
||||
details: {
|
||||
@@ -49,14 +49,15 @@ interface TrafficViewerProps {
|
||||
actionButtons?: JSX.Element,
|
||||
isShowStatusBar?: boolean,
|
||||
webSocketUrl: string,
|
||||
isCloseWebSocket: boolean,
|
||||
shouldCloseWebSocket: boolean,
|
||||
setShouldCloseWebSocket: (flag: boolean) => void,
|
||||
isDemoBannerView: boolean
|
||||
}
|
||||
|
||||
export const TrafficViewer: React.FC<TrafficViewerProps> = ({
|
||||
setAnalyzeStatus, trafficViewerApiProp,
|
||||
actionButtons, isShowStatusBar, webSocketUrl,
|
||||
isCloseWebSocket, isDemoBannerView
|
||||
shouldCloseWebSocket, setShouldCloseWebSocket, isDemoBannerView
|
||||
}) => {
|
||||
|
||||
const classes = useLayoutStyles();
|
||||
@@ -75,10 +76,6 @@ export const TrafficViewer: React.FC<TrafficViewerProps> = ({
|
||||
const setLeftOffTop = useSetRecoilState(leftOffTopAtom);
|
||||
const scrollableRef = useRef(null);
|
||||
|
||||
const [showTLSWarning, setShowTLSWarning] = useState(false);
|
||||
const [userDismissedTLSWarning, setUserDismissedTLSWarning] = useState(false);
|
||||
const [addressesWithTLS, setAddressesWithTLS] = useState(new Set<string>());
|
||||
|
||||
const handleQueryChange = useMemo(
|
||||
() =>
|
||||
debounce(async (query: string) => {
|
||||
@@ -104,8 +101,11 @@ export const TrafficViewer: React.FC<TrafficViewerProps> = ({
|
||||
}, [query, handleQueryChange]);
|
||||
|
||||
useEffect(() => {
|
||||
isCloseWebSocket && closeWebSocket()
|
||||
}, [isCloseWebSocket])
|
||||
if(shouldCloseWebSocket){
|
||||
closeWebSocket()
|
||||
setShouldCloseWebSocket(false);
|
||||
}
|
||||
}, [shouldCloseWebSocket])
|
||||
|
||||
useEffect(() => {
|
||||
reopenConnection()
|
||||
@@ -115,9 +115,9 @@ export const TrafficViewer: React.FC<TrafficViewerProps> = ({
|
||||
|
||||
const openEmptyWebSocket = () => {
|
||||
if (query) {
|
||||
openWebSocket(`(${query}) and leftOff(-1)`, true);
|
||||
openWebSocket(`(${query}) and ${DEFAULT_QUERY}`, true);
|
||||
} else {
|
||||
openWebSocket(`leftOff(-1)`, true);
|
||||
openWebSocket(DEFAULT_QUERY, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ export const TrafficViewer: React.FC<TrafficViewerProps> = ({
|
||||
if (resetEntries) {
|
||||
setFocusedEntryId(null);
|
||||
setEntries([]);
|
||||
setLeftOffTop(null);
|
||||
setLeftOffTop("");
|
||||
setNoMoreDataTop(false);
|
||||
}
|
||||
try {
|
||||
@@ -201,7 +201,9 @@ export const TrafficViewer: React.FC<TrafficViewerProps> = ({
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
ws.current.close();
|
||||
if (ws?.current?.readyState === WebSocket.OPEN) {
|
||||
ws.current.close();
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
@@ -279,12 +281,6 @@ export const TrafficViewer: React.FC<TrafficViewerProps> = ({
|
||||
<EntryDetailed/>
|
||||
</div>
|
||||
</div>}
|
||||
<TLSWarning showTLSWarning={showTLSWarning}
|
||||
setShowTLSWarning={setShowTLSWarning}
|
||||
addressesWithTLS={addressesWithTLS}
|
||||
setAddressesWithTLS={setAddressesWithTLS}
|
||||
userDismissedTLSWarning={userDismissedTLSWarning}
|
||||
setUserDismissedTLSWarning={setUserDismissedTLSWarning}/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -293,11 +289,11 @@ const MemoiedTrafficViewer = React.memo(TrafficViewer)
|
||||
const TrafficViewerContainer: React.FC<TrafficViewerProps> = ({
|
||||
setAnalyzeStatus, trafficViewerApiProp,
|
||||
actionButtons, isShowStatusBar = true,
|
||||
webSocketUrl, isCloseWebSocket, isDemoBannerView
|
||||
webSocketUrl, shouldCloseWebSocket, setShouldCloseWebSocket, isDemoBannerView
|
||||
}) => {
|
||||
return <RecoilRoot>
|
||||
<MemoiedTrafficViewer actionButtons={actionButtons} isShowStatusBar={isShowStatusBar} webSocketUrl={webSocketUrl}
|
||||
isCloseWebSocket={isCloseWebSocket} trafficViewerApiProp={trafficViewerApiProp}
|
||||
shouldCloseWebSocket={shouldCloseWebSocket} setShouldCloseWebSocket={setShouldCloseWebSocket} trafficViewerApiProp={trafficViewerApiProp}
|
||||
setAnalyzeStatus={setAnalyzeStatus} isDemoBannerView={isDemoBannerView}/>
|
||||
<ToastContainer enableMultiContainer containerId={TOAST_CONTAINER_ID}
|
||||
position="bottom-right"
|
||||
|
||||
@@ -4,7 +4,6 @@ type TrafficViewerApi = {
|
||||
analyzeStatus: () => any
|
||||
fetchEntries: (leftOff: any, direction: number, query: any, limit: number, timeoutMs: number) => any
|
||||
getEntry: (entryId: any, query: string) => any
|
||||
getRecentTLSLinks: () => any,
|
||||
webSocket: {
|
||||
close: () => void
|
||||
}
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
import React, { CSSProperties } from "react";
|
||||
import React from "react";
|
||||
import styles from "./style/InformationIcon.module.sass"
|
||||
|
||||
const DEFUALT_LINK = "https://getmizu.io/docs"
|
||||
|
||||
export interface InformationIconProps {
|
||||
interface LinkProps {
|
||||
link?: string,
|
||||
style?: CSSProperties
|
||||
className?: string
|
||||
title?: string
|
||||
}
|
||||
|
||||
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>
|
||||
export const Link: React.FC<LinkProps> = ({ link, className, title, children }) => {
|
||||
return <a href={link} className={className} title={title} target="_blank">
|
||||
{children}
|
||||
</a>
|
||||
}
|
||||
|
||||
|
||||
export const InformationIcon: React.FC<LinkProps> = ({ className }) => {
|
||||
return <Link title="documentation" className={`${styles.linkStyle} ${className}`} link={DEFUALT_LINK}>
|
||||
<span>Docs</span>
|
||||
</Link>
|
||||
}
|
||||
59
ui-common/src/components/UI/Resizeable.tsx
Normal file
59
ui-common/src/components/UI/Resizeable.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import React, { useRef, useState } from "react";
|
||||
|
||||
import styles from './style/Resizeable.module.sass'
|
||||
|
||||
export interface Props {
|
||||
children
|
||||
minWidth: number
|
||||
maxWidth?: number
|
||||
}
|
||||
|
||||
const Resizeable: React.FC<Props> = ({ children, minWidth, maxWidth }) => {
|
||||
const resizeble = useRef(null)
|
||||
let mousePos = { x: 0, y: 0 }
|
||||
let elementDimention = { w: 0, h: 0 }
|
||||
let isPressed = false
|
||||
const [elemWidth, setElemWidth] = useState(resizeble?.current?.style?.width)
|
||||
|
||||
const mouseDownHandler = function (e) {
|
||||
// Get the current mouse position
|
||||
mousePos = { x: e.clientX, y: e.clientY }
|
||||
isPressed = true
|
||||
|
||||
// Calculate the dimension of element
|
||||
const styles = resizeble.current.getBoundingClientRect();
|
||||
elementDimention = { w: parseInt(styles.width, 10), h: parseInt(styles.height, 10) }
|
||||
// Attach the listeners to `document`
|
||||
window.addEventListener('mousemove', mouseMoveHandler);
|
||||
window.addEventListener('mouseup', mouseUpHandler);
|
||||
};
|
||||
|
||||
|
||||
const mouseMoveHandler = function (e) {
|
||||
if (isPressed) {
|
||||
// How far the mouse has been moved
|
||||
const dx = e.clientX - mousePos.x;
|
||||
const widthEl = elementDimention.w + dx
|
||||
|
||||
if (widthEl >= minWidth)
|
||||
// Adjust the dimension of element
|
||||
setElemWidth(widthEl)
|
||||
}
|
||||
};
|
||||
|
||||
const mouseUpHandler = function () {
|
||||
window.removeEventListener('mousemove', mouseMoveHandler);
|
||||
window.removeEventListener('mouseup', mouseUpHandler);
|
||||
isPressed = false
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.resizable} ref={resizeble} style={{ width: elemWidth, maxWidth: maxWidth }}>
|
||||
{children}
|
||||
<div className={`${styles.resizer} ${styles.resizerRight}`} onMouseDown={mouseDownHandler}></div>
|
||||
{/* <div className={`${styles.resizer} ${styles.resizerB}`} onMouseDown={mouseDownHandler}></div> -- FutureUse*/}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Resizeable;
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useMemo } from "react";
|
||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import Radio from "./Radio";
|
||||
import styles from './style/SelectList.module.sass'
|
||||
import NoDataMessage from "./NoDataMessage";
|
||||
@@ -19,12 +19,16 @@ export interface Props {
|
||||
const SelectList: React.FC<Props> = ({ items, tableName, checkedValues = [], multiSelect = true, searchValue = "", setCheckedValues, tableClassName,
|
||||
checkBoxWidth = 50 }) => {
|
||||
const noItemsMessage = "No items to show";
|
||||
const enabledItemsLength = useMemo(() => items.filter(item => !item.disabled).length, [items]);
|
||||
const [headerChecked, setHeaderChecked] = useState(false)
|
||||
|
||||
const filteredValues = useMemo(() => {
|
||||
return items.filter((listValue) => listValue?.value?.includes(searchValue));
|
||||
}, [items, searchValue])
|
||||
|
||||
const filteredValuesKeys = useMemo(() => {
|
||||
return filteredValues.map(x => x.key)
|
||||
}, [filteredValues])
|
||||
|
||||
const toggleValue = (checkedKey) => {
|
||||
if (!multiSelect) {
|
||||
const newCheckedValues = [];
|
||||
@@ -34,25 +38,31 @@ const SelectList: React.FC<Props> = ({ items, tableName, checkedValues = [], mul
|
||||
else {
|
||||
const newCheckedValues = [...checkedValues];
|
||||
let index = newCheckedValues.indexOf(checkedKey);
|
||||
|
||||
if (index > -1)
|
||||
newCheckedValues.splice(index, 1);
|
||||
else
|
||||
newCheckedValues.push(checkedKey);
|
||||
|
||||
setCheckedValues(newCheckedValues);
|
||||
}
|
||||
}
|
||||
|
||||
const toggleAll = () => {
|
||||
const newCheckedValues = [...checkedValues];
|
||||
if (newCheckedValues.length === enabledItemsLength) setCheckedValues([]);
|
||||
else {
|
||||
items.forEach((obj) => {
|
||||
if (!obj.disabled && !newCheckedValues.includes(obj.key))
|
||||
newCheckedValues.push(obj.key);
|
||||
})
|
||||
setCheckedValues(newCheckedValues);
|
||||
useEffect(() => {
|
||||
const setAllChecked = filteredValuesKeys.every(val => checkedValues.includes(val))
|
||||
setHeaderChecked(setAllChecked)
|
||||
}, [filteredValuesKeys, checkedValues])
|
||||
|
||||
const toggleAll = useCallback((shouldCheckAll) => {
|
||||
let newChecked = checkedValues.filter(x => !filteredValuesKeys.includes(x))
|
||||
|
||||
if (shouldCheckAll) {
|
||||
const disabledItems = items.filter(i => i.disabled).map(x => x.key)
|
||||
newChecked = [...filteredValuesKeys, ...newChecked].filter(x => !disabledItems.includes(x))
|
||||
}
|
||||
}
|
||||
|
||||
setCheckedValues(newChecked)
|
||||
}, [searchValue, checkedValues, filteredValuesKeys])
|
||||
|
||||
const dataFieldFunc = (listValue) => listValue.component ? listValue.component :
|
||||
<span className={styles.nowrap} title={listValue.value}>
|
||||
@@ -60,8 +70,8 @@ const SelectList: React.FC<Props> = ({ items, tableName, checkedValues = [], mul
|
||||
</span>
|
||||
|
||||
const tableHead = multiSelect ? <tr style={{ borderBottomWidth: "2px" }}>
|
||||
<th style={{ width: checkBoxWidth }}><Checkbox data-cy="checkbox-all" checked={enabledItemsLength === checkedValues.length}
|
||||
onToggle={toggleAll} /></th>
|
||||
<th style={{ width: checkBoxWidth }}><Checkbox data-cy="checkbox-all" checked={headerChecked}
|
||||
onToggle={(isChecked) => toggleAll(isChecked)} /></th>
|
||||
<th>{tableName}</th>
|
||||
</tr> :
|
||||
<tr style={{ borderBottomWidth: "2px" }}>
|
||||
@@ -70,7 +80,7 @@ const SelectList: React.FC<Props> = ({ items, tableName, checkedValues = [], mul
|
||||
|
||||
const tableBody = filteredValues.length === 0 ?
|
||||
<tr>
|
||||
<td>
|
||||
<td colSpan={2}>
|
||||
<NoDataMessage messageText={noItemsMessage} />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -37,9 +37,9 @@ export function getClassification(statusCode: number): string {
|
||||
|
||||
// 1 - 16 HTTP/2 (gRPC) status codes
|
||||
// 2xx - 5xx HTTP/1.x status codes
|
||||
if ((statusCode >= 200 && statusCode <= 399) || statusCode === 0) {
|
||||
if (statusCode >= 200 && statusCode <= 399) {
|
||||
classification = StatusCodeClassification.SUCCESS;
|
||||
} else if (statusCode >= 400 || (statusCode >= 1 && statusCode <= 16)) {
|
||||
} else if (statusCode >= 400) {
|
||||
classification = StatusCodeClassification.FAILURE;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@ import Tooltip from "./Tooltip";
|
||||
import Checkbox from "./Checkbox"
|
||||
import { StatusBar } from "./StatusBar";
|
||||
import CustomModal from "./CustomModal";
|
||||
import { InformationIcon } from "./InformationIcon";
|
||||
import { InformationIcon, Link } from "./InformationIcon";
|
||||
import SelectList from "./SelectList";
|
||||
import NoDataMessage from "./NoDataMessage";
|
||||
|
||||
|
||||
export { LoadingOverlay, Select, Tabs, Tooltip, Checkbox, CustomModal, InformationIcon, SelectList, NoDataMessage }
|
||||
export { LoadingOverlay, Select, Tabs, Tooltip, Checkbox, CustomModal, InformationIcon, SelectList, NoDataMessage, Link }
|
||||
export { StatusBar }
|
||||
29
ui-common/src/components/UI/style/Resizeable.module.sass
Normal file
29
ui-common/src/components/UI/style/Resizeable.module.sass
Normal file
@@ -0,0 +1,29 @@
|
||||
@import "../../../variables.module"
|
||||
|
||||
.resizable
|
||||
position: relative
|
||||
align-items: center
|
||||
display: flex
|
||||
overflow: hidden
|
||||
border-right: 1px solid $blue-color
|
||||
height: 100%
|
||||
width: 100%
|
||||
padding-right: 3px
|
||||
|
||||
.resizer
|
||||
position: absolute
|
||||
|
||||
&Right
|
||||
cursor: col-resize
|
||||
height: 100%
|
||||
right: 0
|
||||
top: 0
|
||||
width: 5px
|
||||
|
||||
// FutureUse
|
||||
&Bottom
|
||||
bottom: 0
|
||||
cursor: row-resize
|
||||
height: 5px
|
||||
left: 0
|
||||
width: 100%
|
||||
@@ -1,25 +1,27 @@
|
||||
@import '../../../variables.module'
|
||||
|
||||
.selectListTable
|
||||
overflow: auto
|
||||
height: 100%
|
||||
user-select: none // when resizble moved we get unwanted beheviour
|
||||
|
||||
table
|
||||
width: 100%
|
||||
margin-top: 20px
|
||||
height: 100%
|
||||
|
||||
tbody
|
||||
display: block
|
||||
border-collapse: collapse
|
||||
|
||||
th
|
||||
color: $blue-gray
|
||||
text-align: left
|
||||
padding: 10px
|
||||
position: sticky
|
||||
top: 0
|
||||
background: $main-background-color
|
||||
|
||||
tr
|
||||
border-bottom-width: 1px
|
||||
border-bottom-color: $data-background-color
|
||||
border-bottom-style: solid
|
||||
display: table
|
||||
table-layout: fixed
|
||||
width: 100%
|
||||
|
||||
td
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
align-items: center
|
||||
justify-content: center
|
||||
background-color: rgba(25, 25, 25, 0.5)
|
||||
z-index: 1000
|
||||
|
||||
.loadingOverlaySpinner
|
||||
width: 60px
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export const TOAST_CONTAINER_ID = "Common";
|
||||
export const TOAST_CONTAINER_ID = "Common";
|
||||
export const MAX_ENTRIES = 10000;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user