Skip to content

Commit

Permalink
[Heartbeat] filter dev flags inside synthetics args (#35788)
Browse files Browse the repository at this point in the history
* dev flag blocklist

* [Heartbeat] Filter synthetics dev flags if Ui monitor

* Add changelog

* Fix unit tests

* Add missing flags
  • Loading branch information
emilioalvap authored Jun 23, 2023
1 parent 2bdc35b commit c6e955a
Show file tree
Hide file tree
Showing 3 changed files with 230 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.next.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ automatic splitting at root level, if root level element is an array. {pull}3415
- Fix formatting issue with socket trace timeout. {pull}35434[35434]
- Update gval version. {pull}35636[35636]
- Fix serialization of processors when running diagnostics. {pull}35698[35698]
- Filter dev flags for ui monitors inside synthetics_args. {pull}35788[35788]
- Fix temp dir running out of space with project monitors. {issue}35843[35843]

*Heartbeat*
Expand Down
98 changes: 94 additions & 4 deletions x-pack/heartbeat/monitors/browser/sourcejob.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"context"
"encoding/json"
"fmt"
"math"
"strings"
"time"

"github.com/elastic/beats/v7/heartbeat/monitors/jobs"
Expand Down Expand Up @@ -91,8 +93,34 @@ func (sj *SourceJob) Close() error {
return nil
}

func (sj *SourceJob) extraArgs() []string {
extraArgs := sj.browserCfg.SyntheticsArgs
// Dev flags + expected number of params, math.MaxInt32 for variadic flags
var filterMap = map[string]int{
"--dry-run": 0,
"-h": 0,
"--help": 0,
"--inline": 1,
"--match": math.MaxInt32,
"--outfd": 1,
"--pause-on-error": 0,
"--quiet-exit-code": 0,
"-r": math.MaxInt32,
"--require": math.MaxInt32,
"--reporter": 1,
"--tags": math.MaxInt32,
"-V": 0,
"--version": 0,
"--ws-endpoint": 1,
}

func (sj *SourceJob) extraArgs(uiOrigin bool) []string {
extraArgs := []string{}

if uiOrigin {
extraArgs = filterDevFlags(sj.browserCfg.SyntheticsArgs, filterMap)
} else {
extraArgs = append(extraArgs, sj.browserCfg.SyntheticsArgs...)
}

if len(sj.browserCfg.PlaywrightOpts) > 0 {
s, err := json.Marshal(sj.browserCfg.PlaywrightOpts)
if err != nil {
Expand Down Expand Up @@ -137,17 +165,19 @@ func (sj *SourceJob) jobs() []jobs.Job {

isScript := sj.browserCfg.Source.Inline != nil
ctx := context.WithValue(sj.ctx, synthexec.SynthexecTimeout, sj.browserCfg.Timeout+30*time.Second)
sFields := sj.StdFields()

if isScript {
src := sj.browserCfg.Source.Inline.Script
j = synthexec.InlineJourneyJob(ctx, src, sj.Params(), sj.StdFields(), sj.extraArgs()...)
j = synthexec.InlineJourneyJob(ctx, src, sj.Params(), sFields, sj.extraArgs(sFields.Origin != "")...)
} else {
j = func(event *beat.Event) ([]jobs.Job, error) {
err := sj.Fetch()
if err != nil {
return nil, fmt.Errorf("could not fetch for browser source job: %w", err)
}
sj, err := synthexec.ProjectJob(ctx, sj.Workdir(), sj.Params(), sj.FilterJourneys(), sj.StdFields(), sj.extraArgs()...)

sj, err := synthexec.ProjectJob(ctx, sj.Workdir(), sj.Params(), sj.FilterJourneys(), sFields, sj.extraArgs(sFields.Origin != "")...)
if err != nil {
return nil, err
}
Expand All @@ -164,3 +194,63 @@ func (sj *SourceJob) plugin() plugin.Plugin {
Endpoints: 1,
}
}

type argsIterator struct {
i int
args []string
val string
}

func (a *argsIterator) Next() bool {
if a.i >= len(a.args) {
return false
}
a.val = a.args[a.i]
a.i++
return true
}

func (a *argsIterator) Val() string {
return a.val
}

func (a *argsIterator) Peek() (val string, ok bool) {
if a.i >= len(a.args) {
return "", false
}

val = a.args[a.i]
ok = true

return val, ok
}

// Iterate through list and filter dev flags + potential params
func filterDevFlags(args []string, filter map[string]int) []string {
result := []string{}

iter := argsIterator{i: 0, args: args}
for {
next := iter.Next()

if !next {
break
}

if pCount, ok := filter[iter.Val()]; ok {
ParamsIter:
for i := 0; i < pCount; i++ {
// Found filtered flag, check if it has associated params
if param, ok := iter.Peek(); ok && !strings.HasPrefix(param, "-") {
iter.Next()
} else {
break ParamsIter
}
}
} else {
result = append(result, iter.Val())
}
}

return result
}
138 changes: 135 additions & 3 deletions x-pack/heartbeat/monitors/browser/sourcejob_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package browser

import (
"encoding/json"
"fmt"
"path"
"path/filepath"
"reflect"
Expand Down Expand Up @@ -126,36 +127,43 @@ func TestExtraArgs(t *testing.T) {
name string
cfg *Config
want []string
ui bool
}{
{
"no args",
&Config{},
nil,
[]string{},
false,
},
{
"default",
DefaultConfig(),
[]string{"--screenshots", "on"},
false,
},
{
"sandbox",
&Config{Sandbox: true},
[]string{"--sandbox"},
false,
},
{
"throttling truthy",
&Config{Throttling: true},
nil,
[]string{},
false,
},
{
"disable throttling",
&Config{Throttling: false},
[]string{"--no-throttling"},
false,
},
{
"override throttling - text format",
&Config{Throttling: "10d/3u/20l"},
[]string{"--throttling", "10d/3u/20l"},
false,
},
{
"override throttling - JSON format",
Expand All @@ -165,41 +173,65 @@ func TestExtraArgs(t *testing.T) {
"latency": 20,
}},
[]string{"--throttling", `{"download":10,"latency":20,"upload":3}`},
false,
},
{
"ignore_https_errors",
&Config{IgnoreHTTPSErrors: true},
[]string{"--ignore-https-errors"},
false,
},
{
"screenshots",
&Config{Screenshots: "off"},
[]string{"--screenshots", "off"},
false,
},
{
"capabilities",
&Config{SyntheticsArgs: []string{"--capability", "trace", "ssblocks"}},
[]string{"--capability", "trace", "ssblocks"},
false,
},
{
"playwright options",
&Config{
PlaywrightOpts: playWrightOpts,
},
[]string{"--playwright-options", string(playwrightOptsJsonBytes)},
false,
},
{
"kitchen sink",
&Config{SyntheticsArgs: []string{"--capability", "trace", "ssblocks"}, Sandbox: true},
[]string{"--capability", "trace", "ssblocks", "--sandbox"},
false,
},
{
"does not filter dev flags on non-ui origin",
&Config{SyntheticsArgs: []string{"--pause-on-error", "--dry-run", "--quiet-exit-code", "--outfd", "testfd"}, Sandbox: true},
[]string{"--pause-on-error", "--dry-run", "--quiet-exit-code", "--outfd", "testfd", "--sandbox"},
false,
},
{
"filters dev flags on ui origin",
&Config{SyntheticsArgs: []string{"--pause-on-error", "--dry-run", "--quiet-exit-code", "--outfd", "testfd"}, Sandbox: true},
[]string{"--sandbox"},
true,
},
{
"filters variadic dev flags on ui origin",
&Config{SyntheticsArgs: []string{"--tags", "tag1", "tag2", "tag3", "--match", "tag4", "tag5", "--sandbox", "-r", "require1", "require2", "--require", "require3", "require4", "require5"}},
[]string{"--sandbox"},
true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := &SourceJob{
browserCfg: tt.cfg,
}
if got := s.extraArgs(); !reflect.DeepEqual(got, tt.want) {
if got := s.extraArgs(tt.ui); !reflect.DeepEqual(got, tt.want) {
t.Errorf("Project.extraArgs() = %v, want %v", got, tt.want)
}
})
Expand All @@ -223,3 +255,103 @@ func TestEmptyTimeout(t *testing.T) {
require.NotNil(t, s)
require.Equal(t, s.browserCfg.Timeout, defaults.Timeout)
}

func TestFilterDevFlags(t *testing.T) {
allFlags := []string{}
for k := range filterMap {
allFlags = append(allFlags, k)
}

variadicGen := func(flag string, n int) []string {
params := []string{"dummy"}
params = append(params, flag)
for i := 0; i < n; i++ {
params = append(params, fmt.Sprintf("flag-%d", i))
}

return params
}
tests := []struct {
name string
syntheticsArgs []string
want []string
}{
{
"no args",
nil,
[]string{},
},
{
"no args",
[]string{},
[]string{},
},
{
"all filtered",
allFlags,
[]string{},
},
{
"keep unfiltered",
append([]string{"unfiltered"}, allFlags...),
[]string{"unfiltered"},
},
{
"filter associated params",
[]string{"--help", "malformed1", "--outfd", "param1", "malformed2", "--reporter", "-malformed3"},
[]string{"malformed1", "malformed2", "-malformed3"},
},
{
"filter variadic flags - tags - 10",
variadicGen("--tags", 10),
[]string{"dummy"},
},
{
"filter variadic flags - tags - 50",
variadicGen("--tags", 50),
[]string{"dummy"},
},
{
"filter variadic flags - tags - 100",
variadicGen("--tags", 100),
[]string{"dummy"},
},
{
"filter variadic flags - require - 10",
variadicGen("--require", 10),
[]string{"dummy"},
},
{
"filter variadic flags - require - 50",
variadicGen("--require", 50),
[]string{"dummy"},
},
{
"filter variadic flags - require - 100",
variadicGen("-r", 100),
[]string{"dummy"},
},
{
"filter variadic flags - match - 10",
variadicGen("--match", 10),
[]string{"dummy"},
},
{
"filter variadic flags - match - 50",
variadicGen("--match", 50),
[]string{"dummy"},
},
{
"filter variadic flags - match - 100",
variadicGen("--match", 100),
[]string{"dummy"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := filterDevFlags(tt.syntheticsArgs, filterMap); !reflect.DeepEqual(got, tt.want) {
t.Errorf("syntheticsArgs = %v, want %v", got, tt.want)
}
})
}
}

0 comments on commit c6e955a

Please sign in to comment.