mirror of
https://github.com/kubeshark/kubeshark.git
synced 2025-08-19 08:59:16 +00:00
Merge branch 'develop' into fix/spawn-only-two-goroutines
This commit is contained in:
commit
05495eebde
@ -76,6 +76,7 @@ require (
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/googleapis/gnostic v0.5.5 // indirect
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
|
@ -405,6 +405,7 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
|
@ -18,6 +18,7 @@ require (
|
||||
github.com/google/go-cmp v0.5.7 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/martian v2.1.0+incompatible // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
|
@ -71,6 +71,8 @@ github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2c
|
||||
github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
|
@ -48,14 +48,14 @@ static __always_inline int get_count_bytes(struct pt_regs *ctx, struct ssl_info*
|
||||
return countBytes;
|
||||
}
|
||||
|
||||
static __always_inline void add_address_to_chunk(struct pt_regs *ctx, struct tlsChunk* chunk, __u64 id, __u32 fd) {
|
||||
static __always_inline int add_address_to_chunk(struct pt_regs *ctx, struct tlsChunk* chunk, __u64 id, __u32 fd) {
|
||||
__u32 pid = id >> 32;
|
||||
__u64 key = (__u64) pid << 32 | fd;
|
||||
|
||||
struct fd_info *fdinfo = bpf_map_lookup_elem(&file_descriptor_to_ipv4, &key);
|
||||
|
||||
if (fdinfo == NULL) {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int err = bpf_probe_read(chunk->address, sizeof(chunk->address), fdinfo->ipv4_addr);
|
||||
@ -63,7 +63,10 @@ static __always_inline void add_address_to_chunk(struct pt_regs *ctx, struct tls
|
||||
|
||||
if (err != 0) {
|
||||
log_error(ctx, LOG_ERROR_READING_FD_ADDRESS, id, err, 0l);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static __always_inline void send_chunk_part(struct pt_regs *ctx, __u8* buffer, __u64 id,
|
||||
@ -143,7 +146,12 @@ static __always_inline void output_ssl_chunk(struct pt_regs *ctx, struct ssl_inf
|
||||
chunk->len = countBytes;
|
||||
chunk->fd = info->fd;
|
||||
|
||||
add_address_to_chunk(ctx, chunk, id, chunk->fd);
|
||||
if (!add_address_to_chunk(ctx, chunk, id, chunk->fd)) {
|
||||
// Without an address, we drop the chunk because there is not much to do with it in Go
|
||||
//
|
||||
return;
|
||||
}
|
||||
|
||||
send_chunk(ctx, info->buffer, id, chunk);
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"net"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/up9inc/mizu/tap/api"
|
||||
)
|
||||
|
||||
const FLAGS_IS_CLIENT_BIT uint32 = (1 << 0)
|
||||
@ -73,3 +74,27 @@ func (c *tlsChunk) getRecordedData() []byte {
|
||||
func (c *tlsChunk) isRequest() bool {
|
||||
return (c.isClient() && c.isWrite()) || (c.isServer() && c.isRead())
|
||||
}
|
||||
|
||||
func (c *tlsChunk) getAddressPair() (addressPair, error) {
|
||||
ip, port, err := c.getAddress()
|
||||
|
||||
if err != nil {
|
||||
return addressPair{}, err
|
||||
}
|
||||
|
||||
if c.isRequest() {
|
||||
return addressPair{
|
||||
srcIp: api.UnknownIp,
|
||||
srcPort: api.UnknownPort,
|
||||
dstIp: ip,
|
||||
dstPort: port,
|
||||
}, nil
|
||||
} else {
|
||||
return addressPair{
|
||||
srcIp: ip,
|
||||
srcPort: port,
|
||||
dstIp: api.UnknownIp,
|
||||
dstPort: api.UnknownPort,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,13 @@ const (
|
||||
INODE_FILED_INDEX = 9
|
||||
)
|
||||
|
||||
type addressPair struct {
|
||||
srcIp net.IP
|
||||
srcPort uint16
|
||||
dstIp net.IP
|
||||
dstPort uint16
|
||||
}
|
||||
|
||||
// This file helps to extract Ip and Port out of a Socket file descriptor.
|
||||
//
|
||||
// The equivalent bash commands are:
|
||||
@ -31,18 +38,18 @@ const (
|
||||
// The 1st and 2nd fields are the source and dest ip and ports in a Hex format
|
||||
// 0100007F:50 is 127.0.0.1:80
|
||||
|
||||
func getAddressBySockfd(procfs string, pid uint32, fd uint32, src bool) (net.IP, uint16, error) {
|
||||
func getAddressBySockfd(procfs string, pid uint32, fd uint32) (addressPair, error) {
|
||||
inode, err := getSocketInode(procfs, pid, fd)
|
||||
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
return addressPair{}, err
|
||||
}
|
||||
|
||||
tcppath := fmt.Sprintf("%s/%d/net/tcp", procfs, pid)
|
||||
tcp, err := ioutil.ReadFile(tcppath)
|
||||
|
||||
if err != nil {
|
||||
return nil, 0, errors.Wrap(err, 0)
|
||||
return addressPair{}, errors.Wrap(err, 0)
|
||||
}
|
||||
|
||||
for _, line := range strings.Split(string(tcp), "\n") {
|
||||
@ -53,15 +60,28 @@ func getAddressBySockfd(procfs string, pid uint32, fd uint32, src bool) (net.IP,
|
||||
}
|
||||
|
||||
if inode == parts[INODE_FILED_INDEX] {
|
||||
if src {
|
||||
return parseHexAddress(parts[SRC_ADDRESS_FILED_INDEX])
|
||||
} else {
|
||||
return parseHexAddress(parts[DST_ADDRESS_FILED_INDEX])
|
||||
srcIp, srcPort, srcErr := parseHexAddress(parts[SRC_ADDRESS_FILED_INDEX])
|
||||
|
||||
if srcErr != nil {
|
||||
return addressPair{}, srcErr
|
||||
}
|
||||
|
||||
dstIp, dstPort, dstErr := parseHexAddress(parts[DST_ADDRESS_FILED_INDEX])
|
||||
|
||||
if dstErr != nil {
|
||||
return addressPair{}, dstErr
|
||||
}
|
||||
|
||||
return addressPair{
|
||||
srcIp: srcIp,
|
||||
srcPort: srcPort,
|
||||
dstIp: dstIp,
|
||||
dstPort: dstPort,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, 0, errors.Errorf("address not found [pid: %d] [sockfd: %d] [inode: %s]", pid, fd, inode)
|
||||
return addressPair{}, errors.Errorf("address not found [pid: %d] [sockfd: %d] [inode: %s]", pid, fd, inode)
|
||||
}
|
||||
|
||||
func getSocketInode(procfs string, pid uint32, fd uint32) (string, error) {
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -16,10 +15,14 @@ import (
|
||||
|
||||
"github.com/cilium/ebpf/perf"
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/hashicorp/golang-lru/simplelru"
|
||||
"github.com/up9inc/mizu/logger"
|
||||
"github.com/up9inc/mizu/tap/api"
|
||||
)
|
||||
|
||||
const fdCachedItemAvgSize = 40
|
||||
const fdCacheMaxItems = 500000 / fdCachedItemAvgSize
|
||||
|
||||
type tlsPoller struct {
|
||||
tls *TlsTapper
|
||||
readers map[string]*tlsReader
|
||||
@ -29,10 +32,12 @@ type tlsPoller struct {
|
||||
extension *api.Extension
|
||||
procfs string
|
||||
pidToNamespace sync.Map
|
||||
fdCache *simplelru.LRU // Actual typs is map[string]addressPair
|
||||
evictedCounter int
|
||||
}
|
||||
|
||||
func newTlsPoller(tls *TlsTapper, extension *api.Extension, procfs string) *tlsPoller {
|
||||
return &tlsPoller{
|
||||
func newTlsPoller(tls *TlsTapper, extension *api.Extension, procfs string) (*tlsPoller, error) {
|
||||
poller := &tlsPoller{
|
||||
tls: tls,
|
||||
readers: make(map[string]*tlsReader),
|
||||
closedReaders: make(chan string, 100),
|
||||
@ -41,6 +46,15 @@ func newTlsPoller(tls *TlsTapper, extension *api.Extension, procfs string) *tlsP
|
||||
chunksReader: nil,
|
||||
procfs: procfs,
|
||||
}
|
||||
|
||||
fdCache, err := simplelru.NewLRU(fdCacheMaxItems, poller.fdCacheEvictCallback)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, 0)
|
||||
}
|
||||
|
||||
poller.fdCache = fdCache
|
||||
return poller, nil
|
||||
}
|
||||
|
||||
func (p *tlsPoller) init(bpfObjects *tlsTapperObjects, bufferSize int) error {
|
||||
@ -117,35 +131,38 @@ func (p *tlsPoller) pollChunksPerfBuffer(chunks chan<- *tlsChunk) {
|
||||
|
||||
func (p *tlsPoller) handleTlsChunk(chunk *tlsChunk, extension *api.Extension, emitter api.Emitter,
|
||||
options *api.TrafficFilteringOptions, streamsMap api.TcpStreamMap) error {
|
||||
ip, port, err := chunk.getAddress()
|
||||
address, err := p.getSockfdAddressPair(chunk)
|
||||
|
||||
if err != nil {
|
||||
address, err = chunk.getAddressPair()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
key := buildTlsKey(chunk, ip, port)
|
||||
key := buildTlsKey(address)
|
||||
reader, exists := p.readers[key]
|
||||
|
||||
if !exists {
|
||||
reader = p.startNewTlsReader(chunk, ip, port, key, emitter, extension, options, streamsMap)
|
||||
reader = p.startNewTlsReader(chunk, &address, key, emitter, extension, options, streamsMap)
|
||||
p.readers[key] = reader
|
||||
}
|
||||
|
||||
reader.captureTime = time.Now()
|
||||
reader.chunks <- chunk
|
||||
reader.newChunk(chunk)
|
||||
|
||||
if os.Getenv("MIZU_VERBOSE_TLS_TAPPER") == "true" {
|
||||
p.logTls(chunk, ip, port)
|
||||
p.logTls(chunk, key, reader)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *tlsPoller) startNewTlsReader(chunk *tlsChunk, ip net.IP, port uint16, key string,
|
||||
func (p *tlsPoller) startNewTlsReader(chunk *tlsChunk, address *addressPair, key string,
|
||||
emitter api.Emitter, extension *api.Extension, options *api.TrafficFilteringOptions,
|
||||
streamsMap api.TcpStreamMap) *tlsReader {
|
||||
|
||||
tcpid := p.buildTcpId(chunk, ip, port)
|
||||
tcpid := p.buildTcpId(chunk, address)
|
||||
|
||||
doneHandler := func(r *tlsReader) {
|
||||
p.closeReader(key, r)
|
||||
@ -196,37 +213,53 @@ func (p *tlsPoller) closeReader(key string, r *tlsReader) {
|
||||
p.closedReaders <- key
|
||||
}
|
||||
|
||||
func buildTlsKey(chunk *tlsChunk, ip net.IP, port uint16) string {
|
||||
return fmt.Sprintf("%v:%v-%v:%v", chunk.isClient(), chunk.isRead(), ip, port)
|
||||
}
|
||||
func (p *tlsPoller) getSockfdAddressPair(chunk *tlsChunk) (addressPair, error) {
|
||||
address, err := getAddressBySockfd(p.procfs, chunk.Pid, chunk.Fd)
|
||||
fdCacheKey := fmt.Sprintf("%d:%d", chunk.Pid, chunk.Fd)
|
||||
|
||||
func (p *tlsPoller) buildTcpId(chunk *tlsChunk, ip net.IP, port uint16) api.TcpID {
|
||||
myIp, myPort, err := getAddressBySockfd(p.procfs, chunk.Pid, chunk.Fd, chunk.isClient())
|
||||
|
||||
if err != nil {
|
||||
// May happen if the socket already closed, very likely to happen for localhost
|
||||
//
|
||||
myIp = api.UnknownIp
|
||||
myPort = api.UnknownPort
|
||||
}
|
||||
|
||||
if chunk.isRequest() {
|
||||
return api.TcpID{
|
||||
SrcIP: myIp.String(),
|
||||
DstIP: ip.String(),
|
||||
SrcPort: strconv.FormatUint(uint64(myPort), 10),
|
||||
DstPort: strconv.FormatUint(uint64(port), 10),
|
||||
Ident: "",
|
||||
if err == nil {
|
||||
if !chunk.isRequest() {
|
||||
switchedAddress := addressPair{
|
||||
srcIp: address.dstIp,
|
||||
srcPort: address.dstPort,
|
||||
dstIp: address.srcIp,
|
||||
dstPort: address.srcPort,
|
||||
}
|
||||
p.fdCache.Add(fdCacheKey, switchedAddress)
|
||||
return switchedAddress, nil
|
||||
} else {
|
||||
return api.TcpID{
|
||||
SrcIP: ip.String(),
|
||||
DstIP: myIp.String(),
|
||||
SrcPort: strconv.FormatUint(uint64(port), 10),
|
||||
DstPort: strconv.FormatUint(uint64(myPort), 10),
|
||||
Ident: "",
|
||||
p.fdCache.Add(fdCacheKey, address)
|
||||
return address, nil
|
||||
}
|
||||
}
|
||||
|
||||
fromCacheIfc, ok := p.fdCache.Get(fdCacheKey)
|
||||
|
||||
if !ok {
|
||||
return addressPair{}, err
|
||||
}
|
||||
|
||||
fromCache, ok := fromCacheIfc.(addressPair)
|
||||
|
||||
if !ok {
|
||||
return address, errors.Errorf("Unable to cast %T to addressPair", fromCacheIfc)
|
||||
}
|
||||
|
||||
return fromCache, nil
|
||||
}
|
||||
|
||||
func buildTlsKey(address addressPair) string {
|
||||
return fmt.Sprintf("%s:%d>%s:%d", address.srcIp, address.srcPort, address.dstIp, address.dstPort)
|
||||
}
|
||||
|
||||
func (p *tlsPoller) buildTcpId(chunk *tlsChunk, address *addressPair) api.TcpID {
|
||||
return api.TcpID{
|
||||
SrcIP: address.srcIp.String(),
|
||||
DstIP: address.dstIp.String(),
|
||||
SrcPort: strconv.FormatUint(uint64(address.srcPort), 10),
|
||||
DstPort: strconv.FormatUint(uint64(address.dstPort), 10),
|
||||
Ident: "",
|
||||
}
|
||||
}
|
||||
|
||||
func (p *tlsPoller) addPid(pid uint32, namespace string) {
|
||||
@ -256,7 +289,7 @@ func (p *tlsPoller) clearPids() {
|
||||
})
|
||||
}
|
||||
|
||||
func (p *tlsPoller) logTls(chunk *tlsChunk, ip net.IP, port uint16) {
|
||||
func (p *tlsPoller) logTls(chunk *tlsChunk, key string, reader *tlsReader) {
|
||||
var flagsStr string
|
||||
|
||||
if chunk.isClient() {
|
||||
@ -271,13 +304,18 @@ func (p *tlsPoller) logTls(chunk *tlsChunk, ip net.IP, port uint16) {
|
||||
flagsStr += "W"
|
||||
}
|
||||
|
||||
srcIp, srcPort, _ := getAddressBySockfd(p.procfs, chunk.Pid, chunk.Fd, true)
|
||||
dstIp, dstPort, _ := getAddressBySockfd(p.procfs, chunk.Pid, chunk.Fd, false)
|
||||
|
||||
str := strings.ReplaceAll(strings.ReplaceAll(string(chunk.Data[0:chunk.Recorded]), "\n", " "), "\r", "")
|
||||
|
||||
logger.Log.Infof("PID: %v (tid: %v) (fd: %v) (client: %v) (addr: %v:%v) (fdaddr %v:%v>%v:%v) (recorded %v out of %v starting at %v) - %v - %v",
|
||||
chunk.Pid, chunk.Tgid, chunk.Fd, flagsStr, ip, port,
|
||||
srcIp, srcPort, dstIp, dstPort,
|
||||
chunk.Recorded, chunk.Len, chunk.Start, str, hex.EncodeToString(chunk.Data[0:chunk.Recorded]))
|
||||
logger.Log.Infof("[%-44s] %s #%-4d (fd: %d) (recorded %d/%d:%d) - %s - %s",
|
||||
key, flagsStr, reader.seenChunks, chunk.Fd,
|
||||
chunk.Recorded, chunk.Len, chunk.Start,
|
||||
str, hex.EncodeToString(chunk.Data[0:chunk.Recorded]))
|
||||
}
|
||||
|
||||
func (p *tlsPoller) fdCacheEvictCallback(key interface{}, value interface{}) {
|
||||
p.evictedCounter = p.evictedCounter + 1
|
||||
|
||||
if p.evictedCounter%1000000 == 0 {
|
||||
logger.Log.Infof("Tls fdCache evicted %d items", p.evictedCounter)
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
type tlsReader struct {
|
||||
key string
|
||||
chunks chan *tlsChunk
|
||||
seenChunks int
|
||||
data []byte
|
||||
doneHandler func(r *tlsReader)
|
||||
progress *api.ReadProgress
|
||||
@ -23,6 +24,12 @@ type tlsReader struct {
|
||||
reqResMatcher api.RequestResponseMatcher
|
||||
}
|
||||
|
||||
func (r *tlsReader) newChunk(chunk *tlsChunk) {
|
||||
r.captureTime = time.Now()
|
||||
r.seenChunks = r.seenChunks + 1
|
||||
r.chunks <- chunk
|
||||
}
|
||||
|
||||
func (r *tlsReader) Read(p []byte) (int, error) {
|
||||
var chunk *tlsChunk
|
||||
|
||||
|
@ -46,7 +46,13 @@ func (t *TlsTapper) Init(chunksBufferSize int, logBufferSize int, procfs string,
|
||||
return err
|
||||
}
|
||||
|
||||
t.poller = newTlsPoller(t, extension, procfs)
|
||||
var err error
|
||||
t.poller, err = newTlsPoller(t, extension, procfs)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return t.poller.init(&t.bpfObjects, chunksBufferSize)
|
||||
}
|
||||
|
||||
|
Binary file not shown.
Binary file not shown.
@ -1,5 +0,0 @@
|
||||
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.
|
@ -1,13 +0,0 @@
|
||||
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
43491
ui-common/example/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,48 +0,0 @@
|
||||
{
|
||||
"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.
Before Width: | Height: | Size: 3.8 KiB |
@ -1,48 +0,0 @@
|
||||
<!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>
|
@ -1,15 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
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)
|
||||
})
|
@ -1,24 +0,0 @@
|
||||
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";
|
||||
|
||||
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 <>
|
||||
|
||||
</>
|
||||
}
|
||||
|
||||
export default App
|
@ -1,119 +0,0 @@
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
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;
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
import './index.css'
|
||||
|
||||
import ReactDOM from 'react-dom'
|
||||
import App from './App'
|
||||
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root'))
|
1
ui-common/example/src/react-app-env.d.ts
vendored
1
ui-common/example/src/react-app-env.d.ts
vendored
@ -1 +0,0 @@
|
||||
/// <reference types="react-scripts" />
|
@ -1,5 +0,0 @@
|
||||
// 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';
|
@ -1,41 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
]
|
||||
}
|
@ -1,5 +1,12 @@
|
||||
@import '../../variables.module.scss'
|
||||
|
||||
.closeIcon
|
||||
cursor: pointer
|
||||
user-select: none
|
||||
margin-top: -12px
|
||||
margin-right: -12px
|
||||
float: right
|
||||
|
||||
.boxContainer
|
||||
display: flex
|
||||
justifyContent: space-between
|
||||
@ -37,4 +44,3 @@
|
||||
|
||||
.root
|
||||
width: 100%
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Box, Fade, FormControl, MenuItem, Modal, Backdrop } from "@material-ui/core";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Box, Fade, FormControl, Modal, Backdrop } from "@material-ui/core";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { RedocStandalone } from "redoc";
|
||||
import closeIcon from "assets/closeIcon.svg";
|
||||
import { toast } from 'react-toastify';
|
||||
@ -33,7 +33,7 @@ const OasModal = ({ openModal, handleCloseModal, getOasServices, getOasByService
|
||||
|
||||
const classes = { root: style.root }
|
||||
|
||||
const onSelectedOASService = async (selectedService) => {
|
||||
const onSelectedOASService = useCallback (async (selectedService) => {
|
||||
if (oasServices.length === 0) {
|
||||
setSelectedServiceSpec(null);
|
||||
setSelectedServiceName("");
|
||||
@ -49,7 +49,8 @@ const OasModal = ({ openModal, handleCloseModal, getOasServices, getOasByService
|
||||
toast.error("Error occurred while fetching service OAS spec", { containerId: TOAST_CONTAINER_ID });
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
// eslint-disable-next-line
|
||||
},[oasServices]);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
@ -60,11 +61,13 @@ const OasModal = ({ openModal, handleCloseModal, getOasServices, getOasByService
|
||||
console.error(e);
|
||||
}
|
||||
})();
|
||||
// eslint-disable-next-line
|
||||
}, [openModal]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
onSelectedOASService(null);
|
||||
}, [oasServices])
|
||||
}, [oasServices, onSelectedOASService])
|
||||
|
||||
return (
|
||||
<Modal
|
||||
@ -80,15 +83,14 @@ const OasModal = ({ openModal, handleCloseModal, getOasServices, getOasByService
|
||||
>
|
||||
<Fade in={openModal}>
|
||||
<Box sx={modalStyle}>
|
||||
<img src={closeIcon} alt="close" onClick={handleCloseModal} className={style.closeIcon} />
|
||||
<div className={style.boxContainer}>
|
||||
<div className={style.selectHeader}>
|
||||
<div><img src={openApiLogo} alt="openAPI" className={style.openApilogo} /></div>
|
||||
<div className={style.title}>Service Catalog</div>
|
||||
</div>
|
||||
<div style={{ cursor: "pointer" }}>
|
||||
<img src={closeIcon} alt="close" onClick={handleCloseModal} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={style.selectContainer} >
|
||||
<FormControl classes={classes}>
|
||||
<SearchableDropdown
|
||||
|
@ -1,4 +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 width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M18.591 9.99997C18.591 14.7446 14.7447 18.5909 10.0001 18.5909C5.25546 18.5909 1.40918 14.7446 1.40918 9.99997C1.40918 5.25534 5.25546 1.40906 10.0001 1.40906C14.7447 1.40906 18.591 5.25534 18.591 9.99997Z" fill="#E9EBF8" stroke="#BCCEFD"/>
|
||||
<path d="M13.1604 8.23038L11.95 7.01994L10.1392 8.83078L8.32832 7.01994L7.11789 8.23038L8.92872 10.0412L7.12046 11.8495L8.33089 13.0599L10.1392 11.2517L11.9474 13.0599L13.1579 11.8495L11.3496 10.0412L13.1604 8.23038Z" fill="#205CF5"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 507 B After Width: | Height: | Size: 588 B |
@ -56,3 +56,9 @@
|
||||
.separtorLine
|
||||
margin-top: 10px
|
||||
border: 1px solid #E9EBF8
|
||||
|
||||
.closeIcon
|
||||
cursor: pointer
|
||||
user-select: none
|
||||
margin-top: -15px
|
||||
margin-right: 5px
|
||||
|
@ -203,7 +203,7 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onClos
|
||||
>
|
||||
Refresh
|
||||
</Button>
|
||||
<img src={closeIcon} alt="close" onClick={() => onClose()} style={{ cursor: "pointer", userSelect: "none" }}></img>
|
||||
<img src={closeIcon} alt="close" onClick={() => onClose()} className={styles.closeIcon}></img>
|
||||
</div>
|
||||
{isLoading && <div className={spinnerStyle.spinnerContainer}>
|
||||
<img alt="spinner" src={spinnerImg} style={{ height: 50 }} />
|
||||
|
Loading…
Reference in New Issue
Block a user