mirror of
https://github.com/kubeshark/kubeshark.git
synced 2026-02-15 02:19:54 +00:00
Compare commits
37 Commits
33.0-dev0
...
33.0-dev34
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45b368b33e | ||
|
|
8f64fdaa61 | ||
|
|
7edb0b153b | ||
|
|
569a687fdf | ||
|
|
11e8b5eb65 | ||
|
|
3901f3f3fe | ||
|
|
2f1cc21fcb | ||
|
|
433253a27b | ||
|
|
00cc94fbe5 | ||
|
|
8feef78ab1 | ||
|
|
992abc99bc | ||
|
|
486d0b1088 | ||
|
|
f61a02d288 | ||
|
|
03694e57c0 | ||
|
|
1760afda2b | ||
|
|
522e2cc3da | ||
|
|
ab38f4c011 | ||
|
|
a9de4f0bba | ||
|
|
948af518b5 | ||
|
|
73448b514e | ||
|
|
fc194354bc | ||
|
|
8418802c7e | ||
|
|
bfa834e840 | ||
|
|
5c012641a5 | ||
|
|
74bd4b180f | ||
|
|
8ea2dabb34 | ||
|
|
366d34b8d0 | ||
|
|
5fc3e38c1a | ||
|
|
09a0fca2c2 | ||
|
|
0437586908 | ||
|
|
f8181ccb07 | ||
|
|
414e5cfe5a | ||
|
|
2fac0009ea | ||
|
|
36d59ede07 | ||
|
|
ac53508ad7 | ||
|
|
e24c18254c | ||
|
|
b2830f133f |
12
.github/workflows/release.yml
vendored
12
.github/workflows/release.yml
vendored
@@ -290,3 +290,15 @@ jobs:
|
||||
tag: ${{ steps.versioning.outputs.version }}
|
||||
prerelease: ${{ github.ref != 'refs/heads/main' }}
|
||||
bodyFile: 'cli/bin/README.md'
|
||||
|
||||
- name: Slack notification on failure
|
||||
uses: ravsamhq/notify-slack-action@v1
|
||||
if: always()
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
notification_title: 'Mizu enterprise {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.author.name }} <${{ github.event.head_commit.author.email }}> ```${{ github.event.head_commit.message }}```'
|
||||
footer: 'Linked Repo <{repo_url}|{repo}>'
|
||||
notify_when: 'failure'
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -55,4 +55,4 @@ tap/extensions/*/expect
|
||||
*.editorconfig
|
||||
|
||||
# Ignore *.log files
|
||||
*.log
|
||||
*.log
|
||||
|
||||
@@ -68,6 +68,7 @@ COPY shared/go.mod shared/go.mod ../shared/
|
||||
COPY logger/go.mod logger/go.mod ../logger/
|
||||
COPY tap/go.mod tap/go.mod ../tap/
|
||||
COPY tap/api/go.mod ../tap/api/
|
||||
COPY tap/dbgctl/go.mod ../tap/dbgctl/
|
||||
COPY tap/extensions/amqp/go.mod ../tap/extensions/amqp/
|
||||
COPY tap/extensions/http/go.mod ../tap/extensions/http/
|
||||
COPY tap/extensions/kafka/go.mod ../tap/extensions/kafka/
|
||||
@@ -94,8 +95,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.8.1/basenine_linux_${GOARCH} ./basenine_linux_${GOARCH}
|
||||
ADD https://github.com/up9inc/basenine/releases/download/v0.8.1/basenine_linux_${GOARCH}.sha256 ./basenine_linux_${GOARCH}.sha256
|
||||
ADD https://github.com/up9inc/basenine/releases/download/v0.8.2/basenine_linux_${GOARCH} ./basenine_linux_${GOARCH}
|
||||
ADD https://github.com/up9inc/basenine/releases/download/v0.8.2/basenine_linux_${GOARCH}.sha256 ./basenine_linux_${GOARCH}.sha256
|
||||
|
||||
RUN shasum -a 256 -c basenine_linux_"${GOARCH}".sha256 && \
|
||||
chmod +x ./basenine_linux_"${GOARCH}" && \
|
||||
|
||||
1
Makefile
1
Makefile
@@ -83,6 +83,7 @@ test-lint: ## Run lint on all modules
|
||||
cd cli && golangci-lint run
|
||||
cd acceptanceTests && golangci-lint run
|
||||
cd tap/api && golangci-lint run
|
||||
cd tap/dbgctl && golangci-lint run
|
||||
cd tap/extensions/ && for D in */; do cd $$D && golangci-lint run && cd ..; done
|
||||
|
||||
test-cli: ## Run cli tests
|
||||
|
||||
@@ -58,7 +58,7 @@ export function rightOnHoverCheck(path, expectedText) {
|
||||
}
|
||||
|
||||
export function checkFilterByMethod(funcDict) {
|
||||
const {protocol, method, methodQuery, summary, summaryQuery} = funcDict;
|
||||
const {protocol, method, methodQuery, summary, summaryQuery, numberOfRecords} = funcDict;
|
||||
const summaryDict = getSummaryDict(summary, summaryQuery);
|
||||
const methodDict = getMethodDict(method, methodQuery);
|
||||
const protocolDict = getProtocolDict(protocol.name, protocol.text);
|
||||
@@ -69,47 +69,53 @@ export function checkFilterByMethod(funcDict) {
|
||||
cy.get('[type="submit"]').click();
|
||||
cy.get('.w-tc-editor').should('have.attr', 'style').and('include', Cypress.env('greenFilterColor'));
|
||||
|
||||
cy.get('#entries-length').should('not.have.text', '0').then(() => {
|
||||
cy.get(`#list [id]`).then(elements => {
|
||||
const listElmWithIdAttr = Object.values(elements);
|
||||
let doneCheckOnFirst = false;
|
||||
waitForFetch(numberOfRecords);
|
||||
pauseStream();
|
||||
|
||||
cy.get('#entries-length').invoke('text').then(len => {
|
||||
resizeIfNeeded(len);
|
||||
listElmWithIdAttr.forEach(entry => {
|
||||
if (entry?.id && entry.id.match(RegExp(/entry-(\d{24})$/gm))) {
|
||||
const entryId = getEntryId(entry.id);
|
||||
cy.get(`#list [id^=entry]`).then(elements => {
|
||||
const listElmWithIdAttr = Object.values(elements);
|
||||
let doneCheckOnFirst = false;
|
||||
|
||||
leftTextCheck(entryId, methodDict.pathLeft, methodDict.expectedText);
|
||||
leftTextCheck(entryId, protocolDict.pathLeft, protocolDict.expectedTextLeft);
|
||||
if (summaryDict)
|
||||
leftTextCheck(entryId, summaryDict.pathLeft, summaryDict.expectedText);
|
||||
cy.get('#entries-length').invoke('text').then(len => {
|
||||
listElmWithIdAttr.forEach(entry => {
|
||||
if (entry?.id && entry.id.match(RegExp(/entry-(\d{24})$/gm))) {
|
||||
const entryId = getEntryId(entry.id);
|
||||
|
||||
if (!doneCheckOnFirst) {
|
||||
deepCheck(funcDict, protocolDict, methodDict, entry);
|
||||
doneCheckOnFirst = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
resizeIfNeeded(len);
|
||||
});
|
||||
leftTextCheck(entryId, methodDict.pathLeft, methodDict.expectedText);
|
||||
leftTextCheck(entryId, protocolDict.pathLeft, protocolDict.expectedTextLeft);
|
||||
if (summaryDict)
|
||||
leftTextCheck(entryId, summaryDict.pathLeft, summaryDict.expectedText);
|
||||
|
||||
if (!doneCheckOnFirst) {
|
||||
deepCheck(funcDict, protocolDict, methodDict, entry);
|
||||
doneCheckOnFirst = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export const refreshWaitTimeout = 10000;
|
||||
|
||||
export function waitForFetch(gt) {
|
||||
cy.get('#entries-length', {timeout: refreshWaitTimeout}).should((el) => {
|
||||
expect(parseInt(el.text().trim(), 10)).to.be.greaterThan(gt);
|
||||
});
|
||||
}
|
||||
|
||||
export function pauseStream() {
|
||||
cy.get('#pause-icon').click();
|
||||
cy.get('#pause-icon').should('not.be.visible');
|
||||
}
|
||||
|
||||
|
||||
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') ?
|
||||
resizeToHugeMizu() : resizeToNormalMizu()
|
||||
}
|
||||
}
|
||||
|
||||
function deepCheck(generalDict, protocolDict, methodDict, entry) {
|
||||
const entryId = getEntryId(entry.id);
|
||||
const {summary, value} = generalDict;
|
||||
|
||||
@@ -5,6 +5,7 @@ it('opening mizu', function () {
|
||||
});
|
||||
|
||||
const rabbitProtocolDetails = {name: 'AMQP', text: 'Advanced Message Queuing Protocol 0-9-1'};
|
||||
const numberOfRecords = 5;
|
||||
|
||||
checkFilterByMethod({
|
||||
protocol: rabbitProtocolDetails,
|
||||
@@ -12,6 +13,7 @@ checkFilterByMethod({
|
||||
methodQuery: 'request.method == "exchange declare"',
|
||||
summary: 'exchange',
|
||||
summaryQuery: 'request.exchange == "exchange"',
|
||||
numberOfRecords: numberOfRecords,
|
||||
value: null
|
||||
});
|
||||
|
||||
@@ -21,6 +23,7 @@ checkFilterByMethod({
|
||||
methodQuery: 'request.method == "queue declare"',
|
||||
summary: 'queue',
|
||||
summaryQuery: 'request.queue == "queue"',
|
||||
numberOfRecords: numberOfRecords,
|
||||
value: null
|
||||
});
|
||||
|
||||
@@ -30,6 +33,7 @@ checkFilterByMethod({
|
||||
methodQuery: 'request.method == "queue bind"',
|
||||
summary: 'queue',
|
||||
summaryQuery: 'request.queue == "queue"',
|
||||
numberOfRecords: numberOfRecords,
|
||||
value: null
|
||||
});
|
||||
|
||||
@@ -39,6 +43,7 @@ checkFilterByMethod({
|
||||
methodQuery: 'request.method == "basic publish"',
|
||||
summary: 'exchange',
|
||||
summaryQuery: 'request.exchange == "exchange"',
|
||||
numberOfRecords: numberOfRecords,
|
||||
value: {tab: valueTabs.request, regex: /^message$/mg}
|
||||
});
|
||||
|
||||
@@ -48,6 +53,7 @@ checkFilterByMethod({
|
||||
methodQuery: 'request.method == "basic consume"',
|
||||
summary: 'queue',
|
||||
summaryQuery: 'request.queue == "queue"',
|
||||
numberOfRecords: numberOfRecords,
|
||||
value: null
|
||||
});
|
||||
|
||||
@@ -57,5 +63,6 @@ checkFilterByMethod({
|
||||
methodQuery: 'request.method == "basic deliver"',
|
||||
summary: 'exchange',
|
||||
summaryQuery: 'request.queue == "exchange"',
|
||||
numberOfRecords: numberOfRecords,
|
||||
value: {tab: valueTabs.request, regex: /^message$/mg}
|
||||
});
|
||||
|
||||
@@ -5,6 +5,7 @@ it('opening mizu', function () {
|
||||
});
|
||||
|
||||
const redisProtocolDetails = {name: 'redis', text: 'Redis Serialization Protocol'};
|
||||
const numberOfRecords = 5;
|
||||
|
||||
checkFilterByMethod({
|
||||
protocol: redisProtocolDetails,
|
||||
@@ -12,6 +13,7 @@ checkFilterByMethod({
|
||||
methodQuery: 'request.command == "PING"',
|
||||
summary: null,
|
||||
summaryQuery: '',
|
||||
numberOfRecords: numberOfRecords,
|
||||
value: null
|
||||
})
|
||||
|
||||
@@ -21,6 +23,7 @@ checkFilterByMethod({
|
||||
methodQuery: 'request.command == "SET"',
|
||||
summary: 'key',
|
||||
summaryQuery: 'request.key == "key"',
|
||||
numberOfRecords: numberOfRecords,
|
||||
value: {tab: valueTabs.request, regex: /^\[value, keepttl]$/mg}
|
||||
})
|
||||
|
||||
@@ -30,6 +33,7 @@ checkFilterByMethod({
|
||||
methodQuery: 'request.command == "EXISTS"',
|
||||
summary: 'key',
|
||||
summaryQuery: 'request.key == "key"',
|
||||
numberOfRecords: numberOfRecords,
|
||||
value: {tab: valueTabs.response, regex: /^1$/mg}
|
||||
})
|
||||
|
||||
@@ -39,6 +43,7 @@ checkFilterByMethod({
|
||||
methodQuery: 'request.command == "GET"',
|
||||
summary: 'key',
|
||||
summaryQuery: 'request.key == "key"',
|
||||
numberOfRecords: numberOfRecords,
|
||||
value: {tab: valueTabs.response, regex: /^value$/mg}
|
||||
})
|
||||
|
||||
@@ -48,5 +53,6 @@ checkFilterByMethod({
|
||||
methodQuery: 'request.command == "DEL"',
|
||||
summary: 'key',
|
||||
summaryQuery: 'request.key == "key"',
|
||||
numberOfRecords: numberOfRecords,
|
||||
value: {tab: valueTabs.response, regex: /^1$|^0$/mg}
|
||||
})
|
||||
|
||||
@@ -7,12 +7,12 @@ import {
|
||||
resizeToNormalMizu,
|
||||
rightOnHoverCheck,
|
||||
rightTextCheck,
|
||||
verifyMinimumEntries
|
||||
verifyMinimumEntries,
|
||||
refreshWaitTimeout,
|
||||
waitForFetch,
|
||||
pauseStream
|
||||
} from "../testHelpers/TrafficHelper";
|
||||
|
||||
const refreshWaitTimeout = 10000;
|
||||
|
||||
|
||||
const fullParam = Cypress.env('arrayDict'); // "Name:fooNamespace:barName:foo1Namespace:bar1"
|
||||
const podsArray = fullParam.split('Name:').slice(1); // ["fooNamespace:bar", "foo1Namespace:bar1"]
|
||||
podsArray.forEach((podStr, index) => {
|
||||
@@ -65,70 +65,77 @@ it('right side sanity test', function () {
|
||||
checkIllegalFilter('invalid filter');
|
||||
|
||||
checkFilter({
|
||||
name: 'http',
|
||||
filter: 'http',
|
||||
leftSidePath: '> :nth-child(1) > :nth-child(1)',
|
||||
leftSideExpectedText: 'HTTP',
|
||||
rightSidePath: '[title=HTTP]',
|
||||
rightSideExpectedText: 'Hypertext Transfer Protocol -- HTTP/1.1',
|
||||
applyByEnter: true
|
||||
applyByCtrlEnter: true,
|
||||
numberOfRecords: 20,
|
||||
});
|
||||
|
||||
checkFilter({
|
||||
name: 'response.status == 200',
|
||||
filter: 'response.status == 200',
|
||||
leftSidePath: '[title="Status Code"]',
|
||||
leftSideExpectedText: '200',
|
||||
rightSidePath: '> :nth-child(2) [title="Status Code"]',
|
||||
rightSideExpectedText: '200',
|
||||
applyByEnter: false
|
||||
applyByCtrlEnter: false,
|
||||
numberOfRecords: 20
|
||||
});
|
||||
|
||||
if (Cypress.env('shouldCheckSrcAndDest')) {
|
||||
serviceMapCheck();
|
||||
|
||||
checkFilter({
|
||||
name: 'src.name == ""',
|
||||
filter: 'src.name == ""',
|
||||
leftSidePath: '[title="Source Name"]',
|
||||
leftSideExpectedText: '[Unresolved]',
|
||||
rightSidePath: '> :nth-child(2) [title="Source Name"]',
|
||||
rightSideExpectedText: '[Unresolved]',
|
||||
applyByEnter: false
|
||||
applyByCtrlEnter: false,
|
||||
numberOfRecords: 20
|
||||
});
|
||||
|
||||
checkFilter({
|
||||
name: `dst.name == "httpbin.mizu-tests"`,
|
||||
filter: `dst.name == "httpbin.mizu-tests"`,
|
||||
leftSidePath: '> :nth-child(3) > :nth-child(2) > :nth-child(3) > :nth-child(2)',
|
||||
leftSideExpectedText: 'httpbin.mizu-tests',
|
||||
rightSidePath: '> :nth-child(2) > :nth-child(2) > :nth-child(2) > :nth-child(3) > :nth-child(2)',
|
||||
rightSideExpectedText: 'httpbin.mizu-tests',
|
||||
applyByEnter: false
|
||||
applyByCtrlEnter: false,
|
||||
numberOfRecords: 20
|
||||
});
|
||||
}
|
||||
|
||||
checkFilter({
|
||||
name: 'request.method == "GET"',
|
||||
filter: 'request.method == "GET"',
|
||||
leftSidePath: '> :nth-child(3) > :nth-child(1) > :nth-child(1) > :nth-child(2)',
|
||||
leftSideExpectedText: 'GET',
|
||||
rightSidePath: '> :nth-child(2) > :nth-child(2) > :nth-child(1) > :nth-child(1) > :nth-child(2)',
|
||||
rightSideExpectedText: 'GET',
|
||||
applyByEnter: true
|
||||
applyByCtrlEnter: true,
|
||||
numberOfRecords: 20
|
||||
});
|
||||
|
||||
checkFilter({
|
||||
name: 'request.path == "/get"',
|
||||
filter: 'request.path == "/get"',
|
||||
leftSidePath: '> :nth-child(3) > :nth-child(1) > :nth-child(2) > :nth-child(2)',
|
||||
leftSideExpectedText: '/get',
|
||||
rightSidePath: '> :nth-child(2) > :nth-child(2) > :nth-child(1) > :nth-child(2) > :nth-child(2)',
|
||||
rightSideExpectedText: '/get',
|
||||
applyByEnter: false
|
||||
applyByCtrlEnter: false,
|
||||
numberOfRecords: 20
|
||||
});
|
||||
|
||||
checkFilter({
|
||||
name: 'src.ip == "127.0.0.1"',
|
||||
filter: 'src.ip == "127.0.0.1"',
|
||||
leftSidePath: '[title="Source IP"]',
|
||||
leftSideExpectedText: '127.0.0.1',
|
||||
rightSidePath: '> :nth-child(2) [title="Source IP"]',
|
||||
rightSideExpectedText: '127.0.0.1',
|
||||
applyByEnter: false
|
||||
applyByCtrlEnter: false,
|
||||
numberOfRecords: 20
|
||||
});
|
||||
|
||||
checkFilterNoResults('request.method == "POST"');
|
||||
@@ -182,63 +189,62 @@ function checkIllegalFilter(illegalFilterName) {
|
||||
|
||||
function checkFilter(filterDetails) {
|
||||
const {
|
||||
name,
|
||||
filter,
|
||||
leftSidePath,
|
||||
rightSidePath,
|
||||
rightSideExpectedText,
|
||||
leftSideExpectedText,
|
||||
applyByEnter
|
||||
applyByCtrlEnter,
|
||||
numberOfRecords
|
||||
} = 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();
|
||||
it(`checking the filter: ${filter}`, function () {
|
||||
cy.get('.w-tc-editor-text').clear();
|
||||
// applying the filter with alt+enter or with the button
|
||||
cy.get('.w-tc-editor-text').type(`${filter}${applyByCtrlEnter ? '{ctrl+enter}' : ''}`);
|
||||
cy.get('.w-tc-editor').should('have.attr', 'style').and('include', Cypress.env('greenFilterColor'));
|
||||
if (!applyByCtrlEnter)
|
||||
cy.get('[type="submit"]').click();
|
||||
|
||||
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);
|
||||
waitForFetch(numberOfRecords);
|
||||
pauseStream();
|
||||
|
||||
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(`#list [id^=entry]`).last().then(elem => {
|
||||
const element = elem[0];
|
||||
const entryId = getEntryId(element.id);
|
||||
|
||||
// only one entry in DOM after filtering, checking all checks on it
|
||||
leftTextCheck(entryId, leftSidePath, leftSideExpectedText);
|
||||
leftOnHoverCheck(entryId, leftSidePath, name);
|
||||
// only one entry in DOM after filtering, checking all checks on it
|
||||
leftTextCheck(entryId, leftSidePath, leftSideExpectedText);
|
||||
leftOnHoverCheck(entryId, leftSidePath, filter);
|
||||
|
||||
rightTextCheck(rightSidePath, rightSideExpectedText);
|
||||
rightOnHoverCheck(rightSidePath, name);
|
||||
checkRightSideResponseBody();
|
||||
});
|
||||
rightTextCheck(rightSidePath, rightSideExpectedText);
|
||||
rightOnHoverCheck(rightSidePath, filter);
|
||||
checkRightSideResponseBody();
|
||||
});
|
||||
|
||||
resizeToHugeMizu();
|
||||
resizeToHugeMizu();
|
||||
|
||||
// checking only 'leftTextCheck' on all entries because the rest of the checks require more time
|
||||
cy.get(`#list [id^=entry]`).each(elem => {
|
||||
const element = elem[0];
|
||||
let entryId = getEntryId(element.id);
|
||||
leftTextCheck(entryId, leftSidePath, leftSideExpectedText);
|
||||
});
|
||||
// checking only 'leftTextCheck' on all entries because the rest of the checks require more time
|
||||
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)
|
||||
deeperCheck(leftSidePath, rightSidePath, name, leftSideExpectedText, rightSideExpectedText, entriesForDeeperCheck);
|
||||
// making the other 3 checks on the first X entries (longer time for each check)
|
||||
deeperCheck(leftSidePath, rightSidePath, filter, rightSideExpectedText, entriesForDeeperCheck);
|
||||
|
||||
// reloading then waiting for the entries number to load
|
||||
resizeToNormalMizu();
|
||||
cy.reload();
|
||||
cy.get('#total-entries', {timeout: refreshWaitTimeout}).should('have.text', totalEntries);
|
||||
})
|
||||
// reloading then waiting for the entries number to load
|
||||
resizeToNormalMizu();
|
||||
cy.reload();
|
||||
waitForFetch(numberOfRecords);
|
||||
pauseStream();
|
||||
});
|
||||
}
|
||||
|
||||
function deeperCheck(leftSidePath, rightSidePath, filterName, leftSideExpectedText, rightSideExpectedText, entriesNumToCheck) {
|
||||
function deeperCheck(leftSidePath, rightSidePath, filterName, rightSideExpectedText, entriesNumToCheck) {
|
||||
cy.get(`#list [id^=entry]`).each((element, index) => {
|
||||
if (index < entriesNumToCheck) {
|
||||
const entryId = getEntryId(element[0].id);
|
||||
@@ -262,11 +268,12 @@ function checkRightSideResponseBody() {
|
||||
const decodedBody = atob(encodedBody);
|
||||
const responseBody = JSON.parse(decodedBody);
|
||||
|
||||
|
||||
const expectdJsonBody = {
|
||||
args: RegExp({}),
|
||||
url: RegExp('http://.*/get'),
|
||||
headers: {
|
||||
"User-Agent": RegExp('[REDACTED]'),
|
||||
"User-Agent": RegExp('client'),
|
||||
"Accept-Encoding": RegExp('gzip'),
|
||||
"X-Forwarded-Uri": RegExp('/api/v1/namespaces/.*/services/.*/proxy/get')
|
||||
}
|
||||
@@ -283,16 +290,16 @@ function checkRightSideResponseBody() {
|
||||
|
||||
cy.get(`${Cypress.env('bodyJsonClass')} > `).its('length').should('be.gt', 1).then(linesNum => {
|
||||
cy.get(`${Cypress.env('bodyJsonClass')} > >`).its('length').should('be.gt', linesNum).then(jsonItemsNum => {
|
||||
checkPrettyAndLineNums(jsonItemsNum, decodedBody);
|
||||
// checkPrettyAndLineNums(decodedBody);
|
||||
|
||||
clickCheckbox('Line numbers');
|
||||
checkPrettyOrNothing(jsonItemsNum, decodedBody);
|
||||
//clickCheckbox('Line numbers');
|
||||
//checkPrettyOrNothing(jsonItemsNum, decodedBody);
|
||||
|
||||
clickCheckbox('Pretty');
|
||||
checkPrettyOrNothing(jsonItemsNum, decodedBody);
|
||||
|
||||
clickCheckbox('Line numbers');
|
||||
checkOnlyLineNumberes(jsonItemsNum, decodedBody);
|
||||
// clickCheckbox('Pretty');
|
||||
// checkPrettyOrNothing(jsonItemsNum, decodedBody);
|
||||
//
|
||||
// clickCheckbox('Line numbers');
|
||||
// checkOnlyLineNumberes(jsonItemsNum, decodedBody);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -302,7 +309,7 @@ function clickCheckbox(type) {
|
||||
cy.contains(`${type}`).prev().children().click();
|
||||
}
|
||||
|
||||
function checkPrettyAndLineNums(jsonItemsLen, decodedBody) {
|
||||
function checkPrettyAndLineNums(decodedBody) {
|
||||
decodedBody = decodedBody.replaceAll(' ', '');
|
||||
cy.get(`${Cypress.env('bodyJsonClass')} >`).then(elements => {
|
||||
const lines = Object.values(elements);
|
||||
|
||||
@@ -54,3 +54,5 @@ replace github.com/up9inc/mizu/logger v0.0.0 => ../logger
|
||||
replace github.com/up9inc/mizu/shared v0.0.0 => ../shared
|
||||
|
||||
replace github.com/up9inc/mizu/tap/api v0.0.0 => ../tap/api
|
||||
|
||||
replace github.com/up9inc/mizu/tap/dbgctl v0.0.0 => ../tap/dbgctl
|
||||
|
||||
@@ -343,6 +343,7 @@ func TestTapRedact(t *testing.T) {
|
||||
|
||||
tapNamespace := GetDefaultTapNamespace()
|
||||
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
||||
tapCmdArgs = append(tapCmdArgs, "--redact")
|
||||
|
||||
tapCmd := exec.Command(cliPath, tapCmdArgs...)
|
||||
t.Logf("running command: %v", tapCmd.String())
|
||||
@@ -394,8 +395,6 @@ func TestTapNoRedact(t *testing.T) {
|
||||
tapNamespace := GetDefaultTapNamespace()
|
||||
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
||||
|
||||
tapCmdArgs = append(tapCmdArgs, "--no-redact")
|
||||
|
||||
tapCmd := exec.Command(cliPath, tapCmdArgs...)
|
||||
t.Logf("running command: %v", tapCmd.String())
|
||||
|
||||
@@ -446,6 +445,8 @@ func TestTapRegexMasking(t *testing.T) {
|
||||
tapNamespace := GetDefaultTapNamespace()
|
||||
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
||||
|
||||
tapCmdArgs = append(tapCmdArgs, "--redact")
|
||||
|
||||
tapCmdArgs = append(tapCmdArgs, "-r", "Mizu")
|
||||
|
||||
tapCmd := exec.Command(cliPath, tapCmdArgs...)
|
||||
|
||||
13
agent/go.mod
13
agent/go.mod
@@ -6,8 +6,8 @@ require (
|
||||
github.com/antelman107/net-wait-go v0.0.0-20210623112055-cf684aebda7b
|
||||
github.com/chanced/openapi v0.0.8
|
||||
github.com/djherbis/atime v1.1.0
|
||||
github.com/elastic/go-elasticsearch/v7 v7.17.0
|
||||
github.com/getkin/kin-openapi v0.89.0
|
||||
github.com/gin-contrib/pprof v1.3.0
|
||||
github.com/gin-contrib/static v0.0.1
|
||||
github.com/gin-gonic/gin v1.7.7
|
||||
github.com/go-playground/locales v0.14.0
|
||||
@@ -20,11 +20,12 @@ require (
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
||||
github.com/orcaman/concurrent-map v1.0.0
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/up9inc/basenine/client/go v0.0.0-20220508080324-c66c4e1b9337
|
||||
github.com/up9inc/basenine/client/go v0.0.0-20220509204026-c37adfc587f4
|
||||
github.com/up9inc/mizu/logger v0.0.0
|
||||
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
|
||||
github.com/up9inc/mizu/tap/dbgctl v0.0.0
|
||||
github.com/up9inc/mizu/tap/extensions/amqp v0.0.0
|
||||
github.com/up9inc/mizu/tap/extensions/http v0.0.0
|
||||
github.com/up9inc/mizu/tap/extensions/kafka v0.0.0
|
||||
@@ -61,6 +62,7 @@ require (
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-errors/errors v1.4.2 // indirect
|
||||
github.com/go-logr/logr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.19.6 // indirect
|
||||
github.com/go-openapi/swag v0.21.1 // indirect
|
||||
@@ -102,15 +104,20 @@ require (
|
||||
github.com/russross/blackfriday v1.6.0 // indirect
|
||||
github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 // indirect
|
||||
github.com/segmentio/kafka-go v0.4.27 // indirect
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
|
||||
github.com/spf13/cobra v1.3.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/struCoder/pidusage v0.2.1 // indirect
|
||||
github.com/tidwall/gjson v1.14.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/tidwall/sjson v1.2.4 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
||||
github.com/tklauser/numcpus v0.4.0 // indirect
|
||||
github.com/ugorji/go/codec v1.2.6 // indirect
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||
github.com/xlab/treeprint v1.1.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
go.starlark.net v0.0.0-20220203230714-bb14e151c28f // indirect
|
||||
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b // indirect
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
|
||||
@@ -152,3 +159,5 @@ replace github.com/up9inc/mizu/tap/extensions/http v0.0.0 => ../tap/extensions/h
|
||||
replace github.com/up9inc/mizu/tap/extensions/kafka v0.0.0 => ../tap/extensions/kafka
|
||||
|
||||
replace github.com/up9inc/mizu/tap/extensions/redis v0.0.0 => ../tap/extensions/redis
|
||||
|
||||
replace github.com/up9inc/mizu/tap/dbgctl v0.0.0 => ../tap/dbgctl
|
||||
|
||||
23
agent/go.sum
23
agent/go.sum
@@ -166,8 +166,6 @@ github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/elastic/go-elasticsearch/v7 v7.17.0 h1:0fcSh4qeC/i1+7QU1KXpmq2iUAdMk4l0/vmbtW1+KJM=
|
||||
github.com/elastic/go-elasticsearch/v7 v7.17.0/go.mod h1:OJ4wdbtDNk5g503kvlHLyErCgQwwzmDtaFC4XyOxXA4=
|
||||
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc=
|
||||
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
@@ -215,10 +213,13 @@ github.com/getkin/kin-openapi v0.89.0 h1:p4nagHchUKGn85z/f+pse4aSh50nIBOYjOhMIku
|
||||
github.com/getkin/kin-openapi v0.89.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gin-contrib/pprof v1.3.0 h1:G9eK6HnbkSqDZBYbzG4wrjCsA4e+cvYAHUZw6W+W9K0=
|
||||
github.com/gin-contrib/pprof v1.3.0/go.mod h1:waMjT1H9b179t3CxuG1cV3DHpga6ybizwfBaM5OXaB0=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-contrib/static v0.0.1 h1:JVxuvHPuUfkoul12N7dtQw7KRn/pSMq7Ue1Va9Swm1U=
|
||||
github.com/gin-contrib/static v0.0.1/go.mod h1:CSxeF+wep05e0kCOsqWdAWbSszmc31zTIbD8TvWl7Hs=
|
||||
github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||
github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
|
||||
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
|
||||
@@ -240,6 +241,8 @@ github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV
|
||||
github.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
@@ -620,6 +623,8 @@ github.com/segmentio/kafka-go v0.4.27 h1:sIhEozeL/TLN2mZ5dkG462vcGEWYKS+u31sXPjK
|
||||
github.com/segmentio/kafka-go v0.4.27/go.mod h1:XzMcoMjSzDGHcIwpWUI7GB43iKZ2fTVmryPSGLf/MPg=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
@@ -661,6 +666,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/struCoder/pidusage v0.2.1 h1:dFiEgUDkubeIj0XA1NpQ6+8LQmKrLi7NiIQl86E6BoY=
|
||||
github.com/struCoder/pidusage v0.2.1/go.mod h1:bewtP2KUA1TBUyza5+/PCpSQ6sc/H6jJbIKAzqW86BA=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tidwall/gjson v1.10.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.12.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
@@ -674,6 +681,10 @@ github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhso
|
||||
github.com/tidwall/sjson v1.2.3/go.mod h1:5WdjKx3AQMvCJ4RG6/2UYT7dLrGvJUV1x4jdTAyGvZs=
|
||||
github.com/tidwall/sjson v1.2.4 h1:cuiLzLnaMeBhRmEv00Lpk3tkYrcxpmbU81tAY4Dw0tc=
|
||||
github.com/tidwall/sjson v1.2.4/go.mod h1:098SZ494YoMWPmMO6ct4dcFnqxwj9r/gF0Etp19pSNM=
|
||||
github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw=
|
||||
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
|
||||
github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o=
|
||||
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
|
||||
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=
|
||||
@@ -683,8 +694,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-20220508080324-c66c4e1b9337 h1:eRXRZnojrZyhbiSuGHl0EPvFtWvx1ZMrsY/bSoBzYNE=
|
||||
github.com/up9inc/basenine/client/go v0.0.0-20220508080324-c66c4e1b9337/go.mod h1:SvJGPoa/6erhUQV7kvHBwM/0x5LyO6XaG2lUaCaKiUI=
|
||||
github.com/up9inc/basenine/client/go v0.0.0-20220509204026-c37adfc587f4 h1:nNOrU1HVH0fnaG7GNhxCc8kNPVL035Iix7ihUF6lZT8=
|
||||
github.com/up9inc/basenine/client/go v0.0.0-20220509204026-c37adfc587f4/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=
|
||||
@@ -704,6 +715,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
||||
go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
||||
@@ -896,6 +909,7 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -964,6 +978,7 @@ golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220207234003-57398862261d h1:Bm7BNOQt2Qv7ZqysjeLjgCBanX+88Z/OtdvsrEv1Djc=
|
||||
golang.org/x/sys v0.0.0-20220207234003-57398862261d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
|
||||
@@ -14,10 +14,10 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/gin-contrib/pprof"
|
||||
"github.com/gin-contrib/static"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/up9inc/mizu/agent/pkg/dependency"
|
||||
"github.com/up9inc/mizu/agent/pkg/elastic"
|
||||
"github.com/up9inc/mizu/agent/pkg/entries"
|
||||
"github.com/up9inc/mizu/agent/pkg/middlewares"
|
||||
"github.com/up9inc/mizu/agent/pkg/models"
|
||||
@@ -37,6 +37,7 @@ import (
|
||||
"github.com/up9inc/mizu/shared"
|
||||
"github.com/up9inc/mizu/tap"
|
||||
tapApi "github.com/up9inc/mizu/tap/api"
|
||||
"github.com/up9inc/mizu/tap/dbgctl"
|
||||
)
|
||||
|
||||
var tapperMode = flag.Bool("tap", false, "Run in tapper mode without API")
|
||||
@@ -46,6 +47,7 @@ var apiServerAddress = flag.String("api-server-address", "", "Address of mizu AP
|
||||
var namespace = flag.String("namespace", "", "Resolve IPs if they belong to resources in this namespace (default is all)")
|
||||
var harsReaderMode = flag.Bool("hars-read", false, "Run in hars-read mode")
|
||||
var harsDir = flag.String("hars-dir", "", "Directory to read hars from")
|
||||
var profiler = flag.Bool("profiler", false, "Run pprof server")
|
||||
|
||||
const (
|
||||
socketConnectionRetries = 30
|
||||
@@ -62,7 +64,7 @@ func main() {
|
||||
app.LoadExtensions()
|
||||
|
||||
if !*tapperMode && !*apiServerMode && !*standaloneMode && !*harsReaderMode {
|
||||
panic("One of the flags --tap, --api or --standalone or --hars-read must be provided")
|
||||
panic("One of the flags --tap, --api-server, --standalone or --hars-read must be provided")
|
||||
}
|
||||
|
||||
if *standaloneMode {
|
||||
@@ -70,7 +72,14 @@ func main() {
|
||||
} else if *tapperMode {
|
||||
runInTapperMode()
|
||||
} else if *apiServerMode {
|
||||
utils.StartServer(runInApiServerMode(*namespace))
|
||||
app := runInApiServerMode(*namespace)
|
||||
|
||||
if *profiler {
|
||||
pprof.Register(app)
|
||||
}
|
||||
|
||||
utils.StartServer(app)
|
||||
|
||||
} else if *harsReaderMode {
|
||||
runInHarReaderMode()
|
||||
}
|
||||
@@ -153,7 +162,9 @@ func runInTapperMode() {
|
||||
}
|
||||
|
||||
hostMode := os.Getenv(shared.HostModeEnvVar) == "1"
|
||||
tapOpts := &tap.TapOpts{HostMode: hostMode}
|
||||
tapOpts := &tap.TapOpts{
|
||||
HostMode: hostMode,
|
||||
}
|
||||
|
||||
filteredOutputItemsChannel := make(chan *tapApi.OutputChannelItem)
|
||||
|
||||
@@ -199,13 +210,12 @@ func runInHarReaderMode() {
|
||||
func enableExpFeatureIfNeeded() {
|
||||
if config.Config.OAS {
|
||||
oasGenerator := dependency.GetInstance(dependency.OasGeneratorDependency).(oas.OasGenerator)
|
||||
oasGenerator.Start(nil)
|
||||
oasGenerator.Start()
|
||||
}
|
||||
if config.Config.ServiceMap {
|
||||
serviceMapGenerator := dependency.GetInstance(dependency.ServiceMapGeneratorDependency).(servicemap.ServiceMap)
|
||||
serviceMapGenerator.Enable()
|
||||
}
|
||||
elastic.GetInstance().Configure(config.Config.Elastic)
|
||||
}
|
||||
|
||||
func getSyncEntriesConfig() *shared.SyncEntriesConfig {
|
||||
@@ -283,6 +293,10 @@ func pipeTapChannelToSocket(connection *websocket.Conn, messageDataChannel <-cha
|
||||
continue
|
||||
}
|
||||
|
||||
if dbgctl.MizuTapperDisableSending {
|
||||
continue
|
||||
}
|
||||
|
||||
// NOTE: This is where the `*tapApi.OutputChannelItem` leaves the code
|
||||
// and goes into the intermediate WebSocket.
|
||||
err = connection.WriteMessage(websocket.TextMessage, marshaledData)
|
||||
@@ -373,6 +387,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() })
|
||||
dependency.RegisterGenerator(dependency.EntriesInserter, func() interface{} { return api.GetBasenineEntryInserterInstance() })
|
||||
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,20 +5,19 @@ import (
|
||||
|
||||
basenine "github.com/up9inc/basenine/client/go"
|
||||
"github.com/up9inc/mizu/agent/pkg/models"
|
||||
"github.com/up9inc/mizu/logger"
|
||||
tapApi "github.com/up9inc/mizu/tap/api"
|
||||
)
|
||||
|
||||
type EntryStreamerSocketConnector interface {
|
||||
SendEntry(socketId int, entry *tapApi.Entry, params *WebSocketParams)
|
||||
SendMetadata(socketId int, metadata *basenine.Metadata)
|
||||
SendToastError(socketId int, err error)
|
||||
SendEntry(socketId int, entry *tapApi.Entry, params *WebSocketParams) error
|
||||
SendMetadata(socketId int, metadata *basenine.Metadata) error
|
||||
SendToastError(socketId int, err error) error
|
||||
CleanupSocket(socketId int)
|
||||
}
|
||||
|
||||
type DefaultEntryStreamerSocketConnector struct{}
|
||||
|
||||
func (e *DefaultEntryStreamerSocketConnector) SendEntry(socketId int, entry *tapApi.Entry, params *WebSocketParams) {
|
||||
func (e *DefaultEntryStreamerSocketConnector) SendEntry(socketId int, entry *tapApi.Entry, params *WebSocketParams) error {
|
||||
var message []byte
|
||||
if params.EnableFullEntries {
|
||||
message, _ = models.CreateFullEntryWebSocketMessage(entry)
|
||||
@@ -29,26 +28,32 @@ func (e *DefaultEntryStreamerSocketConnector) SendEntry(socketId int, entry *tap
|
||||
}
|
||||
|
||||
if err := SendToSocket(socketId, message); err != nil {
|
||||
logger.Log.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *DefaultEntryStreamerSocketConnector) SendMetadata(socketId int, metadata *basenine.Metadata) {
|
||||
func (e *DefaultEntryStreamerSocketConnector) SendMetadata(socketId int, metadata *basenine.Metadata) error {
|
||||
metadataBytes, _ := models.CreateWebsocketQueryMetadataMessage(metadata)
|
||||
if err := SendToSocket(socketId, metadataBytes); err != nil {
|
||||
logger.Log.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *DefaultEntryStreamerSocketConnector) SendToastError(socketId int, err error) {
|
||||
func (e *DefaultEntryStreamerSocketConnector) SendToastError(socketId int, err error) error {
|
||||
toastBytes, _ := models.CreateWebsocketToastMessage(&models.ToastMessage{
|
||||
Type: "error",
|
||||
AutoClose: 5000,
|
||||
Text: fmt.Sprintf("Syntax error: %s", err.Error()),
|
||||
})
|
||||
if err := SendToSocket(socketId, toastBytes); err != nil {
|
||||
logger.Log.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *DefaultEntryStreamerSocketConnector) CleanupSocket(socketId int) {
|
||||
|
||||
@@ -14,21 +14,18 @@ import (
|
||||
"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"
|
||||
"github.com/up9inc/mizu/agent/pkg/holder"
|
||||
"github.com/up9inc/mizu/agent/pkg/providers"
|
||||
|
||||
"github.com/up9inc/mizu/agent/pkg/oas"
|
||||
"github.com/up9inc/mizu/agent/pkg/servicemap"
|
||||
|
||||
"github.com/up9inc/mizu/agent/pkg/resolver"
|
||||
"github.com/up9inc/mizu/agent/pkg/utils"
|
||||
|
||||
"github.com/up9inc/mizu/logger"
|
||||
"github.com/up9inc/mizu/shared"
|
||||
tapApi "github.com/up9inc/mizu/tap/api"
|
||||
|
||||
basenine "github.com/up9inc/basenine/client/go"
|
||||
)
|
||||
|
||||
var k8sResolver *resolver.Resolver
|
||||
@@ -103,20 +100,6 @@ 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 {
|
||||
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
|
||||
}
|
||||
|
||||
disableOASValidation := false
|
||||
ctx := context.Background()
|
||||
doc, contractContent, router, err := loadOAS(ctx)
|
||||
@@ -163,17 +146,16 @@ BasenineReconnect:
|
||||
|
||||
providers.EntryAdded(len(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
|
||||
entryInserter := dependency.GetInstance(dependency.EntriesInserter).(EntryInserter)
|
||||
if err := entryInserter.Insert(mizuEntry); err != nil {
|
||||
logger.Log.Errorf("Error inserting entry, err: %v", err)
|
||||
}
|
||||
|
||||
serviceMapGenerator := dependency.GetInstance(dependency.ServiceMapGeneratorDependency).(servicemap.ServiceMapSink)
|
||||
serviceMapGenerator.NewTCPEntry(mizuEntry.Source, mizuEntry.Destination, &item.Protocol)
|
||||
|
||||
elastic.GetInstance().PushEntry(mizuEntry)
|
||||
oasGenerator := dependency.GetInstance(dependency.OasGeneratorDependency).(oas.OasGeneratorSink)
|
||||
oasGenerator.HandleEntry(mizuEntry)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
71
agent/pkg/api/socket_data_inserter.go
Normal file
71
agent/pkg/api/socket_data_inserter.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
basenine "github.com/up9inc/basenine/client/go"
|
||||
"github.com/up9inc/mizu/logger"
|
||||
"github.com/up9inc/mizu/shared"
|
||||
"github.com/up9inc/mizu/tap/api"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type EntryInserter interface {
|
||||
Insert(entry *api.Entry) error
|
||||
}
|
||||
|
||||
type BasenineEntryInserter struct {
|
||||
connection *basenine.Connection
|
||||
}
|
||||
|
||||
var instance *BasenineEntryInserter
|
||||
var once sync.Once
|
||||
|
||||
func GetBasenineEntryInserterInstance() *BasenineEntryInserter {
|
||||
once.Do(func() {
|
||||
instance = &BasenineEntryInserter{}
|
||||
})
|
||||
|
||||
return instance
|
||||
}
|
||||
|
||||
func (e *BasenineEntryInserter) Insert(entry *api.Entry) error {
|
||||
if e.connection == nil {
|
||||
e.connection = initializeConnection()
|
||||
}
|
||||
|
||||
data, err := json.Marshal(entry)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error marshling entry, err: %v", err)
|
||||
}
|
||||
|
||||
if err := e.connection.SendText(string(data)); err != nil {
|
||||
e.connection.Close()
|
||||
e.connection = nil
|
||||
|
||||
return fmt.Errorf("error sending text to database, err: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func initializeConnection() *basenine.Connection{
|
||||
for {
|
||||
connection, err := basenine.NewConnection(shared.BasenineHost, shared.BaseninePort)
|
||||
if err != nil {
|
||||
logger.Log.Errorf("Can't establish a new connection to Basenine server: %v", err)
|
||||
time.Sleep(shared.BasenineReconnectInterval * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
if err = connection.InsertMode(); err != nil {
|
||||
logger.Log.Errorf("Insert mode call failed: %v", err)
|
||||
connection.Close()
|
||||
time.Sleep(shared.BasenineReconnectInterval * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
return connection
|
||||
}
|
||||
}
|
||||
@@ -34,14 +34,18 @@ func (e *BasenineEntryStreamer) Get(ctx context.Context, socketId int, params *W
|
||||
meta := make(chan []byte)
|
||||
|
||||
query := params.Query
|
||||
err = basenine.Validate(shared.BasenineHost, shared.BaseninePort, query)
|
||||
if err != nil {
|
||||
entryStreamerSocketConnector.SendToastError(socketId, err)
|
||||
if err = basenine.Validate(shared.BasenineHost, shared.BaseninePort, query); err != nil {
|
||||
if err := entryStreamerSocketConnector.SendToastError(socketId, err); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
entryStreamerSocketConnector.CleanupSocket(socketId)
|
||||
return err
|
||||
}
|
||||
|
||||
leftOff, err := e.fetch(socketId, params, entryStreamerSocketConnector)
|
||||
if err != nil {
|
||||
logger.Log.Errorf("Fetch error: %v", err.Error())
|
||||
logger.Log.Errorf("Fetch error: %v", err)
|
||||
}
|
||||
|
||||
handleDataChannel := func(c *basenine.Connection, data chan []byte) {
|
||||
@@ -53,13 +57,15 @@ 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())
|
||||
if err = json.Unmarshal(bytes, &entry); err != nil {
|
||||
logger.Log.Debugf("Error unmarshalling entry: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
entryStreamerSocketConnector.SendEntry(socketId, entry, params)
|
||||
if err := entryStreamerSocketConnector.SendEntry(socketId, entry, params); err != nil {
|
||||
logger.Log.Errorf("Error sending entry to socket, err: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,13 +78,15 @@ func (e *BasenineEntryStreamer) Get(ctx context.Context, socketId int, params *W
|
||||
}
|
||||
|
||||
var metadata *basenine.Metadata
|
||||
err = json.Unmarshal(bytes, &metadata)
|
||||
if err != nil {
|
||||
logger.Log.Debugf("Error unmarshalling metadata: %v", err.Error())
|
||||
if err = json.Unmarshal(bytes, &metadata); err != nil {
|
||||
logger.Log.Debugf("Error unmarshalling metadata: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
entryStreamerSocketConnector.SendMetadata(socketId, metadata)
|
||||
if err := entryStreamerSocketConnector.SendMetadata(socketId, metadata); err != nil {
|
||||
logger.Log.Errorf("Error sending metadata to socket, err: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,28 +133,31 @@ func (e *BasenineEntryStreamer) fetch(socketId int, params *WebSocketParams, con
|
||||
}
|
||||
|
||||
var firstMetadata *basenine.Metadata
|
||||
err = json.Unmarshal(firstMeta, &firstMetadata)
|
||||
if err != nil {
|
||||
if err = json.Unmarshal(firstMeta, &firstMetadata); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
leftOff = firstMetadata.LeftOff
|
||||
|
||||
var lastMetadata *basenine.Metadata
|
||||
err = json.Unmarshal(lastMeta, &lastMetadata)
|
||||
if err != nil {
|
||||
if err = json.Unmarshal(lastMeta, &lastMetadata); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = connector.SendMetadata(socketId, lastMetadata); err != nil {
|
||||
return
|
||||
}
|
||||
connector.SendMetadata(socketId, lastMetadata)
|
||||
|
||||
data = e.reverseBytesSlice(data)
|
||||
for _, row := range data {
|
||||
var entry *tapApi.Entry
|
||||
err = json.Unmarshal(row, &entry)
|
||||
if err != nil {
|
||||
if err = json.Unmarshal(row, &entry); err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
connector.SendEntry(socketId, entry, params)
|
||||
if err = connector.SendEntry(socketId, entry, params); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/up9inc/mizu/agent/pkg/api"
|
||||
"github.com/up9inc/mizu/agent/pkg/utils"
|
||||
"github.com/up9inc/mizu/logger"
|
||||
"github.com/up9inc/mizu/tap/dbgctl"
|
||||
tapApi "github.com/up9inc/mizu/tap/api"
|
||||
amqpExt "github.com/up9inc/mizu/tap/extensions/amqp"
|
||||
httpExt "github.com/up9inc/mizu/tap/extensions/http"
|
||||
@@ -24,36 +25,38 @@ var (
|
||||
)
|
||||
|
||||
func LoadExtensions() {
|
||||
Extensions = make([]*tapApi.Extension, 4)
|
||||
Extensions = make([]*tapApi.Extension, 0)
|
||||
ExtensionsMap = make(map[string]*tapApi.Extension)
|
||||
|
||||
extensionAmqp := &tapApi.Extension{}
|
||||
dissectorAmqp := amqpExt.NewDissector()
|
||||
dissectorAmqp.Register(extensionAmqp)
|
||||
extensionAmqp.Dissector = dissectorAmqp
|
||||
Extensions[0] = extensionAmqp
|
||||
ExtensionsMap[extensionAmqp.Protocol.Name] = extensionAmqp
|
||||
|
||||
extensionHttp := &tapApi.Extension{}
|
||||
dissectorHttp := httpExt.NewDissector()
|
||||
dissectorHttp.Register(extensionHttp)
|
||||
extensionHttp.Dissector = dissectorHttp
|
||||
Extensions[1] = extensionHttp
|
||||
Extensions = append(Extensions, extensionHttp)
|
||||
ExtensionsMap[extensionHttp.Protocol.Name] = extensionHttp
|
||||
|
||||
extensionKafka := &tapApi.Extension{}
|
||||
dissectorKafka := kafkaExt.NewDissector()
|
||||
dissectorKafka.Register(extensionKafka)
|
||||
extensionKafka.Dissector = dissectorKafka
|
||||
Extensions[2] = extensionKafka
|
||||
ExtensionsMap[extensionKafka.Protocol.Name] = extensionKafka
|
||||
if !dbgctl.MizuTapperDisableNonHttpExtensions {
|
||||
extensionAmqp := &tapApi.Extension{}
|
||||
dissectorAmqp := amqpExt.NewDissector()
|
||||
dissectorAmqp.Register(extensionAmqp)
|
||||
extensionAmqp.Dissector = dissectorAmqp
|
||||
Extensions = append(Extensions, extensionAmqp)
|
||||
ExtensionsMap[extensionAmqp.Protocol.Name] = extensionAmqp
|
||||
|
||||
extensionRedis := &tapApi.Extension{}
|
||||
dissectorRedis := redisExt.NewDissector()
|
||||
dissectorRedis.Register(extensionRedis)
|
||||
extensionRedis.Dissector = dissectorRedis
|
||||
Extensions[3] = extensionRedis
|
||||
ExtensionsMap[extensionRedis.Protocol.Name] = extensionRedis
|
||||
extensionKafka := &tapApi.Extension{}
|
||||
dissectorKafka := kafkaExt.NewDissector()
|
||||
dissectorKafka.Register(extensionKafka)
|
||||
extensionKafka.Dissector = dissectorKafka
|
||||
Extensions = append(Extensions, extensionKafka)
|
||||
ExtensionsMap[extensionKafka.Protocol.Name] = extensionKafka
|
||||
|
||||
extensionRedis := &tapApi.Extension{}
|
||||
dissectorRedis := redisExt.NewDissector()
|
||||
dissectorRedis.Register(extensionRedis)
|
||||
extensionRedis.Dissector = dissectorRedis
|
||||
Extensions = append(Extensions, extensionRedis)
|
||||
ExtensionsMap[extensionRedis.Protocol.Name] = extensionRedis
|
||||
}
|
||||
|
||||
sort.Slice(Extensions, func(i, j int) bool {
|
||||
return Extensions[i].Protocol.Priority < Extensions[j].Protocol.Priority
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
basenine "github.com/up9inc/basenine/client/go"
|
||||
"net"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/up9inc/mizu/agent/pkg/dependency"
|
||||
"github.com/up9inc/mizu/agent/pkg/oas"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/up9inc/mizu/agent/pkg/dependency"
|
||||
"github.com/up9inc/mizu/agent/pkg/oas"
|
||||
)
|
||||
|
||||
func TestGetOASServers(t *testing.T) {
|
||||
@@ -37,33 +32,14 @@ func TestGetOASSpec(t *testing.T) {
|
||||
t.Logf("Written body: %s", recorder.Body.String())
|
||||
}
|
||||
|
||||
type fakeConn struct {
|
||||
sendBuffer *bytes.Buffer
|
||||
receiveBuffer *bytes.Buffer
|
||||
}
|
||||
|
||||
func (f fakeConn) Read(p []byte) (int, error) { return f.sendBuffer.Read(p) }
|
||||
func (f fakeConn) Write(p []byte) (int, error) { return f.receiveBuffer.Write(p) }
|
||||
func (fakeConn) Close() error { return nil }
|
||||
func (fakeConn) LocalAddr() net.Addr { return nil }
|
||||
func (fakeConn) RemoteAddr() net.Addr { return nil }
|
||||
func (fakeConn) SetDeadline(t time.Time) error { return nil }
|
||||
func (fakeConn) SetReadDeadline(t time.Time) error { return nil }
|
||||
func (fakeConn) SetWriteDeadline(t time.Time) error { return nil }
|
||||
|
||||
func getRecorderAndContext() (*httptest.ResponseRecorder, *gin.Context) {
|
||||
dummyConn := new(basenine.Connection)
|
||||
dummyConn.Conn = fakeConn{
|
||||
sendBuffer: bytes.NewBufferString("\n"),
|
||||
receiveBuffer: bytes.NewBufferString("\n"),
|
||||
}
|
||||
dependency.RegisterGenerator(dependency.OasGeneratorDependency, func() interface{} {
|
||||
return oas.GetDefaultOasGeneratorInstance()
|
||||
})
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(recorder)
|
||||
oas.GetDefaultOasGeneratorInstance().Start(dummyConn)
|
||||
oas.GetDefaultOasGeneratorInstance().Start()
|
||||
oas.GetDefaultOasGeneratorInstance().GetServiceSpecs().Store("some", oas.NewGen("some"))
|
||||
return recorder, c
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ type DependencyContainerType string
|
||||
const (
|
||||
ServiceMapGeneratorDependency = "ServiceMapGeneratorDependency"
|
||||
OasGeneratorDependency = "OasGeneratorDependency"
|
||||
EntriesInserter = "EntriesInserter"
|
||||
EntriesProvider = "EntriesProvider"
|
||||
EntriesSocketStreamer = "EntriesSocketStreamer"
|
||||
EntryStreamerSocketConnector = "EntryStreamerSocketConnector"
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
package elastic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/elastic/go-elasticsearch/v7"
|
||||
"github.com/up9inc/mizu/logger"
|
||||
"github.com/up9inc/mizu/shared"
|
||||
"github.com/up9inc/mizu/tap/api"
|
||||
)
|
||||
|
||||
type client struct {
|
||||
es *elasticsearch.Client
|
||||
index string
|
||||
insertedCount int
|
||||
}
|
||||
|
||||
var instance *client
|
||||
var once sync.Once
|
||||
|
||||
func GetInstance() *client {
|
||||
once.Do(func() {
|
||||
instance = newClient()
|
||||
})
|
||||
return instance
|
||||
}
|
||||
|
||||
func (client *client) Configure(config shared.ElasticConfig) {
|
||||
if config.Url == "" || config.User == "" || config.Password == "" {
|
||||
if client.es != nil {
|
||||
client.es = nil
|
||||
}
|
||||
logger.Log.Infof("No elastic configuration was supplied, elastic exporter disabled")
|
||||
return
|
||||
}
|
||||
transport := http.DefaultTransport
|
||||
tlsClientConfig := &tls.Config{InsecureSkipVerify: true}
|
||||
transport.(*http.Transport).TLSClientConfig = tlsClientConfig
|
||||
cfg := elasticsearch.Config{
|
||||
Addresses: []string{config.Url},
|
||||
Username: config.User,
|
||||
Password: config.Password,
|
||||
Transport: transport,
|
||||
}
|
||||
|
||||
es, err := elasticsearch.NewClient(cfg)
|
||||
if err != nil {
|
||||
logger.Log.Errorf("Failed to initialize elastic client %v", err)
|
||||
}
|
||||
|
||||
// Have the client instance return a response
|
||||
res, err := es.Info()
|
||||
if err != nil {
|
||||
logger.Log.Errorf("Elastic client.Info() ERROR: %v", err)
|
||||
} else {
|
||||
client.es = es
|
||||
client.index = "mizu_traffic_http_" + time.Now().Format("2006_01_02_15_04")
|
||||
client.insertedCount = 0
|
||||
logger.Log.Infof("Elastic client configured, index: %s, cluster info: %v", client.index, res)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
}
|
||||
|
||||
func newClient() *client {
|
||||
return &client{
|
||||
es: nil,
|
||||
index: "",
|
||||
}
|
||||
}
|
||||
|
||||
type httpEntry struct {
|
||||
Source *api.TCP `json:"src"`
|
||||
Destination *api.TCP `json:"dst"`
|
||||
Outgoing bool `json:"outgoing"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Request map[string]interface{} `json:"request"`
|
||||
Response map[string]interface{} `json:"response"`
|
||||
ElapsedTime int64 `json:"elapsedTime"`
|
||||
}
|
||||
|
||||
func (client *client) PushEntry(entry *api.Entry) {
|
||||
if client.es == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if entry.Protocol.Name != "http" {
|
||||
return
|
||||
}
|
||||
|
||||
entryToPush := httpEntry{
|
||||
Source: entry.Source,
|
||||
Destination: entry.Destination,
|
||||
Outgoing: entry.Outgoing,
|
||||
CreatedAt: entry.StartTime,
|
||||
Request: entry.Request,
|
||||
Response: entry.Response,
|
||||
ElapsedTime: entry.ElapsedTime,
|
||||
}
|
||||
|
||||
entryJson, err := json.Marshal(entryToPush)
|
||||
if err != nil {
|
||||
logger.Log.Errorf("json.Marshal ERROR: %v", err)
|
||||
return
|
||||
}
|
||||
var buffer bytes.Buffer
|
||||
buffer.WriteString(string(entryJson))
|
||||
res, _ := client.es.Index(client.index, &buffer)
|
||||
if res.StatusCode == 201 {
|
||||
client.insertedCount += 1
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,11 @@
|
||||
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"
|
||||
|
||||
"github.com/up9inc/mizu/logger"
|
||||
@@ -19,22 +16,20 @@ var (
|
||||
instance *defaultOasGenerator
|
||||
)
|
||||
|
||||
type OasGeneratorSink interface {
|
||||
HandleEntry(mizuEntry *api.Entry)
|
||||
}
|
||||
|
||||
type OasGenerator interface {
|
||||
Start(conn *basenine.Connection)
|
||||
Start()
|
||||
Stop()
|
||||
IsStarted() bool
|
||||
GetServiceSpecs() *sync.Map
|
||||
SetEntriesQuery(query string) bool
|
||||
}
|
||||
|
||||
type defaultOasGenerator struct {
|
||||
started bool
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
serviceSpecs *sync.Map
|
||||
dbConn *basenine.Connection
|
||||
dbMutex sync.Mutex
|
||||
entriesQuery string
|
||||
}
|
||||
|
||||
func GetDefaultOasGeneratorInstance() *defaultOasGenerator {
|
||||
@@ -45,102 +40,29 @@ func GetDefaultOasGeneratorInstance() *defaultOasGenerator {
|
||||
return instance
|
||||
}
|
||||
|
||||
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{}
|
||||
|
||||
func (g *defaultOasGenerator) Start() {
|
||||
g.started = true
|
||||
|
||||
go g.runGenerator()
|
||||
}
|
||||
|
||||
func (g *defaultOasGenerator) Stop() {
|
||||
if !g.started {
|
||||
return
|
||||
}
|
||||
|
||||
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 {
|
||||
return g.started
|
||||
}
|
||||
|
||||
func (g *defaultOasGenerator) runGenerator() {
|
||||
// Make []byte channels to receive the data and the meta
|
||||
dataChan := make(chan []byte)
|
||||
metaChan := make(chan []byte)
|
||||
|
||||
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)
|
||||
func (g *defaultOasGenerator) HandleEntry(mizuEntry *api.Entry) {
|
||||
if !g.started {
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-g.ctx.Done():
|
||||
logger.Log.Infof("OAS Generator was canceled")
|
||||
close(dataChan)
|
||||
close(metaChan)
|
||||
return
|
||||
|
||||
case metaBytes, ok := <-metaChan:
|
||||
if !ok {
|
||||
logger.Log.Infof("OAS Generator - meta channel closed")
|
||||
break
|
||||
}
|
||||
logger.Log.Debugf("Meta: %s", metaBytes)
|
||||
|
||||
case dataBytes, ok := <-dataChan:
|
||||
if !ok {
|
||||
logger.Log.Infof("OAS Generator - entries channel closed")
|
||||
break
|
||||
}
|
||||
|
||||
logger.Log.Debugf("Data: %s", dataBytes)
|
||||
e := new(api.Entry)
|
||||
err := json.Unmarshal(dataBytes, e)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
g.handleEntry(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *defaultOasGenerator) handleEntry(mizuEntry *api.Entry) {
|
||||
if mizuEntry.Protocol.Name == "http" {
|
||||
dest := mizuEntry.Destination.Name
|
||||
if dest == "" {
|
||||
@@ -210,18 +132,9 @@ func (g *defaultOasGenerator) GetServiceSpecs() *sync.Map {
|
||||
return g.serviceSpecs
|
||||
}
|
||||
|
||||
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: nil,
|
||||
serviceSpecs: &sync.Map{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
func TestOASGen(t *testing.T) {
|
||||
gen := new(defaultOasGenerator)
|
||||
gen := GetDefaultOasGeneratorInstance()
|
||||
|
||||
e := new(har.Entry)
|
||||
err := json.Unmarshal([]byte(`{"startedDateTime": "20000101","request": {"url": "https://host/path", "method": "GET"}, "response": {"status": 200}}`), e)
|
||||
@@ -21,8 +21,7 @@ func TestOASGen(t *testing.T) {
|
||||
Entry: *e,
|
||||
}
|
||||
|
||||
dummyConn := GetFakeDBConn(`{"startedDateTime": "20000101","request": {"url": "https://host/path", "method": "GET"}, "response": {"status": 200}}`)
|
||||
gen.Start(dummyConn)
|
||||
gen.Start()
|
||||
gen.handleHARWithSource(ews)
|
||||
g, ok := gen.serviceSpecs.Load("some")
|
||||
if !ok {
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
package oas
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
@@ -13,22 +11,11 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/chanced/openapi"
|
||||
"github.com/up9inc/mizu/agent/pkg/har"
|
||||
"github.com/up9inc/mizu/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, "", " ")
|
||||
@@ -278,17 +265,3 @@ 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 }
|
||||
|
||||
@@ -327,7 +327,7 @@ BasenineReconnect:
|
||||
go handleMetaChannel(&wg, connection, meta)
|
||||
wg.Add(2)
|
||||
|
||||
if err = connection.Query("", query, data, meta); err != nil {
|
||||
if err = connection.Query("latest", query, data, meta); err != nil {
|
||||
logger.Log.Errorf("Query mode call failed: %v", err)
|
||||
connection.Close()
|
||||
time.Sleep(shared.BasenineReconnectInterval * time.Second)
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/creasty/defaults"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/up9inc/mizu/cli/config/configStructs"
|
||||
"github.com/up9inc/mizu/cli/telemetry"
|
||||
"github.com/up9inc/mizu/logger"
|
||||
)
|
||||
|
||||
var installCmd = &cobra.Command{
|
||||
@@ -17,4 +20,11 @@ var installCmd = &cobra.Command{
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(installCmd)
|
||||
|
||||
defaultInstallConfig := configStructs.InstallConfig{}
|
||||
if err := defaults.Set(&defaultInstallConfig); err != nil {
|
||||
logger.Log.Debug(err)
|
||||
}
|
||||
|
||||
installCmd.Flags().BoolP(configStructs.OutInstallName, "o", defaultInstallConfig.Out, "print (to stdout) Kubernetes manifest used to install Mizu Pro edition")
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/up9inc/mizu/cli/bucket"
|
||||
"github.com/up9inc/mizu/cli/config"
|
||||
@@ -9,12 +10,23 @@ import (
|
||||
)
|
||||
|
||||
func runMizuInstall() {
|
||||
bucketProvider := bucket.NewProvider(config.Config.Install.TemplateUrl, bucket.DefaultTimeout)
|
||||
installTemplate, err := bucketProvider.GetInstallTemplate(config.Config.Install.TemplateName)
|
||||
if err != nil {
|
||||
logger.Log.Errorf("Failed getting install template, err: %v", err)
|
||||
if config.Config.Install.Out {
|
||||
bucketProvider := bucket.NewProvider(config.Config.Install.TemplateUrl, bucket.DefaultTimeout)
|
||||
installTemplate, err := bucketProvider.GetInstallTemplate(config.Config.Install.TemplateName)
|
||||
if err != nil {
|
||||
logger.Log.Errorf("Failed getting install template, err: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Print(installTemplate)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Print(installTemplate)
|
||||
var sb strings.Builder
|
||||
sb.WriteString("Hello! This command can be used to install Mizu Pro edition on your Kubernetes cluster.")
|
||||
sb.WriteString("\nPlease run:")
|
||||
sb.WriteString("\n\tmizu install -o | kubectl apply -f -")
|
||||
sb.WriteString("\n\nor use helm chart as described in https://getmizu.io/docs/installing-mizu/centralized-installation\n")
|
||||
|
||||
fmt.Print(sb.String())
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ func init() {
|
||||
tapCmd.Flags().Bool(configStructs.AnalysisTapName, defaultTapConfig.Analysis, "Uploads traffic to UP9 for further analysis (Beta)")
|
||||
tapCmd.Flags().BoolP(configStructs.AllNamespacesTapName, "A", defaultTapConfig.AllNamespaces, "Tap all namespaces")
|
||||
tapCmd.Flags().StringSliceP(configStructs.PlainTextFilterRegexesTapName, "r", defaultTapConfig.PlainTextFilterRegexes, "List of regex expressions that are used to filter matching values from text/plain http bodies")
|
||||
tapCmd.Flags().Bool(configStructs.DisableRedactionTapName, defaultTapConfig.DisableRedaction, "Disables redaction of potentially sensitive request/response headers and body values")
|
||||
tapCmd.Flags().Bool(configStructs.EnableRedactionTapName, defaultTapConfig.EnableRedaction, "Enables redaction of potentially sensitive request/response headers and body values")
|
||||
tapCmd.Flags().String(configStructs.HumanMaxEntriesDBSizeTapName, defaultTapConfig.HumanMaxEntriesDBSize, "Override the default max entries db size")
|
||||
tapCmd.Flags().String(configStructs.InsertionFilterName, defaultTapConfig.InsertionFilter, "Set the insertion filter. Accepts string or a file path.")
|
||||
tapCmd.Flags().Bool(configStructs.DryRunTapName, defaultTapConfig.DryRun, "Preview of all pods matching the regex, without tapping them")
|
||||
@@ -123,4 +123,5 @@ func init() {
|
||||
tapCmd.Flags().String(configStructs.ContractFile, defaultTapConfig.ContractFile, "OAS/Swagger file to validate to monitor the contracts")
|
||||
tapCmd.Flags().Bool(configStructs.ServiceMeshName, defaultTapConfig.ServiceMesh, "Record decrypted traffic if the cluster is configured with a service mesh and with mtls")
|
||||
tapCmd.Flags().Bool(configStructs.TlsName, defaultTapConfig.Tls, "Record tls traffic")
|
||||
tapCmd.Flags().Bool(configStructs.ProfilerName, defaultTapConfig.Profiler, "Run pprof server")
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ func RunMizuTap() {
|
||||
}
|
||||
|
||||
logger.Log.Infof("Waiting for Mizu Agent to start...")
|
||||
if state.mizuServiceAccountExists, err = resources.CreateTapMizuResources(ctx, kubernetesProvider, serializedValidationRules, serializedContract, serializedMizuConfig, config.Config.IsNsRestrictedMode(), config.Config.MizuResourcesNamespace, config.Config.AgentImage, getSyncEntriesConfig(), config.Config.Tap.MaxEntriesDBSizeBytes(), config.Config.Tap.ApiServerResources, config.Config.ImagePullPolicy(), config.Config.LogLevel()); err != nil {
|
||||
if state.mizuServiceAccountExists, err = resources.CreateTapMizuResources(ctx, kubernetesProvider, serializedValidationRules, serializedContract, serializedMizuConfig, config.Config.IsNsRestrictedMode(), config.Config.MizuResourcesNamespace, config.Config.AgentImage, getSyncEntriesConfig(), config.Config.Tap.MaxEntriesDBSizeBytes(), config.Config.Tap.ApiServerResources, config.Config.ImagePullPolicy(), config.Config.LogLevel(), config.Config.Tap.Profiler); err != nil {
|
||||
var statusError *k8serrors.StatusError
|
||||
if errors.As(err, &statusError) && (statusError.ErrStatus.Reason == metav1.StatusReasonAlreadyExists) {
|
||||
logger.Log.Info("Mizu is already running in this namespace, change the `mizu-resources-namespace` configuration or run `mizu clean` to remove the currently running Mizu instance")
|
||||
@@ -164,7 +164,6 @@ func getTapMizuAgentConfig() *shared.MizuAgentConfig {
|
||||
ServiceMap: config.Config.ServiceMap,
|
||||
OAS: config.Config.OAS,
|
||||
Telemetry: config.Config.Telemetry,
|
||||
Elastic: config.Config.Elastic,
|
||||
}
|
||||
|
||||
return &mizuAgentConfig
|
||||
@@ -292,7 +291,7 @@ func getMizuApiFilteringOptions() (*api.TrafficFilteringOptions, error) {
|
||||
return &api.TrafficFilteringOptions{
|
||||
PlainTextMaskingRegexes: compiledRegexSlice,
|
||||
IgnoredUserAgents: config.Config.Tap.IgnoredUserAgents,
|
||||
DisableRedaction: config.Config.Tap.DisableRedaction,
|
||||
EnableRedaction: config.Config.Tap.EnableRedaction,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,6 @@ type ConfigStruct struct {
|
||||
LogLevelStr string `yaml:"log-level,omitempty" default:"INFO" readonly:""`
|
||||
ServiceMap bool `yaml:"service-map" default:"true"`
|
||||
OAS bool `yaml:"oas" default:"true"`
|
||||
Elastic shared.ElasticConfig `yaml:"elastic"`
|
||||
}
|
||||
|
||||
func (config *ConfigStruct) validate() error {
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
package configStructs
|
||||
|
||||
const (
|
||||
OutInstallName = "out"
|
||||
)
|
||||
|
||||
type InstallConfig struct {
|
||||
TemplateUrl string `yaml:"template-url" default:"https://storage.googleapis.com/static.up9.io/mizu/helm-template"`
|
||||
TemplateName string `yaml:"template-name" default:"helm-template.yaml"`
|
||||
Out bool `yaml:"out"`
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ const (
|
||||
AnalysisTapName = "analysis"
|
||||
AllNamespacesTapName = "all-namespaces"
|
||||
PlainTextFilterRegexesTapName = "regex-masking"
|
||||
DisableRedactionTapName = "no-redact"
|
||||
EnableRedactionTapName = "redact"
|
||||
HumanMaxEntriesDBSizeTapName = "max-entries-db-size"
|
||||
InsertionFilterName = "insertion-filter"
|
||||
DryRunTapName = "dry-run"
|
||||
@@ -30,6 +30,7 @@ const (
|
||||
ContractFile = "contract"
|
||||
ServiceMeshName = "service-mesh"
|
||||
TlsName = "tls"
|
||||
ProfilerName = "profiler"
|
||||
)
|
||||
|
||||
type TapConfig struct {
|
||||
@@ -42,7 +43,7 @@ type TapConfig struct {
|
||||
AllNamespaces bool `yaml:"all-namespaces" default:"false"`
|
||||
PlainTextFilterRegexes []string `yaml:"regex-masking"`
|
||||
IgnoredUserAgents []string `yaml:"ignored-user-agents"`
|
||||
DisableRedaction bool `yaml:"no-redact" default:"false"`
|
||||
EnableRedaction bool `yaml:"redact" default:"false"`
|
||||
HumanMaxEntriesDBSize string `yaml:"max-entries-db-size" default:"200MB"`
|
||||
InsertionFilter string `yaml:"insertion-filter" default:""`
|
||||
DryRun bool `yaml:"dry-run" default:"false"`
|
||||
@@ -54,6 +55,7 @@ type TapConfig struct {
|
||||
TapperResources shared.Resources `yaml:"tapper-resources"`
|
||||
ServiceMesh bool `yaml:"service-mesh" default:"false"`
|
||||
Tls bool `yaml:"tls" default:"false"`
|
||||
Profiler bool `yaml:"profiler" default:"false"`
|
||||
}
|
||||
|
||||
func (config *TapConfig) PodRegex() *regexp.Regexp {
|
||||
|
||||
@@ -74,6 +74,7 @@ require (
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/russross/blackfriday v1.6.0 // indirect
|
||||
github.com/stretchr/testify v1.7.0 // indirect
|
||||
github.com/up9inc/mizu/tap/dbgctl v0.0.0 // indirect
|
||||
github.com/xlab/treeprint v1.1.0 // indirect
|
||||
go.starlark.net v0.0.0-20220203230714-bb14e151c28f // indirect
|
||||
golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab // indirect
|
||||
@@ -104,3 +105,5 @@ replace github.com/up9inc/mizu/logger v0.0.0 => ../logger
|
||||
replace github.com/up9inc/mizu/shared v0.0.0 => ../shared
|
||||
|
||||
replace github.com/up9inc/mizu/tap/api v0.0.0 => ../tap/api
|
||||
|
||||
replace github.com/up9inc/mizu/tap/dbgctl v0.0.0 => ../tap/dbgctl
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
core "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func CreateTapMizuResources(ctx context.Context, kubernetesProvider *kubernetes.Provider, serializedValidationRules string, serializedContract string, serializedMizuConfig string, isNsRestrictedMode bool, mizuResourcesNamespace string, agentImage string, syncEntriesConfig *shared.SyncEntriesConfig, maxEntriesDBSizeBytes int64, apiServerResources shared.Resources, imagePullPolicy core.PullPolicy, logLevel logging.Level) (bool, error) {
|
||||
func CreateTapMizuResources(ctx context.Context, kubernetesProvider *kubernetes.Provider, serializedValidationRules string, serializedContract string, serializedMizuConfig string, isNsRestrictedMode bool, mizuResourcesNamespace string, agentImage string, syncEntriesConfig *shared.SyncEntriesConfig, maxEntriesDBSizeBytes int64, apiServerResources shared.Resources, imagePullPolicy core.PullPolicy, logLevel logging.Level, profiler bool) (bool, error) {
|
||||
if !isNsRestrictedMode {
|
||||
if err := createMizuNamespace(ctx, kubernetesProvider, mizuResourcesNamespace); err != nil {
|
||||
return false, err
|
||||
@@ -50,6 +50,7 @@ func CreateTapMizuResources(ctx context.Context, kubernetesProvider *kubernetes.
|
||||
Resources: apiServerResources,
|
||||
ImagePullPolicy: imagePullPolicy,
|
||||
LogLevel: logLevel,
|
||||
Profiler: profiler,
|
||||
}
|
||||
|
||||
if err := createMizuApiServerPod(ctx, kubernetesProvider, opts); err != nil {
|
||||
|
||||
107
performance_analysis/README.md
Normal file
107
performance_analysis/README.md
Normal file
@@ -0,0 +1,107 @@
|
||||
|
||||
# Performance analysis
|
||||
|
||||
This directory contains tools for analyzing tapper performance.
|
||||
|
||||
# Periodic tapper logs
|
||||
|
||||
In tapper logs there are some periodic lines that shows its internal state and consumed resources.
|
||||
|
||||
Internal state example (formatted and commented):
|
||||
```
|
||||
stats - {
|
||||
"processedBytes":468940592, // how many bytes we read from pcap
|
||||
"packetsCount":174883, // how many packets we read from pcap
|
||||
"tcpPacketsCount":174883, // how many tcp packets we read from pcap
|
||||
"reassembledTcpPayloadsCount":66893, // how many chunks sent to tcp stream
|
||||
"matchedPairs":24821, // how many request response pairs found
|
||||
"droppedTcpStreams":2 // how many tcp streams remained stale and dropped
|
||||
}
|
||||
```
|
||||
|
||||
Consumed resources example (formatted and commented):
|
||||
```
|
||||
mem: 24441240, // golang heap size
|
||||
goroutines: 29, // how many goroutines
|
||||
cpu: 91.208791, // how much cpu the tapper process consume (in percentage per core)
|
||||
cores: 16, // how many cores there are on the machine
|
||||
rss: 87052288 // how many bytes held by the tapper process
|
||||
```
|
||||
|
||||
# Plot tapper logs
|
||||
|
||||
In order to plot a tapper log or many logs into a graph, use the `plot_from_tapper_logs.py` util.
|
||||
|
||||
It gets a list of tapper logs as a parameter, and output an image with a nice graph.
|
||||
|
||||
The log file names should be named in this format `XX_DESCRIPTION.log` when XX is the number between determining the color of the output graph and description is the name of the series. It allows for easy comparison between various modes.
|
||||
|
||||
Example run:
|
||||
```
|
||||
cd $MIZU_HOME/performance_analysis
|
||||
virtualenv venv
|
||||
source venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
python plot_from_tapper_logs.py 00_tapper.log
|
||||
```
|
||||
|
||||
# Tapper Modes
|
||||
|
||||
Every packet seen by the tapper is processed in a pipeline that contains various stages.
|
||||
* Pcap - Read the packet from libpcap
|
||||
* Assembler - Assemble the packet into a TcpStream
|
||||
* TcpStream - Hold stream information and TcpReaders
|
||||
* Dissectors - Read from TcpReader and recognize the packet content and protocol.
|
||||
* Emit - Marshal the request response pair into a Json
|
||||
* Send - Send the Json to Api Server
|
||||
|
||||
Tapper can be run with various debug modes:
|
||||
* No Pcap - Start the tapper process, but don't read from any packets from pcap
|
||||
* No Assembler - Read packets from pcap, but don't assemble them
|
||||
* No TcpStream - Assemble the packets, but don't create TcpStream for them
|
||||
* No Dissectors - Create a TcpStream for the packets, but don't dissect their content
|
||||
* No Emit - Dissect the TcpStream, but don't emit the matched request response pair
|
||||
* No Send - Emit the request response pair, but don't send them to the Api Server.
|
||||
* Regular mode
|
||||
|
||||

|
||||
|
||||
# Run benchmark with various tapper modes
|
||||
|
||||
## Prerequisite
|
||||
|
||||
In order to run the benchmark you probably want:
|
||||
1. An up and running Api Server
|
||||
2. An up and running Basenine
|
||||
3. An up and running UI (optional)
|
||||
4. An up and running test server, like nginx, that can return a known payload at a known endpoint.
|
||||
5. Set MIZU_HOME environment variable to points to mizu directory
|
||||
6. Install the `hey` tool
|
||||
|
||||
## Running the benchmark
|
||||
|
||||
In order to run a benchmark use the `run_tapper_benchmark.sh` script.
|
||||
|
||||
Example run:
|
||||
```
|
||||
cd $MIZU_HOME/performance_analysis
|
||||
source venv/bin/activate # Assuming you already run plot_from_tapper_logs.py
|
||||
./run_tapper_benchmark.sh
|
||||
```
|
||||
|
||||
Running it without params use the default values, use the following environment variables for customization:
|
||||
```
|
||||
export=MIZU_BENCHMARK_OUTPUT_DIR=/path/to/dir # Set the output directory for tapper logs and graph
|
||||
export=MIZU_BENCHMARK_CLIENT_PERIOD=1m # How long each test run
|
||||
export=MIZU_BENCHMARK_URL=http://server:port/path # The URL to use for the benchmarking process (the test server endpoint)
|
||||
export=MIZU_BENCHMARK_RUN_COUNT=3 # How many times each tapper mode should run
|
||||
export=MIZU_BENCHMARK_QPS=250 # How many queries per second the each client should send to the test server
|
||||
export=MIZU_BENCHMARK_CLIENTS_COUNT=5 # How many clients should run in parallel during the benchmark
|
||||
```
|
||||
|
||||
# Example output graph
|
||||
|
||||
An example output graph from a 15 min run with 15K payload and 1000 QPS looks like
|
||||
|
||||

|
||||
|
||||
BIN
performance_analysis/example-graph.png
Normal file
BIN
performance_analysis/example-graph.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 327 KiB |
182
performance_analysis/plot_from_tapper_logs.py
Normal file
182
performance_analysis/plot_from_tapper_logs.py
Normal file
@@ -0,0 +1,182 @@
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import pathlib
|
||||
import re
|
||||
import sys
|
||||
import typing
|
||||
|
||||
COLORMAP = plt.get_cmap('turbo')
|
||||
|
||||
# Extract cpu and rss samples from log files and plot them
|
||||
# Input: List of log files
|
||||
#
|
||||
# example:
|
||||
# python plot_from_tapper_logs.py 01_no_pcap_01.log 99_normal_00.log
|
||||
#
|
||||
# The script assumes that the log file names start with a number (pattern '\d+')
|
||||
# and groups based on this number. Files that start will the same number will be plotted with the same color.
|
||||
# Change group_pattern to an empty string to disable this, or change to a regex of your liking.
|
||||
|
||||
|
||||
def get_sample(name: str, line: str, default_value: float):
|
||||
pattern = name + r': ?(\d+(\.\d+)?)'
|
||||
maybe_sample = re.findall(pattern, line)
|
||||
if len(maybe_sample) == 0:
|
||||
return default_value
|
||||
|
||||
sample = float(maybe_sample[0][0])
|
||||
return sample
|
||||
|
||||
|
||||
def append_sample(name: str, line: str, samples: typing.List[float]):
|
||||
sample = get_sample(name, line, -1)
|
||||
|
||||
if sample == -1:
|
||||
return
|
||||
|
||||
samples.append(sample)
|
||||
|
||||
|
||||
def extract_samples(f: typing.IO) -> typing.Tuple[pd.Series, pd.Series, pd.Series, pd.Series, pd.Series, pd.Series, pd.Series, pd.Series]:
|
||||
cpu_samples = []
|
||||
rss_samples = []
|
||||
count_samples = []
|
||||
matched_samples = []
|
||||
live_samples = []
|
||||
processed_samples = []
|
||||
heap_samples = []
|
||||
goroutines_samples = []
|
||||
for line in f:
|
||||
append_sample('cpu', line, cpu_samples)
|
||||
append_sample('rss', line, rss_samples)
|
||||
ignored_packets_count = get_sample('"ignoredPacketsCount"', line, -1)
|
||||
packets_count = get_sample('"packetsCount"', line, -1)
|
||||
if ignored_packets_count != -1 and packets_count != -1:
|
||||
count_samples.append(packets_count - ignored_packets_count)
|
||||
append_sample('"matchedPairs"', line, matched_samples)
|
||||
append_sample('"liveTcpStreams"', line, live_samples)
|
||||
append_sample('"processedBytes"', line, processed_samples)
|
||||
append_sample('mem', line, heap_samples)
|
||||
append_sample('goroutines', line, goroutines_samples)
|
||||
|
||||
cpu_samples = pd.Series(cpu_samples)
|
||||
rss_samples = pd.Series(rss_samples)
|
||||
count_samples = pd.Series(count_samples)
|
||||
matched_samples = pd.Series(matched_samples)
|
||||
live_samples = pd.Series(live_samples)
|
||||
processed_samples = pd.Series(processed_samples)
|
||||
heap_samples = pd.Series(heap_samples)
|
||||
goroutines_samples = pd.Series(goroutines_samples)
|
||||
|
||||
return cpu_samples, rss_samples, count_samples, matched_samples, live_samples, processed_samples, heap_samples, goroutines_samples
|
||||
|
||||
|
||||
def plot(ax, df: pd.DataFrame, title: str, xlabel: str, ylabel: str, group_pattern: typing.Optional[str]):
|
||||
if group_pattern:
|
||||
color = get_group_color(df.columns, group_pattern)
|
||||
df.plot(color=color, ax=ax)
|
||||
else:
|
||||
df.plot(cmap=COLORMAP, ax=ax)
|
||||
|
||||
ax.ticklabel_format(style='plain')
|
||||
plt.title(title)
|
||||
plt.legend()
|
||||
plt.xlabel(xlabel)
|
||||
plt.ylabel(ylabel)
|
||||
|
||||
|
||||
def get_group_color(names, pattern):
|
||||
props = [int(re.findall(pattern, pathlib.Path(name).name)[0]) for name in names]
|
||||
key = dict(zip(sorted(list(set(props))), range(len(set(props)))))
|
||||
n_colors = len(key)
|
||||
color_options = plt.get_cmap('jet')(np.linspace(0, 1, n_colors))
|
||||
groups = [key[prop] for prop in props]
|
||||
color = color_options[groups] # type: ignore
|
||||
return color
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
filenames = sys.argv[1:]
|
||||
|
||||
cpu_samples_all_files = []
|
||||
rss_samples_all_files = []
|
||||
count_samples_all_files = []
|
||||
matched_samples_all_files = []
|
||||
live_samples_all_files = []
|
||||
processed_samples_all_files = []
|
||||
heap_samples_all_files = []
|
||||
goroutines_samples_all_files = []
|
||||
|
||||
for ii, filename in enumerate(filenames):
|
||||
print("Analyzing {}".format(filename))
|
||||
with open(filename, 'r') as f:
|
||||
cpu_samples, rss_samples, count_samples, matched_samples, live_samples, processed_samples, heap_samples, goroutines_samples = extract_samples(f)
|
||||
|
||||
cpu_samples.name = pathlib.Path(filename).name
|
||||
rss_samples.name = pathlib.Path(filename).name
|
||||
count_samples.name = pathlib.Path(filename).name
|
||||
matched_samples.name = pathlib.Path(filename).name
|
||||
live_samples.name = pathlib.Path(filename).name
|
||||
processed_samples.name = pathlib.Path(filename).name
|
||||
heap_samples.name = pathlib.Path(filename).name
|
||||
goroutines_samples.name = pathlib.Path(filename).name
|
||||
|
||||
cpu_samples_all_files.append(cpu_samples)
|
||||
rss_samples_all_files.append(rss_samples)
|
||||
count_samples_all_files.append(count_samples)
|
||||
matched_samples_all_files.append(matched_samples)
|
||||
live_samples_all_files.append(live_samples)
|
||||
processed_samples_all_files.append(processed_samples)
|
||||
heap_samples_all_files.append(heap_samples)
|
||||
goroutines_samples_all_files.append(goroutines_samples)
|
||||
|
||||
cpu_samples_df = pd.concat(cpu_samples_all_files, axis=1)
|
||||
rss_samples_df = pd.concat(rss_samples_all_files, axis=1)
|
||||
count_samples_df = pd.concat(count_samples_all_files, axis=1)
|
||||
matched_samples_df = pd.concat(matched_samples_all_files, axis=1)
|
||||
live_samples_df = pd.concat(live_samples_all_files, axis=1)
|
||||
processed_samples_df = pd.concat(processed_samples_all_files, axis=1)
|
||||
heap_samples_df = pd.concat(heap_samples_all_files, axis=1)
|
||||
goroutines_samples_df = pd.concat(goroutines_samples_all_files, axis=1)
|
||||
|
||||
group_pattern = r'^\d+'
|
||||
|
||||
cpu_plot = plt.subplot(8, 2, 1)
|
||||
plot(cpu_plot, cpu_samples_df, 'cpu', '', 'cpu (%)', group_pattern)
|
||||
cpu_plot.legend().remove()
|
||||
|
||||
mem_plot = plt.subplot(8, 2, 2)
|
||||
plot(mem_plot, (rss_samples_df / 1024 / 1024), 'rss', '', 'mem (mega)', group_pattern)
|
||||
mem_plot.legend(loc='center left', bbox_to_anchor=(1, 0.5))
|
||||
|
||||
packets_plot = plt.subplot(8, 2, 3)
|
||||
plot(packets_plot, count_samples_df, 'packetsCount', '', 'packetsCount', group_pattern)
|
||||
packets_plot.legend().remove()
|
||||
|
||||
matched_plot = plt.subplot(8, 2, 4)
|
||||
plot(matched_plot, matched_samples_df, 'matchedCount', '', 'matchedCount', group_pattern)
|
||||
matched_plot.legend().remove()
|
||||
|
||||
live_plot = plt.subplot(8, 2, 5)
|
||||
plot(live_plot, live_samples_df, 'liveStreamsCount', '', 'liveStreamsCount', group_pattern)
|
||||
live_plot.legend().remove()
|
||||
|
||||
processed_plot = plt.subplot(8, 2, 6)
|
||||
plot(processed_plot, (processed_samples_df / 1024 / 1024), 'processedBytes', '', 'bytes (mega)', group_pattern)
|
||||
processed_plot.legend().remove()
|
||||
|
||||
heap_plot = plt.subplot(8, 2, 7)
|
||||
plot(heap_plot, (heap_samples_df / 1024 / 1024), 'heap', '', 'heap (mega)', group_pattern)
|
||||
heap_plot.legend().remove()
|
||||
|
||||
goroutines_plot = plt.subplot(8, 2, 8)
|
||||
plot(goroutines_plot, goroutines_samples_df, 'goroutines', '', 'goroutines', group_pattern)
|
||||
goroutines_plot.legend().remove()
|
||||
|
||||
fig = plt.gcf()
|
||||
fig.set_size_inches(20, 18)
|
||||
|
||||
print('Saving graph to graph.png')
|
||||
plt.savefig('graph.png', bbox_inches='tight')
|
||||
|
||||
2
performance_analysis/requirements.txt
Normal file
2
performance_analysis/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
matplotlib
|
||||
pandas
|
||||
100
performance_analysis/run_tapper_benchmark.sh
Executable file
100
performance_analysis/run_tapper_benchmark.sh
Executable file
@@ -0,0 +1,100 @@
|
||||
#!/bin/bash
|
||||
|
||||
[ -z "$MIZU_HOME" ] && { echo "MIZU_HOME is missing"; exit 1; }
|
||||
[ -z "$MIZU_BENCHMARK_OUTPUT_DIR" ] && export MIZU_BENCHMARK_OUTPUT_DIR="/tmp/mizu-benchmark-results-$(date +%d-%m-%H-%M)"
|
||||
[ -z "$MIZU_BENCHMARK_CLIENT_PERIOD" ] && export MIZU_BENCHMARK_CLIENT_PERIOD="1m"
|
||||
[ -z "$MIZU_BENCHMARK_URL" ] && export MIZU_BENCHMARK_URL="http://localhost:8081/data/b.1000.json"
|
||||
[ -z "$MIZU_BENCHMARK_RUN_COUNT" ] && export MIZU_BENCHMARK_RUN_COUNT="3"
|
||||
[ -z "$MIZU_BENCHMARK_QPS" ] && export MIZU_BENCHMARK_QPS="500"
|
||||
[ -z "$MIZU_BENCHMARK_CLIENTS_COUNT" ] && export MIZU_BENCHMARK_CLIENTS_COUNT="5"
|
||||
|
||||
function log() {
|
||||
local message=$@
|
||||
printf "[%s] %s\n" "$(date "+%d-%m %H:%M:%S")" "$message"
|
||||
}
|
||||
|
||||
function run_single_bench() {
|
||||
local mode_num=$1
|
||||
local mode_str=$2
|
||||
|
||||
log "Starting ${mode_num}_${mode_str} (runs: $MIZU_BENCHMARK_RUN_COUNT) (period: $MIZU_BENCHMARK_CLIENT_PERIOD)"
|
||||
|
||||
for ((i=0;i<"$MIZU_BENCHMARK_RUN_COUNT";i++)); do
|
||||
log " $i: Running tapper"
|
||||
rm -f tapper.log
|
||||
tapper_args=("--tap" "--api-server-address" "ws://localhost:8899/wsTapper" "-stats" "10" "-ignore-ports" "8899,9099")
|
||||
if [[ $(uname) == "Darwin" ]]
|
||||
then
|
||||
tapper_args+=("-i" "lo0" "-"decoder "Loopback")
|
||||
else
|
||||
tapper_args+=("-i" "lo")
|
||||
fi
|
||||
nohup ./agent/build/mizuagent ${tapper_args[@]} > tapper.log 2>&1 &
|
||||
|
||||
log " $i: Running client (hey)"
|
||||
hey -z $MIZU_BENCHMARK_CLIENT_PERIOD -c $MIZU_BENCHMARK_CLIENTS_COUNT -q $MIZU_BENCHMARK_QPS $MIZU_BENCHMARK_URL > /dev/null || return 1
|
||||
|
||||
log " $i: Killing tapper"
|
||||
kill -9 $(ps -ef | grep agent/build/mizuagent | grep tap | grep -v grep | awk '{ print $2 }') > /dev/null 2>&1
|
||||
|
||||
local output_file=$MIZU_BENCHMARK_OUTPUT_DIR/${mode_num}_${mode_str}_${i}.log
|
||||
log " $i: Moving output to $output_file"
|
||||
mv tapper.log $output_file || return 1
|
||||
done
|
||||
}
|
||||
|
||||
function generate_bench_graph() {
|
||||
cd performance_analysis/ || return 1
|
||||
source venv/bin/activate
|
||||
python plot_from_tapper_logs.py $MIZU_BENCHMARK_OUTPUT_DIR/*.log || return 1
|
||||
mv graph.png $MIZU_BENCHMARK_OUTPUT_DIR || return 1
|
||||
}
|
||||
|
||||
mkdir -p $MIZU_BENCHMARK_OUTPUT_DIR
|
||||
rm -f $MIZU_BENCHMARK_OUTPUT_DIR/*
|
||||
log "Writing output to $MIZU_BENCHMARK_OUTPUT_DIR"
|
||||
|
||||
cd $MIZU_HOME || exit 1
|
||||
|
||||
export HOST_MODE=0
|
||||
export SENSITIVE_DATA_FILTERING_OPTIONS='{"EnableRedaction": false}'
|
||||
export MIZU_DEBUG_DISABLE_PCAP=false
|
||||
export MIZU_DEBUG_DISABLE_TCP_REASSEMBLY=false
|
||||
export MIZU_DEBUG_DISABLE_TCP_STREAM=false
|
||||
export MIZU_DEBUG_DISABLE_NON_HTTP_EXTENSSION=false
|
||||
export MIZU_DEBUG_DISABLE_DISSECTORS=false
|
||||
export MIZU_DEBUG_DISABLE_EMITTING=false
|
||||
export MIZU_DEBUG_DISABLE_SENDING=false
|
||||
|
||||
export MIZU_DEBUG_DISABLE_PCAP=true
|
||||
run_single_bench "01" "no_pcap" || exit 1
|
||||
export MIZU_DEBUG_DISABLE_PCAP=false
|
||||
|
||||
export MIZU_DEBUG_DISABLE_TCP_REASSEMBLY=true
|
||||
run_single_bench "02" "no_assembler" || exit 1
|
||||
export MIZU_DEBUG_DISABLE_TCP_REASSEMBLY=false
|
||||
|
||||
export MIZU_DEBUG_DISABLE_TCP_STREAM=true
|
||||
run_single_bench "03" "no_tcp_stream" || exit 1
|
||||
export MIZU_DEBUG_DISABLE_TCP_STREAM=false
|
||||
|
||||
export MIZU_DEBUG_DISABLE_NON_HTTP_EXTENSSION=true
|
||||
run_single_bench "04" "only_http" || exit 1
|
||||
export MIZU_DEBUG_DISABLE_NON_HTTP_EXTENSSION=false
|
||||
|
||||
export MIZU_DEBUG_DISABLE_DISSECTORS=true
|
||||
run_single_bench "05" "no_dissectors" || exit 1
|
||||
export MIZU_DEBUG_DISABLE_DISSECTORS=false
|
||||
|
||||
export MIZU_DEBUG_DISABLE_EMITTING=true
|
||||
run_single_bench "06" "no_emit" || exit 1
|
||||
export MIZU_DEBUG_DISABLE_EMITTING=false
|
||||
|
||||
export MIZU_DEBUG_DISABLE_SENDING=true
|
||||
run_single_bench "07" "no_send" || exit 1
|
||||
export MIZU_DEBUG_DISABLE_SENDING=false
|
||||
|
||||
run_single_bench "08" "normal" || exit 1
|
||||
|
||||
generate_bench_graph || exit 1
|
||||
log "Output written to to $MIZU_BENCHMARK_OUTPUT_DIR"
|
||||
BIN
performance_analysis/tapper-modes.png
Normal file
BIN
performance_analysis/tapper-modes.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 259 KiB |
@@ -67,6 +67,7 @@ require (
|
||||
github.com/spf13/cobra v1.3.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stretchr/testify v1.7.0 // indirect
|
||||
github.com/up9inc/mizu/tap/dbgctl v0.0.0 // indirect
|
||||
github.com/xlab/treeprint v1.1.0 // indirect
|
||||
go.starlark.net v0.0.0-20220203230714-bb14e151c28f // indirect
|
||||
golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab // indirect
|
||||
@@ -95,3 +96,5 @@ require (
|
||||
replace github.com/up9inc/mizu/logger v0.0.0 => ../logger
|
||||
|
||||
replace github.com/up9inc/mizu/tap/api v0.0.0 => ../tap/api
|
||||
|
||||
replace github.com/up9inc/mizu/tap/dbgctl v0.0.0 => ../tap/dbgctl
|
||||
|
||||
@@ -181,6 +181,7 @@ type ApiServerOptions struct {
|
||||
Resources shared.Resources
|
||||
ImagePullPolicy core.PullPolicy
|
||||
LogLevel logging.Level
|
||||
Profiler bool
|
||||
}
|
||||
|
||||
func (provider *Provider) GetMizuApiServerPodObject(opts *ApiServerOptions, mountVolumeClaim bool, volumeClaimName string, createAuthContainer bool) (*core.Pod, error) {
|
||||
@@ -212,7 +213,15 @@ func (provider *Provider) GetMizuApiServerPodObject(opts *ApiServerOptions, moun
|
||||
return nil, fmt.Errorf("invalid memory request for %s container", opts.PodName)
|
||||
}
|
||||
|
||||
command := []string{"./mizuagent", "--api-server"}
|
||||
command := []string{
|
||||
"./mizuagent",
|
||||
"--api-server",
|
||||
}
|
||||
|
||||
if opts.Profiler {
|
||||
command = append(command, "--profiler")
|
||||
}
|
||||
|
||||
if opts.IsNamespaceRestricted {
|
||||
command = append(command, "--namespace", opts.Namespace)
|
||||
}
|
||||
@@ -383,6 +392,16 @@ func (provider *Provider) GetMizuApiServerPodObject(opts *ApiServerOptions, moun
|
||||
Volumes: volumes,
|
||||
DNSPolicy: core.DNSClusterFirstWithHostNet,
|
||||
TerminationGracePeriodSeconds: new(int64),
|
||||
Tolerations: []core.Toleration{
|
||||
{
|
||||
Operator: core.TolerationOpExists,
|
||||
Effect: core.TaintEffectNoExecute,
|
||||
},
|
||||
{
|
||||
Operator: core.TolerationOpExists,
|
||||
Effect: core.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -45,13 +45,6 @@ type MizuAgentConfig struct {
|
||||
ServiceMap bool `json:"serviceMap"`
|
||||
OAS bool `json:"oas"`
|
||||
Telemetry bool `json:"telemetry"`
|
||||
Elastic ElasticConfig `json:"elastic"`
|
||||
}
|
||||
|
||||
type ElasticConfig struct {
|
||||
User string `yaml:"user,omitempty" default:"" readonly:""`
|
||||
Password string `yaml:"password,omitempty" default:"" readonly:""`
|
||||
Url string `yaml:"url,omitempty" default:"" readonly:""`
|
||||
}
|
||||
|
||||
type WebSocketMessageMetadata struct {
|
||||
|
||||
@@ -15,6 +15,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/martian/har"
|
||||
|
||||
"github.com/up9inc/mizu/tap/dbgctl"
|
||||
)
|
||||
|
||||
const mizuTestEnvVar = "MIZU_TEST"
|
||||
@@ -104,11 +106,6 @@ type OutputChannelItem struct {
|
||||
Namespace string
|
||||
}
|
||||
|
||||
type ProtoIdentifier struct {
|
||||
Protocol *Protocol
|
||||
IsClosedOthers bool
|
||||
}
|
||||
|
||||
type ReadProgress struct {
|
||||
readBytes int
|
||||
lastCurrent int
|
||||
@@ -123,6 +120,11 @@ func (p *ReadProgress) Current() (n int) {
|
||||
return p.lastCurrent
|
||||
}
|
||||
|
||||
func (p *ReadProgress) Reset() {
|
||||
p.readBytes = 0
|
||||
p.lastCurrent = 0
|
||||
}
|
||||
|
||||
type Dissector interface {
|
||||
Register(*Extension)
|
||||
Ping()
|
||||
@@ -149,8 +151,13 @@ type Emitter interface {
|
||||
}
|
||||
|
||||
func (e *Emitting) Emit(item *OutputChannelItem) {
|
||||
e.OutputChannel <- item
|
||||
e.AppStats.IncMatchedPairs()
|
||||
|
||||
if dbgctl.MizuTapperDisableEmitting {
|
||||
return
|
||||
}
|
||||
|
||||
e.OutputChannel <- item
|
||||
}
|
||||
|
||||
type Entry struct {
|
||||
@@ -419,13 +426,11 @@ type TcpReader interface {
|
||||
GetCaptureTime() time.Time
|
||||
GetEmitter() Emitter
|
||||
GetIsClosed() bool
|
||||
GetExtension() *Extension
|
||||
}
|
||||
|
||||
type TcpStream interface {
|
||||
SetProtocol(protocol *Protocol)
|
||||
GetOrigin() Capture
|
||||
GetProtoIdentifier() *ProtoIdentifier
|
||||
GetReqResMatchers() []RequestResponseMatcher
|
||||
GetIsTapTarget() bool
|
||||
GetIsClosed() bool
|
||||
|
||||
@@ -2,4 +2,9 @@ module github.com/up9inc/mizu/tap/api
|
||||
|
||||
go 1.17
|
||||
|
||||
require github.com/google/martian v2.1.0+incompatible
|
||||
require (
|
||||
github.com/google/martian v2.1.0+incompatible
|
||||
github.com/up9inc/mizu/tap/dbgctl v0.0.0
|
||||
)
|
||||
|
||||
replace github.com/up9inc/mizu/tap/dbgctl v0.0.0 => ../dbgctl
|
||||
|
||||
@@ -3,5 +3,5 @@ package api
|
||||
type TrafficFilteringOptions struct {
|
||||
IgnoredUserAgents []string
|
||||
PlainTextMaskingRegexes []*SerializableRegexp
|
||||
DisableRedaction bool
|
||||
EnableRedaction bool
|
||||
}
|
||||
|
||||
@@ -10,10 +10,12 @@ type AppStats struct {
|
||||
ProcessedBytes uint64 `json:"processedBytes"`
|
||||
PacketsCount uint64 `json:"packetsCount"`
|
||||
TcpPacketsCount uint64 `json:"tcpPacketsCount"`
|
||||
IgnoredPacketsCount uint64 `json:"ignoredPacketsCount"`
|
||||
ReassembledTcpPayloadsCount uint64 `json:"reassembledTcpPayloadsCount"`
|
||||
TlsConnectionsCount uint64 `json:"tlsConnectionsCount"`
|
||||
MatchedPairs uint64 `json:"matchedPairs"`
|
||||
DroppedTcpStreams uint64 `json:"droppedTcpStreams"`
|
||||
LiveTcpStreams uint64 `json:"liveTcpStreams"`
|
||||
}
|
||||
|
||||
func (as *AppStats) IncMatchedPairs() {
|
||||
@@ -33,6 +35,10 @@ func (as *AppStats) IncTcpPacketsCount() {
|
||||
atomic.AddUint64(&as.TcpPacketsCount, 1)
|
||||
}
|
||||
|
||||
func (as *AppStats) IncIgnoredPacketsCount() {
|
||||
atomic.AddUint64(&as.IgnoredPacketsCount, 1)
|
||||
}
|
||||
|
||||
func (as *AppStats) IncReassembledTcpPayloadsCount() {
|
||||
atomic.AddUint64(&as.ReassembledTcpPayloadsCount, 1)
|
||||
}
|
||||
@@ -41,6 +47,14 @@ func (as *AppStats) IncTlsConnectionsCount() {
|
||||
atomic.AddUint64(&as.TlsConnectionsCount, 1)
|
||||
}
|
||||
|
||||
func (as *AppStats) IncLiveTcpStreams() {
|
||||
atomic.AddUint64(&as.LiveTcpStreams, 1)
|
||||
}
|
||||
|
||||
func (as *AppStats) DecLiveTcpStreams() {
|
||||
atomic.AddUint64(&as.LiveTcpStreams, ^uint64(0))
|
||||
}
|
||||
|
||||
func (as *AppStats) UpdateProcessedBytes(size uint64) {
|
||||
atomic.AddUint64(&as.ProcessedBytes, size)
|
||||
}
|
||||
@@ -55,10 +69,12 @@ func (as *AppStats) DumpStats() *AppStats {
|
||||
currentAppStats.ProcessedBytes = resetUint64(&as.ProcessedBytes)
|
||||
currentAppStats.PacketsCount = resetUint64(&as.PacketsCount)
|
||||
currentAppStats.TcpPacketsCount = resetUint64(&as.TcpPacketsCount)
|
||||
currentAppStats.IgnoredPacketsCount = resetUint64(&as.IgnoredPacketsCount)
|
||||
currentAppStats.ReassembledTcpPayloadsCount = resetUint64(&as.ReassembledTcpPayloadsCount)
|
||||
currentAppStats.TlsConnectionsCount = resetUint64(&as.TlsConnectionsCount)
|
||||
currentAppStats.MatchedPairs = resetUint64(&as.MatchedPairs)
|
||||
currentAppStats.DroppedTcpStreams = resetUint64(&as.DroppedTcpStreams)
|
||||
currentAppStats.LiveTcpStreams = as.LiveTcpStreams
|
||||
|
||||
return currentAppStats
|
||||
}
|
||||
|
||||
15
tap/dbgctl/debug_control.go
Normal file
15
tap/dbgctl/debug_control.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package dbgctl
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
MizuTapperDisablePcap bool = os.Getenv("MIZU_DEBUG_DISABLE_PCAP") == "true"
|
||||
MizuTapperDisableTcpReassembly bool = os.Getenv("MIZU_DEBUG_DISABLE_TCP_REASSEMBLY") == "true"
|
||||
MizuTapperDisableTcpStream bool = os.Getenv("MIZU_DEBUG_DISABLE_TCP_STREAM") == "true"
|
||||
MizuTapperDisableDissectors bool = os.Getenv("MIZU_DEBUG_DISABLE_DISSECTORS") == "true"
|
||||
MizuTapperDisableEmitting bool = os.Getenv("MIZU_DEBUG_DISABLE_EMITTING") == "true"
|
||||
MizuTapperDisableSending bool = os.Getenv("MIZU_DEBUG_DISABLE_SENDING") == "true"
|
||||
MizuTapperDisableNonHttpExtensions bool = os.Getenv("MIZU_DEBUG_DISABLE_NON_HTTP_EXTENSSION") == "true"
|
||||
)
|
||||
3
tap/dbgctl/go.mod
Normal file
3
tap/dbgctl/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module github.com/up9inc/mizu/tap/dbgctl
|
||||
|
||||
go 1.18
|
||||
@@ -11,7 +11,10 @@ require (
|
||||
github.com/davecgh/go-spew v1.1.0 // indirect
|
||||
github.com/google/martian v2.1.0+incompatible // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/up9inc/mizu/tap/dbgctl v0.0.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
|
||||
)
|
||||
|
||||
replace github.com/up9inc/mizu/tap/api v0.0.0 => ../../api
|
||||
|
||||
replace github.com/up9inc/mizu/tap/dbgctl v0.0.0 => ../../dbgctl
|
||||
|
||||
@@ -3,7 +3,6 @@ package amqp
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
@@ -75,14 +74,10 @@ func (d dissecting) Dissect(b *bufio.Reader, reader api.TcpReader, options *api.
|
||||
var lastMethodFrameMessage Message
|
||||
|
||||
for {
|
||||
if reader.GetParent().GetProtoIdentifier().Protocol != nil && reader.GetParent().GetProtoIdentifier().Protocol != &protocol {
|
||||
return errors.New("Identified by another protocol")
|
||||
}
|
||||
|
||||
frame, err := r.ReadFrame()
|
||||
if err == io.EOF {
|
||||
// We must read until we see an EOF... very important!
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
switch f := frame.(type) {
|
||||
@@ -90,6 +85,8 @@ func (d dissecting) Dissect(b *bufio.Reader, reader api.TcpReader, options *api.
|
||||
// drop
|
||||
|
||||
case *HeaderFrame:
|
||||
reader.GetParent().SetProtocol(&protocol)
|
||||
|
||||
// start content state
|
||||
header = f
|
||||
remaining = int(header.Size)
|
||||
@@ -107,20 +104,22 @@ func (d dissecting) Dissect(b *bufio.Reader, reader api.TcpReader, options *api.
|
||||
}
|
||||
|
||||
case *BodyFrame:
|
||||
reader.GetParent().SetProtocol(&protocol)
|
||||
|
||||
// continue until terminated
|
||||
remaining -= len(f.Body)
|
||||
switch lastMethodFrameMessage.(type) {
|
||||
case *BasicPublish:
|
||||
eventBasicPublish.Body = f.Body
|
||||
reader.GetParent().SetProtocol(&protocol)
|
||||
emitAMQP(*eventBasicPublish, amqpRequest, basicMethodMap[40], connectionInfo, reader.GetCaptureTime(), reader.GetReadProgress().Current(), reader.GetEmitter(), reader.GetParent().GetOrigin())
|
||||
case *BasicDeliver:
|
||||
eventBasicDeliver.Body = f.Body
|
||||
reader.GetParent().SetProtocol(&protocol)
|
||||
emitAMQP(*eventBasicDeliver, amqpRequest, basicMethodMap[60], connectionInfo, reader.GetCaptureTime(), reader.GetReadProgress().Current(), reader.GetEmitter(), reader.GetParent().GetOrigin())
|
||||
}
|
||||
|
||||
case *MethodFrame:
|
||||
reader.GetParent().SetProtocol(&protocol)
|
||||
|
||||
lastMethodFrameMessage = f.Method
|
||||
switch m := f.Method.(type) {
|
||||
case *BasicPublish:
|
||||
@@ -137,7 +136,6 @@ func (d dissecting) Dissect(b *bufio.Reader, reader api.TcpReader, options *api.
|
||||
NoWait: m.NoWait,
|
||||
Arguments: m.Arguments,
|
||||
}
|
||||
reader.GetParent().SetProtocol(&protocol)
|
||||
emitAMQP(*eventQueueBind, amqpRequest, queueMethodMap[20], connectionInfo, reader.GetCaptureTime(), reader.GetReadProgress().Current(), reader.GetEmitter(), reader.GetParent().GetOrigin())
|
||||
|
||||
case *BasicConsume:
|
||||
@@ -150,7 +148,6 @@ func (d dissecting) Dissect(b *bufio.Reader, reader api.TcpReader, options *api.
|
||||
NoWait: m.NoWait,
|
||||
Arguments: m.Arguments,
|
||||
}
|
||||
reader.GetParent().SetProtocol(&protocol)
|
||||
emitAMQP(*eventBasicConsume, amqpRequest, basicMethodMap[20], connectionInfo, reader.GetCaptureTime(), reader.GetReadProgress().Current(), reader.GetEmitter(), reader.GetParent().GetOrigin())
|
||||
|
||||
case *BasicDeliver:
|
||||
@@ -170,7 +167,6 @@ func (d dissecting) Dissect(b *bufio.Reader, reader api.TcpReader, options *api.
|
||||
NoWait: m.NoWait,
|
||||
Arguments: m.Arguments,
|
||||
}
|
||||
reader.GetParent().SetProtocol(&protocol)
|
||||
emitAMQP(*eventQueueDeclare, amqpRequest, queueMethodMap[10], connectionInfo, reader.GetCaptureTime(), reader.GetReadProgress().Current(), reader.GetEmitter(), reader.GetParent().GetOrigin())
|
||||
|
||||
case *ExchangeDeclare:
|
||||
@@ -184,7 +180,6 @@ func (d dissecting) Dissect(b *bufio.Reader, reader api.TcpReader, options *api.
|
||||
NoWait: m.NoWait,
|
||||
Arguments: m.Arguments,
|
||||
}
|
||||
reader.GetParent().SetProtocol(&protocol)
|
||||
emitAMQP(*eventExchangeDeclare, amqpRequest, exchangeMethodMap[10], connectionInfo, reader.GetCaptureTime(), reader.GetReadProgress().Current(), reader.GetEmitter(), reader.GetParent().GetOrigin())
|
||||
|
||||
case *ConnectionStart:
|
||||
@@ -195,7 +190,6 @@ func (d dissecting) Dissect(b *bufio.Reader, reader api.TcpReader, options *api.
|
||||
Mechanisms: m.Mechanisms,
|
||||
Locales: m.Locales,
|
||||
}
|
||||
reader.GetParent().SetProtocol(&protocol)
|
||||
emitAMQP(*eventConnectionStart, amqpRequest, connectionMethodMap[10], connectionInfo, reader.GetCaptureTime(), reader.GetReadProgress().Current(), reader.GetEmitter(), reader.GetParent().GetOrigin())
|
||||
|
||||
case *ConnectionClose:
|
||||
@@ -205,7 +199,6 @@ func (d dissecting) Dissect(b *bufio.Reader, reader api.TcpReader, options *api.
|
||||
ClassId: m.ClassId,
|
||||
MethodId: m.MethodId,
|
||||
}
|
||||
reader.GetParent().SetProtocol(&protocol)
|
||||
emitAMQP(*eventConnectionClose, amqpRequest, connectionMethodMap[50], connectionInfo, reader.GetCaptureTime(), reader.GetReadProgress().Current(), reader.GetEmitter(), reader.GetParent().GetOrigin())
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,3 @@ func (reader *tcpReader) GetEmitter() api.Emitter {
|
||||
func (reader *tcpReader) GetIsClosed() bool {
|
||||
return reader.isClosed
|
||||
}
|
||||
|
||||
func (reader *tcpReader) GetExtension() *api.Extension {
|
||||
return reader.extension
|
||||
}
|
||||
|
||||
@@ -7,18 +7,16 @@ import (
|
||||
)
|
||||
|
||||
type tcpStream struct {
|
||||
isClosed bool
|
||||
protoIdentifier *api.ProtoIdentifier
|
||||
isTapTarget bool
|
||||
origin api.Capture
|
||||
reqResMatchers []api.RequestResponseMatcher
|
||||
isClosed bool
|
||||
isTapTarget bool
|
||||
origin api.Capture
|
||||
reqResMatchers []api.RequestResponseMatcher
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func NewTcpStream(capture api.Capture) api.TcpStream {
|
||||
return &tcpStream{
|
||||
origin: capture,
|
||||
protoIdentifier: &api.ProtoIdentifier{},
|
||||
origin: capture,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,10 +26,6 @@ func (t *tcpStream) GetOrigin() api.Capture {
|
||||
return t.origin
|
||||
}
|
||||
|
||||
func (t *tcpStream) GetProtoIdentifier() *api.ProtoIdentifier {
|
||||
return t.protoIdentifier
|
||||
}
|
||||
|
||||
func (t *tcpStream) GetReqResMatchers() []api.RequestResponseMatcher {
|
||||
return t.reqResMatchers
|
||||
}
|
||||
|
||||
@@ -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/expect9/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/expect10/http/\* expect
|
||||
|
||||
@@ -14,8 +14,11 @@ require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/google/martian v2.1.0+incompatible // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/up9inc/mizu/tap/dbgctl v0.0.0 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
)
|
||||
|
||||
replace github.com/up9inc/mizu/tap/api v0.0.0 => ../../api
|
||||
|
||||
replace github.com/up9inc/mizu/tap/dbgctl v0.0.0 => ../../dbgctl
|
||||
|
||||
@@ -18,7 +18,7 @@ func filterAndEmit(item *api.OutputChannelItem, emitter api.Emitter, options *ap
|
||||
return
|
||||
}
|
||||
|
||||
if !options.DisableRedaction {
|
||||
if options.EnableRedaction {
|
||||
FilterSensitiveData(item, options)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package http
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
@@ -66,7 +65,7 @@ var grpcProtocol api.Protocol = api.Protocol{
|
||||
BackgroundColor: "#244c5a",
|
||||
ForegroundColor: "#ffffff",
|
||||
FontSize: 11,
|
||||
ReferenceLink: "https://grpc.github.io/grpc/core/md_doc_statuscodes.html",
|
||||
ReferenceLink: "https://grpc.github.io/grpc/core/md_doc__p_r_o_t_o_c_o_l-_h_t_t_p2.html",
|
||||
Ports: []string{"80", "443", "8080", "50051"},
|
||||
Priority: 0,
|
||||
}
|
||||
@@ -144,10 +143,6 @@ func (d dissecting) Dissect(b *bufio.Reader, reader api.TcpReader, options *api.
|
||||
http2Assembler = createHTTP2Assembler(b)
|
||||
}
|
||||
|
||||
if reader.GetParent().GetProtoIdentifier().Protocol != nil && reader.GetParent().GetProtoIdentifier().Protocol != &http11protocol {
|
||||
return errors.New("Identified by another protocol")
|
||||
}
|
||||
|
||||
if isHTTP2 {
|
||||
err = handleHTTP2Stream(http2Assembler, reader.GetReadProgress(), reader.GetParent().GetOrigin(), reader.GetTcpID(), reader.GetCaptureTime(), reader.GetEmitter(), options, reqResMatcher)
|
||||
if err == io.EOF || err == io.ErrUnexpectedEOF {
|
||||
@@ -200,10 +195,6 @@ func (d dissecting) Dissect(b *bufio.Reader, reader api.TcpReader, options *api.
|
||||
}
|
||||
}
|
||||
|
||||
if reader.GetParent().GetProtoIdentifier().Protocol == nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,3 @@ func (reader *tcpReader) GetEmitter() api.Emitter {
|
||||
func (reader *tcpReader) GetIsClosed() bool {
|
||||
return reader.isClosed
|
||||
}
|
||||
|
||||
func (reader *tcpReader) GetExtension() *api.Extension {
|
||||
return reader.extension
|
||||
}
|
||||
|
||||
@@ -7,18 +7,16 @@ import (
|
||||
)
|
||||
|
||||
type tcpStream struct {
|
||||
isClosed bool
|
||||
protoIdentifier *api.ProtoIdentifier
|
||||
isTapTarget bool
|
||||
origin api.Capture
|
||||
reqResMatchers []api.RequestResponseMatcher
|
||||
isClosed bool
|
||||
isTapTarget bool
|
||||
origin api.Capture
|
||||
reqResMatchers []api.RequestResponseMatcher
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func NewTcpStream(capture api.Capture) api.TcpStream {
|
||||
return &tcpStream{
|
||||
origin: capture,
|
||||
protoIdentifier: &api.ProtoIdentifier{},
|
||||
origin: capture,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,10 +26,6 @@ func (t *tcpStream) GetOrigin() api.Capture {
|
||||
return t.origin
|
||||
}
|
||||
|
||||
func (t *tcpStream) GetProtoIdentifier() *api.ProtoIdentifier {
|
||||
return t.protoIdentifier
|
||||
}
|
||||
|
||||
func (t *tcpStream) GetReqResMatchers() []api.RequestResponseMatcher {
|
||||
return t.reqResMatchers
|
||||
}
|
||||
|
||||
@@ -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/expect8/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/expect9/kafka/\* expect
|
||||
|
||||
@@ -18,7 +18,10 @@ require (
|
||||
github.com/klauspost/compress v1.14.2 // indirect
|
||||
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/up9inc/mizu/tap/dbgctl v0.0.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
|
||||
)
|
||||
|
||||
replace github.com/up9inc/mizu/tap/api v0.0.0 => ../../api
|
||||
|
||||
replace github.com/up9inc/mizu/tap/dbgctl v0.0.0 => ../../dbgctl
|
||||
|
||||
@@ -3,13 +3,14 @@ package kafka
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/fatih/camelcase"
|
||||
"github.com/ohler55/ojg/jp"
|
||||
"github.com/ohler55/ojg/oj"
|
||||
@@ -36,9 +37,14 @@ type KafkaWrapper struct {
|
||||
|
||||
func representRequestHeader(data map[string]interface{}, rep []interface{}) []interface{} {
|
||||
requestHeader, _ := json.Marshal([]api.TableData{
|
||||
{
|
||||
Name: "ApiKeyName",
|
||||
Value: data["apiKeyName"].(string),
|
||||
Selector: `request.apiKeyName`,
|
||||
},
|
||||
{
|
||||
Name: "ApiKey",
|
||||
Value: apiNames[int(data["apiKey"].(float64))],
|
||||
Value: int(data["apiKey"].(float64)),
|
||||
Selector: `request.apiKey`,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -3,7 +3,6 @@ package kafka
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
@@ -38,10 +37,6 @@ func (d dissecting) Ping() {
|
||||
func (d dissecting) Dissect(b *bufio.Reader, reader api.TcpReader, options *api.TrafficFilteringOptions) error {
|
||||
reqResMatcher := reader.GetReqResMatcher().(*requestResponseMatcher)
|
||||
for {
|
||||
if reader.GetParent().GetProtoIdentifier().Protocol != nil && reader.GetParent().GetProtoIdentifier().Protocol != &_protocol {
|
||||
return errors.New("Identified by another protocol")
|
||||
}
|
||||
|
||||
if reader.GetIsClient() {
|
||||
_, _, err := ReadRequest(b, reader.GetTcpID(), reader.GetCounterPair(), reader.GetCaptureTime(), reqResMatcher)
|
||||
if err != nil {
|
||||
@@ -96,8 +91,8 @@ func (d dissecting) Summarize(entry *api.Entry) *api.BaseEntry {
|
||||
statusQuery := ""
|
||||
|
||||
apiKey := ApiKey(entry.Request["apiKey"].(float64))
|
||||
method := apiNames[apiKey]
|
||||
methodQuery := fmt.Sprintf("request.apiKey == %d", int(entry.Request["apiKey"].(float64)))
|
||||
method := entry.Request["apiKeyName"].(string)
|
||||
methodQuery := fmt.Sprintf(`request.apiKeyName == "%s"`, method)
|
||||
|
||||
summary := ""
|
||||
summaryQuery := ""
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
type Request struct {
|
||||
Size int32 `json:"size"`
|
||||
ApiKeyName string `json:"apiKeyName"`
|
||||
ApiKey ApiKey `json:"apiKey"`
|
||||
ApiVersion int16 `json:"apiVersion"`
|
||||
CorrelationID int32 `json:"correlationID"`
|
||||
@@ -202,6 +203,7 @@ func ReadRequest(r io.Reader, tcpID *api.TcpID, counterPair *api.CounterPair, ca
|
||||
|
||||
request := &Request{
|
||||
Size: size,
|
||||
ApiKeyName: apiNames[apiKey],
|
||||
ApiKey: apiKey,
|
||||
ApiVersion: apiVersion,
|
||||
CorrelationID: correlationID,
|
||||
|
||||
@@ -78,7 +78,3 @@ func (reader *tcpReader) GetEmitter() api.Emitter {
|
||||
func (reader *tcpReader) GetIsClosed() bool {
|
||||
return reader.isClosed
|
||||
}
|
||||
|
||||
func (reader *tcpReader) GetExtension() *api.Extension {
|
||||
return reader.extension
|
||||
}
|
||||
|
||||
@@ -7,18 +7,16 @@ import (
|
||||
)
|
||||
|
||||
type tcpStream struct {
|
||||
isClosed bool
|
||||
protoIdentifier *api.ProtoIdentifier
|
||||
isTapTarget bool
|
||||
origin api.Capture
|
||||
reqResMatchers []api.RequestResponseMatcher
|
||||
isClosed bool
|
||||
isTapTarget bool
|
||||
origin api.Capture
|
||||
reqResMatchers []api.RequestResponseMatcher
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func NewTcpStream(capture api.Capture) api.TcpStream {
|
||||
return &tcpStream{
|
||||
origin: capture,
|
||||
protoIdentifier: &api.ProtoIdentifier{},
|
||||
origin: capture,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,10 +26,6 @@ func (t *tcpStream) GetOrigin() api.Capture {
|
||||
return t.origin
|
||||
}
|
||||
|
||||
func (t *tcpStream) GetProtoIdentifier() *api.ProtoIdentifier {
|
||||
return t.protoIdentifier
|
||||
}
|
||||
|
||||
func (t *tcpStream) GetReqResMatchers() []api.RequestResponseMatcher {
|
||||
return t.reqResMatchers
|
||||
}
|
||||
|
||||
@@ -11,7 +11,10 @@ require (
|
||||
github.com/davecgh/go-spew v1.1.0 // indirect
|
||||
github.com/google/martian v2.1.0+incompatible // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/up9inc/mizu/tap/dbgctl v0.0.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
|
||||
)
|
||||
|
||||
replace github.com/up9inc/mizu/tap/api v0.0.0 => ../../api
|
||||
|
||||
replace github.com/up9inc/mizu/tap/dbgctl v0.0.0 => ../../dbgctl
|
||||
|
||||
@@ -78,7 +78,3 @@ func (reader *tcpReader) GetEmitter() api.Emitter {
|
||||
func (reader *tcpReader) GetIsClosed() bool {
|
||||
return reader.isClosed
|
||||
}
|
||||
|
||||
func (reader *tcpReader) GetExtension() *api.Extension {
|
||||
return reader.extension
|
||||
}
|
||||
|
||||
@@ -7,18 +7,16 @@ import (
|
||||
)
|
||||
|
||||
type tcpStream struct {
|
||||
isClosed bool
|
||||
protoIdentifier *api.ProtoIdentifier
|
||||
isTapTarget bool
|
||||
origin api.Capture
|
||||
reqResMatchers []api.RequestResponseMatcher
|
||||
isClosed bool
|
||||
isTapTarget bool
|
||||
origin api.Capture
|
||||
reqResMatchers []api.RequestResponseMatcher
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func NewTcpStream(capture api.Capture) api.TcpStream {
|
||||
return &tcpStream{
|
||||
origin: capture,
|
||||
protoIdentifier: &api.ProtoIdentifier{},
|
||||
origin: capture,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,10 +26,6 @@ func (t *tcpStream) GetOrigin() api.Capture {
|
||||
return t.origin
|
||||
}
|
||||
|
||||
func (t *tcpStream) GetProtoIdentifier() *api.ProtoIdentifier {
|
||||
return t.protoIdentifier
|
||||
}
|
||||
|
||||
func (t *tcpStream) GetReqResMatchers() []api.RequestResponseMatcher {
|
||||
return t.reqResMatchers
|
||||
}
|
||||
|
||||
11
tap/go.mod
11
tap/go.mod
@@ -6,23 +6,30 @@ require (
|
||||
github.com/cilium/ebpf v0.8.0
|
||||
github.com/go-errors/errors v1.4.2
|
||||
github.com/google/gopacket v1.1.19
|
||||
github.com/hashicorp/golang-lru v0.5.4
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible
|
||||
github.com/struCoder/pidusage v0.2.1
|
||||
github.com/up9inc/mizu/logger v0.0.0
|
||||
github.com/up9inc/mizu/tap/api v0.0.0
|
||||
github.com/up9inc/mizu/tap/dbgctl v0.0.0
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74
|
||||
k8s.io/api v0.23.3
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/go-logr/logr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/google/go-cmp v0.5.7 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/martian v2.1.0+incompatible // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
||||
github.com/tklauser/numcpus v0.4.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
|
||||
golang.org/x/sys v0.0.0-20220207234003-57398862261d // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
@@ -39,3 +46,5 @@ require (
|
||||
replace github.com/up9inc/mizu/logger v0.0.0 => ../logger
|
||||
|
||||
replace github.com/up9inc/mizu/tap/api v0.0.0 => ./api
|
||||
|
||||
replace github.com/up9inc/mizu/tap/dbgctl v0.0.0 => ./dbgctl
|
||||
|
||||
17
tap/go.sum
17
tap/go.sum
@@ -31,6 +31,8 @@ github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTg
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
|
||||
@@ -117,6 +119,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
@@ -126,11 +130,19 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/struCoder/pidusage v0.2.1 h1:dFiEgUDkubeIj0XA1NpQ6+8LQmKrLi7NiIQl86E6BoY=
|
||||
github.com/struCoder/pidusage v0.2.1/go.mod h1:bewtP2KUA1TBUyza5+/PCpSQ6sc/H6jJbIKAzqW86BA=
|
||||
github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw=
|
||||
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
|
||||
github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o=
|
||||
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
|
||||
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/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
@@ -170,6 +182,7 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -184,6 +197,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220207234003-57398862261d h1:Bm7BNOQt2Qv7ZqysjeLjgCBanX+88Z/OtdvsrEv1Djc=
|
||||
golang.org/x/sys v0.0.0-20220207234003-57398862261d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
@@ -276,6 +290,5 @@ sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
|
||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
|
||||
@@ -16,7 +16,10 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
"strconv"
|
||||
|
||||
"github.com/shirou/gopsutil/cpu"
|
||||
"github.com/struCoder/pidusage"
|
||||
"github.com/up9inc/mizu/logger"
|
||||
"github.com/up9inc/mizu/tap/api"
|
||||
"github.com/up9inc/mizu/tap/diagnose"
|
||||
@@ -41,6 +44,7 @@ var debug = flag.Bool("debug", false, "Display debug information")
|
||||
var quiet = flag.Bool("quiet", false, "Be quiet regarding errors")
|
||||
var hexdumppkt = flag.Bool("dumppkt", false, "Dump packet as hex")
|
||||
var procfs = flag.String("procfs", "/proc", "The procfs directory, used when mapping host volumes into a container")
|
||||
var ignoredPorts = flag.String("ignore-ports", "", "A comma separated list of ports to ignore")
|
||||
|
||||
// capture
|
||||
var iface = flag.String("i", "en0", "Interface to read packets from")
|
||||
@@ -55,7 +59,8 @@ var tls = flag.Bool("tls", false, "Enable TLS tapper")
|
||||
var memprofile = flag.String("memprofile", "", "Write memory profile")
|
||||
|
||||
type TapOpts struct {
|
||||
HostMode bool
|
||||
HostMode bool
|
||||
IgnoredPorts []uint16
|
||||
}
|
||||
|
||||
var extensions []*api.Extension // global
|
||||
@@ -123,6 +128,16 @@ func printPeriodicStats(cleaner *Cleaner) {
|
||||
statsPeriod := time.Second * time.Duration(*statsevery)
|
||||
ticker := time.NewTicker(statsPeriod)
|
||||
|
||||
logicalCoreCount, err := cpu.Counts(true)
|
||||
if err != nil {
|
||||
logicalCoreCount = -1
|
||||
}
|
||||
|
||||
physicalCoreCount, err := cpu.Counts(false)
|
||||
if err != nil {
|
||||
physicalCoreCount = -1
|
||||
}
|
||||
|
||||
for {
|
||||
<-ticker.C
|
||||
|
||||
@@ -139,11 +154,21 @@ func printPeriodicStats(cleaner *Cleaner) {
|
||||
// At this moment
|
||||
memStats := runtime.MemStats{}
|
||||
runtime.ReadMemStats(&memStats)
|
||||
sysInfo, err := pidusage.GetStat(os.Getpid())
|
||||
if err != nil {
|
||||
sysInfo = &pidusage.SysInfo{
|
||||
CPU: -1,
|
||||
Memory: -1,
|
||||
}
|
||||
}
|
||||
logger.Log.Infof(
|
||||
"mem: %d, goroutines: %d",
|
||||
"mem: %d, goroutines: %d, cpu: %f, cores: %d/%d, rss: %f",
|
||||
memStats.HeapAlloc,
|
||||
runtime.NumGoroutine(),
|
||||
)
|
||||
sysInfo.CPU,
|
||||
logicalCoreCount,
|
||||
physicalCoreCount,
|
||||
sysInfo.Memory)
|
||||
|
||||
// Since the last print
|
||||
cleanStats := cleaner.dumpStats()
|
||||
@@ -193,6 +218,8 @@ func initializePassiveTapper(opts *TapOpts, outputItems chan *api.OutputChannelI
|
||||
logger.Log.Fatal(err)
|
||||
}
|
||||
|
||||
opts.IgnoredPorts = append(opts.IgnoredPorts, buildIgnoredPortsList(*ignoredPorts)...)
|
||||
|
||||
assembler := NewTcpAssembler(outputItems, streamsMap, opts)
|
||||
|
||||
return assembler
|
||||
@@ -267,3 +294,19 @@ func startTlsTapper(extension *api.Extension, outputItems chan *api.OutputChanne
|
||||
|
||||
return &tls
|
||||
}
|
||||
|
||||
func buildIgnoredPortsList(ignoredPorts string) []uint16 {
|
||||
tmp := strings.Split(ignoredPorts, ",")
|
||||
result := make([]uint16, len(tmp))
|
||||
|
||||
for i, raw := range tmp {
|
||||
v, err := strconv.Atoi(raw)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
result[i] = uint16(v)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/google/gopacket/pcap"
|
||||
"github.com/up9inc/mizu/logger"
|
||||
"github.com/up9inc/mizu/tap/api"
|
||||
"github.com/up9inc/mizu/tap/dbgctl"
|
||||
"github.com/up9inc/mizu/tap/diagnose"
|
||||
)
|
||||
|
||||
@@ -116,6 +117,9 @@ func (source *tcpPacketSource) close() {
|
||||
}
|
||||
|
||||
func (source *tcpPacketSource) readPackets(ipdefrag bool, packets chan<- TcpPacketInfo) {
|
||||
if dbgctl.MizuTapperDisablePcap {
|
||||
return
|
||||
}
|
||||
logger.Log.Infof("Start reading packets from %v", source.name)
|
||||
|
||||
for {
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/google/gopacket/reassembly"
|
||||
"github.com/up9inc/mizu/logger"
|
||||
"github.com/up9inc/mizu/tap/api"
|
||||
"github.com/up9inc/mizu/tap/dbgctl"
|
||||
"github.com/up9inc/mizu/tap/diagnose"
|
||||
"github.com/up9inc/mizu/tap/source"
|
||||
)
|
||||
@@ -23,6 +24,7 @@ type tcpAssembler struct {
|
||||
streamPool *reassembly.StreamPool
|
||||
streamFactory *tcpStreamFactory
|
||||
assemblerMutex sync.Mutex
|
||||
ignoredPorts []uint16
|
||||
}
|
||||
|
||||
// Context
|
||||
@@ -48,8 +50,8 @@ func NewTcpAssembler(outputItems chan *api.OutputChannelItem, streamsMap api.Tcp
|
||||
|
||||
maxBufferedPagesTotal := GetMaxBufferedPagesPerConnection()
|
||||
maxBufferedPagesPerConnection := GetMaxBufferedPagesTotal()
|
||||
logger.Log.Infof("Assembler options: maxBufferedPagesTotal=%d, maxBufferedPagesPerConnection=%d",
|
||||
maxBufferedPagesTotal, maxBufferedPagesPerConnection)
|
||||
logger.Log.Infof("Assembler options: maxBufferedPagesTotal=%d, maxBufferedPagesPerConnection=%d, opts=%v",
|
||||
maxBufferedPagesTotal, maxBufferedPagesPerConnection, opts)
|
||||
assembler.AssemblerOptions.MaxBufferedPagesTotal = maxBufferedPagesTotal
|
||||
assembler.AssemblerOptions.MaxBufferedPagesPerConnection = maxBufferedPagesPerConnection
|
||||
|
||||
@@ -57,6 +59,7 @@ func NewTcpAssembler(outputItems chan *api.OutputChannelItem, streamsMap api.Tcp
|
||||
Assembler: assembler,
|
||||
streamPool: streamPool,
|
||||
streamFactory: streamFactory,
|
||||
ignoredPorts: opts.IgnoredPorts,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,14 +86,20 @@ func (a *tcpAssembler) processPackets(dumpPacket bool, packets <-chan source.Tcp
|
||||
diagnose.AppStats.IncTcpPacketsCount()
|
||||
tcp := tcp.(*layers.TCP)
|
||||
|
||||
c := context{
|
||||
CaptureInfo: packet.Metadata().CaptureInfo,
|
||||
Origin: packetInfo.Source.Origin,
|
||||
if a.shouldIgnorePort(uint16(tcp.DstPort)) {
|
||||
diagnose.AppStats.IncIgnoredPacketsCount()
|
||||
} else {
|
||||
c := context{
|
||||
CaptureInfo: packet.Metadata().CaptureInfo,
|
||||
Origin: packetInfo.Source.Origin,
|
||||
}
|
||||
diagnose.InternalStats.Totalsz += len(tcp.Payload)
|
||||
if !dbgctl.MizuTapperDisableTcpReassembly {
|
||||
a.assemblerMutex.Lock()
|
||||
a.AssembleWithContext(packet.NetworkLayer().NetworkFlow(), tcp, &c)
|
||||
a.assemblerMutex.Unlock()
|
||||
}
|
||||
}
|
||||
diagnose.InternalStats.Totalsz += len(tcp.Payload)
|
||||
a.assemblerMutex.Lock()
|
||||
a.AssembleWithContext(packet.NetworkLayer().NetworkFlow(), tcp, &c)
|
||||
a.assemblerMutex.Unlock()
|
||||
}
|
||||
|
||||
done := *maxcount > 0 && int64(diagnose.AppStats.PacketsCount) >= *maxcount
|
||||
@@ -132,3 +141,13 @@ func (a *tcpAssembler) waitAndDump() {
|
||||
logger.Log.Debugf("%s", a.Dump())
|
||||
a.assemblerMutex.Unlock()
|
||||
}
|
||||
|
||||
func (a *tcpAssembler) shouldIgnorePort(port uint16) bool {
|
||||
for _, p := range a.ignoredPorts {
|
||||
if port == p {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -3,12 +3,11 @@ package tap
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/up9inc/mizu/logger"
|
||||
"github.com/up9inc/mizu/tap/api"
|
||||
"github.com/up9inc/mizu/tap/dbgctl"
|
||||
)
|
||||
|
||||
/* TcpReader gets reads from a channel of bytes of tcp payload, and parses it into requests and responses.
|
||||
@@ -17,50 +16,55 @@ import (
|
||||
* Implements io.Reader interface (Read)
|
||||
*/
|
||||
type tcpReader struct {
|
||||
ident string
|
||||
tcpID *api.TcpID
|
||||
isClosed bool
|
||||
isClient bool
|
||||
isOutgoing bool
|
||||
msgQueue chan api.TcpReaderDataMsg // Channel of captured reassembled tcp payload
|
||||
data []byte
|
||||
progress *api.ReadProgress
|
||||
captureTime time.Time
|
||||
parent *tcpStream
|
||||
packetsSeen uint
|
||||
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 api.TcpReaderDataMsg // Channel of captured reassembled tcp payload
|
||||
msgBuffer []api.TcpReaderDataMsg
|
||||
msgBufferMaster []api.TcpReaderDataMsg
|
||||
data []byte
|
||||
progress *api.ReadProgress
|
||||
captureTime time.Time
|
||||
parent *tcpStream
|
||||
emitter api.Emitter
|
||||
counterPair *api.CounterPair
|
||||
reqResMatcher api.RequestResponseMatcher
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func NewTcpReader(msgQueue chan api.TcpReaderDataMsg, progress *api.ReadProgress, ident string, tcpId *api.TcpID, captureTime time.Time, parent *tcpStream, isClient bool, isOutgoing bool, extension *api.Extension, emitter api.Emitter, counterPair *api.CounterPair, reqResMatcher api.RequestResponseMatcher) *tcpReader {
|
||||
func NewTcpReader(ident string, tcpId *api.TcpID, parent *tcpStream, isClient bool, isOutgoing bool, emitter api.Emitter) *tcpReader {
|
||||
return &tcpReader{
|
||||
msgQueue: msgQueue,
|
||||
progress: progress,
|
||||
ident: ident,
|
||||
tcpID: tcpId,
|
||||
captureTime: captureTime,
|
||||
parent: parent,
|
||||
isClient: isClient,
|
||||
isOutgoing: isOutgoing,
|
||||
extension: extension,
|
||||
emitter: emitter,
|
||||
counterPair: counterPair,
|
||||
reqResMatcher: reqResMatcher,
|
||||
msgQueue: make(chan api.TcpReaderDataMsg),
|
||||
progress: &api.ReadProgress{},
|
||||
ident: ident,
|
||||
tcpID: tcpId,
|
||||
parent: parent,
|
||||
isClient: isClient,
|
||||
isOutgoing: isOutgoing,
|
||||
emitter: emitter,
|
||||
}
|
||||
}
|
||||
|
||||
func (reader *tcpReader) run(options *api.TrafficFilteringOptions, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
b := bufio.NewReader(reader)
|
||||
err := reader.extension.Dissector.Dissect(b, reader, options)
|
||||
if err != nil {
|
||||
_, err = io.Copy(ioutil.Discard, reader)
|
||||
if err != nil {
|
||||
logger.Log.Errorf("%v", err)
|
||||
|
||||
if dbgctl.MizuTapperDisableDissectors {
|
||||
b := bufio.NewReader(reader)
|
||||
_, _ = io.ReadAll(b)
|
||||
return
|
||||
}
|
||||
|
||||
for i, extension := range extensions {
|
||||
reader.reqResMatcher = reader.parent.reqResMatchers[i]
|
||||
reader.counterPair = reader.parent.counterPairs[i]
|
||||
b := bufio.NewReader(reader)
|
||||
extension.Dissector.Dissect(b, reader, options) //nolint
|
||||
if reader.isProtocolIdentified() {
|
||||
break
|
||||
}
|
||||
reader.rewind()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,21 +85,60 @@ func (reader *tcpReader) sendMsgIfNotClosed(msg api.TcpReaderDataMsg) {
|
||||
reader.Unlock()
|
||||
}
|
||||
|
||||
func (reader *tcpReader) isProtocolIdentified() bool {
|
||||
return reader.parent.protocol != nil
|
||||
}
|
||||
|
||||
func (reader *tcpReader) rewind() {
|
||||
// Reset the data
|
||||
reader.data = make([]byte, 0)
|
||||
|
||||
// Reset msgBuffer from the master record
|
||||
reader.parent.Lock()
|
||||
reader.msgBuffer = make([]api.TcpReaderDataMsg, len(reader.msgBufferMaster))
|
||||
copy(reader.msgBuffer, reader.msgBufferMaster)
|
||||
reader.parent.Unlock()
|
||||
|
||||
// Reset the read progress
|
||||
reader.progress.Reset()
|
||||
}
|
||||
|
||||
func (reader *tcpReader) populateData(msg api.TcpReaderDataMsg) {
|
||||
reader.data = msg.GetBytes()
|
||||
reader.captureTime = msg.GetTimestamp()
|
||||
}
|
||||
|
||||
func (reader *tcpReader) Read(p []byte) (int, error) {
|
||||
var msg api.TcpReaderDataMsg
|
||||
|
||||
for len(reader.msgBuffer) > 0 && len(reader.data) == 0 {
|
||||
// Pop first message
|
||||
if len(reader.msgBuffer) > 1 {
|
||||
msg, reader.msgBuffer = reader.msgBuffer[0], reader.msgBuffer[1:]
|
||||
} else {
|
||||
msg = reader.msgBuffer[0]
|
||||
reader.msgBuffer = make([]api.TcpReaderDataMsg, 0)
|
||||
}
|
||||
|
||||
// Get the bytes
|
||||
reader.populateData(msg)
|
||||
}
|
||||
|
||||
ok := true
|
||||
for ok && len(reader.data) == 0 {
|
||||
msg, ok = <-reader.msgQueue
|
||||
if msg != nil {
|
||||
reader.data = msg.GetBytes()
|
||||
reader.captureTime = msg.GetTimestamp()
|
||||
}
|
||||
reader.populateData(msg)
|
||||
|
||||
if len(reader.data) > 0 {
|
||||
reader.packetsSeen += 1
|
||||
if !reader.isProtocolIdentified() {
|
||||
reader.msgBufferMaster = append(
|
||||
reader.msgBufferMaster,
|
||||
msg,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !ok || len(reader.data) == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
@@ -142,7 +185,3 @@ func (reader *tcpReader) GetEmitter() api.Emitter {
|
||||
func (reader *tcpReader) GetIsClosed() bool {
|
||||
return reader.isClosed
|
||||
}
|
||||
|
||||
func (reader *tcpReader) GetExtension() *api.Extension {
|
||||
return reader.extension
|
||||
}
|
||||
|
||||
@@ -6,26 +6,19 @@ import (
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers" // pulls in all layers decoders
|
||||
"github.com/google/gopacket/reassembly"
|
||||
"github.com/up9inc/mizu/tap/api"
|
||||
"github.com/up9inc/mizu/tap/diagnose"
|
||||
)
|
||||
|
||||
type ReassemblyStream interface {
|
||||
Accept(tcp *layers.TCP, ci gopacket.CaptureInfo, dir reassembly.TCPFlowDirection, nextSeq reassembly.Sequence, start *bool, ac reassembly.AssemblerContext) bool
|
||||
ReassembledSG(sg reassembly.ScatterGather, ac reassembly.AssemblerContext)
|
||||
ReassemblyComplete(ac reassembly.AssemblerContext) bool
|
||||
}
|
||||
|
||||
type tcpReassemblyStream struct {
|
||||
ident string
|
||||
tcpState *reassembly.TCPSimpleFSM
|
||||
fsmerr bool
|
||||
optchecker reassembly.TCPOptionCheck
|
||||
isDNS bool
|
||||
tcpStream api.TcpStream
|
||||
tcpStream *tcpStream
|
||||
}
|
||||
|
||||
func NewTcpReassemblyStream(ident string, tcp *layers.TCP, fsmOptions reassembly.TCPSimpleFSMOptions, stream api.TcpStream) ReassemblyStream {
|
||||
func NewTcpReassemblyStream(ident string, tcp *layers.TCP, fsmOptions reassembly.TCPSimpleFSMOptions, stream *tcpStream) reassembly.Stream {
|
||||
return &tcpReassemblyStream{
|
||||
ident: ident,
|
||||
tcpState: reassembly.NewTCPSimpleFSM(fsmOptions),
|
||||
@@ -145,17 +138,10 @@ func (t *tcpReassemblyStream) ReassembledSG(sg reassembly.ScatterGather, ac reas
|
||||
// This channel is read by an tcpReader object
|
||||
diagnose.AppStats.IncReassembledTcpPayloadsCount()
|
||||
timestamp := ac.GetCaptureInfo().Timestamp
|
||||
stream := t.tcpStream.(*tcpStream)
|
||||
if dir == reassembly.TCPDirClientToServer {
|
||||
for i := range stream.getClients() {
|
||||
reader := stream.getClient(i)
|
||||
reader.sendMsgIfNotClosed(NewTcpReaderDataMsg(data, timestamp))
|
||||
}
|
||||
t.tcpStream.client.sendMsgIfNotClosed(NewTcpReaderDataMsg(data, timestamp))
|
||||
} else {
|
||||
for i := range stream.getServers() {
|
||||
reader := stream.getServer(i)
|
||||
reader.sendMsgIfNotClosed(NewTcpReaderDataMsg(data, timestamp))
|
||||
}
|
||||
t.tcpStream.server.sendMsgIfNotClosed(NewTcpReaderDataMsg(data, timestamp))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,7 +149,7 @@ func (t *tcpReassemblyStream) ReassembledSG(sg reassembly.ScatterGather, ac reas
|
||||
|
||||
func (t *tcpReassemblyStream) ReassemblyComplete(ac reassembly.AssemblerContext) bool {
|
||||
if t.tcpStream.GetIsTapTarget() && !t.tcpStream.GetIsClosed() {
|
||||
t.tcpStream.(*tcpStream).close()
|
||||
t.tcpStream.close()
|
||||
}
|
||||
// do not remove the connection to allow last ACK
|
||||
return false
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/up9inc/mizu/tap/api"
|
||||
"github.com/up9inc/mizu/tap/dbgctl"
|
||||
)
|
||||
|
||||
/* It's a connection (bidirectional)
|
||||
@@ -13,25 +14,26 @@ import (
|
||||
* In our implementation, we pass information from ReassembledSG to the TcpReader through a shared channel.
|
||||
*/
|
||||
type tcpStream struct {
|
||||
id int64
|
||||
isClosed bool
|
||||
protoIdentifier *api.ProtoIdentifier
|
||||
isTapTarget bool
|
||||
clients []*tcpReader
|
||||
servers []*tcpReader
|
||||
origin api.Capture
|
||||
reqResMatchers []api.RequestResponseMatcher
|
||||
createdAt time.Time
|
||||
streamsMap api.TcpStreamMap
|
||||
id int64
|
||||
isClosed bool
|
||||
protocol *api.Protocol
|
||||
isTapTarget bool
|
||||
client *tcpReader
|
||||
server *tcpReader
|
||||
origin api.Capture
|
||||
counterPairs []*api.CounterPair
|
||||
reqResMatchers []api.RequestResponseMatcher
|
||||
createdAt time.Time
|
||||
streamsMap api.TcpStreamMap
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func NewTcpStream(isTapTarget bool, streamsMap api.TcpStreamMap, capture api.Capture) *tcpStream {
|
||||
return &tcpStream{
|
||||
isTapTarget: isTapTarget,
|
||||
protoIdentifier: &api.ProtoIdentifier{},
|
||||
streamsMap: streamsMap,
|
||||
origin: capture,
|
||||
isTapTarget: isTapTarget,
|
||||
streamsMap: streamsMap,
|
||||
origin: capture,
|
||||
createdAt: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,38 +57,12 @@ func (t *tcpStream) close() {
|
||||
|
||||
t.streamsMap.Delete(t.id)
|
||||
|
||||
for i := range t.clients {
|
||||
reader := t.clients[i]
|
||||
reader.close()
|
||||
}
|
||||
for i := range t.servers {
|
||||
reader := t.servers[i]
|
||||
reader.close()
|
||||
}
|
||||
t.client.close()
|
||||
t.server.close()
|
||||
}
|
||||
|
||||
func (t *tcpStream) addClient(reader *tcpReader) {
|
||||
t.clients = append(t.clients, reader)
|
||||
}
|
||||
|
||||
func (t *tcpStream) addServer(reader *tcpReader) {
|
||||
t.servers = append(t.servers, reader)
|
||||
}
|
||||
|
||||
func (t *tcpStream) getClients() []*tcpReader {
|
||||
return t.clients
|
||||
}
|
||||
|
||||
func (t *tcpStream) getServers() []*tcpReader {
|
||||
return t.servers
|
||||
}
|
||||
|
||||
func (t *tcpStream) getClient(index int) *tcpReader {
|
||||
return t.clients[index]
|
||||
}
|
||||
|
||||
func (t *tcpStream) getServer(index int) *tcpReader {
|
||||
return t.servers[index]
|
||||
func (t *tcpStream) addCounterPair(counterPair *api.CounterPair) {
|
||||
t.counterPairs = append(t.counterPairs, counterPair)
|
||||
}
|
||||
|
||||
func (t *tcpStream) addReqResMatcher(reqResMatcher api.RequestResponseMatcher) {
|
||||
@@ -94,44 +70,27 @@ func (t *tcpStream) addReqResMatcher(reqResMatcher api.RequestResponseMatcher) {
|
||||
}
|
||||
|
||||
func (t *tcpStream) SetProtocol(protocol *api.Protocol) {
|
||||
t.protocol = protocol
|
||||
|
||||
// Clean the buffers
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
|
||||
if t.protoIdentifier.IsClosedOthers {
|
||||
return
|
||||
}
|
||||
|
||||
t.protoIdentifier.Protocol = protocol
|
||||
|
||||
for i := range t.clients {
|
||||
reader := t.clients[i]
|
||||
if reader.GetExtension().Protocol != t.protoIdentifier.Protocol {
|
||||
reader.close()
|
||||
}
|
||||
}
|
||||
for i := range t.servers {
|
||||
reader := t.servers[i]
|
||||
if reader.GetExtension().Protocol != t.protoIdentifier.Protocol {
|
||||
reader.close()
|
||||
}
|
||||
}
|
||||
|
||||
t.protoIdentifier.IsClosedOthers = true
|
||||
t.client.msgBufferMaster = make([]api.TcpReaderDataMsg, 0)
|
||||
t.server.msgBufferMaster = make([]api.TcpReaderDataMsg, 0)
|
||||
t.Unlock()
|
||||
}
|
||||
|
||||
func (t *tcpStream) GetOrigin() api.Capture {
|
||||
return t.origin
|
||||
}
|
||||
|
||||
func (t *tcpStream) GetProtoIdentifier() *api.ProtoIdentifier {
|
||||
return t.protoIdentifier
|
||||
}
|
||||
|
||||
func (t *tcpStream) GetReqResMatchers() []api.RequestResponseMatcher {
|
||||
return t.reqResMatchers
|
||||
}
|
||||
|
||||
func (t *tcpStream) GetIsTapTarget() bool {
|
||||
if dbgctl.MizuTapperDisableTcpStream {
|
||||
return false
|
||||
}
|
||||
return t.isTapTarget
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package tap
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/up9inc/mizu/logger"
|
||||
"github.com/up9inc/mizu/tap/api"
|
||||
@@ -62,62 +61,50 @@ func (factory *tcpStreamFactory) New(net, transport gopacket.Flow, tcpLayer *lay
|
||||
reassemblyStream := NewTcpReassemblyStream(fmt.Sprintf("%s:%s", net, transport), tcpLayer, fsmOptions, stream)
|
||||
if stream.GetIsTapTarget() {
|
||||
stream.setId(factory.streamsMap.NextId())
|
||||
for i, extension := range extensions {
|
||||
reqResMatcher := extension.Dissector.NewResponseRequestMatcher()
|
||||
stream.addReqResMatcher(reqResMatcher)
|
||||
for _, extension := range extensions {
|
||||
counterPair := &api.CounterPair{
|
||||
Request: 0,
|
||||
Response: 0,
|
||||
}
|
||||
stream.addClient(
|
||||
NewTcpReader(
|
||||
make(chan api.TcpReaderDataMsg),
|
||||
&api.ReadProgress{},
|
||||
fmt.Sprintf("%s %s", net, transport),
|
||||
&api.TcpID{
|
||||
SrcIP: srcIp,
|
||||
DstIP: dstIp,
|
||||
SrcPort: srcPort,
|
||||
DstPort: dstPort,
|
||||
},
|
||||
time.Time{},
|
||||
stream,
|
||||
true,
|
||||
props.isOutgoing,
|
||||
extension,
|
||||
factory.emitter,
|
||||
counterPair,
|
||||
reqResMatcher,
|
||||
),
|
||||
)
|
||||
stream.addServer(
|
||||
NewTcpReader(
|
||||
make(chan api.TcpReaderDataMsg),
|
||||
&api.ReadProgress{},
|
||||
fmt.Sprintf("%s %s", net, transport),
|
||||
&api.TcpID{
|
||||
SrcIP: net.Dst().String(),
|
||||
DstIP: net.Src().String(),
|
||||
SrcPort: transport.Dst().String(),
|
||||
DstPort: transport.Src().String(),
|
||||
},
|
||||
time.Time{},
|
||||
stream,
|
||||
false,
|
||||
props.isOutgoing,
|
||||
extension,
|
||||
factory.emitter,
|
||||
counterPair,
|
||||
reqResMatcher,
|
||||
),
|
||||
)
|
||||
stream.addCounterPair(counterPair)
|
||||
|
||||
factory.streamsMap.Store(stream.getId(), stream)
|
||||
|
||||
factory.wg.Add(2)
|
||||
go stream.getClient(i).run(filteringOptions, &factory.wg)
|
||||
go stream.getServer(i).run(filteringOptions, &factory.wg)
|
||||
reqResMatcher := extension.Dissector.NewResponseRequestMatcher()
|
||||
stream.addReqResMatcher(reqResMatcher)
|
||||
}
|
||||
|
||||
stream.client = NewTcpReader(
|
||||
fmt.Sprintf("%s %s", net, transport),
|
||||
&api.TcpID{
|
||||
SrcIP: srcIp,
|
||||
DstIP: dstIp,
|
||||
SrcPort: srcPort,
|
||||
DstPort: dstPort,
|
||||
},
|
||||
stream,
|
||||
true,
|
||||
props.isOutgoing,
|
||||
factory.emitter,
|
||||
)
|
||||
|
||||
stream.server = NewTcpReader(
|
||||
fmt.Sprintf("%s %s", net, transport),
|
||||
&api.TcpID{
|
||||
SrcIP: net.Dst().String(),
|
||||
DstIP: net.Src().String(),
|
||||
SrcPort: transport.Dst().String(),
|
||||
DstPort: transport.Src().String(),
|
||||
},
|
||||
stream,
|
||||
false,
|
||||
props.isOutgoing,
|
||||
factory.emitter,
|
||||
)
|
||||
|
||||
factory.streamsMap.Store(stream.getId(), stream)
|
||||
|
||||
factory.wg.Add(2)
|
||||
go stream.client.run(filteringOptions, &factory.wg)
|
||||
go stream.server.run(filteringOptions, &factory.wg)
|
||||
}
|
||||
return reassemblyStream
|
||||
}
|
||||
|
||||
@@ -28,10 +28,12 @@ func (streamMap *tcpStreamMap) Range(f func(key, value interface{}) bool) {
|
||||
|
||||
func (streamMap *tcpStreamMap) Store(key, value interface{}) {
|
||||
streamMap.streams.Store(key, value)
|
||||
diagnose.AppStats.IncLiveTcpStreams()
|
||||
}
|
||||
|
||||
func (streamMap *tcpStreamMap) Delete(key interface{}) {
|
||||
streamMap.streams.Delete(key)
|
||||
diagnose.AppStats.DecLiveTcpStreams()
|
||||
}
|
||||
|
||||
func (streamMap *tcpStreamMap) NextId() int64 {
|
||||
@@ -57,7 +59,7 @@ func (streamMap *tcpStreamMap) CloseTimedoutTcpStreamChannels() {
|
||||
return true
|
||||
}
|
||||
|
||||
if stream.protoIdentifier.Protocol == nil {
|
||||
if stream.protocol == nil {
|
||||
if !stream.isClosed && time.Now().After(stream.createdAt.Add(tcpStreamChannelTimeoutMs)) {
|
||||
stream.close()
|
||||
diagnose.AppStats.IncDroppedTcpStreams()
|
||||
|
||||
@@ -68,7 +68,7 @@ struct fd_info {
|
||||
BPF_HASH(pids_map, __u32, __u32);
|
||||
BPF_LRU_HASH(ssl_write_context, __u64, struct ssl_info);
|
||||
BPF_LRU_HASH(ssl_read_context, __u64, struct ssl_info);
|
||||
BPF_HASH(file_descriptor_to_ipv4, __u64, struct fd_info);
|
||||
BPF_LRU_HASH(file_descriptor_to_ipv4, __u64, struct fd_info);
|
||||
BPF_PERF_OUTPUT(chunks_buffer);
|
||||
BPF_PERF_OUTPUT(log_buffer);
|
||||
|
||||
|
||||
@@ -188,8 +188,7 @@ func (p *tlsPoller) startNewTlsReader(chunk *tlsChunk, address *addressPair, key
|
||||
}
|
||||
|
||||
stream := &tlsStream{
|
||||
reader: reader,
|
||||
protoIdentifier: &api.ProtoIdentifier{},
|
||||
reader: reader,
|
||||
}
|
||||
streamsMap.Store(streamsMap.NextId(), stream)
|
||||
|
||||
|
||||
@@ -94,7 +94,3 @@ func (r *tlsReader) GetEmitter() api.Emitter {
|
||||
func (r *tlsReader) GetIsClosed() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *tlsReader) GetExtension() *api.Extension {
|
||||
return r.extension
|
||||
}
|
||||
|
||||
@@ -3,20 +3,16 @@ package tlstapper
|
||||
import "github.com/up9inc/mizu/tap/api"
|
||||
|
||||
type tlsStream struct {
|
||||
reader *tlsReader
|
||||
protoIdentifier *api.ProtoIdentifier
|
||||
reader *tlsReader
|
||||
protocol *api.Protocol
|
||||
}
|
||||
|
||||
func (t *tlsStream) GetOrigin() api.Capture {
|
||||
return api.Ebpf
|
||||
}
|
||||
|
||||
func (t *tlsStream) GetProtoIdentifier() *api.ProtoIdentifier {
|
||||
return t.protoIdentifier
|
||||
}
|
||||
|
||||
func (t *tlsStream) SetProtocol(protocol *api.Protocol) {
|
||||
t.protoIdentifier.Protocol = protocol
|
||||
t.protocol = protocol
|
||||
}
|
||||
|
||||
func (t *tlsStream) GetReqResMatchers() []api.RequestResponseMatcher {
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -91,4 +91,4 @@
|
||||
"files": [
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
22
ui-common/src/components.scss
Normal file
22
ui-common/src/components.scss
Normal file
@@ -0,0 +1,22 @@
|
||||
.subSectionHeader{
|
||||
position: relative;
|
||||
font-style: normal;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
color: $font-color;
|
||||
&::after{
|
||||
content: "";
|
||||
border: 1px solid #E9EBF8;
|
||||
transform: rotate(180deg);
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
right: -100%;
|
||||
top: 100%;
|
||||
bottom: 0%;
|
||||
width: 100%;
|
||||
width: -moz-available;
|
||||
width: -webkit-fill-available;
|
||||
width: strech;
|
||||
}
|
||||
}
|
||||
@@ -1,64 +1,98 @@
|
||||
@import "../../variables.module"
|
||||
@import "../../components"
|
||||
|
||||
.closeIcon
|
||||
position: absolute
|
||||
right: 20px
|
||||
top: 20px
|
||||
|
||||
.modalContainer
|
||||
display: flex
|
||||
height: calc(100% - 110px)
|
||||
background: #F0F5FF
|
||||
padding: 0 15px
|
||||
padding-bottom: 25px
|
||||
|
||||
.headerContainer
|
||||
width: 100%
|
||||
height: 100%
|
||||
background: white
|
||||
display: flex
|
||||
align-items: center
|
||||
margin-bottom: 15px
|
||||
|
||||
.headerSection
|
||||
display: flex
|
||||
align-content: center
|
||||
align-items: center
|
||||
margin-left: 35px
|
||||
margin-bottom: 25px
|
||||
margin-top: 25px
|
||||
|
||||
& .title
|
||||
font-size: 28px
|
||||
color: $blue-gray
|
||||
font-weight: 600
|
||||
margin-right: 35px
|
||||
|
||||
& .actions
|
||||
|
||||
.graphSection
|
||||
flex: 85%
|
||||
|
||||
.filterSection
|
||||
flex: 15%
|
||||
height: 100%
|
||||
display: none
|
||||
|
||||
&.show
|
||||
display: inline-block
|
||||
|
||||
.filters table
|
||||
margin-top: 0px
|
||||
|
||||
tr
|
||||
border-style: none
|
||||
|
||||
td
|
||||
color: #8f9bb2
|
||||
color: $light-gray
|
||||
font-size: 11px
|
||||
font-weight: 600
|
||||
padding-top: 2px
|
||||
padding-bottom: 2px
|
||||
padding-top: 5px
|
||||
padding-bottom: 5px
|
||||
th
|
||||
font-size: 12px
|
||||
|
||||
.colorBlock
|
||||
display: inline-block
|
||||
height: 15px
|
||||
width: 50px
|
||||
height: 12px
|
||||
width: 22px
|
||||
|
||||
.filterWrapper
|
||||
height: 100%
|
||||
display: flex
|
||||
flex-direction: column
|
||||
margin-right: 10px
|
||||
width: 100%
|
||||
border-radius: 4px
|
||||
|
||||
.servicesFilterSearch
|
||||
width: calc(100% - 10px)
|
||||
max-width: 300px
|
||||
width: -moz-available
|
||||
width: -webkit-fill-available
|
||||
width: fill-available
|
||||
max-width: 200px
|
||||
box-shadow: 0px 1px 5px #979797
|
||||
margin-left: 10px
|
||||
margin-bottom: 5px
|
||||
margin-top: 10px
|
||||
margin-right: 10px
|
||||
|
||||
.servicesFilter
|
||||
margin-top: 15px
|
||||
.card
|
||||
background: white
|
||||
padding: 10px
|
||||
border-radius: 4px
|
||||
user-select: none
|
||||
box-shadow: 0px 1px 5px #979797
|
||||
|
||||
.servicesFilterWrapper
|
||||
margin-top: 20px
|
||||
margin-bottom: 3px
|
||||
height: 100%
|
||||
overflow: hidden
|
||||
|
||||
& .servicesFilterList
|
||||
overflow-y: auto
|
||||
height: calc(100% - 30px - 5px)
|
||||
|
||||
.separtorLine
|
||||
margin-top: 10px
|
||||
border: 1px solid #E9EBF8
|
||||
|
||||
.closeIcon
|
||||
cursor: pointer
|
||||
user-select: none
|
||||
margin-top: -15px
|
||||
margin-right: 5px
|
||||
.servicesFilterList
|
||||
height: calc(100% - 30px - 52px)
|
||||
|
||||
@@ -8,6 +8,8 @@ import debounce from 'lodash/debounce';
|
||||
import ServiceMapOptions from './ServiceMapOptions'
|
||||
import { useCommonStyles } from "../../helpers/commonStyle";
|
||||
import refreshIcon from "assets/refresh.svg";
|
||||
import filterIcon from "assets/filter-icon.svg";
|
||||
import filterIconClicked from "assets/filter-icon-clicked.svg";
|
||||
import closeIcon from "assets/close.svg"
|
||||
import styles from './ServiceMapModal.module.sass'
|
||||
import SelectList from "../UI/SelectList";
|
||||
@@ -23,14 +25,14 @@ const modalStyle = {
|
||||
transform: 'translate(-50%, 0%)',
|
||||
width: '89vw',
|
||||
height: '82vh',
|
||||
bgcolor: 'background.paper',
|
||||
bgcolor: '#F0F5FF',
|
||||
borderRadius: '5px',
|
||||
boxShadow: 24,
|
||||
p: 4,
|
||||
color: '#000',
|
||||
padding: "25px 15px"
|
||||
padding: "1px 1px",
|
||||
paddingBottom: "15px"
|
||||
};
|
||||
|
||||
interface LegentLabelProps {
|
||||
color: string,
|
||||
name: string
|
||||
@@ -45,14 +47,11 @@ const LegentLabel: React.FC<LegentLabelProps> = ({ color, name }) => {
|
||||
</React.Fragment>
|
||||
}
|
||||
|
||||
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: "GQL", value: "GQL", component: <LegentLabel color='#e10098' name="GQL" /> },
|
||||
{ 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" /> },]
|
||||
type ProtocolType = {
|
||||
key: string;
|
||||
value: string;
|
||||
component: JSX.Element;
|
||||
};
|
||||
|
||||
|
||||
interface ServiceMapModalProps {
|
||||
@@ -66,11 +65,11 @@ 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 [checkedProtocols, setCheckedProtocols] = useState(protocols.map(x => x.key))
|
||||
const [checkedProtocols, setCheckedProtocols] = useState([])
|
||||
const [checkedServices, setCheckedServices] = useState([])
|
||||
const [serviceMapApiData, setServiceMapApiData] = useState<ServiceMapGraph>({ edges: [], nodes: [] })
|
||||
const [servicesSearchVal, setServicesSearchVal] = useState("")
|
||||
const [graphOptions, setGraphOptions] = useState(ServiceMapOptions);
|
||||
const [isFilterClicked, setIsFilterClicked] = useState(true)
|
||||
|
||||
const getServiceMapData = useCallback(async () => {
|
||||
try {
|
||||
@@ -113,15 +112,23 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onClos
|
||||
},
|
||||
}
|
||||
}
|
||||
const mapToKeyValForFilter = (arr) => arr.map(mapNodesDatatoGraph)
|
||||
const mapToKeyValForFilter = useCallback((arr) => arr.map(mapNodesDatatoGraph)
|
||||
.map((edge) => { return { key: edge.label, value: edge.label } })
|
||||
.sort((a, b) => { return a.key.localeCompare(b.key) });
|
||||
.sort((a, b) => { return a.key.localeCompare(b.key) }), [])
|
||||
|
||||
const getProtocolsForFilter = useMemo(() => {
|
||||
return serviceMapApiData.edges.reduce<ProtocolType[]>((returnArr, currentValue, currentIndex, array) => {
|
||||
if (!returnArr.find(prot => prot.key === currentValue.protocol.abbr))
|
||||
returnArr.push({ key: currentValue.protocol.abbr, value: currentValue.protocol.abbr, component: <LegentLabel color={currentValue.protocol.backgroundColor} name={currentValue.protocol.abbr} /> })
|
||||
return returnArr
|
||||
}, new Array<ProtocolType>())
|
||||
}, [serviceMapApiData])
|
||||
|
||||
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])
|
||||
}, [mapToKeyValForFilter, serviceMapApiData.nodes])
|
||||
|
||||
useEffect(() => {
|
||||
const newGraphData: GraphData = {
|
||||
@@ -142,10 +149,18 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onClos
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (checkedServices.length == 0)
|
||||
if (checkedServices.length === 0)
|
||||
setCheckedServices(getServicesForFilter.map(x => x.key).filter(serviceName => !Utils.isIpAddress(serviceName)))
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [getServicesForFilter])
|
||||
|
||||
useEffect(() => {
|
||||
if (checkedProtocols.length === 0) {
|
||||
setCheckedProtocols(getProtocolsForFilter.map(x => x.key))
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [getProtocolsForFilter])
|
||||
|
||||
useEffect(() => {
|
||||
getServiceMapData()
|
||||
}, [getServiceMapData])
|
||||
@@ -173,20 +188,45 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onClos
|
||||
BackdropProps={{ timeout: 500 }}>
|
||||
<Fade in={isOpen}>
|
||||
<Box sx={modalStyle}>
|
||||
<div className={styles.closeIcon}>
|
||||
<img src={closeIcon} alt="close" onClick={() => onClose()} style={{ cursor: "pointer", userSelect: "none" }}></img>
|
||||
</div>
|
||||
<div className={styles.headerContainer}>
|
||||
<div className={styles.headerSection}>
|
||||
<span className={styles.title}>Services</span>
|
||||
<Button size="medium"
|
||||
variant="contained"
|
||||
startIcon={<img src={isFilterClicked ? filterIconClicked : filterIcon} className="custom" alt="refresh" style={{ height: "26px", width: "26px" }}></img>}
|
||||
className={commonClasses.outlinedButton + " " + commonClasses.imagedButton + ` ${isFilterClicked ? commonClasses.clickedButton : ""}`}
|
||||
onClick={() => setIsFilterClicked(prevState => !prevState)}
|
||||
style={{ textTransform: 'unset' }}>
|
||||
Filter
|
||||
</Button >
|
||||
<Button style={{ marginLeft: "2%", textTransform: 'unset' }}
|
||||
startIcon={<img src={refreshIcon} className="custom" alt="refresh"></img>}
|
||||
size="medium"
|
||||
variant="contained"
|
||||
className={commonClasses.outlinedButton + " " + commonClasses.imagedButton}
|
||||
onClick={refreshServiceMap}
|
||||
>
|
||||
Refresh
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.modalContainer}>
|
||||
<div className={styles.filterSection}>
|
||||
<div className={styles.filterSection + ` ${isFilterClicked ? styles.show : ""}`}>
|
||||
<Resizeable minWidth={170} maxWidth={320}>
|
||||
<div className={styles.filterWrapper}>
|
||||
<div className={styles.protocolsFilterList}>
|
||||
<SelectList items={protocols} checkBoxWidth="5%" tableName={"Protocols"} multiSelect={true}
|
||||
checkedValues={checkedProtocols} setCheckedValues={onProtocolsChange} tableClassName={styles.filters} />
|
||||
<div className={styles.card}>
|
||||
<SelectList items={getProtocolsForFilter} checkBoxWidth="5%" tableName={"PROTOCOLS"} multiSelect={true}
|
||||
checkedValues={checkedProtocols} setCheckedValues={onProtocolsChange} tableClassName={styles.filters}
|
||||
inputSearchClass={styles.servicesFilterSearch} isFilterable={false}/>
|
||||
</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.servicesFilterWrapper + ` ${styles.card}`}>
|
||||
<div className={styles.servicesFilterList}>
|
||||
<SelectList items={getServicesForFilter} tableName={"Services"} tableClassName={styles.filters} multiSelect={true} searchValue={servicesSearchVal}
|
||||
checkBoxWidth="5%" checkedValues={checkedServices} setCheckedValues={onServiceChanges} />
|
||||
<SelectList items={getServicesForFilter} tableName={"SERVICES"} tableClassName={styles.filters} multiSelect={true}
|
||||
checkBoxWidth="5%" checkedValues={checkedServices} setCheckedValues={onServiceChanges} inputSearchClass={styles.servicesFilterSearch}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -194,16 +234,6 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onClos
|
||||
</div>
|
||||
<div className={styles.graphSection}>
|
||||
<div style={{ display: "flex", justifyContent: "space-between" }}>
|
||||
<Button style={{ marginLeft: "3%" }}
|
||||
startIcon={<img src={refreshIcon} className="custom" alt="refresh" style={{ marginRight: "8%" }}></img>}
|
||||
size="medium"
|
||||
variant="contained"
|
||||
className={commonClasses.outlinedButton + " " + commonClasses.imagedButton}
|
||||
onClick={refreshServiceMap}
|
||||
>
|
||||
Refresh
|
||||
</Button>
|
||||
<img src={closeIcon} alt="close" onClick={() => onClose()} className={styles.closeIcon}></img>
|
||||
</div>
|
||||
{isLoading && <div className={spinnerStyle.spinnerContainer}>
|
||||
<img alt="spinner" src={spinnerImg} style={{ height: 50 }} />
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6 8L10.6667 12.6667V18H13.3333V12.6667L18 8V6H6V8Z" stroke="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 182 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6 8L10.6667 12.6667V18H13.3333V12.6667L18 8V6H6V8Z" stroke="#205CF5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 184 B |
@@ -1,5 +1,5 @@
|
||||
import styles from "./EntrySections.module.sass";
|
||||
import React, { useState } from "react";
|
||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { SyntaxHighlighter } from "../../UI/SyntaxHighlighter/index";
|
||||
import CollapsibleContainer from "../../UI/CollapsibleContainer";
|
||||
import FancyTextDisplay from "../../UI/FancyTextDisplay";
|
||||
@@ -8,6 +8,7 @@ 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 { Utils } from "../../../helpers/Utils"
|
||||
|
||||
interface EntryViewLineProps {
|
||||
label: string;
|
||||
@@ -101,6 +102,12 @@ export const EntrySectionContainer: React.FC<EntrySectionContainerProps> = ({ ti
|
||||
</CollapsibleContainer>
|
||||
}
|
||||
|
||||
const MAXIMUM_BYTES_TO_FORMAT = 1000000; // The maximum of chars to highlight in body, in case the response can be megabytes
|
||||
const jsonLikeFormats = ['json', 'yaml', 'yml'];
|
||||
const xmlLikeFormats = ['xml', 'html'];
|
||||
const protobufFormats = ['application/grpc'];
|
||||
const supportedFormats = jsonLikeFormats.concat(xmlLikeFormats, protobufFormats);
|
||||
|
||||
interface EntryBodySectionProps {
|
||||
title: string,
|
||||
content: any,
|
||||
@@ -118,21 +125,21 @@ export const EntryBodySection: React.FC<EntryBodySectionProps> = ({
|
||||
contentType,
|
||||
selector,
|
||||
}) => {
|
||||
const MAXIMUM_BYTES_TO_FORMAT = 1000000; // The maximum of chars to highlight in body, in case the response can be megabytes
|
||||
const jsonLikeFormats = ['json', 'yaml', 'yml'];
|
||||
const xmlLikeFormats = ['xml', 'html'];
|
||||
const protobufFormats = ['application/grpc'];
|
||||
const supportedFormats = jsonLikeFormats.concat(xmlLikeFormats, protobufFormats);
|
||||
|
||||
const [isPretty, setIsPretty] = useState(true);
|
||||
const [showLineNumbers, setShowLineNumbers] = useState(true);
|
||||
const [showLineNumbers, setShowLineNumbers] = useState(false);
|
||||
const [decodeBase64, setDecodeBase64] = useState(true);
|
||||
|
||||
const isBase64Encoding = encoding === 'base64';
|
||||
const supportsPrettying = supportedFormats.some(format => contentType?.indexOf(format) > -1);
|
||||
const [isDecodeGrpc, setIsDecodeGrpc] = useState(true);
|
||||
const [isLineNumbersGreaterThenOne, setIsLineNumbersGreaterThenOne] = useState(true);
|
||||
|
||||
const formatTextBody = (body: any): string => {
|
||||
useEffect(() => {
|
||||
(isLineNumbersGreaterThenOne && isPretty) && setShowLineNumbers(true);
|
||||
!isLineNumbersGreaterThenOne && setShowLineNumbers(false);
|
||||
}, [isLineNumbersGreaterThenOne, isPretty])
|
||||
|
||||
const formatTextBody = useCallback((body: any): string => {
|
||||
if (!decodeBase64) return body;
|
||||
|
||||
const chunk = body.slice(0, MAXIMUM_BYTES_TO_FORMAT);
|
||||
@@ -158,15 +165,24 @@ export const EntryBodySection: React.FC<EntryBodySectionProps> = ({
|
||||
return jsonBeautify(protobufDecoded, null, 2, 80);
|
||||
}
|
||||
} catch (error) {
|
||||
if (String(error).includes("More than one message in")){
|
||||
if(isDecodeGrpc)
|
||||
setIsDecodeGrpc(false);
|
||||
if (String(error).includes("More than one message in")) {
|
||||
if (isDecodeGrpc)
|
||||
setIsDecodeGrpc(false);
|
||||
} else if (String(error).includes("Failed to parse")) {
|
||||
console.warn(error);
|
||||
} else {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
return bodyBuf;
|
||||
}
|
||||
}, [isPretty, contentType, isDecodeGrpc, decodeBase64, isBase64Encoding])
|
||||
|
||||
const formattedText = useMemo(() => formatTextBody(content), [formatTextBody, content]);
|
||||
|
||||
useEffect(() => {
|
||||
const lineNumbers = Utils.lineNumbersInString(formattedText);
|
||||
setIsLineNumbersGreaterThenOne(lineNumbers > 1);
|
||||
}, [isPretty, content, showLineNumbers, formattedText]);
|
||||
|
||||
return <React.Fragment>
|
||||
{content && content?.length > 0 && <EntrySectionContainer
|
||||
@@ -181,18 +197,19 @@ export const EntryBodySection: React.FC<EntryBodySectionProps> = ({
|
||||
{supportsPrettying && <span style={{ marginLeft: '.2rem' }}>Pretty</span>}
|
||||
|
||||
<div style={{ paddingTop: 3, paddingLeft: supportsPrettying ? 20 : 0 }}>
|
||||
<Checkbox checked={showLineNumbers} onToggle={() => { setShowLineNumbers(!showLineNumbers) }} />
|
||||
<Checkbox checked={showLineNumbers} onToggle={() => { setShowLineNumbers(!showLineNumbers) }} disabled={!isLineNumbersGreaterThenOne || !decodeBase64} />
|
||||
</div>
|
||||
<span style={{ marginLeft: '.2rem' }}>Line numbers</span>
|
||||
{isBase64Encoding && <div style={{ paddingTop: 3, paddingLeft: 20 }}>
|
||||
<span style={{ marginLeft: '.2rem' }}>Line numbers</span>
|
||||
|
||||
{isBase64Encoding && <div style={{ paddingTop: 3, paddingLeft: (isLineNumbersGreaterThenOne || supportsPrettying) ? 20 : 0 }}>
|
||||
<Checkbox checked={decodeBase64} onToggle={() => { setDecodeBase64(!decodeBase64) }} />
|
||||
</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>
|
||||
</div>
|
||||
|
||||
<SyntaxHighlighter
|
||||
code={formatTextBody(content)}
|
||||
<SyntaxHighlighter
|
||||
code={formattedText}
|
||||
showLineNumbers={showLineNumbers}
|
||||
/>
|
||||
|
||||
|
||||
@@ -296,7 +296,7 @@ export const EntryItem: React.FC<EntryProps> = ({entry, style, headingMode, name
|
||||
query={`dst.ip == "${entry.dst.ip}"`}
|
||||
displayIconOnMouseOver={true}
|
||||
flipped={false}
|
||||
iconStyle={{marginTop: "28px"}}
|
||||
iconStyle={{marginTop: "30px", marginLeft: "-2px",right: "35px", position: "relative"}}
|
||||
>
|
||||
<span
|
||||
className={`${styles.tcpInfo} ${styles.ip}`}
|
||||
|
||||
@@ -245,13 +245,18 @@ export const TrafficViewer: React.FC<TrafficViewerProps> = ({
|
||||
{tappingStatus && isShowStatusBar && <StatusBar disabled={ws?.current?.readyState !== WebSocket.OPEN} isDemoBannerView={isDemoBannerView}/>}
|
||||
<div className={TrafficViewerStyles.TrafficPageHeader}>
|
||||
<div className={TrafficViewerStyles.TrafficPageStreamStatus}>
|
||||
<img className={TrafficViewerStyles.playPauseIcon}
|
||||
style={{visibility: wsReadyState === WebSocket.OPEN ? "visible" : "hidden"}} alt="pause"
|
||||
src={pauseIcon} onClick={toggleConnection}/>
|
||||
<img className={TrafficViewerStyles.playPauseIcon}
|
||||
<img id="pause-icon"
|
||||
className={TrafficViewerStyles.playPauseIcon}
|
||||
style={{visibility: wsReadyState === WebSocket.OPEN ? "visible" : "hidden"}}
|
||||
alt="pause"
|
||||
src={pauseIcon}
|
||||
onClick={toggleConnection}/>
|
||||
<img id="play-icon"
|
||||
className={TrafficViewerStyles.playPauseIcon}
|
||||
style={{position: "absolute", visibility: wsReadyState === WebSocket.OPEN ? "hidden" : "visible"}}
|
||||
alt="play"
|
||||
src={playIcon} onClick={toggleConnection}/>
|
||||
src={playIcon}
|
||||
onClick={toggleConnection}/>
|
||||
<div className={TrafficViewerStyles.connectionText}>
|
||||
{getConnectionTitle()}
|
||||
{getConnectionIndicator()}
|
||||
|
||||
@@ -54,7 +54,7 @@ const Queryable: React.FC<Props> = ({query, style, iconStyle, className, useTool
|
||||
{flipped && addButton}
|
||||
{children}
|
||||
{!flipped && addButton}
|
||||
{useTooltip && showTooltip && <span data-cy={"QueryableTooltip"} className={QueryableStyle.QueryableTooltip} style={tooltipStyle}>{query}</span>}
|
||||
{useTooltip && showTooltip && (query !== "") && <span data-cy={"QueryableTooltip"} className={QueryableStyle.QueryableTooltip} style={tooltipStyle}>{query}</span>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@ import Radio from "./Radio";
|
||||
import styles from './style/SelectList.module.sass'
|
||||
import NoDataMessage from "./NoDataMessage";
|
||||
import Checkbox from "./Checkbox";
|
||||
import { useCommonStyles } from "../../helpers/commonStyle";
|
||||
|
||||
|
||||
export interface Props {
|
||||
@@ -10,14 +11,17 @@ export interface Props {
|
||||
tableName: string;
|
||||
checkedValues?: string[];
|
||||
multiSelect: boolean;
|
||||
searchValue?: string;
|
||||
setCheckedValues: (newValues) => void;
|
||||
tableClassName?
|
||||
checkBoxWidth?: string
|
||||
tableClassName?;
|
||||
checkBoxWidth?: string;
|
||||
inputSearchClass? : string
|
||||
isFilterable? : boolean
|
||||
}
|
||||
|
||||
const SelectList: React.FC<Props> = ({ items, tableName, checkedValues = [], multiSelect = true, searchValue = "", setCheckedValues, tableClassName,
|
||||
checkBoxWidth = 50 }) => {
|
||||
const SelectList: React.FC<Props> = ({ items, tableName, checkedValues = [], multiSelect = true, setCheckedValues, tableClassName,
|
||||
checkBoxWidth = 50 ,inputSearchClass,isFilterable = true}) => {
|
||||
const commonClasses = useCommonStyles();
|
||||
const [searchValue, setSearchValue] = useState("")
|
||||
const noItemsMessage = "No items to show";
|
||||
const [headerChecked, setHeaderChecked] = useState(false)
|
||||
|
||||
@@ -72,15 +76,16 @@ const SelectList: React.FC<Props> = ({ items, tableName, checkedValues = [], mul
|
||||
const tableHead = multiSelect ? <tr style={{ borderBottomWidth: "2px" }}>
|
||||
<th style={{ width: checkBoxWidth }}><Checkbox data-cy="checkbox-all" checked={headerChecked}
|
||||
onToggle={(isChecked) => toggleAll(isChecked)} /></th>
|
||||
<th>{tableName}</th>
|
||||
<th>
|
||||
All
|
||||
</th>
|
||||
</tr> :
|
||||
<tr style={{ borderBottomWidth: "2px" }}>
|
||||
<th>{tableName}</th>
|
||||
<tr>
|
||||
</tr>
|
||||
|
||||
const tableBody = filteredValues.length === 0 ?
|
||||
<tr>
|
||||
<td colSpan={2}>
|
||||
<td colSpan={2} className={styles.displayBlock}>
|
||||
<NoDataMessage messageText={noItemsMessage} />
|
||||
</td>
|
||||
</tr>
|
||||
@@ -98,7 +103,14 @@ const SelectList: React.FC<Props> = ({ items, tableName, checkedValues = [], mul
|
||||
}
|
||||
)
|
||||
|
||||
return <div className={tableClassName ? tableClassName + ` ${styles.selectListTable}` : ` ${styles.selectListTable}`}>
|
||||
return <React.Fragment>
|
||||
<h3 className={styles.subSectionHeader}>
|
||||
{tableName}
|
||||
<span className={styles.totalSelected}> ({checkedValues.length})</span>
|
||||
</h3>
|
||||
{isFilterable && <input className={commonClasses.textField + ` ${inputSearchClass}`} placeholder="Search" value={searchValue}
|
||||
onChange={(event) => setSearchValue(event.target.value)} data-cy="searchInput" />}
|
||||
<div className={tableClassName ? tableClassName + ` ${styles.selectListTable}` : ` ${styles.selectListTable}`} style={{marginTop: !multiSelect ? "20px": ""}}>
|
||||
<table cellPadding={5} style={{ borderCollapse: "collapse" }}>
|
||||
<thead>
|
||||
{tableHead}
|
||||
@@ -108,6 +120,7 @@ const SelectList: React.FC<Props> = ({ items, tableName, checkedValues = [], mul
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
}
|
||||
|
||||
export default SelectList;
|
||||
export default SelectList;
|
||||
|
||||
@@ -22,8 +22,8 @@ export const StatusBar: React.FC<StatusBarProps> = ({isDemoBannerView, disabled}
|
||||
const {uniqueNamespaces, amountOfPods, amountOfTappedPods, amountOfUntappedPods} = useRecoilValue(tappingStatusDetails);
|
||||
return <div style={{opacity: disabled ? 0.4 : 1}} className={`${isDemoBannerView ? `${style.banner}` : ''} ${style.statusBar} ${(expandedBar && !disabled ? `${style.expandedStatusBar}` : "")}`} onMouseOver={() => setExpandedBar(true)} onMouseLeave={() => setExpandedBar(false)} data-cy="expandedStatusBar">
|
||||
<div className={style.podsCount}>
|
||||
{tappingStatus.some(pod => !pod.isTapped) && <img src={warningIcon} alt="warning"/>}
|
||||
{disabled && <Tooltip title={"Tapping status is not updated when streaming is paused"} isSimple><img src={warningIcon} alt="warning"/></Tooltip>}
|
||||
{!disabled && tappingStatus.some(pod => !pod.isTapped) && <img src={warningIcon} alt="warning"/>}
|
||||
{disabled && <Tooltip title={"Tapping status is not updated when streaming is paused"} isSimple><img src={warningIcon} alt="warning"/></Tooltip>}
|
||||
<span className={style.podsCountText} data-cy="podsCountText">
|
||||
{`Tapping ${amountOfUntappedPods > 0 ? amountOfTappedPods + " / " + amountOfPods : amountOfPods} ${pluralize('pod', amountOfPods)} in ${pluralize('namespace', uniqueNamespaces.length)} ${uniqueNamespaces.join(", ")}`}
|
||||
</span>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import Lowlight from 'react-lowlight'
|
||||
import 'highlight.js/styles/atom-one-light.css'
|
||||
import styles from './index.module.sass';
|
||||
@@ -30,18 +30,23 @@ interface Props {
|
||||
}
|
||||
|
||||
export const SyntaxHighlighter: React.FC<Props> = ({
|
||||
code,
|
||||
showLineNumbers = false,
|
||||
language = null
|
||||
}) => {
|
||||
const markers = showLineNumbers ? code.split("\n").map((item, i) => {
|
||||
return {
|
||||
line: i + 1,
|
||||
className: styles.hljsMarkerLine
|
||||
}
|
||||
}) : [];
|
||||
code,
|
||||
showLineNumbers = false,
|
||||
language = null,
|
||||
}) => {
|
||||
const [markers, setMarkers] = useState([])
|
||||
|
||||
return <div style={{fontSize: ".75rem"}} className={styles.highlighterContainer}><Lowlight language={language ? language : ""} value={code} markers={markers}/></div>;
|
||||
useEffect(() => {
|
||||
const newMarkers = code.split("\n").map((item, i) => {
|
||||
return {
|
||||
line: i + 1,
|
||||
className: styles.hljsMarkerLine
|
||||
}
|
||||
});
|
||||
setMarkers(showLineNumbers ? newMarkers : []);
|
||||
}, [showLineNumbers, code])
|
||||
|
||||
return <div style={{ fontSize: ".75rem" }} className={styles.highlighterContainer}><Lowlight language={language ? language : ""} value={code} markers={markers} /></div>;
|
||||
};
|
||||
|
||||
export default SyntaxHighlighter;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
align-items: center
|
||||
display: flex
|
||||
overflow: hidden
|
||||
border-right: 1px solid $blue-color
|
||||
height: 100%
|
||||
width: 100%
|
||||
padding-right: 3px
|
||||
|
||||
@@ -1,33 +1,69 @@
|
||||
@import '../../../variables.module'
|
||||
@import '../../../components'
|
||||
|
||||
.selectListTable
|
||||
overflow: auto
|
||||
height: 100%
|
||||
user-select: none // when resizble moved we get unwanted beheviour
|
||||
height: 100%
|
||||
|
||||
table
|
||||
width: 100%
|
||||
margin-top: 20px
|
||||
border-collapse: collapse
|
||||
table-layout: fixed
|
||||
height: 100%
|
||||
display: flex
|
||||
flex-flow: column
|
||||
height: 100%
|
||||
|
||||
th
|
||||
color: $blue-gray
|
||||
text-align: left
|
||||
padding: 10px
|
||||
position: sticky
|
||||
top: 0
|
||||
background: $main-background-color
|
||||
thead
|
||||
display: table
|
||||
table-layout: fixed
|
||||
flex: 0 0 auto
|
||||
width: calc(100% - 0.9em)
|
||||
|
||||
tr
|
||||
border-bottom-width: 1px
|
||||
border-bottom-color: $data-background-color
|
||||
border-bottom-style: solid
|
||||
width: 100%
|
||||
tbody
|
||||
display: block
|
||||
overflow: auto
|
||||
width: 100%
|
||||
height: 100%
|
||||
flex: 1 1 auto
|
||||
|
||||
td
|
||||
color: $light-gray
|
||||
padding: 10px
|
||||
font-size: 16px
|
||||
tbody tr:hover
|
||||
background: $header-background-color
|
||||
|
||||
th
|
||||
color: $blue-gray
|
||||
text-align: left
|
||||
padding: 10px
|
||||
background: $main-background-color
|
||||
font-size: 12px
|
||||
|
||||
tr
|
||||
border-bottom-width: 1px
|
||||
border-bottom-color: $data-background-color
|
||||
border-bottom-style: solid
|
||||
width: 100%
|
||||
display: block
|
||||
position: relative
|
||||
display: table
|
||||
table-layout: fixed
|
||||
|
||||
td
|
||||
color: $light-gray
|
||||
padding: 10px
|
||||
font-size: 15px
|
||||
font-weight: 600
|
||||
padding: 10px
|
||||
|
||||
.nowrap
|
||||
white-space: nowrap
|
||||
|
||||
.totalSelected
|
||||
font-size: 12px
|
||||
color: $light-blue-color
|
||||
font-weight: 700
|
||||
|
||||
.displayBlock
|
||||
display: block
|
||||
|
||||
.filterInput
|
||||
margin-bottom: 20px
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user