mirror of
https://github.com/kubeshark/kubeshark.git
synced 2026-06-07 08:47:00 +00:00
Compare commits
12 Commits
33.0-dev22
...
33.0-dev32
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7edb0b153b | ||
|
|
569a687fdf | ||
|
|
11e8b5eb65 | ||
|
|
3901f3f3fe | ||
|
|
2f1cc21fcb | ||
|
|
433253a27b | ||
|
|
00cc94fbe5 | ||
|
|
8feef78ab1 | ||
|
|
992abc99bc | ||
|
|
486d0b1088 | ||
|
|
f61a02d288 | ||
|
|
03694e57c0 |
@@ -58,7 +58,7 @@ export function rightOnHoverCheck(path, expectedText) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function checkFilterByMethod(funcDict) {
|
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 summaryDict = getSummaryDict(summary, summaryQuery);
|
||||||
const methodDict = getMethodDict(method, methodQuery);
|
const methodDict = getMethodDict(method, methodQuery);
|
||||||
const protocolDict = getProtocolDict(protocol.name, protocol.text);
|
const protocolDict = getProtocolDict(protocol.name, protocol.text);
|
||||||
@@ -69,47 +69,53 @@ export function checkFilterByMethod(funcDict) {
|
|||||||
cy.get('[type="submit"]').click();
|
cy.get('[type="submit"]').click();
|
||||||
cy.get('.w-tc-editor').should('have.attr', 'style').and('include', Cypress.env('greenFilterColor'));
|
cy.get('.w-tc-editor').should('have.attr', 'style').and('include', Cypress.env('greenFilterColor'));
|
||||||
|
|
||||||
cy.get('#entries-length').should('not.have.text', '0').then(() => {
|
waitForFetch(numberOfRecords);
|
||||||
cy.get(`#list [id]`).then(elements => {
|
pauseStream();
|
||||||
const listElmWithIdAttr = Object.values(elements);
|
|
||||||
let doneCheckOnFirst = false;
|
|
||||||
|
|
||||||
cy.get('#entries-length').invoke('text').then(len => {
|
cy.get(`#list [id^=entry]`).then(elements => {
|
||||||
resizeIfNeeded(len);
|
const listElmWithIdAttr = Object.values(elements);
|
||||||
listElmWithIdAttr.forEach(entry => {
|
let doneCheckOnFirst = false;
|
||||||
if (entry?.id && entry.id.match(RegExp(/entry-(\d{24})$/gm))) {
|
|
||||||
const entryId = getEntryId(entry.id);
|
|
||||||
|
|
||||||
leftTextCheck(entryId, methodDict.pathLeft, methodDict.expectedText);
|
cy.get('#entries-length').invoke('text').then(len => {
|
||||||
leftTextCheck(entryId, protocolDict.pathLeft, protocolDict.expectedTextLeft);
|
listElmWithIdAttr.forEach(entry => {
|
||||||
if (summaryDict)
|
if (entry?.id && entry.id.match(RegExp(/entry-(\d{24})$/gm))) {
|
||||||
leftTextCheck(entryId, summaryDict.pathLeft, summaryDict.expectedText);
|
const entryId = getEntryId(entry.id);
|
||||||
|
|
||||||
if (!doneCheckOnFirst) {
|
leftTextCheck(entryId, methodDict.pathLeft, methodDict.expectedText);
|
||||||
deepCheck(funcDict, protocolDict, methodDict, entry);
|
leftTextCheck(entryId, protocolDict.pathLeft, protocolDict.expectedTextLeft);
|
||||||
doneCheckOnFirst = true;
|
if (summaryDict)
|
||||||
}
|
leftTextCheck(entryId, summaryDict.pathLeft, summaryDict.expectedText);
|
||||||
}
|
|
||||||
});
|
if (!doneCheckOnFirst) {
|
||||||
resizeIfNeeded(len);
|
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) {
|
export function getEntryId(id) {
|
||||||
// take the second part from the string (entry-<ID>)
|
// take the second part from the string (entry-<ID>)
|
||||||
return id.split('-')[1];
|
return id.split('-')[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
function resizeIfNeeded(entriesLen) {
|
|
||||||
if (entriesLen > maxEntriesInDom){
|
|
||||||
Cypress.config().viewportHeight === Cypress.env('normalMizuHeight') ?
|
|
||||||
resizeToHugeMizu() : resizeToNormalMizu()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function deepCheck(generalDict, protocolDict, methodDict, entry) {
|
function deepCheck(generalDict, protocolDict, methodDict, entry) {
|
||||||
const entryId = getEntryId(entry.id);
|
const entryId = getEntryId(entry.id);
|
||||||
const {summary, value} = generalDict;
|
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 rabbitProtocolDetails = {name: 'AMQP', text: 'Advanced Message Queuing Protocol 0-9-1'};
|
||||||
|
const numberOfRecords = 5;
|
||||||
|
|
||||||
checkFilterByMethod({
|
checkFilterByMethod({
|
||||||
protocol: rabbitProtocolDetails,
|
protocol: rabbitProtocolDetails,
|
||||||
@@ -12,6 +13,7 @@ checkFilterByMethod({
|
|||||||
methodQuery: 'request.method == "exchange declare"',
|
methodQuery: 'request.method == "exchange declare"',
|
||||||
summary: 'exchange',
|
summary: 'exchange',
|
||||||
summaryQuery: 'request.exchange == "exchange"',
|
summaryQuery: 'request.exchange == "exchange"',
|
||||||
|
numberOfRecords: numberOfRecords,
|
||||||
value: null
|
value: null
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -21,6 +23,7 @@ checkFilterByMethod({
|
|||||||
methodQuery: 'request.method == "queue declare"',
|
methodQuery: 'request.method == "queue declare"',
|
||||||
summary: 'queue',
|
summary: 'queue',
|
||||||
summaryQuery: 'request.queue == "queue"',
|
summaryQuery: 'request.queue == "queue"',
|
||||||
|
numberOfRecords: numberOfRecords,
|
||||||
value: null
|
value: null
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -30,6 +33,7 @@ checkFilterByMethod({
|
|||||||
methodQuery: 'request.method == "queue bind"',
|
methodQuery: 'request.method == "queue bind"',
|
||||||
summary: 'queue',
|
summary: 'queue',
|
||||||
summaryQuery: 'request.queue == "queue"',
|
summaryQuery: 'request.queue == "queue"',
|
||||||
|
numberOfRecords: numberOfRecords,
|
||||||
value: null
|
value: null
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -39,6 +43,7 @@ checkFilterByMethod({
|
|||||||
methodQuery: 'request.method == "basic publish"',
|
methodQuery: 'request.method == "basic publish"',
|
||||||
summary: 'exchange',
|
summary: 'exchange',
|
||||||
summaryQuery: 'request.exchange == "exchange"',
|
summaryQuery: 'request.exchange == "exchange"',
|
||||||
|
numberOfRecords: numberOfRecords,
|
||||||
value: {tab: valueTabs.request, regex: /^message$/mg}
|
value: {tab: valueTabs.request, regex: /^message$/mg}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -48,6 +53,7 @@ checkFilterByMethod({
|
|||||||
methodQuery: 'request.method == "basic consume"',
|
methodQuery: 'request.method == "basic consume"',
|
||||||
summary: 'queue',
|
summary: 'queue',
|
||||||
summaryQuery: 'request.queue == "queue"',
|
summaryQuery: 'request.queue == "queue"',
|
||||||
|
numberOfRecords: numberOfRecords,
|
||||||
value: null
|
value: null
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -57,5 +63,6 @@ checkFilterByMethod({
|
|||||||
methodQuery: 'request.method == "basic deliver"',
|
methodQuery: 'request.method == "basic deliver"',
|
||||||
summary: 'exchange',
|
summary: 'exchange',
|
||||||
summaryQuery: 'request.queue == "exchange"',
|
summaryQuery: 'request.queue == "exchange"',
|
||||||
|
numberOfRecords: numberOfRecords,
|
||||||
value: {tab: valueTabs.request, regex: /^message$/mg}
|
value: {tab: valueTabs.request, regex: /^message$/mg}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ it('opening mizu', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const redisProtocolDetails = {name: 'redis', text: 'Redis Serialization Protocol'};
|
const redisProtocolDetails = {name: 'redis', text: 'Redis Serialization Protocol'};
|
||||||
|
const numberOfRecords = 5;
|
||||||
|
|
||||||
checkFilterByMethod({
|
checkFilterByMethod({
|
||||||
protocol: redisProtocolDetails,
|
protocol: redisProtocolDetails,
|
||||||
@@ -12,6 +13,7 @@ checkFilterByMethod({
|
|||||||
methodQuery: 'request.command == "PING"',
|
methodQuery: 'request.command == "PING"',
|
||||||
summary: null,
|
summary: null,
|
||||||
summaryQuery: '',
|
summaryQuery: '',
|
||||||
|
numberOfRecords: numberOfRecords,
|
||||||
value: null
|
value: null
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -21,6 +23,7 @@ checkFilterByMethod({
|
|||||||
methodQuery: 'request.command == "SET"',
|
methodQuery: 'request.command == "SET"',
|
||||||
summary: 'key',
|
summary: 'key',
|
||||||
summaryQuery: 'request.key == "key"',
|
summaryQuery: 'request.key == "key"',
|
||||||
|
numberOfRecords: numberOfRecords,
|
||||||
value: {tab: valueTabs.request, regex: /^\[value, keepttl]$/mg}
|
value: {tab: valueTabs.request, regex: /^\[value, keepttl]$/mg}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -30,6 +33,7 @@ checkFilterByMethod({
|
|||||||
methodQuery: 'request.command == "EXISTS"',
|
methodQuery: 'request.command == "EXISTS"',
|
||||||
summary: 'key',
|
summary: 'key',
|
||||||
summaryQuery: 'request.key == "key"',
|
summaryQuery: 'request.key == "key"',
|
||||||
|
numberOfRecords: numberOfRecords,
|
||||||
value: {tab: valueTabs.response, regex: /^1$/mg}
|
value: {tab: valueTabs.response, regex: /^1$/mg}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -39,6 +43,7 @@ checkFilterByMethod({
|
|||||||
methodQuery: 'request.command == "GET"',
|
methodQuery: 'request.command == "GET"',
|
||||||
summary: 'key',
|
summary: 'key',
|
||||||
summaryQuery: 'request.key == "key"',
|
summaryQuery: 'request.key == "key"',
|
||||||
|
numberOfRecords: numberOfRecords,
|
||||||
value: {tab: valueTabs.response, regex: /^value$/mg}
|
value: {tab: valueTabs.response, regex: /^value$/mg}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -48,5 +53,6 @@ checkFilterByMethod({
|
|||||||
methodQuery: 'request.command == "DEL"',
|
methodQuery: 'request.command == "DEL"',
|
||||||
summary: 'key',
|
summary: 'key',
|
||||||
summaryQuery: 'request.key == "key"',
|
summaryQuery: 'request.key == "key"',
|
||||||
|
numberOfRecords: numberOfRecords,
|
||||||
value: {tab: valueTabs.response, regex: /^1$|^0$/mg}
|
value: {tab: valueTabs.response, regex: /^1$|^0$/mg}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ import {
|
|||||||
resizeToNormalMizu,
|
resizeToNormalMizu,
|
||||||
rightOnHoverCheck,
|
rightOnHoverCheck,
|
||||||
rightTextCheck,
|
rightTextCheck,
|
||||||
verifyMinimumEntries
|
verifyMinimumEntries,
|
||||||
|
refreshWaitTimeout,
|
||||||
|
waitForFetch,
|
||||||
|
pauseStream
|
||||||
} from "../testHelpers/TrafficHelper";
|
} from "../testHelpers/TrafficHelper";
|
||||||
|
|
||||||
const refreshWaitTimeout = 10000;
|
|
||||||
|
|
||||||
|
|
||||||
const fullParam = Cypress.env('arrayDict'); // "Name:fooNamespace:barName:foo1Namespace:bar1"
|
const fullParam = Cypress.env('arrayDict'); // "Name:fooNamespace:barName:foo1Namespace:bar1"
|
||||||
const podsArray = fullParam.split('Name:').slice(1); // ["fooNamespace:bar", "foo1Namespace:bar1"]
|
const podsArray = fullParam.split('Name:').slice(1); // ["fooNamespace:bar", "foo1Namespace:bar1"]
|
||||||
podsArray.forEach((podStr, index) => {
|
podsArray.forEach((podStr, index) => {
|
||||||
@@ -70,7 +70,8 @@ checkFilter({
|
|||||||
leftSideExpectedText: 'HTTP',
|
leftSideExpectedText: 'HTTP',
|
||||||
rightSidePath: '[title=HTTP]',
|
rightSidePath: '[title=HTTP]',
|
||||||
rightSideExpectedText: 'Hypertext Transfer Protocol -- HTTP/1.1',
|
rightSideExpectedText: 'Hypertext Transfer Protocol -- HTTP/1.1',
|
||||||
applyByCtrlEnter: true
|
applyByCtrlEnter: true,
|
||||||
|
numberOfRecords: 20,
|
||||||
});
|
});
|
||||||
|
|
||||||
checkFilter({
|
checkFilter({
|
||||||
@@ -79,7 +80,8 @@ checkFilter({
|
|||||||
leftSideExpectedText: '200',
|
leftSideExpectedText: '200',
|
||||||
rightSidePath: '> :nth-child(2) [title="Status Code"]',
|
rightSidePath: '> :nth-child(2) [title="Status Code"]',
|
||||||
rightSideExpectedText: '200',
|
rightSideExpectedText: '200',
|
||||||
applyByCtrlEnter: false
|
applyByCtrlEnter: false,
|
||||||
|
numberOfRecords: 20
|
||||||
});
|
});
|
||||||
|
|
||||||
if (Cypress.env('shouldCheckSrcAndDest')) {
|
if (Cypress.env('shouldCheckSrcAndDest')) {
|
||||||
@@ -91,7 +93,8 @@ if (Cypress.env('shouldCheckSrcAndDest')) {
|
|||||||
leftSideExpectedText: '[Unresolved]',
|
leftSideExpectedText: '[Unresolved]',
|
||||||
rightSidePath: '> :nth-child(2) [title="Source Name"]',
|
rightSidePath: '> :nth-child(2) [title="Source Name"]',
|
||||||
rightSideExpectedText: '[Unresolved]',
|
rightSideExpectedText: '[Unresolved]',
|
||||||
applyByCtrlEnter: false
|
applyByCtrlEnter: false,
|
||||||
|
numberOfRecords: 20
|
||||||
});
|
});
|
||||||
|
|
||||||
checkFilter({
|
checkFilter({
|
||||||
@@ -100,7 +103,8 @@ if (Cypress.env('shouldCheckSrcAndDest')) {
|
|||||||
leftSideExpectedText: 'httpbin.mizu-tests',
|
leftSideExpectedText: 'httpbin.mizu-tests',
|
||||||
rightSidePath: '> :nth-child(2) > :nth-child(2) > :nth-child(2) > :nth-child(3) > :nth-child(2)',
|
rightSidePath: '> :nth-child(2) > :nth-child(2) > :nth-child(2) > :nth-child(3) > :nth-child(2)',
|
||||||
rightSideExpectedText: 'httpbin.mizu-tests',
|
rightSideExpectedText: 'httpbin.mizu-tests',
|
||||||
applyByCtrlEnter: false
|
applyByCtrlEnter: false,
|
||||||
|
numberOfRecords: 20
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,7 +114,8 @@ checkFilter({
|
|||||||
leftSideExpectedText: 'GET',
|
leftSideExpectedText: 'GET',
|
||||||
rightSidePath: '> :nth-child(2) > :nth-child(2) > :nth-child(1) > :nth-child(1) > :nth-child(2)',
|
rightSidePath: '> :nth-child(2) > :nth-child(2) > :nth-child(1) > :nth-child(1) > :nth-child(2)',
|
||||||
rightSideExpectedText: 'GET',
|
rightSideExpectedText: 'GET',
|
||||||
applyByCtrlEnter: true
|
applyByCtrlEnter: true,
|
||||||
|
numberOfRecords: 20
|
||||||
});
|
});
|
||||||
|
|
||||||
checkFilter({
|
checkFilter({
|
||||||
@@ -119,7 +124,8 @@ checkFilter({
|
|||||||
leftSideExpectedText: '/get',
|
leftSideExpectedText: '/get',
|
||||||
rightSidePath: '> :nth-child(2) > :nth-child(2) > :nth-child(1) > :nth-child(2) > :nth-child(2)',
|
rightSidePath: '> :nth-child(2) > :nth-child(2) > :nth-child(1) > :nth-child(2) > :nth-child(2)',
|
||||||
rightSideExpectedText: '/get',
|
rightSideExpectedText: '/get',
|
||||||
applyByCtrlEnter: false
|
applyByCtrlEnter: false,
|
||||||
|
numberOfRecords: 20
|
||||||
});
|
});
|
||||||
|
|
||||||
checkFilter({
|
checkFilter({
|
||||||
@@ -128,7 +134,8 @@ checkFilter({
|
|||||||
leftSideExpectedText: '127.0.0.1',
|
leftSideExpectedText: '127.0.0.1',
|
||||||
rightSidePath: '> :nth-child(2) [title="Source IP"]',
|
rightSidePath: '> :nth-child(2) [title="Source IP"]',
|
||||||
rightSideExpectedText: '127.0.0.1',
|
rightSideExpectedText: '127.0.0.1',
|
||||||
applyByCtrlEnter: false
|
applyByCtrlEnter: false,
|
||||||
|
numberOfRecords: 20
|
||||||
});
|
});
|
||||||
|
|
||||||
checkFilterNoResults('request.method == "POST"');
|
checkFilterNoResults('request.method == "POST"');
|
||||||
@@ -187,7 +194,8 @@ function checkFilter(filterDetails) {
|
|||||||
rightSidePath,
|
rightSidePath,
|
||||||
rightSideExpectedText,
|
rightSideExpectedText,
|
||||||
leftSideExpectedText,
|
leftSideExpectedText,
|
||||||
applyByCtrlEnter
|
applyByCtrlEnter,
|
||||||
|
numberOfRecords
|
||||||
} = filterDetails;
|
} = filterDetails;
|
||||||
|
|
||||||
const entriesForDeeperCheck = 5;
|
const entriesForDeeperCheck = 5;
|
||||||
@@ -200,7 +208,7 @@ function checkFilter(filterDetails) {
|
|||||||
if (!applyByCtrlEnter)
|
if (!applyByCtrlEnter)
|
||||||
cy.get('[type="submit"]').click();
|
cy.get('[type="submit"]').click();
|
||||||
|
|
||||||
waitForFetch();
|
waitForFetch(numberOfRecords);
|
||||||
pauseStream();
|
pauseStream();
|
||||||
|
|
||||||
cy.get(`#list [id^=entry]`).last().then(elem => {
|
cy.get(`#list [id^=entry]`).last().then(elem => {
|
||||||
@@ -231,22 +239,11 @@ function checkFilter(filterDetails) {
|
|||||||
// reloading then waiting for the entries number to load
|
// reloading then waiting for the entries number to load
|
||||||
resizeToNormalMizu();
|
resizeToNormalMizu();
|
||||||
cy.reload();
|
cy.reload();
|
||||||
waitForFetch();
|
waitForFetch(numberOfRecords);
|
||||||
pauseStream();
|
pauseStream();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function waitForFetch() {
|
|
||||||
cy.get('#entries-length', {timeout: refreshWaitTimeout}).should((el) => {
|
|
||||||
expect(parseInt(el.text().trim(), 10)).to.be.greaterThan(20);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function pauseStream() {
|
|
||||||
cy.get('#pause-icon').click();
|
|
||||||
cy.get('#pause-icon').should('not.be.visible');
|
|
||||||
}
|
|
||||||
|
|
||||||
function deeperCheck(leftSidePath, rightSidePath, filterName, rightSideExpectedText, entriesNumToCheck) {
|
function deeperCheck(leftSidePath, rightSidePath, filterName, rightSideExpectedText, entriesNumToCheck) {
|
||||||
cy.get(`#list [id^=entry]`).each((element, index) => {
|
cy.get(`#list [id^=entry]`).each((element, index) => {
|
||||||
if (index < entriesNumToCheck) {
|
if (index < entriesNumToCheck) {
|
||||||
@@ -271,11 +268,12 @@ function checkRightSideResponseBody() {
|
|||||||
const decodedBody = atob(encodedBody);
|
const decodedBody = atob(encodedBody);
|
||||||
const responseBody = JSON.parse(decodedBody);
|
const responseBody = JSON.parse(decodedBody);
|
||||||
|
|
||||||
|
|
||||||
const expectdJsonBody = {
|
const expectdJsonBody = {
|
||||||
args: RegExp({}),
|
args: RegExp({}),
|
||||||
url: RegExp('http://.*/get'),
|
url: RegExp('http://.*/get'),
|
||||||
headers: {
|
headers: {
|
||||||
"User-Agent": RegExp('[REDACTED]'),
|
"User-Agent": RegExp('client'),
|
||||||
"Accept-Encoding": RegExp('gzip'),
|
"Accept-Encoding": RegExp('gzip'),
|
||||||
"X-Forwarded-Uri": RegExp('/api/v1/namespaces/.*/services/.*/proxy/get')
|
"X-Forwarded-Uri": RegExp('/api/v1/namespaces/.*/services/.*/proxy/get')
|
||||||
}
|
}
|
||||||
@@ -292,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', 1).then(linesNum => {
|
||||||
cy.get(`${Cypress.env('bodyJsonClass')} > >`).its('length').should('be.gt', linesNum).then(jsonItemsNum => {
|
cy.get(`${Cypress.env('bodyJsonClass')} > >`).its('length').should('be.gt', linesNum).then(jsonItemsNum => {
|
||||||
checkPrettyAndLineNums(jsonItemsNum, decodedBody);
|
// checkPrettyAndLineNums(decodedBody);
|
||||||
|
|
||||||
clickCheckbox('Line numbers');
|
//clickCheckbox('Line numbers');
|
||||||
checkPrettyOrNothing(jsonItemsNum, decodedBody);
|
//checkPrettyOrNothing(jsonItemsNum, decodedBody);
|
||||||
|
|
||||||
clickCheckbox('Pretty');
|
// clickCheckbox('Pretty');
|
||||||
checkPrettyOrNothing(jsonItemsNum, decodedBody);
|
// checkPrettyOrNothing(jsonItemsNum, decodedBody);
|
||||||
|
//
|
||||||
clickCheckbox('Line numbers');
|
// clickCheckbox('Line numbers');
|
||||||
checkOnlyLineNumberes(jsonItemsNum, decodedBody);
|
// checkOnlyLineNumberes(jsonItemsNum, decodedBody);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -311,7 +309,7 @@ function clickCheckbox(type) {
|
|||||||
cy.contains(`${type}`).prev().children().click();
|
cy.contains(`${type}`).prev().children().click();
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkPrettyAndLineNums(jsonItemsLen, decodedBody) {
|
function checkPrettyAndLineNums(decodedBody) {
|
||||||
decodedBody = decodedBody.replaceAll(' ', '');
|
decodedBody = decodedBody.replaceAll(' ', '');
|
||||||
cy.get(`${Cypress.env('bodyJsonClass')} >`).then(elements => {
|
cy.get(`${Cypress.env('bodyJsonClass')} >`).then(elements => {
|
||||||
const lines = Object.values(elements);
|
const lines = Object.values(elements);
|
||||||
|
|||||||
@@ -343,6 +343,7 @@ func TestTapRedact(t *testing.T) {
|
|||||||
|
|
||||||
tapNamespace := GetDefaultTapNamespace()
|
tapNamespace := GetDefaultTapNamespace()
|
||||||
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
||||||
|
tapCmdArgs = append(tapCmdArgs, "--redact")
|
||||||
|
|
||||||
tapCmd := exec.Command(cliPath, tapCmdArgs...)
|
tapCmd := exec.Command(cliPath, tapCmdArgs...)
|
||||||
t.Logf("running command: %v", tapCmd.String())
|
t.Logf("running command: %v", tapCmd.String())
|
||||||
@@ -394,8 +395,6 @@ func TestTapNoRedact(t *testing.T) {
|
|||||||
tapNamespace := GetDefaultTapNamespace()
|
tapNamespace := GetDefaultTapNamespace()
|
||||||
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
||||||
|
|
||||||
tapCmdArgs = append(tapCmdArgs, "--no-redact")
|
|
||||||
|
|
||||||
tapCmd := exec.Command(cliPath, tapCmdArgs...)
|
tapCmd := exec.Command(cliPath, tapCmdArgs...)
|
||||||
t.Logf("running command: %v", tapCmd.String())
|
t.Logf("running command: %v", tapCmd.String())
|
||||||
|
|
||||||
@@ -446,6 +445,8 @@ func TestTapRegexMasking(t *testing.T) {
|
|||||||
tapNamespace := GetDefaultTapNamespace()
|
tapNamespace := GetDefaultTapNamespace()
|
||||||
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
tapCmdArgs = append(tapCmdArgs, tapNamespace...)
|
||||||
|
|
||||||
|
tapCmdArgs = append(tapCmdArgs, "--redact")
|
||||||
|
|
||||||
tapCmdArgs = append(tapCmdArgs, "-r", "Mizu")
|
tapCmdArgs = append(tapCmdArgs, "-r", "Mizu")
|
||||||
|
|
||||||
tapCmd := exec.Command(cliPath, tapCmdArgs...)
|
tapCmd := exec.Command(cliPath, tapCmdArgs...)
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ func runInHarReaderMode() {
|
|||||||
func enableExpFeatureIfNeeded() {
|
func enableExpFeatureIfNeeded() {
|
||||||
if config.Config.OAS {
|
if config.Config.OAS {
|
||||||
oasGenerator := dependency.GetInstance(dependency.OasGeneratorDependency).(oas.OasGenerator)
|
oasGenerator := dependency.GetInstance(dependency.OasGeneratorDependency).(oas.OasGenerator)
|
||||||
oasGenerator.Start(nil)
|
oasGenerator.Start()
|
||||||
}
|
}
|
||||||
if config.Config.ServiceMap {
|
if config.Config.ServiceMap {
|
||||||
serviceMapGenerator := dependency.GetInstance(dependency.ServiceMapGeneratorDependency).(servicemap.ServiceMap)
|
serviceMapGenerator := dependency.GetInstance(dependency.ServiceMapGeneratorDependency).(servicemap.ServiceMap)
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/up9inc/mizu/agent/pkg/holder"
|
"github.com/up9inc/mizu/agent/pkg/holder"
|
||||||
"github.com/up9inc/mizu/agent/pkg/providers"
|
"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/servicemap"
|
||||||
|
|
||||||
"github.com/up9inc/mizu/agent/pkg/resolver"
|
"github.com/up9inc/mizu/agent/pkg/resolver"
|
||||||
@@ -152,6 +153,9 @@ func startReadingChannel(outputItems <-chan *tapApi.OutputChannelItem, extension
|
|||||||
|
|
||||||
serviceMapGenerator := dependency.GetInstance(dependency.ServiceMapGeneratorDependency).(servicemap.ServiceMapSink)
|
serviceMapGenerator := dependency.GetInstance(dependency.ServiceMapGeneratorDependency).(servicemap.ServiceMapSink)
|
||||||
serviceMapGenerator.NewTCPEntry(mizuEntry.Source, mizuEntry.Destination, &item.Protocol)
|
serviceMapGenerator.NewTCPEntry(mizuEntry.Source, mizuEntry.Destination, &item.Protocol)
|
||||||
|
|
||||||
|
oasGenerator := dependency.GetInstance(dependency.OasGeneratorDependency).(oas.OasGeneratorSink)
|
||||||
|
oasGenerator.HandleEntry(mizuEntry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,12 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
basenine "github.com/up9inc/basenine/client/go"
|
|
||||||
"net"
|
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/up9inc/mizu/agent/pkg/dependency"
|
|
||||||
"github.com/up9inc/mizu/agent/pkg/oas"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/dependency"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/oas"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetOASServers(t *testing.T) {
|
func TestGetOASServers(t *testing.T) {
|
||||||
@@ -37,33 +32,14 @@ func TestGetOASSpec(t *testing.T) {
|
|||||||
t.Logf("Written body: %s", recorder.Body.String())
|
t.Logf("Written body: %s", recorder.Body.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
type fakeConn struct {
|
|
||||||
sendBuffer *bytes.Buffer
|
|
||||||
receiveBuffer *bytes.Buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f fakeConn) Read(p []byte) (int, error) { return f.sendBuffer.Read(p) }
|
|
||||||
func (f fakeConn) Write(p []byte) (int, error) { return f.receiveBuffer.Write(p) }
|
|
||||||
func (fakeConn) Close() error { return nil }
|
|
||||||
func (fakeConn) LocalAddr() net.Addr { return nil }
|
|
||||||
func (fakeConn) RemoteAddr() net.Addr { return nil }
|
|
||||||
func (fakeConn) SetDeadline(t time.Time) error { return nil }
|
|
||||||
func (fakeConn) SetReadDeadline(t time.Time) error { return nil }
|
|
||||||
func (fakeConn) SetWriteDeadline(t time.Time) error { return nil }
|
|
||||||
|
|
||||||
func getRecorderAndContext() (*httptest.ResponseRecorder, *gin.Context) {
|
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{} {
|
dependency.RegisterGenerator(dependency.OasGeneratorDependency, func() interface{} {
|
||||||
return oas.GetDefaultOasGeneratorInstance()
|
return oas.GetDefaultOasGeneratorInstance()
|
||||||
})
|
})
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
c, _ := gin.CreateTestContext(recorder)
|
c, _ := gin.CreateTestContext(recorder)
|
||||||
oas.GetDefaultOasGeneratorInstance().Start(dummyConn)
|
oas.GetDefaultOasGeneratorInstance().Start()
|
||||||
oas.GetDefaultOasGeneratorInstance().GetServiceSpecs().Store("some", oas.NewGen("some"))
|
oas.GetDefaultOasGeneratorInstance().GetServiceSpecs().Store("some", oas.NewGen("some"))
|
||||||
return recorder, c
|
return recorder, c
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
package oas
|
package oas
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/url"
|
"net/url"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
basenine "github.com/up9inc/basenine/client/go"
|
|
||||||
"github.com/up9inc/mizu/agent/pkg/har"
|
"github.com/up9inc/mizu/agent/pkg/har"
|
||||||
"github.com/up9inc/mizu/shared"
|
|
||||||
"github.com/up9inc/mizu/tap/api"
|
"github.com/up9inc/mizu/tap/api"
|
||||||
|
|
||||||
"github.com/up9inc/mizu/logger"
|
"github.com/up9inc/mizu/logger"
|
||||||
@@ -19,22 +16,20 @@ var (
|
|||||||
instance *defaultOasGenerator
|
instance *defaultOasGenerator
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type OasGeneratorSink interface {
|
||||||
|
HandleEntry(mizuEntry *api.Entry)
|
||||||
|
}
|
||||||
|
|
||||||
type OasGenerator interface {
|
type OasGenerator interface {
|
||||||
Start(conn *basenine.Connection)
|
Start()
|
||||||
Stop()
|
Stop()
|
||||||
IsStarted() bool
|
IsStarted() bool
|
||||||
GetServiceSpecs() *sync.Map
|
GetServiceSpecs() *sync.Map
|
||||||
SetEntriesQuery(query string) bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type defaultOasGenerator struct {
|
type defaultOasGenerator struct {
|
||||||
started bool
|
started bool
|
||||||
ctx context.Context
|
|
||||||
cancel context.CancelFunc
|
|
||||||
serviceSpecs *sync.Map
|
serviceSpecs *sync.Map
|
||||||
dbConn *basenine.Connection
|
|
||||||
dbMutex sync.Mutex
|
|
||||||
entriesQuery string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetDefaultOasGeneratorInstance() *defaultOasGenerator {
|
func GetDefaultOasGeneratorInstance() *defaultOasGenerator {
|
||||||
@@ -45,102 +40,29 @@ func GetDefaultOasGeneratorInstance() *defaultOasGenerator {
|
|||||||
return instance
|
return instance
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *defaultOasGenerator) Start(conn *basenine.Connection) {
|
func (g *defaultOasGenerator) Start() {
|
||||||
if g.started {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if g.dbConn == nil {
|
|
||||||
if conn == nil {
|
|
||||||
logger.Log.Infof("Creating new DB connection for OAS generator to address %s:%s", shared.BasenineHost, shared.BaseninePort)
|
|
||||||
newConn, err := basenine.NewConnection(shared.BasenineHost, shared.BaseninePort)
|
|
||||||
if err != nil {
|
|
||||||
logger.Log.Error("Error connecting to DB for OAS generator, err: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
conn = newConn
|
|
||||||
}
|
|
||||||
|
|
||||||
g.dbConn = conn
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
g.cancel = cancel
|
|
||||||
g.ctx = ctx
|
|
||||||
g.serviceSpecs = &sync.Map{}
|
|
||||||
|
|
||||||
g.started = true
|
g.started = true
|
||||||
|
|
||||||
go g.runGenerator()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *defaultOasGenerator) Stop() {
|
func (g *defaultOasGenerator) Stop() {
|
||||||
if !g.started {
|
if !g.started {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
g.started = false
|
g.started = false
|
||||||
|
|
||||||
g.cancel()
|
|
||||||
g.reset()
|
g.reset()
|
||||||
|
|
||||||
g.dbMutex.Lock()
|
|
||||||
defer g.dbMutex.Unlock()
|
|
||||||
if g.dbConn != nil {
|
|
||||||
g.dbConn.Close()
|
|
||||||
g.dbConn = nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *defaultOasGenerator) IsStarted() bool {
|
func (g *defaultOasGenerator) IsStarted() bool {
|
||||||
return g.started
|
return g.started
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *defaultOasGenerator) runGenerator() {
|
func (g *defaultOasGenerator) HandleEntry(mizuEntry *api.Entry) {
|
||||||
// Make []byte channels to receive the data and the meta
|
if !g.started {
|
||||||
dataChan := make(chan []byte)
|
return
|
||||||
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("latest", g.entriesQuery, dataChan, metaChan); err != nil {
|
|
||||||
logger.Log.Errorf("Query mode call failed: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-g.ctx.Done():
|
|
||||||
logger.Log.Infof("OAS Generator was canceled")
|
|
||||||
close(dataChan)
|
|
||||||
close(metaChan)
|
|
||||||
return
|
|
||||||
|
|
||||||
case metaBytes, ok := <-metaChan:
|
|
||||||
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" {
|
if mizuEntry.Protocol.Name == "http" {
|
||||||
dest := mizuEntry.Destination.Name
|
dest := mizuEntry.Destination.Name
|
||||||
if dest == "" {
|
if dest == "" {
|
||||||
@@ -210,18 +132,9 @@ func (g *defaultOasGenerator) GetServiceSpecs() *sync.Map {
|
|||||||
return g.serviceSpecs
|
return g.serviceSpecs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *defaultOasGenerator) SetEntriesQuery(query string) bool {
|
|
||||||
changed := g.entriesQuery != query
|
|
||||||
g.entriesQuery = query
|
|
||||||
return changed
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDefaultOasGenerator() *defaultOasGenerator {
|
func NewDefaultOasGenerator() *defaultOasGenerator {
|
||||||
return &defaultOasGenerator{
|
return &defaultOasGenerator{
|
||||||
started: false,
|
started: false,
|
||||||
ctx: nil,
|
serviceSpecs: &sync.Map{},
|
||||||
cancel: nil,
|
|
||||||
serviceSpecs: nil,
|
|
||||||
dbConn: nil,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestOASGen(t *testing.T) {
|
func TestOASGen(t *testing.T) {
|
||||||
gen := new(defaultOasGenerator)
|
gen := GetDefaultOasGeneratorInstance()
|
||||||
|
|
||||||
e := new(har.Entry)
|
e := new(har.Entry)
|
||||||
err := json.Unmarshal([]byte(`{"startedDateTime": "20000101","request": {"url": "https://host/path", "method": "GET"}, "response": {"status": 200}}`), e)
|
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,
|
Entry: *e,
|
||||||
}
|
}
|
||||||
|
|
||||||
dummyConn := GetFakeDBConn(`{"startedDateTime": "20000101","request": {"url": "https://host/path", "method": "GET"}, "response": {"status": 200}}`)
|
gen.Start()
|
||||||
gen.Start(dummyConn)
|
|
||||||
gen.handleHARWithSource(ews)
|
gen.handleHARWithSource(ews)
|
||||||
g, ok := gen.serviceSpecs.Load("some")
|
g, ok := gen.serviceSpecs.Load("some")
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
package oas
|
package oas
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -13,22 +11,11 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/chanced/openapi"
|
"github.com/chanced/openapi"
|
||||||
|
"github.com/up9inc/mizu/agent/pkg/har"
|
||||||
"github.com/up9inc/mizu/logger"
|
"github.com/up9inc/mizu/logger"
|
||||||
"github.com/wI2L/jsondiff"
|
"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
|
// if started via env, write file into subdir
|
||||||
func outputSpec(label string, spec *openapi.OpenAPI, t *testing.T) string {
|
func outputSpec(label string, spec *openapi.OpenAPI, t *testing.T) string {
|
||||||
content, err := json.MarshalIndent(spec, "", " ")
|
content, err := json.MarshalIndent(spec, "", " ")
|
||||||
@@ -278,17 +265,3 @@ func TestLoadValid3_1(t *testing.T) {
|
|||||||
t.FailNow()
|
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 }
|
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ func init() {
|
|||||||
tapCmd.Flags().Bool(configStructs.AnalysisTapName, defaultTapConfig.Analysis, "Uploads traffic to UP9 for further analysis (Beta)")
|
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().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().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.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().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")
|
tapCmd.Flags().Bool(configStructs.DryRunTapName, defaultTapConfig.DryRun, "Preview of all pods matching the regex, without tapping them")
|
||||||
|
|||||||
@@ -291,7 +291,7 @@ func getMizuApiFilteringOptions() (*api.TrafficFilteringOptions, error) {
|
|||||||
return &api.TrafficFilteringOptions{
|
return &api.TrafficFilteringOptions{
|
||||||
PlainTextMaskingRegexes: compiledRegexSlice,
|
PlainTextMaskingRegexes: compiledRegexSlice,
|
||||||
IgnoredUserAgents: config.Config.Tap.IgnoredUserAgents,
|
IgnoredUserAgents: config.Config.Tap.IgnoredUserAgents,
|
||||||
DisableRedaction: config.Config.Tap.DisableRedaction,
|
EnableRedaction: config.Config.Tap.EnableRedaction,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const (
|
|||||||
AnalysisTapName = "analysis"
|
AnalysisTapName = "analysis"
|
||||||
AllNamespacesTapName = "all-namespaces"
|
AllNamespacesTapName = "all-namespaces"
|
||||||
PlainTextFilterRegexesTapName = "regex-masking"
|
PlainTextFilterRegexesTapName = "regex-masking"
|
||||||
DisableRedactionTapName = "no-redact"
|
EnableRedactionTapName = "redact"
|
||||||
HumanMaxEntriesDBSizeTapName = "max-entries-db-size"
|
HumanMaxEntriesDBSizeTapName = "max-entries-db-size"
|
||||||
InsertionFilterName = "insertion-filter"
|
InsertionFilterName = "insertion-filter"
|
||||||
DryRunTapName = "dry-run"
|
DryRunTapName = "dry-run"
|
||||||
@@ -43,7 +43,7 @@ type TapConfig struct {
|
|||||||
AllNamespaces bool `yaml:"all-namespaces" default:"false"`
|
AllNamespaces bool `yaml:"all-namespaces" default:"false"`
|
||||||
PlainTextFilterRegexes []string `yaml:"regex-masking"`
|
PlainTextFilterRegexes []string `yaml:"regex-masking"`
|
||||||
IgnoredUserAgents []string `yaml:"ignored-user-agents"`
|
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"`
|
HumanMaxEntriesDBSize string `yaml:"max-entries-db-size" default:"200MB"`
|
||||||
InsertionFilter string `yaml:"insertion-filter" default:""`
|
InsertionFilter string `yaml:"insertion-filter" default:""`
|
||||||
DryRun bool `yaml:"dry-run" default:"false"`
|
DryRun bool `yaml:"dry-run" default:"false"`
|
||||||
|
|||||||
@@ -128,8 +128,8 @@ if __name__ == '__main__':
|
|||||||
matched_samples_all_files.append(matched_samples)
|
matched_samples_all_files.append(matched_samples)
|
||||||
live_samples_all_files.append(live_samples)
|
live_samples_all_files.append(live_samples)
|
||||||
processed_samples_all_files.append(processed_samples)
|
processed_samples_all_files.append(processed_samples)
|
||||||
heap_samples_all_files.append(processed_samples)
|
heap_samples_all_files.append(heap_samples)
|
||||||
goroutines_samples_all_files.append(processed_samples)
|
goroutines_samples_all_files.append(goroutines_samples)
|
||||||
|
|
||||||
cpu_samples_df = pd.concat(cpu_samples_all_files, axis=1)
|
cpu_samples_df = pd.concat(cpu_samples_all_files, axis=1)
|
||||||
rss_samples_df = pd.concat(rss_samples_all_files, axis=1)
|
rss_samples_df = pd.concat(rss_samples_all_files, axis=1)
|
||||||
@@ -137,7 +137,6 @@ if __name__ == '__main__':
|
|||||||
matched_samples_df = pd.concat(matched_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)
|
live_samples_df = pd.concat(live_samples_all_files, axis=1)
|
||||||
processed_samples_df = pd.concat(processed_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)
|
heap_samples_df = pd.concat(heap_samples_all_files, axis=1)
|
||||||
goroutines_samples_df = pd.concat(goroutines_samples_all_files, axis=1)
|
goroutines_samples_df = pd.concat(goroutines_samples_all_files, axis=1)
|
||||||
|
|
||||||
@@ -172,7 +171,7 @@ if __name__ == '__main__':
|
|||||||
heap_plot.legend().remove()
|
heap_plot.legend().remove()
|
||||||
|
|
||||||
goroutines_plot = plt.subplot(8, 2, 8)
|
goroutines_plot = plt.subplot(8, 2, 8)
|
||||||
plot(goroutines_plot, (goroutines_samples_df / 1024 / 1024), 'goroutines', '', 'goroutines', group_pattern)
|
plot(goroutines_plot, goroutines_samples_df, 'goroutines', '', 'goroutines', group_pattern)
|
||||||
goroutines_plot.legend().remove()
|
goroutines_plot.legend().remove()
|
||||||
|
|
||||||
fig = plt.gcf()
|
fig = plt.gcf()
|
||||||
|
|||||||
@@ -22,7 +22,14 @@ function run_single_bench() {
|
|||||||
for ((i=0;i<"$MIZU_BENCHMARK_RUN_COUNT";i++)); do
|
for ((i=0;i<"$MIZU_BENCHMARK_RUN_COUNT";i++)); do
|
||||||
log " $i: Running tapper"
|
log " $i: Running tapper"
|
||||||
rm -f tapper.log
|
rm -f tapper.log
|
||||||
nohup ./agent/build/mizuagent --tap --api-server-address ws://localhost:8899/wsTapper -i lo -stats 10 > tapper.log 2>&1 &
|
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)"
|
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
|
hey -z $MIZU_BENCHMARK_CLIENT_PERIOD -c $MIZU_BENCHMARK_CLIENTS_COUNT -q $MIZU_BENCHMARK_QPS $MIZU_BENCHMARK_URL > /dev/null || return 1
|
||||||
@@ -50,6 +57,7 @@ log "Writing output to $MIZU_BENCHMARK_OUTPUT_DIR"
|
|||||||
cd $MIZU_HOME || exit 1
|
cd $MIZU_HOME || exit 1
|
||||||
|
|
||||||
export HOST_MODE=0
|
export HOST_MODE=0
|
||||||
|
export SENSITIVE_DATA_FILTERING_OPTIONS='{"EnableRedaction": false}'
|
||||||
export MIZU_DEBUG_DISABLE_PCAP=false
|
export MIZU_DEBUG_DISABLE_PCAP=false
|
||||||
export MIZU_DEBUG_DISABLE_TCP_REASSEMBLY=false
|
export MIZU_DEBUG_DISABLE_TCP_REASSEMBLY=false
|
||||||
export MIZU_DEBUG_DISABLE_TCP_STREAM=false
|
export MIZU_DEBUG_DISABLE_TCP_STREAM=false
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ package api
|
|||||||
type TrafficFilteringOptions struct {
|
type TrafficFilteringOptions struct {
|
||||||
IgnoredUserAgents []string
|
IgnoredUserAgents []string
|
||||||
PlainTextMaskingRegexes []*SerializableRegexp
|
PlainTextMaskingRegexes []*SerializableRegexp
|
||||||
DisableRedaction bool
|
EnableRedaction bool
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,4 +13,4 @@ test-pull-bin:
|
|||||||
|
|
||||||
test-pull-expect:
|
test-pull-expect:
|
||||||
@mkdir -p 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
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ func filterAndEmit(item *api.OutputChannelItem, emitter api.Emitter, options *ap
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !options.DisableRedaction {
|
if options.EnableRedaction {
|
||||||
FilterSensitiveData(item, options)
|
FilterSensitiveData(item, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import styles from "./EntrySections.module.sass";
|
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 { SyntaxHighlighter } from "../../UI/SyntaxHighlighter/index";
|
||||||
import CollapsibleContainer from "../../UI/CollapsibleContainer";
|
import CollapsibleContainer from "../../UI/CollapsibleContainer";
|
||||||
import FancyTextDisplay from "../../UI/FancyTextDisplay";
|
import FancyTextDisplay from "../../UI/FancyTextDisplay";
|
||||||
@@ -8,6 +8,7 @@ import Checkbox from "../../UI/Checkbox";
|
|||||||
import ProtobufDecoder from "protobuf-decoder";
|
import ProtobufDecoder from "protobuf-decoder";
|
||||||
import { default as jsonBeautify } from "json-beautify";
|
import { default as jsonBeautify } from "json-beautify";
|
||||||
import { default as xmlBeautify } from "xml-formatter";
|
import { default as xmlBeautify } from "xml-formatter";
|
||||||
|
import { Utils } from "../../../helpers/Utils"
|
||||||
|
|
||||||
interface EntryViewLineProps {
|
interface EntryViewLineProps {
|
||||||
label: string;
|
label: string;
|
||||||
@@ -101,6 +102,12 @@ export const EntrySectionContainer: React.FC<EntrySectionContainerProps> = ({ ti
|
|||||||
</CollapsibleContainer>
|
</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 {
|
interface EntryBodySectionProps {
|
||||||
title: string,
|
title: string,
|
||||||
content: any,
|
content: any,
|
||||||
@@ -118,21 +125,21 @@ export const EntryBodySection: React.FC<EntryBodySectionProps> = ({
|
|||||||
contentType,
|
contentType,
|
||||||
selector,
|
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 [isPretty, setIsPretty] = useState(true);
|
||||||
const [showLineNumbers, setShowLineNumbers] = useState(true);
|
const [showLineNumbers, setShowLineNumbers] = useState(false);
|
||||||
const [decodeBase64, setDecodeBase64] = useState(true);
|
const [decodeBase64, setDecodeBase64] = useState(true);
|
||||||
|
|
||||||
const isBase64Encoding = encoding === 'base64';
|
const isBase64Encoding = encoding === 'base64';
|
||||||
const supportsPrettying = supportedFormats.some(format => contentType?.indexOf(format) > -1);
|
const supportsPrettying = supportedFormats.some(format => contentType?.indexOf(format) > -1);
|
||||||
const [isDecodeGrpc, setIsDecodeGrpc] = useState(true);
|
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;
|
if (!decodeBase64) return body;
|
||||||
|
|
||||||
const chunk = body.slice(0, MAXIMUM_BYTES_TO_FORMAT);
|
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);
|
return jsonBeautify(protobufDecoded, null, 2, 80);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (String(error).includes("More than one message in")){
|
if (String(error).includes("More than one message in")) {
|
||||||
if(isDecodeGrpc)
|
if (isDecodeGrpc)
|
||||||
setIsDecodeGrpc(false);
|
setIsDecodeGrpc(false);
|
||||||
|
} else if (String(error).includes("Failed to parse")) {
|
||||||
|
console.warn(error);
|
||||||
} else {
|
} else {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return bodyBuf;
|
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>
|
return <React.Fragment>
|
||||||
{content && content?.length > 0 && <EntrySectionContainer
|
{content && content?.length > 0 && <EntrySectionContainer
|
||||||
@@ -181,18 +197,19 @@ export const EntryBodySection: React.FC<EntryBodySectionProps> = ({
|
|||||||
{supportsPrettying && <span style={{ marginLeft: '.2rem' }}>Pretty</span>}
|
{supportsPrettying && <span style={{ marginLeft: '.2rem' }}>Pretty</span>}
|
||||||
|
|
||||||
<div style={{ paddingTop: 3, paddingLeft: supportsPrettying ? 20 : 0 }}>
|
<div style={{ paddingTop: 3, paddingLeft: supportsPrettying ? 20 : 0 }}>
|
||||||
<Checkbox checked={showLineNumbers} onToggle={() => { setShowLineNumbers(!showLineNumbers) }} />
|
<Checkbox checked={showLineNumbers} onToggle={() => { setShowLineNumbers(!showLineNumbers) }} disabled={!isLineNumbersGreaterThenOne || !decodeBase64} />
|
||||||
</div>
|
</div>
|
||||||
<span style={{ marginLeft: '.2rem' }}>Line numbers</span>
|
<span style={{ marginLeft: '.2rem' }}>Line numbers</span>
|
||||||
{isBase64Encoding && <div style={{ paddingTop: 3, paddingLeft: 20 }}>
|
|
||||||
|
{isBase64Encoding && <div style={{ paddingTop: 3, paddingLeft: (isLineNumbersGreaterThenOne || supportsPrettying) ? 20 : 0 }}>
|
||||||
<Checkbox checked={decodeBase64} onToggle={() => { setDecodeBase64(!decodeBase64) }} />
|
<Checkbox checked={decodeBase64} onToggle={() => { setDecodeBase64(!decodeBase64) }} />
|
||||||
</div>}
|
</div>}
|
||||||
{isBase64Encoding && <span style={{ marginLeft: '.2rem' }}>Decode Base64</span>}
|
{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>}
|
{!isDecodeGrpc && <span style={{ fontSize: '12px', color: '#DB2156', marginLeft: '.8rem' }}>More than one message in protobuf payload is not supported</span>}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
code={formatTextBody(content)}
|
code={formattedText}
|
||||||
showLineNumbers={showLineNumbers}
|
showLineNumbers={showLineNumbers}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -296,7 +296,7 @@ export const EntryItem: React.FC<EntryProps> = ({entry, style, headingMode, name
|
|||||||
query={`dst.ip == "${entry.dst.ip}"`}
|
query={`dst.ip == "${entry.dst.ip}"`}
|
||||||
displayIconOnMouseOver={true}
|
displayIconOnMouseOver={true}
|
||||||
flipped={false}
|
flipped={false}
|
||||||
iconStyle={{marginTop: "28px"}}
|
iconStyle={{marginTop: "30px", marginLeft: "-2px",right: "35px", position: "relative"}}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className={`${styles.tcpInfo} ${styles.ip}`}
|
className={`${styles.tcpInfo} ${styles.ip}`}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ const Queryable: React.FC<Props> = ({query, style, iconStyle, className, useTool
|
|||||||
{flipped && addButton}
|
{flipped && addButton}
|
||||||
{children}
|
{children}
|
||||||
{!flipped && addButton}
|
{!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>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import Lowlight from 'react-lowlight'
|
import Lowlight from 'react-lowlight'
|
||||||
import 'highlight.js/styles/atom-one-light.css'
|
import 'highlight.js/styles/atom-one-light.css'
|
||||||
import styles from './index.module.sass';
|
import styles from './index.module.sass';
|
||||||
@@ -30,18 +30,23 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const SyntaxHighlighter: React.FC<Props> = ({
|
export const SyntaxHighlighter: React.FC<Props> = ({
|
||||||
code,
|
code,
|
||||||
showLineNumbers = false,
|
showLineNumbers = false,
|
||||||
language = null
|
language = null,
|
||||||
}) => {
|
}) => {
|
||||||
const markers = showLineNumbers ? code.split("\n").map((item, i) => {
|
const [markers, setMarkers] = useState([])
|
||||||
return {
|
|
||||||
line: i + 1,
|
|
||||||
className: styles.hljsMarkerLine
|
|
||||||
}
|
|
||||||
}) : [];
|
|
||||||
|
|
||||||
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;
|
export default SyntaxHighlighter;
|
||||||
|
|||||||
@@ -50,10 +50,9 @@
|
|||||||
td
|
td
|
||||||
color: $light-gray
|
color: $light-gray
|
||||||
padding: 10px
|
padding: 10px
|
||||||
font-size: 11px
|
font-size: 15px
|
||||||
font-weight: 600
|
font-weight: 600
|
||||||
padding-top: 5px
|
padding: 10px
|
||||||
padding-bottom: 5px
|
|
||||||
|
|
||||||
.nowrap
|
.nowrap
|
||||||
white-space: nowrap
|
white-space: nowrap
|
||||||
|
|||||||
@@ -3,4 +3,5 @@ const IP_ADDRESS_REGEX = /([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3})(:([0-9]{
|
|||||||
|
|
||||||
export class Utils {
|
export class Utils {
|
||||||
static isIpAddress = (address: string): boolean => IP_ADDRESS_REGEX.test(address)
|
static isIpAddress = (address: string): boolean => IP_ADDRESS_REGEX.test(address)
|
||||||
}
|
static lineNumbersInString = (code:string): number => code.split("\n").length;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user