Skip to content
Draft
Show file tree
Hide file tree
Changes from 7 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
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -174,3 +174,5 @@ require (
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)

replace github.com/elastic/package-spec/v2 => github.com/jsoriano/package-spec/v2 v2.0.0-20230830222135-333f997ace72
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,6 @@ github.com/elastic/go-ucfg v0.8.6 h1:stUeyh2goTgGX+/wb9gzKvTv0YB0231LTpKUgCKj4U0
github.com/elastic/go-ucfg v0.8.6/go.mod h1:4E8mPOLSUV9hQ7sgLEJ4bvt0KhMuDJa8joDT2QGAEKA=
github.com/elastic/gojsonschema v1.2.1 h1:cUMbgsz0wyEB4x7xf3zUEvUVDl6WCz2RKcQPul8OsQc=
github.com/elastic/gojsonschema v1.2.1/go.mod h1:biw5eBS2Z4T02wjATMRSfecfjCmwaDPvuaqf844gLrg=
github.com/elastic/package-spec/v2 v2.10.0 h1:p/T/xgue/7eJW3drXqaZ/Zsjbrc2hT2w652OJ7jEmmY=
github.com/elastic/package-spec/v2 v2.10.0/go.mod h1:iQ/8tKtmsIUI9zXM2x2punKb77fSX1FPcChDr8zXPR8=
github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0=
github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ=
Expand Down Expand Up @@ -232,6 +230,8 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jsoriano/package-spec/v2 v2.0.0-20230830222135-333f997ace72 h1:qaSoeqMpC9p8AIA1HvtOUWW9zbFGxPakZftHyscgBjg=
github.com/jsoriano/package-spec/v2 v2.0.0-20230830222135-333f997ace72/go.mod h1:iQ/8tKtmsIUI9zXM2x2punKb77fSX1FPcChDr8zXPR8=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
Expand Down
2 changes: 1 addition & 1 deletion internal/docs/readme.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ func renderReadme(fileName, packageRoot, templatePath string, linksMap linkMap)
t := template.New(fileName)
t, err := t.Funcs(template.FuncMap{
"event": func(dataStreamName string) (string, error) {
return renderSampleEvent(packageRoot, dataStreamName)
return renderSampleEvents(packageRoot, dataStreamName)
},
"fields": func(args ...string) (string, error) {
if len(args) > 0 {
Expand Down
2 changes: 1 addition & 1 deletion internal/docs/readme_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ func createSampleEventFile(packageRoot, dataStreamName, contents string) error {
return err
}

sampleEventFile := filepath.Join(dataStreamFolder, sampleEventFile)
sampleEventFile := filepath.Join(dataStreamFolder, "sample_event.json")
if err := os.WriteFile(sampleEventFile, []byte(contents), 0644); err != nil {
return err
}
Expand Down
53 changes: 41 additions & 12 deletions internal/docs/sample_event.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,64 @@ package docs

import (
"fmt"
"io"
"os"
"path/filepath"
"sort"
"strings"

"github.com/elastic/elastic-package/internal/formatter"
)

const sampleEventFile = "sample_event.json"
const sampleEventFilePattern = "sample_event*.json"

func renderSampleEvent(packageRoot, dataStreamName string) (string, error) {
eventPath := filepath.Join(packageRoot, "data_stream", dataStreamName, sampleEventFile)
func renderSampleEvents(packageRoot, dataStreamName string) (string, error) {
eventPaths, err := filepath.Glob(filepath.Join(packageRoot, "data_stream", dataStreamName, sampleEventFilePattern))
if err != nil {
return "", fmt.Errorf("failed to look for sample event files: %w", err)
}
if len(eventPaths) == 0 {
return "", fmt.Errorf("could not find any sample event for data stream %s", dataStreamName)
}

var builder strings.Builder
if len(eventPaths) == 1 {
fmt.Fprintf(&builder, "An example event for `%s` looks as following:\n\n",
stripDataStreamFolderSuffix(dataStreamName))
} else {
fmt.Fprintf(&builder, "Example events for `%s` look as following:\n\n",
stripDataStreamFolderSuffix(dataStreamName))
}

sort.Strings(eventPaths)
for i, eventPath := range eventPaths {
if i > 0 {
fmt.Fprintln(&builder)
}
err := renderSampleEvent(&builder, eventPath)
if err != nil {
return "", err
}
}

return builder.String(), nil
}

func renderSampleEvent(w io.Writer, eventPath string) error {
body, err := os.ReadFile(eventPath)
if err != nil {
return "", fmt.Errorf("reading sample event file failed (path: %s): %w", eventPath, err)
return fmt.Errorf("reading sample event file failed (path: %s): %w", eventPath, err)
}

formatted, _, err := formatter.JSONFormatter(body)
if err != nil {
return "", fmt.Errorf("formatting sample event file failed (path: %s): %w", eventPath, err)
return fmt.Errorf("formatting sample event file failed (path: %s): %w", eventPath, err)
}

var builder strings.Builder
builder.WriteString(fmt.Sprintf("An example event for `%s` looks as following:\n\n",
stripDataStreamFolderSuffix(dataStreamName)))
builder.WriteString("```json\n")
builder.Write(formatted)
builder.WriteString("\n```")
return builder.String(), nil
fmt.Fprintln(w, "```json")
fmt.Fprintln(w, string(formatted))
fmt.Fprint(w, "```")
return nil
}

func stripDataStreamFolderSuffix(dataStreamName string) string {
Expand Down
13 changes: 5 additions & 8 deletions internal/formatter/json_formatter.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,19 @@
package formatter

import (
"bytes"
"encoding/json"
"fmt"
)

// JSONFormatter function is responsible for formatting the given JSON input.
// The function is exposed, so it can be used by other internal packages, e.g. to format sample events in docs.
func JSONFormatter(content []byte) ([]byte, bool, error) {
var rawMessage json.RawMessage
err := json.Unmarshal(content, &rawMessage)
var formatted bytes.Buffer
err := json.Indent(&formatted, content, "", " ")
if err != nil {
return nil, false, fmt.Errorf("unmarshalling JSON file failed: %w", err)
return nil, false, fmt.Errorf("indenting JSON failed: %w", err)
}

formatted, err := json.MarshalIndent(&rawMessage, "", " ")
if err != nil {
return nil, false, fmt.Errorf("marshalling JSON raw message failed: %w", err)
}
return formatted, string(content) == string(formatted), nil
return formatted.Bytes(), string(content) == formatted.String(), nil
}
61 changes: 31 additions & 30 deletions internal/testrunner/runners/static/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
package static

import (
"errors"
"fmt"
"os"
"path/filepath"
Expand All @@ -16,7 +15,7 @@ import (
"github.com/elastic/elastic-package/internal/testrunner"
)

const sampleEventJSON = "sample_event.json"
const sampleEventFilePattern = "sample_event*.json"

type runner struct {
options testrunner.TestOptions
Expand Down Expand Up @@ -75,18 +74,18 @@ func (r runner) run() ([]testrunner.TestResult, error) {

func (r runner) verifySampleEvent(pkgManifest *packages.PackageManifest) []testrunner.TestResult {
resultComposer := testrunner.NewResultComposer(testrunner.TestResult{
Name: "Verify " + sampleEventJSON,
Name: "Verify sample events",
TestType: TestType,
Package: r.options.TestFolder.Package,
DataStream: r.options.TestFolder.DataStream,
})

sampleEventPath, found, err := r.getSampleEventPath()
sampleEventPaths, err := r.findSampleEventPaths()
if err != nil {
results, _ := resultComposer.WithError(err)
return results
}
if !found {
if len(sampleEventPaths) == 0 {
// Nothing to do.
return []testrunner.TestResult{}
}
Expand All @@ -96,7 +95,7 @@ func (r runner) verifySampleEvent(pkgManifest *packages.PackageManifest) []testr
results, _ := resultComposer.WithError(err)
return results
}
fieldsValidator, err := fields.CreateValidatorForDirectory(filepath.Dir(sampleEventPath),
fieldsValidator, err := fields.CreateValidatorForDirectory(filepath.Dir(sampleEventPaths[0]),
fields.WithSpecVersion(pkgManifest.SpecVersion),
fields.WithDefaultNumericConversion(),
fields.WithExpectedDatasets(expectedDatasets),
Expand All @@ -107,44 +106,46 @@ func (r runner) verifySampleEvent(pkgManifest *packages.PackageManifest) []testr
return results
}

content, err := os.ReadFile(sampleEventPath)
if err != nil {
results, _ := resultComposer.WithError(fmt.Errorf("can't read file: %w", err))
return results
}

multiErr := fieldsValidator.ValidateDocumentBody(content)
if len(multiErr) > 0 {
results, _ := resultComposer.WithError(testrunner.ErrTestCaseFailed{
Reason: "one or more errors found in document",
Details: multiErr.Error(),
})
return results
for _, sampleEventPath := range sampleEventPaths {
logger.Debugf("Validating fields in sample event %s", sampleEventPath)
content, err := os.ReadFile(sampleEventPath)
if err != nil {
results, _ := resultComposer.WithError(fmt.Errorf("can't read file: %w", err))
return results
}

multiErr := fieldsValidator.ValidateDocumentBody(content)
if len(multiErr) > 0 {
results, _ := resultComposer.WithError(testrunner.ErrTestCaseFailed{
Reason: fmt.Sprintf("one or more errors found in sample document \"%s\"", sampleEventPath),
Details: multiErr.Error(),
})
return results
}
}

results, _ := resultComposer.WithSuccess()
return results
}

func (r runner) getSampleEventPath() (string, bool, error) {
var sampleEventPath string
func (r runner) findSampleEventPaths() ([]string, error) {
var pattern string
if r.options.TestFolder.DataStream != "" {
sampleEventPath = filepath.Join(
pattern = filepath.Join(
r.options.PackageRootPath,
"data_stream",
r.options.TestFolder.DataStream,
sampleEventJSON)
sampleEventFilePattern)
} else {
sampleEventPath = filepath.Join(r.options.PackageRootPath, sampleEventJSON)
}
_, err := os.Stat(sampleEventPath)
if errors.Is(err, os.ErrNotExist) {
return "", false, nil
pattern = filepath.Join(r.options.PackageRootPath, sampleEventFilePattern)
}

files, err := filepath.Glob(pattern)
if err != nil {
return "", false, fmt.Errorf("stat file failed: %w", err)
return nil, fmt.Errorf("glob failed (pattern: %s): %w", pattern, err)
}
return sampleEventPath, true, nil

return files, nil
}

func (r runner) getExpectedDatasets(pkgManifest *packages.PackageManifest) ([]string, error) {
Expand Down
36 changes: 30 additions & 6 deletions internal/testrunner/runners/system/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,7 @@ func (r *runner) runTest(config *testConfig, ctxt servicedeployer.ServiceContext
}

// Write sample events file from first doc, if requested
if err := r.generateTestResult(docs); err != nil {
if err := r.generateTestResult(config, docs); err != nil {
return result.WithError(err)
}

Expand Down Expand Up @@ -1183,13 +1183,18 @@ func filterAgents(allAgents []kibana.Agent, ctx servicedeployer.ServiceContext)
return filtered
}

func writeSampleEvent(path string, doc common.MapStr) error {
func writeSampleEvent(path string, doc common.MapStr, name string) error {
body, err := json.MarshalIndent(doc, "", " ")
if err != nil {
return fmt.Errorf("marshalling sample event failed: %w", err)
}

err = os.WriteFile(filepath.Join(path, "sample_event.json"), body, 0644)
sampleName := "sample_event.json"
if name != "" {
sampleName = "sample_event_" + name + ".json"
}

err = os.WriteFile(filepath.Join(path, sampleName), body, 0644)
if err != nil {
return fmt.Errorf("writing sample event failed: %w", err)
}
Expand Down Expand Up @@ -1249,7 +1254,7 @@ func (r *runner) selectVariants(variantsFile *servicedeployer.VariantsFile) []st
return variantNames
}

func (r *runner) generateTestResult(docs []common.MapStr) error {
func (r *runner) generateTestResult(config *testConfig, docs []common.MapStr) error {
if !r.options.GenerateTestResult {
return nil
}
Expand All @@ -1259,8 +1264,27 @@ func (r *runner) generateTestResult(docs []common.MapStr) error {
rootPath = filepath.Join(rootPath, "data_stream", ds)
}

if err := writeSampleEvent(rootPath, docs[0]); err != nil {
return fmt.Errorf("failed to write sample event file: %w", err)
if len(config.Samples) == 0 {
if err := writeSampleEvent(rootPath, docs[0], ""); err != nil {
return fmt.Errorf("failed to write sample event file: %w", err)
}
return nil
}

for _, sample := range config.Samples {
found := false
for _, doc := range docs {
if sample.Matches(doc) {
if err := writeSampleEvent(rootPath, docs[0], sample.Name); err != nil {
return fmt.Errorf("failed to write sample event file: %w", err)
}
found = true
break
}
}
if !found {
return fmt.Errorf("could not find sample for %q", sample.Name)
}
}

return nil
Expand Down
Loading