mirror of
https://github.com/kubeshark/kubeshark.git
synced 2025-08-20 09:26:00 +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/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||||
github.com/googleapis/gnostic v0.5.5 // indirect
|
github.com/googleapis/gnostic v0.5.5 // indirect
|
||||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // 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/imdario/mergo v0.3.12 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||||
github.com/josharian/intern 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/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.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.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/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/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=
|
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/go-cmp v0.5.7 // indirect
|
||||||
github.com/google/gofuzz v1.2.0 // indirect
|
github.com/google/gofuzz v1.2.0 // indirect
|
||||||
github.com/google/martian v2.1.0+incompatible // 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/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // 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/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/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
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/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.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
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;
|
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;
|
__u32 pid = id >> 32;
|
||||||
__u64 key = (__u64) pid << 32 | fd;
|
__u64 key = (__u64) pid << 32 | fd;
|
||||||
|
|
||||||
struct fd_info *fdinfo = bpf_map_lookup_elem(&file_descriptor_to_ipv4, &key);
|
struct fd_info *fdinfo = bpf_map_lookup_elem(&file_descriptor_to_ipv4, &key);
|
||||||
|
|
||||||
if (fdinfo == NULL) {
|
if (fdinfo == NULL) {
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int err = bpf_probe_read(chunk->address, sizeof(chunk->address), fdinfo->ipv4_addr);
|
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) {
|
if (err != 0) {
|
||||||
log_error(ctx, LOG_ERROR_READING_FD_ADDRESS, id, err, 0l);
|
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,
|
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->len = countBytes;
|
||||||
chunk->fd = info->fd;
|
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);
|
send_chunk(ctx, info->buffer, id, chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
|
"github.com/up9inc/mizu/tap/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
const FLAGS_IS_CLIENT_BIT uint32 = (1 << 0)
|
const FLAGS_IS_CLIENT_BIT uint32 = (1 << 0)
|
||||||
@ -73,3 +74,27 @@ func (c *tlsChunk) getRecordedData() []byte {
|
|||||||
func (c *tlsChunk) isRequest() bool {
|
func (c *tlsChunk) isRequest() bool {
|
||||||
return (c.isClient() && c.isWrite()) || (c.isServer() && c.isRead())
|
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,10 +20,17 @@ const (
|
|||||||
INODE_FILED_INDEX = 9
|
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.
|
// This file helps to extract Ip and Port out of a Socket file descriptor.
|
||||||
//
|
//
|
||||||
// The equivalent bash commands are:
|
// The equivalent bash commands are:
|
||||||
//
|
//
|
||||||
// > ls -l /proc/<pid>/fd/<fd>
|
// > ls -l /proc/<pid>/fd/<fd>
|
||||||
// Output something like "socket:[1234]" for sockets - 1234 is the inode of the socket
|
// Output something like "socket:[1234]" for sockets - 1234 is the inode of the socket
|
||||||
// > cat /proc/<pid>/net/tcp | grep <inode>
|
// > cat /proc/<pid>/net/tcp | grep <inode>
|
||||||
@ -31,18 +38,18 @@ const (
|
|||||||
// The 1st and 2nd fields are the source and dest ip and ports in a Hex format
|
// 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
|
// 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)
|
inode, err := getSocketInode(procfs, pid, fd)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return addressPair{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tcppath := fmt.Sprintf("%s/%d/net/tcp", procfs, pid)
|
tcppath := fmt.Sprintf("%s/%d/net/tcp", procfs, pid)
|
||||||
tcp, err := ioutil.ReadFile(tcppath)
|
tcp, err := ioutil.ReadFile(tcppath)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, errors.Wrap(err, 0)
|
return addressPair{}, errors.Wrap(err, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, line := range strings.Split(string(tcp), "\n") {
|
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 inode == parts[INODE_FILED_INDEX] {
|
||||||
if src {
|
srcIp, srcPort, srcErr := parseHexAddress(parts[SRC_ADDRESS_FILED_INDEX])
|
||||||
return parseHexAddress(parts[SRC_ADDRESS_FILED_INDEX])
|
|
||||||
} else {
|
if srcErr != nil {
|
||||||
return parseHexAddress(parts[DST_ADDRESS_FILED_INDEX])
|
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) {
|
func getSocketInode(procfs string, pid uint32, fd uint32) (string, error) {
|
||||||
|
@ -4,7 +4,6 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -16,10 +15,14 @@ import (
|
|||||||
|
|
||||||
"github.com/cilium/ebpf/perf"
|
"github.com/cilium/ebpf/perf"
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
|
"github.com/hashicorp/golang-lru/simplelru"
|
||||||
"github.com/up9inc/mizu/logger"
|
"github.com/up9inc/mizu/logger"
|
||||||
"github.com/up9inc/mizu/tap/api"
|
"github.com/up9inc/mizu/tap/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const fdCachedItemAvgSize = 40
|
||||||
|
const fdCacheMaxItems = 500000 / fdCachedItemAvgSize
|
||||||
|
|
||||||
type tlsPoller struct {
|
type tlsPoller struct {
|
||||||
tls *TlsTapper
|
tls *TlsTapper
|
||||||
readers map[string]*tlsReader
|
readers map[string]*tlsReader
|
||||||
@ -29,10 +32,12 @@ type tlsPoller struct {
|
|||||||
extension *api.Extension
|
extension *api.Extension
|
||||||
procfs string
|
procfs string
|
||||||
pidToNamespace sync.Map
|
pidToNamespace sync.Map
|
||||||
|
fdCache *simplelru.LRU // Actual typs is map[string]addressPair
|
||||||
|
evictedCounter int
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTlsPoller(tls *TlsTapper, extension *api.Extension, procfs string) *tlsPoller {
|
func newTlsPoller(tls *TlsTapper, extension *api.Extension, procfs string) (*tlsPoller, error) {
|
||||||
return &tlsPoller{
|
poller := &tlsPoller{
|
||||||
tls: tls,
|
tls: tls,
|
||||||
readers: make(map[string]*tlsReader),
|
readers: make(map[string]*tlsReader),
|
||||||
closedReaders: make(chan string, 100),
|
closedReaders: make(chan string, 100),
|
||||||
@ -41,6 +46,15 @@ func newTlsPoller(tls *TlsTapper, extension *api.Extension, procfs string) *tlsP
|
|||||||
chunksReader: nil,
|
chunksReader: nil,
|
||||||
procfs: procfs,
|
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 {
|
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,
|
func (p *tlsPoller) handleTlsChunk(chunk *tlsChunk, extension *api.Extension, emitter api.Emitter,
|
||||||
options *api.TrafficFilteringOptions, streamsMap api.TcpStreamMap) error {
|
options *api.TrafficFilteringOptions, streamsMap api.TcpStreamMap) error {
|
||||||
ip, port, err := chunk.getAddress()
|
address, err := p.getSockfdAddressPair(chunk)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
address, err = chunk.getAddressPair()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
key := buildTlsKey(chunk, ip, port)
|
key := buildTlsKey(address)
|
||||||
reader, exists := p.readers[key]
|
reader, exists := p.readers[key]
|
||||||
|
|
||||||
if !exists {
|
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
|
p.readers[key] = reader
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.captureTime = time.Now()
|
reader.newChunk(chunk)
|
||||||
reader.chunks <- chunk
|
|
||||||
|
|
||||||
if os.Getenv("MIZU_VERBOSE_TLS_TAPPER") == "true" {
|
if os.Getenv("MIZU_VERBOSE_TLS_TAPPER") == "true" {
|
||||||
p.logTls(chunk, ip, port)
|
p.logTls(chunk, key, reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
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,
|
emitter api.Emitter, extension *api.Extension, options *api.TrafficFilteringOptions,
|
||||||
streamsMap api.TcpStreamMap) *tlsReader {
|
streamsMap api.TcpStreamMap) *tlsReader {
|
||||||
|
|
||||||
tcpid := p.buildTcpId(chunk, ip, port)
|
tcpid := p.buildTcpId(chunk, address)
|
||||||
|
|
||||||
doneHandler := func(r *tlsReader) {
|
doneHandler := func(r *tlsReader) {
|
||||||
p.closeReader(key, r)
|
p.closeReader(key, r)
|
||||||
@ -196,36 +213,52 @@ func (p *tlsPoller) closeReader(key string, r *tlsReader) {
|
|||||||
p.closedReaders <- key
|
p.closedReaders <- key
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildTlsKey(chunk *tlsChunk, ip net.IP, port uint16) string {
|
func (p *tlsPoller) getSockfdAddressPair(chunk *tlsChunk) (addressPair, error) {
|
||||||
return fmt.Sprintf("%v:%v-%v:%v", chunk.isClient(), chunk.isRead(), ip, port)
|
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 {
|
if err == nil {
|
||||||
myIp, myPort, err := getAddressBySockfd(p.procfs, chunk.Pid, chunk.Fd, chunk.isClient())
|
if !chunk.isRequest() {
|
||||||
|
switchedAddress := addressPair{
|
||||||
if err != nil {
|
srcIp: address.dstIp,
|
||||||
// May happen if the socket already closed, very likely to happen for localhost
|
srcPort: address.dstPort,
|
||||||
//
|
dstIp: address.srcIp,
|
||||||
myIp = api.UnknownIp
|
dstPort: address.srcPort,
|
||||||
myPort = api.UnknownPort
|
}
|
||||||
|
p.fdCache.Add(fdCacheKey, switchedAddress)
|
||||||
|
return switchedAddress, nil
|
||||||
|
} else {
|
||||||
|
p.fdCache.Add(fdCacheKey, address)
|
||||||
|
return address, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if chunk.isRequest() {
|
fromCacheIfc, ok := p.fdCache.Get(fdCacheKey)
|
||||||
return api.TcpID{
|
|
||||||
SrcIP: myIp.String(),
|
if !ok {
|
||||||
DstIP: ip.String(),
|
return addressPair{}, err
|
||||||
SrcPort: strconv.FormatUint(uint64(myPort), 10),
|
}
|
||||||
DstPort: strconv.FormatUint(uint64(port), 10),
|
|
||||||
Ident: "",
|
fromCache, ok := fromCacheIfc.(addressPair)
|
||||||
}
|
|
||||||
} else {
|
if !ok {
|
||||||
return api.TcpID{
|
return address, errors.Errorf("Unable to cast %T to addressPair", fromCacheIfc)
|
||||||
SrcIP: ip.String(),
|
}
|
||||||
DstIP: myIp.String(),
|
|
||||||
SrcPort: strconv.FormatUint(uint64(port), 10),
|
return fromCache, nil
|
||||||
DstPort: strconv.FormatUint(uint64(myPort), 10),
|
}
|
||||||
Ident: "",
|
|
||||||
}
|
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: "",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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
|
var flagsStr string
|
||||||
|
|
||||||
if chunk.isClient() {
|
if chunk.isClient() {
|
||||||
@ -271,13 +304,18 @@ func (p *tlsPoller) logTls(chunk *tlsChunk, ip net.IP, port uint16) {
|
|||||||
flagsStr += "W"
|
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", "")
|
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",
|
logger.Log.Infof("[%-44s] %s #%-4d (fd: %d) (recorded %d/%d:%d) - %s - %s",
|
||||||
chunk.Pid, chunk.Tgid, chunk.Fd, flagsStr, ip, port,
|
key, flagsStr, reader.seenChunks, chunk.Fd,
|
||||||
srcIp, srcPort, dstIp, dstPort,
|
chunk.Recorded, chunk.Len, chunk.Start,
|
||||||
chunk.Recorded, chunk.Len, chunk.Start, str, hex.EncodeToString(chunk.Data[0:chunk.Recorded]))
|
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 {
|
type tlsReader struct {
|
||||||
key string
|
key string
|
||||||
chunks chan *tlsChunk
|
chunks chan *tlsChunk
|
||||||
|
seenChunks int
|
||||||
data []byte
|
data []byte
|
||||||
doneHandler func(r *tlsReader)
|
doneHandler func(r *tlsReader)
|
||||||
progress *api.ReadProgress
|
progress *api.ReadProgress
|
||||||
@ -23,6 +24,12 @@ type tlsReader struct {
|
|||||||
reqResMatcher api.RequestResponseMatcher
|
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) {
|
func (r *tlsReader) Read(p []byte) (int, error) {
|
||||||
var chunk *tlsChunk
|
var chunk *tlsChunk
|
||||||
|
|
||||||
|
@ -46,7 +46,13 @@ func (t *TlsTapper) Init(chunksBufferSize int, logBufferSize int, procfs string,
|
|||||||
return err
|
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)
|
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,6 +1,13 @@
|
|||||||
@import '../../variables.module.scss'
|
@import '../../variables.module.scss'
|
||||||
|
|
||||||
.boxContainer
|
.closeIcon
|
||||||
|
cursor: pointer
|
||||||
|
user-select: none
|
||||||
|
margin-top: -12px
|
||||||
|
margin-right: -12px
|
||||||
|
float: right
|
||||||
|
|
||||||
|
.boxContainer
|
||||||
display: flex
|
display: flex
|
||||||
justifyContent: space-between
|
justifyContent: space-between
|
||||||
padding: 10px
|
padding: 10px
|
||||||
@ -17,7 +24,7 @@
|
|||||||
width: 43px
|
width: 43px
|
||||||
|
|
||||||
.title
|
.title
|
||||||
color:#494677
|
color: #494677
|
||||||
font-family: Source Sans Pro,Lucida Grande,Tahoma,sans-serif
|
font-family: Source Sans Pro,Lucida Grande,Tahoma,sans-serif
|
||||||
font-size: 28px
|
font-size: 28px
|
||||||
font-weight: 600
|
font-weight: 600
|
||||||
@ -37,4 +44,3 @@
|
|||||||
|
|
||||||
.root
|
.root
|
||||||
width: 100%
|
width: 100%
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Box, Fade, FormControl, MenuItem, Modal, Backdrop } from "@material-ui/core";
|
import { Box, Fade, FormControl, Modal, Backdrop } from "@material-ui/core";
|
||||||
import { useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import { RedocStandalone } from "redoc";
|
import { RedocStandalone } from "redoc";
|
||||||
import closeIcon from "assets/closeIcon.svg";
|
import closeIcon from "assets/closeIcon.svg";
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
@ -30,10 +30,10 @@ const OasModal = ({ openModal, handleCloseModal, getOasServices, getOasByService
|
|||||||
const [oasServices, setOasServices] = useState([] as string[])
|
const [oasServices, setOasServices] = useState([] as string[])
|
||||||
const [selectedServiceName, setSelectedServiceName] = useState("");
|
const [selectedServiceName, setSelectedServiceName] = useState("");
|
||||||
const [selectedServiceSpec, setSelectedServiceSpec] = useState(null);
|
const [selectedServiceSpec, setSelectedServiceSpec] = useState(null);
|
||||||
|
|
||||||
const classes = {root: style.root}
|
|
||||||
|
|
||||||
const onSelectedOASService = async (selectedService) => {
|
const classes = { root: style.root }
|
||||||
|
|
||||||
|
const onSelectedOASService = useCallback (async (selectedService) => {
|
||||||
if (oasServices.length === 0) {
|
if (oasServices.length === 0) {
|
||||||
setSelectedServiceSpec(null);
|
setSelectedServiceSpec(null);
|
||||||
setSelectedServiceName("");
|
setSelectedServiceName("");
|
||||||
@ -49,7 +49,8 @@ const OasModal = ({ openModal, handleCloseModal, getOasServices, getOasByService
|
|||||||
toast.error("Error occurred while fetching service OAS spec", { containerId: TOAST_CONTAINER_ID });
|
toast.error("Error occurred while fetching service OAS spec", { containerId: TOAST_CONTAINER_ID });
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
};
|
// eslint-disable-next-line
|
||||||
|
},[oasServices]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
@ -60,11 +61,13 @@ const OasModal = ({ openModal, handleCloseModal, getOasServices, getOasByService
|
|||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
// eslint-disable-next-line
|
||||||
}, [openModal]);
|
}, [openModal]);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onSelectedOASService(null);
|
onSelectedOASService(null);
|
||||||
}, [oasServices])
|
}, [oasServices, onSelectedOASService])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
@ -80,18 +83,17 @@ const OasModal = ({ openModal, handleCloseModal, getOasServices, getOasByService
|
|||||||
>
|
>
|
||||||
<Fade in={openModal}>
|
<Fade in={openModal}>
|
||||||
<Box sx={modalStyle}>
|
<Box sx={modalStyle}>
|
||||||
|
<img src={closeIcon} alt="close" onClick={handleCloseModal} className={style.closeIcon} />
|
||||||
<div className={style.boxContainer}>
|
<div className={style.boxContainer}>
|
||||||
<div className={style.selectHeader}>
|
<div className={style.selectHeader}>
|
||||||
<div><img src={openApiLogo} alt="openAPI" className={style.openApilogo} /></div>
|
<div><img src={openApiLogo} alt="openAPI" className={style.openApilogo} /></div>
|
||||||
<div className={style.title}>Service Catalog</div>
|
<div className={style.title}>Service Catalog</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ cursor: "pointer" }}>
|
|
||||||
<img src={closeIcon} alt="close" onClick={handleCloseModal} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={style.selectContainer} >
|
<div className={style.selectContainer} >
|
||||||
<FormControl classes={classes}>
|
<FormControl classes={classes}>
|
||||||
<SearchableDropdown
|
<SearchableDropdown
|
||||||
options={oasServices}
|
options={oasServices}
|
||||||
selectedValues={selectedServiceName}
|
selectedValues={selectedServiceName}
|
||||||
onChange={onSelectedOASService}
|
onChange={onSelectedOASService}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="20" height="20" viewBox="0 0 20 20" 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="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="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"/>
|
<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>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 507 B After Width: | Height: | Size: 588 B |
@ -56,3 +56,9 @@
|
|||||||
.separtorLine
|
.separtorLine
|
||||||
margin-top: 10px
|
margin-top: 10px
|
||||||
border: 1px solid #E9EBF8
|
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
|
Refresh
|
||||||
</Button>
|
</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>
|
</div>
|
||||||
{isLoading && <div className={spinnerStyle.spinnerContainer}>
|
{isLoading && <div className={spinnerStyle.spinnerContainer}>
|
||||||
<img alt="spinner" src={spinnerImg} style={{ height: 50 }} />
|
<img alt="spinner" src={spinnerImg} style={{ height: 50 }} />
|
||||||
|
Loading…
Reference in New Issue
Block a user