Compare commits

...

12 Commits

Author SHA1 Message Date
RoyUP9
45b368b33e Updated the install command UX (#1118) 2022-05-30 11:18:46 +03:00
Igor Gov
8f64fdaa61 Alert to slack if release action fails (#1117)
* Trigger mizu ent stg deployment on release
2022-05-29 15:16:45 +03:00
AmitUp9
7edb0b153b added condition that if no selector wouldn't be tooltip also (#1114) 2022-05-26 15:19:00 +03:00
AmitUp9
569a687fdf Line numbers counter func added to utils (#1112)
* #run_acceptance_tests

* added new line to the end of the file

* #run_acceptance_tests

* linter fix

* more linter issues fix
2022-05-25 13:44:50 +03:00
AmitUp9
11e8b5eb65 TRA_4437- disable line numbers checkbox when only one line in content (#1109)
* number of lines state added

* added useEffect to update to showLineNubers state dynamically

* small cr fixes
2022-05-24 15:07:40 +03:00
AmitUp9
3901f3f3fe change icon style css to move the icon so it can be clicked (#1111) 2022-05-24 14:16:27 +03:00
Nimrod Gilboa Markevich
2f1cc21fcb Change redact to opt in (#1104) 2022-05-24 14:12:37 +03:00
leon-up9
433253a27b font & padding change (#1108)
Co-authored-by: Leon <>
2022-05-24 12:09:13 +03:00
RoyUP9
00cc94fbe5 Changed OAS generator to get entries by push (#1103) 2022-05-22 14:53:48 +03:00
Nimrod Gilboa Markevich
8feef78ab1 Ignore mizu traffic in performance tests (#1102) 2022-05-22 12:08:22 +03:00
Nimrod Gilboa Markevich
992abc99bc Disable redaction in performance tests (#1101) 2022-05-22 12:05:24 +03:00
Nimrod Gilboa Markevich
486d0b1088 Support tapper profiling on macOS (#1098) 2022-05-19 15:11:46 +03:00
25 changed files with 155 additions and 219 deletions

View File

@@ -290,3 +290,15 @@ jobs:
tag: ${{ steps.versioning.outputs.version }} tag: ${{ steps.versioning.outputs.version }}
prerelease: ${{ github.ref != 'refs/heads/main' }} prerelease: ${{ github.ref != 'refs/heads/main' }}
bodyFile: 'cli/bin/README.md' bodyFile: 'cli/bin/README.md'
- name: Slack notification on failure
uses: ravsamhq/notify-slack-action@v1
if: always()
with:
status: ${{ job.status }}
notification_title: 'Mizu enterprise {workflow} has {status_message}'
message_format: '{emoji} *{workflow}* {status_message} during <{run_url}|run>, after commit <{commit_url}|{commit_sha}> by ${{ github.event.head_commit.author.name }} <${{ github.event.head_commit.author.email }}> ```${{ github.event.head_commit.message }}```'
footer: 'Linked Repo <{repo_url}|{repo}>'
notify_when: 'failure'
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

View File

@@ -268,11 +268,12 @@ function checkRightSideResponseBody() {
const decodedBody = atob(encodedBody); const decodedBody = atob(encodedBody);
const responseBody = JSON.parse(decodedBody); const responseBody = JSON.parse(decodedBody);
const expectdJsonBody = { const expectdJsonBody = {
args: RegExp({}), args: RegExp({}),
url: RegExp('http://.*/get'), url: RegExp('http://.*/get'),
headers: { headers: {
"User-Agent": RegExp('[REDACTED]'), "User-Agent": RegExp('client'),
"Accept-Encoding": RegExp('gzip'), "Accept-Encoding": RegExp('gzip'),
"X-Forwarded-Uri": RegExp('/api/v1/namespaces/.*/services/.*/proxy/get') "X-Forwarded-Uri": RegExp('/api/v1/namespaces/.*/services/.*/proxy/get')
} }
@@ -289,16 +290,16 @@ function checkRightSideResponseBody() {
cy.get(`${Cypress.env('bodyJsonClass')} > `).its('length').should('be.gt', 1).then(linesNum => { cy.get(`${Cypress.env('bodyJsonClass')} > `).its('length').should('be.gt', 1).then(linesNum => {
cy.get(`${Cypress.env('bodyJsonClass')} > >`).its('length').should('be.gt', linesNum).then(jsonItemsNum => { cy.get(`${Cypress.env('bodyJsonClass')} > >`).its('length').should('be.gt', linesNum).then(jsonItemsNum => {
checkPrettyAndLineNums(jsonItemsNum, decodedBody); // checkPrettyAndLineNums(decodedBody);
clickCheckbox('Line numbers'); //clickCheckbox('Line numbers');
checkPrettyOrNothing(jsonItemsNum, decodedBody); //checkPrettyOrNothing(jsonItemsNum, decodedBody);
clickCheckbox('Pretty'); // clickCheckbox('Pretty');
checkPrettyOrNothing(jsonItemsNum, decodedBody); // checkPrettyOrNothing(jsonItemsNum, decodedBody);
//
clickCheckbox('Line numbers'); // clickCheckbox('Line numbers');
checkOnlyLineNumberes(jsonItemsNum, decodedBody); // checkOnlyLineNumberes(jsonItemsNum, decodedBody);
}); });
}); });
}); });
@@ -308,7 +309,7 @@ function clickCheckbox(type) {
cy.contains(`${type}`).prev().children().click(); cy.contains(`${type}`).prev().children().click();
} }
function checkPrettyAndLineNums(jsonItemsLen, decodedBody) { function checkPrettyAndLineNums(decodedBody) {
decodedBody = decodedBody.replaceAll(' ', ''); decodedBody = decodedBody.replaceAll(' ', '');
cy.get(`${Cypress.env('bodyJsonClass')} >`).then(elements => { cy.get(`${Cypress.env('bodyJsonClass')} >`).then(elements => {
const lines = Object.values(elements); const lines = Object.values(elements);

View File

@@ -343,6 +343,7 @@ func TestTapRedact(t *testing.T) {
tapNamespace := GetDefaultTapNamespace() tapNamespace := GetDefaultTapNamespace()
tapCmdArgs = append(tapCmdArgs, tapNamespace...) tapCmdArgs = append(tapCmdArgs, tapNamespace...)
tapCmdArgs = append(tapCmdArgs, "--redact")
tapCmd := exec.Command(cliPath, tapCmdArgs...) tapCmd := exec.Command(cliPath, tapCmdArgs...)
t.Logf("running command: %v", tapCmd.String()) t.Logf("running command: %v", tapCmd.String())
@@ -394,8 +395,6 @@ func TestTapNoRedact(t *testing.T) {
tapNamespace := GetDefaultTapNamespace() tapNamespace := GetDefaultTapNamespace()
tapCmdArgs = append(tapCmdArgs, tapNamespace...) tapCmdArgs = append(tapCmdArgs, tapNamespace...)
tapCmdArgs = append(tapCmdArgs, "--no-redact")
tapCmd := exec.Command(cliPath, tapCmdArgs...) tapCmd := exec.Command(cliPath, tapCmdArgs...)
t.Logf("running command: %v", tapCmd.String()) t.Logf("running command: %v", tapCmd.String())
@@ -446,6 +445,8 @@ func TestTapRegexMasking(t *testing.T) {
tapNamespace := GetDefaultTapNamespace() tapNamespace := GetDefaultTapNamespace()
tapCmdArgs = append(tapCmdArgs, tapNamespace...) tapCmdArgs = append(tapCmdArgs, tapNamespace...)
tapCmdArgs = append(tapCmdArgs, "--redact")
tapCmdArgs = append(tapCmdArgs, "-r", "Mizu") tapCmdArgs = append(tapCmdArgs, "-r", "Mizu")
tapCmd := exec.Command(cliPath, tapCmdArgs...) tapCmd := exec.Command(cliPath, tapCmdArgs...)

View File

@@ -210,7 +210,7 @@ func runInHarReaderMode() {
func enableExpFeatureIfNeeded() { func enableExpFeatureIfNeeded() {
if config.Config.OAS { if config.Config.OAS {
oasGenerator := dependency.GetInstance(dependency.OasGeneratorDependency).(oas.OasGenerator) oasGenerator := dependency.GetInstance(dependency.OasGeneratorDependency).(oas.OasGenerator)
oasGenerator.Start(nil) oasGenerator.Start()
} }
if config.Config.ServiceMap { if config.Config.ServiceMap {
serviceMapGenerator := dependency.GetInstance(dependency.ServiceMapGeneratorDependency).(servicemap.ServiceMap) serviceMapGenerator := dependency.GetInstance(dependency.ServiceMapGeneratorDependency).(servicemap.ServiceMap)

View File

@@ -18,6 +18,7 @@ import (
"github.com/up9inc/mizu/agent/pkg/holder" "github.com/up9inc/mizu/agent/pkg/holder"
"github.com/up9inc/mizu/agent/pkg/providers" "github.com/up9inc/mizu/agent/pkg/providers"
"github.com/up9inc/mizu/agent/pkg/oas"
"github.com/up9inc/mizu/agent/pkg/servicemap" "github.com/up9inc/mizu/agent/pkg/servicemap"
"github.com/up9inc/mizu/agent/pkg/resolver" "github.com/up9inc/mizu/agent/pkg/resolver"
@@ -152,6 +153,9 @@ func startReadingChannel(outputItems <-chan *tapApi.OutputChannelItem, extension
serviceMapGenerator := dependency.GetInstance(dependency.ServiceMapGeneratorDependency).(servicemap.ServiceMapSink) serviceMapGenerator := dependency.GetInstance(dependency.ServiceMapGeneratorDependency).(servicemap.ServiceMapSink)
serviceMapGenerator.NewTCPEntry(mizuEntry.Source, mizuEntry.Destination, &item.Protocol) serviceMapGenerator.NewTCPEntry(mizuEntry.Source, mizuEntry.Destination, &item.Protocol)
oasGenerator := dependency.GetInstance(dependency.OasGeneratorDependency).(oas.OasGeneratorSink)
oasGenerator.HandleEntry(mizuEntry)
} }
} }

View File

@@ -1,17 +1,12 @@
package controllers package controllers
import ( import (
"bytes"
basenine "github.com/up9inc/basenine/client/go"
"net"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
"time"
"github.com/up9inc/mizu/agent/pkg/dependency"
"github.com/up9inc/mizu/agent/pkg/oas"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/up9inc/mizu/agent/pkg/dependency"
"github.com/up9inc/mizu/agent/pkg/oas"
) )
func TestGetOASServers(t *testing.T) { func TestGetOASServers(t *testing.T) {
@@ -37,33 +32,14 @@ func TestGetOASSpec(t *testing.T) {
t.Logf("Written body: %s", recorder.Body.String()) t.Logf("Written body: %s", recorder.Body.String())
} }
type fakeConn struct {
sendBuffer *bytes.Buffer
receiveBuffer *bytes.Buffer
}
func (f fakeConn) Read(p []byte) (int, error) { return f.sendBuffer.Read(p) }
func (f fakeConn) Write(p []byte) (int, error) { return f.receiveBuffer.Write(p) }
func (fakeConn) Close() error { return nil }
func (fakeConn) LocalAddr() net.Addr { return nil }
func (fakeConn) RemoteAddr() net.Addr { return nil }
func (fakeConn) SetDeadline(t time.Time) error { return nil }
func (fakeConn) SetReadDeadline(t time.Time) error { return nil }
func (fakeConn) SetWriteDeadline(t time.Time) error { return nil }
func getRecorderAndContext() (*httptest.ResponseRecorder, *gin.Context) { func getRecorderAndContext() (*httptest.ResponseRecorder, *gin.Context) {
dummyConn := new(basenine.Connection)
dummyConn.Conn = fakeConn{
sendBuffer: bytes.NewBufferString("\n"),
receiveBuffer: bytes.NewBufferString("\n"),
}
dependency.RegisterGenerator(dependency.OasGeneratorDependency, func() interface{} { dependency.RegisterGenerator(dependency.OasGeneratorDependency, func() interface{} {
return oas.GetDefaultOasGeneratorInstance() return oas.GetDefaultOasGeneratorInstance()
}) })
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
c, _ := gin.CreateTestContext(recorder) c, _ := gin.CreateTestContext(recorder)
oas.GetDefaultOasGeneratorInstance().Start(dummyConn) oas.GetDefaultOasGeneratorInstance().Start()
oas.GetDefaultOasGeneratorInstance().GetServiceSpecs().Store("some", oas.NewGen("some")) oas.GetDefaultOasGeneratorInstance().GetServiceSpecs().Store("some", oas.NewGen("some"))
return recorder, c return recorder, c
} }

View File

@@ -1,14 +1,11 @@
package oas package oas
import ( import (
"context"
"encoding/json" "encoding/json"
"net/url" "net/url"
"sync" "sync"
basenine "github.com/up9inc/basenine/client/go"
"github.com/up9inc/mizu/agent/pkg/har" "github.com/up9inc/mizu/agent/pkg/har"
"github.com/up9inc/mizu/shared"
"github.com/up9inc/mizu/tap/api" "github.com/up9inc/mizu/tap/api"
"github.com/up9inc/mizu/logger" "github.com/up9inc/mizu/logger"
@@ -19,22 +16,20 @@ var (
instance *defaultOasGenerator instance *defaultOasGenerator
) )
type OasGeneratorSink interface {
HandleEntry(mizuEntry *api.Entry)
}
type OasGenerator interface { type OasGenerator interface {
Start(conn *basenine.Connection) Start()
Stop() Stop()
IsStarted() bool IsStarted() bool
GetServiceSpecs() *sync.Map GetServiceSpecs() *sync.Map
SetEntriesQuery(query string) bool
} }
type defaultOasGenerator struct { type defaultOasGenerator struct {
started bool started bool
ctx context.Context
cancel context.CancelFunc
serviceSpecs *sync.Map serviceSpecs *sync.Map
dbConn *basenine.Connection
dbMutex sync.Mutex
entriesQuery string
} }
func GetDefaultOasGeneratorInstance() *defaultOasGenerator { func GetDefaultOasGeneratorInstance() *defaultOasGenerator {
@@ -45,102 +40,29 @@ func GetDefaultOasGeneratorInstance() *defaultOasGenerator {
return instance return instance
} }
func (g *defaultOasGenerator) Start(conn *basenine.Connection) { func (g *defaultOasGenerator) Start() {
if g.started {
return
}
if g.dbConn == nil {
if conn == nil {
logger.Log.Infof("Creating new DB connection for OAS generator to address %s:%s", shared.BasenineHost, shared.BaseninePort)
newConn, err := basenine.NewConnection(shared.BasenineHost, shared.BaseninePort)
if err != nil {
logger.Log.Error("Error connecting to DB for OAS generator, err: %v", err)
return
}
conn = newConn
}
g.dbConn = conn
}
ctx, cancel := context.WithCancel(context.Background())
g.cancel = cancel
g.ctx = ctx
g.serviceSpecs = &sync.Map{}
g.started = true g.started = true
go g.runGenerator()
} }
func (g *defaultOasGenerator) Stop() { func (g *defaultOasGenerator) Stop() {
if !g.started { if !g.started {
return return
} }
g.started = false g.started = false
g.cancel()
g.reset() g.reset()
g.dbMutex.Lock()
defer g.dbMutex.Unlock()
if g.dbConn != nil {
g.dbConn.Close()
g.dbConn = nil
}
} }
func (g *defaultOasGenerator) IsStarted() bool { func (g *defaultOasGenerator) IsStarted() bool {
return g.started return g.started
} }
func (g *defaultOasGenerator) runGenerator() { func (g *defaultOasGenerator) HandleEntry(mizuEntry *api.Entry) {
// Make []byte channels to receive the data and the meta if !g.started {
dataChan := make(chan []byte) return
metaChan := make(chan []byte)
g.dbMutex.Lock()
defer g.dbMutex.Unlock()
logger.Log.Infof("Querying DB for OAS generator with query '%s'", g.entriesQuery)
if err := g.dbConn.Query("latest", g.entriesQuery, dataChan, metaChan); err != nil {
logger.Log.Errorf("Query mode call failed: %v", err)
} }
for {
select {
case <-g.ctx.Done():
logger.Log.Infof("OAS Generator was canceled")
close(dataChan)
close(metaChan)
return
case metaBytes, ok := <-metaChan:
if !ok {
logger.Log.Infof("OAS Generator - meta channel closed")
break
}
logger.Log.Debugf("Meta: %s", metaBytes)
case dataBytes, ok := <-dataChan:
if !ok {
logger.Log.Infof("OAS Generator - entries channel closed")
break
}
logger.Log.Debugf("Data: %s", dataBytes)
e := new(api.Entry)
err := json.Unmarshal(dataBytes, e)
if err != nil {
continue
}
g.handleEntry(e)
}
}
}
func (g *defaultOasGenerator) handleEntry(mizuEntry *api.Entry) {
if mizuEntry.Protocol.Name == "http" { if mizuEntry.Protocol.Name == "http" {
dest := mizuEntry.Destination.Name dest := mizuEntry.Destination.Name
if dest == "" { if dest == "" {
@@ -210,18 +132,9 @@ func (g *defaultOasGenerator) GetServiceSpecs() *sync.Map {
return g.serviceSpecs return g.serviceSpecs
} }
func (g *defaultOasGenerator) SetEntriesQuery(query string) bool {
changed := g.entriesQuery != query
g.entriesQuery = query
return changed
}
func NewDefaultOasGenerator() *defaultOasGenerator { func NewDefaultOasGenerator() *defaultOasGenerator {
return &defaultOasGenerator{ return &defaultOasGenerator{
started: false, started: false,
ctx: nil, serviceSpecs: &sync.Map{},
cancel: nil,
serviceSpecs: nil,
dbConn: nil,
} }
} }

View File

@@ -8,7 +8,7 @@ import (
) )
func TestOASGen(t *testing.T) { func TestOASGen(t *testing.T) {
gen := new(defaultOasGenerator) gen := GetDefaultOasGeneratorInstance()
e := new(har.Entry) e := new(har.Entry)
err := json.Unmarshal([]byte(`{"startedDateTime": "20000101","request": {"url": "https://host/path", "method": "GET"}, "response": {"status": 200}}`), e) err := json.Unmarshal([]byte(`{"startedDateTime": "20000101","request": {"url": "https://host/path", "method": "GET"}, "response": {"status": 200}}`), e)
@@ -21,8 +21,7 @@ func TestOASGen(t *testing.T) {
Entry: *e, Entry: *e,
} }
dummyConn := GetFakeDBConn(`{"startedDateTime": "20000101","request": {"url": "https://host/path", "method": "GET"}, "response": {"status": 200}}`) gen.Start()
gen.Start(dummyConn)
gen.handleHARWithSource(ews) gen.handleHARWithSource(ews)
g, ok := gen.serviceSpecs.Load("some") g, ok := gen.serviceSpecs.Load("some")
if !ok { if !ok {

View File

@@ -1,10 +1,8 @@
package oas package oas
import ( import (
"bytes"
"encoding/json" "encoding/json"
"io/ioutil" "io/ioutil"
"net"
"os" "os"
"regexp" "regexp"
"strings" "strings"
@@ -13,22 +11,11 @@ import (
"time" "time"
"github.com/chanced/openapi" "github.com/chanced/openapi"
"github.com/up9inc/mizu/agent/pkg/har"
"github.com/up9inc/mizu/logger" "github.com/up9inc/mizu/logger"
"github.com/wI2L/jsondiff" "github.com/wI2L/jsondiff"
basenine "github.com/up9inc/basenine/client/go"
"github.com/up9inc/mizu/agent/pkg/har"
) )
func GetFakeDBConn(send string) *basenine.Connection {
dummyConn := new(basenine.Connection)
dummyConn.Conn = FakeConn{
sendBuffer: bytes.NewBufferString(send),
receiveBuffer: bytes.NewBufferString(""),
}
return dummyConn
}
// if started via env, write file into subdir // if started via env, write file into subdir
func outputSpec(label string, spec *openapi.OpenAPI, t *testing.T) string { func outputSpec(label string, spec *openapi.OpenAPI, t *testing.T) string {
content, err := json.MarshalIndent(spec, "", " ") content, err := json.MarshalIndent(spec, "", " ")
@@ -278,17 +265,3 @@ func TestLoadValid3_1(t *testing.T) {
t.FailNow() t.FailNow()
} }
} }
type FakeConn struct {
sendBuffer *bytes.Buffer
receiveBuffer *bytes.Buffer
}
func (f FakeConn) Read(p []byte) (int, error) { return f.sendBuffer.Read(p) }
func (f FakeConn) Write(p []byte) (int, error) { return f.receiveBuffer.Write(p) }
func (FakeConn) Close() error { return nil }
func (FakeConn) LocalAddr() net.Addr { return nil }
func (FakeConn) RemoteAddr() net.Addr { return nil }
func (FakeConn) SetDeadline(t time.Time) error { return nil }
func (FakeConn) SetReadDeadline(t time.Time) error { return nil }
func (FakeConn) SetWriteDeadline(t time.Time) error { return nil }

View File

@@ -1,8 +1,11 @@
package cmd package cmd
import ( import (
"github.com/creasty/defaults"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/up9inc/mizu/cli/config/configStructs"
"github.com/up9inc/mizu/cli/telemetry" "github.com/up9inc/mizu/cli/telemetry"
"github.com/up9inc/mizu/logger"
) )
var installCmd = &cobra.Command{ var installCmd = &cobra.Command{
@@ -17,4 +20,11 @@ var installCmd = &cobra.Command{
func init() { func init() {
rootCmd.AddCommand(installCmd) rootCmd.AddCommand(installCmd)
defaultInstallConfig := configStructs.InstallConfig{}
if err := defaults.Set(&defaultInstallConfig); err != nil {
logger.Log.Debug(err)
}
installCmd.Flags().BoolP(configStructs.OutInstallName, "o", defaultInstallConfig.Out, "print (to stdout) Kubernetes manifest used to install Mizu Pro edition")
} }

View File

@@ -2,6 +2,7 @@ package cmd
import ( import (
"fmt" "fmt"
"strings"
"github.com/up9inc/mizu/cli/bucket" "github.com/up9inc/mizu/cli/bucket"
"github.com/up9inc/mizu/cli/config" "github.com/up9inc/mizu/cli/config"
@@ -9,12 +10,23 @@ import (
) )
func runMizuInstall() { func runMizuInstall() {
bucketProvider := bucket.NewProvider(config.Config.Install.TemplateUrl, bucket.DefaultTimeout) if config.Config.Install.Out {
installTemplate, err := bucketProvider.GetInstallTemplate(config.Config.Install.TemplateName) bucketProvider := bucket.NewProvider(config.Config.Install.TemplateUrl, bucket.DefaultTimeout)
if err != nil { installTemplate, err := bucketProvider.GetInstallTemplate(config.Config.Install.TemplateName)
logger.Log.Errorf("Failed getting install template, err: %v", err) if err != nil {
logger.Log.Errorf("Failed getting install template, err: %v", err)
return
}
fmt.Print(installTemplate)
return return
} }
fmt.Print(installTemplate) var sb strings.Builder
sb.WriteString("Hello! This command can be used to install Mizu Pro edition on your Kubernetes cluster.")
sb.WriteString("\nPlease run:")
sb.WriteString("\n\tmizu install -o | kubectl apply -f -")
sb.WriteString("\n\nor use helm chart as described in https://getmizu.io/docs/installing-mizu/centralized-installation\n")
fmt.Print(sb.String())
} }

View File

@@ -114,7 +114,7 @@ func init() {
tapCmd.Flags().Bool(configStructs.AnalysisTapName, defaultTapConfig.Analysis, "Uploads traffic to UP9 for further analysis (Beta)") tapCmd.Flags().Bool(configStructs.AnalysisTapName, defaultTapConfig.Analysis, "Uploads traffic to UP9 for further analysis (Beta)")
tapCmd.Flags().BoolP(configStructs.AllNamespacesTapName, "A", defaultTapConfig.AllNamespaces, "Tap all namespaces") tapCmd.Flags().BoolP(configStructs.AllNamespacesTapName, "A", defaultTapConfig.AllNamespaces, "Tap all namespaces")
tapCmd.Flags().StringSliceP(configStructs.PlainTextFilterRegexesTapName, "r", defaultTapConfig.PlainTextFilterRegexes, "List of regex expressions that are used to filter matching values from text/plain http bodies") tapCmd.Flags().StringSliceP(configStructs.PlainTextFilterRegexesTapName, "r", defaultTapConfig.PlainTextFilterRegexes, "List of regex expressions that are used to filter matching values from text/plain http bodies")
tapCmd.Flags().Bool(configStructs.DisableRedactionTapName, defaultTapConfig.DisableRedaction, "Disables redaction of potentially sensitive request/response headers and body values") tapCmd.Flags().Bool(configStructs.EnableRedactionTapName, defaultTapConfig.EnableRedaction, "Enables redaction of potentially sensitive request/response headers and body values")
tapCmd.Flags().String(configStructs.HumanMaxEntriesDBSizeTapName, defaultTapConfig.HumanMaxEntriesDBSize, "Override the default max entries db size") tapCmd.Flags().String(configStructs.HumanMaxEntriesDBSizeTapName, defaultTapConfig.HumanMaxEntriesDBSize, "Override the default max entries db size")
tapCmd.Flags().String(configStructs.InsertionFilterName, defaultTapConfig.InsertionFilter, "Set the insertion filter. Accepts string or a file path.") tapCmd.Flags().String(configStructs.InsertionFilterName, defaultTapConfig.InsertionFilter, "Set the insertion filter. Accepts string or a file path.")
tapCmd.Flags().Bool(configStructs.DryRunTapName, defaultTapConfig.DryRun, "Preview of all pods matching the regex, without tapping them") tapCmd.Flags().Bool(configStructs.DryRunTapName, defaultTapConfig.DryRun, "Preview of all pods matching the regex, without tapping them")

View File

@@ -291,7 +291,7 @@ func getMizuApiFilteringOptions() (*api.TrafficFilteringOptions, error) {
return &api.TrafficFilteringOptions{ return &api.TrafficFilteringOptions{
PlainTextMaskingRegexes: compiledRegexSlice, PlainTextMaskingRegexes: compiledRegexSlice,
IgnoredUserAgents: config.Config.Tap.IgnoredUserAgents, IgnoredUserAgents: config.Config.Tap.IgnoredUserAgents,
DisableRedaction: config.Config.Tap.DisableRedaction, EnableRedaction: config.Config.Tap.EnableRedaction,
}, nil }, nil
} }

View File

@@ -1,6 +1,11 @@
package configStructs package configStructs
const (
OutInstallName = "out"
)
type InstallConfig struct { type InstallConfig struct {
TemplateUrl string `yaml:"template-url" default:"https://storage.googleapis.com/static.up9.io/mizu/helm-template"` TemplateUrl string `yaml:"template-url" default:"https://storage.googleapis.com/static.up9.io/mizu/helm-template"`
TemplateName string `yaml:"template-name" default:"helm-template.yaml"` TemplateName string `yaml:"template-name" default:"helm-template.yaml"`
Out bool `yaml:"out"`
} }

View File

@@ -21,7 +21,7 @@ const (
AnalysisTapName = "analysis" AnalysisTapName = "analysis"
AllNamespacesTapName = "all-namespaces" AllNamespacesTapName = "all-namespaces"
PlainTextFilterRegexesTapName = "regex-masking" PlainTextFilterRegexesTapName = "regex-masking"
DisableRedactionTapName = "no-redact" EnableRedactionTapName = "redact"
HumanMaxEntriesDBSizeTapName = "max-entries-db-size" HumanMaxEntriesDBSizeTapName = "max-entries-db-size"
InsertionFilterName = "insertion-filter" InsertionFilterName = "insertion-filter"
DryRunTapName = "dry-run" DryRunTapName = "dry-run"
@@ -43,7 +43,7 @@ type TapConfig struct {
AllNamespaces bool `yaml:"all-namespaces" default:"false"` AllNamespaces bool `yaml:"all-namespaces" default:"false"`
PlainTextFilterRegexes []string `yaml:"regex-masking"` PlainTextFilterRegexes []string `yaml:"regex-masking"`
IgnoredUserAgents []string `yaml:"ignored-user-agents"` IgnoredUserAgents []string `yaml:"ignored-user-agents"`
DisableRedaction bool `yaml:"no-redact" default:"false"` EnableRedaction bool `yaml:"redact" default:"false"`
HumanMaxEntriesDBSize string `yaml:"max-entries-db-size" default:"200MB"` HumanMaxEntriesDBSize string `yaml:"max-entries-db-size" default:"200MB"`
InsertionFilter string `yaml:"insertion-filter" default:""` InsertionFilter string `yaml:"insertion-filter" default:""`
DryRun bool `yaml:"dry-run" default:"false"` DryRun bool `yaml:"dry-run" default:"false"`

View File

@@ -22,7 +22,14 @@ function run_single_bench() {
for ((i=0;i<"$MIZU_BENCHMARK_RUN_COUNT";i++)); do for ((i=0;i<"$MIZU_BENCHMARK_RUN_COUNT";i++)); do
log " $i: Running tapper" log " $i: Running tapper"
rm -f tapper.log rm -f tapper.log
nohup ./agent/build/mizuagent --tap --api-server-address ws://localhost:8899/wsTapper -i lo -stats 10 > tapper.log 2>&1 & tapper_args=("--tap" "--api-server-address" "ws://localhost:8899/wsTapper" "-stats" "10" "-ignore-ports" "8899,9099")
if [[ $(uname) == "Darwin" ]]
then
tapper_args+=("-i" "lo0" "-"decoder "Loopback")
else
tapper_args+=("-i" "lo")
fi
nohup ./agent/build/mizuagent ${tapper_args[@]} > tapper.log 2>&1 &
log " $i: Running client (hey)" log " $i: Running client (hey)"
hey -z $MIZU_BENCHMARK_CLIENT_PERIOD -c $MIZU_BENCHMARK_CLIENTS_COUNT -q $MIZU_BENCHMARK_QPS $MIZU_BENCHMARK_URL > /dev/null || return 1 hey -z $MIZU_BENCHMARK_CLIENT_PERIOD -c $MIZU_BENCHMARK_CLIENTS_COUNT -q $MIZU_BENCHMARK_QPS $MIZU_BENCHMARK_URL > /dev/null || return 1
@@ -50,6 +57,7 @@ log "Writing output to $MIZU_BENCHMARK_OUTPUT_DIR"
cd $MIZU_HOME || exit 1 cd $MIZU_HOME || exit 1
export HOST_MODE=0 export HOST_MODE=0
export SENSITIVE_DATA_FILTERING_OPTIONS='{"EnableRedaction": false}'
export MIZU_DEBUG_DISABLE_PCAP=false export MIZU_DEBUG_DISABLE_PCAP=false
export MIZU_DEBUG_DISABLE_TCP_REASSEMBLY=false export MIZU_DEBUG_DISABLE_TCP_REASSEMBLY=false
export MIZU_DEBUG_DISABLE_TCP_STREAM=false export MIZU_DEBUG_DISABLE_TCP_STREAM=false

View File

@@ -3,5 +3,5 @@ package api
type TrafficFilteringOptions struct { type TrafficFilteringOptions struct {
IgnoredUserAgents []string IgnoredUserAgents []string
PlainTextMaskingRegexes []*SerializableRegexp PlainTextMaskingRegexes []*SerializableRegexp
DisableRedaction bool EnableRedaction bool
} }

View File

@@ -13,4 +13,4 @@ test-pull-bin:
test-pull-expect: test-pull-expect:
@mkdir -p expect @mkdir -p expect
@[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect9/http/\* expect @[ "${skipexpect}" ] && echo "Skipping downloading expected JSONs" || gsutil -o 'GSUtil:parallel_process_count=5' -o 'GSUtil:parallel_thread_count=5' -m cp -r gs://static.up9.io/mizu/test-pcap/expect10/http/\* expect

View File

@@ -18,7 +18,7 @@ func filterAndEmit(item *api.OutputChannelItem, emitter api.Emitter, options *ap
return return
} }
if !options.DisableRedaction { if options.EnableRedaction {
FilterSensitiveData(item, options) FilterSensitiveData(item, options)
} }

View File

@@ -1,5 +1,5 @@
import styles from "./EntrySections.module.sass"; import styles from "./EntrySections.module.sass";
import React, { useState } from "react"; import React, { useCallback, useEffect, useMemo, useState } from "react";
import { SyntaxHighlighter } from "../../UI/SyntaxHighlighter/index"; import { SyntaxHighlighter } from "../../UI/SyntaxHighlighter/index";
import CollapsibleContainer from "../../UI/CollapsibleContainer"; import CollapsibleContainer from "../../UI/CollapsibleContainer";
import FancyTextDisplay from "../../UI/FancyTextDisplay"; import FancyTextDisplay from "../../UI/FancyTextDisplay";
@@ -8,6 +8,7 @@ import Checkbox from "../../UI/Checkbox";
import ProtobufDecoder from "protobuf-decoder"; import ProtobufDecoder from "protobuf-decoder";
import { default as jsonBeautify } from "json-beautify"; import { default as jsonBeautify } from "json-beautify";
import { default as xmlBeautify } from "xml-formatter"; import { default as xmlBeautify } from "xml-formatter";
import { Utils } from "../../../helpers/Utils"
interface EntryViewLineProps { interface EntryViewLineProps {
label: string; label: string;
@@ -101,6 +102,12 @@ export const EntrySectionContainer: React.FC<EntrySectionContainerProps> = ({ ti
</CollapsibleContainer> </CollapsibleContainer>
} }
const MAXIMUM_BYTES_TO_FORMAT = 1000000; // The maximum of chars to highlight in body, in case the response can be megabytes
const jsonLikeFormats = ['json', 'yaml', 'yml'];
const xmlLikeFormats = ['xml', 'html'];
const protobufFormats = ['application/grpc'];
const supportedFormats = jsonLikeFormats.concat(xmlLikeFormats, protobufFormats);
interface EntryBodySectionProps { interface EntryBodySectionProps {
title: string, title: string,
content: any, content: any,
@@ -118,21 +125,21 @@ export const EntryBodySection: React.FC<EntryBodySectionProps> = ({
contentType, contentType,
selector, selector,
}) => { }) => {
const MAXIMUM_BYTES_TO_FORMAT = 1000000; // The maximum of chars to highlight in body, in case the response can be megabytes
const jsonLikeFormats = ['json', 'yaml', 'yml'];
const xmlLikeFormats = ['xml', 'html'];
const protobufFormats = ['application/grpc'];
const supportedFormats = jsonLikeFormats.concat(xmlLikeFormats, protobufFormats);
const [isPretty, setIsPretty] = useState(true); const [isPretty, setIsPretty] = useState(true);
const [showLineNumbers, setShowLineNumbers] = useState(true); const [showLineNumbers, setShowLineNumbers] = useState(false);
const [decodeBase64, setDecodeBase64] = useState(true); const [decodeBase64, setDecodeBase64] = useState(true);
const isBase64Encoding = encoding === 'base64'; const isBase64Encoding = encoding === 'base64';
const supportsPrettying = supportedFormats.some(format => contentType?.indexOf(format) > -1); const supportsPrettying = supportedFormats.some(format => contentType?.indexOf(format) > -1);
const [isDecodeGrpc, setIsDecodeGrpc] = useState(true); const [isDecodeGrpc, setIsDecodeGrpc] = useState(true);
const [isLineNumbersGreaterThenOne, setIsLineNumbersGreaterThenOne] = useState(true);
const formatTextBody = (body: any): string => { useEffect(() => {
(isLineNumbersGreaterThenOne && isPretty) && setShowLineNumbers(true);
!isLineNumbersGreaterThenOne && setShowLineNumbers(false);
}, [isLineNumbersGreaterThenOne, isPretty])
const formatTextBody = useCallback((body: any): string => {
if (!decodeBase64) return body; if (!decodeBase64) return body;
const chunk = body.slice(0, MAXIMUM_BYTES_TO_FORMAT); const chunk = body.slice(0, MAXIMUM_BYTES_TO_FORMAT);
@@ -158,15 +165,24 @@ export const EntryBodySection: React.FC<EntryBodySectionProps> = ({
return jsonBeautify(protobufDecoded, null, 2, 80); return jsonBeautify(protobufDecoded, null, 2, 80);
} }
} catch (error) { } catch (error) {
if (String(error).includes("More than one message in")){ if (String(error).includes("More than one message in")) {
if(isDecodeGrpc) if (isDecodeGrpc)
setIsDecodeGrpc(false); setIsDecodeGrpc(false);
} else if (String(error).includes("Failed to parse")) {
console.warn(error);
} else { } else {
console.error(error); console.error(error);
} }
} }
return bodyBuf; return bodyBuf;
} }, [isPretty, contentType, isDecodeGrpc, decodeBase64, isBase64Encoding])
const formattedText = useMemo(() => formatTextBody(content), [formatTextBody, content]);
useEffect(() => {
const lineNumbers = Utils.lineNumbersInString(formattedText);
setIsLineNumbersGreaterThenOne(lineNumbers > 1);
}, [isPretty, content, showLineNumbers, formattedText]);
return <React.Fragment> return <React.Fragment>
{content && content?.length > 0 && <EntrySectionContainer {content && content?.length > 0 && <EntrySectionContainer
@@ -181,18 +197,19 @@ export const EntryBodySection: React.FC<EntryBodySectionProps> = ({
{supportsPrettying && <span style={{ marginLeft: '.2rem' }}>Pretty</span>} {supportsPrettying && <span style={{ marginLeft: '.2rem' }}>Pretty</span>}
<div style={{ paddingTop: 3, paddingLeft: supportsPrettying ? 20 : 0 }}> <div style={{ paddingTop: 3, paddingLeft: supportsPrettying ? 20 : 0 }}>
<Checkbox checked={showLineNumbers} onToggle={() => { setShowLineNumbers(!showLineNumbers) }} /> <Checkbox checked={showLineNumbers} onToggle={() => { setShowLineNumbers(!showLineNumbers) }} disabled={!isLineNumbersGreaterThenOne || !decodeBase64} />
</div> </div>
<span style={{ marginLeft: '.2rem' }}>Line numbers</span> <span style={{ marginLeft: '.2rem' }}>Line numbers</span>
{isBase64Encoding && <div style={{ paddingTop: 3, paddingLeft: 20 }}>
{isBase64Encoding && <div style={{ paddingTop: 3, paddingLeft: (isLineNumbersGreaterThenOne || supportsPrettying) ? 20 : 0 }}>
<Checkbox checked={decodeBase64} onToggle={() => { setDecodeBase64(!decodeBase64) }} /> <Checkbox checked={decodeBase64} onToggle={() => { setDecodeBase64(!decodeBase64) }} />
</div>} </div>}
{isBase64Encoding && <span style={{ marginLeft: '.2rem' }}>Decode Base64</span>} {isBase64Encoding && <span style={{ marginLeft: '.2rem' }}>Decode Base64</span>}
{!isDecodeGrpc && <span style={{ fontSize: '12px', color: '#DB2156', marginLeft: '.8rem' }}>More than one message in protobuf payload is not supported</span>} {!isDecodeGrpc && <span style={{ fontSize: '12px', color: '#DB2156', marginLeft: '.8rem' }}>More than one message in protobuf payload is not supported</span>}
</div> </div>
<SyntaxHighlighter <SyntaxHighlighter
code={formatTextBody(content)} code={formattedText}
showLineNumbers={showLineNumbers} showLineNumbers={showLineNumbers}
/> />

View File

@@ -296,7 +296,7 @@ export const EntryItem: React.FC<EntryProps> = ({entry, style, headingMode, name
query={`dst.ip == "${entry.dst.ip}"`} query={`dst.ip == "${entry.dst.ip}"`}
displayIconOnMouseOver={true} displayIconOnMouseOver={true}
flipped={false} flipped={false}
iconStyle={{marginTop: "28px"}} iconStyle={{marginTop: "30px", marginLeft: "-2px",right: "35px", position: "relative"}}
> >
<span <span
className={`${styles.tcpInfo} ${styles.ip}`} className={`${styles.tcpInfo} ${styles.ip}`}

View File

@@ -54,7 +54,7 @@ const Queryable: React.FC<Props> = ({query, style, iconStyle, className, useTool
{flipped && addButton} {flipped && addButton}
{children} {children}
{!flipped && addButton} {!flipped && addButton}
{useTooltip && showTooltip && <span data-cy={"QueryableTooltip"} className={QueryableStyle.QueryableTooltip} style={tooltipStyle}>{query}</span>} {useTooltip && showTooltip && (query !== "") && <span data-cy={"QueryableTooltip"} className={QueryableStyle.QueryableTooltip} style={tooltipStyle}>{query}</span>}
</div> </div>
); );
}; };

View File

@@ -1,4 +1,4 @@
import React from 'react'; import React, { useEffect, useState } from 'react';
import Lowlight from 'react-lowlight' import Lowlight from 'react-lowlight'
import 'highlight.js/styles/atom-one-light.css' import 'highlight.js/styles/atom-one-light.css'
import styles from './index.module.sass'; import styles from './index.module.sass';
@@ -30,18 +30,23 @@ interface Props {
} }
export const SyntaxHighlighter: React.FC<Props> = ({ export const SyntaxHighlighter: React.FC<Props> = ({
code, code,
showLineNumbers = false, showLineNumbers = false,
language = null language = null,
}) => { }) => {
const markers = showLineNumbers ? code.split("\n").map((item, i) => { const [markers, setMarkers] = useState([])
return {
line: i + 1,
className: styles.hljsMarkerLine
}
}) : [];
return <div style={{fontSize: ".75rem"}} className={styles.highlighterContainer}><Lowlight language={language ? language : ""} value={code} markers={markers}/></div>; useEffect(() => {
const newMarkers = code.split("\n").map((item, i) => {
return {
line: i + 1,
className: styles.hljsMarkerLine
}
});
setMarkers(showLineNumbers ? newMarkers : []);
}, [showLineNumbers, code])
return <div style={{ fontSize: ".75rem" }} className={styles.highlighterContainer}><Lowlight language={language ? language : ""} value={code} markers={markers} /></div>;
}; };
export default SyntaxHighlighter; export default SyntaxHighlighter;

View File

@@ -50,10 +50,9 @@
td td
color: $light-gray color: $light-gray
padding: 10px padding: 10px
font-size: 11px font-size: 15px
font-weight: 600 font-weight: 600
padding-top: 5px padding: 10px
padding-bottom: 5px
.nowrap .nowrap
white-space: nowrap white-space: nowrap

View File

@@ -3,4 +3,5 @@ const IP_ADDRESS_REGEX = /([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3})(:([0-9]{
export class Utils { export class Utils {
static isIpAddress = (address: string): boolean => IP_ADDRESS_REGEX.test(address) static isIpAddress = (address: string): boolean => IP_ADDRESS_REGEX.test(address)
} static lineNumbersInString = (code:string): number => code.split("\n").length;
}