mirror of
https://github.com/kubeshark/kubeshark.git
synced 2025-08-25 03:49:12 +00:00
* debug builds and gcflags * update dockerfile for debug * service map routes and controller * service map graph structure * service map interface and new methods * adding service map edges from mizu entries * new service map count methods * implementing the status endpoint * ServiceMapResponse and ServiceMapEdge models * service map get endpoint logic * reset logic and endpoint * fixed service map get status * improvements to graph node structure * front-end implementation and service map buttons * new render endpoint to render the graph in real time * spinner sass * new ServiceMapModal component * testing react-force-graph-2d lib * Improvements to service map graph structure, added node id and updated edge source/destination type * Revert "testing react-force-graph-2d lib" This reverts commit1153938386
. * testing react-graph-vis lib * updated to work with react-graph-vis lib * removed render endpoint * go mod tidy * serviceMap config flag * using the serviceMap config flag * passing mizu config to service map as a dependency * service map tests * Removed print functions * finished service map tests * new service property * service map controller tests * moved service map reset button to service map modal reset closes the modal * service map modal refresh button and logic * reset button resets data and refresh * service map modal close button * node size/edge size based on the count value edge color based on protocol * nodes and edges shadow * enabled physics to avoid node overlap, changed kafka protocol color to dark green * showing edges count values and fixed bidirectional edges overlap * go mod tidy * removed console.log * Using the destination node protocol instead of the source node protocol * Revert "debug builds and gcflags" Addressed by #624 and #626 This reverts commit17ecaece3e
. * Revert "update dockerfile for debug" Addressed by #635 This reverts commit5dfc15b140
. * using the entire tap Protocol struct instead of only the protocol name * using the backend protocol background color for node colors * fixed test, the node list order can change * re-factoring to get 100% coverage * using protocol colors just for edges * re-factored service map to use TCP Entry data. Node key is the entry ip-address instead of the name * fallback to ip-address when entry name is unresolved * re-factored front-end * adjustment to main div style * added support for multiple protocols for the same edge * using the item protocol instead of the extension variable * fixed controller tests * displaying service name and ip-address on graph nodes * fixed service map test, we cannot guarantee the slice order * auth middleware * created a new pkg for the service map * re-factoring * re-factored front-end * reverting the import order as previous * Aligning with other UI feature flags handling * we don't need to get the status anymore, we have window["isServiceMapEnabled"] * small adjustments * renamed from .tsx to .ts * button styles and minor improvements * moved service map modal from trafficPage to app component Co-authored-by: Igor Gov <igor.govorov1@gmail.com>
272 lines
5.2 KiB
Go
272 lines
5.2 KiB
Go
package servicemap
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"github.com/up9inc/mizu/shared"
|
|
"github.com/up9inc/mizu/shared/logger"
|
|
tapApi "github.com/up9inc/mizu/tap/api"
|
|
)
|
|
|
|
const (
|
|
ServiceMapEnabled = "enabled"
|
|
ServiceMapDisabled = "disabled"
|
|
UnresolvedNodeName = "unresolved"
|
|
)
|
|
|
|
var instance *serviceMap
|
|
var once sync.Once
|
|
|
|
func GetInstance() ServiceMap {
|
|
once.Do(func() {
|
|
instance = newServiceMap()
|
|
logger.Log.Debug("Service Map Initialized")
|
|
})
|
|
return instance
|
|
}
|
|
|
|
type serviceMap struct {
|
|
config *shared.MizuAgentConfig
|
|
graph *graph
|
|
entriesProcessed int
|
|
}
|
|
|
|
type ServiceMap interface {
|
|
SetConfig(config *shared.MizuAgentConfig)
|
|
IsEnabled() bool
|
|
NewTCPEntry(source *tapApi.TCP, destination *tapApi.TCP, protocol *tapApi.Protocol)
|
|
GetStatus() ServiceMapStatus
|
|
GetNodes() []ServiceMapNode
|
|
GetEdges() []ServiceMapEdge
|
|
GetEntriesProcessedCount() int
|
|
GetNodesCount() int
|
|
GetEdgesCount() int
|
|
Reset()
|
|
}
|
|
|
|
func newServiceMap() *serviceMap {
|
|
return &serviceMap{
|
|
config: nil,
|
|
entriesProcessed: 0,
|
|
graph: newDirectedGraph(),
|
|
}
|
|
}
|
|
|
|
type key string
|
|
|
|
type entryData struct {
|
|
key key
|
|
entry *tapApi.TCP
|
|
}
|
|
|
|
type nodeData struct {
|
|
id int
|
|
entry *tapApi.TCP
|
|
count int
|
|
}
|
|
|
|
type edgeProtocol struct {
|
|
protocol *tapApi.Protocol
|
|
count int
|
|
}
|
|
|
|
type edgeData struct {
|
|
data map[key]*edgeProtocol
|
|
}
|
|
|
|
type graph struct {
|
|
Nodes map[key]*nodeData
|
|
Edges map[key]map[key]*edgeData
|
|
}
|
|
|
|
func newDirectedGraph() *graph {
|
|
return &graph{
|
|
Nodes: make(map[key]*nodeData),
|
|
Edges: make(map[key]map[key]*edgeData),
|
|
}
|
|
}
|
|
|
|
func newNodeData(id int, e *tapApi.TCP) *nodeData {
|
|
return &nodeData{
|
|
id: id,
|
|
entry: e,
|
|
count: 1,
|
|
}
|
|
}
|
|
|
|
func newEdgeData(p *tapApi.Protocol) *edgeData {
|
|
return &edgeData{
|
|
data: map[key]*edgeProtocol{
|
|
key(p.Name): {
|
|
protocol: p,
|
|
count: 1,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func (s *serviceMap) nodeExists(k key) (*nodeData, bool) {
|
|
n, ok := s.graph.Nodes[k]
|
|
return n, ok
|
|
}
|
|
|
|
func (s *serviceMap) addNode(k key, e *tapApi.TCP) (*nodeData, bool) {
|
|
nd, exists := s.nodeExists(k)
|
|
if !exists {
|
|
s.graph.Nodes[k] = newNodeData(len(s.graph.Nodes)+1, e)
|
|
return s.graph.Nodes[k], true
|
|
}
|
|
return nd, false
|
|
}
|
|
|
|
func (s *serviceMap) addEdge(u, v *entryData, p *tapApi.Protocol) {
|
|
if n, ok := s.addNode(u.key, u.entry); !ok {
|
|
n.count++
|
|
}
|
|
if n, ok := s.addNode(v.key, v.entry); !ok {
|
|
n.count++
|
|
}
|
|
|
|
if _, ok := s.graph.Edges[u.key]; !ok {
|
|
s.graph.Edges[u.key] = make(map[key]*edgeData)
|
|
}
|
|
|
|
// new edge u -> v pair
|
|
// protocol is the same for u and v
|
|
if e, ok := s.graph.Edges[u.key][v.key]; ok {
|
|
// edge data already exists for u -> v pair
|
|
// we have a new protocol for this u -> v pair
|
|
|
|
k := key(p.Name)
|
|
if pd, pOk := e.data[k]; pOk {
|
|
// protocol key already exists, just increment the count
|
|
pd.count++
|
|
} else {
|
|
// new protocol key
|
|
e.data[k] = &edgeProtocol{
|
|
protocol: p,
|
|
count: 1,
|
|
}
|
|
}
|
|
} else {
|
|
// new edge data for u -> v pair
|
|
s.graph.Edges[u.key][v.key] = newEdgeData(p)
|
|
}
|
|
|
|
s.entriesProcessed++
|
|
}
|
|
|
|
func (s *serviceMap) SetConfig(config *shared.MizuAgentConfig) {
|
|
s.config = config
|
|
}
|
|
|
|
func (s *serviceMap) IsEnabled() bool {
|
|
if s.config != nil && s.config.ServiceMap {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (s *serviceMap) NewTCPEntry(src *tapApi.TCP, dst *tapApi.TCP, p *tapApi.Protocol) {
|
|
if !s.IsEnabled() {
|
|
return
|
|
}
|
|
|
|
srcEntry := &entryData{
|
|
key: key(src.IP),
|
|
entry: src,
|
|
}
|
|
if len(srcEntry.entry.Name) == 0 {
|
|
srcEntry.entry.Name = UnresolvedNodeName
|
|
}
|
|
|
|
dstEntry := &entryData{
|
|
key: key(dst.IP),
|
|
entry: dst,
|
|
}
|
|
if len(dstEntry.entry.Name) == 0 {
|
|
dstEntry.entry.Name = UnresolvedNodeName
|
|
}
|
|
|
|
s.addEdge(srcEntry, dstEntry, p)
|
|
}
|
|
|
|
func (s *serviceMap) GetStatus() ServiceMapStatus {
|
|
status := ServiceMapDisabled
|
|
if s.IsEnabled() {
|
|
status = ServiceMapEnabled
|
|
}
|
|
|
|
return ServiceMapStatus{
|
|
Status: status,
|
|
EntriesProcessedCount: s.entriesProcessed,
|
|
NodeCount: s.GetNodesCount(),
|
|
EdgeCount: s.GetEdgesCount(),
|
|
}
|
|
}
|
|
|
|
func (s *serviceMap) GetNodes() []ServiceMapNode {
|
|
var nodes []ServiceMapNode
|
|
for i, n := range s.graph.Nodes {
|
|
nodes = append(nodes, ServiceMapNode{
|
|
Id: n.id,
|
|
Name: string(i),
|
|
Entry: n.entry,
|
|
Count: n.count,
|
|
})
|
|
}
|
|
return nodes
|
|
}
|
|
|
|
func (s *serviceMap) GetEdges() []ServiceMapEdge {
|
|
var edges []ServiceMapEdge
|
|
for u, m := range s.graph.Edges {
|
|
for v := range m {
|
|
for _, p := range s.graph.Edges[u][v].data {
|
|
edges = append(edges, ServiceMapEdge{
|
|
Source: ServiceMapNode{
|
|
Id: s.graph.Nodes[u].id,
|
|
Name: string(u),
|
|
Entry: s.graph.Nodes[u].entry,
|
|
Count: s.graph.Nodes[u].count,
|
|
},
|
|
Destination: ServiceMapNode{
|
|
Id: s.graph.Nodes[v].id,
|
|
Name: string(v),
|
|
Entry: s.graph.Nodes[v].entry,
|
|
Count: s.graph.Nodes[v].count,
|
|
},
|
|
Count: p.count,
|
|
Protocol: p.protocol,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
return edges
|
|
}
|
|
|
|
func (s *serviceMap) GetEntriesProcessedCount() int {
|
|
return s.entriesProcessed
|
|
}
|
|
|
|
func (s *serviceMap) GetNodesCount() int {
|
|
return len(s.graph.Nodes)
|
|
}
|
|
|
|
func (s *serviceMap) GetEdgesCount() int {
|
|
var count int
|
|
for u, m := range s.graph.Edges {
|
|
for v := range m {
|
|
for range s.graph.Edges[u][v].data {
|
|
count++
|
|
}
|
|
}
|
|
}
|
|
return count
|
|
}
|
|
|
|
func (s *serviceMap) Reset() {
|
|
s.entriesProcessed = 0
|
|
s.graph = newDirectedGraph()
|
|
}
|