From dfbb9511b9df1cd0711fd8695e438a6f9e91e510 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Fri, 12 Jul 2024 20:34:40 +0200 Subject: [PATCH 01/18] Add helper to enable or disable failure store in index template --- .../elasticsearch/ingest/failurestorage.go | 75 +++ .../ingest/failurestorage_test.go | 75 +++ .../elasticsearch-9-enable-failure-store.yaml | 436 ++++++++++++++++++ 3 files changed, 586 insertions(+) create mode 100644 internal/elasticsearch/ingest/failurestorage.go create mode 100644 internal/elasticsearch/ingest/failurestorage_test.go create mode 100644 internal/elasticsearch/ingest/testdata/elasticsearch-9-enable-failure-store.yaml diff --git a/internal/elasticsearch/ingest/failurestorage.go b/internal/elasticsearch/ingest/failurestorage.go new file mode 100644 index 0000000000..e15f519e7d --- /dev/null +++ b/internal/elasticsearch/ingest/failurestorage.go @@ -0,0 +1,75 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package ingest + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + + "github.com/elastic/elastic-package/internal/elasticsearch" +) + +func EnableFailureStore(ctx context.Context, api *elasticsearch.API, indexTemplateName string, enabled bool) error { + resp, err := api.Indices.GetIndexTemplate( + api.Indices.GetIndexTemplate.WithContext(ctx), + api.Indices.GetIndexTemplate.WithName(indexTemplateName), + ) + if err != nil { + return fmt.Errorf("failed to get index template %s: %w", indexTemplateName, err) + } + defer resp.Body.Close() + if resp.IsError() { + return fmt.Errorf("failed to get index template %s: %s", indexTemplateName, resp.String()) + } + + var templateResponse struct { + IndexTemplates []struct { + IndexTemplate map[string]any `json:"index_template"` + } `json:"index_templates"` + } + err = json.NewDecoder(resp.Body).Decode(&templateResponse) + if err != nil { + return fmt.Errorf("failed to decode response while getting index template %s: %w", indexTemplateName, err) + } + if n := len(templateResponse.IndexTemplates); n != 1 { + return fmt.Errorf("unexpected number of index templates obtained while getting %s, expected 1, found %d", indexTemplateName, err) + } + + template := templateResponse.IndexTemplates[0].IndexTemplate + dsSettings, found := template["data_stream"] + if found { + dsMap, ok := dsSettings.(map[string]any) + if !ok { + return fmt.Errorf("unexpected type for data stream settings in index template %s, expected map, found %T", indexTemplateName, dsMap) + } + dsMap["failure_store"] = enabled + template["data_stream"] = dsMap + } else { + template["data_stream"] = map[string]any{ + "failure_store": enabled, + } + } + + d, err := json.Marshal(template) + if err != nil { + return fmt.Errorf("failed to marshal template after updating it: %w", err) + } + + updateResp, err := api.Indices.PutIndexTemplate(indexTemplateName, bytes.NewReader(d), + api.Indices.PutIndexTemplate.WithContext(ctx), + api.Indices.PutIndexTemplate.WithCreate(false), + ) + if err != nil { + return fmt.Errorf("failed to update index template %s: %w", indexTemplateName, err) + } + defer updateResp.Body.Close() + if updateResp.IsError() { + return fmt.Errorf("failed to update index template %s: %s", indexTemplateName, resp.String()) + } + + return nil +} diff --git a/internal/elasticsearch/ingest/failurestorage_test.go b/internal/elasticsearch/ingest/failurestorage_test.go new file mode 100644 index 0000000000..53a86ffba4 --- /dev/null +++ b/internal/elasticsearch/ingest/failurestorage_test.go @@ -0,0 +1,75 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package ingest + +import ( + "bytes" + "context" + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/elastic/elastic-package/internal/elasticsearch" + estest "github.com/elastic/elastic-package/internal/elasticsearch/test" +) + +func TestEnableFailureStore(t *testing.T) { + client := estest.NewClient(t, "testdata/elasticsearch-9-enable-failure-store") + + templateName := "ep-test-index-template" + templateBody := []byte(`{"index_patterns": ["metrics-eptest.failurestore-*"],"data_stream": {}}`) + createTempIndexTemplate(t, client.API, templateName, templateBody) + assertFailureStore(t, client.API, templateName, false) + + err := EnableFailureStore(context.Background(), client.API, templateName, true) + assert.NoError(t, err) + assertFailureStore(t, client.API, templateName, true) + + err = EnableFailureStore(context.Background(), client.API, templateName, false) + assert.NoError(t, err) + assertFailureStore(t, client.API, templateName, false) +} + +func createTempIndexTemplate(t *testing.T, api *elasticsearch.API, name string, body []byte) { + createResp, err := api.Indices.PutIndexTemplate(name, bytes.NewReader(body), + api.Indices.PutIndexTemplate.WithCreate(true), + ) + require.NoError(t, err) + require.False(t, createResp.IsError(), createResp.String()) + t.Cleanup(func() { + deleteResp, err := api.Indices.DeleteIndexTemplate(name) + require.NoError(t, err) + require.False(t, deleteResp.IsError()) + }) +} + +func assertFailureStore(t *testing.T, api *elasticsearch.API, name string, expected bool) { + resp, err := api.Indices.GetIndexTemplate( + api.Indices.GetIndexTemplate.WithName(name), + ) + require.NoError(t, err) + require.False(t, resp.IsError()) + defer resp.Body.Close() + + var templateResponse struct { + IndexTemplates []struct { + IndexTemplate struct { + DataStream struct { + FailureStore *bool `json:"failure_store"` + } `json:"data_stream"` + } `json:"index_template"` + } `json:"index_templates"` + } + err = json.NewDecoder(resp.Body).Decode(&templateResponse) + require.NoError(t, err) + require.Len(t, templateResponse.IndexTemplates, 1) + found := templateResponse.IndexTemplates[0].IndexTemplate.DataStream.FailureStore + + if assert.NotNil(t, found) { + assert.Equal(t, expected, *found) + } +} diff --git a/internal/elasticsearch/ingest/testdata/elasticsearch-9-enable-failure-store.yaml b/internal/elasticsearch/ingest/testdata/elasticsearch-9-enable-failure-store.yaml new file mode 100644 index 0000000000..5674218baa --- /dev/null +++ b/internal/elasticsearch/ingest/testdata/elasticsearch-9-enable-failure-store.yaml @@ -0,0 +1,436 @@ +--- +version: 2 +interactions: + - id: 0 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: "" + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Authorization: + - Basic ZWxhc3RpYzpjaGFuZ2VtZQ== + User-Agent: + - go-elasticsearch/7.17.10 (linux amd64; Go 1.22.1) + X-Elastic-Client-Meta: + - es=7.17.10,go=1.22.1,t=7.17.10,hc=1.22.1 + url: https://127.0.0.1:9200/ + method: GET + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 548 + uncompressed: false + body: | + { + "name" : "a31464810e80", + "cluster_name" : "elasticsearch", + "cluster_uuid" : "zOcXOIv5R_qHrrA4HILi7g", + "version" : { + "number" : "8.15.0-SNAPSHOT", + "build_flavor" : "default", + "build_type" : "docker", + "build_hash" : "9092394b19dea9e0f20290a2571a82b1d3610987", + "build_date" : "2024-07-11T17:13:25.001995094Z", + "build_snapshot" : true, + "lucene_version" : "9.11.1", + "minimum_wire_compatibility_version" : "7.17.0", + "minimum_index_compatibility_version" : "7.0.0" + }, + "tagline" : "You Know, for Search" + } + headers: + Content-Length: + - "548" + Content-Type: + - application/json + X-Elastic-Product: + - Elasticsearch + status: 200 OK + code: 200 + duration: 6.311531ms + - id: 1 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 71 + transfer_encoding: [] + trailer: {} + host: "" + remote_addr: "" + request_uri: "" + body: '{"index_patterns": ["metrics-eptest.failurestore-*"],"data_stream": {}}' + form: {} + headers: + Authorization: + - Basic ZWxhc3RpYzpjaGFuZ2VtZQ== + Content-Type: + - application/json + User-Agent: + - go-elasticsearch/7.17.10 (linux amd64; Go 1.22.1) + X-Elastic-Client-Meta: + - es=7.17.10,go=1.22.1,t=7.17.10,hc=1.22.1 + url: https://127.0.0.1:9200/_index_template/ep-test-index-template?create=true + method: PUT + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 21 + uncompressed: false + body: '{"acknowledged":true}' + headers: + Content-Length: + - "21" + Content-Type: + - application/json + X-Elastic-Product: + - Elasticsearch + status: 200 OK + code: 200 + duration: 137.831843ms + - id: 2 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: "" + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Authorization: + - Basic ZWxhc3RpYzpjaGFuZ2VtZQ== + User-Agent: + - go-elasticsearch/7.17.10 (linux amd64; Go 1.22.1) + X-Elastic-Client-Meta: + - es=7.17.10,go=1.22.1,t=7.17.10,hc=1.22.1 + url: https://127.0.0.1:9200/_index_template/ep-test-index-template + method: GET + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 224 + uncompressed: false + body: '{"index_templates":[{"name":"ep-test-index-template","index_template":{"index_patterns":["metrics-eptest.failurestore-*"],"composed_of":[],"data_stream":{"hidden":false,"allow_custom_routing":false,"failure_store":false}}}]}' + headers: + Content-Length: + - "224" + Content-Type: + - application/json + X-Elastic-Product: + - Elasticsearch + status: 200 OK + code: 200 + duration: 1.034594ms + - id: 3 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: "" + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Authorization: + - Basic ZWxhc3RpYzpjaGFuZ2VtZQ== + User-Agent: + - go-elasticsearch/7.17.10 (linux amd64; Go 1.22.1) + X-Elastic-Client-Meta: + - es=7.17.10,go=1.22.1,t=7.17.10,hc=1.22.1 + url: https://127.0.0.1:9200/_index_template/ep-test-index-template + method: GET + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 224 + uncompressed: false + body: '{"index_templates":[{"name":"ep-test-index-template","index_template":{"index_patterns":["metrics-eptest.failurestore-*"],"composed_of":[],"data_stream":{"hidden":false,"allow_custom_routing":false,"failure_store":false}}}]}' + headers: + Content-Length: + - "224" + Content-Type: + - application/json + X-Elastic-Product: + - Elasticsearch + status: 200 OK + code: 200 + duration: 788.776µs + - id: 4 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 150 + transfer_encoding: [] + trailer: {} + host: "" + remote_addr: "" + request_uri: "" + body: '{"composed_of":[],"data_stream":{"allow_custom_routing":false,"failure_store":true,"hidden":false},"index_patterns":["metrics-eptest.failurestore-*"]}' + form: {} + headers: + Authorization: + - Basic ZWxhc3RpYzpjaGFuZ2VtZQ== + Content-Type: + - application/json + User-Agent: + - go-elasticsearch/7.17.10 (linux amd64; Go 1.22.1) + X-Elastic-Client-Meta: + - es=7.17.10,go=1.22.1,t=7.17.10,hc=1.22.1 + url: https://127.0.0.1:9200/_index_template/ep-test-index-template?create=false + method: PUT + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 21 + uncompressed: false + body: '{"acknowledged":true}' + headers: + Content-Length: + - "21" + Content-Type: + - application/json + X-Elastic-Product: + - Elasticsearch + status: 200 OK + code: 200 + duration: 103.772284ms + - id: 5 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: "" + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Authorization: + - Basic ZWxhc3RpYzpjaGFuZ2VtZQ== + User-Agent: + - go-elasticsearch/7.17.10 (linux amd64; Go 1.22.1) + X-Elastic-Client-Meta: + - es=7.17.10,go=1.22.1,t=7.17.10,hc=1.22.1 + url: https://127.0.0.1:9200/_index_template/ep-test-index-template + method: GET + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 223 + uncompressed: false + body: '{"index_templates":[{"name":"ep-test-index-template","index_template":{"index_patterns":["metrics-eptest.failurestore-*"],"composed_of":[],"data_stream":{"hidden":false,"allow_custom_routing":false,"failure_store":true}}}]}' + headers: + Content-Length: + - "223" + Content-Type: + - application/json + X-Elastic-Product: + - Elasticsearch + status: 200 OK + code: 200 + duration: 1.043947ms + - id: 6 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: "" + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Authorization: + - Basic ZWxhc3RpYzpjaGFuZ2VtZQ== + User-Agent: + - go-elasticsearch/7.17.10 (linux amd64; Go 1.22.1) + X-Elastic-Client-Meta: + - es=7.17.10,go=1.22.1,t=7.17.10,hc=1.22.1 + url: https://127.0.0.1:9200/_index_template/ep-test-index-template + method: GET + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 223 + uncompressed: false + body: '{"index_templates":[{"name":"ep-test-index-template","index_template":{"index_patterns":["metrics-eptest.failurestore-*"],"composed_of":[],"data_stream":{"hidden":false,"allow_custom_routing":false,"failure_store":true}}}]}' + headers: + Content-Length: + - "223" + Content-Type: + - application/json + X-Elastic-Product: + - Elasticsearch + status: 200 OK + code: 200 + duration: 664.249µs + - id: 7 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 151 + transfer_encoding: [] + trailer: {} + host: "" + remote_addr: "" + request_uri: "" + body: '{"composed_of":[],"data_stream":{"allow_custom_routing":false,"failure_store":false,"hidden":false},"index_patterns":["metrics-eptest.failurestore-*"]}' + form: {} + headers: + Authorization: + - Basic ZWxhc3RpYzpjaGFuZ2VtZQ== + Content-Type: + - application/json + User-Agent: + - go-elasticsearch/7.17.10 (linux amd64; Go 1.22.1) + X-Elastic-Client-Meta: + - es=7.17.10,go=1.22.1,t=7.17.10,hc=1.22.1 + url: https://127.0.0.1:9200/_index_template/ep-test-index-template?create=false + method: PUT + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 21 + uncompressed: false + body: '{"acknowledged":true}' + headers: + Content-Length: + - "21" + Content-Type: + - application/json + X-Elastic-Product: + - Elasticsearch + status: 200 OK + code: 200 + duration: 99.796675ms + - id: 8 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: "" + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Authorization: + - Basic ZWxhc3RpYzpjaGFuZ2VtZQ== + User-Agent: + - go-elasticsearch/7.17.10 (linux amd64; Go 1.22.1) + X-Elastic-Client-Meta: + - es=7.17.10,go=1.22.1,t=7.17.10,hc=1.22.1 + url: https://127.0.0.1:9200/_index_template/ep-test-index-template + method: GET + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 224 + uncompressed: false + body: '{"index_templates":[{"name":"ep-test-index-template","index_template":{"index_patterns":["metrics-eptest.failurestore-*"],"composed_of":[],"data_stream":{"hidden":false,"allow_custom_routing":false,"failure_store":false}}}]}' + headers: + Content-Length: + - "224" + Content-Type: + - application/json + X-Elastic-Product: + - Elasticsearch + status: 200 OK + code: 200 + duration: 1.317273ms + - id: 9 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: "" + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Authorization: + - Basic ZWxhc3RpYzpjaGFuZ2VtZQ== + User-Agent: + - go-elasticsearch/7.17.10 (linux amd64; Go 1.22.1) + X-Elastic-Client-Meta: + - es=7.17.10,go=1.22.1,t=7.17.10,hc=1.22.1 + url: https://127.0.0.1:9200/_index_template/ep-test-index-template + method: DELETE + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 21 + uncompressed: false + body: '{"acknowledged":true}' + headers: + Content-Length: + - "21" + Content-Type: + - application/json + X-Elastic-Product: + - Elasticsearch + status: 200 OK + code: 200 + duration: 92.256183ms From 67b94261a2cacd3de432797ac769e2b800f69c3d Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Fri, 12 Jul 2024 20:42:48 +0200 Subject: [PATCH 02/18] Don't update the data stream if already in the expected state --- .../elasticsearch/ingest/failurestorage.go | 4 + .../ingest/failurestorage_test.go | 13 + ...ticsearch-9-enable-failure-store-noop.yaml | 268 ++++++++++++++++++ 3 files changed, 285 insertions(+) create mode 100644 internal/elasticsearch/ingest/testdata/elasticsearch-9-enable-failure-store-noop.yaml diff --git a/internal/elasticsearch/ingest/failurestorage.go b/internal/elasticsearch/ingest/failurestorage.go index e15f519e7d..52271d16a8 100644 --- a/internal/elasticsearch/ingest/failurestorage.go +++ b/internal/elasticsearch/ingest/failurestorage.go @@ -46,6 +46,10 @@ func EnableFailureStore(ctx context.Context, api *elasticsearch.API, indexTempla if !ok { return fmt.Errorf("unexpected type for data stream settings in index template %s, expected map, found %T", indexTemplateName, dsMap) } + if current, found := dsMap["failure_store"].(bool); found && current == enabled { + // Nothing to do, it already has the expected value. + return nil + } dsMap["failure_store"] = enabled template["data_stream"] = dsMap } else { diff --git a/internal/elasticsearch/ingest/failurestorage_test.go b/internal/elasticsearch/ingest/failurestorage_test.go index 53a86ffba4..d61530cf69 100644 --- a/internal/elasticsearch/ingest/failurestorage_test.go +++ b/internal/elasticsearch/ingest/failurestorage_test.go @@ -34,6 +34,19 @@ func TestEnableFailureStore(t *testing.T) { assertFailureStore(t, client.API, templateName, false) } +func TestEnableFailureStoreNothingToDo(t *testing.T) { + client := estest.NewClient(t, "testdata/elasticsearch-9-enable-failure-store-noop") + + templateName := "ep-test-index-template" + templateBody := []byte(`{"index_patterns": ["metrics-eptest.failurestore-*"],"data_stream": {"failure_store":true}}`) + createTempIndexTemplate(t, client.API, templateName, templateBody) + assertFailureStore(t, client.API, templateName, true) + + err := EnableFailureStore(context.Background(), client.API, templateName, true) + assert.NoError(t, err) + assertFailureStore(t, client.API, templateName, true) +} + func createTempIndexTemplate(t *testing.T, api *elasticsearch.API, name string, body []byte) { createResp, err := api.Indices.PutIndexTemplate(name, bytes.NewReader(body), api.Indices.PutIndexTemplate.WithCreate(true), diff --git a/internal/elasticsearch/ingest/testdata/elasticsearch-9-enable-failure-store-noop.yaml b/internal/elasticsearch/ingest/testdata/elasticsearch-9-enable-failure-store-noop.yaml new file mode 100644 index 0000000000..a83aabdce9 --- /dev/null +++ b/internal/elasticsearch/ingest/testdata/elasticsearch-9-enable-failure-store-noop.yaml @@ -0,0 +1,268 @@ +--- +version: 2 +interactions: + - id: 0 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: "" + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Authorization: + - Basic ZWxhc3RpYzpjaGFuZ2VtZQ== + User-Agent: + - go-elasticsearch/7.17.10 (linux amd64; Go 1.22.1) + X-Elastic-Client-Meta: + - es=7.17.10,go=1.22.1,t=7.17.10,hc=1.22.1 + url: https://127.0.0.1:9200/ + method: GET + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 548 + uncompressed: false + body: | + { + "name" : "a31464810e80", + "cluster_name" : "elasticsearch", + "cluster_uuid" : "zOcXOIv5R_qHrrA4HILi7g", + "version" : { + "number" : "8.15.0-SNAPSHOT", + "build_flavor" : "default", + "build_type" : "docker", + "build_hash" : "9092394b19dea9e0f20290a2571a82b1d3610987", + "build_date" : "2024-07-11T17:13:25.001995094Z", + "build_snapshot" : true, + "lucene_version" : "9.11.1", + "minimum_wire_compatibility_version" : "7.17.0", + "minimum_index_compatibility_version" : "7.0.0" + }, + "tagline" : "You Know, for Search" + } + headers: + Content-Length: + - "548" + Content-Type: + - application/json + X-Elastic-Product: + - Elasticsearch + status: 200 OK + code: 200 + duration: 4.25671ms + - id: 1 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 91 + transfer_encoding: [] + trailer: {} + host: "" + remote_addr: "" + request_uri: "" + body: '{"index_patterns": ["metrics-eptest.failurestore-*"],"data_stream": {"failure_store":true}}' + form: {} + headers: + Authorization: + - Basic ZWxhc3RpYzpjaGFuZ2VtZQ== + Content-Type: + - application/json + User-Agent: + - go-elasticsearch/7.17.10 (linux amd64; Go 1.22.1) + X-Elastic-Client-Meta: + - es=7.17.10,go=1.22.1,t=7.17.10,hc=1.22.1 + url: https://127.0.0.1:9200/_index_template/ep-test-index-template?create=true + method: PUT + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 21 + uncompressed: false + body: '{"acknowledged":true}' + headers: + Content-Length: + - "21" + Content-Type: + - application/json + X-Elastic-Product: + - Elasticsearch + status: 200 OK + code: 200 + duration: 96.553464ms + - id: 2 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: "" + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Authorization: + - Basic ZWxhc3RpYzpjaGFuZ2VtZQ== + User-Agent: + - go-elasticsearch/7.17.10 (linux amd64; Go 1.22.1) + X-Elastic-Client-Meta: + - es=7.17.10,go=1.22.1,t=7.17.10,hc=1.22.1 + url: https://127.0.0.1:9200/_index_template/ep-test-index-template + method: GET + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 223 + uncompressed: false + body: '{"index_templates":[{"name":"ep-test-index-template","index_template":{"index_patterns":["metrics-eptest.failurestore-*"],"composed_of":[],"data_stream":{"hidden":false,"allow_custom_routing":false,"failure_store":true}}}]}' + headers: + Content-Length: + - "223" + Content-Type: + - application/json + X-Elastic-Product: + - Elasticsearch + status: 200 OK + code: 200 + duration: 931.406µs + - id: 3 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: "" + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Authorization: + - Basic ZWxhc3RpYzpjaGFuZ2VtZQ== + User-Agent: + - go-elasticsearch/7.17.10 (linux amd64; Go 1.22.1) + X-Elastic-Client-Meta: + - es=7.17.10,go=1.22.1,t=7.17.10,hc=1.22.1 + url: https://127.0.0.1:9200/_index_template/ep-test-index-template + method: GET + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 223 + uncompressed: false + body: '{"index_templates":[{"name":"ep-test-index-template","index_template":{"index_patterns":["metrics-eptest.failurestore-*"],"composed_of":[],"data_stream":{"hidden":false,"allow_custom_routing":false,"failure_store":true}}}]}' + headers: + Content-Length: + - "223" + Content-Type: + - application/json + X-Elastic-Product: + - Elasticsearch + status: 200 OK + code: 200 + duration: 808.886µs + - id: 4 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: "" + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Authorization: + - Basic ZWxhc3RpYzpjaGFuZ2VtZQ== + User-Agent: + - go-elasticsearch/7.17.10 (linux amd64; Go 1.22.1) + X-Elastic-Client-Meta: + - es=7.17.10,go=1.22.1,t=7.17.10,hc=1.22.1 + url: https://127.0.0.1:9200/_index_template/ep-test-index-template + method: GET + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 223 + uncompressed: false + body: '{"index_templates":[{"name":"ep-test-index-template","index_template":{"index_patterns":["metrics-eptest.failurestore-*"],"composed_of":[],"data_stream":{"hidden":false,"allow_custom_routing":false,"failure_store":true}}}]}' + headers: + Content-Length: + - "223" + Content-Type: + - application/json + X-Elastic-Product: + - Elasticsearch + status: 200 OK + code: 200 + duration: 721.915µs + - id: 5 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: "" + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Authorization: + - Basic ZWxhc3RpYzpjaGFuZ2VtZQ== + User-Agent: + - go-elasticsearch/7.17.10 (linux amd64; Go 1.22.1) + X-Elastic-Client-Meta: + - es=7.17.10,go=1.22.1,t=7.17.10,hc=1.22.1 + url: https://127.0.0.1:9200/_index_template/ep-test-index-template + method: DELETE + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 21 + uncompressed: false + body: '{"acknowledged":true}' + headers: + Content-Length: + - "21" + Content-Type: + - application/json + X-Elastic-Product: + - Elasticsearch + status: 200 OK + code: 200 + duration: 83.638384ms From 0199402d0e0a35806926a190063ab0cbd6994e29 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Fri, 12 Jul 2024 20:48:50 +0200 Subject: [PATCH 03/18] Fix typo --- internal/elasticsearch/ingest/failurestorage_test.go | 4 ++-- ...op.yaml => elasticsearch-8-enable-failure-store-noop.yaml} | 0 ...e-store.yaml => elasticsearch-8-enable-failure-store.yaml} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename internal/elasticsearch/ingest/testdata/{elasticsearch-9-enable-failure-store-noop.yaml => elasticsearch-8-enable-failure-store-noop.yaml} (100%) rename internal/elasticsearch/ingest/testdata/{elasticsearch-9-enable-failure-store.yaml => elasticsearch-8-enable-failure-store.yaml} (100%) diff --git a/internal/elasticsearch/ingest/failurestorage_test.go b/internal/elasticsearch/ingest/failurestorage_test.go index d61530cf69..5e4ede4cff 100644 --- a/internal/elasticsearch/ingest/failurestorage_test.go +++ b/internal/elasticsearch/ingest/failurestorage_test.go @@ -18,7 +18,7 @@ import ( ) func TestEnableFailureStore(t *testing.T) { - client := estest.NewClient(t, "testdata/elasticsearch-9-enable-failure-store") + client := estest.NewClient(t, "testdata/elasticsearch-8-enable-failure-store") templateName := "ep-test-index-template" templateBody := []byte(`{"index_patterns": ["metrics-eptest.failurestore-*"],"data_stream": {}}`) @@ -35,7 +35,7 @@ func TestEnableFailureStore(t *testing.T) { } func TestEnableFailureStoreNothingToDo(t *testing.T) { - client := estest.NewClient(t, "testdata/elasticsearch-9-enable-failure-store-noop") + client := estest.NewClient(t, "testdata/elasticsearch-8-enable-failure-store-noop") templateName := "ep-test-index-template" templateBody := []byte(`{"index_patterns": ["metrics-eptest.failurestore-*"],"data_stream": {"failure_store":true}}`) diff --git a/internal/elasticsearch/ingest/testdata/elasticsearch-9-enable-failure-store-noop.yaml b/internal/elasticsearch/ingest/testdata/elasticsearch-8-enable-failure-store-noop.yaml similarity index 100% rename from internal/elasticsearch/ingest/testdata/elasticsearch-9-enable-failure-store-noop.yaml rename to internal/elasticsearch/ingest/testdata/elasticsearch-8-enable-failure-store-noop.yaml diff --git a/internal/elasticsearch/ingest/testdata/elasticsearch-9-enable-failure-store.yaml b/internal/elasticsearch/ingest/testdata/elasticsearch-8-enable-failure-store.yaml similarity index 100% rename from internal/elasticsearch/ingest/testdata/elasticsearch-9-enable-failure-store.yaml rename to internal/elasticsearch/ingest/testdata/elasticsearch-8-enable-failure-store.yaml From 63b883c31d1eb03d217e09765a033ce1d20f5c9d Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Mon, 15 Jul 2024 12:16:08 +0200 Subject: [PATCH 04/18] Enable and check failure store on system tests --- cmd/testrunner.go | 12 ++ internal/dump/indextemplates.go | 115 +---------------- internal/elasticsearch/ingest/packages.go | 125 +++++++++++++++++++ internal/testrunner/runners/system/runner.go | 39 ++++++ internal/testrunner/runners/system/tester.go | 71 ++++++++++- 5 files changed, 246 insertions(+), 116 deletions(-) create mode 100644 internal/elasticsearch/ingest/packages.go diff --git a/cmd/testrunner.go b/cmd/testrunner.go index 057d9b52cc..6701ee9349 100644 --- a/cmd/testrunner.go +++ b/cmd/testrunner.go @@ -13,6 +13,7 @@ import ( "sort" "strings" + "github.com/Masterminds/semver/v3" "github.com/spf13/cobra" "github.com/elastic/elastic-package/internal/cobraext" @@ -534,6 +535,15 @@ func testRunnerSystemCommandAction(cmd *cobra.Command, args []string) error { if err != nil { return fmt.Errorf("can't create Kibana client: %w", err) } + versionInfo, err := kibanaClient.Version() + if err != nil { + return fmt.Errorf("can't get version info from Kibana client: %w", err) + } + stackVersion, err := semver.NewVersion(versionInfo.Number) + if err != nil { + return fmt.Errorf("can't parse Kibana version %q: %w", versionInfo.Number, err) + } + checkFailureStore := !stackVersion.LessThan(semver.MustParse("8.15.0")) esClient, err := stack.NewElasticsearchClientFromProfile(profile) if err != nil { @@ -565,6 +575,7 @@ func testRunnerSystemCommandAction(cmd *cobra.Command, args []string) error { PackageRootPath: packageRootPath, KibanaClient: kibanaClient, API: esClient.API, + ESClient: esClient, ConfigFilePath: configFileFlag, RunSetup: runSetup, RunTearDown: runTearDown, @@ -577,6 +588,7 @@ func testRunnerSystemCommandAction(cmd *cobra.Command, args []string) error { GlobalTestConfig: globalTestConfig.System, WithCoverage: testCoverage, CoverageType: testCoverageFormat, + CheckFailureStore: checkFailureStore, }) logger.Debugf("Running suite...") diff --git a/internal/dump/indextemplates.go b/internal/dump/indextemplates.go index 7fc50d3cf2..e1cba11b4e 100644 --- a/internal/dump/indextemplates.go +++ b/internal/dump/indextemplates.go @@ -6,119 +6,14 @@ package dump import ( "context" - "encoding/json" - "fmt" - "io" - "net/http" - "slices" "github.com/elastic/elastic-package/internal/elasticsearch" + "github.com/elastic/elastic-package/internal/elasticsearch/ingest" ) -// IndexTemplate contains information related to an index template for exporting purpouses. -// It contains a partially parsed index template and the original JSON from the response. -type IndexTemplate struct { - TemplateName string `json:"name"` - IndexTemplate struct { - Meta struct { - ManagedBy string `json:"managed_by"` - Managed bool `json:"managed"` - Package struct { - Name string `json:"name"` - } `json:"package"` - } `json:"_meta"` - ComposedOf []string `json:"composed_of"` - Template struct { - Settings TemplateSettings `json:"settings"` - } `json:"template"` - } `json:"index_template"` +type IndexTemplate = ingest.IndexTemplate +type TemplateSettings = ingest.TemplateSettings - raw json.RawMessage -} - -// TemplateSettings are common settings to all kinds of templates. -type TemplateSettings struct { - Index struct { - DefaultPipeline string `json:"default_pipeline"` - FinalPipeline string `json:"final_pipeline"` - Lifecycle struct { - Name string `json:"name"` - } `json:"lifecycle"` - } `json:"index"` -} - -// Name returns the name of the index template. -func (t IndexTemplate) Name() string { - return t.TemplateName -} - -// JSON returns the JSON representation of the index template. -func (t IndexTemplate) JSON() []byte { - return []byte(t.raw) -} - -// TemplateSettings returns the template settings of this template. -func (t IndexTemplate) TemplateSettings() TemplateSettings { - return t.IndexTemplate.Template.Settings -} - -type getIndexTemplateResponse struct { - IndexTemplates []json.RawMessage `json:"index_templates"` -} - -func getIndexTemplatesForPackage(ctx context.Context, api *elasticsearch.API, packageName string) ([]IndexTemplate, error) { - resp, err := api.Indices.GetIndexTemplate( - api.Indices.GetIndexTemplate.WithContext(ctx), - - // Wildcard may be too wide, we will double check below if it is a managed template. - api.Indices.GetIndexTemplate.WithName(fmt.Sprintf("*-%s.*", packageName)), - ) - if err != nil { - return nil, fmt.Errorf("failed to get index templates: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode == http.StatusNotFound { - // Some packages don't have index templates. - return nil, nil - } - if resp.IsError() { - return nil, fmt.Errorf("failed to get index templates: %s", resp.String()) - } - - d, err := io.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("failed to read response body: %w", err) - } - - var templateResponse getIndexTemplateResponse - err = json.Unmarshal(d, &templateResponse) - if err != nil { - return nil, fmt.Errorf("failed to decode response: %w", err) - } - - var indexTemplates []IndexTemplate - for _, indexTemplateRaw := range templateResponse.IndexTemplates { - var indexTemplate IndexTemplate - err = json.Unmarshal(indexTemplateRaw, &indexTemplate) - if err != nil { - return nil, fmt.Errorf("failed to parse index template: %w", err) - } - indexTemplate.raw = indexTemplateRaw - - meta := indexTemplate.IndexTemplate.Meta - if meta.Package.Name != packageName || !managedByFleet(meta.ManagedBy) { - // This is not the droid you are looking for. - continue - } - - indexTemplates = append(indexTemplates, indexTemplate) - } - - return indexTemplates, nil -} - -func managedByFleet(managedBy string) bool { - var managers = []string{"ingest-manager", "fleet"} - return slices.Contains(managers, managedBy) +func getIndexTemplatesForPackage(ctx context.Context, api *elasticsearch.API, packageName string) ([]ingest.IndexTemplate, error) { + return ingest.GetIndexTemplatesForPackage(ctx, api, packageName) } diff --git a/internal/elasticsearch/ingest/packages.go b/internal/elasticsearch/ingest/packages.go new file mode 100644 index 0000000000..0989e4bec6 --- /dev/null +++ b/internal/elasticsearch/ingest/packages.go @@ -0,0 +1,125 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package ingest + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "slices" + + "github.com/elastic/elastic-package/internal/elasticsearch" +) + +// IndexTemplate contains information related to an index template for exporting purpouses. +// It contains a partially parsed index template and the original JSON from the response. +type IndexTemplate struct { + TemplateName string `json:"name"` + IndexTemplate struct { + Meta struct { + ManagedBy string `json:"managed_by"` + Managed bool `json:"managed"` + Package struct { + Name string `json:"name"` + } `json:"package"` + } `json:"_meta"` + ComposedOf []string `json:"composed_of"` + Template struct { + Settings TemplateSettings `json:"settings"` + } `json:"template"` + } `json:"index_template"` + + raw json.RawMessage +} + +// TemplateSettings are common settings to all kinds of templates. +type TemplateSettings struct { + Index struct { + DefaultPipeline string `json:"default_pipeline"` + FinalPipeline string `json:"final_pipeline"` + Lifecycle struct { + Name string `json:"name"` + } `json:"lifecycle"` + } `json:"index"` +} + +// Name returns the name of the index template. +func (t IndexTemplate) Name() string { + return t.TemplateName +} + +// JSON returns the JSON representation of the index template. +func (t IndexTemplate) JSON() []byte { + return []byte(t.raw) +} + +// TemplateSettings returns the template settings of this template. +func (t IndexTemplate) TemplateSettings() TemplateSettings { + return t.IndexTemplate.Template.Settings +} + +type getIndexTemplateResponse struct { + IndexTemplates []json.RawMessage `json:"index_templates"` +} + +// GetIndexTemplatesForPackage gets the index templates installed for a package. +func GetIndexTemplatesForPackage(ctx context.Context, api *elasticsearch.API, packageName string) ([]IndexTemplate, error) { + resp, err := api.Indices.GetIndexTemplate( + api.Indices.GetIndexTemplate.WithContext(ctx), + + // Wildcard may be too wide, we will double check below if it is a managed template. + api.Indices.GetIndexTemplate.WithName(fmt.Sprintf("*-%s.*", packageName)), + ) + if err != nil { + return nil, fmt.Errorf("failed to get index templates: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode == http.StatusNotFound { + // Some packages don't have index templates. + return nil, nil + } + if resp.IsError() { + return nil, fmt.Errorf("failed to get index templates: %s", resp.String()) + } + + d, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("failed to read response body: %w", err) + } + + var templateResponse getIndexTemplateResponse + err = json.Unmarshal(d, &templateResponse) + if err != nil { + return nil, fmt.Errorf("failed to decode response: %w", err) + } + + var indexTemplates []IndexTemplate + for _, indexTemplateRaw := range templateResponse.IndexTemplates { + var indexTemplate IndexTemplate + err = json.Unmarshal(indexTemplateRaw, &indexTemplate) + if err != nil { + return nil, fmt.Errorf("failed to parse index template: %w", err) + } + indexTemplate.raw = indexTemplateRaw + + meta := indexTemplate.IndexTemplate.Meta + if meta.Package.Name != packageName || !managedByFleet(meta.ManagedBy) { + // This is not the droid you are looking for. + continue + } + + indexTemplates = append(indexTemplates, indexTemplate) + } + + return indexTemplates, nil +} + +func managedByFleet(managedBy string) bool { + var managers = []string{"ingest-manager", "fleet"} + return slices.Contains(managers, managedBy) +} diff --git a/internal/testrunner/runners/system/runner.go b/internal/testrunner/runners/system/runner.go index 1bea6ea80f..31e515c0a0 100644 --- a/internal/testrunner/runners/system/runner.go +++ b/internal/testrunner/runners/system/runner.go @@ -14,6 +14,7 @@ import ( "time" "github.com/elastic/elastic-package/internal/elasticsearch" + "github.com/elastic/elastic-package/internal/elasticsearch/ingest" "github.com/elastic/elastic-package/internal/kibana" "github.com/elastic/elastic-package/internal/logger" "github.com/elastic/elastic-package/internal/packages" @@ -28,12 +29,14 @@ type runner struct { packageRootPath string kibanaClient *kibana.Client esAPI *elasticsearch.API + esClient *elasticsearch.Client dataStreams []string serviceVariant string globalTestConfig testrunner.GlobalRunnerTestConfig failOnMissingTests bool + checkFailureStore bool deferCleanup time.Duration generateTestResult bool withCoverage bool @@ -57,6 +60,9 @@ type SystemTestRunnerOptions struct { KibanaClient *kibana.Client API *elasticsearch.API + // FIXME: Keeping Elasticsearch client to be able to do low-level requests for parameters not supported yet by the API. + ESClient *elasticsearch.Client + DataStreams []string ServiceVariant string @@ -68,6 +74,7 @@ type SystemTestRunnerOptions struct { GlobalTestConfig testrunner.GlobalRunnerTestConfig FailOnMissingTests bool + CheckFailureStore bool GenerateTestResult bool DeferCleanup time.Duration WithCoverage bool @@ -79,6 +86,7 @@ func NewSystemTestRunner(options SystemTestRunnerOptions) *runner { packageRootPath: options.PackageRootPath, kibanaClient: options.KibanaClient, esAPI: options.API, + esClient: options.ESClient, profile: options.Profile, dataStreams: options.DataStreams, serviceVariant: options.ServiceVariant, @@ -87,6 +95,7 @@ func NewSystemTestRunner(options SystemTestRunnerOptions) *runner { runTestsOnly: options.RunTestsOnly, runTearDown: options.RunTearDown, failOnMissingTests: options.FailOnMissingTests, + checkFailureStore: options.CheckFailureStore, generateTestResult: options.GenerateTestResult, deferCleanup: options.DeferCleanup, globalTestConfig: options.GlobalTestConfig, @@ -120,6 +129,34 @@ func (r *runner) SetupRunner(ctx context.Context) error { return fmt.Errorf("can't install the package: %w", err) } + if r.checkFailureStore { + err := r.setupFailureStore(ctx) + if err != nil { + return fmt.Errorf("can't enable the failure store: %w", err) + } + } + + return nil +} + +func (r *runner) setupFailureStore(ctx context.Context) error { + manifest, err := packages.ReadPackageManifestFromPackageRoot(r.packageRootPath) + if err != nil { + return fmt.Errorf("failed to read package manifest: %w", err) + } + + indexTemplates, err := ingest.GetIndexTemplatesForPackage(ctx, r.esAPI, manifest.Name) + if err != nil { + return fmt.Errorf("failed to get index templates for package %s: %w", manifest.Name, err) + } + + for _, template := range indexTemplates { + err := ingest.EnableFailureStore(ctx, r.esAPI, template.Name(), true) + if err != nil { + return fmt.Errorf("failed to enable failure store for index template %s: %w", template.Name(), err) + } + } + return nil } @@ -245,6 +282,7 @@ func (r *runner) GetTests(ctx context.Context) ([]testrunner.Tester, error) { PackageRootPath: r.packageRootPath, KibanaClient: r.kibanaClient, API: r.esAPI, + ESClient: r.esClient, TestFolder: t, ServiceVariant: variant, GenerateTestResult: r.generateTestResult, @@ -256,6 +294,7 @@ func (r *runner) GetTests(ctx context.Context) ([]testrunner.Tester, error) { GlobalTestConfig: r.globalTestConfig, WithCoverage: r.withCoverage, CoverageType: r.coverageType, + CheckFailureStore: r.checkFailureStore, }) if err != nil { return nil, fmt.Errorf( diff --git a/internal/testrunner/runners/system/tester.go b/internal/testrunner/runners/system/tester.go index 74d0e4c0db..38dbd1a38b 100644 --- a/internal/testrunner/runners/system/tester.go +++ b/internal/testrunner/runners/system/tester.go @@ -131,6 +131,7 @@ type tester struct { packageRootPath string generateTestResult bool esAPI *elasticsearch.API + esClient *elasticsearch.Client kibanaClient *kibana.Client runIndependentElasticAgent bool @@ -153,6 +154,7 @@ type tester struct { dataStreamManifest *packages.DataStreamManifest withCoverage bool coverageType string + checkFailureStore bool serviceStateFilePath string @@ -176,12 +178,16 @@ type SystemTesterOptions struct { API *elasticsearch.API KibanaClient *kibana.Client - DeferCleanup time.Duration - ServiceVariant string - ConfigFileName string - GlobalTestConfig testrunner.GlobalRunnerTestConfig - WithCoverage bool - CoverageType string + // FIXME: Keeping Elasticsearch client to be able to do low-level requests for parameters not supported yet by the API. + ESClient *elasticsearch.Client + + DeferCleanup time.Duration + ServiceVariant string + ConfigFileName string + GlobalTestConfig testrunner.GlobalRunnerTestConfig + WithCoverage bool + CoverageType string + CheckFailureStore bool RunSetup bool RunTearDown bool @@ -195,6 +201,7 @@ func NewSystemTester(options SystemTesterOptions) (*tester, error) { packageRootPath: options.PackageRootPath, generateTestResult: options.GenerateTestResult, esAPI: options.API, + esClient: options.ESClient, kibanaClient: options.KibanaClient, deferCleanup: options.DeferCleanup, serviceVariant: options.ServiceVariant, @@ -205,6 +212,7 @@ func NewSystemTester(options SystemTesterOptions) (*tester, error) { globalTestConfig: options.GlobalTestConfig, withCoverage: options.WithCoverage, coverageType: options.CoverageType, + checkFailureStore: options.CheckFailureStore, runIndependentElasticAgent: true, } r.resourcesManager = resources.NewManager() @@ -755,12 +763,50 @@ func (r *tester) getDocs(ctx context.Context, dataStream string) (*hits, error) return &hits, nil } +func (r *tester) getFailureStoreDocs(ctx context.Context, dataStream string) ([]common.MapStr, error) { + // FIXME: Using the low-level transport till support the failure store. + request, err := http.NewRequest(http.MethodPost, fmt.Sprintf("/%s/_search?failure_store=only", dataStream), nil) + if err != nil { + return nil, fmt.Errorf("failed to create search request: %w", err) + } + resp, err := r.esClient.Transport.Perform(request) + if err != nil { + return nil, fmt.Errorf("failed to perform search request: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode >= 400 { + return nil, fmt.Errorf("search request returned status code %d", resp.StatusCode) + } + + var results struct { + Hits struct { + Hits []struct { + Source common.MapStr `json:"_source"` + Fields common.MapStr `json:"fields"` + } `json:"hits"` + } `json:"hits"` + } + err = json.NewDecoder(resp.Body).Decode(&results) + if err != nil { + return nil, fmt.Errorf("failed to decode search response: %w", err) + } + + var docs []common.MapStr + for _, hit := range results.Hits.Hits { + docs = append(docs, hit.Source) + } + + return docs, nil +} + type scenarioTest struct { dataStream string policyTemplateName string kibanaDataStream kibana.PackageDataStream syntheticEnabled bool docs []common.MapStr + failureStore []common.MapStr ignoredFields []string degradedDocs []common.MapStr agent agentdeployer.DeployedAgent @@ -1132,6 +1178,14 @@ func (r *tester) prepareScenario(ctx context.Context, config *testConfig, svcInf scenario.docs = hits.getDocs(scenario.syntheticEnabled) scenario.ignoredFields = hits.IgnoredFields scenario.degradedDocs = hits.DegradedDocs + if r.checkFailureStore { + logger.Debugf("Checking failure store for data stream %s", scenario.dataStream) + scenario.failureStore, err = r.getFailureStoreDocs(ctx, scenario.dataStream) + if err != nil { + return nil, fmt.Errorf("failed to get documents from the failure store for data stream %s: %w", scenario.dataStream, err) + } + logger.Debugf("Found %d docs in failure store for data stream %s", len(scenario.failureStore), scenario.dataStream) + } if r.runSetup { opts := scenarioStateOpts{ @@ -1269,6 +1323,11 @@ func (r *tester) createServiceStateDir() error { } func (r *tester) validateTestScenario(ctx context.Context, result *testrunner.ResultComposer, scenario *scenarioTest, config *testConfig) ([]testrunner.TestResult, error) { + if r.checkFailureStore && len(scenario.failureStore) > 0 { + // TODO: Report failures found. + return result.WithErrorf("there are %d documents in the failure store", len(scenario.failureStore)) + } + // Validate fields in docs // when reroute processors are used, expectedDatasets should be set depends on the processor config var expectedDatasets []string From 22fa9b7f2a36d33a102c275b5dbe337a7773e5e1 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Mon, 15 Jul 2024 13:38:36 +0200 Subject: [PATCH 05/18] Interrupt loop waiting docs if there are docs in the failure store --- internal/testrunner/runners/system/tester.go | 42 +++++++++++++++++--- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/internal/testrunner/runners/system/tester.go b/internal/testrunner/runners/system/tester.go index 38dbd1a38b..60e6be989e 100644 --- a/internal/testrunner/runners/system/tester.go +++ b/internal/testrunner/runners/system/tester.go @@ -775,7 +775,15 @@ func (r *tester) getFailureStoreDocs(ctx context.Context, dataStream string) ([] } defer resp.Body.Close() - if resp.StatusCode >= 400 { + switch { + case resp.StatusCode == http.StatusNotFound: + // Can happen if the data stream hasn't been created yet. + return nil, nil + case resp.StatusCode == http.StatusServiceUnavailable: + // Index is being created, but no shards are available yet. + // See https://github.com/elastic/elasticsearch/issues/65846 + return nil, nil + case resp.StatusCode >= 400: return nil, fmt.Errorf("search request returned status code %d", resp.StatusCode) } @@ -1133,6 +1141,18 @@ func (r *tester) prepareScenario(ctx context.Context, config *testConfig, svcInf return false, err } + if r.checkFailureStore { + failureStore, err := r.getFailureStoreDocs(ctx, scenario.dataStream) + if err != nil { + return false, fmt.Errorf("failed to check failure store: %w", err) + } + if n := len(failureStore); n > 0 { + // Interrupt loop earlier if there are failures in the document store. + logger.Debugf("Found %d hits in the failure store for %s", len(failureStore), scenario.dataStream) + return true, nil + } + } + if config.Assert.HitCount > 0 { if hits.size() < config.Assert.HitCount { return false, nil @@ -1325,7 +1345,15 @@ func (r *tester) createServiceStateDir() error { func (r *tester) validateTestScenario(ctx context.Context, result *testrunner.ResultComposer, scenario *scenarioTest, config *testConfig) ([]testrunner.TestResult, error) { if r.checkFailureStore && len(scenario.failureStore) > 0 { // TODO: Report failures found. - return result.WithErrorf("there are %d documents in the failure store", len(scenario.failureStore)) + for _, doc := range scenario.failureStore { + d, err := json.MarshalIndent(doc, "", " ") + if err != nil { + return result.WithErrorf("failed to encode document from the failure store: %w", err) + } + logger.Debugf("Document found in the failure store: %s", string(d)) + } + results, _ := result.WithErrorf("there are %d documents in the failure store", len(scenario.failureStore)) + return results, nil } // Validate fields in docs @@ -1375,7 +1403,7 @@ func (r *tester) validateTestScenario(ctx context.Context, result *testrunner.Re fields.WithDisableNormalization(scenario.syntheticEnabled), ) if err != nil { - return result.WithError(fmt.Errorf("creating fields validator for data stream failed (path: %s): %w", r.dataStreamPath, err)) + return result.WithErrorf("creating fields validator for data stream failed (path: %s): %w", r.dataStreamPath, err) } if err := validateFields(scenario.docs, fieldsValidator, scenario.dataStream); err != nil { return result.WithError(err) @@ -1390,13 +1418,14 @@ func (r *tester) validateTestScenario(ctx context.Context, result *testrunner.Re if scenario.syntheticEnabled { docs, err = fieldsValidator.SanitizeSyntheticSourceDocs(scenario.docs) if err != nil { - return result.WithError(fmt.Errorf("failed to sanitize synthetic source docs: %w", err)) + results, _ := result.WithErrorf("failed to sanitize synthetic source docs: %w", err) + return results, nil } } specVersion, err := semver.NewVersion(r.pkgManifest.SpecVersion) if err != nil { - return result.WithError(fmt.Errorf("failed to parse format version %q: %w", r.pkgManifest.SpecVersion, err)) + return result.WithErrorf("failed to parse format version %q: %w", r.pkgManifest.SpecVersion, err) } // Write sample events file from first doc, if requested @@ -1411,7 +1440,8 @@ func (r *tester) validateTestScenario(ctx context.Context, result *testrunner.Re // Check transforms if present if err := r.checkTransforms(ctx, config, r.pkgManifest, scenario.kibanaDataStream, scenario.dataStream); err != nil { - return result.WithError(err) + results, _ := result.WithError(err) + return results, nil } if scenario.agent != nil { From 574a8a2c2b7f9e77d4397cdd8af29cb3b0785df3 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Mon, 15 Jul 2024 13:53:32 +0200 Subject: [PATCH 06/18] Include error message in results --- internal/testrunner/runners/system/tester.go | 26 +++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/internal/testrunner/runners/system/tester.go b/internal/testrunner/runners/system/tester.go index 60e6be989e..9f53cd73d5 100644 --- a/internal/testrunner/runners/system/tester.go +++ b/internal/testrunner/runners/system/tester.go @@ -763,7 +763,7 @@ func (r *tester) getDocs(ctx context.Context, dataStream string) (*hits, error) return &hits, nil } -func (r *tester) getFailureStoreDocs(ctx context.Context, dataStream string) ([]common.MapStr, error) { +func (r *tester) getFailureStoreDocs(ctx context.Context, dataStream string) ([]failureStoreDocument, error) { // FIXME: Using the low-level transport till support the failure store. request, err := http.NewRequest(http.MethodPost, fmt.Sprintf("/%s/_search?failure_store=only", dataStream), nil) if err != nil { @@ -790,8 +790,8 @@ func (r *tester) getFailureStoreDocs(ctx context.Context, dataStream string) ([] var results struct { Hits struct { Hits []struct { - Source common.MapStr `json:"_source"` - Fields common.MapStr `json:"fields"` + Source failureStoreDocument `json:"_source"` + Fields common.MapStr `json:"fields"` } `json:"hits"` } `json:"hits"` } @@ -800,7 +800,7 @@ func (r *tester) getFailureStoreDocs(ctx context.Context, dataStream string) ([] return nil, fmt.Errorf("failed to decode search response: %w", err) } - var docs []common.MapStr + var docs []failureStoreDocument for _, hit := range results.Hits.Hits { docs = append(docs, hit.Source) } @@ -814,13 +814,19 @@ type scenarioTest struct { kibanaDataStream kibana.PackageDataStream syntheticEnabled bool docs []common.MapStr - failureStore []common.MapStr + failureStore []failureStoreDocument ignoredFields []string degradedDocs []common.MapStr agent agentdeployer.DeployedAgent startTestTime time.Time } +type failureStoreDocument struct { + Error struct { + Message string `json:"message"` + } `json:"error"` +} + func (r *tester) deleteDataStream(ctx context.Context, dataStream string) error { resp, err := r.esAPI.Indices.DeleteDataStream([]string{dataStream}, r.esAPI.Indices.DeleteDataStream.WithContext(ctx), @@ -1344,15 +1350,11 @@ func (r *tester) createServiceStateDir() error { func (r *tester) validateTestScenario(ctx context.Context, result *testrunner.ResultComposer, scenario *scenarioTest, config *testConfig) ([]testrunner.TestResult, error) { if r.checkFailureStore && len(scenario.failureStore) > 0 { - // TODO: Report failures found. + var errorMessages []string for _, doc := range scenario.failureStore { - d, err := json.MarshalIndent(doc, "", " ") - if err != nil { - return result.WithErrorf("failed to encode document from the failure store: %w", err) - } - logger.Debugf("Document found in the failure store: %s", string(d)) + errorMessages = append(errorMessages, doc.Error.Message) } - results, _ := result.WithErrorf("there are %d documents in the failure store", len(scenario.failureStore)) + results, _ := result.WithErrorf("there are %d documents in the failure store with errors: %s", len(scenario.failureStore), strings.Join(errorMessages, ", ")) return results, nil } From 12548f252acdc41caf7f891998bc4409b82b2e8f Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Tue, 16 Jul 2024 11:37:40 +0200 Subject: [PATCH 07/18] Ignore errors in the failure store caused by document conflicts --- internal/testrunner/runners/system/tester.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/internal/testrunner/runners/system/tester.go b/internal/testrunner/runners/system/tester.go index 9f53cd73d5..6e6d0996c9 100644 --- a/internal/testrunner/runners/system/tester.go +++ b/internal/testrunner/runners/system/tester.go @@ -764,8 +764,10 @@ func (r *tester) getDocs(ctx context.Context, dataStream string) (*hits, error) } func (r *tester) getFailureStoreDocs(ctx context.Context, dataStream string) ([]failureStoreDocument, error) { - // FIXME: Using the low-level transport till support the failure store. - request, err := http.NewRequest(http.MethodPost, fmt.Sprintf("/%s/_search?failure_store=only", dataStream), nil) + // FIXME: Using the low-level transport till the API SDK supports the failure store. + // Ignoring failures with error.type version_conflict_engine_exception because there are packages which + // explicitly set the _id with the fingerprint processor to avoid duplicates. + request, err := http.NewRequest(http.MethodPost, fmt.Sprintf("/%s/_search?failure_store=only&q=error.type:-version_conflict_engine_exception", dataStream), nil) if err != nil { return nil, fmt.Errorf("failed to create search request: %w", err) } @@ -823,6 +825,7 @@ type scenarioTest struct { type failureStoreDocument struct { Error struct { + Type string `json:"type"` Message string `json:"message"` } `json:"error"` } From e14ac740fcf8c829ea15fdd7bc8cb44d09cb6d74 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Tue, 16 Jul 2024 12:18:36 +0200 Subject: [PATCH 08/18] Filter out errors about conflicting documents --- internal/testrunner/runners/system/tester.go | 50 ++++++++++++++++---- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/internal/testrunner/runners/system/tester.go b/internal/testrunner/runners/system/tester.go index 6e6d0996c9..e70c4f42ec 100644 --- a/internal/testrunner/runners/system/tester.go +++ b/internal/testrunner/runners/system/tester.go @@ -5,6 +5,7 @@ package system import ( + "bytes" "context" "encoding/json" "errors" @@ -764,13 +765,30 @@ func (r *tester) getDocs(ctx context.Context, dataStream string) (*hits, error) } func (r *tester) getFailureStoreDocs(ctx context.Context, dataStream string) ([]failureStoreDocument, error) { + query := map[string]any{ + "query": map[string]any{ + "bool": map[string]any{ + // Ignoring failures with error.type version_conflict_engine_exception because there are packages which + // explicitly set the _id with the fingerprint processor to avoid duplicates. + "must_not": map[string]any{ + "term": map[string]any{ + "error.type": "version_conflict_engine_exception", + }, + }, + }, + }, + } + body, err := json.Marshal(query) + if err != nil { + return nil, fmt.Errorf("failed to encode search query: %w", err) + } // FIXME: Using the low-level transport till the API SDK supports the failure store. - // Ignoring failures with error.type version_conflict_engine_exception because there are packages which - // explicitly set the _id with the fingerprint processor to avoid duplicates. - request, err := http.NewRequest(http.MethodPost, fmt.Sprintf("/%s/_search?failure_store=only&q=error.type:-version_conflict_engine_exception", dataStream), nil) + request, err := http.NewRequest(http.MethodPost, fmt.Sprintf("/%s/_search?failure_store=only", dataStream), bytes.NewReader(body)) if err != nil { return nil, fmt.Errorf("failed to create search request: %w", err) } + request.Header.Set("Content-Type", "application/json") + resp, err := r.esClient.Transport.Perform(request) if err != nil { return nil, fmt.Errorf("failed to perform search request: %w", err) @@ -1352,13 +1370,8 @@ func (r *tester) createServiceStateDir() error { } func (r *tester) validateTestScenario(ctx context.Context, result *testrunner.ResultComposer, scenario *scenarioTest, config *testConfig) ([]testrunner.TestResult, error) { - if r.checkFailureStore && len(scenario.failureStore) > 0 { - var errorMessages []string - for _, doc := range scenario.failureStore { - errorMessages = append(errorMessages, doc.Error.Message) - } - results, _ := result.WithErrorf("there are %d documents in the failure store with errors: %s", len(scenario.failureStore), strings.Join(errorMessages, ", ")) - return results, nil + if err := validateFailureStore(scenario.failureStore); err != nil { + return result.WithError(err) } // Validate fields in docs @@ -1950,6 +1963,23 @@ func writeSampleEvent(path string, doc common.MapStr, specVersion semver.Version return nil } +func validateFailureStore(failureStore []failureStoreDocument) error { + var multiErr multierror.Error + for _, doc := range failureStore { + multiErr = append(multiErr, errors.New(doc.Error.Message)) + } + + if len(multiErr) > 0 { + multiErr = multiErr.Unique() + return testrunner.ErrTestCaseFailed{ + Reason: "one or more documents found in the failure store", + Details: multiErr.Error(), + } + } + + return nil +} + func validateFields(docs []common.MapStr, fieldsValidator *fields.Validator, dataStream string) error { var multiErr multierror.Error for _, doc := range docs { From 08332d494d64103a6aafeff7958f401b1614bf16 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Thu, 18 Jul 2024 11:10:29 +0200 Subject: [PATCH 09/18] Add test case --- .../failure_store.expected_errors | 1 + .../false_positives/failure_store/LICENSE.txt | 202 ++++++++++++++++++ .../failure_store/changelog.yml | 6 + .../_dev/deploy/docker/docker-compose.yml | 8 + .../test/_dev/deploy/docker/logs/logs.log | 8 + .../_dev/test/system/test-fail-config.yml | 6 + .../test/agent/stream/stream.yml.hbs | 7 + .../elasticsearch/ingest_pipeline/default.yml | 9 + .../data_stream/test/fields/base-fields.yml | 14 ++ .../data_stream/test/manifest.yml | 17 ++ .../failure_store/docs/README.md | 84 ++++++++ .../failure_store/img/sample-logo.svg | 1 + .../failure_store/img/sample-screenshot.png | Bin 0 -> 18849 bytes .../failure_store/manifest.yml | 36 ++++ 14 files changed, 399 insertions(+) create mode 100644 test/packages/false_positives/failure_store.expected_errors create mode 100644 test/packages/false_positives/failure_store/LICENSE.txt create mode 100644 test/packages/false_positives/failure_store/changelog.yml create mode 100644 test/packages/false_positives/failure_store/data_stream/test/_dev/deploy/docker/docker-compose.yml create mode 100644 test/packages/false_positives/failure_store/data_stream/test/_dev/deploy/docker/logs/logs.log create mode 100644 test/packages/false_positives/failure_store/data_stream/test/_dev/test/system/test-fail-config.yml create mode 100644 test/packages/false_positives/failure_store/data_stream/test/agent/stream/stream.yml.hbs create mode 100644 test/packages/false_positives/failure_store/data_stream/test/elasticsearch/ingest_pipeline/default.yml create mode 100644 test/packages/false_positives/failure_store/data_stream/test/fields/base-fields.yml create mode 100644 test/packages/false_positives/failure_store/data_stream/test/manifest.yml create mode 100644 test/packages/false_positives/failure_store/docs/README.md create mode 100644 test/packages/false_positives/failure_store/img/sample-logo.svg create mode 100644 test/packages/false_positives/failure_store/img/sample-screenshot.png create mode 100644 test/packages/false_positives/failure_store/manifest.yml diff --git a/test/packages/false_positives/failure_store.expected_errors b/test/packages/false_positives/failure_store.expected_errors new file mode 100644 index 0000000000..7e2f2d181c --- /dev/null +++ b/test/packages/false_positives/failure_store.expected_errors @@ -0,0 +1 @@ +test case failed: one or more documents found in the failure store:.* diff --git a/test/packages/false_positives/failure_store/LICENSE.txt b/test/packages/false_positives/failure_store/LICENSE.txt new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/test/packages/false_positives/failure_store/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/test/packages/false_positives/failure_store/changelog.yml b/test/packages/false_positives/failure_store/changelog.yml new file mode 100644 index 0000000000..bb0320a524 --- /dev/null +++ b/test/packages/false_positives/failure_store/changelog.yml @@ -0,0 +1,6 @@ +# newer versions go on top +- version: "0.0.1" + changes: + - description: Initial draft of the package + type: enhancement + link: https://github.com/elastic/integrations/pull/1 # FIXME Replace with the real PR link diff --git a/test/packages/false_positives/failure_store/data_stream/test/_dev/deploy/docker/docker-compose.yml b/test/packages/false_positives/failure_store/data_stream/test/_dev/deploy/docker/docker-compose.yml new file mode 100644 index 0000000000..ec376194ca --- /dev/null +++ b/test/packages/false_positives/failure_store/data_stream/test/_dev/deploy/docker/docker-compose.yml @@ -0,0 +1,8 @@ +version: '2.3' +services: + logs: + image: alpine + volumes: + - ./logs:/logs:ro + - ${SERVICE_LOGS_DIR}:/var/log + command: /bin/sh -c "echo \"Copying files...\"; cp /logs/* /var/log/; echo \"Done.\"; sleep 500" diff --git a/test/packages/false_positives/failure_store/data_stream/test/_dev/deploy/docker/logs/logs.log b/test/packages/false_positives/failure_store/data_stream/test/_dev/deploy/docker/logs/logs.log new file mode 100644 index 0000000000..b915715817 --- /dev/null +++ b/test/packages/false_positives/failure_store/data_stream/test/_dev/deploy/docker/logs/logs.log @@ -0,0 +1,8 @@ +1 +2 +3 +4 +five +six +seven +eight diff --git a/test/packages/false_positives/failure_store/data_stream/test/_dev/test/system/test-fail-config.yml b/test/packages/false_positives/failure_store/data_stream/test/_dev/test/system/test-fail-config.yml new file mode 100644 index 0000000000..34521f5001 --- /dev/null +++ b/test/packages/false_positives/failure_store/data_stream/test/_dev/test/system/test-fail-config.yml @@ -0,0 +1,6 @@ +input: logfile +service: logs +data_stream: + vars: + paths: + - "{{SERVICE_LOGS_DIR}}/*.log" diff --git a/test/packages/false_positives/failure_store/data_stream/test/agent/stream/stream.yml.hbs b/test/packages/false_positives/failure_store/data_stream/test/agent/stream/stream.yml.hbs new file mode 100644 index 0000000000..5845510de8 --- /dev/null +++ b/test/packages/false_positives/failure_store/data_stream/test/agent/stream/stream.yml.hbs @@ -0,0 +1,7 @@ +paths: +{{#each paths as |path i|}} + - {{path}} +{{/each}} +exclude_files: [".gz$"] +processors: + - add_locale: ~ diff --git a/test/packages/false_positives/failure_store/data_stream/test/elasticsearch/ingest_pipeline/default.yml b/test/packages/false_positives/failure_store/data_stream/test/elasticsearch/ingest_pipeline/default.yml new file mode 100644 index 0000000000..3a361c646b --- /dev/null +++ b/test/packages/false_positives/failure_store/data_stream/test/elasticsearch/ingest_pipeline/default.yml @@ -0,0 +1,9 @@ +--- +description: Pipeline for processing sample logs +processors: +- convert: + field: message + type: integer +- rename: + field: message + target_field: number diff --git a/test/packages/false_positives/failure_store/data_stream/test/fields/base-fields.yml b/test/packages/false_positives/failure_store/data_stream/test/fields/base-fields.yml new file mode 100644 index 0000000000..ea02af1f8e --- /dev/null +++ b/test/packages/false_positives/failure_store/data_stream/test/fields/base-fields.yml @@ -0,0 +1,14 @@ +- name: data_stream.type + type: constant_keyword + description: Data stream type. +- name: data_stream.dataset + type: constant_keyword + description: Data stream dataset. +- name: data_stream.namespace + type: constant_keyword + description: Data stream namespace. +- name: '@timestamp' + type: date + description: Event timestamp. +- name: number + type: long diff --git a/test/packages/false_positives/failure_store/data_stream/test/manifest.yml b/test/packages/false_positives/failure_store/data_stream/test/manifest.yml new file mode 100644 index 0000000000..d711b8ce30 --- /dev/null +++ b/test/packages/false_positives/failure_store/data_stream/test/manifest.yml @@ -0,0 +1,17 @@ +title: "Test Data Stream" +type: logs +streams: + - input: logfile + title: Sample logs + description: Collect sample logs + vars: + - name: paths + type: text + title: Paths + multi: true + default: + - /var/log/*.log +elasticsearch: + index_template: + mappings: + subobjects: false diff --git a/test/packages/false_positives/failure_store/docs/README.md b/test/packages/false_positives/failure_store/docs/README.md new file mode 100644 index 0000000000..b2478003a5 --- /dev/null +++ b/test/packages/false_positives/failure_store/docs/README.md @@ -0,0 +1,84 @@ + + + +# Failure goes to failure store + + + +## Data streams + + + + + + + + + + + +## Requirements + +You need Elasticsearch for storing and searching your data and Kibana for visualizing and managing it. +You can use our hosted Elasticsearch Service on Elastic Cloud, which is recommended, or self-manage the Elastic Stack on your own hardware. + + + +## Setup + + + +For step-by-step instructions on how to set up an integration, see the +[Getting started](https://www.elastic.co/guide/en/welcome-to-elastic/current/getting-started-observability.html) guide. + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/packages/false_positives/failure_store/img/sample-logo.svg b/test/packages/false_positives/failure_store/img/sample-logo.svg new file mode 100644 index 0000000000..6268dd88f3 --- /dev/null +++ b/test/packages/false_positives/failure_store/img/sample-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/packages/false_positives/failure_store/img/sample-screenshot.png b/test/packages/false_positives/failure_store/img/sample-screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..d7a56a3ecc078c38636698cefba33f86291dd178 GIT binary patch literal 18849 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

}qal5$moSACwfNXLXG5|3R0AtBcN` z?%yS)&>O>sqxU64U~C3&Q^>z-Zt}WuX4Wh3dKj9EO zfSbV!c3e;EOeKHQmWEw#NM4;*tw-2o@x&kKT?rsmy-F|$jw-F>WgA7?C@{O1qPg*J zf92|RTBMh&ptHADFc{T+cB?+mOj>h2HKgwkxq6w&XBxPc?>=JKvU2K9aU93@vp-R% z{5T=P$9U}AYZ5QU{3%7}YZ+ACWXw#-U zWyxU(OP#Q9-2AeGmCwcp`zWghf2hvsOjWjDQbU?U`v0&a--f1`v0Bd8HLiLmo)PKz5!A1|XVO+89 zm3h2~6yI~cpWor!_yt-?Lt>z`c0a7cJAW)#d8N8nNIf0H<+v;s4{0guDD(?T7Z<~$ zd`$vpZ_QQgFaMT0_d5&+(jwGU?M1FqUu6wjA-9z?mRM}(CmSdK;2e$Na}F-8jbhgN z9)@AIQeghf{xCC^{9P%VdYW1PP#}2BJwWt z0Hd8%st1NK5%h+)UB^mVwh{e#8TIm$xxgGo6I5;e{~VUeeMGRpM_Z%=eH5$X1}?Z5 z`|*_Vp~K&ziz45-Ih9y>EOr(Buy0&n$dbQ4$5eSr=Ti z#~7^n8dmem;$0D4+6eV7&G2D~d@ z+R#u8+nw_N%7_U_1e53P?~&10^m|ZUXrZhVp04lQLsGos%0fRDhS=@>8TOAAxK;Cy z9GZw_1pfSxD5~xoR!INI?tU0wrKDd6^Tv{jL>`Xb49kBaNPlhMaIfh_nq_)zB7NcX z05XeQKz`@BDUx7*i!V~%dc8XQ#ngBw0A2tSr(npSCrNy5Z7>48v&Zz?0{%FRElh_h zN2|?#EhJL5HQMIu6m1=ypTR?tVymHK)xQvS9ir7FzMp?CjlND39PK`od#GytVhZWp zQ1@>MTE1*Ip>hnXSWa?XbMH#708@j12yPbm`JfcqIgmJepn$5YgkJn_%5I)mr`Q(k z-a0yFR3A`houhvf&|wNpIsV{2p%MqhR@`@R(l6`}iufEgI*UxWq~26?WTpZCV{JtG zYL?&#I98fyf_;2S0?_V{=Aa4t^x%vy$pF$_Lh7W2f*~5uPvGYh;vZhMv|u+Z?2t0~ zcYPXdxbg6OS*LUjR_=jLDt)ab6;?g1IuySLG@UE;jLpt-wjLX&RlY>fnd@f&?0NyT zht5vhP^};k6`U76$%&I)iWPNxG6KPjdh`S6>g9GN@;KObQsLG zKyjfrPR0PU1B0a0=)3@9eCDl?mB9rFdlTMtTAeZv2}F*|@JWleq2+H1bt>>x!^wTk z+I)cgsZwzCMwoRpW_*!3IySTQu!`HWugAXe(Ai(a9Rsu;*0#o6torxwNMxPzEAjt` z>70Vw;HCQ?AnP`RKQ;2R8h%;LI#tx^(MO*lMWJe4_?)Q571P`kTmN#(ez21V!<6+S z@Uap+y%#8&cGgdf+E@y$dUx3g#)=#5k31Vqv0p!%L`*=-PiQAiSg-d9lKRZQDuJ-| zA96zwwomG+4}X$vR*IU=NC!vL<`rUTbf_uRJC4FS;k&HtV<=<)p(qymH)=MDV^aqK z#%sid7K|~!H`J!7hRr~Z!emxgWq6#GpQs%c#BM+scvNGz|Gi4G`;8Z~dP8)+51iB8 zw)0fazNz5(iK$LJeC_4e^8&@wT(DZ~~>SStz3P(>V8CLNlZqgv=2K-|Lu~si@XFwMN>QE^k zVS2U_A?Q$?M`NkU}^!M8m%O&T=kW>dG}1s2I~hxp9Y=a=1XX-(fB5) zej3`e5Et~R^r%?CZK0)UZsF_+tSOGIBMdrtMf#oJjGF9U`*P8t>i*TWed$Z2WNUZ* z_1Qw4Yr+Q0@bD?hD0P-^v}?FpPBg~zz5~g@J#J76C695|P>1l;OS8%~hZh5&-9Ji# z50%&56ZK4FC9}{jHL0!=qo9Yd(GGHCEX2|-F(f}q6@NMT4P3rQd{Q!=bz-8N(Z^!N;;ZzAWRf@C?X>mG=_NgyQX_?Jv$m(9$W>P;+e}O|&w&DjbsJPdWp0A2$yLr*!BY73Z z5d*BCaTI)w=sTlofc>n}@v_tSXIK?8(g`G_06u>SD*fOZJ~visq3lBVS2+cf-r$UQ zZ(8A0g&5M$IV7w5nqL(m$VS0X?=yy-e6>S>Ca3wZNT)b{GF39_gJdONflqc-j$b~o z2l@@h{$KVfC)V?#We*)@xYC;L^<@cHo>8axRMbSzw|eYTl|8pkabsQJ(3`z{>5H}c z`psz_Y6t)hvzL^=}P#++XUl6v`-j)SuXd6BynjNZ!&c2hnyE&4*K$nXn31Zk)cm+lx;> zya{T?{MRtSu?^3Y9bS&O$*mW^vRUpv!J3Tz12?3&Y62b_oiZ$24O(75Z)JWb+Rj)ACbK`f<&tSwtT$|Sy z$41kRPiM-jnPY9PKrLyI`pHm6LusMsrO*HpmE){Kp1^u2t%6nW^;GB|!4k!Ik8oav zjM?DBKh9G@W0gEwiU-M}0B)}olvoM71RccgiZBCs)L?q_GX&JDhegx4k2&cNatr5w zU)1#2USb8&`etO5Vk z?0}K+*2*@a5yt*X{qg0@8jEz~jcylVj>-042p1PBnabI#xUiCRD!ouw3?u-wwsqwF z8(@m8-Lk7q@v154g6yvx_tRDa>}oqpVda)wfI9(;ZVGt1v^{<|X?vC_(i@IJC+2I_lusrT=$h zF1lPc*Neb`;Xgrdf`p$w)~MzQW0M3_FYRKu{2$VU82J^B=X1#^<&P$_`=S$Ey04WU zTxG;hrFNLhWC*p+sH3x=JVcBJ9*7>eO20)n671SxQhZQlHMRP8FyO}yai~OTsbms0 zQ3b$C1Cn!>jMHDq{VX1ab^~_Q!z+f75+_AuwiN0*wA_#M#0|rU{+NlB%>Y+TNT0Gj z`3^LKMSJjz2(?lwg~ixDl_5%rzzZ}o_6Fj9e)T7gpH4=BgT1zmwJpC@g(f%&0`}8B z%7Y&qlP3aFmI#nmT`|R3+Lwzp+PLXt|5g%vlY_$fvse7zjus0D0fA##r+i4G4K-2Y zC#H95NGoYfWP#ZF_v$^Li{PZpm}fc&)aL?5doPcb835Cr6`T+EzzcEvLtmXcbAb<^ zw!_Zgk6Az7YA@*vb)(G{_W-B|zrf76z^`X%jOgqIIaqi~5nUup3vugzzg&rA^w(zR z+qCzvIV~nGR=47pDOcNTzuBw#5a=<=DMvGa)g zPw$^pmq9Fg&b#BZrPSoml(149rZS!fioV*Dy$z440U3MXDJmI?RZqLy0}IKSxN)o( z8+8wIZs#q(|KTg6y;Z(=96>xfpUsr@SP}I^v zN^R;ZVrDaWmNrM5-<X@k6JyjvA3;jHhma|Y|7!Vk& zgf(UK_6~cC;!|b!YTjke=nBiUqQdb#I9TY}!s5P)H+^c;9cW(QO8O%n5J^8Xfktd*qrn)+?-gP`m%B&q zi^}7jKm`yMW8ITFOMN#!QIB6$SWx*75tnCMaNg*_J*WuwBh~AT>0($nS8%&zmFQDp z$dL65niDtTV%!Kg1`6epWoQGNG`$`doy;Zjaa`keyL0F6iJMae6FIgnhAfzU%m@V+ zm5rQihLwS~b6{-bVR1ZSzBI7(Yj+V6T-8V*7I`ptWArGdy~8pnV>fALpi~NQLZ7;^ zpaj35=md<~-(tNmF69UX3?ua}A7UIn)q5i1iPYEGlhYSbkfeX`5epkxtzk3Qbu| zlgA`7ts%IvF4HJ}-98akyRnjCo{u-`A4&b+r?s|o`4wdYAHs-yh91p$7C_|+EdYH5 z10`!*=n+W9g>V&dfU1H!J}ASZi&-?`2IlDOAHnu306rD`y>jT)4^@S(X4XhN2{g9i zj-ym98+RT|d0ejIFJCM5>S{mT-8uGmRRqkJ3sMO_AQDrv77Q zv$t>zaVpVF6eBguE%9M2u?E-Oleft8z5+~W`G}KXD(Yc;7m4{Op>Le(k`g1UK7(1# zt6g}$n=Tdn{T4pu>v!c;xRCd_WI$Ali13x=U_0T!Ga-U~9W88q-lU+RLn2`N8Ouho z^0@SvC>$DguHWx)?^*ms-{PVq%dn(U3vrLj9zITDqQZ`H>Wsp@Gf%}SG=m)Vh}F$ztQAbwVGdDgd!28j&yX9wLW&s! zNR~6`nYg;ULAq8zi<;gUchAV5ib67Y##l2 zy+%gaD(|~G4@||{A;TYDSoS>q2o{t23t-^!NDSDEm8j3ao7Ei>KYLEpb$jz}7ciAM zD}trDN+AVVT_lXW<++~>8>Cj8fzJo@R;>%nGq)6+w?(#mNc#1J4W+!hA}?g$0Xqo? zn67qJmss)e%k(xO*&K@z6+}nHA(lCkb6n-|{pSztys$8HiOWTVR)tCO*Q9~if%3n7`uxGzE+OCu zwcVV|tgQdq60952$>85-GHk$lwM(uI+CU1?i{sVnKd0+UNq#eSSKjUKfDDgLnBG1y z^v?f#MRFkph~TgkoKBvM`L_~we8__xpLcjh`GwV|87q`vazJq?SX=mXhdvK>VqUf~ z4sYoTIpt5S)KrE-?>&=cRoBumD7;b5pq!Y07)#I$`)<@U+mo*dE*P~773p*u^6waO z2#thJahX_ySlYMpjx%h<)i43ao~Is`^Ya zMNZkuChEA7+ZJe6$>-C*dzTYf3#1SY82yFG?S&Q)5rTbKS-XLjckTLEc7>^sFcntQ zBeNXCSg&q1N3Bi^4zlQ%mcEBQ%2ab$?(;t-$HYd2%cnX$uuwU#I_6D3($m zR(>gHzM9ODf;r8b0l5LuEIQVZiQ0-|3Y_xzJkZc*CD=bPJ+&J+>>se%D4uTq?Ny{l z0Z5~og*Wa1O&anlcRWu_%o)(x?IZ0CfUNk_R-ik>GyvdFmpu1wHZaKTDGhL zqxsji)n<+)VKbV0_BRq9E;Kb`f=&vn(BK0Ba-gL?ZN;^^b3YFg6R=!q#zM;tcX0dM zdy5PPx@6pJPXHzH7$dGjM|6@6777nXPWV;CIQdNf(*Znv)sMy&Xcq> zhCq+6h6&v8<0}vd2(sKqU3j>fr7&#Xy%qZHcMU3m{wld^Nstkz8GagB?Y=SI&H z&{&BSA-|(i35$9(l6LpFyLm$0M0fK`Dz!~ezL?yEInsXAFR!bHe;ZL>Gd(#Hv?<$%`^b)oi?x%(jkylCPb=juPlF znMo&o961=NZ_$gd{xp1ZY2dNDOS!=XVj!M^A z+$z`EK4v=m{Bs{&I4W)({`&<5*^BV#z{IBAI_d+9Qx;~ zby?2zEjzUUeZWBDo5cz>%;z||z)<+6UtC)y60yD5J5`oo_zSM;l21@CY<0_|)NME5 zs)kHCMBa5YzB#N=W2aR?y9((~WuYwwf+HAc2mvU>NYlxOTvGf^Ye3za?*f-qUs^`a zT3>RPh9*Jf%3*bf|kqtnD_Buxv!<9N>BbuD#uYv-q^ z%RDnd7a3O4M9Y~TNISS@9K}JDkdg@>x8E6@n8jF=6qiDV+}{!V)(o?ykcr0sxBGEx zo!X;pc=r{H^vw6ztV5VZXBa4~(ujB$rZQ|AaGN@J7#q%2nU9gJ)g6dcj}zYB1& z@iFE0vMQVxa|v7tDHS$gwX$Ihc#M^DXRC>J@Zk?dC(3uB_s~*W&m-01DFMQGWjj5x z5po1@1gPl!v1Yra@qPG{D;$bYLM3qOwpl~7f~l)#n< zP+6`!NYe3EE~4RFR#_e=7YctPRBt6$He@`%e5m}f$M%yzC2S0<1}hRPjO>HJY~ z*dx(nbMbjv*;o&k{qzBdF|lS;UNVKziV=gbLq}UOCwr8GT5E9oRYQ}+>DhbQ1R=lj zgcNJN8|D)$Mx3#c+t@lhqcDUnHGVt0&EyQ{b5)=52B(VTzw=pQ^ba3`JB@BU^lS`_ zJEiLzgU#Acd_!}FMxCWC**FP^i#P}bYzNs78)#uSejEtYLbG>JJ7Igtho2oKQ;XW~ z4eMGO+t!_;G^V6c&R`5Tg+Pz2ToN(aybq4Q0ssie_{`t*DO%V7FaZ`{MBobFc9|pV z70o5ayHGJo9$$&Pgbs)pWNzduAcbh?~U?_P)(ve0S*3H%eNF&a5XR=!J#4c z;t992n7ZJr{*%`^dU1d-ALE8!3i#v;3r4r%j+JFCe=%3Vj=8{aXe zs)jrcUBZ=;LudcTUXj2ub>K5!{HHFHJ}Trx(PYugbQ8yK7&sqX;(;|UWjk3tGs3zuceeX)i4i_jA8Qz2Bc%DxN8 zXw!$+9jBtEHd1y90bYG4f8DcJM)Ab!M39tH5zz94*MAvnhA377@buNupSOUU3j8~> zd6&hk^ENRCp9T?_QUHk<=(&9Q^MJ^pi;nKOYNR@?L=RCSmKMJ5UQJQ`X!i~(gD*P! zs`RobzJG3Ra_Pg+WZUXUmMU$ilpwfcEti6)mw(~MZ0q!^sza>#jv!-+7B6F3QuMWg zVO!rXwD+lF1BBTito?ml-CV3vxuek~TKuOX^N6sol$v*{_%nAuD7i81eXm^Lz(Z~I z2Xj_Dts#G0&C;PV_Wkq*1QvB7+Post4={v;gk7b9u%#DC_bh(iJm$rqog^{JEx6NE zrs5^2SEL$|98#2WV#iG@L6cq|)SuTMSfGocPl65wUd^|5Lbpnb(;t>-Qu2jvANLgv zdte0vED-3C@^BdyHWLL(7{G$WA02z@JG!T-U^Q7HZ(7Bs&vchkh(p&}KvnS{MG^i6 z4r){gJp9p7WyWOEiKA2Cm6EXIn&&gk|Fc6^78OpPrX4ExCFE=SD$xcH;C2eB^{XTI zaxz_Cef*Yj==w_i_BTGXP;8C&f? z*QEM>={jFM8)lWAR870pG4XEWsl%%K|82S5b=9hVz7p_6i-d(Iyvq76&a#PV zR;VbQV|n?mg}&(ehClg%tK%IjgtnTR-u)lxH06XxXqH0soAZbB_Rm)XX=6Nge1uoG7 z9vQM_S~2h53n|W`y{{R9+=08rv~MohI_v4-BU^7fZ0-A}#b5{AOSTJm+(J;9yw%pD zX6u62GJ&@HKX5zQwq~j8T!Hrv-Mk^QSB5cu09L03{ToDO7jikM0WAcsjW>D}^jqCF zT0DEZ@K^KO_MD*%M!+V)lGVU6?LpX)eQVXEmq}R`NIJv;kBitJ!nW?0OxTVlu2ADf zE{A!*0g3%nwVcBD+AgT5bGx@WOnQk{zRpiZ4HhP`3BF%N|HdqPbbiV5)7x)kzC3ID zZ;27>0^mrMgWc7evsbQY`l`l})wr+e;=8U_!2&B77;1qL!N8y)eTJ2lf#CvhR~!Qa mc;sM|90DP5A*JW%f2r=u1xt!e4gwD_V(@hJb6Mw<&;$SznOm^{ literal 0 HcmV?d00001 diff --git a/test/packages/false_positives/failure_store/manifest.yml b/test/packages/false_positives/failure_store/manifest.yml new file mode 100644 index 0000000000..41a4a38aca --- /dev/null +++ b/test/packages/false_positives/failure_store/manifest.yml @@ -0,0 +1,36 @@ +format_version: 3.2.1 +name: failure_store +title: "Failure goes to failure store" +version: 0.0.1 +source: + license: "Apache-2.0" +description: "This is a package whose system tests fail with documents in the failure store." +type: integration +categories: + - custom +conditions: + kibana: + version: "^8.14.3" + elastic: + subscription: "basic" +screenshots: + - src: /img/sample-screenshot.png + title: Sample screenshot + size: 600x600 + type: image/png +icons: + - src: /img/sample-logo.svg + title: Sample logo + size: 32x32 + type: image/svg+xml +policy_templates: + - name: sample + title: Sample logs + description: Collect sample logs + inputs: + - type: logfile + title: Collect sample logs from instances + description: Collecting sample logs +owner: + github: elastic/integrations + type: elastic From 842cc748374f6a189cb0fd4537704da8ba3ea76a Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Thu, 18 Jul 2024 12:08:01 +0200 Subject: [PATCH 10/18] Ensure feature is enabled --- internal/stack/_static/elasticsearch.yml.tmpl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/internal/stack/_static/elasticsearch.yml.tmpl b/internal/stack/_static/elasticsearch.yml.tmpl index 9007ed5584..fd7ec31d82 100644 --- a/internal/stack/_static/elasticsearch.yml.tmpl +++ b/internal/stack/_static/elasticsearch.yml.tmpl @@ -19,17 +19,21 @@ ingest.geoip.downloader.enabled: false cluster.logsdb.enabled: true {{- end -}} +{{ if (not (semverLessThan $version "8.15.0-SNAPSHOT")) }} +es.failure_store_feature_flag_enabled: true +{{- end -}} + {{ if semverLessThan $version "8.0.0" }} script.max_compilations_rate: "use-context" script.context.template.max_compilations_rate: "unlimited" script.context.ingest.cache_max_size: 2000 script.context.processor_conditional.cache_max_size: 2000 script.context.template.cache_max_size: 2000 -{{ end }} +{{- end -}} {{ $apm_enabled := fact "apm_enabled" }} {{ if eq $apm_enabled "true" }} tracing.apm.enabled: true tracing.apm.agent.server_url: "http://fleet-server:8200" tracing.apm.agent.environment: "dev" -{{ end }} +{{- end -}} From 42f9b522cae986e45f6b9a1dabb38c269e49fc6b Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Thu, 18 Jul 2024 12:19:08 +0200 Subject: [PATCH 11/18] Add missing fields to test package --- .../data_stream/test/fields/base-fields.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/packages/false_positives/failure_store/data_stream/test/fields/base-fields.yml b/test/packages/false_positives/failure_store/data_stream/test/fields/base-fields.yml index ea02af1f8e..5977a33eac 100644 --- a/test/packages/false_positives/failure_store/data_stream/test/fields/base-fields.yml +++ b/test/packages/false_positives/failure_store/data_stream/test/fields/base-fields.yml @@ -10,5 +10,14 @@ - name: '@timestamp' type: date description: Event timestamp. +- name: ecs.version + type: keyword +- name: log.file.path + type: keyword +- name: log.offset + type: long +- name: input.type + type: keyword + description: Input type - name: number type: long From 9fe6090206639a72a341c0dbfd1f94baaf0f0b04 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Thu, 18 Jul 2024 13:34:21 +0200 Subject: [PATCH 12/18] Remove use of feature flag --- internal/stack/_static/elasticsearch.yml.tmpl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/internal/stack/_static/elasticsearch.yml.tmpl b/internal/stack/_static/elasticsearch.yml.tmpl index fd7ec31d82..43a8a981aa 100644 --- a/internal/stack/_static/elasticsearch.yml.tmpl +++ b/internal/stack/_static/elasticsearch.yml.tmpl @@ -19,10 +19,6 @@ ingest.geoip.downloader.enabled: false cluster.logsdb.enabled: true {{- end -}} -{{ if (not (semverLessThan $version "8.15.0-SNAPSHOT")) }} -es.failure_store_feature_flag_enabled: true -{{- end -}} - {{ if semverLessThan $version "8.0.0" }} script.max_compilations_rate: "use-context" script.context.template.max_compilations_rate: "unlimited" From f9bcf0e86a620e1d20ef14265e7674899c8a6563 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Thu, 18 Jul 2024 13:58:28 +0200 Subject: [PATCH 13/18] Enable failure store also in 8.14 --- cmd/testrunner.go | 2 +- internal/stack/_static/docker-compose-stack.yml.tmpl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/testrunner.go b/cmd/testrunner.go index 6701ee9349..94fa304615 100644 --- a/cmd/testrunner.go +++ b/cmd/testrunner.go @@ -543,7 +543,7 @@ func testRunnerSystemCommandAction(cmd *cobra.Command, args []string) error { if err != nil { return fmt.Errorf("can't parse Kibana version %q: %w", versionInfo.Number, err) } - checkFailureStore := !stackVersion.LessThan(semver.MustParse("8.15.0")) + checkFailureStore := !stackVersion.LessThan(semver.MustParse("8.14.0")) esClient, err := stack.NewElasticsearchClientFromProfile(profile) if err != nil { diff --git a/internal/stack/_static/docker-compose-stack.yml.tmpl b/internal/stack/_static/docker-compose-stack.yml.tmpl index 7e182bfb9d..68d80a6217 100644 --- a/internal/stack/_static/docker-compose-stack.yml.tmpl +++ b/internal/stack/_static/docker-compose-stack.yml.tmpl @@ -17,7 +17,7 @@ services: start_period: 300s interval: 5s environment: - - "ES_JAVA_OPTS=-Xms1g -Xmx1g" + - "ES_JAVA_OPTS=-Xms1g -Xmx1g {{ if not (semverLessThan $version "8.14.0") -}}-Des.failure_store_feature_flag_enabled=true{{- end -}}" - "ELASTIC_PASSWORD={{ $password }}" volumes: - "./elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml" From 0f633087383574cb81c67ea753f96c3e6f35b601 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Thu, 18 Jul 2024 14:39:01 +0200 Subject: [PATCH 14/18] Explicitly check if the failure store is available --- cmd/testrunner.go | 14 ++++---------- internal/elasticsearch/client.go | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/cmd/testrunner.go b/cmd/testrunner.go index 94fa304615..86b88b58d6 100644 --- a/cmd/testrunner.go +++ b/cmd/testrunner.go @@ -13,7 +13,6 @@ import ( "sort" "strings" - "github.com/Masterminds/semver/v3" "github.com/spf13/cobra" "github.com/elastic/elastic-package/internal/cobraext" @@ -535,15 +534,6 @@ func testRunnerSystemCommandAction(cmd *cobra.Command, args []string) error { if err != nil { return fmt.Errorf("can't create Kibana client: %w", err) } - versionInfo, err := kibanaClient.Version() - if err != nil { - return fmt.Errorf("can't get version info from Kibana client: %w", err) - } - stackVersion, err := semver.NewVersion(versionInfo.Number) - if err != nil { - return fmt.Errorf("can't parse Kibana version %q: %w", versionInfo.Number, err) - } - checkFailureStore := !stackVersion.LessThan(semver.MustParse("8.14.0")) esClient, err := stack.NewElasticsearchClientFromProfile(profile) if err != nil { @@ -553,6 +543,10 @@ func testRunnerSystemCommandAction(cmd *cobra.Command, args []string) error { if err != nil { return err } + checkFailureStore, err := esClient.CheckFailureStore(ctx) + if err != nil { + return fmt.Errorf("can't check if failure store is available: %w", err) + } if runTearDown || runTestsOnly { if variantFlag != "" { diff --git a/internal/elasticsearch/client.go b/internal/elasticsearch/client.go index 694306c14c..d11a1fb350 100644 --- a/internal/elasticsearch/client.go +++ b/internal/elasticsearch/client.go @@ -176,6 +176,32 @@ func (client *Client) CheckHealth(ctx context.Context) error { return nil } +// CheckFailureStore checks if the failure store is available. +func (client *Client) CheckFailureStore(ctx context.Context) (bool, error) { + // FIXME: Using the low-level transport till the API SDK supports the failure store. + request, err := http.NewRequest(http.MethodGet, fmt.Sprintf("/_search?failure_store=only"), nil) + if err != nil { + return false, fmt.Errorf("failed to create search request: %w", err) + } + request.Header.Set("Content-Type", "application/json") + + resp, err := client.Transport.Perform(request) + if err != nil { + return false, fmt.Errorf("failed to perform search request: %w", err) + } + defer resp.Body.Close() + + switch resp.StatusCode { + case http.StatusOK: + return true, nil + case http.StatusBadRequest: + // Error expected when using an unrecognized parameter. + return false, nil + default: + return false, fmt.Errorf("unexpected status code received: %d", resp.StatusCode) + } +} + // redHealthCause tries to identify the cause of a cluster in red state. This could be // also used as a replacement of CheckHealth, but keeping them separated because it uses // internal undocumented APIs that might change. From a8c71d5640b4ecc6af0594ae8b8de1260914cc47 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Thu, 18 Jul 2024 14:44:09 +0200 Subject: [PATCH 15/18] Rename method to check if failure stoe is available --- cmd/testrunner.go | 2 +- internal/elasticsearch/client.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/testrunner.go b/cmd/testrunner.go index 86b88b58d6..8e46025904 100644 --- a/cmd/testrunner.go +++ b/cmd/testrunner.go @@ -543,7 +543,7 @@ func testRunnerSystemCommandAction(cmd *cobra.Command, args []string) error { if err != nil { return err } - checkFailureStore, err := esClient.CheckFailureStore(ctx) + checkFailureStore, err := esClient.IsFailureStoreAvailable(ctx) if err != nil { return fmt.Errorf("can't check if failure store is available: %w", err) } diff --git a/internal/elasticsearch/client.go b/internal/elasticsearch/client.go index d11a1fb350..4cf5ae1f8d 100644 --- a/internal/elasticsearch/client.go +++ b/internal/elasticsearch/client.go @@ -176,8 +176,8 @@ func (client *Client) CheckHealth(ctx context.Context) error { return nil } -// CheckFailureStore checks if the failure store is available. -func (client *Client) CheckFailureStore(ctx context.Context) (bool, error) { +// IsFailureStoreAvailable checks if the failure store is available. +func (client *Client) IsFailureStoreAvailable(ctx context.Context) (bool, error) { // FIXME: Using the low-level transport till the API SDK supports the failure store. request, err := http.NewRequest(http.MethodGet, fmt.Sprintf("/_search?failure_store=only"), nil) if err != nil { From 69ffa3643ba285a1dbc70cbb3669812afc18ae70 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Thu, 18 Jul 2024 14:48:42 +0200 Subject: [PATCH 16/18] Linting --- internal/elasticsearch/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/elasticsearch/client.go b/internal/elasticsearch/client.go index 4cf5ae1f8d..20dfff16be 100644 --- a/internal/elasticsearch/client.go +++ b/internal/elasticsearch/client.go @@ -179,7 +179,7 @@ func (client *Client) CheckHealth(ctx context.Context) error { // IsFailureStoreAvailable checks if the failure store is available. func (client *Client) IsFailureStoreAvailable(ctx context.Context) (bool, error) { // FIXME: Using the low-level transport till the API SDK supports the failure store. - request, err := http.NewRequest(http.MethodGet, fmt.Sprintf("/_search?failure_store=only"), nil) + request, err := http.NewRequest(http.MethodGet, "/_search?failure_store=only", nil) if err != nil { return false, fmt.Errorf("failed to create search request: %w", err) } From 11cddae91c47fe06c5352888592eab0fa0fea51c Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Thu, 18 Jul 2024 17:08:58 +0200 Subject: [PATCH 17/18] Support failure store since 8.15.0 --- .../_static/docker-compose-stack.yml.tmpl | 2 +- scripts/test-check-false-positives.sh | 21 +++++++++++++++++-- .../failure_store.stack_version | 1 + 3 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 test/packages/false_positives/failure_store.stack_version diff --git a/internal/stack/_static/docker-compose-stack.yml.tmpl b/internal/stack/_static/docker-compose-stack.yml.tmpl index 68d80a6217..f07bcad9cf 100644 --- a/internal/stack/_static/docker-compose-stack.yml.tmpl +++ b/internal/stack/_static/docker-compose-stack.yml.tmpl @@ -17,7 +17,7 @@ services: start_period: 300s interval: 5s environment: - - "ES_JAVA_OPTS=-Xms1g -Xmx1g {{ if not (semverLessThan $version "8.14.0") -}}-Des.failure_store_feature_flag_enabled=true{{- end -}}" + - "ES_JAVA_OPTS=-Xms1g -Xmx1g {{ if not (semverLessThan $version "8.15.0-SNAPSHOT") -}}-Des.failure_store_feature_flag_enabled=true{{- end -}}" - "ELASTIC_PASSWORD={{ $password }}" volumes: - "./elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml" diff --git a/scripts/test-check-false-positives.sh b/scripts/test-check-false-positives.sh index ffb02aec1c..9751003841 100755 --- a/scripts/test-check-false-positives.sh +++ b/scripts/test-check-false-positives.sh @@ -76,16 +76,33 @@ function check_build_output() { ) } +function stack_version_args() { + if [[ -z "$PACKAGE_UNDER_TEST" ]]; then + # Don't force stack version if we are testing multiple packages. + return + fi + + local package_root=test/packages/${PACKAGE_TEST_TYPE:-false_positives}/${PACKAGE_UNDER_TEST}/ + local stack_version_file="${package_root%/}.stack_version" + if [[ ! -f $stack_version_file ]]; then + return + fi + + echo -n "--version $(cat $stack_version_file)" +} + trap cleanup EXIT ELASTIC_PACKAGE_LINKS_FILE_PATH="$(pwd)/scripts/links_table.yml" export ELASTIC_PACKAGE_LINKS_FILE_PATH +stack_args=$(stack_version_args) + # Update the stack -elastic-package stack update -v +elastic-package stack update -v ${stack_args} # Boot up the stack -elastic-package stack up -d -v +elastic-package stack up -d -v ${stack_args} elastic-package stack status diff --git a/test/packages/false_positives/failure_store.stack_version b/test/packages/false_positives/failure_store.stack_version new file mode 100644 index 0000000000..d95b929b33 --- /dev/null +++ b/test/packages/false_positives/failure_store.stack_version @@ -0,0 +1 @@ +8.15.0-SNAPSHOT From b91155614c243a3c91e5252d17f2d1727bab2c44 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Tue, 23 Jul 2024 17:54:53 +0200 Subject: [PATCH 18/18] Show additional info in errors --- internal/testrunner/runners/system/tester.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/internal/testrunner/runners/system/tester.go b/internal/testrunner/runners/system/tester.go index e70c4f42ec..6025b68c83 100644 --- a/internal/testrunner/runners/system/tester.go +++ b/internal/testrunner/runners/system/tester.go @@ -843,8 +843,12 @@ type scenarioTest struct { type failureStoreDocument struct { Error struct { - Type string `json:"type"` - Message string `json:"message"` + Type string `json:"type"` + Message string `json:"message"` + StackTrace string `json:"stack_trace"` + PipelineTrace []string `json:"pipeline_trace"` + Pipeline string `json:"pipeline"` + ProcessorType string `json:"processor_type"` } `json:"error"` } @@ -1966,7 +1970,14 @@ func writeSampleEvent(path string, doc common.MapStr, specVersion semver.Version func validateFailureStore(failureStore []failureStoreDocument) error { var multiErr multierror.Error for _, doc := range failureStore { - multiErr = append(multiErr, errors.New(doc.Error.Message)) + // TODO: Move this to the trace log level when available. + logger.Debug("Error found in failure store: ", doc.Error.StackTrace) + multiErr = append(multiErr, + fmt.Errorf("%s: %s (processor: %s, pipelines: %s)", + doc.Error.Type, + doc.Error.Message, + doc.Error.ProcessorType, + strings.Join(doc.Error.PipelineTrace, ","))) } if len(multiErr) > 0 {