Compare commits

...

8 Commits

Author SHA1 Message Date
Adam Kol
7893b4596d closing ws on modal open (#904)
Co-authored-by: gadotroee <55343099+gadotroee@users.noreply.github.com>
2022-03-21 16:44:40 +02:00
M. Mert Yıldıran
774f07fccd Add /db/flush and /db/reset API endpoints (#896)
* Add `/db/flush` and `/db/reset` API endpoints

* Handle the unmarshalling errors better in the WebSocket

* Handle Basenine connection error better in the WebSocket

* Upgrade to Basenine `v0.6.5`

* Fix the duplicated `StartTime` state

Co-authored-by: gadotroee <55343099+gadotroee@users.noreply.github.com>
2022-03-21 15:54:36 +02:00
RoyUP9
482e5c8b69 Added check pull image flag (#899)
Co-authored-by: gadotroee <55343099+gadotroee@users.noreply.github.com>
2022-03-21 15:24:03 +02:00
RamiBerm
21902b5f86 Fix tapping status falling out of sync (#898)
Co-authored-by: gadotroee <55343099+gadotroee@users.noreply.github.com>
2022-03-21 14:54:25 +02:00
gadotroee
a4d0e250c9 Fixing acceptance tests (#900) 2022-03-21 14:21:08 +02:00
M. Mert Yıldıran
5455220a3a Add an indicator for the eBPF sourced entries (#886)
* Define `Capture` type and expect it as an argument in `Dissect` method

* Add an indicator for the  eBPF sourced entries

* Fix the Go lint error

* Fix the logic in the UI

* Update the expected JSONs

* Add TODOs

* Add `UndefinedCapture` constant

* Define `CaptureTypes` enum
2022-03-17 09:32:09 +03:00
RamiBerm
237002ef29 Dependency injection for oas servicemap (#895)
* Update main.go, main.go, and 3 more files...

* WIP

* Update main.go, oas_controller.go, and 3 more files...

* Update main.go, oas_generator.go, and servicemap.go

* Update loader.go and resolver.go

* Update oas_generator.go

* Update oas_generator.go, specgen_test.go, and 3 more files...

* Update service_map_controller_test.go

* Update oas_controller_test.go
2022-03-16 17:21:50 +02:00
leon-up9
9430e291b4 UI Splitting to common components (#883)
* initial commit

* removing files

* after pr

* move StatusBar to common

* last changes from dev

* update common-ui

* webSocket was added to TrafficViewerApi

* useWS

* actionButtons added to TrafficV

* comment clean

* api clean up

* api clean up

* statusbar position changed

* Checkbox changed

* AnalyzeButton exported to common

* CustomModal added from Ent

* oas modal exported to common

* removed redundant

* oasmodal usage

* es6 function

* api changed

* removed react-scripts

Co-authored-by: Leon <>
2022-03-15 18:45:43 +02:00
216 changed files with 112556 additions and 7292 deletions

5
.gitignore vendored
View File

@@ -48,3 +48,8 @@ cypress.env.json
# Ignore test data in extensions # Ignore test data in extensions
tap/extensions/*/bin tap/extensions/*/bin
tap/extensions/*/expect tap/extensions/*/expect
# UI folders to ignore
**/node_modules/**
**/dist/**
*.editorconfig

View File

@@ -77,8 +77,8 @@ RUN go build -ldflags="-extldflags=-static -s -w \
-X 'github.com/up9inc/mizu/agent/pkg/version.Ver=${VER}'" -o mizuagent . -X 'github.com/up9inc/mizu/agent/pkg/version.Ver=${VER}'" -o mizuagent .
# Download Basenine executable, verify the sha1sum # Download Basenine executable, verify the sha1sum
ADD https://github.com/up9inc/basenine/releases/download/v0.6.3/basenine_linux_${GOARCH} ./basenine_linux_${GOARCH} ADD https://github.com/up9inc/basenine/releases/download/v0.6.5/basenine_linux_${GOARCH} ./basenine_linux_${GOARCH}
ADD https://github.com/up9inc/basenine/releases/download/v0.6.3/basenine_linux_${GOARCH}.sha256 ./basenine_linux_${GOARCH}.sha256 ADD https://github.com/up9inc/basenine/releases/download/v0.6.5/basenine_linux_${GOARCH}.sha256 ./basenine_linux_${GOARCH}.sha256
RUN shasum -a 256 -c basenine_linux_${GOARCH}.sha256 RUN shasum -a 256 -c basenine_linux_${GOARCH}.sha256
RUN chmod +x ./basenine_linux_${GOARCH} RUN chmod +x ./basenine_linux_${GOARCH}
RUN mv ./basenine_linux_${GOARCH} ./basenine RUN mv ./basenine_linux_${GOARCH} ./basenine

View File

@@ -1,8 +1,7 @@
const columns = {podName : 1, namespace : 2, tapping : 3}; const columns = {podName : 1, namespace : 2, tapping : 3};
const greenStatusImageSrc = '/static/media/success.662997eb.svg';
function getDomPathInStatusBar(line, column) { function getDomPathInStatusBar(line, column) {
return `.expandedStatusBar > :nth-child(2) > > :nth-child(2) > :nth-child(${line}) > :nth-child(${column})`; return `[data-cy="expandedStatusBar"] > :nth-child(2) > > :nth-child(2) > :nth-child(${line}) > :nth-child(${column})`;
} }
export function checkLine(line, expectedValues) { export function checkLine(line, expectedValues) {
@@ -12,14 +11,14 @@ export function checkLine(line, expectedValues) {
cy.get(getDomPathInStatusBar(line, columns.namespace)).invoke('text').then(namespaceValue => { cy.get(getDomPathInStatusBar(line, columns.namespace)).invoke('text').then(namespaceValue => {
expect(namespaceValue).to.equal(expectedValues.namespace); expect(namespaceValue).to.equal(expectedValues.namespace);
cy.get(getDomPathInStatusBar(line, columns.tapping)).children().should('have.attr', 'src', greenStatusImageSrc); cy.get(getDomPathInStatusBar(line, columns.tapping)).children().should('have.attr', 'src').and("match", /success.*\.svg/);
}); });
}); });
} }
export function findLineAndCheck(expectedValues) { export function findLineAndCheck(expectedValues) {
cy.get('.expandedStatusBar > :nth-child(2) > > :nth-child(2) > > :nth-child(1)').then(pods => { cy.get('[data-cy="expandedStatusBar"] > :nth-child(2) > > :nth-child(2) > > :nth-child(1)').then(pods => {
cy.get('.expandedStatusBar > :nth-child(2) > > :nth-child(2) > > :nth-child(2)').then(namespaces => { cy.get('[data-cy="expandedStatusBar"] > :nth-child(2) > > :nth-child(2) > > :nth-child(2)').then(namespaces => {
// organizing namespaces array // organizing namespaces array
const podObjectsArray = Object.values(pods ?? {}); const podObjectsArray = Object.values(pods ?? {});
const namespacesObjectsArray = Object.values(namespaces ?? {}); const namespacesObjectsArray = Object.values(namespaces ?? {});

View File

@@ -45,7 +45,7 @@ export function leftTextCheck(entryNum, path, expectedText) {
export function leftOnHoverCheck(entryNum, path, filterName) { export function leftOnHoverCheck(entryNum, path, filterName) {
cy.get(`#list #entry-${entryNum} ${path}`).trigger('mouseover'); cy.get(`#list #entry-${entryNum} ${path}`).trigger('mouseover');
cy.get(`#list #entry-${entryNum} .Queryable-Tooltip`).invoke('text').should('match', new RegExp(filterName)); cy.get(`#list #entry-${entryNum} [data-cy='QueryableTooltip']`).invoke('text').should('match', new RegExp(filterName));
} }
export function rightTextCheck(path, expectedText) { export function rightTextCheck(path, expectedText) {
@@ -54,7 +54,7 @@ export function rightTextCheck(path, expectedText) {
export function rightOnHoverCheck(path, expectedText) { export function rightOnHoverCheck(path, expectedText) {
cy.get(`#rightSideContainer ${path}`).trigger('mouseover'); cy.get(`#rightSideContainer ${path}`).trigger('mouseover');
cy.get(`#rightSideContainer .Queryable-Tooltip`).invoke('text').should('match', new RegExp(expectedText)); cy.get(`#rightSideContainer [data-cy='QueryableTooltip']`).invoke('text').should('match', new RegExp(expectedText));
} }
export function checkThatAllEntriesShown() { export function checkThatAllEntriesShown() {

View File

@@ -8,6 +8,6 @@ it('check', function () {
cy.visit(`http://localhost:${port}`); cy.visit(`http://localhost:${port}`);
cy.wait('@statusTap').its('response.statusCode').should('match', /^2\d{2}/); cy.wait('@statusTap').its('response.statusCode').should('match', /^2\d{2}/);
cy.get('.podsCount').trigger('mouseover'); cy.get(`[data-cy="expandedStatusBar"]`).trigger('mouseover',{force: true});
findLineAndCheck(getExpectedDetailsDict(podName, namespace)); findLineAndCheck(getExpectedDetailsDict(podName, namespace));
}); });

View File

@@ -2,7 +2,7 @@ import {findLineAndCheck, getExpectedDetailsDict} from '../testHelpers/StatusBar
it('opening', function () { it('opening', function () {
cy.visit(Cypress.env('testUrl')); cy.visit(Cypress.env('testUrl'));
cy.get('.podsCount').trigger('mouseover'); cy.get(`[data-cy="podsCountText"]`).trigger('mouseover');
}); });
[1, 2, 3].map(doItFunc); [1, 2, 3].map(doItFunc);

View File

@@ -3,9 +3,9 @@ import {getExpectedDetailsDict, checkLine} from '../testHelpers/StatusBarHelper'
it('opening', function () { it('opening', function () {
cy.visit(Cypress.env('testUrl')); cy.visit(Cypress.env('testUrl'));
cy.get('.podsCount').trigger('mouseover'); cy.get(`[data-cy="podsCountText"]`).trigger('mouseover');
cy.get('.expandedStatusBar > :nth-child(2) > > :nth-child(2) >').should('have.length', 1); // one line cy.get('[data-cy="expandedStatusBar"] > :nth-child(2) > > :nth-child(2) >').should('have.length', 1); // one line
checkLine(1, getExpectedDetailsDict(Cypress.env('name'), Cypress.env('namespace'))); checkLine(1, getExpectedDetailsDict(Cypress.env('name'), Cypress.env('namespace')));
}); });

View File

@@ -26,7 +26,7 @@ it('opening mizu', function () {
verifyMinimumEntries(); verifyMinimumEntries();
it('top bar check', function () { it('top bar check', function () {
cy.get('.podsCount').trigger('mouseover'); cy.get(`[data-cy="podsCountText"]`).trigger('mouseover');
podsArray.map(findLineAndCheck); podsArray.map(findLineAndCheck);
cy.reload(); cy.reload();
}); });
@@ -205,6 +205,7 @@ function checkFilter(filterDetails){
// checks the hover on the last entry (the only one in DOM at the beginning) // checks the hover on the last entry (the only one in DOM at the beginning)
leftOnHoverCheck(totalEntries - 1, leftSidePath, name); leftOnHoverCheck(totalEntries - 1, leftSidePath, name);
cy.get('.w-tc-editor-text').clear();
// applying the filter with alt+enter or with the button // 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-text').type(`${name}${applyByEnter ? '{alt+enter}' : ''}`);
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'));

View File

@@ -20,7 +20,7 @@ require (
github.com/orcaman/concurrent-map v1.0.0 github.com/orcaman/concurrent-map v1.0.0
github.com/patrickmn/go-cache v2.1.0+incompatible github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/stretchr/testify v1.7.0 github.com/stretchr/testify v1.7.0
github.com/up9inc/basenine/client/go v0.0.0-20220315070758-3a76cfc4378e github.com/up9inc/basenine/client/go v0.0.0-20220317230530-8472d80307f6
github.com/up9inc/mizu/shared v0.0.0 github.com/up9inc/mizu/shared v0.0.0
github.com/up9inc/mizu/tap v0.0.0 github.com/up9inc/mizu/tap v0.0.0
github.com/up9inc/mizu/tap/api v0.0.0 github.com/up9inc/mizu/tap/api v0.0.0

View File

@@ -655,8 +655,8 @@ github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ= github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
github.com/up9inc/basenine/client/go v0.0.0-20220315070758-3a76cfc4378e h1:/9dFXqvRDHcwPQdIGHP6iz6M0iAWBPOxYf6C+Ntq5w0= github.com/up9inc/basenine/client/go v0.0.0-20220317230530-8472d80307f6 h1:c0aVbLKYeFDAg246+NDgie2y484bsc20NaKLo8ODV3E=
github.com/up9inc/basenine/client/go v0.0.0-20220315070758-3a76cfc4378e/go.mod h1:SvJGPoa/6erhUQV7kvHBwM/0x5LyO6XaG2lUaCaKiUI= github.com/up9inc/basenine/client/go v0.0.0-20220317230530-8472d80307f6/go.mod h1:SvJGPoa/6erhUQV7kvHBwM/0x5LyO6XaG2lUaCaKiUI=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg= github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/wI2L/jsondiff v0.1.1 h1:r2TkoEet7E4JMO5+s1RCY2R0LrNPNHY6hbDeow2hRHw= github.com/wI2L/jsondiff v0.1.1 h1:r2TkoEet7E4JMO5+s1RCY2R0LrNPNHY6hbDeow2hRHw=

View File

@@ -16,6 +16,7 @@ import (
"github.com/gin-contrib/static" "github.com/gin-contrib/static"
"github.com/gin-gonic/gin" "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/elastic"
"github.com/up9inc/mizu/agent/pkg/middlewares" "github.com/up9inc/mizu/agent/pkg/middlewares"
"github.com/up9inc/mizu/agent/pkg/models" "github.com/up9inc/mizu/agent/pkg/models"
@@ -46,7 +47,6 @@ 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 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 harsReaderMode = flag.Bool("hars-read", false, "Run in hars-read mode")
var harsDir = flag.String("hars-dir", "", "Directory to read hars from") var harsDir = flag.String("hars-dir", "", "Directory to read hars from")
var startTime int64
const ( const (
socketConnectionRetries = 30 socketConnectionRetries = 30
@@ -55,6 +55,7 @@ const (
) )
func main() { func main() {
initializeDependencies()
logLevel := determineLogLevel() logLevel := determineLogLevel()
logger.InitLoggerStd(logLevel) logger.InitLoggerStd(logLevel)
flag.Parse() flag.Parse()
@@ -108,7 +109,7 @@ func hostApi(socketHarOutputChannel chan<- *tapApi.OutputChannelItem) *gin.Engin
app.Use(middlewares.CORSMiddleware()) // This has to be called after the static middleware, does not work if its called before app.Use(middlewares.CORSMiddleware()) // This has to be called after the static middleware, does not work if its called before
api.WebSocketRoutes(app, &eventHandlers, startTime) api.WebSocketRoutes(app, &eventHandlers)
if config.Config.OAS { if config.Config.OAS {
routes.OASRoutes(app) routes.OASRoutes(app)
@@ -122,6 +123,7 @@ func hostApi(socketHarOutputChannel chan<- *tapApi.OutputChannelItem) *gin.Engin
routes.EntriesRoutes(app) routes.EntriesRoutes(app)
routes.MetadataRoutes(app) routes.MetadataRoutes(app)
routes.StatusRoutes(app) routes.StatusRoutes(app)
routes.DbRoutes(app)
return app return app
} }
@@ -131,7 +133,6 @@ func runInApiServerMode(namespace string) *gin.Engine {
logger.Log.Fatalf("Error loading config file %v", err) logger.Log.Fatalf("Error loading config file %v", err)
} }
app.ConfigureBasenineServer(shared.BasenineHost, shared.BaseninePort, config.Config.MaxDBSizeBytes, config.Config.LogLevel, config.Config.InsertionFilter) app.ConfigureBasenineServer(shared.BasenineHost, shared.BaseninePort, config.Config.MaxDBSizeBytes, config.Config.LogLevel, config.Config.InsertionFilter)
startTime = time.Now().UnixNano() / int64(time.Millisecond)
api.StartResolving(namespace) api.StartResolving(namespace)
enableExpFeatureIfNeeded() enableExpFeatureIfNeeded()
@@ -203,10 +204,12 @@ func runInHarReaderMode() {
func enableExpFeatureIfNeeded() { func enableExpFeatureIfNeeded() {
if config.Config.OAS { if config.Config.OAS {
oas.GetOasGeneratorInstance().Start() oasGenerator := dependency.GetInstance(dependency.OasGeneratorDependency).(oas.OasGenerator)
oasGenerator.Start()
} }
if config.Config.ServiceMap { if config.Config.ServiceMap {
servicemap.GetInstance().Enable() serviceMapGenerator := dependency.GetInstance(dependency.ServiceMapGeneratorDependency).(servicemap.ServiceMap)
serviceMapGenerator.Enable()
} }
elastic.GetInstance().Configure(config.Config.Elastic) elastic.GetInstance().Configure(config.Config.Elastic)
} }
@@ -385,3 +388,8 @@ 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() })
}

View File

@@ -11,6 +11,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/up9inc/mizu/agent/pkg/dependency"
"github.com/up9inc/mizu/agent/pkg/elastic" "github.com/up9inc/mizu/agent/pkg/elastic"
"github.com/up9inc/mizu/agent/pkg/har" "github.com/up9inc/mizu/agent/pkg/har"
"github.com/up9inc/mizu/agent/pkg/holder" "github.com/up9inc/mizu/agent/pkg/holder"
@@ -151,7 +152,8 @@ func startReadingChannel(outputItems <-chan *tapApi.OutputChannelItem, extension
entryWSource.Destination = mizuEntry.Destination.IP + ":" + mizuEntry.Destination.Port entryWSource.Destination = mizuEntry.Destination.IP + ":" + mizuEntry.Destination.Port
} }
oas.GetOasGeneratorInstance().PushEntry(&entryWSource) oasGenerator := dependency.GetInstance(dependency.OasGeneratorDependency).(oas.OasGeneratorSink)
oasGenerator.PushEntry(&entryWSource)
} }
data, err := json.Marshal(mizuEntry) data, err := json.Marshal(mizuEntry)
@@ -163,7 +165,9 @@ func startReadingChannel(outputItems <-chan *tapApi.OutputChannelItem, extension
connection.SendText(string(data)) connection.SendText(string(data))
servicemap.GetInstance().NewTCPEntry(mizuEntry.Source, mizuEntry.Destination, &item.Protocol) serviceMapGenerator := dependency.GetInstance(dependency.ServiceMapGeneratorDependency).(servicemap.ServiceMapSink)
serviceMapGenerator.NewTCPEntry(mizuEntry.Source, mizuEntry.Destination, &item.Protocol)
elastic.GetInstance().PushEntry(mizuEntry) elastic.GetInstance().PushEntry(mizuEntry)
} }
} }

View File

@@ -8,6 +8,7 @@ import (
"time" "time"
"github.com/up9inc/mizu/agent/pkg/models" "github.com/up9inc/mizu/agent/pkg/models"
"github.com/up9inc/mizu/agent/pkg/utils"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
@@ -59,13 +60,13 @@ func init() {
connectedWebsockets = make(map[int]*SocketConnection) connectedWebsockets = make(map[int]*SocketConnection)
} }
func WebSocketRoutes(app *gin.Engine, eventHandlers EventHandlers, startTime int64) { func WebSocketRoutes(app *gin.Engine, eventHandlers EventHandlers) {
SocketGetBrowserHandler = func(c *gin.Context) { SocketGetBrowserHandler = func(c *gin.Context) {
websocketHandler(c.Writer, c.Request, eventHandlers, false, startTime) websocketHandler(c.Writer, c.Request, eventHandlers, false)
} }
SocketGetTapperHandler = func(c *gin.Context) { SocketGetTapperHandler = func(c *gin.Context) {
websocketHandler(c.Writer, c.Request, eventHandlers, true, startTime) websocketHandler(c.Writer, c.Request, eventHandlers, true)
} }
app.GET("/ws", func(c *gin.Context) { app.GET("/ws", func(c *gin.Context) {
@@ -77,7 +78,7 @@ func WebSocketRoutes(app *gin.Engine, eventHandlers EventHandlers, startTime int
}) })
} }
func websocketHandler(w http.ResponseWriter, r *http.Request, eventHandlers EventHandlers, isTapper bool, startTime int64) { func websocketHandler(w http.ResponseWriter, r *http.Request, eventHandlers EventHandlers, isTapper bool) {
ws, err := websocketUpgrader.Upgrade(w, r, nil) ws, err := websocketUpgrader.Upgrade(w, r, nil)
if err != nil { if err != nil {
logger.Log.Errorf("Failed to set websocket upgrade: %v", err) logger.Log.Errorf("Failed to set websocket upgrade: %v", err)
@@ -99,7 +100,9 @@ func websocketHandler(w http.ResponseWriter, r *http.Request, eventHandlers Even
if !isTapper { if !isTapper {
connection, err = basenine.NewConnection(shared.BasenineHost, shared.BaseninePort) connection, err = basenine.NewConnection(shared.BasenineHost, shared.BaseninePort)
if err != nil { if err != nil {
panic(err) logger.Log.Errorf("Failed to establish a connection to Basenine: %v", err)
socketCleanup(socketId, connectedWebsockets[socketId])
return
} }
} }
@@ -115,7 +118,7 @@ func websocketHandler(w http.ResponseWriter, r *http.Request, eventHandlers Even
eventHandlers.WebSocketConnect(socketId, isTapper) eventHandlers.WebSocketConnect(socketId, isTapper)
startTimeBytes, _ := models.CreateWebsocketStartTimeMessage(startTime) startTimeBytes, _ := models.CreateWebsocketStartTimeMessage(utils.StartTime)
if err = SendToSocket(socketId, startTimeBytes); err != nil { if err = SendToSocket(socketId, startTimeBytes); err != nil {
logger.Log.Error(err) logger.Log.Error(err)
@@ -137,7 +140,8 @@ func websocketHandler(w http.ResponseWriter, r *http.Request, eventHandlers Even
if !isTapper && !isQuerySet { if !isTapper && !isQuerySet {
if err := json.Unmarshal(msg, &params); err != nil { if err := json.Unmarshal(msg, &params); err != nil {
logger.Log.Errorf("Error: %v", socketId, err) logger.Log.Errorf("Error unmarshalling parameters: %v", socketId, err)
continue
} }
query := params.Query query := params.Query
@@ -166,6 +170,10 @@ func websocketHandler(w http.ResponseWriter, r *http.Request, eventHandlers Even
var entry *tapApi.Entry var entry *tapApi.Entry
err = json.Unmarshal(bytes, &entry) err = json.Unmarshal(bytes, &entry)
if err != nil {
logger.Log.Debugf("Error unmarshalling entry: %v", err.Error())
continue
}
var message []byte var message []byte
if params.EnableFullEntries { if params.EnableFullEntries {
@@ -193,7 +201,8 @@ func websocketHandler(w http.ResponseWriter, r *http.Request, eventHandlers Even
var metadata *basenine.Metadata var metadata *basenine.Metadata
err = json.Unmarshal(bytes, &metadata) err = json.Unmarshal(bytes, &metadata)
if err != nil { if err != nil {
logger.Log.Debugf("Error recieving metadata: %v", err.Error()) logger.Log.Debugf("Error unmarshalling metadata: %v", err.Error())
continue
} }
metadataBytes, _ := models.CreateWebsocketQueryMetadataMessage(metadata) metadataBytes, _ := models.CreateWebsocketQueryMetadataMessage(metadata)

View File

@@ -9,7 +9,7 @@ import (
"github.com/op/go-logging" "github.com/op/go-logging"
basenine "github.com/up9inc/basenine/client/go" basenine "github.com/up9inc/basenine/client/go"
"github.com/up9inc/mizu/agent/pkg/api" "github.com/up9inc/mizu/agent/pkg/api"
"github.com/up9inc/mizu/agent/pkg/controllers" "github.com/up9inc/mizu/agent/pkg/utils"
"github.com/up9inc/mizu/shared/logger" "github.com/up9inc/mizu/shared/logger"
tapApi "github.com/up9inc/mizu/tap/api" tapApi "github.com/up9inc/mizu/tap/api"
amqpExt "github.com/up9inc/mizu/tap/extensions/amqp" amqpExt "github.com/up9inc/mizu/tap/extensions/amqp"
@@ -59,7 +59,6 @@ func LoadExtensions() {
return Extensions[i].Protocol.Priority < Extensions[j].Protocol.Priority return Extensions[i].Protocol.Priority < Extensions[j].Protocol.Priority
}) })
controllers.InitExtensionsMap(ExtensionsMap)
api.InitExtensionsMap(ExtensionsMap) api.InitExtensionsMap(ExtensionsMap)
} }
@@ -92,6 +91,8 @@ func ConfigureBasenineServer(host string, port string, dbSize int64, logLevel lo
if err := basenine.InsertionFilter(host, port, insertionFilter); err != nil { if err := basenine.InsertionFilter(host, port, insertionFilter); err != nil {
logger.Log.Errorf("Error while setting the insertion filter: %v", err) logger.Log.Errorf("Error while setting the insertion filter: %v", err)
} }
utils.StartTime = time.Now().UnixNano() / int64(time.Millisecond)
} }
func GetEntryInputChannel() chan *tapApi.OutputChannelItem { func GetEntryInputChannel() chan *tapApi.OutputChannelItem {

View File

@@ -0,0 +1,28 @@
package controllers
import (
"net/http"
"github.com/gin-gonic/gin"
basenine "github.com/up9inc/basenine/client/go"
"github.com/up9inc/mizu/agent/pkg/app"
"github.com/up9inc/mizu/agent/pkg/config"
"github.com/up9inc/mizu/shared"
)
func Flush(c *gin.Context) {
if err := basenine.Flush(shared.BasenineHost, shared.BaseninePort); err != nil {
c.JSON(http.StatusBadRequest, err)
} else {
c.JSON(http.StatusOK, "Flushed.")
}
}
func Reset(c *gin.Context) {
if err := basenine.Reset(shared.BasenineHost, shared.BaseninePort); err != nil {
c.JSON(http.StatusBadRequest, err)
} else {
app.ConfigureBasenineServer(shared.BasenineHost, shared.BaseninePort, config.Config.MaxDBSizeBytes, config.Config.LogLevel, config.Config.InsertionFilter)
c.JSON(http.StatusOK, "Resetted.")
}
}

View File

@@ -6,6 +6,7 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/up9inc/mizu/agent/pkg/app"
"github.com/up9inc/mizu/agent/pkg/har" "github.com/up9inc/mizu/agent/pkg/har"
"github.com/up9inc/mizu/agent/pkg/models" "github.com/up9inc/mizu/agent/pkg/models"
"github.com/up9inc/mizu/agent/pkg/validation" "github.com/up9inc/mizu/agent/pkg/validation"
@@ -18,12 +19,6 @@ import (
tapApi "github.com/up9inc/mizu/tap/api" tapApi "github.com/up9inc/mizu/tap/api"
) )
var extensionsMap map[string]*tapApi.Extension // global
func InitExtensionsMap(ref map[string]*tapApi.Extension) {
extensionsMap = ref
}
func Error(c *gin.Context, err error) bool { func Error(c *gin.Context, err error) bool {
if err != nil { if err != nil {
logger.Log.Errorf("Error getting entry: %v", err) logger.Log.Errorf("Error getting entry: %v", err)
@@ -77,7 +72,7 @@ func GetEntries(c *gin.Context) {
return // exit return // exit
} }
extension := extensionsMap[entry.Protocol.Name] extension := app.ExtensionsMap[entry.Protocol.Name]
base := extension.Dissector.Summarize(entry) base := extension.Dissector.Summarize(entry)
dataSlice = append(dataSlice, base) dataSlice = append(dataSlice, base)
@@ -123,7 +118,7 @@ func GetEntry(c *gin.Context) {
return // exit return // exit
} }
extension := extensionsMap[entry.Protocol.Name] extension := app.ExtensionsMap[entry.Protocol.Name]
base := extension.Dissector.Summarize(entry) base := extension.Dissector.Summarize(entry)
representation, bodySize, _ := extension.Dissector.Represent(entry.Request, entry.Response) representation, bodySize, _ := extension.Dissector.Represent(entry.Request, entry.Response)

View File

@@ -5,13 +5,15 @@ import (
"github.com/chanced/openapi" "github.com/chanced/openapi"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/up9inc/mizu/agent/pkg/dependency"
"github.com/up9inc/mizu/agent/pkg/oas" "github.com/up9inc/mizu/agent/pkg/oas"
"github.com/up9inc/mizu/shared/logger" "github.com/up9inc/mizu/shared/logger"
) )
func GetOASServers(c *gin.Context) { func GetOASServers(c *gin.Context) {
m := make([]string, 0) m := make([]string, 0)
oas.GetOasGeneratorInstance().ServiceSpecs.Range(func(key, value interface{}) bool { oasGenerator := dependency.GetInstance(dependency.OasGeneratorDependency).(oas.OasGenerator)
oasGenerator.GetServiceSpecs().Range(func(key, value interface{}) bool {
m = append(m, key.(string)) m = append(m, key.(string))
return true return true
}) })
@@ -20,7 +22,8 @@ func GetOASServers(c *gin.Context) {
} }
func GetOASSpec(c *gin.Context) { func GetOASSpec(c *gin.Context) {
res, ok := oas.GetOasGeneratorInstance().ServiceSpecs.Load(c.Param("id")) oasGenerator := dependency.GetInstance(dependency.OasGeneratorDependency).(oas.OasGenerator)
res, ok := oasGenerator.GetServiceSpecs().Load(c.Param("id"))
if !ok { if !ok {
c.JSON(http.StatusNotFound, gin.H{ c.JSON(http.StatusNotFound, gin.H{
"error": true, "error": true,
@@ -48,7 +51,9 @@ func GetOASSpec(c *gin.Context) {
func GetOASAllSpecs(c *gin.Context) { func GetOASAllSpecs(c *gin.Context) {
res := map[string]*openapi.OpenAPI{} res := map[string]*openapi.OpenAPI{}
oas.GetOasGeneratorInstance().ServiceSpecs.Range(func(key, value interface{}) bool {
oasGenerator := dependency.GetInstance(dependency.OasGeneratorDependency).(oas.OasGenerator)
oasGenerator.GetServiceSpecs().Range(func(key, value interface{}) bool {
svc := key.(string) svc := key.(string)
gen := value.(*oas.SpecGen) gen := value.(*oas.SpecGen)
spec, err := gen.GetSpec() spec, err := gen.GetSpec()

View File

@@ -4,36 +4,43 @@ import (
"net/http/httptest" "net/http/httptest"
"testing" "testing"
"github.com/up9inc/mizu/agent/pkg/dependency"
"github.com/up9inc/mizu/agent/pkg/oas" "github.com/up9inc/mizu/agent/pkg/oas"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
func TestGetOASServers(t *testing.T) { func TestGetOASServers(t *testing.T) {
dependency.RegisterGenerator(dependency.OasGeneratorDependency, func() interface{} { return oas.GetDefaultOasGeneratorInstance() })
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
c, _ := gin.CreateTestContext(recorder) c, _ := gin.CreateTestContext(recorder)
oas.GetOasGeneratorInstance().Start() oas.GetDefaultOasGeneratorInstance().Start()
oas.GetOasGeneratorInstance().ServiceSpecs.Store("some", oas.NewGen("some")) oas.GetDefaultOasGeneratorInstance().GetServiceSpecs().Store("some", oas.NewGen("some"))
GetOASServers(c) GetOASServers(c)
t.Logf("Written body: %s", recorder.Body.String()) t.Logf("Written body: %s", recorder.Body.String())
} }
func TestGetOASAllSpecs(t *testing.T) { func TestGetOASAllSpecs(t *testing.T) {
dependency.RegisterGenerator(dependency.OasGeneratorDependency, func() interface{} { return oas.GetDefaultOasGeneratorInstance() })
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
c, _ := gin.CreateTestContext(recorder) c, _ := gin.CreateTestContext(recorder)
oas.GetOasGeneratorInstance().Start() oas.GetDefaultOasGeneratorInstance().Start()
oas.GetOasGeneratorInstance().ServiceSpecs.Store("some", oas.NewGen("some")) oas.GetDefaultOasGeneratorInstance().GetServiceSpecs().Store("some", oas.NewGen("some"))
GetOASAllSpecs(c) GetOASAllSpecs(c)
t.Logf("Written body: %s", recorder.Body.String()) t.Logf("Written body: %s", recorder.Body.String())
} }
func TestGetOASSpec(t *testing.T) { func TestGetOASSpec(t *testing.T) {
dependency.RegisterGenerator(dependency.OasGeneratorDependency, func() interface{} { return oas.GetDefaultOasGeneratorInstance() })
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
c, _ := gin.CreateTestContext(recorder) c, _ := gin.CreateTestContext(recorder)
oas.GetOasGeneratorInstance().Start() oas.GetDefaultOasGeneratorInstance().Start()
oas.GetOasGeneratorInstance().ServiceSpecs.Store("some", oas.NewGen("some")) oas.GetDefaultOasGeneratorInstance().GetServiceSpecs().Store("some", oas.NewGen("some"))
c.Params = []gin.Param{{Key: "id", Value: "some"}} c.Params = []gin.Param{{Key: "id", Value: "some"}}

View File

@@ -3,6 +3,7 @@ package controllers
import ( import (
"net/http" "net/http"
"github.com/up9inc/mizu/agent/pkg/dependency"
"github.com/up9inc/mizu/agent/pkg/servicemap" "github.com/up9inc/mizu/agent/pkg/servicemap"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@@ -13,8 +14,9 @@ type ServiceMapController struct {
} }
func NewServiceMapController() *ServiceMapController { func NewServiceMapController() *ServiceMapController {
serviceMapGenerator := dependency.GetInstance(dependency.ServiceMapGeneratorDependency).(servicemap.ServiceMap)
return &ServiceMapController{ return &ServiceMapController{
service: servicemap.GetInstance(), service: serviceMapGenerator,
} }
} }

View File

@@ -7,6 +7,7 @@ import (
"net/http/httptest" "net/http/httptest"
"testing" "testing"
"github.com/up9inc/mizu/agent/pkg/dependency"
"github.com/up9inc/mizu/agent/pkg/servicemap" "github.com/up9inc/mizu/agent/pkg/servicemap"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@@ -57,9 +58,11 @@ type ServiceMapControllerSuite struct {
} }
func (s *ServiceMapControllerSuite) SetupTest() { func (s *ServiceMapControllerSuite) SetupTest() {
dependency.RegisterGenerator(dependency.ServiceMapGeneratorDependency, func() interface{} { return servicemap.GetDefaultServiceMapInstance() })
s.c = NewServiceMapController() s.c = NewServiceMapController()
s.c.service.Enable() s.c.service.Enable()
s.c.service.NewTCPEntry(TCPEntryA, TCPEntryB, ProtocolHttp) s.c.service.(servicemap.ServiceMapSink).NewTCPEntry(TCPEntryA, TCPEntryB, ProtocolHttp)
s.w = httptest.NewRecorder() s.w = httptest.NewRecorder()
s.g, _ = gin.CreateTestContext(s.w) s.g, _ = gin.CreateTestContext(s.w)

View File

@@ -0,0 +1,11 @@
package dependency
var typeIntializerMap = make(map[DependencyContainerType]func() interface{}, 0)
func RegisterGenerator(name DependencyContainerType, fn func() interface{}) {
typeIntializerMap[name] = fn
}
func GetInstance(name DependencyContainerType) interface{} {
return typeIntializerMap[name]()
}

View File

@@ -0,0 +1,8 @@
package dependency
type DependencyContainerType string
const (
ServiceMapGeneratorDependency = "ServiceMapGeneratorDependency"
OasGeneratorDependency = "OasGeneratorDependency"
)

View File

@@ -147,9 +147,9 @@ func feedEntry(entry *har.Entry, source string, isSync bool, file string) {
ews := EntryWithSource{Entry: *entry, Source: source, Destination: u.Host, Id: uint(0)} ews := EntryWithSource{Entry: *entry, Source: source, Destination: u.Host, Id: uint(0)}
if isSync { if isSync {
GetOasGeneratorInstance().entriesChan <- ews // blocking variant, right? GetDefaultOasGeneratorInstance().entriesChan <- ews // blocking variant, right?
} else { } else {
GetOasGeneratorInstance().PushEntry(&ews) GetDefaultOasGeneratorInstance().PushEntry(&ews)
} }
} }

View File

@@ -12,18 +12,38 @@ import (
var ( var (
syncOnce sync.Once syncOnce sync.Once
instance *oasGenerator instance *defaultOasGenerator
) )
func GetOasGeneratorInstance() *oasGenerator { type OasGeneratorSink interface {
PushEntry(entryWithSource *EntryWithSource)
}
type OasGenerator interface {
Start()
Stop()
IsStarted() bool
Reset()
GetServiceSpecs() *sync.Map
}
type defaultOasGenerator struct {
started bool
ctx context.Context
cancel context.CancelFunc
serviceSpecs *sync.Map
entriesChan chan EntryWithSource
}
func GetDefaultOasGeneratorInstance() *defaultOasGenerator {
syncOnce.Do(func() { syncOnce.Do(func() {
instance = newOasGenerator() instance = NewDefaultOasGenerator()
logger.Log.Debug("OAS Generator Initialized") logger.Log.Debug("OAS Generator Initialized")
}) })
return instance return instance
} }
func (g *oasGenerator) Start() { func (g *defaultOasGenerator) Start() {
if g.started { if g.started {
return return
} }
@@ -31,12 +51,12 @@ func (g *oasGenerator) Start() {
g.cancel = cancel g.cancel = cancel
g.ctx = ctx g.ctx = ctx
g.entriesChan = make(chan EntryWithSource, 100) // buffer up to 100 entries for OAS processing g.entriesChan = make(chan EntryWithSource, 100) // buffer up to 100 entries for OAS processing
g.ServiceSpecs = &sync.Map{} g.serviceSpecs = &sync.Map{}
g.started = true g.started = true
go instance.runGenerator() go g.runGenerator()
} }
func (g *oasGenerator) Stop() { func (g *defaultOasGenerator) Stop() {
if !g.started { if !g.started {
return return
} }
@@ -45,11 +65,11 @@ func (g *oasGenerator) Stop() {
g.started = false g.started = false
} }
func (g *oasGenerator) IsStarted() bool { func (g *defaultOasGenerator) IsStarted() bool {
return g.started return g.started
} }
func (g *oasGenerator) runGenerator() { func (g *defaultOasGenerator) runGenerator() {
for { for {
select { select {
case <-g.ctx.Done(): case <-g.ctx.Done():
@@ -67,11 +87,11 @@ func (g *oasGenerator) runGenerator() {
logger.Log.Errorf("Failed to parse entry URL: %v, err: %v", entry.Request.URL, err) logger.Log.Errorf("Failed to parse entry URL: %v, err: %v", entry.Request.URL, err)
} }
val, found := g.ServiceSpecs.Load(entryWithSource.Destination) val, found := g.serviceSpecs.Load(entryWithSource.Destination)
var gen *SpecGen var gen *SpecGen
if !found { if !found {
gen = NewGen(u.Scheme + "://" + entryWithSource.Destination) gen = NewGen(u.Scheme + "://" + entryWithSource.Destination)
g.ServiceSpecs.Store(entryWithSource.Destination, gen) g.serviceSpecs.Store(entryWithSource.Destination, gen)
} else { } else {
gen = val.(*SpecGen) gen = val.(*SpecGen)
} }
@@ -92,11 +112,11 @@ func (g *oasGenerator) runGenerator() {
} }
} }
func (g *oasGenerator) Reset() { func (g *defaultOasGenerator) Reset() {
g.ServiceSpecs = &sync.Map{} g.serviceSpecs = &sync.Map{}
} }
func (g *oasGenerator) PushEntry(entryWithSource *EntryWithSource) { func (g *defaultOasGenerator) PushEntry(entryWithSource *EntryWithSource) {
if !g.started { if !g.started {
return return
} }
@@ -107,12 +127,16 @@ func (g *oasGenerator) PushEntry(entryWithSource *EntryWithSource) {
} }
} }
func newOasGenerator() *oasGenerator { func (g *defaultOasGenerator) GetServiceSpecs() *sync.Map {
return &oasGenerator{ return g.serviceSpecs
}
func NewDefaultOasGenerator() *defaultOasGenerator {
return &defaultOasGenerator{
started: false, started: false,
ctx: nil, ctx: nil,
cancel: nil, cancel: nil,
ServiceSpecs: nil, serviceSpecs: nil,
entriesChan: nil, entriesChan: nil,
} }
} }
@@ -123,11 +147,3 @@ type EntryWithSource struct {
Entry har.Entry Entry har.Entry
Id uint Id uint
} }
type oasGenerator struct {
started bool
ctx context.Context
cancel context.CancelFunc
ServiceSpecs *sync.Map
entriesChan chan EntryWithSource
}

View File

@@ -2,10 +2,6 @@ package oas
import ( import (
"encoding/json" "encoding/json"
"github.com/chanced/openapi"
"github.com/op/go-logging"
"github.com/up9inc/mizu/shared/logger"
"github.com/wI2L/jsondiff"
"io/ioutil" "io/ioutil"
"os" "os"
"regexp" "regexp"
@@ -13,6 +9,11 @@ import (
"testing" "testing"
"time" "time"
"github.com/chanced/openapi"
"github.com/op/go-logging"
"github.com/up9inc/mizu/shared/logger"
"github.com/wI2L/jsondiff"
"github.com/up9inc/mizu/agent/pkg/har" "github.com/up9inc/mizu/agent/pkg/har"
) )
@@ -47,14 +48,14 @@ func TestEntries(t *testing.T) {
t.Log(err) t.Log(err)
t.FailNow() t.FailNow()
} }
GetOasGeneratorInstance().Start() GetDefaultOasGeneratorInstance().Start()
loadStartingOAS("test_artifacts/catalogue.json", "catalogue") loadStartingOAS("test_artifacts/catalogue.json", "catalogue")
loadStartingOAS("test_artifacts/trcc.json", "trcc-api-service") loadStartingOAS("test_artifacts/trcc.json", "trcc-api-service")
go func() { go func() {
for { for {
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
GetOasGeneratorInstance().ServiceSpecs.Range(func(key, val interface{}) bool { GetDefaultOasGeneratorInstance().GetServiceSpecs().Range(func(key, val interface{}) bool {
svc := key.(string) svc := key.(string)
t.Logf("Getting spec for %s", svc) t.Logf("Getting spec for %s", svc)
gen := val.(*SpecGen) gen := val.(*SpecGen)
@@ -76,7 +77,7 @@ func TestEntries(t *testing.T) {
waitQueueProcessed() waitQueueProcessed()
svcs := strings.Builder{} svcs := strings.Builder{}
GetOasGeneratorInstance().ServiceSpecs.Range(func(key, val interface{}) bool { GetDefaultOasGeneratorInstance().GetServiceSpecs().Range(func(key, val interface{}) bool {
gen := val.(*SpecGen) gen := val.(*SpecGen)
svc := key.(string) svc := key.(string)
svcs.WriteString(svc + ",") svcs.WriteString(svc + ",")
@@ -98,7 +99,7 @@ func TestEntries(t *testing.T) {
return true return true
}) })
GetOasGeneratorInstance().ServiceSpecs.Range(func(key, val interface{}) bool { GetDefaultOasGeneratorInstance().GetServiceSpecs().Range(func(key, val interface{}) bool {
svc := key.(string) svc := key.(string)
gen := val.(*SpecGen) gen := val.(*SpecGen)
spec, err := gen.GetSpec() spec, err := gen.GetSpec()
@@ -122,8 +123,8 @@ func TestEntries(t *testing.T) {
} }
func TestFileSingle(t *testing.T) { func TestFileSingle(t *testing.T) {
GetOasGeneratorInstance().Start() GetDefaultOasGeneratorInstance().Start()
GetOasGeneratorInstance().Reset() GetDefaultOasGeneratorInstance().Reset()
// loadStartingOAS() // loadStartingOAS()
file := "test_artifacts/params.har" file := "test_artifacts/params.har"
files := []string{file} files := []string{file}
@@ -135,7 +136,7 @@ func TestFileSingle(t *testing.T) {
waitQueueProcessed() waitQueueProcessed()
GetOasGeneratorInstance().ServiceSpecs.Range(func(key, val interface{}) bool { GetDefaultOasGeneratorInstance().GetServiceSpecs().Range(func(key, val interface{}) bool {
svc := key.(string) svc := key.(string)
gen := val.(*SpecGen) gen := val.(*SpecGen)
spec, err := gen.GetSpec() spec, err := gen.GetSpec()
@@ -191,7 +192,7 @@ func TestFileSingle(t *testing.T) {
func waitQueueProcessed() { func waitQueueProcessed() {
for { for {
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
queue := len(GetOasGeneratorInstance().entriesChan) queue := len(GetDefaultOasGeneratorInstance().entriesChan)
logger.Log.Infof("Queue: %d", queue) logger.Log.Infof("Queue: %d", queue)
if queue < 1 { if queue < 1 {
break break
@@ -221,7 +222,7 @@ func loadStartingOAS(file string, label string) {
gen := NewGen(label) gen := NewGen(label)
gen.StartFromSpec(doc) gen.StartFromSpec(doc)
GetOasGeneratorInstance().ServiceSpecs.Store(label, gen) GetDefaultOasGeneratorInstance().GetServiceSpecs().Store(label, gen)
} }
func TestEntriesNegative(t *testing.T) { func TestEntriesNegative(t *testing.T) {

View File

@@ -1,9 +1,10 @@
package oas package oas
import ( import (
"github.com/chanced/openapi"
"strings" "strings"
"testing" "testing"
"github.com/chanced/openapi"
) )
func TestTree(t *testing.T) { func TestTree(t *testing.T) {

View File

@@ -10,7 +10,7 @@ import (
restclient "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest"
) )
func NewFromInCluster(errOut chan error, namesapce string) (*Resolver, error) { func NewFromInCluster(errOut chan error, namespace string) (*Resolver, error) {
config, err := restclient.InClusterConfig() config, err := restclient.InClusterConfig()
if err != nil { if err != nil {
return nil, err return nil, err
@@ -19,5 +19,5 @@ func NewFromInCluster(errOut chan error, namesapce string) (*Resolver, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &Resolver{clientConfig: config, clientSet: clientset, nameMap: cmap.New(), serviceMap: cmap.New(), errOut: errOut, namespace: namesapce}, nil return &Resolver{clientConfig: config, clientSet: clientset, nameMap: cmap.New(), serviceMap: cmap.New(), errOut: errOut, namespace: namespace}, nil
} }

View File

@@ -168,11 +168,13 @@ func (resolver *Resolver) watchServices(ctx context.Context) error {
func (resolver *Resolver) saveResolvedName(key string, resolved string, namespace string, eventType watch.EventType) { func (resolver *Resolver) saveResolvedName(key string, resolved string, namespace string, eventType watch.EventType) {
if eventType == watch.Deleted { if eventType == watch.Deleted {
resolver.nameMap.Remove(resolved)
resolver.nameMap.Remove(key) resolver.nameMap.Remove(key)
logger.Log.Infof("setting %s=nil", key) logger.Log.Infof("setting %s=nil", key)
} else { } else {
resolver.nameMap.Set(key, &ResolvedObjectInfo{FullAddress: resolved, Namespace: namespace}) resolver.nameMap.Set(key, &ResolvedObjectInfo{FullAddress: resolved, Namespace: namespace})
resolver.nameMap.Set(resolved, &ResolvedObjectInfo{FullAddress: resolved, Namespace: namespace})
logger.Log.Infof("setting %s=%s", key, resolved) logger.Log.Infof("setting %s=%s", key, resolved)
} }
} }

View File

@@ -0,0 +1,15 @@
package routes
import (
"github.com/up9inc/mizu/agent/pkg/controllers"
"github.com/gin-gonic/gin"
)
// DdRoutes defines the group of database routes.
func DbRoutes(app *gin.Engine) {
routeGroup := app.Group("/db")
routeGroup.GET("/flush", controllers.Flush)
routeGroup.GET("/reset", controllers.Reset)
}

View File

@@ -13,28 +13,31 @@ const (
UnresolvedNodeName = "unresolved" UnresolvedNodeName = "unresolved"
) )
var instance *serviceMap var instance *defaultServiceMap
var once sync.Once var once sync.Once
func GetInstance() ServiceMap { func GetDefaultServiceMapInstance() *defaultServiceMap {
once.Do(func() { once.Do(func() {
instance = newServiceMap() instance = NewDefaultServiceMapGenerator()
logger.Log.Debug("Service Map Initialized") logger.Log.Debug("Service Map Initialized")
}) })
return instance return instance
} }
type serviceMap struct { type defaultServiceMap struct {
enabled bool enabled bool
graph *graph graph *graph
entriesProcessed int entriesProcessed int
} }
type ServiceMapSink interface {
NewTCPEntry(source *tapApi.TCP, destination *tapApi.TCP, protocol *tapApi.Protocol)
}
type ServiceMap interface { type ServiceMap interface {
Enable() Enable()
Disable() Disable()
IsEnabled() bool IsEnabled() bool
NewTCPEntry(source *tapApi.TCP, destination *tapApi.TCP, protocol *tapApi.Protocol)
GetStatus() ServiceMapStatus GetStatus() ServiceMapStatus
GetNodes() []ServiceMapNode GetNodes() []ServiceMapNode
GetEdges() []ServiceMapEdge GetEdges() []ServiceMapEdge
@@ -44,8 +47,8 @@ type ServiceMap interface {
Reset() Reset()
} }
func newServiceMap() *serviceMap { func NewDefaultServiceMapGenerator() *defaultServiceMap {
return &serviceMap{ return &defaultServiceMap{
enabled: false, enabled: false,
entriesProcessed: 0, entriesProcessed: 0,
graph: newDirectedGraph(), graph: newDirectedGraph(),
@@ -105,12 +108,12 @@ func newEdgeData(p *tapApi.Protocol) *edgeData {
} }
} }
func (s *serviceMap) nodeExists(k key) (*nodeData, bool) { func (s *defaultServiceMap) nodeExists(k key) (*nodeData, bool) {
n, ok := s.graph.Nodes[k] n, ok := s.graph.Nodes[k]
return n, ok return n, ok
} }
func (s *serviceMap) addNode(k key, e *tapApi.TCP) (*nodeData, bool) { func (s *defaultServiceMap) addNode(k key, e *tapApi.TCP) (*nodeData, bool) {
nd, exists := s.nodeExists(k) nd, exists := s.nodeExists(k)
if !exists { if !exists {
s.graph.Nodes[k] = newNodeData(len(s.graph.Nodes)+1, e) s.graph.Nodes[k] = newNodeData(len(s.graph.Nodes)+1, e)
@@ -119,7 +122,7 @@ func (s *serviceMap) addNode(k key, e *tapApi.TCP) (*nodeData, bool) {
return nd, false return nd, false
} }
func (s *serviceMap) addEdge(u, v *entryData, p *tapApi.Protocol) { func (s *defaultServiceMap) addEdge(u, v *entryData, p *tapApi.Protocol) {
if n, ok := s.addNode(u.key, u.entry); !ok { if n, ok := s.addNode(u.key, u.entry); !ok {
n.count++ n.count++
} }
@@ -156,20 +159,20 @@ func (s *serviceMap) addEdge(u, v *entryData, p *tapApi.Protocol) {
s.entriesProcessed++ s.entriesProcessed++
} }
func (s *serviceMap) Enable() { func (s *defaultServiceMap) Enable() {
s.enabled = true s.enabled = true
} }
func (s *serviceMap) Disable() { func (s *defaultServiceMap) Disable() {
s.Reset() s.Reset()
s.enabled = false s.enabled = false
} }
func (s *serviceMap) IsEnabled() bool { func (s *defaultServiceMap) IsEnabled() bool {
return s.enabled return s.enabled
} }
func (s *serviceMap) NewTCPEntry(src *tapApi.TCP, dst *tapApi.TCP, p *tapApi.Protocol) { func (s *defaultServiceMap) NewTCPEntry(src *tapApi.TCP, dst *tapApi.TCP, p *tapApi.Protocol) {
if !s.IsEnabled() { if !s.IsEnabled() {
return return
} }
@@ -206,7 +209,7 @@ func (s *serviceMap) NewTCPEntry(src *tapApi.TCP, dst *tapApi.TCP, p *tapApi.Pro
s.addEdge(srcEntry, dstEntry, p) s.addEdge(srcEntry, dstEntry, p)
} }
func (s *serviceMap) GetStatus() ServiceMapStatus { func (s *defaultServiceMap) GetStatus() ServiceMapStatus {
status := ServiceMapDisabled status := ServiceMapDisabled
if s.IsEnabled() { if s.IsEnabled() {
status = ServiceMapEnabled status = ServiceMapEnabled
@@ -220,7 +223,7 @@ func (s *serviceMap) GetStatus() ServiceMapStatus {
} }
} }
func (s *serviceMap) GetNodes() []ServiceMapNode { func (s *defaultServiceMap) GetNodes() []ServiceMapNode {
var nodes []ServiceMapNode var nodes []ServiceMapNode
for i, n := range s.graph.Nodes { for i, n := range s.graph.Nodes {
nodes = append(nodes, ServiceMapNode{ nodes = append(nodes, ServiceMapNode{
@@ -233,7 +236,7 @@ func (s *serviceMap) GetNodes() []ServiceMapNode {
return nodes return nodes
} }
func (s *serviceMap) GetEdges() []ServiceMapEdge { func (s *defaultServiceMap) GetEdges() []ServiceMapEdge {
var edges []ServiceMapEdge var edges []ServiceMapEdge
for u, m := range s.graph.Edges { for u, m := range s.graph.Edges {
for v := range m { for v := range m {
@@ -260,15 +263,15 @@ func (s *serviceMap) GetEdges() []ServiceMapEdge {
return edges return edges
} }
func (s *serviceMap) GetEntriesProcessedCount() int { func (s *defaultServiceMap) GetEntriesProcessedCount() int {
return s.entriesProcessed return s.entriesProcessed
} }
func (s *serviceMap) GetNodesCount() int { func (s *defaultServiceMap) GetNodesCount() int {
return len(s.graph.Nodes) return len(s.graph.Nodes)
} }
func (s *serviceMap) GetEdgesCount() int { func (s *defaultServiceMap) GetEdgesCount() int {
var count int var count int
for u, m := range s.graph.Edges { for u, m := range s.graph.Edges {
for v := range m { for v := range m {
@@ -280,7 +283,7 @@ func (s *serviceMap) GetEdgesCount() int {
return count return count
} }
func (s *serviceMap) Reset() { func (s *defaultServiceMap) Reset() {
s.entriesProcessed = 0 s.entriesProcessed = 0
s.graph = newDirectedGraph() s.graph = newDirectedGraph()
} }

View File

@@ -80,21 +80,21 @@ var (
type ServiceMapDisabledSuite struct { type ServiceMapDisabledSuite struct {
suite.Suite suite.Suite
instance ServiceMap instance *defaultServiceMap
} }
type ServiceMapEnabledSuite struct { type ServiceMapEnabledSuite struct {
suite.Suite suite.Suite
instance ServiceMap instance *defaultServiceMap
} }
func (s *ServiceMapDisabledSuite) SetupTest() { func (s *ServiceMapDisabledSuite) SetupTest() {
s.instance = GetInstance() s.instance = GetDefaultServiceMapInstance()
} }
func (s *ServiceMapEnabledSuite) SetupTest() { func (s *ServiceMapEnabledSuite) SetupTest() {
s.instance = GetInstance() s.instance = GetDefaultServiceMapInstance()
s.instance.Enable() s.instance.Enable()
} }
@@ -107,7 +107,7 @@ func (s *ServiceMapDisabledSuite) TestServiceMapInstance() {
func (s *ServiceMapDisabledSuite) TestServiceMapSingletonInstance() { func (s *ServiceMapDisabledSuite) TestServiceMapSingletonInstance() {
assert := s.Assert() assert := s.Assert()
instance2 := GetInstance() instance2 := GetDefaultServiceMapInstance()
assert.NotNil(s.instance) assert.NotNil(s.instance)
assert.NotNil(instance2) assert.NotNil(instance2)

View File

@@ -17,6 +17,10 @@ import (
"github.com/up9inc/mizu/shared/logger" "github.com/up9inc/mizu/shared/logger"
) )
var (
StartTime int64 // global
)
// StartServer starts the server with a graceful shutdown // StartServer starts the server with a graceful shutdown
func StartServer(app *gin.Engine) { func StartServer(app *gin.Engine) {
signals := make(chan os.Signal, 2) signals := make(chan os.Signal, 2)

View File

@@ -27,4 +27,5 @@ func init() {
} }
checkCmd.Flags().Bool(configStructs.PreTapCheckName, defaultCheckConfig.PreTap, "Check pre-tap Mizu installation for potential problems") checkCmd.Flags().Bool(configStructs.PreTapCheckName, defaultCheckConfig.PreTap, "Check pre-tap Mizu installation for potential problems")
checkCmd.Flags().Bool(configStructs.ImagePullCheckName, defaultCheckConfig.ImagePull, "Test connectivity to container image registry by creating and removing a temporary pod in 'default' namespace")
} }

View File

@@ -0,0 +1,102 @@
package check
import (
"context"
"fmt"
"github.com/up9inc/mizu/cli/uiUtils"
"github.com/up9inc/mizu/shared/kubernetes"
"github.com/up9inc/mizu/shared/logger"
core "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"regexp"
"time"
)
func ImagePullInCluster(ctx context.Context, kubernetesProvider *kubernetes.Provider) bool {
logger.Log.Infof("\nimage-pull-in-cluster\n--------------------")
namespace := "default"
podName := "mizu-test"
defer func() {
if err := kubernetesProvider.RemovePod(ctx, namespace, podName); err != nil {
logger.Log.Errorf("%v error while removing test pod in cluster, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), err)
}
}()
if err := createImagePullInClusterPod(ctx, kubernetesProvider, namespace, podName); err != nil {
logger.Log.Errorf("%v error while creating test pod in cluster, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), err)
return false
}
if err := checkImagePulled(ctx, kubernetesProvider, namespace, podName); err != nil {
logger.Log.Errorf("%v cluster is not able to pull mizu containers from docker hub, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), err)
return false
}
logger.Log.Infof("%v cluster is able to pull mizu containers from docker hub", fmt.Sprintf(uiUtils.Green, "√"))
return true
}
func checkImagePulled(ctx context.Context, kubernetesProvider *kubernetes.Provider, namespace string, podName string) error {
podExactRegex := regexp.MustCompile(fmt.Sprintf("^%s$", podName))
podWatchHelper := kubernetes.NewPodWatchHelper(kubernetesProvider, podExactRegex)
eventChan, errorChan := kubernetes.FilteredWatch(ctx, podWatchHelper, []string{namespace}, podWatchHelper)
timeAfter := time.After(30 * time.Second)
for {
select {
case wEvent, ok := <-eventChan:
if !ok {
eventChan = nil
continue
}
pod, err := wEvent.ToPod()
if err != nil {
return err
}
if pod.Status.Phase == core.PodRunning {
return nil
}
case err, ok := <-errorChan:
if !ok {
errorChan = nil
continue
}
return err
case <-timeAfter:
return fmt.Errorf("image not pulled in time")
}
}
}
func createImagePullInClusterPod(ctx context.Context, kubernetesProvider *kubernetes.Provider, namespace string, podName string) error {
var zero int64
pod := &core.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: podName,
},
Spec: core.PodSpec{
Containers: []core.Container{
{
Name: "probe",
Image: "up9inc/busybox",
ImagePullPolicy: "Always",
Command: []string{"cat"},
Stdin: true,
},
},
TerminationGracePeriodSeconds: &zero,
},
}
if _, err := kubernetesProvider.CreatePod(ctx, namespace, pod); err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,31 @@
package check
import (
"fmt"
"github.com/up9inc/mizu/cli/config"
"github.com/up9inc/mizu/cli/uiUtils"
"github.com/up9inc/mizu/shared/kubernetes"
"github.com/up9inc/mizu/shared/logger"
"github.com/up9inc/mizu/shared/semver"
)
func KubernetesApi() (*kubernetes.Provider, *semver.SemVersion, bool) {
logger.Log.Infof("\nkubernetes-api\n--------------------")
kubernetesProvider, err := kubernetes.NewProvider(config.Config.KubeConfigPath(), config.Config.KubeContext)
if err != nil {
logger.Log.Errorf("%v can't initialize the client, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), err)
return nil, nil, false
}
logger.Log.Infof("%v can initialize the client", fmt.Sprintf(uiUtils.Green, "√"))
kubernetesVersion, err := kubernetesProvider.GetKubernetesVersion()
if err != nil {
logger.Log.Errorf("%v can't query the Kubernetes API, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), err)
return nil, nil, false
}
logger.Log.Infof("%v can query the Kubernetes API", fmt.Sprintf(uiUtils.Green, "√"))
return kubernetesProvider, kubernetesVersion, true
}

View File

@@ -0,0 +1,87 @@
package check
import (
"context"
"embed"
"fmt"
"github.com/up9inc/mizu/cli/config"
"github.com/up9inc/mizu/cli/uiUtils"
"github.com/up9inc/mizu/shared/kubernetes"
"github.com/up9inc/mizu/shared/logger"
rbac "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/scheme"
)
func TapKubernetesPermissions(ctx context.Context, embedFS embed.FS, kubernetesProvider *kubernetes.Provider) bool {
logger.Log.Infof("\nkubernetes-permissions\n--------------------")
var filePath string
if config.Config.IsNsRestrictedMode() {
filePath = "permissionFiles/permissions-ns-tap.yaml"
} else {
filePath = "permissionFiles/permissions-all-namespaces-tap.yaml"
}
data, err := embedFS.ReadFile(filePath)
if err != nil {
logger.Log.Errorf("%v error while checking kubernetes permissions, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), err)
return false
}
obj, err := getDecodedObject(data)
if err != nil {
logger.Log.Errorf("%v error while checking kubernetes permissions, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), err)
return false
}
var rules []rbac.PolicyRule
if config.Config.IsNsRestrictedMode() {
rules = obj.(*rbac.Role).Rules
} else {
rules = obj.(*rbac.ClusterRole).Rules
}
return checkPermissions(ctx, kubernetesProvider, rules)
}
func getDecodedObject(data []byte) (runtime.Object, error) {
decode := scheme.Codecs.UniversalDeserializer().Decode
obj, _, err := decode(data, nil, nil)
if err != nil {
return nil, err
}
return obj, nil
}
func checkPermissions(ctx context.Context, kubernetesProvider *kubernetes.Provider, rules []rbac.PolicyRule) bool {
permissionsExist := true
for _, rule := range rules {
for _, group := range rule.APIGroups {
for _, resource := range rule.Resources {
for _, verb := range rule.Verbs {
exist, err := kubernetesProvider.CanI(ctx, config.Config.MizuResourcesNamespace, resource, verb, group)
permissionsExist = checkPermissionExist(group, resource, verb, exist, err) && permissionsExist
}
}
}
}
return permissionsExist
}
func checkPermissionExist(group string, resource string, verb string, exist bool, err error) bool {
if err != nil {
logger.Log.Errorf("%v error checking permission for %v %v in group '%v', err: %v", fmt.Sprintf(uiUtils.Red, "✗"), verb, resource, group, err)
return false
} else if !exist {
logger.Log.Errorf("%v can't %v %v in group '%v'", fmt.Sprintf(uiUtils.Red, "✗"), verb, resource, group)
return false
}
logger.Log.Infof("%v can %v %v in group '%v'", fmt.Sprintf(uiUtils.Green, "√"), verb, resource, group)
return true
}

View File

@@ -0,0 +1,95 @@
package check
import (
"context"
"fmt"
"github.com/up9inc/mizu/cli/config"
"github.com/up9inc/mizu/cli/uiUtils"
"github.com/up9inc/mizu/shared/kubernetes"
"github.com/up9inc/mizu/shared/logger"
)
func KubernetesResources(ctx context.Context, kubernetesProvider *kubernetes.Provider) bool {
logger.Log.Infof("\nk8s-components\n--------------------")
exist, err := kubernetesProvider.DoesNamespaceExist(ctx, config.Config.MizuResourcesNamespace)
allResourcesExist := checkResourceExist(config.Config.MizuResourcesNamespace, "namespace", exist, err)
exist, err = kubernetesProvider.DoesConfigMapExist(ctx, config.Config.MizuResourcesNamespace, kubernetes.ConfigMapName)
allResourcesExist = checkResourceExist(kubernetes.ConfigMapName, "config map", exist, err) && allResourcesExist
exist, err = kubernetesProvider.DoesServiceAccountExist(ctx, config.Config.MizuResourcesNamespace, kubernetes.ServiceAccountName)
allResourcesExist = checkResourceExist(kubernetes.ServiceAccountName, "service account", exist, err) && allResourcesExist
if config.Config.IsNsRestrictedMode() {
exist, err = kubernetesProvider.DoesRoleExist(ctx, config.Config.MizuResourcesNamespace, kubernetes.RoleName)
allResourcesExist = checkResourceExist(kubernetes.RoleName, "role", exist, err) && allResourcesExist
exist, err = kubernetesProvider.DoesRoleBindingExist(ctx, config.Config.MizuResourcesNamespace, kubernetes.RoleBindingName)
allResourcesExist = checkResourceExist(kubernetes.RoleBindingName, "role binding", exist, err) && allResourcesExist
} else {
exist, err = kubernetesProvider.DoesClusterRoleExist(ctx, kubernetes.ClusterRoleName)
allResourcesExist = checkResourceExist(kubernetes.ClusterRoleName, "cluster role", exist, err) && allResourcesExist
exist, err = kubernetesProvider.DoesClusterRoleBindingExist(ctx, kubernetes.ClusterRoleBindingName)
allResourcesExist = checkResourceExist(kubernetes.ClusterRoleBindingName, "cluster role binding", exist, err) && allResourcesExist
}
exist, err = kubernetesProvider.DoesServiceExist(ctx, config.Config.MizuResourcesNamespace, kubernetes.ApiServerPodName)
allResourcesExist = checkResourceExist(kubernetes.ApiServerPodName, "service", exist, err) && allResourcesExist
allResourcesExist = checkPodResourcesExist(ctx, kubernetesProvider) && allResourcesExist
return allResourcesExist
}
func checkPodResourcesExist(ctx context.Context, kubernetesProvider *kubernetes.Provider) bool {
if pods, err := kubernetesProvider.ListPodsByAppLabel(ctx, config.Config.MizuResourcesNamespace, kubernetes.ApiServerPodName); err != nil {
logger.Log.Errorf("%v error checking if '%v' pod is running, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), kubernetes.ApiServerPodName, err)
return false
} else if len(pods) == 0 {
logger.Log.Errorf("%v '%v' pod doesn't exist", fmt.Sprintf(uiUtils.Red, "✗"), kubernetes.ApiServerPodName)
return false
} else if !kubernetes.IsPodRunning(&pods[0]) {
logger.Log.Errorf("%v '%v' pod not running", fmt.Sprintf(uiUtils.Red, "✗"), kubernetes.ApiServerPodName)
return false
}
logger.Log.Infof("%v '%v' pod running", fmt.Sprintf(uiUtils.Green, "√"), kubernetes.ApiServerPodName)
if pods, err := kubernetesProvider.ListPodsByAppLabel(ctx, config.Config.MizuResourcesNamespace, kubernetes.TapperPodName); err != nil {
logger.Log.Errorf("%v error checking if '%v' pods are running, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), kubernetes.TapperPodName, err)
return false
} else {
tappers := 0
notRunningTappers := 0
for _, pod := range pods {
tappers += 1
if !kubernetes.IsPodRunning(&pod) {
notRunningTappers += 1
}
}
if notRunningTappers > 0 {
logger.Log.Errorf("%v '%v' %v/%v pods are not running", fmt.Sprintf(uiUtils.Red, "✗"), kubernetes.TapperPodName, notRunningTappers, tappers)
return false
}
logger.Log.Infof("%v '%v' %v pods running", fmt.Sprintf(uiUtils.Green, "√"), kubernetes.TapperPodName, tappers)
return true
}
}
func checkResourceExist(resourceName string, resourceType string, exist bool, err error) bool {
if err != nil {
logger.Log.Errorf("%v error checking if '%v' %v exists, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), resourceName, resourceType, err)
return false
} else if !exist {
logger.Log.Errorf("%v '%v' %v doesn't exist", fmt.Sprintf(uiUtils.Red, "✗"), resourceName, resourceType)
return false
}
logger.Log.Infof("%v '%v' %v exists", fmt.Sprintf(uiUtils.Green, "√"), resourceName, resourceType)
return true
}

View File

@@ -0,0 +1,21 @@
package check
import (
"fmt"
"github.com/up9inc/mizu/cli/uiUtils"
"github.com/up9inc/mizu/shared/kubernetes"
"github.com/up9inc/mizu/shared/logger"
"github.com/up9inc/mizu/shared/semver"
)
func KubernetesVersion(kubernetesVersion *semver.SemVersion) bool {
logger.Log.Infof("\nkubernetes-version\n--------------------")
if err := kubernetes.ValidateKubernetesVersion(kubernetesVersion); err != nil {
logger.Log.Errorf("%v not running the minimum Kubernetes API version, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), err)
return false
}
logger.Log.Infof("%v is running the minimum Kubernetes API version", fmt.Sprintf(uiUtils.Green, "√"))
return true
}

View File

@@ -0,0 +1,83 @@
package check
import (
"context"
"fmt"
"github.com/up9inc/mizu/cli/apiserver"
"github.com/up9inc/mizu/cli/config"
"github.com/up9inc/mizu/cli/uiUtils"
"github.com/up9inc/mizu/shared/kubernetes"
"github.com/up9inc/mizu/shared/logger"
"regexp"
)
func ServerConnection(kubernetesProvider *kubernetes.Provider) bool {
logger.Log.Infof("\nAPI-server-connectivity\n--------------------")
serverUrl := fmt.Sprintf("http://%s", kubernetes.GetMizuApiServerProxiedHostAndPath(config.Config.Tap.GuiPort))
apiServerProvider := apiserver.NewProvider(serverUrl, 1, apiserver.DefaultTimeout)
if err := apiServerProvider.TestConnection(); err == nil {
logger.Log.Infof("%v found Mizu server tunnel available and connected successfully to API server", fmt.Sprintf(uiUtils.Green, "√"))
return true
}
connectedToApiServer := false
if err := checkProxy(serverUrl, kubernetesProvider); err != nil {
logger.Log.Errorf("%v couldn't connect to API server using proxy, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), err)
} else {
connectedToApiServer = true
logger.Log.Infof("%v connected successfully to API server using proxy", fmt.Sprintf(uiUtils.Green, "√"))
}
if err := checkPortForward(serverUrl, kubernetesProvider); err != nil {
logger.Log.Errorf("%v couldn't connect to API server using port-forward, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), err)
} else {
connectedToApiServer = true
logger.Log.Infof("%v connected successfully to API server using port-forward", fmt.Sprintf(uiUtils.Green, "√"))
}
return connectedToApiServer
}
func checkProxy(serverUrl string, kubernetesProvider *kubernetes.Provider) error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
httpServer, err := kubernetes.StartProxy(kubernetesProvider, config.Config.Tap.ProxyHost, config.Config.Tap.GuiPort, config.Config.MizuResourcesNamespace, kubernetes.ApiServerPodName, cancel)
if err != nil {
return err
}
apiServerProvider := apiserver.NewProvider(serverUrl, apiserver.DefaultRetries, apiserver.DefaultTimeout)
if err := apiServerProvider.TestConnection(); err != nil {
return err
}
if err := httpServer.Shutdown(ctx); err != nil {
logger.Log.Debugf("Error occurred while stopping proxy, err: %v", err)
}
return nil
}
func checkPortForward(serverUrl string, kubernetesProvider *kubernetes.Provider) error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
podRegex, _ := regexp.Compile(kubernetes.ApiServerPodName)
forwarder, err := kubernetes.NewPortForward(kubernetesProvider, config.Config.MizuResourcesNamespace, podRegex, config.Config.Tap.GuiPort, ctx, cancel)
if err != nil {
return err
}
apiServerProvider := apiserver.NewProvider(serverUrl, apiserver.DefaultRetries, apiserver.DefaultTimeout)
if err := apiServerProvider.TestConnection(); err != nil {
return err
}
forwarder.Close()
return nil
}

View File

@@ -4,20 +4,10 @@ import (
"context" "context"
"embed" "embed"
"fmt" "fmt"
core "k8s.io/api/core/v1" "github.com/up9inc/mizu/cli/cmd/check"
rbac "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/scheme"
"regexp"
"time"
"github.com/up9inc/mizu/cli/apiserver"
"github.com/up9inc/mizu/cli/config" "github.com/up9inc/mizu/cli/config"
"github.com/up9inc/mizu/cli/uiUtils" "github.com/up9inc/mizu/cli/uiUtils"
"github.com/up9inc/mizu/shared/kubernetes"
"github.com/up9inc/mizu/shared/logger" "github.com/up9inc/mizu/shared/logger"
"github.com/up9inc/mizu/shared/semver"
) )
var ( var (
@@ -31,27 +21,29 @@ func runMizuCheck() {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() // cancel will be called when this function exits defer cancel() // cancel will be called when this function exits
kubernetesProvider, kubernetesVersion, checkPassed := checkKubernetesApi() kubernetesProvider, kubernetesVersion, checkPassed := check.KubernetesApi()
if checkPassed { if checkPassed {
checkPassed = checkKubernetesVersion(kubernetesVersion) checkPassed = check.KubernetesVersion(kubernetesVersion)
} }
if config.Config.Check.PreTap { if config.Config.Check.PreTap {
if checkPassed { if checkPassed {
checkPassed = checkK8sTapPermissions(ctx, kubernetesProvider) checkPassed = check.TapKubernetesPermissions(ctx, embedFS, kubernetesProvider)
}
if checkPassed {
checkPassed = checkImagePullInCluster(ctx, kubernetesProvider)
} }
} else { } else {
if checkPassed { if checkPassed {
checkPassed = checkK8sResources(ctx, kubernetesProvider) checkPassed = check.KubernetesResources(ctx, kubernetesProvider)
} }
if checkPassed { if checkPassed {
checkPassed = checkServerConnection(kubernetesProvider) checkPassed = check.ServerConnection(kubernetesProvider)
}
}
if config.Config.Check.ImagePull {
if checkPassed {
checkPassed = check.ImagePullInCluster(ctx, kubernetesProvider)
} }
} }
@@ -61,365 +53,3 @@ func runMizuCheck() {
logger.Log.Errorf("\nStatus check results are %v", fmt.Sprintf(uiUtils.Red, "✗")) logger.Log.Errorf("\nStatus check results are %v", fmt.Sprintf(uiUtils.Red, "✗"))
} }
} }
func checkKubernetesApi() (*kubernetes.Provider, *semver.SemVersion, bool) {
logger.Log.Infof("\nkubernetes-api\n--------------------")
kubernetesProvider, err := kubernetes.NewProvider(config.Config.KubeConfigPath(), config.Config.KubeContext)
if err != nil {
logger.Log.Errorf("%v can't initialize the client, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), err)
return nil, nil, false
}
logger.Log.Infof("%v can initialize the client", fmt.Sprintf(uiUtils.Green, "√"))
kubernetesVersion, err := kubernetesProvider.GetKubernetesVersion()
if err != nil {
logger.Log.Errorf("%v can't query the Kubernetes API, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), err)
return nil, nil, false
}
logger.Log.Infof("%v can query the Kubernetes API", fmt.Sprintf(uiUtils.Green, "√"))
return kubernetesProvider, kubernetesVersion, true
}
func checkKubernetesVersion(kubernetesVersion *semver.SemVersion) bool {
logger.Log.Infof("\nkubernetes-version\n--------------------")
if err := kubernetes.ValidateKubernetesVersion(kubernetesVersion); err != nil {
logger.Log.Errorf("%v not running the minimum Kubernetes API version, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), err)
return false
}
logger.Log.Infof("%v is running the minimum Kubernetes API version", fmt.Sprintf(uiUtils.Green, "√"))
return true
}
func checkServerConnection(kubernetesProvider *kubernetes.Provider) bool {
logger.Log.Infof("\nAPI-server-connectivity\n--------------------")
serverUrl := GetApiServerUrl(config.Config.Tap.GuiPort)
apiServerProvider := apiserver.NewProvider(serverUrl, 1, apiserver.DefaultTimeout)
if err := apiServerProvider.TestConnection(); err == nil {
logger.Log.Infof("%v found Mizu server tunnel available and connected successfully to API server", fmt.Sprintf(uiUtils.Green, "√"))
return true
}
connectedToApiServer := false
if err := checkProxy(serverUrl, kubernetesProvider); err != nil {
logger.Log.Errorf("%v couldn't connect to API server using proxy, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), err)
} else {
connectedToApiServer = true
logger.Log.Infof("%v connected successfully to API server using proxy", fmt.Sprintf(uiUtils.Green, "√"))
}
if err := checkPortForward(serverUrl, kubernetesProvider); err != nil {
logger.Log.Errorf("%v couldn't connect to API server using port-forward, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), err)
} else {
connectedToApiServer = true
logger.Log.Infof("%v connected successfully to API server using port-forward", fmt.Sprintf(uiUtils.Green, "√"))
}
return connectedToApiServer
}
func checkProxy(serverUrl string, kubernetesProvider *kubernetes.Provider) error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
httpServer, err := kubernetes.StartProxy(kubernetesProvider, config.Config.Tap.ProxyHost, config.Config.Tap.GuiPort, config.Config.MizuResourcesNamespace, kubernetes.ApiServerPodName, cancel)
if err != nil {
return err
}
apiServerProvider := apiserver.NewProvider(serverUrl, apiserver.DefaultRetries, apiserver.DefaultTimeout)
if err := apiServerProvider.TestConnection(); err != nil {
return err
}
if err := httpServer.Shutdown(ctx); err != nil {
logger.Log.Debugf("Error occurred while stopping proxy, err: %v", err)
}
return nil
}
func checkPortForward(serverUrl string, kubernetesProvider *kubernetes.Provider) error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
podRegex, _ := regexp.Compile(kubernetes.ApiServerPodName)
forwarder, err := kubernetes.NewPortForward(kubernetesProvider, config.Config.MizuResourcesNamespace, podRegex, config.Config.Tap.GuiPort, ctx, cancel)
if err != nil {
return err
}
apiServerProvider := apiserver.NewProvider(serverUrl, apiserver.DefaultRetries, apiserver.DefaultTimeout)
if err := apiServerProvider.TestConnection(); err != nil {
return err
}
forwarder.Close()
return nil
}
func checkK8sResources(ctx context.Context, kubernetesProvider *kubernetes.Provider) bool {
logger.Log.Infof("\nk8s-components\n--------------------")
exist, err := kubernetesProvider.DoesNamespaceExist(ctx, config.Config.MizuResourcesNamespace)
allResourcesExist := checkResourceExist(config.Config.MizuResourcesNamespace, "namespace", exist, err)
exist, err = kubernetesProvider.DoesConfigMapExist(ctx, config.Config.MizuResourcesNamespace, kubernetes.ConfigMapName)
allResourcesExist = checkResourceExist(kubernetes.ConfigMapName, "config map", exist, err) && allResourcesExist
exist, err = kubernetesProvider.DoesServiceAccountExist(ctx, config.Config.MizuResourcesNamespace, kubernetes.ServiceAccountName)
allResourcesExist = checkResourceExist(kubernetes.ServiceAccountName, "service account", exist, err) && allResourcesExist
if config.Config.IsNsRestrictedMode() {
exist, err = kubernetesProvider.DoesRoleExist(ctx, config.Config.MizuResourcesNamespace, kubernetes.RoleName)
allResourcesExist = checkResourceExist(kubernetes.RoleName, "role", exist, err) && allResourcesExist
exist, err = kubernetesProvider.DoesRoleBindingExist(ctx, config.Config.MizuResourcesNamespace, kubernetes.RoleBindingName)
allResourcesExist = checkResourceExist(kubernetes.RoleBindingName, "role binding", exist, err) && allResourcesExist
} else {
exist, err = kubernetesProvider.DoesClusterRoleExist(ctx, kubernetes.ClusterRoleName)
allResourcesExist = checkResourceExist(kubernetes.ClusterRoleName, "cluster role", exist, err) && allResourcesExist
exist, err = kubernetesProvider.DoesClusterRoleBindingExist(ctx, kubernetes.ClusterRoleBindingName)
allResourcesExist = checkResourceExist(kubernetes.ClusterRoleBindingName, "cluster role binding", exist, err) && allResourcesExist
}
exist, err = kubernetesProvider.DoesServiceExist(ctx, config.Config.MizuResourcesNamespace, kubernetes.ApiServerPodName)
allResourcesExist = checkResourceExist(kubernetes.ApiServerPodName, "service", exist, err) && allResourcesExist
allResourcesExist = checkPodResourcesExist(ctx, kubernetesProvider) && allResourcesExist
return allResourcesExist
}
func checkPodResourcesExist(ctx context.Context, kubernetesProvider *kubernetes.Provider) bool {
if pods, err := kubernetesProvider.ListPodsByAppLabel(ctx, config.Config.MizuResourcesNamespace, kubernetes.ApiServerPodName); err != nil {
logger.Log.Errorf("%v error checking if '%v' pod is running, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), kubernetes.ApiServerPodName, err)
return false
} else if len(pods) == 0 {
logger.Log.Errorf("%v '%v' pod doesn't exist", fmt.Sprintf(uiUtils.Red, "✗"), kubernetes.ApiServerPodName)
return false
} else if !kubernetes.IsPodRunning(&pods[0]) {
logger.Log.Errorf("%v '%v' pod not running", fmt.Sprintf(uiUtils.Red, "✗"), kubernetes.ApiServerPodName)
return false
}
logger.Log.Infof("%v '%v' pod running", fmt.Sprintf(uiUtils.Green, "√"), kubernetes.ApiServerPodName)
if pods, err := kubernetesProvider.ListPodsByAppLabel(ctx, config.Config.MizuResourcesNamespace, kubernetes.TapperPodName); err != nil {
logger.Log.Errorf("%v error checking if '%v' pods are running, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), kubernetes.TapperPodName, err)
return false
} else {
tappers := 0
notRunningTappers := 0
for _, pod := range pods {
tappers += 1
if !kubernetes.IsPodRunning(&pod) {
notRunningTappers += 1
}
}
if notRunningTappers > 0 {
logger.Log.Errorf("%v '%v' %v/%v pods are not running", fmt.Sprintf(uiUtils.Red, "✗"), kubernetes.TapperPodName, notRunningTappers, tappers)
return false
}
logger.Log.Infof("%v '%v' %v pods running", fmt.Sprintf(uiUtils.Green, "√"), kubernetes.TapperPodName, tappers)
return true
}
}
func checkResourceExist(resourceName string, resourceType string, exist bool, err error) bool {
if err != nil {
logger.Log.Errorf("%v error checking if '%v' %v exists, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), resourceName, resourceType, err)
return false
} else if !exist {
logger.Log.Errorf("%v '%v' %v doesn't exist", fmt.Sprintf(uiUtils.Red, "✗"), resourceName, resourceType)
return false
}
logger.Log.Infof("%v '%v' %v exists", fmt.Sprintf(uiUtils.Green, "√"), resourceName, resourceType)
return true
}
func checkK8sTapPermissions(ctx context.Context, kubernetesProvider *kubernetes.Provider) bool {
logger.Log.Infof("\nkubernetes-permissions\n--------------------")
var filePath string
if config.Config.IsNsRestrictedMode() {
filePath = "permissionFiles/permissions-ns-tap.yaml"
} else {
filePath = "permissionFiles/permissions-all-namespaces-tap.yaml"
}
data, err := embedFS.ReadFile(filePath)
if err != nil {
logger.Log.Errorf("%v error while checking kubernetes permissions, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), err)
return false
}
obj, err := getDecodedObject(data)
if err != nil {
logger.Log.Errorf("%v error while checking kubernetes permissions, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), err)
return false
}
var rules []rbac.PolicyRule
if config.Config.IsNsRestrictedMode() {
rules = obj.(*rbac.Role).Rules
} else {
rules = obj.(*rbac.ClusterRole).Rules
}
return checkPermissions(ctx, kubernetesProvider, rules)
}
func getDecodedObject(data []byte) (runtime.Object, error) {
decode := scheme.Codecs.UniversalDeserializer().Decode
obj, _, err := decode(data, nil, nil)
if err != nil {
return nil, err
}
return obj, nil
}
func checkPermissions(ctx context.Context, kubernetesProvider *kubernetes.Provider, rules []rbac.PolicyRule) bool {
permissionsExist := true
for _, rule := range rules {
for _, group := range rule.APIGroups {
for _, resource := range rule.Resources {
for _, verb := range rule.Verbs {
exist, err := kubernetesProvider.CanI(ctx, config.Config.MizuResourcesNamespace, resource, verb, group)
permissionsExist = checkPermissionExist(group, resource, verb, exist, err) && permissionsExist
}
}
}
}
return permissionsExist
}
func checkPermissionExist(group string, resource string, verb string, exist bool, err error) bool {
if err != nil {
logger.Log.Errorf("%v error checking permission for %v %v in group '%v', err: %v", fmt.Sprintf(uiUtils.Red, "✗"), verb, resource, group, err)
return false
} else if !exist {
logger.Log.Errorf("%v can't %v %v in group '%v'", fmt.Sprintf(uiUtils.Red, "✗"), verb, resource, group)
return false
}
logger.Log.Infof("%v can %v %v in group '%v'", fmt.Sprintf(uiUtils.Green, "√"), verb, resource, group)
return true
}
func checkImagePullInCluster(ctx context.Context, kubernetesProvider *kubernetes.Provider) bool {
logger.Log.Infof("\nimage-pull-in-cluster\n--------------------")
podName := "image-pull-in-cluster"
defer removeImagePullInClusterResources(ctx, kubernetesProvider, podName)
if err := createImagePullInClusterResources(ctx, kubernetesProvider, podName); err != nil {
logger.Log.Errorf("%v error while creating image pull in cluster resources, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), err)
return false
}
if err := checkImagePulled(ctx, kubernetesProvider, podName); err != nil {
logger.Log.Errorf("%v cluster is not able to pull mizu containers from docker hub, err: %v", fmt.Sprintf(uiUtils.Red, "✗"), err)
return false
}
logger.Log.Infof("%v cluster is able to pull mizu containers from docker hub", fmt.Sprintf(uiUtils.Green, "√"))
return true
}
func checkImagePulled(ctx context.Context, kubernetesProvider *kubernetes.Provider, podName string) error {
podExactRegex := regexp.MustCompile(fmt.Sprintf("^%s$", podName))
podWatchHelper := kubernetes.NewPodWatchHelper(kubernetesProvider, podExactRegex)
eventChan, errorChan := kubernetes.FilteredWatch(ctx, podWatchHelper, []string{config.Config.MizuResourcesNamespace}, podWatchHelper)
timeAfter := time.After(30 * time.Second)
for {
select {
case wEvent, ok := <-eventChan:
if !ok {
eventChan = nil
continue
}
pod, err := wEvent.ToPod()
if err != nil {
return err
}
if pod.Status.Phase == core.PodRunning {
return nil
}
case err, ok := <-errorChan:
if !ok {
errorChan = nil
continue
}
return err
case <-timeAfter:
return fmt.Errorf("image not pulled in time")
}
}
}
func removeImagePullInClusterResources(ctx context.Context, kubernetesProvider *kubernetes.Provider, podName string) {
if err := kubernetesProvider.RemovePod(ctx, config.Config.MizuResourcesNamespace, podName); err != nil {
logger.Log.Debugf("error while removing image pull in cluster resources, err: %v", err)
}
if !config.Config.IsNsRestrictedMode() {
if err := kubernetesProvider.RemoveNamespace(ctx, config.Config.MizuResourcesNamespace); err != nil {
logger.Log.Debugf("error while removing image pull in cluster resources, err: %v", err)
}
}
}
func createImagePullInClusterResources(ctx context.Context, kubernetesProvider *kubernetes.Provider, podName string) error {
if !config.Config.IsNsRestrictedMode() {
if _, err := kubernetesProvider.CreateNamespace(ctx, config.Config.MizuResourcesNamespace); err != nil {
return err
}
}
var zero int64
pod := &core.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: podName,
},
Spec: core.PodSpec{
Containers: []core.Container{
{
Name: "probe",
Image: "up9inc/busybox",
ImagePullPolicy: "Always",
Command: []string{"cat"},
Stdin: true,
},
},
TerminationGracePeriodSeconds: &zero,
},
}
if _, err := kubernetesProvider.CreatePod(ctx, config.Config.MizuResourcesNamespace, pod); err != nil {
return err
}
return nil
}

View File

@@ -1,9 +1,11 @@
package configStructs package configStructs
const ( const (
PreTapCheckName = "pre-tap" PreTapCheckName = "pre-tap"
ImagePullCheckName = "image-pull"
) )
type CheckConfig struct { type CheckConfig struct {
PreTap bool `yaml:"pre-tap"` PreTap bool `yaml:"pre-tap"`
ImagePull bool `yaml:"image-pull"`
} }

View File

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

View File

@@ -600,8 +600,8 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/up9inc/basenine/server/lib v0.0.0-20220315070758-3a76cfc4378e h1:reG/QwyxdfvGObfdrae7DZc3rTMiGwQ6S/4PRkwtBoE= github.com/up9inc/basenine/server/lib v0.0.0-20220317230530-8472d80307f6 h1:+RZTD+HdfIW2SMbc65yWkruTY+g5/1Av074m62A74ls=
github.com/up9inc/basenine/server/lib v0.0.0-20220315070758-3a76cfc4378e/go.mod h1:ZIkxWiJm65jYQIso9k+OZKhR7gQ1we2jNyE2kQX9IQI= github.com/up9inc/basenine/server/lib v0.0.0-20220317230530-8472d80307f6/go.mod h1:ZIkxWiJm65jYQIso9k+OZKhR7gQ1we2jNyE2kQX9IQI=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk=

View File

@@ -94,10 +94,6 @@ func (tapperSyncer *MizuTapperSyncer) watchTapperPods() {
continue continue
} }
if tapperSyncer.startTime.After(pod.CreationTimestamp.Time) {
continue
}
logger.Log.Debugf("Watching tapper pods loop, tapper: %v, node: %v, status: %v", pod.Name, pod.Spec.NodeName, pod.Status.Phase) logger.Log.Debugf("Watching tapper pods loop, tapper: %v, node: %v, status: %v", pod.Name, pod.Spec.NodeName, pod.Status.Phase)
if pod.Spec.NodeName != "" { if pod.Spec.NodeName != "" {
tapperStatus := shared.TapperStatus{TapperName: pod.Name, NodeName: pod.Spec.NodeName, Status: string(pod.Status.Phase)} tapperStatus := shared.TapperStatus{TapperName: pod.Name, NodeName: pod.Spec.NodeName, Status: string(pod.Status.Phase)}
@@ -137,10 +133,6 @@ func (tapperSyncer *MizuTapperSyncer) watchTapperEvents() {
continue continue
} }
if tapperSyncer.startTime.After(event.CreationTimestamp.Time) {
continue
}
logger.Log.Debugf( logger.Log.Debugf(
fmt.Sprintf("Watching tapper events loop, event %s, time: %v, resource: %s (%s), reason: %s, note: %s", fmt.Sprintf("Watching tapper events loop, event %s, time: %v, resource: %s (%s), reason: %s, note: %s",
event.Name, event.Name,

View File

@@ -48,6 +48,16 @@ type Extension struct {
Dissector Dissector Dissector Dissector
} }
type Capture string
const (
UndefinedCapture Capture = ""
Pcap Capture = "pcap"
Envoy Capture = "envoy"
Linkerd Capture = "linkerd"
Ebpf Capture = "ebpf"
)
type ConnectionInfo struct { type ConnectionInfo struct {
ClientIP string ClientIP string
ClientPort string ClientPort string
@@ -84,6 +94,7 @@ type RequestResponsePair struct {
// `Protocol` is modified in the later stages of data propagation. Therefore it's not a pointer. // `Protocol` is modified in the later stages of data propagation. Therefore it's not a pointer.
type OutputChannelItem struct { type OutputChannelItem struct {
Protocol Protocol Protocol Protocol
Capture Capture
Timestamp int64 Timestamp int64
ConnectionInfo *ConnectionInfo ConnectionInfo *ConnectionInfo
Pair *RequestResponsePair Pair *RequestResponsePair
@@ -102,7 +113,7 @@ type SuperIdentifier struct {
type Dissector interface { type Dissector interface {
Register(*Extension) Register(*Extension)
Ping() Ping()
Dissect(b *bufio.Reader, isClient bool, tcpID *TcpID, counterPair *CounterPair, superTimer *SuperTimer, superIdentifier *SuperIdentifier, emitter Emitter, options *TrafficFilteringOptions, reqResMatcher RequestResponseMatcher) error Dissect(b *bufio.Reader, capture Capture, isClient bool, tcpID *TcpID, counterPair *CounterPair, superTimer *SuperTimer, superIdentifier *SuperIdentifier, emitter Emitter, options *TrafficFilteringOptions, reqResMatcher RequestResponseMatcher) error
Analyze(item *OutputChannelItem, resolvedSource string, resolvedDestination string, namespace string) *Entry Analyze(item *OutputChannelItem, resolvedSource string, resolvedDestination string, namespace string) *Entry
Summarize(entry *Entry) *BaseEntry Summarize(entry *Entry) *BaseEntry
Represent(request map[string]interface{}, response map[string]interface{}) (object []byte, bodySize int64, err error) Represent(request map[string]interface{}, response map[string]interface{}) (object []byte, bodySize int64, err error)
@@ -132,6 +143,7 @@ func (e *Emitting) Emit(item *OutputChannelItem) {
type Entry struct { type Entry struct {
Id uint `json:"id"` Id uint `json:"id"`
Protocol Protocol `json:"proto"` Protocol Protocol `json:"proto"`
Capture Capture `json:"capture"`
Source *TCP `json:"src"` Source *TCP `json:"src"`
Destination *TCP `json:"dst"` Destination *TCP `json:"dst"`
Namespace string `json:"namespace,omitempty"` Namespace string `json:"namespace,omitempty"`
@@ -162,6 +174,7 @@ type EntryWrapper struct {
type BaseEntry struct { type BaseEntry struct {
Id uint `json:"id"` Id uint `json:"id"`
Protocol Protocol `json:"proto,omitempty"` Protocol Protocol `json:"proto,omitempty"`
Capture Capture `json:"capture"`
Summary string `json:"summary,omitempty"` Summary string `json:"summary,omitempty"`
SummaryQuery string `json:"summaryQuery,omitempty"` SummaryQuery string `json:"summaryQuery,omitempty"`
Status int `json:"status"` Status int `json:"status"`

View File

@@ -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/expect3/amqp/\* expect @[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect4/amqp/\* expect

View File

@@ -94,7 +94,7 @@ type AMQPWrapper struct {
Details interface{} `json:"details"` Details interface{} `json:"details"`
} }
func emitAMQP(event interface{}, _type string, method string, connectionInfo *api.ConnectionInfo, captureTime time.Time, emitter api.Emitter) { func emitAMQP(event interface{}, _type string, method string, connectionInfo *api.ConnectionInfo, captureTime time.Time, emitter api.Emitter, capture api.Capture) {
request := &api.GenericMessage{ request := &api.GenericMessage{
IsRequest: true, IsRequest: true,
CaptureTime: captureTime, CaptureTime: captureTime,
@@ -108,6 +108,7 @@ func emitAMQP(event interface{}, _type string, method string, connectionInfo *ap
} }
item := &api.OutputChannelItem{ item := &api.OutputChannelItem{
Protocol: protocol, Protocol: protocol,
Capture: capture,
Timestamp: captureTime.UnixNano() / int64(time.Millisecond), Timestamp: captureTime.UnixNano() / int64(time.Millisecond),
ConnectionInfo: connectionInfo, ConnectionInfo: connectionInfo,
Pair: &api.RequestResponsePair{ Pair: &api.RequestResponsePair{

View File

@@ -39,7 +39,7 @@ func (d dissecting) Ping() {
const amqpRequest string = "amqp_request" const amqpRequest string = "amqp_request"
func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, counterPair *api.CounterPair, superTimer *api.SuperTimer, superIdentifier *api.SuperIdentifier, emitter api.Emitter, options *api.TrafficFilteringOptions, _reqResMatcher api.RequestResponseMatcher) error { func (d dissecting) Dissect(b *bufio.Reader, capture api.Capture, isClient bool, tcpID *api.TcpID, counterPair *api.CounterPair, superTimer *api.SuperTimer, superIdentifier *api.SuperIdentifier, emitter api.Emitter, options *api.TrafficFilteringOptions, _reqResMatcher api.RequestResponseMatcher) error {
r := AmqpReader{b} r := AmqpReader{b}
var remaining int var remaining int
@@ -113,11 +113,11 @@ func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, co
case *BasicPublish: case *BasicPublish:
eventBasicPublish.Body = f.Body eventBasicPublish.Body = f.Body
superIdentifier.Protocol = &protocol superIdentifier.Protocol = &protocol
emitAMQP(*eventBasicPublish, amqpRequest, basicMethodMap[40], connectionInfo, superTimer.CaptureTime, emitter) emitAMQP(*eventBasicPublish, amqpRequest, basicMethodMap[40], connectionInfo, superTimer.CaptureTime, emitter, capture)
case *BasicDeliver: case *BasicDeliver:
eventBasicDeliver.Body = f.Body eventBasicDeliver.Body = f.Body
superIdentifier.Protocol = &protocol superIdentifier.Protocol = &protocol
emitAMQP(*eventBasicDeliver, amqpRequest, basicMethodMap[60], connectionInfo, superTimer.CaptureTime, emitter) emitAMQP(*eventBasicDeliver, amqpRequest, basicMethodMap[60], connectionInfo, superTimer.CaptureTime, emitter, capture)
} }
case *MethodFrame: case *MethodFrame:
@@ -138,7 +138,7 @@ func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, co
Arguments: m.Arguments, Arguments: m.Arguments,
} }
superIdentifier.Protocol = &protocol superIdentifier.Protocol = &protocol
emitAMQP(*eventQueueBind, amqpRequest, queueMethodMap[20], connectionInfo, superTimer.CaptureTime, emitter) emitAMQP(*eventQueueBind, amqpRequest, queueMethodMap[20], connectionInfo, superTimer.CaptureTime, emitter, capture)
case *BasicConsume: case *BasicConsume:
eventBasicConsume := &BasicConsume{ eventBasicConsume := &BasicConsume{
@@ -151,7 +151,7 @@ func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, co
Arguments: m.Arguments, Arguments: m.Arguments,
} }
superIdentifier.Protocol = &protocol superIdentifier.Protocol = &protocol
emitAMQP(*eventBasicConsume, amqpRequest, basicMethodMap[20], connectionInfo, superTimer.CaptureTime, emitter) emitAMQP(*eventBasicConsume, amqpRequest, basicMethodMap[20], connectionInfo, superTimer.CaptureTime, emitter, capture)
case *BasicDeliver: case *BasicDeliver:
eventBasicDeliver.ConsumerTag = m.ConsumerTag eventBasicDeliver.ConsumerTag = m.ConsumerTag
@@ -171,7 +171,7 @@ func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, co
Arguments: m.Arguments, Arguments: m.Arguments,
} }
superIdentifier.Protocol = &protocol superIdentifier.Protocol = &protocol
emitAMQP(*eventQueueDeclare, amqpRequest, queueMethodMap[10], connectionInfo, superTimer.CaptureTime, emitter) emitAMQP(*eventQueueDeclare, amqpRequest, queueMethodMap[10], connectionInfo, superTimer.CaptureTime, emitter, capture)
case *ExchangeDeclare: case *ExchangeDeclare:
eventExchangeDeclare := &ExchangeDeclare{ eventExchangeDeclare := &ExchangeDeclare{
@@ -185,7 +185,7 @@ func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, co
Arguments: m.Arguments, Arguments: m.Arguments,
} }
superIdentifier.Protocol = &protocol superIdentifier.Protocol = &protocol
emitAMQP(*eventExchangeDeclare, amqpRequest, exchangeMethodMap[10], connectionInfo, superTimer.CaptureTime, emitter) emitAMQP(*eventExchangeDeclare, amqpRequest, exchangeMethodMap[10], connectionInfo, superTimer.CaptureTime, emitter, capture)
case *ConnectionStart: case *ConnectionStart:
eventConnectionStart := &ConnectionStart{ eventConnectionStart := &ConnectionStart{
@@ -196,7 +196,7 @@ func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, co
Locales: m.Locales, Locales: m.Locales,
} }
superIdentifier.Protocol = &protocol superIdentifier.Protocol = &protocol
emitAMQP(*eventConnectionStart, amqpRequest, connectionMethodMap[10], connectionInfo, superTimer.CaptureTime, emitter) emitAMQP(*eventConnectionStart, amqpRequest, connectionMethodMap[10], connectionInfo, superTimer.CaptureTime, emitter, capture)
case *ConnectionClose: case *ConnectionClose:
eventConnectionClose := &ConnectionClose{ eventConnectionClose := &ConnectionClose{
@@ -206,7 +206,7 @@ func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, co
MethodId: m.MethodId, MethodId: m.MethodId,
} }
superIdentifier.Protocol = &protocol superIdentifier.Protocol = &protocol
emitAMQP(*eventConnectionClose, amqpRequest, connectionMethodMap[50], connectionInfo, superTimer.CaptureTime, emitter) emitAMQP(*eventConnectionClose, amqpRequest, connectionMethodMap[50], connectionInfo, superTimer.CaptureTime, emitter, capture)
} }
default: default:
@@ -222,6 +222,7 @@ func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string,
reqDetails["method"] = request["method"] reqDetails["method"] = request["method"]
return &api.Entry{ return &api.Entry{
Protocol: protocol, Protocol: protocol,
Capture: item.Capture,
Source: &api.TCP{ Source: &api.TCP{
Name: resolvedSource, Name: resolvedSource,
IP: item.ConnectionInfo.ClientIP, IP: item.ConnectionInfo.ClientIP,
@@ -283,6 +284,7 @@ func (d dissecting) Summarize(entry *api.Entry) *api.BaseEntry {
return &api.BaseEntry{ return &api.BaseEntry{
Id: entry.Id, Id: entry.Id,
Protocol: entry.Protocol, Protocol: entry.Protocol,
Capture: entry.Capture,
Summary: summary, Summary: summary,
SummaryQuery: summaryQuery, SummaryQuery: summaryQuery,
Status: 0, Status: 0,

View File

@@ -122,7 +122,7 @@ func TestDissect(t *testing.T) {
DstPort: "2", DstPort: "2",
} }
reqResMatcher := dissector.NewResponseRequestMatcher() reqResMatcher := dissector.NewResponseRequestMatcher()
err = dissector.Dissect(bufferClient, true, tcpIDClient, counterPair, &api.SuperTimer{}, superIdentifier, emitter, options, reqResMatcher) err = dissector.Dissect(bufferClient, api.Pcap, true, tcpIDClient, counterPair, &api.SuperTimer{}, superIdentifier, emitter, options, reqResMatcher)
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF { if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
panic(err) panic(err)
} }
@@ -140,7 +140,7 @@ func TestDissect(t *testing.T) {
SrcPort: "2", SrcPort: "2",
DstPort: "1", DstPort: "1",
} }
err = dissector.Dissect(bufferServer, false, tcpIDServer, counterPair, &api.SuperTimer{}, superIdentifier, emitter, options, reqResMatcher) err = dissector.Dissect(bufferServer, api.Pcap, false, tcpIDServer, counterPair, &api.SuperTimer{}, superIdentifier, emitter, options, reqResMatcher)
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF { if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
panic(err) panic(err)
} }

View File

@@ -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/expect3/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/expect4/http/\* expect

View File

@@ -47,7 +47,7 @@ func replaceForwardedFor(item *api.OutputChannelItem) {
item.ConnectionInfo.ClientPort = "" item.ConnectionInfo.ClientPort = ""
} }
func handleHTTP2Stream(http2Assembler *Http2Assembler, tcpID *api.TcpID, superTimer *api.SuperTimer, emitter api.Emitter, options *api.TrafficFilteringOptions, reqResMatcher *requestResponseMatcher) error { func handleHTTP2Stream(http2Assembler *Http2Assembler, capture api.Capture, tcpID *api.TcpID, superTimer *api.SuperTimer, emitter api.Emitter, options *api.TrafficFilteringOptions, reqResMatcher *requestResponseMatcher) error {
streamID, messageHTTP1, isGrpc, err := http2Assembler.readMessage() streamID, messageHTTP1, isGrpc, err := http2Assembler.readMessage()
if err != nil { if err != nil {
return err return err
@@ -104,13 +104,14 @@ func handleHTTP2Stream(http2Assembler *Http2Assembler, tcpID *api.TcpID, superTi
} else { } else {
item.Protocol = http2Protocol item.Protocol = http2Protocol
} }
item.Capture = capture
filterAndEmit(item, emitter, options) filterAndEmit(item, emitter, options)
} }
return nil return nil
} }
func handleHTTP1ClientStream(b *bufio.Reader, tcpID *api.TcpID, counterPair *api.CounterPair, superTimer *api.SuperTimer, emitter api.Emitter, options *api.TrafficFilteringOptions, reqResMatcher *requestResponseMatcher) (switchingProtocolsHTTP2 bool, req *http.Request, err error) { func handleHTTP1ClientStream(b *bufio.Reader, capture api.Capture, tcpID *api.TcpID, counterPair *api.CounterPair, superTimer *api.SuperTimer, emitter api.Emitter, options *api.TrafficFilteringOptions, reqResMatcher *requestResponseMatcher) (switchingProtocolsHTTP2 bool, req *http.Request, err error) {
req, err = http.ReadRequest(b) req, err = http.ReadRequest(b)
if err != nil { if err != nil {
return return
@@ -147,12 +148,13 @@ func handleHTTP1ClientStream(b *bufio.Reader, tcpID *api.TcpID, counterPair *api
ServerPort: tcpID.DstPort, ServerPort: tcpID.DstPort,
IsOutgoing: true, IsOutgoing: true,
} }
item.Capture = capture
filterAndEmit(item, emitter, options) filterAndEmit(item, emitter, options)
} }
return return
} }
func handleHTTP1ServerStream(b *bufio.Reader, tcpID *api.TcpID, counterPair *api.CounterPair, superTimer *api.SuperTimer, emitter api.Emitter, options *api.TrafficFilteringOptions, reqResMatcher *requestResponseMatcher) (switchingProtocolsHTTP2 bool, err error) { func handleHTTP1ServerStream(b *bufio.Reader, capture api.Capture, tcpID *api.TcpID, counterPair *api.CounterPair, superTimer *api.SuperTimer, emitter api.Emitter, options *api.TrafficFilteringOptions, reqResMatcher *requestResponseMatcher) (switchingProtocolsHTTP2 bool, err error) {
var res *http.Response var res *http.Response
res, err = http.ReadResponse(b, nil) res, err = http.ReadResponse(b, nil)
if err != nil { if err != nil {
@@ -190,6 +192,7 @@ func handleHTTP1ServerStream(b *bufio.Reader, tcpID *api.TcpID, counterPair *api
ServerPort: tcpID.SrcPort, ServerPort: tcpID.SrcPort,
IsOutgoing: false, IsOutgoing: false,
} }
item.Capture = capture
filterAndEmit(item, emitter, options) filterAndEmit(item, emitter, options)
} }
return return

View File

@@ -86,7 +86,7 @@ func (d dissecting) Ping() {
log.Printf("pong %s", http11protocol.Name) log.Printf("pong %s", http11protocol.Name)
} }
func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, counterPair *api.CounterPair, superTimer *api.SuperTimer, superIdentifier *api.SuperIdentifier, emitter api.Emitter, options *api.TrafficFilteringOptions, _reqResMatcher api.RequestResponseMatcher) error { func (d dissecting) Dissect(b *bufio.Reader, capture api.Capture, isClient bool, tcpID *api.TcpID, counterPair *api.CounterPair, superTimer *api.SuperTimer, superIdentifier *api.SuperIdentifier, emitter api.Emitter, options *api.TrafficFilteringOptions, _reqResMatcher api.RequestResponseMatcher) error {
reqResMatcher := _reqResMatcher.(*requestResponseMatcher) reqResMatcher := _reqResMatcher.(*requestResponseMatcher)
var err error var err error
@@ -121,7 +121,7 @@ func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, co
} }
if isHTTP2 { if isHTTP2 {
err = handleHTTP2Stream(http2Assembler, tcpID, superTimer, emitter, options, reqResMatcher) err = handleHTTP2Stream(http2Assembler, capture, tcpID, superTimer, emitter, options, reqResMatcher)
if err == io.EOF || err == io.ErrUnexpectedEOF { if err == io.EOF || err == io.ErrUnexpectedEOF {
break break
} else if err != nil { } else if err != nil {
@@ -130,7 +130,7 @@ func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, co
superIdentifier.Protocol = &http11protocol superIdentifier.Protocol = &http11protocol
} else if isClient { } else if isClient {
var req *http.Request var req *http.Request
switchingProtocolsHTTP2, req, err = handleHTTP1ClientStream(b, tcpID, counterPair, superTimer, emitter, options, reqResMatcher) switchingProtocolsHTTP2, req, err = handleHTTP1ClientStream(b, capture, tcpID, counterPair, superTimer, emitter, options, reqResMatcher)
if err == io.EOF || err == io.ErrUnexpectedEOF { if err == io.EOF || err == io.ErrUnexpectedEOF {
break break
} else if err != nil { } else if err != nil {
@@ -157,11 +157,12 @@ func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, co
ServerPort: tcpID.DstPort, ServerPort: tcpID.DstPort,
IsOutgoing: true, IsOutgoing: true,
} }
item.Capture = capture
filterAndEmit(item, emitter, options) filterAndEmit(item, emitter, options)
} }
} }
} else { } else {
switchingProtocolsHTTP2, err = handleHTTP1ServerStream(b, tcpID, counterPair, superTimer, emitter, options, reqResMatcher) switchingProtocolsHTTP2, err = handleHTTP1ServerStream(b, capture, tcpID, counterPair, superTimer, emitter, options, reqResMatcher)
if err == io.EOF || err == io.ErrUnexpectedEOF { if err == io.EOF || err == io.ErrUnexpectedEOF {
break break
} else if err != nil { } else if err != nil {
@@ -259,6 +260,7 @@ func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string,
httpPair, _ := json.Marshal(item.Pair) httpPair, _ := json.Marshal(item.Pair)
return &api.Entry{ return &api.Entry{
Protocol: item.Protocol, Protocol: item.Protocol,
Capture: item.Capture,
Source: &api.TCP{ Source: &api.TCP{
Name: resolvedSource, Name: resolvedSource,
IP: item.ConnectionInfo.ClientIP, IP: item.ConnectionInfo.ClientIP,
@@ -291,6 +293,7 @@ func (d dissecting) Summarize(entry *api.Entry) *api.BaseEntry {
return &api.BaseEntry{ return &api.BaseEntry{
Id: entry.Id, Id: entry.Id,
Protocol: entry.Protocol, Protocol: entry.Protocol,
Capture: entry.Capture,
Summary: summary, Summary: summary,
SummaryQuery: summaryQuery, SummaryQuery: summaryQuery,
Status: status, Status: status,

View File

@@ -124,7 +124,7 @@ func TestDissect(t *testing.T) {
DstPort: "2", DstPort: "2",
} }
reqResMatcher := dissector.NewResponseRequestMatcher() reqResMatcher := dissector.NewResponseRequestMatcher()
err = dissector.Dissect(bufferClient, true, tcpIDClient, counterPair, &api.SuperTimer{}, superIdentifier, emitter, options, reqResMatcher) err = dissector.Dissect(bufferClient, api.Pcap, true, tcpIDClient, counterPair, &api.SuperTimer{}, superIdentifier, emitter, options, reqResMatcher)
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF { if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
panic(err) panic(err)
} }
@@ -142,7 +142,7 @@ func TestDissect(t *testing.T) {
SrcPort: "2", SrcPort: "2",
DstPort: "1", DstPort: "1",
} }
err = dissector.Dissect(bufferServer, false, tcpIDServer, counterPair, &api.SuperTimer{}, superIdentifier, emitter, options, reqResMatcher) err = dissector.Dissect(bufferServer, api.Pcap, false, tcpIDServer, counterPair, &api.SuperTimer{}, superIdentifier, emitter, options, reqResMatcher)
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF { if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
panic(err) panic(err)
} }

View File

@@ -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/expect3/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/expect4/kafka/\* expect

View File

@@ -35,7 +35,7 @@ func (d dissecting) Ping() {
log.Printf("pong %s", _protocol.Name) log.Printf("pong %s", _protocol.Name)
} }
func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, counterPair *api.CounterPair, superTimer *api.SuperTimer, superIdentifier *api.SuperIdentifier, emitter api.Emitter, options *api.TrafficFilteringOptions, _reqResMatcher api.RequestResponseMatcher) error { func (d dissecting) Dissect(b *bufio.Reader, capture api.Capture, isClient bool, tcpID *api.TcpID, counterPair *api.CounterPair, superTimer *api.SuperTimer, superIdentifier *api.SuperIdentifier, emitter api.Emitter, options *api.TrafficFilteringOptions, _reqResMatcher api.RequestResponseMatcher) error {
reqResMatcher := _reqResMatcher.(*requestResponseMatcher) reqResMatcher := _reqResMatcher.(*requestResponseMatcher)
for { for {
if superIdentifier.Protocol != nil && superIdentifier.Protocol != &_protocol { if superIdentifier.Protocol != nil && superIdentifier.Protocol != &_protocol {
@@ -49,7 +49,7 @@ func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, co
} }
superIdentifier.Protocol = &_protocol superIdentifier.Protocol = &_protocol
} else { } else {
err := ReadResponse(b, tcpID, counterPair, superTimer, emitter, reqResMatcher) err := ReadResponse(b, capture, tcpID, counterPair, superTimer, emitter, reqResMatcher)
if err != nil { if err != nil {
return err return err
} }
@@ -68,6 +68,7 @@ func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string,
} }
return &api.Entry{ return &api.Entry{
Protocol: _protocol, Protocol: _protocol,
Capture: item.Capture,
Source: &api.TCP{ Source: &api.TCP{
Name: resolvedSource, Name: resolvedSource,
IP: item.ConnectionInfo.ClientIP, IP: item.ConnectionInfo.ClientIP,
@@ -190,6 +191,7 @@ func (d dissecting) Summarize(entry *api.Entry) *api.BaseEntry {
return &api.BaseEntry{ return &api.BaseEntry{
Id: entry.Id, Id: entry.Id,
Protocol: entry.Protocol, Protocol: entry.Protocol,
Capture: entry.Capture,
Summary: summary, Summary: summary,
SummaryQuery: summaryQuery, SummaryQuery: summaryQuery,
Status: status, Status: status,

View File

@@ -123,7 +123,7 @@ func TestDissect(t *testing.T) {
} }
reqResMatcher := dissector.NewResponseRequestMatcher() reqResMatcher := dissector.NewResponseRequestMatcher()
reqResMatcher.SetMaxTry(10) reqResMatcher.SetMaxTry(10)
err = dissector.Dissect(bufferClient, true, tcpIDClient, counterPair, &api.SuperTimer{}, superIdentifier, emitter, options, reqResMatcher) err = dissector.Dissect(bufferClient, api.Pcap, true, tcpIDClient, counterPair, &api.SuperTimer{}, superIdentifier, emitter, options, reqResMatcher)
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF { if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
log.Println(err) log.Println(err)
} }
@@ -141,7 +141,7 @@ func TestDissect(t *testing.T) {
SrcPort: "2", SrcPort: "2",
DstPort: "1", DstPort: "1",
} }
err = dissector.Dissect(bufferServer, false, tcpIDServer, counterPair, &api.SuperTimer{}, superIdentifier, emitter, options, reqResMatcher) err = dissector.Dissect(bufferServer, api.Pcap, false, tcpIDServer, counterPair, &api.SuperTimer{}, superIdentifier, emitter, options, reqResMatcher)
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF { if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
log.Println(err) log.Println(err)
} }

View File

@@ -16,7 +16,7 @@ type Response struct {
CaptureTime time.Time `json:"captureTime"` CaptureTime time.Time `json:"captureTime"`
} }
func ReadResponse(r io.Reader, tcpID *api.TcpID, counterPair *api.CounterPair, superTimer *api.SuperTimer, emitter api.Emitter, reqResMatcher *requestResponseMatcher) (err error) { func ReadResponse(r io.Reader, capture api.Capture, tcpID *api.TcpID, counterPair *api.CounterPair, superTimer *api.SuperTimer, emitter api.Emitter, reqResMatcher *requestResponseMatcher) (err error) {
d := &decoder{reader: r, remain: 4} d := &decoder{reader: r, remain: 4}
size := d.readInt32() size := d.readInt32()
@@ -258,6 +258,7 @@ func ReadResponse(r io.Reader, tcpID *api.TcpID, counterPair *api.CounterPair, s
item := &api.OutputChannelItem{ item := &api.OutputChannelItem{
Protocol: _protocol, Protocol: _protocol,
Capture: capture,
Timestamp: reqResPair.Request.CaptureTime.UnixNano() / int64(time.Millisecond), Timestamp: reqResPair.Request.CaptureTime.UnixNano() / int64(time.Millisecond),
ConnectionInfo: connectionInfo, ConnectionInfo: connectionInfo,
Pair: &api.RequestResponsePair{ Pair: &api.RequestResponsePair{

View File

@@ -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/expect3/redis/\* expect @[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect4/redis/\* expect

View File

@@ -6,7 +6,7 @@ import (
"github.com/up9inc/mizu/tap/api" "github.com/up9inc/mizu/tap/api"
) )
func handleClientStream(tcpID *api.TcpID, counterPair *api.CounterPair, superTimer *api.SuperTimer, emitter api.Emitter, request *RedisPacket, reqResMatcher *requestResponseMatcher) error { func handleClientStream(capture api.Capture, tcpID *api.TcpID, counterPair *api.CounterPair, superTimer *api.SuperTimer, emitter api.Emitter, request *RedisPacket, reqResMatcher *requestResponseMatcher) error {
counterPair.Lock() counterPair.Lock()
counterPair.Request++ counterPair.Request++
requestCounter := counterPair.Request requestCounter := counterPair.Request
@@ -23,6 +23,7 @@ func handleClientStream(tcpID *api.TcpID, counterPair *api.CounterPair, superTim
item := reqResMatcher.registerRequest(ident, request, superTimer.CaptureTime) item := reqResMatcher.registerRequest(ident, request, superTimer.CaptureTime)
if item != nil { if item != nil {
item.Capture = capture
item.ConnectionInfo = &api.ConnectionInfo{ item.ConnectionInfo = &api.ConnectionInfo{
ClientIP: tcpID.SrcIP, ClientIP: tcpID.SrcIP,
ClientPort: tcpID.SrcPort, ClientPort: tcpID.SrcPort,
@@ -35,7 +36,7 @@ func handleClientStream(tcpID *api.TcpID, counterPair *api.CounterPair, superTim
return nil return nil
} }
func handleServerStream(tcpID *api.TcpID, counterPair *api.CounterPair, superTimer *api.SuperTimer, emitter api.Emitter, response *RedisPacket, reqResMatcher *requestResponseMatcher) error { func handleServerStream(capture api.Capture, tcpID *api.TcpID, counterPair *api.CounterPair, superTimer *api.SuperTimer, emitter api.Emitter, response *RedisPacket, reqResMatcher *requestResponseMatcher) error {
counterPair.Lock() counterPair.Lock()
counterPair.Response++ counterPair.Response++
responseCounter := counterPair.Response responseCounter := counterPair.Response
@@ -52,6 +53,7 @@ func handleServerStream(tcpID *api.TcpID, counterPair *api.CounterPair, superTim
item := reqResMatcher.registerResponse(ident, response, superTimer.CaptureTime) item := reqResMatcher.registerResponse(ident, response, superTimer.CaptureTime)
if item != nil { if item != nil {
item.Capture = capture
item.ConnectionInfo = &api.ConnectionInfo{ item.ConnectionInfo = &api.ConnectionInfo{
ClientIP: tcpID.DstIP, ClientIP: tcpID.DstIP,
ClientPort: tcpID.DstPort, ClientPort: tcpID.DstPort,

View File

@@ -34,7 +34,7 @@ func (d dissecting) Ping() {
log.Printf("pong %s", protocol.Name) log.Printf("pong %s", protocol.Name)
} }
func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, counterPair *api.CounterPair, superTimer *api.SuperTimer, superIdentifier *api.SuperIdentifier, emitter api.Emitter, options *api.TrafficFilteringOptions, _reqResMatcher api.RequestResponseMatcher) error { func (d dissecting) Dissect(b *bufio.Reader, capture api.Capture, isClient bool, tcpID *api.TcpID, counterPair *api.CounterPair, superTimer *api.SuperTimer, superIdentifier *api.SuperIdentifier, emitter api.Emitter, options *api.TrafficFilteringOptions, _reqResMatcher api.RequestResponseMatcher) error {
reqResMatcher := _reqResMatcher.(*requestResponseMatcher) reqResMatcher := _reqResMatcher.(*requestResponseMatcher)
is := &RedisInputStream{ is := &RedisInputStream{
Reader: b, Reader: b,
@@ -48,9 +48,9 @@ func (d dissecting) Dissect(b *bufio.Reader, isClient bool, tcpID *api.TcpID, co
} }
if isClient { if isClient {
err = handleClientStream(tcpID, counterPair, superTimer, emitter, redisPacket, reqResMatcher) err = handleClientStream(capture, tcpID, counterPair, superTimer, emitter, redisPacket, reqResMatcher)
} else { } else {
err = handleServerStream(tcpID, counterPair, superTimer, emitter, redisPacket, reqResMatcher) err = handleServerStream(capture, tcpID, counterPair, superTimer, emitter, redisPacket, reqResMatcher)
} }
if err != nil { if err != nil {
@@ -71,6 +71,7 @@ func (d dissecting) Analyze(item *api.OutputChannelItem, resolvedSource string,
} }
return &api.Entry{ return &api.Entry{
Protocol: protocol, Protocol: protocol,
Capture: item.Capture,
Source: &api.TCP{ Source: &api.TCP{
Name: resolvedSource, Name: resolvedSource,
IP: item.ConnectionInfo.ClientIP, IP: item.ConnectionInfo.ClientIP,
@@ -113,6 +114,7 @@ func (d dissecting) Summarize(entry *api.Entry) *api.BaseEntry {
return &api.BaseEntry{ return &api.BaseEntry{
Id: entry.Id, Id: entry.Id,
Protocol: entry.Protocol, Protocol: entry.Protocol,
Capture: entry.Capture,
Summary: summary, Summary: summary,
SummaryQuery: summaryQuery, SummaryQuery: summaryQuery,
Status: status, Status: status,

View File

@@ -123,7 +123,7 @@ func TestDissect(t *testing.T) {
DstPort: "2", DstPort: "2",
} }
reqResMatcher := dissector.NewResponseRequestMatcher() reqResMatcher := dissector.NewResponseRequestMatcher()
err = dissector.Dissect(bufferClient, true, tcpIDClient, counterPair, &api.SuperTimer{}, superIdentifier, emitter, options, reqResMatcher) err = dissector.Dissect(bufferClient, api.Pcap, true, tcpIDClient, counterPair, &api.SuperTimer{}, superIdentifier, emitter, options, reqResMatcher)
if err != nil && reflect.TypeOf(err) != reflect.TypeOf(&ConnectError{}) && err != io.EOF && err != io.ErrUnexpectedEOF { if err != nil && reflect.TypeOf(err) != reflect.TypeOf(&ConnectError{}) && err != io.EOF && err != io.ErrUnexpectedEOF {
log.Println(err) log.Println(err)
} }
@@ -141,7 +141,7 @@ func TestDissect(t *testing.T) {
SrcPort: "2", SrcPort: "2",
DstPort: "1", DstPort: "1",
} }
err = dissector.Dissect(bufferServer, false, tcpIDServer, counterPair, &api.SuperTimer{}, superIdentifier, emitter, options, reqResMatcher) err = dissector.Dissect(bufferServer, api.Pcap, false, tcpIDServer, counterPair, &api.SuperTimer{}, superIdentifier, emitter, options, reqResMatcher)
if err != nil && reflect.TypeOf(err) != reflect.TypeOf(&ConnectError{}) && err != io.EOF && err != io.ErrUnexpectedEOF { if err != nil && reflect.TypeOf(err) != reflect.TypeOf(&ConnectError{}) && err != io.EOF && err != io.ErrUnexpectedEOF {
log.Println(err) log.Println(err)
} }

View File

@@ -95,7 +95,8 @@ func (h *tcpReader) Close() {
func (h *tcpReader) run(wg *sync.WaitGroup) { func (h *tcpReader) run(wg *sync.WaitGroup) {
defer wg.Done() defer wg.Done()
b := bufio.NewReader(h) b := bufio.NewReader(h)
err := h.extension.Dissector.Dissect(b, h.isClient, h.tcpID, h.counterPair, h.superTimer, h.parent.superIdentifier, h.emitter, filteringOptions, h.reqResMatcher) // TODO: Add api.Pcap, api.Envoy and api.Linkerd distinction by refactoring NewPacketSourceManager method
err := h.extension.Dissector.Dissect(b, api.Pcap, h.isClient, h.tcpID, h.counterPair, h.superTimer, h.parent.superIdentifier, h.emitter, filteringOptions, h.reqResMatcher)
if err != nil { if err != nil {
_, err = io.Copy(ioutil.Discard, b) _, err = io.Copy(ioutil.Discard, b)
if err != nil { if err != nil {

View File

@@ -158,7 +158,7 @@ func dissect(extension *api.Extension, reader *tlsReader, isRequest bool, tcpid
emitter api.Emitter, options *api.TrafficFilteringOptions, reqResMatcher api.RequestResponseMatcher) { emitter api.Emitter, options *api.TrafficFilteringOptions, reqResMatcher api.RequestResponseMatcher) {
b := bufio.NewReader(reader) b := bufio.NewReader(reader)
err := extension.Dissector.Dissect(b, isRequest, tcpid, &api.CounterPair{}, err := extension.Dissector.Dissect(b, api.Ebpf, isRequest, tcpid, &api.CounterPair{},
&api.SuperTimer{}, &api.SuperIdentifier{}, emitter, options, reqResMatcher) &api.SuperTimer{}, &api.SuperIdentifier{}, emitter, options, reqResMatcher)
if err != nil { if err != nil {

9
ui-common/.editorconfig Normal file
View File

@@ -0,0 +1,9 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

6
ui-common/.eslintignore Normal file
View File

@@ -0,0 +1,6 @@
build/
dist/
src/
node_modules/
.snapshots/
*.min.js

34
ui-common/.eslintrc Normal file
View File

@@ -0,0 +1,34 @@
{
"parser": "@typescript-eslint/parser",
"extends": [
"standard",
"standard-react",
"plugin:prettier/recommended",
"prettier/standard",
"prettier/react",
"plugin:@typescript-eslint/eslint-recommended"
],
"env": {
"node": true
},
"parserOptions": {
"ecmaVersion": 2020,
"ecmaFeatures": {
"legacyDecorators": true,
"jsx": true
}
},
"settings": {
"react": {
"version": "16"
}
},
"rules": {
"space-before-function-paren": 0,
"react/prop-types": 0,
"react/jsx-handler-names": 0,
"react/jsx-fragments": 0,
"react/no-unused-prop-types": 0,
"import/export": 0
}
}

10
ui-common/.prettierrc Normal file
View File

@@ -0,0 +1,10 @@
{
"singleQuote": true,
"jsxSingleQuote": true,
"semi": false,
"tabWidth": 2,
"bracketSpacing": true,
"jsxBracketSameLine": false,
"arrowParens": "always",
"trailingComma": "none"
}

4
ui-common/.travis.yml Normal file
View File

@@ -0,0 +1,4 @@
language: node_js
node_js:
- 12
- 10

30
ui-common/README.md Normal file
View File

@@ -0,0 +1,30 @@
# mizu-common
> Made with create-react-library
[![NPM](https://img.shields.io/npm/v/liraz-test.svg)](https://www.npmjs.com/package/liraz-test) [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
## Install
```bash
npm install --save @up9/mizu-common
```
## Usage
```tsx
import React, { Component } from 'react'
import MyComponent from 'l@up9/mizu-common'
import '@up9/mizu-common/dist/index.css'
class Example extends Component {
render() {
return <MyComponent />
}
}
```
## License
MIT © [](https://github.com/)

View File

@@ -0,0 +1,5 @@
This example was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
It is linked to the liraz-test package in the parent directory for development purposes.
You can run `npm install` and then `npm start` to test your package.

View File

@@ -0,0 +1,13 @@
module.exports = {
webpack: {
configure: (webpackConfig) => {
const instanceOfMiniCssExtractPlugin = webpackConfig.plugins.find(
(plugin) => plugin.options && plugin.options.ignoreOrder != null,
);
if(instanceOfMiniCssExtractPlugin)
instanceOfMiniCssExtractPlugin.options.ignoreOrder = true;
return webpackConfig;
}
}
}

43491
ui-common/example/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,48 @@
{
"name": "@up9/mizu-common-example",
"homepage": ".",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "craco start",
"comment-start": "node ../node_modules/react-scripts/bin/react-scripts.js start",
"build": "node ../node_modules/react-scripts/bin/react-scripts.js build",
"test": "node ../node_modules/react-scripts/bin/react-scripts.js test",
"eject": "node ../node_modules/react-scripts/bin/react-scripts.js eject"
},
"dependencies": {
"@testing-library/jest-dom": "file:../node_modules/@testing-library/jest-dom",
"@testing-library/react": "file:../node_modules/@testing-library/react",
"@testing-library/user-event": "file:../node_modules/@testing-library/user-event",
"@types/jest": "file:../node_modules/@types/jest",
"@types/node": "file:../node_modules/@types/node",
"@types/react": "file:../node_modules/@types/react",
"@types/react-dom": "file:../node_modules/@types/react-dom",
"@up9/mizu-common": "file:..",
"react": "file:../node_modules/react",
"react-dom": "file:../node_modules/react-dom"
},
"devDependencies": {
"@babel/plugin-syntax-object-rest-spread": "^7.8.3",
"@craco/craco": "^6.4.3",
"eslint": "^7.11.0",
"node-sass": "^6.0.0",
"recoil": "^0.5.2",
"react-scripts": "^4.0.3"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta name="theme-color" content="#000000" />
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>@up9/mizu-common</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

View File

@@ -0,0 +1,15 @@
{
"short_name": "@up9/mizu-common",
"name": "@up9/mizu-common",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View File

@@ -0,0 +1,9 @@
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
it('renders without crashing', () => {
const div = document.createElement('div')
ReactDOM.render(<App />, div)
ReactDOM.unmountComponentAtNode(div)
})

View File

@@ -0,0 +1,25 @@
import TrafficViewer,{useWS, DEFAULT_QUERY} from '@up9/mizu-common';
import "@up9/mizu-common/dist/index.css"
import {useEffect} from 'react';
import Api, {getWebsocketUrl} from "./api";
const api = Api.getInstance()
const App = () => {
const {message,error,isOpen, openSocket, closeSocket, sendQueryWhenWsOpen} = useWS(getWebsocketUrl())
const trafficViewerApi = {...api, webSocket:{open : openSocket, close: closeSocket, sendQueryWhenWsOpen: sendQueryWhenWsOpen}}
sendQueryWhenWsOpen(DEFAULT_QUERY);
useEffect(() => {
return () =>{
closeSocket()
}
},[])
return <>
<TrafficViewer message={message} error={error} isWebSocketOpen={isOpen}
trafficViewerApiProp={trafficViewerApi} ></TrafficViewer>
</>
}
export default App

View File

@@ -0,0 +1,124 @@
import * as axios from "axios";
export const MizuWebsocketURL = process.env.REACT_APP_OVERRIDE_WS_URL ? process.env.REACT_APP_OVERRIDE_WS_URL :
window.location.protocol === 'https:' ? `wss://${window.location.host}/ws` : `ws://${window.location.host}/ws`;
export const FormValidationErrorType = "formError";
const CancelToken = axios.CancelToken;
const apiURL = process.env.REACT_APP_OVERRIDE_API_URL ? process.env.REACT_APP_OVERRIDE_API_URL : `${window.location.origin}/`;
let token = null
let client = null
let source = null
export default class Api {
static instance;
static getInstance() {
if (!Api.instance) {
Api.instance = new Api();
}
return Api.instance;
}
constructor() {
token = localStorage.getItem("token");
client = this.getAxiosClient();
source = null;
}
tapStatus = async () => {
const response = await client.get("/status/tap");
return response.data;
}
analyzeStatus = async () => {
const response = await client.get("/status/analyze");
return response.data;
}
getEntry = async (id, query) => {
const response = await client.get(`/entries/${id}?query=${query}`);
return response.data;
}
fetchEntries = async (leftOff, direction, query, limit, timeoutMs) => {
const response = await client.get(`/entries/?leftOff=${leftOff}&direction=${direction}&query=${query}&limit=${limit}&timeoutMs=${timeoutMs}`).catch(function (thrown) {
console.error(thrown.message);
return {};
});
return response.data;
}
getRecentTLSLinks = async () => {
const response = await client.get("/status/recentTLSLinks");
return response.data;
}
getOasServices = async () => {
const response = await client.get("/oas");
return response.data;
}
getOasByService = async (selectedService) => {
const response = await client.get(`/oas/${selectedService}`);
return response.data;
}
validateQuery = async (query) => {
if (source) {
source.cancel();
}
source = CancelToken.source();
const form = new FormData();
form.append('query', query)
const response = await client.post(`/query/validate`, form, {
cancelToken: source.token
}).catch(function (thrown) {
if (!axios.isCancel(thrown)) {
console.error('Validate error', thrown.message);
}
});
if (!response) {
return null;
}
return response.data;
}
persistToken = (tk) => {
token = tk;
client = this.getAxiosClient();
localStorage.setItem('token', token);
}
getAxiosClient = () => {
const headers = {
Accept: "application/json"
}
if (token) {
headers['x-session-token'] = `${token}`; // we use `x-session-token` instead of `Authorization` because the latter is reserved by kubectl proxy, making mizu view not work
}
return axios.create({
baseURL: apiURL,
timeout: 31000,
headers
});
}
}
export function getWebsocketUrl(){
let websocketUrl = MizuWebsocketURL;
if (token) {
websocketUrl += `/${token}`;
}
return websocketUrl;
}

View File

@@ -0,0 +1,14 @@
body {
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

View File

@@ -0,0 +1,7 @@
import './index.css'
import ReactDOM from 'react-dom'
import App from './App'
ReactDOM.render(<App />, document.getElementById('root'))

View File

@@ -0,0 +1 @@
/// <reference types="react-scripts" />

View File

@@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom/extend-expect';

View File

@@ -0,0 +1,41 @@
{
"compilerOptions": {
"outDir": "dist",
"module": "esnext",
"lib": [
"dom",
"esnext"
],
"moduleResolution": "node",
"jsx": "react-jsx",
"sourceMap": true,
"declaration": true,
"esModuleInterop": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitAny": true,
"strictNullChecks": false,
"suppressImplicitAnyIndexErrors": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
"allowSyntheticDefaultImports": true,
"target": "es5",
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"noFallthroughCasesInSwitch": true
},
"include": [
"src"
],
"exclude": [
"node_modules",
"build",
"exmaple"
]
}

62803
ui-common/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

93
ui-common/package.json Normal file
View File

@@ -0,0 +1,93 @@
{
"name": "@up9/mizu-common",
"version": "1.0.129",
"description": "Made with create-react-library",
"author": "",
"license": "MIT",
"repository": "/mizu-common",
"main": "dist/index.js",
"module": "dist/index.modern.js",
"source": "src/index.tsx",
"engines": {
"node": ">=10"
},
"scripts": {
"build": "microbundle-crl --no-compress --format modern,cjs",
"start": "microbundle-crl watch --no-compress --format modern,cjs",
"prepare": "run-s build",
"test": "run-s test:unit test:lint test:build",
"test:build": "run-s build",
"test:lint": "eslint .",
"predeploy": "cd example && npm install && npm run build",
"deploy": "gh-pages -d example/build"
},
"peerDependencies": {
"@craco/craco": "^6.4.3",
"@material-ui/core": "^4.11.3",
"@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.60",
"node-sass": "^6.0.0",
"react":"^17.0.2",
"react-dom": "^17.0.2",
"recoil": "^0.5.2",
"react-copy-to-clipboard": "^5.0.3",
"@types/jest": "^26.0.22",
"@types/node": "^12.20.10"
},
"dependencies": {
"@types/lodash": "^4.14.179",
"@uiw/react-textarea-code-editor": "^1.4.12",
"axios": "^0.25.0",
"core-js": "^3.20.2",
"highlight.js": "^11.3.1",
"json-beautify": "^1.1.1",
"jsonpath": "^1.1.1",
"marked": "^4.0.10",
"material-ui-popup-state": "^2.0.0",
"mobx": "^6.3.10",
"moment": "^2.29.1",
"node-fetch": "^3.1.1",
"numeral": "^2.0.6",
"protobuf-decoder": "^0.1.0",
"react-graph-vis": "^1.0.7",
"react-lowlight": "^3.0.0",
"react-router-dom": "^6.2.1",
"react-scrollable-feed-virtualized": "^1.4.9",
"react-syntax-highlighter": "^15.4.3",
"react-toastify": "^8.0.3",
"redoc": "^2.0.0-rc.59",
"styled-components": "^5.3.3",
"web-vitals": "^1.1.1",
"xml-formatter": "^2.6.0",
"@craco/craco": "^6.4.3"
},
"devDependencies": {
"@rollup/plugin-node-resolve": "^13.1.3",
"@svgr/rollup": "^6.2.1",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
"cross-env": "^7.0.2",
"env-cmd": "^10.0.1",
"gh-pages": "^2.2.0",
"microbundle-crl": "^0.13.10",
"npm-run-all": "^4.1.5",
"prettier": "^2.0.4",
"rollup-plugin-import-css": "^3.0.2",
"rollup-plugin-postcss": "^4.0.2",
"rollup-plugin-sass": "^1.2.10",
"rollup-plugin-scss": "^3.0.0",
"react":"^17.0.2",
"react-dom": "^17.0.2",
"typescript": "^4.2.4"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"files": [
"dist"
]
}

View File

@@ -0,0 +1,86 @@
import {Button} from "@material-ui/core";
import React from "react";
import logo_up9 from "logo_up9.svg";
import {makeStyles} from "@material-ui/core/styles";
import { Tooltip } from "../UI";
const useStyles = makeStyles(() => ({
tooltip: {
backgroundColor: "#3868dc",
color: "white",
fontSize: 13,
},
}));
interface AnalyseButtonProps {
analyzeStatus: any
}
export const AnalyzeButton: React.FC<AnalyseButtonProps> = ({analyzeStatus}) => {
const classes = useStyles();
const analysisMessage = analyzeStatus?.isRemoteReady ?
<span>
<table>
<tr>
<td>Status</td>
<td><b>Available</b></td>
</tr>
<tr>
<td>Messages</td>
<td><b>{analyzeStatus?.sentCount}</b></td>
</tr>
</table>
</span> :
analyzeStatus?.sentCount > 0 ?
<span>
<table>
<tr>
<td>Status</td>
<td><b>Processing</b></td>
</tr>
<tr>
<td>Messages</td>
<td><b>{analyzeStatus?.sentCount}</b></td>
</tr>
<tr>
<td colSpan={2}> Please allow a few minutes for the analysis to complete</td>
</tr>
</table>
</span> :
<span>
<table>
<tr>
<td>Status</td>
<td><b>Waiting for traffic</b></td>
</tr>
<tr>
<td>Messages</td>
<td><b>{analyzeStatus?.sentCount}</b></td>
</tr>
</table>
</span>
return ( <div>
<Tooltip title={analysisMessage} isSimple classes={classes}>
<div>
<Button
style={{fontFamily: "system-ui",
fontWeight: 600,
fontSize: 12,
padding: 8}}
size={"small"}
variant="contained"
color="primary"
startIcon={<img style={{height: 24, maxHeight: "none", maxWidth: "none"}} src={logo_up9} alt={"up9"}/>}
disabled={!analyzeStatus?.isRemoteReady}
onClick={() => {
window.open(analyzeStatus?.remoteUrl)
}}>
Analysis
</Button>
</div>
</Tooltip>
</div>);
}

View File

@@ -0,0 +1,39 @@
<svg xmlns="http://www.w3.org/2000/svg" id="prefix__logo" width="148" height="88" viewBox="0 0 148 88">
<defs>
<style>
.prefix__cls-1{fill:#070047}.prefix__cls-2{fill:#fff}.prefix__cls-4{fill:#8f9bb2}
</style>
</defs>
<g id="prefix__Group_4690_2_">
<path id="prefix__Path_2985_1_" d="M0 62.1a7.832 7.832 0 0 0 3.928 6.786l31.405 18.071a7.866 7.866 0 0 0 7.84 0L74.563 68.9a7.823 7.823 0 0 0 3.928-6.784v-36.2a7.828 7.828 0 0 0-3.926-6.782L43.175 1.052a7.869 7.869 0 0 0-7.86.006L3.914 19.217A7.827 7.827 0 0 0 0 25.994z" class="prefix__cls-1" transform="translate(0 -.004)"/>
<path id="prefix__Path_2986_2_" d="M55.987 134.68a.976.976 0 0 1-.008 1.7l-4.4 2.482-.033-.02-6.832 3.948a1.966 1.966 0 0 1-1.966 0l-8.83-5.09A7.818 7.818 0 0 1 30 130.927v-29.356a7.813 7.813 0 0 1 3.979-6.8l.443-.25a.984.984 0 0 1 1.339.369.964.964 0 0 1 .127.482v36.187a.979.979 0 0 0 .488.845l4.909 2.855a.984.984 0 0 0 .981 0l.063-.035a.977.977 0 0 0 0-1.7l-3.994-2.3a.978.978 0 0 1-.49-.847V93.654a1.955 1.955 0 0 1 .995-1.7l3.429-1.936a.986.986 0 0 1 1.339.369.962.962 0 0 1 .127.48v36.187a.978.978 0 0 0 .49.847z" class="prefix__cls-2" transform="translate(-24.126 -72.289)"/>
<path id="prefix__Path_2987_2_" d="M117.554 80.338a.981.981 0 0 1-.366-1.338.969.969 0 0 1 .37-.368l9.788-5.733a.981.981 0 0 0 .484-.846V37.572a1.961 1.961 0 0 0-2.951-1.694l-8.823 5.148-4.891 2.767a1.962 1.962 0 0 0-.995 1.707v36.849a.982.982 0 0 0 .49.85l12.246 7.086a1.965 1.965 0 0 0 1.991-.016l3.46-2.076a.983.983 0 0 0-.018-1.694zM116.532 47.6l3.924-2.356a.98.98 0 0 1 1.484.842v22.619a.982.982 0 0 1-.5.854l-3.924 2.224a.984.984 0 0 1-1.339-.37.97.97 0 0 1-.127-.484V48.447a1 1 0 0 1 .482-.847z" class="prefix__cls-2" transform="translate(-88.598 -28.638)"/>
<path id="prefix__Path_2988_2_" d="M133.819 43.492V77.9a.979.979 0 0 1-.49.848l-7.858 4.545a.978.978 0 0 0 0 1.693l10.8 6.235a.977.977 0 0 1 0 1.693l-20.607 11.9a.978.978 0 0 0 0 1.7l12.279 7.01a7.87 7.87 0 0 0 7.8 0l26.5-15.161a5.871 5.871 0 0 0 2.957-5.094V62.723a7.825 7.825 0 0 0-3.92-6.778L136.762 41.8a1.959 1.959 0 0 0-2.943 1.7zm24.037 42.144L140.684 75.7a1.957 1.957 0 0 1-.977-1.693V60.981a.983.983 0 0 1 1.472-.848l14.239 8.237a7.826 7.826 0 0 1 3.91 6.772v9.647a.979.979 0 0 1-.979.98.968.968 0 0 1-.493-.133z" transform="translate(-92.625 -33.401)" style="fill:#627ef7"/>
<path id="prefix__Path_2989_2_" d="M60.346 291.555l-4.4 2.491-.033-.02-6.832 3.963a1.963 1.963 0 0 1-1.968 0l-8.835-5.111a7.826 7.826 0 0 1-2.853-2.851l4.938-2.851a1 1 0 0 0 .37.384l4.909 2.865a.981.981 0 0 0 .981 0l.063-.035a.982.982 0 0 0 0-1.7l-3.986-2.306a.981.981 0 0 1-.352-.352l5.86-3.382a.99.99 0 0 0 .378.4l11.764 6.8a.981.981 0 0 1-.006 1.7z" class="prefix__cls-4" transform="translate(-28.493 -227.445)"/>
<path id="prefix__Path_2991_2_" d="M164.463 248.2a5.858 5.858 0 0 1-2.193 2.207l-26.5 15.2a7.854 7.854 0 0 1-7.8 0l-12.279-7.026a.983.983 0 0 1 0-1.7l16.187-9.375 4.419-2.559a.98.98 0 0 0 0-1.7l-4.419-2.557-6.392-3.69a.98.98 0 0 1 0-1.7l6.385-3.7 1.472-.854a.968.968 0 0 0 .333-.321z" transform="translate(-92.637 -185.485)" style="fill:#3b4c95"/>
<path id="prefix__Path_2992_2_" d="M128.9 265.267l-.525.313-2.933 1.762a1.965 1.965 0 0 1-1.991.016l-12.244-7.082a1.021 1.021 0 0 1-.206-.161.928.928 0 0 1-.151-.2v-.006l2.692-1.555 4.445-2.567a.762.762 0 0 0-.094.084.6.6 0 0 0-.08.092.035.035 0 0 0-.012.018.479.479 0 0 0-.055.084.316.316 0 0 0-.02.035c-.01.02-.02.039-.027.059a.369.369 0 0 0-.023.059.378.378 0 0 0-.022.067.08.08 0 0 0-.01.035.729.729 0 0 0-.018.09.974.974 0 0 0 .482 1l10.274 5.872.507.288a.979.979 0 0 1 .366 1.335 1 1 0 0 1-.355.362z" class="prefix__cls-4" transform="translate(-89.145 -205.825)"/>
</g>
<g id="prefix__Group_4695" data-name="Group 4695" transform="translate(72.538 19.232)">
<g id="prefix__Group_4694" data-name="Group 4694">
<g id="prefix__Group_4691" data-name="Group 4691">
<path id="prefix__Path_2993" d="M394.794 158.1a9.4 9.4 0 0 1-6.626-2.728 9.4 9.4 0 0 1-1.987-2.937 9.248 9.248 0 0 1-.74-3.687v-31.142a3.92 3.92 0 0 1 3.916-3.916h4.455a3.92 3.92 0 0 1 3.916 3.916v28.2h.54v-28.2a3.92 3.92 0 0 1 3.916-3.916h4.507a3.92 3.92 0 0 1 3.916 3.916v36.577a3.92 3.92 0 0 1-3.916 3.916z" class="prefix__cls-2" data-name="Path 2993" transform="translate(-382.507 -110.753)"/>
<path id="prefix__Path_2994" d="M394.648 104.564a.979.979 0 0 1 .979.979v36.577a.979.979 0 0 1-.979.979h-11.9a6.454 6.454 0 0 1-4.547-1.866 6.447 6.447 0 0 1-1.367-2.025 6.291 6.291 0 0 1-.5-2.524v-31.141a.979.979 0 0 1 .979-.979h4.455a.979.979 0 0 1 .979.979v30.162a.979.979 0 0 0 .979.979h4.457a.979.979 0 0 0 .979-.979v-30.162a.979.979 0 0 1 .979-.979h4.507m0-5.874h-4.508a6.832 6.832 0 0 0-4.186 1.429 6.819 6.819 0 0 0-4.186-1.429h-4.455a6.861 6.861 0 0 0-6.853 6.853v31.141a12.147 12.147 0 0 0 .983 4.856 12.29 12.29 0 0 0 11.3 7.433h11.9a6.861 6.861 0 0 0 6.853-6.853v-36.577a6.861 6.861 0 0 0-6.853-6.853z" class="prefix__cls-1" data-name="Path 2994" transform="translate(-370.46 -98.69)"/>
</g>
<g id="prefix__Group_4692" data-name="Group 4692" transform="translate(22.447)">
<path id="prefix__Path_2995" d="M504.016 158.1a3.92 3.92 0 0 1-3.916-3.916v-36.578a3.92 3.92 0 0 1 3.916-3.916h11.848a9.332 9.332 0 0 1 3.64.73 9.635 9.635 0 0 1 2.978 1.964 9.144 9.144 0 0 1 2.054 3.015 9.312 9.312 0 0 1 .73 3.642v9.62a9.235 9.235 0 0 1-.74 3.687 9.48 9.48 0 0 1-5.024 4.985 9.3 9.3 0 0 1-3.638.73h-3.478v12.118a3.92 3.92 0 0 1-3.916 3.916zm8.911-28.374v-3.746h-.54v3.746z" class="prefix__cls-2" data-name="Path 2995" transform="translate(-497.163 -110.753)"/>
<path id="prefix__Path_2996" d="M503.8 104.564a6.351 6.351 0 0 1 2.5.5 6.709 6.709 0 0 1 2.078 1.367 6.157 6.157 0 0 1 1.392 2.05 6.358 6.358 0 0 1 .5 2.5v9.62a6.3 6.3 0 0 1-.5 2.524 6.536 6.536 0 0 1-1.392 2.05 6.453 6.453 0 0 1-2.078 1.394 6.358 6.358 0 0 1-2.5.5h-5.436a.979.979 0 0 0-.979.979v14.072a.979.979 0 0 1-.979.979h-4.455a.979.979 0 0 1-.979-.979v-36.577a.979.979 0 0 1 .979-.979H503.8m-5.434 16.036h4.457a.979.979 0 0 0 .979-.979v-7.662a.979.979 0 0 0-.979-.979h-4.457a.979.979 0 0 0-.979.979v7.662a.979.979 0 0 0 .979.979m5.434-21.91h-11.847a6.861 6.861 0 0 0-6.853 6.853v36.577a6.861 6.861 0 0 0 6.853 6.853h4.455a6.861 6.861 0 0 0 6.853-6.853v-9.181h.54a12.337 12.337 0 0 0 8.729-3.613 12.423 12.423 0 0 0 2.632-3.873 12.164 12.164 0 0 0 .981-4.854v-9.62a12.217 12.217 0 0 0-.961-4.78 12.067 12.067 0 0 0-2.714-3.981 12.589 12.589 0 0 0-3.881-2.563 12.193 12.193 0 0 0-4.786-.965z" class="prefix__cls-1" data-name="Path 2996" transform="translate(-485.1 -98.69)"/>
</g>
<g id="prefix__Group_4693" data-name="Group 4693" transform="translate(44.421)">
<path id="prefix__Path_2997" d="M621.68 158.107a9.331 9.331 0 0 1-9.35-9.35V144.9a3.92 3.92 0 0 1 3.916-3.916h1.038a9.008 9.008 0 0 1-2.26-1.7 9.676 9.676 0 0 1-1.954-2.931 9.249 9.249 0 0 1-.74-3.687v-9.62a9.328 9.328 0 0 1 9.35-9.35h6.415a9.332 9.332 0 0 1 3.64.73 9.613 9.613 0 0 1 2.978 1.964 9.127 9.127 0 0 1 2.054 3.015 9.316 9.316 0 0 1 .732 3.642v25.707a9.287 9.287 0 0 1-.73 3.638 9.113 9.113 0 0 1-2.054 3.015 9.66 9.66 0 0 1-2.98 1.966 9.308 9.308 0 0 1-3.638.73h-6.417zm3.478-12.289v-3.746h-1.747a3.905 3.905 0 0 1 1.208 2.825v.92zm0-16.085v-3.746h-.54v3.746z" class="prefix__cls-2" data-name="Path 2997" transform="translate(-609.391 -110.761)"/>
<path id="prefix__Path_2998" d="M616.024 104.564a6.351 6.351 0 0 1 2.5.5 6.708 6.708 0 0 1 2.078 1.367 6.157 6.157 0 0 1 1.392 2.05 6.358 6.358 0 0 1 .5 2.5v25.707a6.344 6.344 0 0 1-.5 2.5 6.157 6.157 0 0 1-1.392 2.05 6.709 6.709 0 0 1-2.078 1.367 6.358 6.358 0 0 1-2.5.5h-6.415a6.393 6.393 0 0 1-6.413-6.413v-3.857a.979.979 0 0 1 .979-.979h4.455a.979.979 0 0 1 .979.979v2.878a.979.979 0 0 0 .979.979h4.457a.979.979 0 0 0 .979-.979v-7.662a.979.979 0 0 0-.979-.979h-5.436a6.344 6.344 0 0 1-2.5-.5 6.133 6.133 0 0 1-2.05-1.394 6.769 6.769 0 0 1-1.367-2.05 6.291 6.291 0 0 1-.5-2.524v-9.62a6.393 6.393 0 0 1 6.413-6.413h6.415m-5.432 16.029h4.457a.979.979 0 0 0 .979-.979v-7.662a.979.979 0 0 0-.979-.979h-4.457a.979.979 0 0 0-.979.979v7.662a.979.979 0 0 0 .979.979m5.436-21.909h-6.415a12.268 12.268 0 0 0-12.289 12.287v9.62a12.147 12.147 0 0 0 .983 4.856 12.629 12.629 0 0 0 1.281 2.287 6.841 6.841 0 0 0-2.264 5.085v3.857a12.268 12.268 0 0 0 12.287 12.289h6.415a12.2 12.2 0 0 0 4.784-.963 12.579 12.579 0 0 0 3.879-2.559 12.047 12.047 0 0 0 2.714-3.981 12.233 12.233 0 0 0 .963-4.785v-25.707a12.234 12.234 0 0 0-.961-4.782 12.066 12.066 0 0 0-2.714-3.981 12.623 12.623 0 0 0-3.881-2.563 12.233 12.233 0 0 0-4.782-.961z" class="prefix__cls-1" data-name="Path 2998" transform="translate(-597.32 -98.69)"/>
</g>
</g>
</g>
<g id="prefix__Group_4697" data-name="Group 4697" transform="translate(78.412 25.106)">
<g id="prefix__Group_4696" data-name="Group 4696">
<path id="prefix__Path_2999" d="M419.753 129.669v36.577a.979.979 0 0 1-.979.979h-11.9a6.454 6.454 0 0 1-4.547-1.866 6.447 6.447 0 0 1-1.367-2.025 6.292 6.292 0 0 1-.5-2.524v-31.141a.979.979 0 0 1 .979-.979h4.455a.979.979 0 0 1 .979.979v30.162a.979.979 0 0 0 .979.979h4.457a.979.979 0 0 0 .979-.979v-30.162a.979.979 0 0 1 .979-.979h4.507a.979.979 0 0 1 .979.979z" class="prefix__cls-2" data-name="Path 2999" transform="translate(-400.46 -128.69)"/>
<path id="prefix__Path_3000" d="M534.393 135.1v9.62a6.3 6.3 0 0 1-.5 2.524 6.535 6.535 0 0 1-1.392 2.05 6.453 6.453 0 0 1-2.077 1.394 6.358 6.358 0 0 1-2.5.5h-5.436a.979.979 0 0 0-.979.979v14.076a.979.979 0 0 1-.979.979h-4.455a.979.979 0 0 1-.979-.979v-36.574a.979.979 0 0 1 .979-.979h11.848a6.351 6.351 0 0 1 2.5.5 6.708 6.708 0 0 1 2.077 1.367 6.157 6.157 0 0 1 1.392 2.05 6.358 6.358 0 0 1 .501 2.493zm-6.466 8.643v-7.662a.979.979 0 0 0-.979-.979h-4.457a.979.979 0 0 0-.979.979v7.662a.979.979 0 0 0 .979.979h4.457a.979.979 0 0 0 .979-.977z" class="prefix__cls-2" data-name="Path 3000" transform="translate(-492.653 -128.69)"/>
<path id="prefix__Path_3001" d="M646.623 135.1v25.71a6.345 6.345 0 0 1-.5 2.5 6.158 6.158 0 0 1-1.392 2.05 6.71 6.71 0 0 1-2.078 1.367 6.358 6.358 0 0 1-2.5.5h-6.415a6.393 6.393 0 0 1-6.413-6.413v-3.857a.979.979 0 0 1 .979-.979h4.455a.979.979 0 0 1 .979.979v2.878a.979.979 0 0 0 .979.979h4.457a.979.979 0 0 0 .979-.979v-7.662a.979.979 0 0 0-.979-.979h-5.436a6.344 6.344 0 0 1-2.5-.5 6.133 6.133 0 0 1-2.05-1.394 6.77 6.77 0 0 1-1.367-2.05 6.292 6.292 0 0 1-.5-2.524V135.1a6.393 6.393 0 0 1 6.413-6.413h6.415a6.351 6.351 0 0 1 2.5.5 6.709 6.709 0 0 1 2.078 1.367 6.157 6.157 0 0 1 1.392 2.05 6.359 6.359 0 0 1 .504 2.496zm-6.466 8.643v-7.662a.979.979 0 0 0-.979-.979h-4.457a.979.979 0 0 0-.979.979v7.662a.979.979 0 0 0 .979.979h4.457a.979.979 0 0 0 .979-.976z" class="prefix__cls-2" data-name="Path 3001" transform="translate(-582.908 -128.69)"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -25,5 +25,4 @@
.redoc .redoc
height: 98% height: 98%
overflow-y: scroll overflow-y: scroll

View File

@@ -1,13 +1,13 @@
import { Box, Fade, FormControl, MenuItem, Modal, Backdrop, ListSubheader } from "@material-ui/core"; import { Box, Fade, FormControl, MenuItem, Modal, Backdrop, ListSubheader } from "@material-ui/core";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { RedocStandalone } from "redoc"; import { RedocStandalone } from "redoc";
import Api from "../../helpers/api"; import closeIcon from "assets/closeIcon.svg";
import { Select } from "../UI/Select";
import closeIcon from "../assets/closeIcon.svg";
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import style from './OasModal.module.sass'; import style from './OasModal.module.sass';
import openApiLogo from '../assets/openApiLogo.png' import openApiLogo from 'assets/openApiLogo.png'
import { redocThemeOptions } from "./redocThemeOptions"; import { redocThemeOptions } from "./redocThemeOptions";
import React from "react";
import { Select } from "../UI/Select";
const modalStyle = { const modalStyle = {
position: 'absolute', position: 'absolute',
@@ -23,10 +23,9 @@ const modalStyle = {
color: '#000', color: '#000',
}; };
const api = Api.getInstance();
const ipAddressWithPortRegex = new RegExp('([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}):([0-9]{1,5})'); const ipAddressWithPortRegex = new RegExp('([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}):([0-9]{1,5})');
const OasModal = ({ openModal, handleCloseModal }) => { const OasModal = ({ openModal, handleCloseModal, getOasServices, getOasByService}) => {
const [oasServices, setOasServices] = useState([] as string[]) const [oasServices, setOasServices] = useState([] as string[])
const [selectedServiceName, setSelectedServiceName] = useState(""); const [selectedServiceName, setSelectedServiceName] = useState("");
const [selectedServiceSpec, setSelectedServiceSpec] = useState(null); const [selectedServiceSpec, setSelectedServiceSpec] = useState(null);
@@ -40,7 +39,7 @@ const OasModal = ({ openModal, handleCloseModal }) => {
return return
} }
try { try {
const data = await api.getOasByService(selectedService); const data = await getOasByService(selectedService);
setSelectedServiceSpec(data); setSelectedServiceSpec(data);
} catch (e) { } catch (e) {
toast.error("Error occurred while fetching service OAS spec"); toast.error("Error occurred while fetching service OAS spec");
@@ -71,7 +70,7 @@ const OasModal = ({ openModal, handleCloseModal }) => {
useEffect(() => { useEffect(() => {
(async () => { (async () => {
try { try {
const services = await api.getOasServices(); const services = await getOasServices();
resolvedArrayBuilder(services); resolvedArrayBuilder(services);
setOasServices(services); setOasServices(services);
} catch (e) { } catch (e) {
@@ -107,7 +106,7 @@ const OasModal = ({ openModal, handleCloseModal }) => {
id="service-select" id="service-select"
placeholder="Show OAS" placeholder="Show OAS"
value={selectedServiceName} value={selectedServiceName}
onChange={onSelectedOASService} onChangeCb={onSelectedOASService}
> >
<ListSubheader disableSticky={true}>Resolved</ListSubheader> <ListSubheader disableSticky={true}>Resolved</ListSubheader>
{resolvedServices.map((service) => ( {resolvedServices.map((service) => (
@@ -140,4 +139,4 @@ const OasModal = ({ openModal, handleCloseModal }) => {
); );
}; };
export default OasModal; export default OasModal;

View File

@@ -0,0 +1,4 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20.5 11C20.5 16.2467 16.2467 20.5 11 20.5C5.75329 20.5 1.5 16.2467 1.5 11C1.5 5.75329 5.75329 1.5 11 1.5C16.2467 1.5 20.5 5.75329 20.5 11Z" stroke="#2B3560"/>
<path d="M14.4762 9.05338L13.1448 7.7219L11.1528 9.71382L9.16091 7.7219L7.82943 9.05338L9.82135 11.0453L7.83226 13.0344L9.16374 14.3659L11.1528 12.3768L13.1419 14.3659L14.4734 13.0344L12.4843 11.0453L14.4762 9.05338Z" fill="#627EF7"/>
</svg>

After

Width:  |  Height:  |  Size: 507 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@@ -0,0 +1,35 @@
export const redocThemeOptions = {
theme:{
codeBlock:{
backgroundColor:"#11171a",
},
colors:{
responses:{
error:{
tabTextColor:"#1b1b29"
},
info:{
tabTextColor:"#1b1b29",
},
success:{
tabTextColor:"#0c0b1a"
},
},
text:{
primary:"#1b1b29",
secondary:"#4d4d4d"
}
},
rightPanel:{
backgroundColor:"#253237",
},
sidebar:{
backgroundColor:"#ffffff"
},
typography:{
code:{
color:"#0c0b1a"
}
}
}
}

View File

@@ -1,11 +1,11 @@
import {Snackbar} from "@material-ui/core"; import {Snackbar} from "@material-ui/core";
import MuiAlert from "@material-ui/lab/Alert"; import MuiAlert from "@material-ui/lab/Alert";
import React, {useEffect} from "react"; import React, {useEffect} from "react";
import Api from "../../helpers/api"; import { RecoilState, useRecoilValue } from "recoil";
import TrafficViewerApiAtom from "../../recoil/TrafficViewerApi/atom";
import TrafficViewerApi from "../TrafficViewer/TrafficViewerApi";
import './TLSWarning.sass'; import './TLSWarning.sass';
const api = Api.getInstance();
interface TLSWarningProps { interface TLSWarningProps {
showTLSWarning: boolean showTLSWarning: boolean
setShowTLSWarning: (show: boolean) => void setShowTLSWarning: (show: boolean) => void
@@ -17,10 +17,12 @@ interface TLSWarningProps {
export const TLSWarning: React.FC<TLSWarningProps> = ({showTLSWarning, setShowTLSWarning, addressesWithTLS, setAddressesWithTLS, userDismissedTLSWarning, setUserDismissedTLSWarning}) => { export const TLSWarning: React.FC<TLSWarningProps> = ({showTLSWarning, setShowTLSWarning, addressesWithTLS, setAddressesWithTLS, userDismissedTLSWarning, setUserDismissedTLSWarning}) => {
const trafficViewerApi = useRecoilValue(TrafficViewerApiAtom as RecoilState<TrafficViewerApi>)
useEffect(() => { useEffect(() => {
(async () => { (async () => {
try { try {
const recentTLSLinks = await api.getRecentTLSLinks(); const getRecentTLSLinksFunc = trafficViewerApi?.getRecentTLSLinks ? trafficViewerApi?.getRecentTLSLinks : function(){}
const recentTLSLinks = await getRecentTLSLinksFunc();
if (recentTLSLinks?.length > 0) { if (recentTLSLinks?.length > 0) {
setAddressesWithTLS(new Set(recentTLSLinks)); setAddressesWithTLS(new Set(recentTLSLinks));
setShowTLSWarning(true); setShowTLSWarning(true);
@@ -29,7 +31,7 @@ export const TLSWarning: React.FC<TLSWarningProps> = ({showTLSWarning, setShowT
console.error(e); console.error(e);
} }
})(); })();
}, [setShowTLSWarning, setAddressesWithTLS]); }, [setShowTLSWarning, setAddressesWithTLS,trafficViewerApi]);
return (<Snackbar open={showTLSWarning && !userDismissedTLSWarning}> return (<Snackbar open={showTLSWarning && !userDismissedTLSWarning}>
<MuiAlert classes={{filledWarning: 'customWarningStyle'}} elevation={6} variant="filled" <MuiAlert classes={{filledWarning: 'customWarningStyle'}} elevation={6} variant="filled"

View File

@@ -1,15 +1,17 @@
import React, {useCallback, useEffect, useMemo, useState} from "react"; import React, {useCallback, useEffect, useMemo, useState} from "react";
import styles from './style/EntriesList.module.sass'; import styles from '../style/EntriesList.module.sass';
import ScrollableFeedVirtualized from "react-scrollable-feed-virtualized"; import ScrollableFeedVirtualized from "react-scrollable-feed-virtualized";
import Moment from 'moment'; import Moment from 'moment';
import {EntryItem} from "./EntryListItem/EntryListItem"; import {EntryItem} from "./EntryListItem/EntryListItem";
import down from "./assets/downImg.svg"; import down from "assets/downImg.svg";
import spinner from './assets/spinner.svg'; import spinner from 'assets/spinner.svg';
import Api from "../helpers/api";
import {useRecoilState, useRecoilValue} from "recoil"; import {RecoilState, useRecoilState, useRecoilValue} from "recoil";
import entriesAtom from "../recoil/entries"; import entriesAtom from "../../recoil/entries";
import wsConnectionAtom, {WsConnectionStatus} from "../recoil/wsConnection"; import wsConnectionAtom, {WsConnectionStatus} from "../../recoil/wsConnection";
import queryAtom from "../recoil/query"; import queryAtom from "../../recoil/query";
import TrafficViewerApiAtom from "../../recoil/TrafficViewerApi";
import TrafficViewerApi from "./TrafficViewerApi";
interface EntriesListProps { interface EntriesListProps {
listEntryREF: any; listEntryREF: any;
@@ -25,7 +27,6 @@ interface EntriesListProps {
setNoMoreDataTop: (flag: boolean) => void; setNoMoreDataTop: (flag: boolean) => void;
leftOffTop: number; leftOffTop: number;
setLeftOffTop: (leftOffTop: number) => void; setLeftOffTop: (leftOffTop: number) => void;
ws: any;
openWebSocket: (query: string, resetEntries: boolean) => void; openWebSocket: (query: string, resetEntries: boolean) => void;
leftOffBottom: number; leftOffBottom: number;
truncatedTimestamp: number; truncatedTimestamp: number;
@@ -33,14 +34,13 @@ interface EntriesListProps {
scrollableRef: any; scrollableRef: any;
} }
const api = Api.getInstance(); export const EntriesList: React.FC<EntriesListProps> = ({listEntryREF, onSnapBrokenEvent, isSnappedToBottom, setIsSnappedToBottom, queriedCurrent, setQueriedCurrent, queriedTotal, setQueriedTotal, startTime, noMoreDataTop, setNoMoreDataTop, leftOffTop, setLeftOffTop, openWebSocket, leftOffBottom, truncatedTimestamp, setTruncatedTimestamp, scrollableRef}) => {
export const EntriesList: React.FC<EntriesListProps> = ({listEntryREF, onSnapBrokenEvent, isSnappedToBottom, setIsSnappedToBottom, queriedCurrent, setQueriedCurrent, queriedTotal, setQueriedTotal, startTime, noMoreDataTop, setNoMoreDataTop, leftOffTop, setLeftOffTop, ws, openWebSocket, leftOffBottom, truncatedTimestamp, setTruncatedTimestamp, scrollableRef}) => {
const [entries, setEntries] = useRecoilState(entriesAtom); const [entries, setEntries] = useRecoilState(entriesAtom);
const wsConnection = useRecoilValue(wsConnectionAtom); const wsConnection = useRecoilValue(wsConnectionAtom);
const query = useRecoilValue(queryAtom); const query = useRecoilValue(queryAtom);
const isWsConnectionClosed = wsConnection === WsConnectionStatus.Closed; const isWsConnectionClosed = wsConnection === WsConnectionStatus.Closed;
const trafficViewerApi = useRecoilValue(TrafficViewerApiAtom as RecoilState<TrafficViewerApi>)
const [loadMoreTop, setLoadMoreTop] = useState(false); const [loadMoreTop, setLoadMoreTop] = useState(false);
const [isLoadingTop, setIsLoadingTop] = useState(false); const [isLoadingTop, setIsLoadingTop] = useState(false);
@@ -68,7 +68,7 @@ export const EntriesList: React.FC<EntriesListProps> = ({listEntryREF, onSnapBro
return; return;
} }
setIsLoadingTop(true); setIsLoadingTop(true);
const data = await api.fetchEntries(leftOffTop, -1, query, 100, 3000); const data = await trafficViewerApi.fetchEntries(leftOffTop, -1, query, 100, 3000);
if (!data || data.data === null || data.meta === null) { if (!data || data.data === null || data.meta === null) {
setNoMoreDataTop(true); setNoMoreDataTop(true);
setIsLoadingTop(false); setIsLoadingTop(false);
@@ -104,7 +104,7 @@ export const EntriesList: React.FC<EntriesListProps> = ({listEntryREF, onSnapBro
const scrollbarVisible = scrollableRef.current?.childWrapperRef.current.clientHeight > scrollableRef.current?.wrapperRef.current.clientHeight; const scrollbarVisible = scrollableRef.current?.childWrapperRef.current.clientHeight > scrollableRef.current?.wrapperRef.current.clientHeight;
return <> return <React.Fragment>
<div className={styles.list}> <div className={styles.list}>
<div id="list" ref={listEntryREF} className={styles.list}> <div id="list" ref={listEntryREF} className={styles.list}>
{isLoadingTop && <div className={styles.spinnerContainer}> {isLoadingTop && <div className={styles.spinnerContainer}>
@@ -124,7 +124,7 @@ export const EntriesList: React.FC<EntriesListProps> = ({listEntryREF, onSnapBro
title="Fetch old records" title="Fetch old records"
className={`${styles.btnOld} ${!scrollbarVisible && leftOffTop > 0 ? styles.showButton : styles.hideButton}`} className={`${styles.btnOld} ${!scrollbarVisible && leftOffTop > 0 ? styles.showButton : styles.hideButton}`}
onClick={(_) => { onClick={(_) => {
ws.close(); trafficViewerApi.webSocket.close()
getOldEntries(); getOldEntries();
}}> }}>
<img alt="down" src={down} /> <img alt="down" src={down} />
@@ -152,5 +152,5 @@ export const EntriesList: React.FC<EntriesListProps> = ({listEntryREF, onSnapBro
{startTime !== 0 && <div>Started listening at <span style={{marginRight: 5, fontWeight: 600, fontSize: 13}}>{Moment(truncatedTimestamp ? truncatedTimestamp : startTime).utc().format('MM/DD/YYYY, h:mm:ss.SSS A')}</span></div>} {startTime !== 0 && <div>Started listening at <span style={{marginRight: 5, fontWeight: 600, fontSize: 13}}>{Moment(truncatedTimestamp ? truncatedTimestamp : startTime).utc().format('MM/DD/YYYY, h:mm:ss.SSS A')}</span></div>}
</div> </div>
</div> </div>
</>; </React.Fragment>;
}; };

View File

@@ -2,13 +2,15 @@ import React, {useEffect, useState} from "react";
import EntryViewer from "./EntryDetailed/EntryViewer"; import EntryViewer from "./EntryDetailed/EntryViewer";
import {EntryItem} from "./EntryListItem/EntryListItem"; import {EntryItem} from "./EntryListItem/EntryListItem";
import {makeStyles} from "@material-ui/core"; import {makeStyles} from "@material-ui/core";
import Protocol from "./UI/Protocol" import Protocol from "../UI/Protocol"
import Queryable from "./UI/Queryable"; import Queryable from "../UI/Queryable";
import {toast} from "react-toastify"; import {toast} from "react-toastify";
import {useRecoilValue} from "recoil"; import {RecoilState, useRecoilState, useRecoilValue} from "recoil";
import focusedEntryIdAtom from "../recoil/focusedEntryId"; import focusedEntryIdAtom from "../../recoil/focusedEntryId";
import Api from "../helpers/api"; import trafficViewerApi from "../../recoil/TrafficViewerApi";
import queryAtom from "../recoil/query"; import TrafficViewerApi from "./TrafficViewerApi";
import TrafficViewerApiAtom from "../../recoil/TrafficViewerApi/atom";
import queryAtom from "../../recoil/query/atom";
const useStyles = makeStyles(() => ({ const useStyles = makeStyles(() => ({
entryTitle: { entryTitle: {
@@ -78,12 +80,14 @@ const EntrySummary: React.FC<any> = ({entry}) => {
/>; />;
}; };
const api = Api.getInstance();
export const EntryDetailed = () => { export const EntryDetailed = () => {
const focusedEntryId = useRecoilValue(focusedEntryIdAtom); const focusedEntryId = useRecoilValue(focusedEntryIdAtom);
const trafficViewerApi = useRecoilValue(TrafficViewerApiAtom as RecoilState<TrafficViewerApi>)
const query = useRecoilValue(queryAtom); const query = useRecoilValue(queryAtom);
const [entryData, setEntryData] = useState(null); const [entryData, setEntryData] = useState(null);
useEffect(() => { useEffect(() => {
@@ -91,7 +95,7 @@ export const EntryDetailed = () => {
setEntryData(null); setEntryData(null);
(async () => { (async () => {
try { try {
const entryData = await api.getEntry(focusedEntryId, query); const entryData = await trafficViewerApi.getEntry(focusedEntryId, query);
setEntryData(entryData); setEntryData(entryData);
} catch (error) { } catch (error) {
if (error.response?.data?.type) { if (error.response?.data?.type) {
@@ -112,7 +116,7 @@ export const EntryDetailed = () => {
// eslint-disable-next-line // eslint-disable-next-line
}, [focusedEntryId]); }, [focusedEntryId]);
return <> return <React.Fragment>
{entryData && <EntryTitle {entryData && <EntryTitle
protocol={entryData.protocol} protocol={entryData.protocol}
data={entryData.data} data={entryData.data}
@@ -120,7 +124,7 @@ export const EntryDetailed = () => {
elapsedTime={entryData.data.elapsedTime} elapsedTime={entryData.data.elapsedTime}
/>} />}
{entryData && <EntrySummary entry={entryData.base}/>} {entryData && <EntrySummary entry={entryData.base}/>}
<> <React.Fragment>
{entryData && <EntryViewer {entryData && <EntryViewer
representation={entryData.representation} representation={entryData.representation}
isRulesEnabled={entryData.isRulesEnabled} isRulesEnabled={entryData.isRulesEnabled}
@@ -132,6 +136,6 @@ export const EntryDetailed = () => {
elapsedTime={entryData.data.elapsedTime} elapsedTime={entryData.data.elapsedTime}
color={entryData.protocol.backgroundColor} color={entryData.protocol.backgroundColor}
/>} />}
</> </React.Fragment>
</> </React.Fragment>
}; };

View File

@@ -1,4 +1,4 @@
@import '../../variables.module' @import '../../../variables.module'
.title .title
display: flex display: flex

View File

@@ -1,10 +1,10 @@
import styles from "./EntrySections.module.sass"; import styles from "./EntrySections.module.sass";
import React, {useState} from "react"; import React, {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";
import Queryable from "../UI/Queryable"; import Queryable from "../../UI/Queryable";
import Checkbox from "../UI/Checkbox"; 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";
@@ -288,7 +288,7 @@ export const EntryTablePolicySection: React.FC<EntryPolicySectionProps> = ({titl
return <React.Fragment> return <React.Fragment>
{ {
arrayToIterate && arrayToIterate.length > 0 ? arrayToIterate && arrayToIterate.length > 0 ?
<> <React.Fragment>
<EntrySectionContainer title={title} color={color}> <EntrySectionContainer title={title} color={color}>
<table> <table>
<tbody> <tbody>
@@ -296,7 +296,7 @@ export const EntryTablePolicySection: React.FC<EntryPolicySectionProps> = ({titl
return ( return (
<EntryPolicySectionContainer key={index} label={rule.Name} matched={matched && (rule.Type === 'slo' ? rule.ResponseTime >= latency : true)? "Success" : "Failure"}> <EntryPolicySectionContainer key={index} label={rule.Name} matched={matched && (rule.Type === 'slo' ? rule.ResponseTime >= latency : true)? "Success" : "Failure"}>
{ {
<> <React.Fragment>
{ {
rule.Key && rule.Key &&
<tr className={styles.dataValue}><td><b>Key:</b></td> <td>{rule.Key}</td></tr> <tr className={styles.dataValue}><td><b>Key:</b></td> <td>{rule.Key}</td></tr>
@@ -325,7 +325,7 @@ export const EntryTablePolicySection: React.FC<EntryPolicySectionProps> = ({titl
rule.Value && rule.Value &&
<tr className={styles.dataValue}><td><b>Value:</b></td> <td>{rule.Value}</td></tr> <tr className={styles.dataValue}><td><b>Value:</b></td> <td>{rule.Value}</td></tr>
} }
</> </React.Fragment>
} }
</EntryPolicySectionContainer> </EntryPolicySectionContainer>
) )
@@ -335,7 +335,7 @@ export const EntryTablePolicySection: React.FC<EntryPolicySectionProps> = ({titl
</tbody> </tbody>
</table> </table>
</EntrySectionContainer> </EntrySectionContainer>
</> : <span className={styles.noRules}>No rules could be applied to this request.</span> </React.Fragment> : <span className={styles.noRules}>No rules could be applied to this request.</span>
} }
</React.Fragment> </React.Fragment>
} }

View File

@@ -1,4 +1,4 @@
@import "../../variables.module" @import "../../../variables.module"
.Entry .Entry
font-family: "Source Sans Pro", Lucida Grande, Tahoma, sans-serif font-family: "Source Sans Pro", Lucida Grande, Tahoma, sans-serif

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