Skip to content

Commit 60c8989

Browse files
mergify[bot]emilioalvapmcapell
authored
[9.0](backport #45100) [Heartbeat] Add base64 encoding option to inline monitors (#45260)
* [Heartbeat] Add base64 encoding option to inline monitors (#45100) * Add base64 encoding option to inline monitors * Fix linter * Fix linter * Remove unused Decode() method * Fix linter * Add changelog (cherry picked from commit 2266395) * Update CHANGELOG.next.asciidoc Co-authored-by: Marc Capell <marc.capell@gmail.com> --------- Co-authored-by: Emilio Alvarez Piñeiro <95703246+emilioalvap@users.noreply.github.com> Co-authored-by: Marc Capell <marc.capell@gmail.com>
1 parent 60aa5c4 commit 60c8989

File tree

10 files changed

+126
-34
lines changed

10 files changed

+126
-34
lines changed

CHANGELOG.next.asciidoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ https://github.com/elastic/beats/compare/v8.8.1\...main[Check the HEAD diff]
121121

122122
*Heartbeat*
123123

124+
- Add base64 encoding option to inline monitors. {pull}45100[45100]
124125

125126

126127
*Metricbeat*

x-pack/heartbeat/monitors/browser/source/inline.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66
package source
77

88
import (
9+
"encoding/base64"
910
"fmt"
1011
"regexp"
1112
)
1213

1314
type InlineSource struct {
14-
Script string `config:"script"`
15+
Script string `config:"script"`
16+
Encoding string `config:"encoding"`
1517
BaseSource
1618
}
1719

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

27+
if s.Encoding != "" && s.Encoding != "base64" {
28+
return fmt.Errorf("unsupported encoding: %v", s.Encoding)
29+
}
30+
2531
return nil
2632
}
2733

@@ -36,3 +42,19 @@ func (s *InlineSource) Workdir() string {
3642
func (s *InlineSource) Close() error {
3743
return nil
3844
}
45+
46+
func (s *InlineSource) Decode() error {
47+
// Don't decode if flag is missing
48+
if s.Encoding != "base64" {
49+
return nil
50+
}
51+
52+
decoded, err := base64.StdEncoding.DecodeString(s.Script)
53+
if err != nil {
54+
return fmt.Errorf("error decoding from base64: %w", err)
55+
}
56+
57+
s.Script = string(decoded)
58+
59+
return nil
60+
}

x-pack/heartbeat/monitors/browser/source/local.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,7 @@ func (l *LocalSource) Workdir() string {
3232
func (l *LocalSource) Close() error {
3333
return ecserr.NewUnsupportedMonitorTypeError(ErrLocalUnsupportedType)
3434
}
35+
36+
func (l *LocalSource) Decode() error {
37+
return nil
38+
}

x-pack/heartbeat/monitors/browser/source/project.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"encoding/json"
1212
"fmt"
1313
"io"
14-
"io/ioutil"
1514
"os"
1615
"os/exec"
1716
"path/filepath"
@@ -55,7 +54,7 @@ func (p *ProjectSource) Fetch() error {
5554
return err
5655
}
5756

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

70-
p.TargetDirectory, err = ioutil.TempDir(os.TempDir(), "elastic-synthetics-unzip-")
69+
p.TargetDirectory, err = os.MkdirTemp(os.TempDir(), "elastic-synthetics-unzip-")
7170
if err != nil {
7271
return fmt.Errorf("could not make temp dir for unzipping project source: %w", err)
7372
}
@@ -132,7 +131,7 @@ func setupProjectDir(workdir string) error {
132131
if err != nil {
133132
return err
134133
}
135-
err = ioutil.WriteFile(filepath.Join(workdir, "package.json"), pkgJsonContent, defaultMod)
134+
err = os.WriteFile(filepath.Join(workdir, "package.json"), pkgJsonContent, defaultMod)
136135
if err != nil {
137136
return err
138137
}
@@ -173,3 +172,7 @@ func runSimpleCommand(cmd *exec.Cmd, dir string) error {
173172
logp.L().Infof("Ran %s (%d) got '%s': (%s) as (%d/%d)", cmd, cmd.ProcessState.ExitCode(), string(output), err, syscall.Getuid(), syscall.Geteuid())
174173
return err
175174
}
175+
176+
func (p *ProjectSource) Decode() error {
177+
return nil
178+
}

x-pack/heartbeat/monitors/browser/source/source.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ type ISource interface {
5454
Fetch() error
5555
Workdir() string
5656
Close() error
57+
Decode() error
5758
}
5859

5960
type BaseSource struct {

x-pack/heartbeat/monitors/browser/source/zipurl.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,7 @@ func (z *ZipURLSource) Workdir() string {
3232
func (z *ZipURLSource) Close() error {
3333
return ecserr.NewUnsupportedMonitorTypeError(ErrZipURLUnsupportedType)
3434
}
35+
36+
func (z *ZipURLSource) Decode() error {
37+
return nil
38+
}

x-pack/heartbeat/monitors/browser/sourcejob.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ func NewSourceJob(rawCfg *config.C) (*SourceJob, error) {
4444
if err != nil {
4545
return nil, ErrBadConfig(err)
4646
}
47+
err = s.browserCfg.Source.Active().Decode()
48+
if err != nil {
49+
return nil, ErrBadConfig(err)
50+
}
4751

4852
return s, nil
4953
}
@@ -164,7 +168,7 @@ func (sj *SourceJob) jobs() []jobs.Job {
164168
var j jobs.Job
165169

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

170174
if isScript {

x-pack/heartbeat/monitors/browser/sourcejob_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package browser
77

88
import (
9+
"encoding/base64"
910
"encoding/json"
1011
"fmt"
1112
"path"
@@ -355,3 +356,52 @@ func TestFilterDevFlags(t *testing.T) {
355356
})
356357
}
357358
}
359+
360+
func TestSourceDecoding(t *testing.T) {
361+
script := "a script"
362+
encoded := base64.StdEncoding.EncodeToString([]byte(script))
363+
timeout := 30
364+
cfg := conf.MustNewConfigFrom(mapstr.M{
365+
"name": "My Name",
366+
"id": "myId",
367+
"source": mapstr.M{
368+
"inline": mapstr.M{
369+
"script": encoded,
370+
"encoding": "base64",
371+
},
372+
},
373+
"timeout": timeout,
374+
})
375+
s, e := NewSourceJob(cfg)
376+
require.NoError(t, e)
377+
require.NotNil(t, s)
378+
require.Equal(t, script, s.browserCfg.Source.Inline.Script)
379+
require.Equal(t, "", s.Workdir())
380+
381+
e = s.Close()
382+
require.NoError(t, e)
383+
}
384+
385+
func TestDisabledSourceDecoding(t *testing.T) {
386+
script := "a script"
387+
encoded := base64.StdEncoding.EncodeToString([]byte(script))
388+
timeout := 30
389+
cfg := conf.MustNewConfigFrom(mapstr.M{
390+
"name": "My Name",
391+
"id": "myId",
392+
"source": mapstr.M{
393+
"inline": mapstr.M{
394+
"script": encoded,
395+
},
396+
},
397+
"timeout": timeout,
398+
})
399+
s, e := NewSourceJob(cfg)
400+
require.NoError(t, e)
401+
require.NotNil(t, s)
402+
require.Equal(t, encoded, s.browserCfg.Source.Inline.Script)
403+
require.Equal(t, "", s.Workdir())
404+
405+
e = s.Close()
406+
require.NoError(t, e)
407+
}

x-pack/heartbeat/monitors/browser/synthexec/synthexec.go

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515
"os"
1616
"os/exec"
1717
"path/filepath"
18-
"regexp"
1918
"runtime"
2019
"strings"
2120
"sync"
@@ -43,7 +42,9 @@ type FilterJourneyConfig struct {
4342
// where these are unsupported
4443
var platformCmdMutate func(*SynthCmd) = func(*SynthCmd) {}
4544

46-
var SynthexecTimeout struct{}
45+
type SynthexecTimeout string
46+
47+
var SynthexecTimeoutKey = SynthexecTimeout("synthexec_timeout")
4748

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

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

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

348-
var emptyStringRegexp = regexp.MustCompile(`^\s*$`)
349-
350-
// jsonToSynthEvent can take a line from the scanner and transform it into a *SynthEvent. Will return
351-
// nil res on empty lines.
352-
func jsonToSynthEvent(bytes []byte, text string) (res *SynthEvent, err error) {
353-
// Skip empty lines
354-
if emptyStringRegexp.Match(bytes) {
355-
return nil, nil
356-
}
357-
358-
res = &SynthEvent{}
359-
err = json.Unmarshal(bytes, res)
360-
361-
if err != nil {
362-
return nil, err
363-
}
364-
365-
if res.Type == "" {
366-
return nil, fmt.Errorf("unmarshal succeeded, but no type found for: %s", text)
367-
}
368-
return res, err
369-
}
370-
371349
// getNpmRoot gets the closest ancestor path that contains package.json.
372350
func getNpmRoot(path string) (string, error) {
373351
return getNpmRootIn(path, path)

x-pack/heartbeat/monitors/browser/synthexec/synthexec_test.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ package synthexec
88

99
import (
1010
"context"
11+
"encoding/json"
1112
"fmt"
1213
"os"
1314
"os/exec"
1415
"path/filepath"
16+
"regexp"
1517
"runtime"
1618
"testing"
1719
"time"
@@ -98,6 +100,29 @@ func TestJsonToSynthEvent(t *testing.T) {
98100
}
99101
}
100102

103+
var emptyStringRegexp = regexp.MustCompile(`^\s*$`)
104+
105+
// jsonToSynthEvent can take a line from the scanner and transform it into a *SynthEvent. Will return
106+
// nil res on empty lines.
107+
func jsonToSynthEvent(bytes []byte, text string) (res *SynthEvent, err error) {
108+
// Skip empty lines
109+
if emptyStringRegexp.Match(bytes) {
110+
return nil, nil
111+
}
112+
113+
res = &SynthEvent{}
114+
err = json.Unmarshal(bytes, res)
115+
116+
if err != nil {
117+
return nil, err
118+
}
119+
120+
if res.Type == "" {
121+
return nil, fmt.Errorf("unmarshal succeeded, but no type found for: %s", text)
122+
}
123+
return res, err
124+
}
125+
101126
func goCmd(args ...string) *exec.Cmd {
102127
goBinary := "go" // relative by default
103128
// GET the GOROOT if defined, this helps in scenarios where
@@ -182,7 +207,7 @@ func runAndCollect(t *testing.T, cmd *exec.Cmd, stdinStr string, cmdTimeout time
182207
cwd, err := os.Getwd()
183208
require.NoError(t, err)
184209
cmd.Dir = filepath.Join(cwd, "testcmd")
185-
ctx := context.WithValue(context.TODO(), SynthexecTimeout, cmdTimeout)
210+
ctx := context.WithValue(context.TODO(), SynthexecTimeoutKey, cmdTimeout)
186211

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

0 commit comments

Comments
 (0)