Compare commits

...

29 Commits

Author SHA1 Message Date
lirazyehezkel
f8c0ddd69a HOTFIX: [FE] Avoid overlap only for service map including under 10 services
HOTFIX: [FE] Avoid overlap only for service map including under 10 services #patch
2022-03-30 15:11:08 +03:00
Liraz Yehezkel
2253737625 Try to avoid overlap only for maps including under 10 services 2022-03-30 13:09:10 +03:00
Igor Gov
4a0294c61a Hotfix: [FE] Remove user token from websocket
Hotfix: Remove token from mizu community #patch
Merge pull request #947 from up9inc/hotfix/Remove_token_from_community
2022-03-29 13:01:53 +03:00
Liraz Yehezkel
9053c58146 Remove token from mizu community 2022-03-29 10:25:25 +03:00
Igor Gov
5d5b13fc86 Develop -> main (Release 30.0)
Merge pull request #942 from up9inc/develop #major
Develop -> main (Release 30.0)
2022-03-28 13:00:15 +03:00
Igor Gov
0a9be1884a Traffic viewer list item ebpf to lock icon (#939)
* Traffic viewer changing ebpf to lock icon
2022-03-28 11:10:54 +03:00
Igor Gov
40c745068e Traffic viewer changing ebpf to lock icon (#938) 2022-03-28 10:45:52 +03:00
AmitUp9
10dffd9331 TRA-4419 remove headers from OAS select (#936) 2022-03-28 10:01:57 +03:00
leon-up9
0a800e8d8a passed containerId in the object config (#935)
* passed containerId in the object config

* pkg version

* tmp: run acceptance tests, revert before merge

* reverte

Co-authored-by: Leon <>
2022-03-27 19:41:51 +03:00
RamiBerm
068a4ff86e TRA-4422 line numbers fix (#934)
* fix line numbering in body syntax highlighter

* Packages
2022-03-27 16:17:34 +03:00
leon-up9
c45a869b75 Fix/regration-fixes-24.3 (#933)
* link open in a new tab

* fixed double toast

* added const

* javiers new icon

* epbg img fix

* pkg update

* changed import

* formated

Co-authored-by: Leon <>
2022-03-27 15:47:46 +03:00
leon-up9
0a793cd9e0 added props (#924) 2022-03-27 11:31:40 +03:00
Adrian Gąsior
8d19080c11 Updated Dockerfile and fixed typo in Makefile (#929)
* Updated Dockerfile and fixed typo in Makefile

* PR request

* added small hotfix to basenine
2022-03-27 01:39:50 +03:00
Nimrod Gilboa Markevich
319c3c7a8d Initialize tapper before ws (#932) 2022-03-26 21:25:18 +03:00
M. Mert Yıldıran
0e7704eb15 Bump the version to 0.6.6 and fix the index shift bug that occurs after database truncation (#931) 2022-03-26 18:25:38 +03:00
gadotroee
dbcb776139 no message (#930) 2022-03-26 13:19:04 +03:00
Igor Gov
a3de34f544 Turn OAS feature ON by default (#927)
* Turn OAS feature ON by default
2022-03-24 20:40:16 +02:00
Nimrod Gilboa Markevich
99667984d6 Update tap targets without reinitializing packet source manager (#925) 2022-03-24 15:39:20 +02:00
David Levanon
763b0e7362 quick tls update pods solution (#918)
Update TLS tappers when tapped pods are updated via WS.
2022-03-24 15:21:56 +02:00
AmitUp9
e07e04377f mizu and ui-common version update (#923) 2022-03-24 12:31:08 +02:00
AmitUp9
3c8f63ed92 Feature/UI/tra 4390 status bar banner demo env (#921)
* no message

* banner styling condition added and button text transform disabled

* added prps to container

* build fix
2022-03-24 12:14:31 +02:00
leon-up9
11a2246cb9 Fix/entry-title-responsive-comments (#922)
* Make `EntryTitle` component responsive

* useRequetTextByWidth added

* renamed

Co-authored-by: M. Mert Yildiran <mehmet@up9.com>
Co-authored-by: Leon <>
2022-03-24 12:09:28 +02:00
leon-up9
a2595afd5e changed icon (#920)
Co-authored-by: Leon <>
2022-03-24 10:43:35 +02:00
AmitUp9
0f4710918f TRA-4122 oas gui grooming (#915)
* oas styling
* oas button text change
* ui-common version update
* font fix
* version updating
Co-authored-by: gadotroee <55343099+gadotroee@users.noreply.github.com>
2022-03-23 22:15:04 +02:00
RoyUP9
4bdda920d5 Fix for check command (#916)
* Fix for check command

* empty commit for checks

Co-authored-by: Roee Gadot <roee.gadot@up9.com>
2022-03-23 22:02:29 +02:00
gadotroee
af61c69fb6 Merge pull request #889 from up9inc/develop
develop -> main (Release 29.0) #major
2022-03-13 10:30:12 +02:00
RamiBerm
94dfa68858 Revert "Support stopping oas/servicemesh/telemetry in flight (#867)" (#868)
This reverts commit 50c0062db4.
2022-03-02 09:48:42 +02:00
RamiBerm
50c0062db4 Support stopping oas/servicemesh/telemetry in flight (#867)
* Update oas_generator.go and servicemap.go

* Update oas_generator.go

* Update esClient.go

* Update servicemap.go
2022-03-01 14:41:23 +02:00
Igor Gov
720969bbe6 Merge pull request #861 from up9inc/develop
Develop -> main (28.0) #major
2022-02-27 11:27:05 +02:00
47 changed files with 4294 additions and 1165 deletions

View File

@@ -6,8 +6,7 @@ FROM node:16 AS front-end
WORKDIR /app/ui-build
COPY ui/package.json .
COPY ui/package-lock.json .
COPY ui/package.json ui/package-lock.json ./
RUN npm i
COPY ui .
RUN npm run build
@@ -15,7 +14,7 @@ RUN npm run build
### Base builder image for native builds architecture
FROM golang:1.17-alpine AS builder-native-base
ENV CGO_ENABLED=1 GOOS=linux
RUN apk add libpcap-dev g++ perl-utils
RUN apk add --no-cache libpcap-dev g++ perl-utils
### Intermediate builder image for x86-64 to x86-64 native builds
@@ -77,17 +76,16 @@ RUN go build -ldflags="-extldflags=-static -s -w \
-X 'github.com/up9inc/mizu/agent/pkg/version.Ver=${VER}'" -o mizuagent .
# Download Basenine executable, verify the sha1sum
ADD https://github.com/up9inc/basenine/releases/download/v0.6.5/basenine_linux_${GOARCH} ./basenine_linux_${GOARCH}
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 chmod +x ./basenine_linux_${GOARCH}
RUN mv ./basenine_linux_${GOARCH} ./basenine
ADD https://github.com/up9inc/basenine/releases/download/v0.6.6/basenine_linux_${GOARCH} ./basenine_linux_${GOARCH}
ADD https://github.com/up9inc/basenine/releases/download/v0.6.6/basenine_linux_${GOARCH}.sha256 ./basenine_linux_${GOARCH}.sha256
RUN shasum -a 256 -c basenine_linux_"${GOARCH}".sha256 && \
chmod +x ./basenine_linux_"${GOARCH}" && \
mv ./basenine_linux_"${GOARCH}" ./basenine
### The shipped image
ARG TARGETARCH=amd64
FROM ${TARGETARCH}/busybox:latest
# gin-gonic runs in debug mode without this
ENV GIN_MODE=release

View File

@@ -73,7 +73,7 @@ clean-agent: ## Clean agent.
clean-cli: ## Clean CLI.
@(cd cli; make clean ; echo "CLI cleanup done" )
clean-docker: ## Run clen docker
clean-docker: ## Run clean docker
@(echo "DOCKER cleanup - NOT IMPLEMENTED YET " )
test-lint: ## Run lint on all modules

View File

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

View File

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

View File

@@ -111,9 +111,9 @@ func checkRulesPermissions(ctx context.Context, kubernetesProvider *kubernetes.P
func checkPermissionExist(group string, resource string, verb string, namespace string, exist bool, err error) bool {
var groupAndNamespace string
if group != "" && namespace != "" {
groupAndNamespace = fmt.Sprintf("in group '%v' and namespace '%v'", group, namespace)
groupAndNamespace = fmt.Sprintf("in api group '%v' and namespace '%v'", group, namespace)
} else if group != "" {
groupAndNamespace = fmt.Sprintf("in group '%v'", group)
groupAndNamespace = fmt.Sprintf("in api group '%v'", group)
} else if namespace != "" {
groupAndNamespace = fmt.Sprintf("in namespace '%v'", namespace)
}

View File

@@ -27,13 +27,21 @@ func runMizuCheck() {
checkPassed = check.KubernetesVersion(kubernetesVersion)
}
if config.Config.Check.PreTap {
if checkPassed {
checkPassed = check.TapKubernetesPermissions(ctx, embedFS, kubernetesProvider)
if config.Config.Check.PreTap || config.Config.Check.PreInstall || config.Config.Check.ImagePull {
if config.Config.Check.PreTap {
if checkPassed {
checkPassed = check.TapKubernetesPermissions(ctx, embedFS, kubernetesProvider)
}
} else if config.Config.Check.PreInstall {
if checkPassed {
checkPassed = check.InstallKubernetesPermissions(ctx, kubernetesProvider)
}
}
} else if config.Config.Check.PreInstall {
if checkPassed {
checkPassed = check.InstallKubernetesPermissions(ctx, kubernetesProvider)
if config.Config.Check.ImagePull {
if checkPassed {
checkPassed = check.ImagePullInCluster(ctx, kubernetesProvider)
}
}
} else {
if checkPassed {
@@ -45,12 +53,6 @@ func runMizuCheck() {
}
}
if config.Config.Check.ImagePull {
if checkPassed {
checkPassed = check.ImagePullInCluster(ctx, kubernetesProvider)
}
}
if checkPassed {
logger.Log.Infof("\nStatus check results are %v", fmt.Sprintf(uiUtils.Green, "√"))
} else {

View File

@@ -40,7 +40,7 @@ type ConfigStruct struct {
HeadlessMode bool `yaml:"headless" default:"false"`
LogLevelStr string `yaml:"log-level,omitempty" default:"INFO" readonly:""`
ServiceMap bool `yaml:"service-map" default:"true"`
OAS bool `yaml:"oas,omitempty" default:"false" readonly:""`
OAS bool `yaml:"oas" default:"true"`
Elastic shared.ElasticConfig `yaml:"elastic"`
}

View File

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

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/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/up9inc/basenine/server/lib v0.0.0-20220317230530-8472d80307f6 h1:+RZTD+HdfIW2SMbc65yWkruTY+g5/1Av074m62A74ls=
github.com/up9inc/basenine/server/lib v0.0.0-20220317230530-8472d80307f6/go.mod h1:ZIkxWiJm65jYQIso9k+OZKhR7gQ1we2jNyE2kQX9IQI=
github.com/up9inc/basenine/server/lib v0.0.0-20220326121918-785f3061c8ce h1:PypqybjmuxftGkX4NmP4JAUyEykZj2r6W4r9lnRZ/kE=
github.com/up9inc/basenine/server/lib v0.0.0-20220326121918-785f3061c8ce/go.mod h1:ZIkxWiJm65jYQIso9k+OZKhR7gQ1we2jNyE2kQX9IQI=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk=

View File

@@ -66,6 +66,7 @@ var filteringOptions *api.TrafficFilteringOptions // global
var tapTargets []v1.Pod // global
var packetSourceManager *source.PacketSourceManager // global
var mainPacketInputChan chan source.TcpPacketInfo // global
var tlsTapperInstance *tlstapper.TlsTapper // global
func inArrayInt(arr []int, valueToCheck int) bool {
for _, value := range arr {
@@ -92,7 +93,7 @@ func StartPassiveTapper(opts *TapOpts, outputItems chan *api.OutputChannelItem,
if *tls {
for _, e := range extensions {
if e.Protocol.Name == "http" {
startTlsTapper(e, outputItems, options)
tlsTapperInstance = startTlsTapper(e, outputItems, options)
break
}
}
@@ -102,24 +103,39 @@ func StartPassiveTapper(opts *TapOpts, outputItems chan *api.OutputChannelItem,
diagnose.StartMemoryProfiler(os.Getenv(MemoryProfilingDumpPath), os.Getenv(MemoryProfilingTimeIntervalSeconds))
}
go startPassiveTapper(opts, outputItems)
streamsMap, assembler := initializePassiveTapper(opts, outputItems)
go startPassiveTapper(streamsMap, assembler)
}
func UpdateTapTargets(newTapTargets []v1.Pod) {
success := true
tapTargets = newTapTargets
if err := initializePacketSources(); err != nil {
logger.Log.Fatal(err)
packetSourceManager.UpdatePods(tapTargets)
if tlsTapperInstance != nil {
if err := tlstapper.UpdateTapTargets(tlsTapperInstance, &tapTargets, *procfs); err != nil {
tlstapper.LogError(err)
success = false
}
}
printNewTapTargets()
printNewTapTargets(success)
}
func printNewTapTargets() {
func printNewTapTargets(success bool) {
printStr := ""
for _, tapTarget := range tapTargets {
printStr += fmt.Sprintf("%s (%s), ", tapTarget.Status.PodIP, tapTarget.Name)
}
printStr = strings.TrimRight(printStr, ", ")
logger.Log.Infof("Now tapping: %s", printStr)
if success {
logger.Log.Infof("Now tapping: %s", printStr)
} else {
logger.Log.Errorf("Failed to start tapping: %s", printStr)
}
}
func printPeriodicStats(cleaner *Cleaner) {
@@ -190,9 +206,8 @@ func initializePacketSources() error {
}
}
func startPassiveTapper(opts *TapOpts, outputItems chan *api.OutputChannelItem) {
func initializePassiveTapper(opts *TapOpts, outputItems chan *api.OutputChannelItem) (*tcpStreamMap, *tcpAssembler) {
streamsMap := NewTcpStreamMap()
go streamsMap.closeTimedoutTcpStreamChannels()
diagnose.InitializeErrorsMap(*debug, *verbose, *quiet)
diagnose.InitializeTapperInternalStats()
@@ -205,6 +220,12 @@ func startPassiveTapper(opts *TapOpts, outputItems chan *api.OutputChannelItem)
assembler := NewTcpAssembler(outputItems, streamsMap, opts)
return streamsMap, assembler
}
func startPassiveTapper(streamsMap *tcpStreamMap, assembler *tcpAssembler) {
go streamsMap.closeTimedoutTcpStreamChannels()
diagnose.AppStats.SetStartTime(time.Now())
staleConnectionTimeout := time.Second * time.Duration(*staleTimeoutSeconds)
@@ -236,13 +257,18 @@ func startPassiveTapper(opts *TapOpts, outputItems chan *api.OutputChannelItem)
logger.Log.Infof("AppStats: %v", diagnose.AppStats)
}
func startTlsTapper(extension *api.Extension, outputItems chan *api.OutputChannelItem, options *api.TrafficFilteringOptions) {
func startTlsTapper(extension *api.Extension, outputItems chan *api.OutputChannelItem, options *api.TrafficFilteringOptions) *tlstapper.TlsTapper {
tls := tlstapper.TlsTapper{}
tlsPerfBufferSize := os.Getpagesize() * 100
if err := tls.Init(tlsPerfBufferSize, *procfs, extension); err != nil {
tlstapper.LogError(err)
return
return nil
}
if err := tlstapper.UpdateTapTargets(&tls, &tapTargets, *procfs); err != nil {
tlstapper.LogError(err)
return nil
}
// A quick way to instrument libssl.so without PID filtering - used for debuging and troubleshooting
@@ -250,19 +276,16 @@ func startTlsTapper(extension *api.Extension, outputItems chan *api.OutputChanne
if os.Getenv("MIZU_GLOBAL_SSL_LIBRARY") != "" {
if err := tls.GlobalTap(os.Getenv("MIZU_GLOBAL_SSL_LIBRARY")); err != nil {
tlstapper.LogError(err)
return
return nil
}
}
if err := tlstapper.UpdateTapTargets(&tls, &tapTargets, *procfs); err != nil {
tlstapper.LogError(err)
return
}
var emitter api.Emitter = &api.Emitting{
AppStats: &diagnose.AppStats,
OutputChannel: outputItems,
}
go tls.Poll(emitter, options)
return &tls
}

View File

@@ -11,8 +11,16 @@ import (
const bpfFilterMaxPods = 150
const hostSourcePid = "0"
type PacketSourceManagerConfig struct {
mtls bool
procfs string
interfaceName string
behaviour TcpPacketSourceBehaviour
}
type PacketSourceManager struct {
sources map[string]*tcpPacketSource
config PacketSourceManagerConfig
}
func NewPacketSourceManager(procfs string, filename string, interfaceName string,
@@ -28,7 +36,14 @@ func NewPacketSourceManager(procfs string, filename string, interfaceName string
},
}
sourceManager.UpdatePods(mtls, procfs, pods, interfaceName, behaviour)
sourceManager.config = PacketSourceManagerConfig{
mtls: mtls,
procfs: procfs,
interfaceName: interfaceName,
behaviour: behaviour,
}
sourceManager.UpdatePods(pods)
return sourceManager, nil
}
@@ -49,10 +64,9 @@ func newHostPacketSource(filename string, interfaceName string,
return source, nil
}
func (m *PacketSourceManager) UpdatePods(mtls bool, procfs string, pods []v1.Pod,
interfaceName string, behaviour TcpPacketSourceBehaviour) {
if mtls {
m.updateMtlsPods(procfs, pods, interfaceName, behaviour)
func (m *PacketSourceManager) UpdatePods(pods []v1.Pod) {
if m.config.mtls {
m.updateMtlsPods(m.config.procfs, pods, m.config.interfaceName, m.config.behaviour)
}
m.setBPFFilter(pods)

View File

@@ -24,6 +24,8 @@ func UpdateTapTargets(tls *TlsTapper, pods *[]v1.Pod, procfs string) error {
if err != nil {
return err
}
tls.ClearPids()
for _, pid := range containerPids {
if err := tls.AddPid(procfs, pid); err != nil {

View File

@@ -5,6 +5,7 @@ import (
"github.com/go-errors/errors"
"github.com/up9inc/mizu/shared/logger"
"github.com/up9inc/mizu/tap/api"
"sync"
)
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go tlsTapper bpf/tls_tapper.c -- -O2 -g -D__TARGET_ARCH_x86
@@ -14,6 +15,7 @@ type TlsTapper struct {
syscallHooks syscallHooks
sslHooksStructs []sslHooks
poller *tlsPoller
registeredPids sync.Map
}
func (t *TlsTapper) Init(bufferSize int, procfs string, extension *api.Extension) error {
@@ -70,6 +72,16 @@ func (t *TlsTapper) RemovePid(pid uint32) error {
return nil
}
func (t *TlsTapper) ClearPids() {
t.registeredPids.Range(func(key, v interface{}) bool {
if err := t.RemovePid(key.(uint32)); err != nil {
LogError(err)
}
t.registeredPids.Delete(key)
return true
})
}
func (t *TlsTapper) Close() []error {
errors := make([]error, 0)
@@ -116,6 +128,8 @@ func (t *TlsTapper) tapPid(pid uint32, sslLibrary string) error {
if err := pids.Put(pid, uint32(1)); err != nil {
return errors.Wrap(err, 0)
}
t.registeredPids.Store(pid, true)
return nil
}

View File

@@ -1,4 +1,4 @@
import TrafficViewer,{useWS, DEFAULT_QUERY} from '@up9/mizu-common';
import TrafficViewer,{useWS, DEFAULT_QUERY, OasModal} from '@up9/mizu-common';
import "@up9/mizu-common/dist/index.css"
import {useEffect} from 'react';
import Api, {getWebsocketUrl} from "./api";
@@ -17,8 +17,7 @@ const App = () => {
},[])
return <>
<TrafficViewer message={message} error={error} isWebSocketOpen={isOpen}
trafficViewerApiProp={trafficViewerApi} ></TrafficViewer>
</>
}

View File

@@ -1,12 +1,12 @@
{
"name": "@up9/mizu-common",
"version": "1.0.10",
"version": "1.0.135",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@up9/mizu-common",
"version": "1.0.10",
"version": "1.0.135",
"license": "MIT",
"dependencies": {
"@craco/craco": "^6.4.3",

View File

@@ -1,6 +1,6 @@
{
"name": "@up9/mizu-common",
"version": "1.0.133",
"version": "1.0.144",
"description": "Made with create-react-library",
"author": "",
"license": "MIT",
@@ -27,7 +27,7 @@
"@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.60",
"node-sass": "^6.0.0",
"react":"^17.0.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"recoil": "^0.5.2",
"react-copy-to-clipboard": "^5.0.3",
@@ -77,7 +77,7 @@
"rollup-plugin-postcss": "^4.0.2",
"rollup-plugin-sass": "^1.2.10",
"rollup-plugin-scss": "^3.0.0",
"react":"^17.0.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"typescript": "^4.2.4"
},
@@ -90,4 +90,4 @@
"files": [
"dist"
]
}
}

View File

@@ -6,23 +6,32 @@
padding: 10px
.selectHeader
font-family: Source Sans Pro,Lucida Grande,Tahoma,sans-serif
display: flex
align-items: center
font-weight: 900
width: 100%
margin-top: -1%
.openApilogo
width: 36px
width: 43px
.title
color:#494677
font-family: Lato
font-size: 20px
font-family: Source Sans Pro,Lucida Grande,Tahoma,sans-serif
font-size: 28px
font-weight: 600
.selectContainer
margin-left: 1%
width: 14%
margin-bottom: 1%
margin-top: 1%
.redoc
height: 98%
overflow-y: scroll
height: 85%
overflow-y: scroll
.borderLine
border-top: 1px solid #dee6fe
margin-bottom: 1%

View File

@@ -1,4 +1,4 @@
import { Box, Fade, FormControl, MenuItem, Modal, Backdrop, ListSubheader } from "@material-ui/core";
import { Box, Fade, FormControl, MenuItem, Modal, Backdrop } from "@material-ui/core";
import { useCallback, useEffect, useState } from "react";
import { RedocStandalone } from "redoc";
import closeIcon from "assets/closeIcon.svg";
@@ -9,6 +9,7 @@ import { redocThemeOptions } from "./redocThemeOptions";
import React from "react";
import { Select } from "../UI/Select";
const modalStyle = {
position: 'absolute',
top: '6%',
@@ -23,63 +24,44 @@ const modalStyle = {
color: '#000',
};
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, getOasServices, getOasByService}) => {
const OasModal = ({ openModal, handleCloseModal, getOasServices, getOasByService }) => {
const [oasServices, setOasServices] = useState([] as string[])
const [selectedServiceName, setSelectedServiceName] = useState("");
const [selectedServiceSpec, setSelectedServiceSpec] = useState(null);
const [resolvedServices, setResolvedServices] = useState([]);
const [unResolvedServices, setUnResolvedServices] = useState([]);
const onSelectedOASService = useCallback( async (selectedService) => {
if (!!selectedService){
setSelectedServiceName(selectedService);
if(oasServices.length === 0){
return
}
try {
const data = await getOasByService(selectedService);
setSelectedServiceSpec(data);
const onSelectedOASService = async (selectedService) => {
if (oasServices.length === 0) {
setSelectedServiceSpec(null);
setSelectedServiceName("");
return
}
else {
setSelectedServiceName(selectedService ? selectedService : oasServices[0]);
}
try {
const data = await getOasByService(selectedService ? selectedService : oasServices[0]);
setSelectedServiceSpec(data);
} catch (e) {
toast.error("Error occurred while fetching service OAS spec");
console.error(e);
}
}
},[oasServices.length])
const resolvedArrayBuilder = useCallback(async(services) => {
const resServices = [];
const unResServices = [];
services.forEach(s => {
if(ipAddressWithPortRegex.test(s)){
unResServices.push(s);
}
else {
resServices.push(s);
}
});
resServices.sort();
unResServices.sort();
onSelectedOASService(resServices[0]);
setResolvedServices(resServices);
setUnResolvedServices(unResServices);
},[onSelectedOASService])
}
};
useEffect(() => {
(async () => {
try {
const services = await getOasServices();
resolvedArrayBuilder(services);
setOasServices(services);
} catch (e) {
console.error(e);
}
})();
}, [openModal,resolvedArrayBuilder]);
}, [openModal]);
useEffect(() => {
onSelectedOASService(null);
},[oasServices])
return (
<Modal
@@ -90,48 +72,41 @@ const OasModal = ({ openModal, handleCloseModal, getOasServices, getOasByService
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500,
timeout: 500,
}}
>
<Fade in={openModal}>
<Box sx={modalStyle}>
<div className={style.boxContainer}>
<div className={style.selectHeader}>
<div><img src={openApiLogo} alt="openApi" className={style.openApilogo}/></div>
<div className={style.title}>OpenAPI selected service: </div>
<div className={style.selectContainer} >
<FormControl>
<Select
labelId="service-select-label"
id="service-select"
placeholder="Show OAS"
value={selectedServiceName}
onChangeCb={onSelectedOASService}
>
<ListSubheader disableSticky={true}>Resolved</ListSubheader>
{resolvedServices.map((service) => (
<MenuItem key={service} value={service}>
{service}
</MenuItem>
))}
<ListSubheader disableSticky={true}>UnResolved</ListSubheader>
{unResolvedServices.map((service) => (
<MenuItem key={service} value={service}>
{service}
</MenuItem>
))}
</Select>
</FormControl>
</div>
<div><img src={openApiLogo} alt="openAPI" className={style.openApilogo} /></div>
<div className={style.title}>OpenAPI</div>
</div>
<div style={{ cursor: "pointer" }}>
<img src={closeIcon} alt="close" onClick={handleCloseModal} />
</div>
</div>
<div className={style.selectContainer} >
<FormControl>
<Select
labelId="service-select-label"
id="service-select"
value={selectedServiceName}
onChangeCb={onSelectedOASService}
>
{oasServices.map((service) => (
<MenuItem key={service} value={service}>
{service}
</MenuItem>
))}
</Select>
</FormControl>
</div>
<div className={style.borderLine}></div>
<div className={style.redoc}>
{selectedServiceSpec && <RedocStandalone
spec={selectedServiceSpec}
options={redocThemeOptions}/>}
{selectedServiceSpec && <RedocStandalone
spec={selectedServiceSpec}
options={redocThemeOptions} />}
</div>
</Box>
</Fade>

View File

@@ -1,35 +1,52 @@
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"
}
}
}
}
const fontFamilyVar = "Source Sans Pro, Lucida Grande, Tahoma, sans-serif"
export const redocThemeOptions = {
theme: {
codeBlock: {
backgroundColor: "#14161c",
},
components: {
buttons: {
fontFamily: fontFamilyVar,
},
httpBadges: {
fontFamily: fontFamilyVar,
}
},
colors: {
responses: {
error: {
tabTextColor: "#1b1b29"
},
info: {
tabTextColor: "#1b1b29",
},
success: {
tabTextColor: "#0c0b1a"
},
},
text: {
primary: "#1b1b29",
secondary: "#4d4d4d"
}
},
rightPanel: {
backgroundColor: "#0D0B1D",
},
sidebar: {
backgroundColor: "#ffffff"
},
typography: {
code: {
color: "#0c0b1a",
fontFamily: fontFamilyVar
},
fontFamily: fontFamilyVar,
fontSize: "90%",
fontWieght: "normal",
headings: {
fontFamily: fontFamilyVar
}
}
}
}

View File

@@ -1,16 +1,18 @@
import React, {useEffect, useState} from "react";
import React, { useEffect, useState } from "react";
import EntryViewer from "./EntryDetailed/EntryViewer";
import {EntryItem} from "./EntryListItem/EntryListItem";
import {makeStyles} from "@material-ui/core";
import { EntryItem } from "./EntryListItem/EntryListItem";
import { makeStyles } from "@material-ui/core";
import Protocol from "../UI/Protocol"
import Queryable from "../UI/Queryable";
import {toast} from "react-toastify";
import {RecoilState, useRecoilState, useRecoilValue} from "recoil";
import { toast } from "react-toastify";
import { RecoilState, useRecoilState, useRecoilValue } from "recoil";
import focusedEntryIdAtom from "../../recoil/focusedEntryId";
import trafficViewerApi from "../../recoil/TrafficViewerApi";
import TrafficViewerApi from "./TrafficViewerApi";
import TrafficViewerApiAtom from "../../recoil/TrafficViewerApi/atom";
import queryAtom from "../../recoil/query/atom";
import useWindowDimensions, { useRequestTextByWidth } from "../../hooks/WindowDimensionsHook";
import { TOAST_CONTAINER_ID } from "../../configs/Consts";
const useStyles = makeStyles(() => ({
entryTitle: {
@@ -35,56 +37,59 @@ const useStyles = makeStyles(() => ({
}));
export const formatSize = (n: number) => n > 1000 ? `${Math.round(n / 1000)}KB` : `${n} B`;
const EntryTitle: React.FC<any> = ({protocol, data, elapsedTime}) => {
const minSizeDisplayRequestSize = 880;
const EntryTitle: React.FC<any> = ({ protocol, data, elapsedTime }) => {
const classes = useStyles();
const request = data.request;
const response = data.response;
const { width } = useWindowDimensions();
const { requestText, responseText, elapsedTimeText } = useRequestTextByWidth(width)
return <div className={classes.entryTitle}>
<Protocol protocol={protocol} horizontal={true}/>
<div style={{right: "30px", position: "absolute", display: "flex"}}>
<Protocol protocol={protocol} horizontal={true} />
{(width > minSizeDisplayRequestSize) && <div style={{ right: "30px", position: "absolute", display: "flex" }}>
{request && <Queryable
query={`requestSize == ${data.requestSize}`}
style={{margin: "0 18px"}}
style={{ margin: "0 18px" }}
displayIconOnMouseOver={true}
>
<div
style={{opacity: 0.5}}
style={{ opacity: 0.5 }}
id="entryDetailedTitleRequestSize"
>
{`Request: ${formatSize(data.requestSize)}`}
{`${requestText}${formatSize(data.requestSize)}`}
</div>
</Queryable>}
{response && <Queryable
query={`responseSize == ${data.responseSize}`}
style={{margin: "0 18px"}}
style={{ margin: "0 18px" }}
displayIconOnMouseOver={true}
>
<div
style={{opacity: 0.5}}
style={{ opacity: 0.5 }}
id="entryDetailedTitleResponseSize"
>
{`Response: ${formatSize(data.responseSize)}`}
{`${responseText}${formatSize(data.responseSize)}`}
</div>
</Queryable>}
{response && <Queryable
query={`elapsedTime >= ${elapsedTime}`}
style={{marginRight: 18}}
style={{ margin: "0 0 0 18px" }}
displayIconOnMouseOver={true}
>
<div
style={{opacity: 0.5}}
style={{ opacity: 0.5 }}
id="entryDetailedTitleElapsedTime"
>
{`Elapsed Time: ${Math.round(elapsedTime)}ms`}
{`${elapsedTimeText}${Math.round(elapsedTime)}ms`}
</div>
</Queryable>}
</div>
</div>}
</div>;
};
const EntrySummary: React.FC<any> = ({entry}) => {
const EntrySummary: React.FC<any> = ({ entry }) => {
return <EntryItem
key={`entry-${entry.id}`}
entry={entry}
@@ -113,14 +118,10 @@ export const EntryDetailed = () => {
} catch (error) {
if (error.response?.data?.type) {
toast[error.response.data.type](`Entry[${focusedEntryId}]: ${error.response.data.msg}`, {
position: "bottom-right",
theme: "colored",
autoClose: error.response.data.autoClose,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
containerId: TOAST_CONTAINER_ID
});
}
console.error(error);
@@ -135,7 +136,7 @@ export const EntryDetailed = () => {
data={entryData.data}
elapsedTime={entryData.data.elapsedTime}
/>}
{entryData && <EntrySummary entry={entryData.base}/>}
{entryData && <EntrySummary entry={entryData.base} />}
<React.Fragment>
{entryData && <EntryViewer
representation={entryData.representation}

View File

@@ -4,7 +4,7 @@ import SwapHorizIcon from '@material-ui/icons/SwapHoriz';
import styles from './EntryListItem.module.sass';
import StatusCode, {getClassification, StatusCodeClassification} from "../../UI/StatusCode";
import Protocol, {ProtocolInterface} from "../../UI/Protocol"
import eBPFLogo from '../../assets/ebpf.png';
import eBPFLogo from 'assets/lock.svg';
import {Summary} from "../../UI/Summary";
import Queryable from "../../UI/Queryable";
import ingoingIconSuccess from "assets/ingoing-traffic-success.svg"

View File

@@ -0,0 +1,3 @@
<svg width="12" height="14" viewBox="0 0 12 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.33333 6.36364H8.66667V6.36011H10.6667V6.36364H11C11.2778 6.36364 11.5139 6.45644 11.7083 6.64205C11.9028 6.82765 12 7.05303 12 7.31818V13.0455C12 13.3106 11.9028 13.536 11.7083 13.7216C11.5139 13.9072 11.2778 14 11 14H1C0.722222 14 0.486111 13.9072 0.291666 13.7216C0.0972223 13.536 0 13.3106 0 13.0455V7.31818C0 7.05303 0.0972223 6.82765 0.291666 6.64205C0.486111 6.45644 0.722222 6.36364 1 6.36364H1.33333V4.45455C1.33333 3.23485 1.79167 2.1875 2.70833 1.3125C3.625 0.4375 4.72222 0 6 0C7.27778 0 8.375 0.4375 9.29167 1.3125C9.92325 1.91538 10.3373 2.60007 10.5337 3.36658L8.59659 3.85085C8.48731 3.40176 8.25026 3.00309 7.88542 2.65483C7.36458 2.15767 6.73611 1.90909 6 1.90909C5.26389 1.90909 4.63542 2.15767 4.11458 2.65483C3.59375 3.15199 3.33333 3.75189 3.33333 4.45455V6.36364Z" fill="#BCCEFD"/>
</svg>

After

Width:  |  Height:  |  Size: 959 B

View File

@@ -8,8 +8,7 @@ import { EntryDetailed } from "./EntryDetailed";
import playIcon from 'assets/run.svg';
import pauseIcon from 'assets/pause.svg';
import variables from '../../variables.module.scss';
import { toast,ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import { toast, ToastContainer } from 'react-toastify';
import debounce from 'lodash/debounce';
import { RecoilRoot, RecoilState, useRecoilState, useRecoilValue, useSetRecoilState } from "recoil";
import entriesAtom from "../../recoil/entries";
@@ -20,6 +19,7 @@ import trafficViewerApiAtom from "../../recoil/TrafficViewerApi"
import TrafficViewerApi from "./TrafficViewerApi";
import { StatusBar } from "../UI/StatusBar";
import tappingStatusAtom from "../../recoil/tappingStatus/atom";
import { TOAST_CONTAINER_ID } from "../../configs/Consts";
const useLayoutStyles = makeStyles(() => ({
details: {
@@ -47,13 +47,14 @@ interface TrafficViewerProps {
trafficViewerApiProp: TrafficViewerApi,
actionButtons?: JSX.Element,
isShowStatusBar?: boolean,
webSocketUrl : string,
isCloseWebSocket : boolean
webSocketUrl: string,
isCloseWebSocket: boolean,
isDemoBannerView: boolean
}
export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus, trafficViewerApiProp,
actionButtons,isShowStatusBar,webSocketUrl,
isCloseWebSocket}) => {
export const TrafficViewer: React.FC<TrafficViewerProps> = ({ setAnalyzeStatus, trafficViewerApiProp,
actionButtons, isShowStatusBar, webSocketUrl,
isCloseWebSocket, isDemoBannerView }) => {
const classes = useLayoutStyles();
@@ -105,9 +106,9 @@ export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus,
handleQueryChange(query);
}, [query, handleQueryChange]);
useEffect(()=>{
useEffect(() => {
isCloseWebSocket && closeWebSocket()
},[isCloseWebSocket])
}, [isCloseWebSocket])
const ws = useRef(null);
@@ -125,7 +126,7 @@ export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus,
sendQueryWhenWsOpen(query);
ws.current.onclose = () => {
if(window.location.pathname === "/")
if (window.location.pathname === "/")
setForceRender(forceRender + 1);
}
ws.current.onerror = (event) => {
@@ -139,13 +140,13 @@ export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus,
openWebSocket(`leftOff(${leftOffBottom})`, false);
}
}
} catch (e) {}
} catch (e) { }
}
const sendQueryWhenWsOpen = (query) => {
setTimeout(() => {
if (ws?.current?.readyState === WebSocket.OPEN) {
ws.current.send(JSON.stringify({"query": query, "enableFullEntries": false}));
ws.current.send(JSON.stringify({ "query": query, "enableFullEntries": false }));
} else {
sendQueryWhenWsOpen(query);
}
@@ -153,7 +154,7 @@ export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus,
}
const closeWebSocket = () => {
if(ws?.current?.readyState === WebSocket.OPEN) {
if (ws?.current?.readyState === WebSocket.OPEN) {
ws.current.close();
}
}
@@ -185,14 +186,11 @@ export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus,
break;
case "toast":
toast[message.data.type](message.data.text, {
position: "bottom-right",
theme: "colored",
autoClose: message.data.autoClose,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
containerId: TOAST_CONTAINER_ID
});
break;
case "queryMetadata":
@@ -216,13 +214,13 @@ export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus,
}
useEffect(() => {
setTrafficViewerApiState({...trafficViewerApiProp, webSocket : {close : closeWebSocket}});
setTrafficViewerApiState({ ...trafficViewerApiProp, webSocket: { close: closeWebSocket } });
(async () => {
openWebSocket("leftOff(-1)", true);
try{
try {
const tapStatusResponse = await trafficViewerApiProp.tapStatus();
setTappingStatus(tapStatusResponse);
if(setAnalyzeStatus) {
if (setAnalyzeStatus) {
const analyzeStatusResponse = await trafficViewerApiProp.analyzeStatus();
setAnalyzeStatus(analyzeStatusResponse);
}
@@ -234,7 +232,7 @@ export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus,
}, []);
const toggleConnection = () => {
if(ws?.current?.readyState === WebSocket.OPEN) {
if (ws?.current?.readyState === WebSocket.OPEN) {
ws?.current?.close();
} else {
if (query) {
@@ -293,7 +291,7 @@ export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus,
return (
<div className={TrafficViewerStyles.TrafficPage}>
{tappingStatus && isShowStatusBar && <StatusBar />}
{tappingStatus && isShowStatusBar && <StatusBar isDemoBannerView={isDemoBannerView} />}
<div className={TrafficViewerStyles.TrafficPageHeader}>
<div className={TrafficViewerStyles.TrafficPageStreamStatus}>
<img className={TrafficViewerStyles.playPauseIcon} style={{ visibility: ws?.current?.readyState === WebSocket.OPEN ? "visible" : "hidden" }} alt="pause"
@@ -348,19 +346,28 @@ export const TrafficViewer : React.FC<TrafficViewerProps> = ({setAnalyzeStatus,
setAddressesWithTLS={setAddressesWithTLS}
userDismissedTLSWarning={userDismissedTLSWarning}
setUserDismissedTLSWarning={setUserDismissedTLSWarning} />
<ToastContainer/>
</div>
);
};
const MemoiedTrafficViewer = React.memo(TrafficViewer)
const TrafficViewerContainer: React.FC<TrafficViewerProps> = ({ setAnalyzeStatus, trafficViewerApiProp,
actionButtons, isShowStatusBar = true ,
webSocketUrl, isCloseWebSocket}) => {
actionButtons, isShowStatusBar = true,
webSocketUrl, isCloseWebSocket, isDemoBannerView }) => {
return <RecoilRoot>
<MemoiedTrafficViewer actionButtons={actionButtons} isShowStatusBar={isShowStatusBar} webSocketUrl={webSocketUrl}
isCloseWebSocket={isCloseWebSocket} trafficViewerApiProp={trafficViewerApiProp}
setAnalyzeStatus={setAnalyzeStatus} />
isCloseWebSocket={isCloseWebSocket} trafficViewerApiProp={trafficViewerApiProp}
setAnalyzeStatus={setAnalyzeStatus} isDemoBannerView={isDemoBannerView} />
<ToastContainer enableMultiContainer containerId={TOAST_CONTAINER_ID}
position="bottom-right"
autoClose={5000}
hideProgressBar={false}
newestOnTop={false}
closeOnClick
rtl={false}
pauseOnFocusLoss
draggable
pauseOnHover />
</RecoilRoot>
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -6,11 +6,11 @@ export interface Props {
disabled?: boolean;
}
const Checkbox: React.FC<Props> = ({checked, onToggle, disabled}) => {
const Checkbox: React.FC<Props> = ({checked, onToggle, disabled, ...props}) => {
return (
<div>
<input style={!disabled ? {cursor: "pointer"}: {}} type="checkbox" checked={checked} disabled={disabled} onChange={(event) => onToggle(event.target.checked)}/>
<input style={!disabled ? {cursor: "pointer"}: {}} type="checkbox" checked={checked} disabled={disabled} onChange={(event) => onToggle(event.target.checked)} {...props}/>
</div>
);
};

View File

@@ -11,8 +11,7 @@ export interface InformationIconProps{
export const InformationIcon: React.FC<InformationIconProps> = ({link,style}) => {
return <React.Fragment>
<a href={DEFUALT_LINK ? DEFUALT_LINK : link} style={style} className={styles.flex}>
<a href={DEFUALT_LINK ? DEFUALT_LINK : link} style={style} className={styles.flex} title="documentation" target="_blank">
<img className="headerIcon" src={infoImg} alt="Info icon"/>
</a>
</React.Fragment>

View File

@@ -19,7 +19,7 @@ const menuProps: any = {
};
// icons styles are not overwritten from the Props, only as a separate object
const classes = {icon: styles.icon, selectMenu: styles.list};
const classes = {icon: styles.icon, selectMenu: styles.list, select: styles.oasSelect, root:styles.root};
const defaultProps = {
MenuProps: menuProps,

View File

@@ -10,12 +10,16 @@ const pluralize = (noun: string, amount: number) => {
return `${noun}${amount !== 1 ? 's' : ''}`
}
export const StatusBar = () => {
interface StatusBarProps {
isDemoBannerView: boolean;
}
export const StatusBar = ({isDemoBannerView}) => {
const tappingStatus = useRecoilValue(tappingStatusAtom);
const [expandedBar, setExpandedBar] = useState(false);
const {uniqueNamespaces, amountOfPods, amountOfTappedPods, amountOfUntappedPods} = useRecoilValue(tappingStatusDetails);
return <div className={`${style.statusBar} ${(expandedBar ? `${style.expandedStatusBar}` : "")}`} onMouseOver={() => setExpandedBar(true)} onMouseLeave={() => setExpandedBar(false)} data-cy="expandedStatusBar">
return <div className={`${isDemoBannerView ? `${style.banner}` : ''} ${style.statusBar} ${(expandedBar ? `${style.expandedStatusBar}` : "")}`} onMouseOver={() => setExpandedBar(true)} onMouseLeave={() => setExpandedBar(false)} data-cy="expandedStatusBar">
<div className={style.podsCount}>
{tappingStatus.some(pod => !pod.isTapped) && <img src={warningIcon} alt="warning"/>}
<span className={style.podsCountText} data-cy="podsCountText">

View File

@@ -0,0 +1,39 @@
.highlighterContainer
&.fitScreen
pre
max-height: 90vh
overflow: auto
pre
code
font-size: 0.75rem
&:first-child
margin-right: 0.75rem
background: #F7F9FC
.react-syntax-highlighter-line-number
color: rgb(98, 126, 247)
&:last-child
display: block
code.hljs
white-space: pre-wrap
code.hljs:before
counter-reset: listing
.hljsMarkerLine
counter-increment: listing
.hljsMarkerLine:before
content: counter(listing) " "
display: inline-block
width: 3rem
padding-left: auto
margin-left: auto
text-align: right
opacity: .5

View File

@@ -1,49 +0,0 @@
.highlighterContainer {
&.fitScreen {
pre {
max-height: 90vh;
overflow: auto;
}
}
pre {
code {
font-size: 0.75rem;
&:first-child {
margin-right: 0.75rem;
background: #F7F9FC;
.react-syntax-highlighter-line-number {
color: rgb(98, 126, 247);
}
}
&:last-child {
display: block;
}
}
}
}
code.hljs {
white-space: pre-wrap;
}
code.hljs:before {
counter-reset: listing;
}
code.hljs .hljs-marker-line {
counter-increment: listing;
}
code.hljs .hljs-marker-line:before {
content: counter(listing) " ";
display: inline-block;
width: 3rem;
padding-left: auto;
margin-left: auto;
text-align: right;
opacity: .5;
}

View File

@@ -1,7 +1,7 @@
import React from 'react';
import Lowlight from 'react-lowlight'
import 'highlight.js/styles/atom-one-light.css'
import './index.scss';
import styles from './index.module.sass';
import xml from 'highlight.js/lib/languages/xml'
import json from 'highlight.js/lib/languages/json'
@@ -37,11 +37,11 @@ export const SyntaxHighlighter: React.FC<Props> = ({
const markers = showLineNumbers ? code.split("\n").map((item, i) => {
return {
line: i + 1,
className: 'hljs-marker-line'
className: styles.hljsMarkerLine
}
}) : [];
return <div style={{fontSize: ".75rem"}}><Lowlight language={language ? language : ""} value={code} markers={markers}/></div>;
return <div style={{fontSize: ".75rem"}} className={styles.highlighterContainer}><Lowlight language={language ? language : ""} value={code} markers={markers}/></div>;
};
export default SyntaxHighlighter;

View File

@@ -76,6 +76,7 @@ const Tabs: React.FC<Props> = ({classes={}, tabs, currentTab, color, onChange, l
{tabs.map(({tab, disabled, disabledMessage, highlight, badge}, index) => {
const active = currentTab === tab;
const tabLink = <span
data-cy={"tab-" + tab}
key={tab}
className={`${_classes.tab} ${active ? _classes.active : ''} ${disabled ? _classes.disabled : ''} ${highlight ? _classes.highlight : ''} ${dark ? 'dark' : ''}`}
onClick={() => !disabled && onChange(tab)}

View File

@@ -1,6 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4ZM2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12Z" fill="#627EF7"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 10.1C12.5522 10.1 13 10.5477 13 11.1V14.6H13.8C14.3522 14.6 14.8 15.0477 14.8 15.6C14.8 16.1523 14.3522 16.6 13.8 16.6H10.2C9.64767 16.6 9.19995 16.1523 9.19995 15.6C9.19995 15.0477 9.64767 14.6 10.2 14.6H11V11.1C11 10.5477 11.4477 10.1 12 10.1Z" fill="#627EF7"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 11C13 11.5523 12.5523 12 12 12L10 12C9.44772 12 9 11.5523 9 11C9 10.4477 9.44772 10 10 10L12 10C12.5523 10 13 10.4477 13 11Z" fill="#627EF7"/>
<circle cx="12" cy="8" r="1" fill="#627EF7"/>
<path d="M19 21H6.14286C5.07143 21 4 20.32 4 18.96C4 17.6 5.07143 16.92 6.14286 16.92H19V4H6.14286C5.07143 4 4 5.02 4 6.04V18.96M16.8571 17.6V20.32V17.6Z" stroke="#627EF7" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<rect x="8" y="7" width="7" height="2" fill="#627EF7"/>
<rect x="8" y="11" width="4" height="2" fill="#627EF7"/>
</svg>

Before

Width:  |  Height:  |  Size: 954 B

After

Width:  |  Height:  |  Size: 454 B

View File

@@ -9,4 +9,4 @@ import { InformationIcon } from "./InformationIcon";
export {LoadingOverlay,Select,Tabs,Tooltip,Checkbox,CustomModal,InformationIcon}
export {StatusBar}
export {StatusBar}

View File

@@ -3,3 +3,15 @@
.list
margin-top: 8px
.oasSelect
font-weight: normal
padding: 8px 4px 8px 12px !important
border: 1px solid #9d9d9d !important
border-radius: 9px !important
font-family: Source Sans Pro, Lucida Grande, Tahoma, sans-serif !important
width: 216px !important
.root
font-family: Source Sans Pro, Lucida Grande, Tahoma, sans-serif !important

View File

@@ -19,6 +19,9 @@
overflow: hidden
max-width: clamp(150px,50%,600px)
&.banner
top: 53px
.podsCount
display: flex
justify-content: center

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -0,0 +1 @@
export const TOAST_CONTAINER_ID = "Common";

View File

@@ -0,0 +1,43 @@
import { useState, useEffect } from 'react';
function getWindowDimensions() {
const { innerWidth: width, innerHeight: height } = window;
return {
width,
height
};
}
export function useRequestTextByWidth(windowWidth){
let requestText = "Request: "
let responseText = "Response: "
let elapsedTimeText = "Elapsed Time: "
if (windowWidth < 1078) {
requestText = ""
responseText = ""
elapsedTimeText = ""
} else if (windowWidth < 1356) {
requestText = "Req: "
responseText = "Res: "
elapsedTimeText = "ET: "
}
return {requestText, responseText, elapsedTimeText}
}
export default function useWindowDimensions() {
const [windowDimensions, setWindowDimensions] = useState(getWindowDimensions());
useEffect(() => {
function handleResize() {
setWindowDimensions(getWindowDimensions());
}
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return windowDimensions;
}

4657
ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -13,7 +13,7 @@
"@types/jest": "^26.0.22",
"@types/node": "^12.20.10",
"@uiw/react-textarea-code-editor": "^1.4.12",
"@up9/mizu-common": "1.0.133",
"@up9/mizu-common": "1.0.144",
"axios": "^0.25.0",
"core-js": "^3.20.2",
"craco-babel-loader": "^1.0.3",
@@ -75,4 +75,4 @@
"last 1 safari version"
]
}
}
}

View File

@@ -1,6 +1,6 @@
import React, {useEffect, useState} from "react";
import { Button } from "@material-ui/core";
import Api,{getWebsocketUrl} from "../../../helpers/api";
import Api, { MizuWebsocketURL } from "../../../helpers/api";
import debounce from 'lodash/debounce';
import {useSetRecoilState, useRecoilState} from "recoil";
import {useCommonStyles} from "../../../helpers/commonStyle"
@@ -40,19 +40,19 @@ const trafficViewerApi = {...api}
{window["isOasEnabled"] && <Button
startIcon={<img className="custom" src={services} alt="services"></img>}
size="large"
type="submit"
variant="contained"
className={commonClasses.outlinedButton + " " + commonClasses.imagedButton}
style={{ marginRight: 25 }}
style={{ marginRight: 25, textTransform: 'unset' }}
onClick={handleOpenOasModal}>
Show OAS
OpenAPI Specs
</Button>}
{window["isServiceMapEnabled"] && <Button
startIcon={<img src={serviceMap} className="custom" alt="service-map" style={{marginRight:"8%"}}></img>}
size="large"
variant="contained"
className={commonClasses.outlinedButton + " " + commonClasses.imagedButton}
onClick={openServiceMapModalDebounce}>
onClick={openServiceMapModalDebounce}
style={{textTransform: 'unset'}}>
Service Map
</Button>}
</div>
@@ -65,8 +65,8 @@ const trafficViewerApi = {...api}
return (
<>
<TrafficViewer setAnalyzeStatus={setAnalyzeStatus} webSocketUrl={getWebsocketUrl()} isCloseWebSocket={!openWebSocket}
trafficViewerApiProp={trafficViewerApi} actionButtons={actionButtons} isShowStatusBar={!openOasModal}/>
<TrafficViewer setAnalyzeStatus={setAnalyzeStatus} webSocketUrl={MizuWebsocketURL} isCloseWebSocket={!openWebSocket}
trafficViewerApiProp={trafficViewerApi} actionButtons={actionButtons} isShowStatusBar={!openOasModal} isDemoBannerView={false}/>
</>
);
};

View File

@@ -11,6 +11,7 @@ import ServiceMapOptions from './ServiceMapOptions'
import { useCommonStyles } from "../../helpers/commonStyle";
import refresh from "../assets/refresh.svg";
import close from "../assets/close.svg";
import { TOAST_CONTAINER_ID } from "../../consts";
interface GraphData {
nodes: Node[];
@@ -99,6 +100,7 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onOpen
const commonClasses = useCommonStyles();
const [isLoading, setIsLoading] = useState<boolean>(true);
const [graphData, setGraphData] = useState<GraphData>({ nodes: [], edges: [] });
const [graphOptions, setGraphOptions] = useState(ServiceMapOptions);
const getServiceMapData = useCallback(async () => {
try {
@@ -140,7 +142,7 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onOpen
setGraphData(newGraphData)
} catch (ex) {
toast.error("An error occurred while loading Mizu Service Map, see console for mode details");
toast.error("An error occurred while loading Mizu Service Map, see console for mode details", { containerId: TOAST_CONTAINER_ID });
console.error(ex);
} finally {
setIsLoading(false)
@@ -148,6 +150,14 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onOpen
// eslint-disable-next-line
}, [isOpen])
useEffect(() => {
if(graphData?.nodes?.length === 0) return;
let options = {...graphOptions};
options.physics.barnesHut.avoidOverlap = graphData?.nodes?.length > 10 ? 0 : 1;
setGraphOptions(options);
// eslint-disable-next-line
},[graphData?.nodes?.length])
useEffect(() => {
getServiceMapData();
return () => setGraphData({ nodes: [], edges: [] })
@@ -176,32 +186,32 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onOpen
<img alt="spinner" src={spinnerImg} style={{ height: 50 }} />
</div>}
{!isLoading && <div style={{ height: "100%", width: "100%" }}>
<div style={{ display: "flex", justifyContent: "space-between" }}>
<div>
<Button
startIcon={<img src={refresh} className="custom" alt="refresh" style={{ marginRight:"8%"}}></img>}
size="medium"
variant="contained"
className={commonClasses.outlinedButton + " " + commonClasses.imagedButton}
onClick={refreshServiceMap}
>
Refresh
</Button>
<div style={{ display: "flex", justifyContent: "space-between" }}>
<div>
<Button
startIcon={<img src={refresh} className="custom" alt="refresh" style={{ marginRight: "8%" }}/>}
size="medium"
variant="contained"
className={commonClasses.outlinedButton + " " + commonClasses.imagedButton}
onClick={refreshServiceMap}
>
Refresh
</Button>
</div>
<img src={close} alt="close" onClick={() => onClose()} style={{ cursor: "pointer" }}/>
</div>
<img src={close} alt="close" onClick={() => onClose()} style={{cursor:"pointer"}}></img>
</div>
<Graph
graph={graphData}
options={ServiceMapOptions}
options={graphOptions}
/>
<div className='legend-scale'>
<ul className='legend-labels'>
<li><span style={{ background: '#205cf5' }}></span>HTTP</li>
<li><span style={{ background: '#244c5a' }}></span>HTTP/2</li>
<li><span style={{ background: '#244c5a' }}></span>gRPC</li>
<li><span style={{ background: '#ff6600' }}></span>AMQP</li>
<li><span style={{ background: '#000000' }}></span>KAFKA</li>
<li><span style={{ background: '#a41e11' }}></span>REDIS</li>
<li><span style={{ background: '#205cf5' }}/>HTTP</li>
<li><span style={{ background: '#244c5a' }}/>HTTP/2</li>
<li><span style={{ background: '#244c5a' }}/>gRPC</li>
<li><span style={{ background: '#ff6600' }}/>AMQP</li>
<li><span style={{ background: '#000000' }}/>KAFKA</li>
<li><span style={{ background: '#a41e11' }}/>REDIS</li>
</ul>
</div>
</div>}
@@ -210,4 +220,4 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onOpen
</Modal>
);
}
}

View File

@@ -148,7 +148,7 @@ const ServiceMapOptions = {
springLength: 180,
springConstant: 0.04,
damping: 0.2,
avoidOverlap: 1
avoidOverlap: 0
},
},
layout: {
@@ -171,4 +171,4 @@ const ServiceMapOptions = {
},
};
export default ServiceMapOptions
export default ServiceMapOptions

View File

@@ -1 +1,2 @@
export const adminUsername = "admin";
export const TOAST_CONTAINER_ID = "Community";

View File

@@ -3,13 +3,10 @@ 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 = ""
let client = null
let source = null
@@ -24,8 +21,6 @@ export default class Api {
}
constructor() {
token = localStorage.getItem("token");
client = this.getAxiosClient();
source = null;
}
@@ -125,20 +120,10 @@ export default class Api {
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,
@@ -146,12 +131,3 @@ export default class Api {
});
}
}
export function getWebsocketUrl() {
let websocketUrl = MizuWebsocketURL;
if (token) {
websocketUrl += `/${token}`;
}
return websocketUrl;
}

View File

@@ -1,14 +1,15 @@
import ReactDOM from 'react-dom';
import './index.sass';
import {ToastContainer} from "react-toastify";
import 'react-toastify/dist/ReactToastify.css';
import 'react-toastify/dist/ReactToastify.min.css';
import {RecoilRoot} from "recoil";
import App from './App';
import { TOAST_CONTAINER_ID } from './consts';
ReactDOM.render( <>
<RecoilRoot>
<App/>
<ToastContainer
<ToastContainer enableMultiContainer containerId={TOAST_CONTAINER_ID}
position="bottom-right"
autoClose={5000}
hideProgressBar={false}