Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.next.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ otherwise no tag is added. {issue}42208[42208] {pull}42403[42403]

- Added maintenance windows support for Heartbeat. {pull}41508[41508]
- Add missing dependencies to ubi9-minimal distro. {pull}44556[44556]
- Add base64 encoding option to inline monitors. {pull}45100[45100]


*Metricbeat*
Expand Down
24 changes: 23 additions & 1 deletion x-pack/heartbeat/monitors/browser/source/inline.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
package source

import (
"encoding/base64"
"fmt"
"regexp"
)

type InlineSource struct {
Script string `config:"script"`
Script string `config:"script"`
Encoding string `config:"encoding"`
BaseSource
}

Expand All @@ -22,6 +24,10 @@ func (s *InlineSource) Validate() error {
return ErrNoInlineScript
}

if s.Encoding != "" && s.Encoding != "base64" {
return fmt.Errorf("unsupported encoding: %v", s.Encoding)
}

return nil
}

Expand All @@ -36,3 +42,19 @@ func (s *InlineSource) Workdir() string {
func (s *InlineSource) Close() error {
return nil
}

func (s *InlineSource) Decode() error {
// Don't decode if flag is missing
if s.Encoding != "base64" {
return nil
}

decoded, err := base64.StdEncoding.DecodeString(s.Script)
if err != nil {
return fmt.Errorf("error decoding from base64: %w", err)
}

s.Script = string(decoded)

return nil
}
4 changes: 4 additions & 0 deletions x-pack/heartbeat/monitors/browser/source/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@ func (l *LocalSource) Workdir() string {
func (l *LocalSource) Close() error {
return ecserr.NewUnsupportedMonitorTypeError(ErrLocalUnsupportedType)
}

func (l *LocalSource) Decode() error {
return nil
}
11 changes: 7 additions & 4 deletions x-pack/heartbeat/monitors/browser/source/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
Expand Down Expand Up @@ -55,7 +54,7 @@ func (p *ProjectSource) Fetch() error {
return err
}

tf, err := ioutil.TempFile(os.TempDir(), "elastic-synthetics-zip-")
tf, err := os.CreateTemp(os.TempDir(), "elastic-synthetics-zip-")
if err != nil {
return fmt.Errorf("could not create tmpfile for project monitor source: %w", err)
}
Expand All @@ -67,7 +66,7 @@ func (p *ProjectSource) Fetch() error {
return err
}

p.TargetDirectory, err = ioutil.TempDir(os.TempDir(), "elastic-synthetics-unzip-")
p.TargetDirectory, err = os.MkdirTemp(os.TempDir(), "elastic-synthetics-unzip-")
if err != nil {
return fmt.Errorf("could not make temp dir for unzipping project source: %w", err)
}
Expand Down Expand Up @@ -132,7 +131,7 @@ func setupProjectDir(workdir string) error {
if err != nil {
return err
}
err = ioutil.WriteFile(filepath.Join(workdir, "package.json"), pkgJsonContent, defaultMod)
err = os.WriteFile(filepath.Join(workdir, "package.json"), pkgJsonContent, defaultMod)
if err != nil {
return err
}
Expand Down Expand Up @@ -173,3 +172,7 @@ func runSimpleCommand(cmd *exec.Cmd, dir string) error {
logp.L().Infof("Ran %s (%d) got '%s': (%s) as (%d/%d)", cmd, cmd.ProcessState.ExitCode(), string(output), err, syscall.Getuid(), syscall.Geteuid())
return err
}

func (p *ProjectSource) Decode() error {
return nil
}
1 change: 1 addition & 0 deletions x-pack/heartbeat/monitors/browser/source/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type ISource interface {
Fetch() error
Workdir() string
Close() error
Decode() error
}

type BaseSource struct {
Expand Down
4 changes: 4 additions & 0 deletions x-pack/heartbeat/monitors/browser/source/zipurl.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@ func (z *ZipURLSource) Workdir() string {
func (z *ZipURLSource) Close() error {
return ecserr.NewUnsupportedMonitorTypeError(ErrZipURLUnsupportedType)
}

func (z *ZipURLSource) Decode() error {
return nil
}
6 changes: 5 additions & 1 deletion x-pack/heartbeat/monitors/browser/sourcejob.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ func NewSourceJob(rawCfg *config.C) (*SourceJob, error) {
if err != nil {
return nil, ErrBadConfig(err)
}
err = s.browserCfg.Source.Active().Decode()
if err != nil {
return nil, ErrBadConfig(err)
}

return s, nil
}
Expand Down Expand Up @@ -164,7 +168,7 @@ func (sj *SourceJob) jobs() []jobs.Job {
var j jobs.Job

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

if isScript {
Expand Down
50 changes: 50 additions & 0 deletions x-pack/heartbeat/monitors/browser/sourcejob_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package browser

import (
"encoding/base64"
"encoding/json"
"fmt"
"path"
Expand Down Expand Up @@ -355,3 +356,52 @@ func TestFilterDevFlags(t *testing.T) {
})
}
}

func TestSourceDecoding(t *testing.T) {
script := "a script"
encoded := base64.StdEncoding.EncodeToString([]byte(script))
timeout := 30
cfg := conf.MustNewConfigFrom(mapstr.M{
"name": "My Name",
"id": "myId",
"source": mapstr.M{
"inline": mapstr.M{
"script": encoded,
"encoding": "base64",
},
},
"timeout": timeout,
})
s, e := NewSourceJob(cfg)
require.NoError(t, e)
require.NotNil(t, s)
require.Equal(t, script, s.browserCfg.Source.Inline.Script)
require.Equal(t, "", s.Workdir())

e = s.Close()
require.NoError(t, e)
}

func TestDisabledSourceDecoding(t *testing.T) {
script := "a script"
encoded := base64.StdEncoding.EncodeToString([]byte(script))
timeout := 30
cfg := conf.MustNewConfigFrom(mapstr.M{
"name": "My Name",
"id": "myId",
"source": mapstr.M{
"inline": mapstr.M{
"script": encoded,
},
},
"timeout": timeout,
})
s, e := NewSourceJob(cfg)
require.NoError(t, e)
require.NotNil(t, s)
require.Equal(t, encoded, s.browserCfg.Source.Inline.Script)
require.Equal(t, "", s.Workdir())

e = s.Close()
require.NoError(t, e)
}
32 changes: 5 additions & 27 deletions x-pack/heartbeat/monitors/browser/synthexec/synthexec.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (
"os"
"os/exec"
"path/filepath"
"regexp"
"runtime"
"strings"
"sync"
Expand Down Expand Up @@ -43,7 +42,9 @@ type FilterJourneyConfig struct {
// where these are unsupported
var platformCmdMutate func(*SynthCmd) = func(*SynthCmd) {}

var SynthexecTimeout struct{}
type SynthexecTimeout string

var SynthexecTimeoutKey = SynthexecTimeout("synthexec_timeout")

// ProjectJob will run a single journey by name from the given project.
func ProjectJob(ctx context.Context, projectPath string, params mapstr.M, filterJourneys FilterJourneyConfig, fields stdfields.StdMonitorFields, extraArgs ...string) (jobs.Job, error) {
Expand Down Expand Up @@ -249,7 +250,7 @@ func runCmd(
}

// Get timeout from parent ctx
timeout, _ := ctx.Value(SynthexecTimeout).(time.Duration)
timeout, _ := ctx.Value(SynthexecTimeoutKey).(time.Duration)
ctx, cancel := context.WithTimeout(ctx, timeout)
go func() {
<-ctx.Done()
Expand Down Expand Up @@ -278,7 +279,7 @@ func runCmd(
logp.L().Warn("Error executing command '%s' (%d): %s", cmd, cmd.ProcessState.ExitCode(), err)

if errors.Is(ctx.Err(), context.DeadlineExceeded) {
timeout, _ := ctx.Value(SynthexecTimeout).(time.Duration)
timeout, _ := ctx.Value(SynthexecTimeoutKey).(time.Duration)
cmdError = ECSErrToSynthError(ecserr.NewCmdTimeoutStatusErr(timeout, cmd.String()))
} else {
cmdError = ECSErrToSynthError(ecserr.NewBadCmdStatusErr(cmd.ProcessState.ExitCode(), cmd.String()))
Expand Down Expand Up @@ -345,29 +346,6 @@ func lineToSynthEventFactory(typ string) func(bytes []byte, text string) (res *S
}
}

var emptyStringRegexp = regexp.MustCompile(`^\s*$`)

// jsonToSynthEvent can take a line from the scanner and transform it into a *SynthEvent. Will return
// nil res on empty lines.
func jsonToSynthEvent(bytes []byte, text string) (res *SynthEvent, err error) {
// Skip empty lines
if emptyStringRegexp.Match(bytes) {
return nil, nil
}

res = &SynthEvent{}
err = json.Unmarshal(bytes, res)

if err != nil {
return nil, err
}

if res.Type == "" {
return nil, fmt.Errorf("unmarshal succeeded, but no type found for: %s", text)
}
return res, err
}

// getNpmRoot gets the closest ancestor path that contains package.json.
func getNpmRoot(path string) (string, error) {
return getNpmRootIn(path, path)
Expand Down
27 changes: 26 additions & 1 deletion x-pack/heartbeat/monitors/browser/synthexec/synthexec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ package synthexec

import (
"context"
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"regexp"
"runtime"
"testing"
"time"
Expand Down Expand Up @@ -98,6 +100,29 @@ func TestJsonToSynthEvent(t *testing.T) {
}
}

var emptyStringRegexp = regexp.MustCompile(`^\s*$`)

// jsonToSynthEvent can take a line from the scanner and transform it into a *SynthEvent. Will return
// nil res on empty lines.
func jsonToSynthEvent(bytes []byte, text string) (res *SynthEvent, err error) {
// Skip empty lines
if emptyStringRegexp.Match(bytes) {
return nil, nil
}

res = &SynthEvent{}
err = json.Unmarshal(bytes, res)

if err != nil {
return nil, err
}

if res.Type == "" {
return nil, fmt.Errorf("unmarshal succeeded, but no type found for: %s", text)
}
return res, err
}

func goCmd(args ...string) *exec.Cmd {
goBinary := "go" // relative by default
// GET the GOROOT if defined, this helps in scenarios where
Expand Down Expand Up @@ -182,7 +207,7 @@ func runAndCollect(t *testing.T, cmd *exec.Cmd, stdinStr string, cmdTimeout time
cwd, err := os.Getwd()
require.NoError(t, err)
cmd.Dir = filepath.Join(cwd, "testcmd")
ctx := context.WithValue(context.TODO(), SynthexecTimeout, cmdTimeout)
ctx := context.WithValue(context.TODO(), SynthexecTimeoutKey, cmdTimeout)

mpx, err := runCmd(ctx, &SynthCmd{cmd}, &stdinStr, nil, FilterJourneyConfig{})
require.NoError(t, err)
Expand Down