From 58668e3a4af1702c509a5a5632d57246be5cec06 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Tue, 29 Aug 2023 20:51:43 +0200 Subject: [PATCH 1/8] Render multiple sample events when present --- internal/docs/readme.go | 2 +- internal/docs/sample_event.go | 51 ++++++-- internal/formatter/json_formatter.go | 13 +-- .../access/sample_event_other.json | 106 +++++++++++++++++ test/packages/parallel/nginx/docs/README.md | 110 +++++++++++++++++- 5 files changed, 260 insertions(+), 22 deletions(-) create mode 100644 test/packages/parallel/nginx/data_stream/access/sample_event_other.json diff --git a/internal/docs/readme.go b/internal/docs/readme.go index 3b60a7197..13a9be92f 100644 --- a/internal/docs/readme.go +++ b/internal/docs/readme.go @@ -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 { diff --git a/internal/docs/sample_event.go b/internal/docs/sample_event.go index 60305e532..61749e9ac 100644 --- a/internal/docs/sample_event.go +++ b/internal/docs/sample_event.go @@ -6,6 +6,7 @@ package docs import ( "fmt" + "io" "os" "path/filepath" "strings" @@ -13,28 +14,54 @@ import ( "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)) + } + + 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 { diff --git a/internal/formatter/json_formatter.go b/internal/formatter/json_formatter.go index b20f30e90..8dca0a097 100644 --- a/internal/formatter/json_formatter.go +++ b/internal/formatter/json_formatter.go @@ -5,6 +5,7 @@ package formatter import ( + "bytes" "encoding/json" "fmt" ) @@ -12,15 +13,11 @@ import ( // 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 } diff --git a/test/packages/parallel/nginx/data_stream/access/sample_event_other.json b/test/packages/parallel/nginx/data_stream/access/sample_event_other.json new file mode 100644 index 000000000..b44215034 --- /dev/null +++ b/test/packages/parallel/nginx/data_stream/access/sample_event_other.json @@ -0,0 +1,106 @@ +{ + "agent": { + "hostname": "a73e7856c209", + "name": "a73e7856c209", + "id": "3987d2b3-b40a-4aa0-99fc-478f9d7079ea", + "ephemeral_id": "6d41da1c-5f71-4bd4-b326-a8913bfaa884", + "type": "filebeat", + "version": "7.11.0" + }, + "nginx": { + "access": { + "remote_ip_list": [ + "127.0.0.1" + ] + } + }, + "log": { + "file": { + "path": "/tmp/service_logs/access.log" + }, + "offset": 0 + }, + "elastic_agent": { + "id": "5ca3af72-37c3-48b6-92e8-176d154bb66f", + "version": "7.11.0", + "snapshot": true + }, + "source": { + "address": "127.0.0.1", + "ip": "127.0.0.1" + }, + "url": { + "original": "/server-status" + }, + "input": { + "type": "log" + }, + "@timestamp": "2020-12-03T11:41:57.000Z", + "ecs": { + "version": "1.6.0" + }, + "related": { + "ip": [ + "127.0.0.1" + ] + }, + "data_stream": { + "namespace": "ep", + "type": "logs", + "dataset": "nginx.access" + }, + "host": { + "hostname": "a73e7856c209", + "os": { + "kernel": "4.9.184-linuxkit", + "codename": "Core", + "name": "CentOS Linux", + "family": "redhat", + "version": "7 (Core)", + "platform": "centos" + }, + "containerized": true, + "ip": [ + "192.168.80.6" + ], + "name": "a73e7856c209", + "id": "06c26569966fd125c15acac5d7feffb6", + "mac": [ + "02:42:c0:a8:50:06" + ], + "architecture": "x86_64" + }, + "http": { + "request": { + "method": "get" + }, + "response": { + "status_code": 200, + "body": { + "bytes": 97 + } + }, + "version": "1.1" + }, + "event": { + "timezone": "+00:00", + "created": "2020-12-03T11:42:17.116Z", + "kind": "event", + "category": [ + "web" + ], + "type": [ + "access" + ], + "dataset": "nginx.access", + "outcome": "success" + }, + "user_agent": { + "original": "curl/7.64.0", + "name": "curl", + "device": { + "name": "Other" + }, + "version": "7.64.0" + } +} \ No newline at end of file diff --git a/test/packages/parallel/nginx/docs/README.md b/test/packages/parallel/nginx/docs/README.md index 8d7edbc10..87ddf759e 100644 --- a/test/packages/parallel/nginx/docs/README.md +++ b/test/packages/parallel/nginx/docs/README.md @@ -26,7 +26,7 @@ field can be overwritten with the original timezone using the add_fields process Access logs collects the nginx access logs. -An example event for `access` looks as following: +Example events for `access` look as following: ```json { @@ -136,6 +136,114 @@ An example event for `access` looks as following: } } ``` +```json +{ + "agent": { + "hostname": "a73e7856c209", + "name": "a73e7856c209", + "id": "3987d2b3-b40a-4aa0-99fc-478f9d7079ea", + "ephemeral_id": "6d41da1c-5f71-4bd4-b326-a8913bfaa884", + "type": "filebeat", + "version": "7.11.0" + }, + "nginx": { + "access": { + "remote_ip_list": [ + "127.0.0.1" + ] + } + }, + "log": { + "file": { + "path": "/tmp/service_logs/access.log" + }, + "offset": 0 + }, + "elastic_agent": { + "id": "5ca3af72-37c3-48b6-92e8-176d154bb66f", + "version": "7.11.0", + "snapshot": true + }, + "source": { + "address": "127.0.0.1", + "ip": "127.0.0.1" + }, + "url": { + "original": "/server-status" + }, + "input": { + "type": "log" + }, + "@timestamp": "2020-12-03T11:41:57.000Z", + "ecs": { + "version": "1.6.0" + }, + "related": { + "ip": [ + "127.0.0.1" + ] + }, + "data_stream": { + "namespace": "ep", + "type": "logs", + "dataset": "nginx.access" + }, + "host": { + "hostname": "a73e7856c209", + "os": { + "kernel": "4.9.184-linuxkit", + "codename": "Core", + "name": "CentOS Linux", + "family": "redhat", + "version": "7 (Core)", + "platform": "centos" + }, + "containerized": true, + "ip": [ + "192.168.80.6" + ], + "name": "a73e7856c209", + "id": "06c26569966fd125c15acac5d7feffb6", + "mac": [ + "02:42:c0:a8:50:06" + ], + "architecture": "x86_64" + }, + "http": { + "request": { + "method": "get" + }, + "response": { + "status_code": 200, + "body": { + "bytes": 97 + } + }, + "version": "1.1" + }, + "event": { + "timezone": "+00:00", + "created": "2020-12-03T11:42:17.116Z", + "kind": "event", + "category": [ + "web" + ], + "type": [ + "access" + ], + "dataset": "nginx.access", + "outcome": "success" + }, + "user_agent": { + "original": "curl/7.64.0", + "name": "curl", + "device": { + "name": "Other" + }, + "version": "7.64.0" + } +} +``` **Exported fields** From 70a7a05253dba8f0c228365566153fb2b412253b Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Wed, 30 Aug 2023 12:48:38 +0200 Subject: [PATCH 2/8] Initial implementation of basic conditions --- .../testrunner/runners/system/test_config.go | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/internal/testrunner/runners/system/test_config.go b/internal/testrunner/runners/system/test_config.go index 6a55f9d27..683beba42 100644 --- a/internal/testrunner/runners/system/test_config.go +++ b/internal/testrunner/runners/system/test_config.go @@ -10,6 +10,7 @@ import ( "os" "path/filepath" "regexp" + "strconv" "strings" "time" @@ -48,10 +49,76 @@ type testConfig struct { // type but can be ingested as numeric type. NumericKeywordFields []string `config:"numeric_keyword_fields"` + // Samples can be used to collect additional sample documents. + Samples []sampleConfig `config:"samples"` + Path string `config:",ignore"` // Path of config file. ServiceVariantName string `config:",ignore"` // Name of test variant when using variants.yml. } +type sampleConfig struct { + Name string `config:"name"` + Condition struct { + Key string `config:"key"` + Value *string `config:"value"` + } `config:"condition"` +} + +func (c *sampleConfig) Matches(doc common.MapStr) bool { + v, err := doc.GetValue(c.Condition.Key) + if err != nil { + return false + } + + if expected := c.Condition.Value; expected != nil && !matchesValue(*expected, v) { + return false + } + + return true +} + +func matchesValue(expected string, v any) bool { + switch v := v.(type) { + case common.MapStr, map[string]any: + return false + case []any: + for _, e := range v { + if matchesSingleValue(expected, e) { + return true + } + } + default: + return matchesSingleValue(expected, v) + } + + return false +} + +func matchesSingleValue(expected string, v any) bool { + switch v := v.(type) { + case common.MapStr, map[string]any, []any: + return false + case string: + return expected == v + case int, int32, int64: + n, err := strconv.Atoi(expected) + if err != nil { + return false + } + return n == v + case float64: + n, err := strconv.ParseFloat(expected, 64) + if err != nil { + return false + } + return n == v + default: + return false + } + + return false +} + func (t testConfig) Name() string { name := filepath.Base(t.Path) if matches := systemTestConfigFilePattern.FindStringSubmatch(name); len(matches) > 1 { From 817982c157cacc63aafcb7067a8f4e620fe928b1 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Wed, 30 Aug 2023 23:44:09 +0200 Subject: [PATCH 3/8] Add samples to nginx package --- internal/testrunner/runners/system/runner.go | 36 +++++- .../_dev/test/system/test-default-config.yml | 5 + .../data_stream/access/sample_event.json | 106 ---------------- .../data_stream/access/sample_event_200.json | 113 +++++++++++++++++ .../access/sample_event_other.json | 106 ---------------- test/packages/parallel/nginx/docs/README.md | 115 ++++++++++++++++++ 6 files changed, 263 insertions(+), 218 deletions(-) delete mode 100644 test/packages/parallel/nginx/data_stream/access/sample_event.json create mode 100644 test/packages/parallel/nginx/data_stream/access/sample_event_200.json delete mode 100644 test/packages/parallel/nginx/data_stream/access/sample_event_other.json diff --git a/internal/testrunner/runners/system/runner.go b/internal/testrunner/runners/system/runner.go index a6cc8e316..ce98e8767 100644 --- a/internal/testrunner/runners/system/runner.go +++ b/internal/testrunner/runners/system/runner.go @@ -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) } @@ -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) } @@ -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 } @@ -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 diff --git a/test/packages/parallel/nginx/data_stream/access/_dev/test/system/test-default-config.yml b/test/packages/parallel/nginx/data_stream/access/_dev/test/system/test-default-config.yml index 0f73c5dd9..86bdf96f1 100644 --- a/test/packages/parallel/nginx/data_stream/access/_dev/test/system/test-default-config.yml +++ b/test/packages/parallel/nginx/data_stream/access/_dev/test/system/test-default-config.yml @@ -3,3 +3,8 @@ data_stream: vars: paths: - "{{SERVICE_LOGS_DIR}}/access.log*" +samples: + - name: "200" + condition: + key: "http.response.status_code" + value: "200" diff --git a/test/packages/parallel/nginx/data_stream/access/sample_event.json b/test/packages/parallel/nginx/data_stream/access/sample_event.json deleted file mode 100644 index b44215034..000000000 --- a/test/packages/parallel/nginx/data_stream/access/sample_event.json +++ /dev/null @@ -1,106 +0,0 @@ -{ - "agent": { - "hostname": "a73e7856c209", - "name": "a73e7856c209", - "id": "3987d2b3-b40a-4aa0-99fc-478f9d7079ea", - "ephemeral_id": "6d41da1c-5f71-4bd4-b326-a8913bfaa884", - "type": "filebeat", - "version": "7.11.0" - }, - "nginx": { - "access": { - "remote_ip_list": [ - "127.0.0.1" - ] - } - }, - "log": { - "file": { - "path": "/tmp/service_logs/access.log" - }, - "offset": 0 - }, - "elastic_agent": { - "id": "5ca3af72-37c3-48b6-92e8-176d154bb66f", - "version": "7.11.0", - "snapshot": true - }, - "source": { - "address": "127.0.0.1", - "ip": "127.0.0.1" - }, - "url": { - "original": "/server-status" - }, - "input": { - "type": "log" - }, - "@timestamp": "2020-12-03T11:41:57.000Z", - "ecs": { - "version": "1.6.0" - }, - "related": { - "ip": [ - "127.0.0.1" - ] - }, - "data_stream": { - "namespace": "ep", - "type": "logs", - "dataset": "nginx.access" - }, - "host": { - "hostname": "a73e7856c209", - "os": { - "kernel": "4.9.184-linuxkit", - "codename": "Core", - "name": "CentOS Linux", - "family": "redhat", - "version": "7 (Core)", - "platform": "centos" - }, - "containerized": true, - "ip": [ - "192.168.80.6" - ], - "name": "a73e7856c209", - "id": "06c26569966fd125c15acac5d7feffb6", - "mac": [ - "02:42:c0:a8:50:06" - ], - "architecture": "x86_64" - }, - "http": { - "request": { - "method": "get" - }, - "response": { - "status_code": 200, - "body": { - "bytes": 97 - } - }, - "version": "1.1" - }, - "event": { - "timezone": "+00:00", - "created": "2020-12-03T11:42:17.116Z", - "kind": "event", - "category": [ - "web" - ], - "type": [ - "access" - ], - "dataset": "nginx.access", - "outcome": "success" - }, - "user_agent": { - "original": "curl/7.64.0", - "name": "curl", - "device": { - "name": "Other" - }, - "version": "7.64.0" - } -} \ No newline at end of file diff --git a/test/packages/parallel/nginx/data_stream/access/sample_event_200.json b/test/packages/parallel/nginx/data_stream/access/sample_event_200.json new file mode 100644 index 000000000..fbc468ec9 --- /dev/null +++ b/test/packages/parallel/nginx/data_stream/access/sample_event_200.json @@ -0,0 +1,113 @@ +{ + "@timestamp": "2023-08-30T21:58:41.000Z", + "_tmp": {}, + "agent": { + "ephemeral_id": "539ad62b-52e5-459a-8412-ab7c327cfd3e", + "id": "1fa30280-2acf-49e3-834c-cb600d113118", + "name": "docker-fleet-agent", + "type": "filebeat", + "version": "8.8.2" + }, + "data_stream": { + "dataset": "nginx.access", + "namespace": "ep", + "type": "logs" + }, + "ecs": { + "version": "1.12.0" + }, + "elastic_agent": { + "id": "1fa30280-2acf-49e3-834c-cb600d113118", + "snapshot": false, + "version": "8.8.2" + }, + "event": { + "agent_id_status": "verified", + "category": [ + "web" + ], + "created": "2023-08-30T21:58:57.127Z", + "dataset": "nginx.access", + "ingested": "2023-08-30T21:58:58Z", + "kind": "event", + "outcome": "success", + "timezone": "+00:00", + "type": [ + "access" + ] + }, + "host": { + "architecture": "x86_64", + "containerized": false, + "hostname": "docker-fleet-agent", + "id": "f61391496aaa43bb94736676494450c5", + "ip": [ + "172.19.0.7" + ], + "mac": [ + "02-42-AC-13-00-07" + ], + "name": "docker-fleet-agent", + "os": { + "codename": "focal", + "family": "debian", + "kernel": "5.19.0-50-generic", + "name": "Ubuntu", + "platform": "ubuntu", + "type": "linux", + "version": "20.04.6 LTS (Focal Fossa)" + } + }, + "http": { + "request": { + "method": "get" + }, + "response": { + "body": { + "bytes": 97 + }, + "status_code": 200 + }, + "version": "1.1" + }, + "input": { + "type": "log" + }, + "log": { + "file": { + "path": "/tmp/service_logs/access.log" + }, + "offset": 0 + }, + "nginx": { + "access": { + "remote_ip_list": [ + "127.0.0.1" + ] + } + }, + "related": { + "ip": [ + "127.0.0.1" + ] + }, + "source": { + "address": "127.0.0.1", + "ip": "127.0.0.1" + }, + "tags": [ + "nginx-access" + ], + "url": { + "original": "/server-status", + "path": "/server-status" + }, + "user_agent": { + "device": { + "name": "Other" + }, + "name": "curl", + "original": "curl/7.64.0", + "version": "7.64.0" + } +} \ No newline at end of file diff --git a/test/packages/parallel/nginx/data_stream/access/sample_event_other.json b/test/packages/parallel/nginx/data_stream/access/sample_event_other.json deleted file mode 100644 index b44215034..000000000 --- a/test/packages/parallel/nginx/data_stream/access/sample_event_other.json +++ /dev/null @@ -1,106 +0,0 @@ -{ - "agent": { - "hostname": "a73e7856c209", - "name": "a73e7856c209", - "id": "3987d2b3-b40a-4aa0-99fc-478f9d7079ea", - "ephemeral_id": "6d41da1c-5f71-4bd4-b326-a8913bfaa884", - "type": "filebeat", - "version": "7.11.0" - }, - "nginx": { - "access": { - "remote_ip_list": [ - "127.0.0.1" - ] - } - }, - "log": { - "file": { - "path": "/tmp/service_logs/access.log" - }, - "offset": 0 - }, - "elastic_agent": { - "id": "5ca3af72-37c3-48b6-92e8-176d154bb66f", - "version": "7.11.0", - "snapshot": true - }, - "source": { - "address": "127.0.0.1", - "ip": "127.0.0.1" - }, - "url": { - "original": "/server-status" - }, - "input": { - "type": "log" - }, - "@timestamp": "2020-12-03T11:41:57.000Z", - "ecs": { - "version": "1.6.0" - }, - "related": { - "ip": [ - "127.0.0.1" - ] - }, - "data_stream": { - "namespace": "ep", - "type": "logs", - "dataset": "nginx.access" - }, - "host": { - "hostname": "a73e7856c209", - "os": { - "kernel": "4.9.184-linuxkit", - "codename": "Core", - "name": "CentOS Linux", - "family": "redhat", - "version": "7 (Core)", - "platform": "centos" - }, - "containerized": true, - "ip": [ - "192.168.80.6" - ], - "name": "a73e7856c209", - "id": "06c26569966fd125c15acac5d7feffb6", - "mac": [ - "02:42:c0:a8:50:06" - ], - "architecture": "x86_64" - }, - "http": { - "request": { - "method": "get" - }, - "response": { - "status_code": 200, - "body": { - "bytes": 97 - } - }, - "version": "1.1" - }, - "event": { - "timezone": "+00:00", - "created": "2020-12-03T11:42:17.116Z", - "kind": "event", - "category": [ - "web" - ], - "type": [ - "access" - ], - "dataset": "nginx.access", - "outcome": "success" - }, - "user_agent": { - "original": "curl/7.64.0", - "name": "curl", - "device": { - "name": "Other" - }, - "version": "7.64.0" - } -} \ No newline at end of file diff --git a/test/packages/parallel/nginx/docs/README.md b/test/packages/parallel/nginx/docs/README.md index 87ddf759e..7fb8fb4b4 100644 --- a/test/packages/parallel/nginx/docs/README.md +++ b/test/packages/parallel/nginx/docs/README.md @@ -137,6 +137,121 @@ Example events for `access` look as following: } ``` ```json +{ + "@timestamp": "2023-08-30T21:58:41.000Z", + "_tmp": {}, + "agent": { + "ephemeral_id": "539ad62b-52e5-459a-8412-ab7c327cfd3e", + "id": "1fa30280-2acf-49e3-834c-cb600d113118", + "name": "docker-fleet-agent", + "type": "filebeat", + "version": "8.8.2" + }, + "data_stream": { + "dataset": "nginx.access", + "namespace": "ep", + "type": "logs" + }, + "ecs": { + "version": "1.12.0" + }, + "elastic_agent": { + "id": "1fa30280-2acf-49e3-834c-cb600d113118", + "snapshot": false, + "version": "8.8.2" + }, + "event": { + "agent_id_status": "verified", + "category": [ + "web" + ], + "created": "2023-08-30T21:58:57.127Z", + "dataset": "nginx.access", + "ingested": "2023-08-30T21:58:58Z", + "kind": "event", + "outcome": "success", + "timezone": "+00:00", + "type": [ + "access" + ] + }, + "host": { + "architecture": "x86_64", + "containerized": false, + "hostname": "docker-fleet-agent", + "id": "f61391496aaa43bb94736676494450c5", + "ip": [ + "172.19.0.7" + ], + "mac": [ + "02-42-AC-13-00-07" + ], + "name": "docker-fleet-agent", + "os": { + "codename": "focal", + "family": "debian", + "kernel": "5.19.0-50-generic", + "name": "Ubuntu", + "platform": "ubuntu", + "type": "linux", + "version": "20.04.6 LTS (Focal Fossa)" + } + }, + "http": { + "request": { + "method": "get" + }, + "response": { + "body": { + "bytes": 97 + }, + "status_code": 200 + }, + "version": "1.1" + }, + "input": { + "type": "log" + }, + "log": { + "file": { + "path": "/tmp/service_logs/access.log" + }, + "offset": 0 + }, + "nginx": { + "access": { + "remote_ip_list": [ + "127.0.0.1" + ] + } + }, + "related": { + "ip": [ + "127.0.0.1" + ] + }, + "source": { + "address": "127.0.0.1", + "ip": "127.0.0.1" + }, + "tags": [ + "nginx-access" + ], + "url": { + "original": "/server-status", + "path": "/server-status" + }, + "user_agent": { + "device": { + "name": "Other" + }, + "name": "curl", + "original": "curl/7.64.0", + "version": "7.64.0" + } +} +``` +```json { "agent": { "hostname": "a73e7856c209", From a96769a035b18e79cc4d16477341c456e2a204ad Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Thu, 31 Aug 2023 00:27:48 +0200 Subject: [PATCH 4/8] Fix test --- internal/docs/readme_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/docs/readme_test.go b/internal/docs/readme_test.go index ab0afeb8b..78f9d5767 100644 --- a/internal/docs/readme_test.go +++ b/internal/docs/readme_test.go @@ -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 } From 14d25c056a2316493de47dd13c252160607738db Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Thu, 31 Aug 2023 12:15:57 +0200 Subject: [PATCH 5/8] Replace package-spec --- go.mod | 2 + go.sum | 4 +- internal/docs/sample_event.go | 2 + test/packages/parallel/nginx/docs/README.md | 218 +------------------- 4 files changed, 7 insertions(+), 219 deletions(-) diff --git a/go.mod b/go.mod index 9b28ab431..1d817b80e 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index 1997b13b9..a5ed9217e 100644 --- a/go.sum +++ b/go.sum @@ -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= @@ -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= diff --git a/internal/docs/sample_event.go b/internal/docs/sample_event.go index 61749e9ac..f976ecbd3 100644 --- a/internal/docs/sample_event.go +++ b/internal/docs/sample_event.go @@ -9,6 +9,7 @@ import ( "io" "os" "path/filepath" + "sort" "strings" "github.com/elastic/elastic-package/internal/formatter" @@ -34,6 +35,7 @@ func renderSampleEvents(packageRoot, dataStreamName string) (string, error) { stripDataStreamFolderSuffix(dataStreamName)) } + sort.Sort(sort.StringSlice(eventPaths)) for i, eventPath := range eventPaths { if i > 0 { fmt.Fprintln(&builder) diff --git a/test/packages/parallel/nginx/docs/README.md b/test/packages/parallel/nginx/docs/README.md index 7fb8fb4b4..44dd88c23 100644 --- a/test/packages/parallel/nginx/docs/README.md +++ b/test/packages/parallel/nginx/docs/README.md @@ -26,116 +26,8 @@ field can be overwritten with the original timezone using the add_fields process Access logs collects the nginx access logs. -Example events for `access` look as following: +An example event for `access` looks as following: -```json -{ - "agent": { - "hostname": "a73e7856c209", - "name": "a73e7856c209", - "id": "3987d2b3-b40a-4aa0-99fc-478f9d7079ea", - "ephemeral_id": "6d41da1c-5f71-4bd4-b326-a8913bfaa884", - "type": "filebeat", - "version": "7.11.0" - }, - "nginx": { - "access": { - "remote_ip_list": [ - "127.0.0.1" - ] - } - }, - "log": { - "file": { - "path": "/tmp/service_logs/access.log" - }, - "offset": 0 - }, - "elastic_agent": { - "id": "5ca3af72-37c3-48b6-92e8-176d154bb66f", - "version": "7.11.0", - "snapshot": true - }, - "source": { - "address": "127.0.0.1", - "ip": "127.0.0.1" - }, - "url": { - "original": "/server-status" - }, - "input": { - "type": "log" - }, - "@timestamp": "2020-12-03T11:41:57.000Z", - "ecs": { - "version": "1.6.0" - }, - "related": { - "ip": [ - "127.0.0.1" - ] - }, - "data_stream": { - "namespace": "ep", - "type": "logs", - "dataset": "nginx.access" - }, - "host": { - "hostname": "a73e7856c209", - "os": { - "kernel": "4.9.184-linuxkit", - "codename": "Core", - "name": "CentOS Linux", - "family": "redhat", - "version": "7 (Core)", - "platform": "centos" - }, - "containerized": true, - "ip": [ - "192.168.80.6" - ], - "name": "a73e7856c209", - "id": "06c26569966fd125c15acac5d7feffb6", - "mac": [ - "02:42:c0:a8:50:06" - ], - "architecture": "x86_64" - }, - "http": { - "request": { - "method": "get" - }, - "response": { - "status_code": 200, - "body": { - "bytes": 97 - } - }, - "version": "1.1" - }, - "event": { - "timezone": "+00:00", - "created": "2020-12-03T11:42:17.116Z", - "kind": "event", - "category": [ - "web" - ], - "type": [ - "access" - ], - "dataset": "nginx.access", - "outcome": "success" - }, - "user_agent": { - "original": "curl/7.64.0", - "name": "curl", - "device": { - "name": "Other" - }, - "version": "7.64.0" - } -} -``` ```json { "@timestamp": "2023-08-30T21:58:41.000Z", @@ -251,114 +143,6 @@ Example events for `access` look as following: } } ``` -```json -{ - "agent": { - "hostname": "a73e7856c209", - "name": "a73e7856c209", - "id": "3987d2b3-b40a-4aa0-99fc-478f9d7079ea", - "ephemeral_id": "6d41da1c-5f71-4bd4-b326-a8913bfaa884", - "type": "filebeat", - "version": "7.11.0" - }, - "nginx": { - "access": { - "remote_ip_list": [ - "127.0.0.1" - ] - } - }, - "log": { - "file": { - "path": "/tmp/service_logs/access.log" - }, - "offset": 0 - }, - "elastic_agent": { - "id": "5ca3af72-37c3-48b6-92e8-176d154bb66f", - "version": "7.11.0", - "snapshot": true - }, - "source": { - "address": "127.0.0.1", - "ip": "127.0.0.1" - }, - "url": { - "original": "/server-status" - }, - "input": { - "type": "log" - }, - "@timestamp": "2020-12-03T11:41:57.000Z", - "ecs": { - "version": "1.6.0" - }, - "related": { - "ip": [ - "127.0.0.1" - ] - }, - "data_stream": { - "namespace": "ep", - "type": "logs", - "dataset": "nginx.access" - }, - "host": { - "hostname": "a73e7856c209", - "os": { - "kernel": "4.9.184-linuxkit", - "codename": "Core", - "name": "CentOS Linux", - "family": "redhat", - "version": "7 (Core)", - "platform": "centos" - }, - "containerized": true, - "ip": [ - "192.168.80.6" - ], - "name": "a73e7856c209", - "id": "06c26569966fd125c15acac5d7feffb6", - "mac": [ - "02:42:c0:a8:50:06" - ], - "architecture": "x86_64" - }, - "http": { - "request": { - "method": "get" - }, - "response": { - "status_code": 200, - "body": { - "bytes": 97 - } - }, - "version": "1.1" - }, - "event": { - "timezone": "+00:00", - "created": "2020-12-03T11:42:17.116Z", - "kind": "event", - "category": [ - "web" - ], - "type": [ - "access" - ], - "dataset": "nginx.access", - "outcome": "success" - }, - "user_agent": { - "original": "curl/7.64.0", - "name": "curl", - "device": { - "name": "Other" - }, - "version": "7.64.0" - } -} -``` **Exported fields** From 0575b81ba557732f391febff0c4ff105f43701f1 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Thu, 31 Aug 2023 12:22:16 +0200 Subject: [PATCH 6/8] Linting --- internal/docs/sample_event.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/docs/sample_event.go b/internal/docs/sample_event.go index f976ecbd3..4f476bc9a 100644 --- a/internal/docs/sample_event.go +++ b/internal/docs/sample_event.go @@ -35,7 +35,7 @@ func renderSampleEvents(packageRoot, dataStreamName string) (string, error) { stripDataStreamFolderSuffix(dataStreamName)) } - sort.Sort(sort.StringSlice(eventPaths)) + sort.Strings(eventPaths) for i, eventPath := range eventPaths { if i > 0 { fmt.Fprintln(&builder) From 313bec793c683038b211f3d5f78d2b703a96cd1c Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Thu, 31 Aug 2023 17:20:12 +0200 Subject: [PATCH 7/8] Validate all sample documents in asset tests --- internal/testrunner/runners/static/runner.go | 61 ++++++++++---------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/internal/testrunner/runners/static/runner.go b/internal/testrunner/runners/static/runner.go index e0bd881d3..1e3db1abc 100644 --- a/internal/testrunner/runners/static/runner.go +++ b/internal/testrunner/runners/static/runner.go @@ -5,7 +5,6 @@ package static import ( - "errors" "fmt" "os" "path/filepath" @@ -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 @@ -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{} } @@ -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), @@ -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) { From baabdc889f0c74c1bd27a1549d25f819b61f0530 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Thu, 31 Aug 2023 18:02:34 +0200 Subject: [PATCH 8/8] Actually write all events and not only the first one --- internal/testrunner/runners/system/runner.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/testrunner/runners/system/runner.go b/internal/testrunner/runners/system/runner.go index ce98e8767..fe70bc648 100644 --- a/internal/testrunner/runners/system/runner.go +++ b/internal/testrunner/runners/system/runner.go @@ -1275,7 +1275,7 @@ func (r *runner) generateTestResult(config *testConfig, docs []common.MapStr) er found := false for _, doc := range docs { if sample.Matches(doc) { - if err := writeSampleEvent(rootPath, docs[0], sample.Name); err != nil { + if err := writeSampleEvent(rootPath, doc, sample.Name); err != nil { return fmt.Errorf("failed to write sample event file: %w", err) } found = true