From b58aeb6e4b928659572df8d70118d7e31f26e1ef Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Wed, 7 Oct 2020 12:33:19 +0200 Subject: [PATCH 01/46] Fix badger build in 386 (#21613) Use a fork with the latest released version including the fix for the issue with 32-bit architectures. Fix uses uint32 instead of 32-bit int to avoid overflow in constant with max uint32. --- NOTICE.txt | 6 +++--- go.mod | 1 + go.sum | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/NOTICE.txt b/NOTICE.txt index 0017abeba1a..b62c4b5d202 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -4473,12 +4473,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- -Dependency : github.com/dgraph-io/badger/v2 -Version: v2.2007.2 +Dependency : github.com/elastic/badger/v2 +Version: v2.2007.2-beats Licence type (autodetected): Apache-2.0 -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/dgraph-io/badger/v2@v2.2007.2/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/elastic/badger/v2@v2.2007.2-beats/LICENSE: Apache License Version 2.0, January 2004 diff --git a/go.mod b/go.mod index bb31374384a..0f0bdb3a422 100644 --- a/go.mod +++ b/go.mod @@ -191,6 +191,7 @@ replace ( github.com/Azure/go-autorest => github.com/Azure/go-autorest v12.2.0+incompatible github.com/Shopify/sarama => github.com/elastic/sarama v1.19.1-0.20200629123429-0e7b69039eec github.com/cucumber/godog => github.com/cucumber/godog v0.8.1 + github.com/dgraph-io/badger/v2 => github.com/elastic/badger/v2 v2.2007.2-beats github.com/docker/docker => github.com/docker/engine v0.0.0-20191113042239-ea84732a7725 github.com/docker/go-plugins-helpers => github.com/elastic/go-plugins-helpers v0.0.0-20200207104224-bdf17607b79f github.com/dop251/goja => github.com/andrewkroh/goja v0.0.0-20190128172624-dd2ac4456e20 diff --git a/go.sum b/go.sum index 8113d42f321..6f737d7cee2 100644 --- a/go.sum +++ b/go.sum @@ -201,8 +201,6 @@ github.com/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e/go.mod h1:xb github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= github.com/devigned/tab v0.1.2-0.20190607222403-0c15cf42f9a2 h1:6+hM8KeYKV0Z9EIINNqIEDyyIRAcNc2FW+/TUYNmWyw= github.com/devigned/tab v0.1.2-0.20190607222403-0c15cf42f9a2/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= -github.com/dgraph-io/badger/v2 v2.2007.2 h1:EjjK0KqwaFMlPin1ajhP943VPENHJdEz1KLIegjaI3k= -github.com/dgraph-io/badger/v2 v2.2007.2/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de h1:t0UHb5vdojIDUqktM6+xJAfScFBsVpXZmqC9dsgJmeA= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= @@ -241,6 +239,8 @@ github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/eclipse/paho.mqtt.golang v1.2.1-0.20200121105743-0d940dd29fd2 h1:DW6WrARxK5J+o8uAKCiACi5wy9EK1UzrsCpGBPsKHAA= github.com/eclipse/paho.mqtt.golang v1.2.1-0.20200121105743-0d940dd29fd2/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= +github.com/elastic/badger/v2 v2.2007.2-beats h1:/rV4bM6fdYvPQhFf2bHHivIb0H4nX8Or7nkWbQ/Q6Ko= +github.com/elastic/badger/v2 v2.2007.2-beats/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= github.com/elastic/dhcp v0.0.0-20200227161230-57ec251c7eb3 h1:lnDkqiRFKm0rxdljqrj3lotWinO9+jFmeDXIC4gvIQs= github.com/elastic/dhcp v0.0.0-20200227161230-57ec251c7eb3/go.mod h1:aPqzac6AYkipvp4hufTyMj5PDIphF3+At8zr7r51xjY= github.com/elastic/ecs v1.6.0 h1:8NmgfnsjmKXh9hVsK3H2tZtfUptepNc3msJOAynhtmc= From 3e86c2af0594d792f62af39a0b0e6b530e9aa90d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 7 Oct 2020 13:02:34 +0200 Subject: [PATCH 02/46] [E2E Tests] fix: set versions ony for PRs (#21608) * fix: set versions ony for PRs We want to use default versions per branch when running after a merge * fix: add trailing comma Co-authored-by: Victor Martinez Co-authored-by: Victor Martinez --- .ci/packaging.groovy | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/.ci/packaging.groovy b/.ci/packaging.groovy index 301ead43bab..26916372b70 100644 --- a/.ci/packaging.groovy +++ b/.ci/packaging.groovy @@ -245,19 +245,25 @@ def triggerE2ETests(String suite) { def branchName = isPR() ? "${env.CHANGE_TARGET}" : "${env.JOB_BASE_NAME}" def e2eTestsPipeline = "e2e-tests/e2e-testing-mbp/${branchName}" + + def parameters = [ + booleanParam(name: 'forceSkipGitChecks', value: true), + booleanParam(name: 'forceSkipPresubmit', value: true), + booleanParam(name: 'notifyOnGreenBuilds', value: !isPR()), + string(name: 'runTestsSuites', value: suite), + string(name: 'GITHUB_CHECK_NAME', value: env.GITHUB_CHECK_E2E_TESTS_NAME), + string(name: 'GITHUB_CHECK_REPO', value: env.REPO), + string(name: 'GITHUB_CHECK_SHA1', value: env.GIT_BASE_COMMIT), + ] + if (isPR()) { + def version = "pr-${env.CHANGE_ID}" + parameters.push(booleanParam(name: 'USE_CI_SNAPSHOTS', value: true)) + parameters.push(string(name: 'ELASTIC_AGENT_VERSION', value: "${version}")) + parameters.push(string(name: 'METRICBEAT_VERSION', value: "${version}")) + } + build(job: "${e2eTestsPipeline}", - parameters: [ - booleanParam(name: 'forceSkipGitChecks', value: true), - booleanParam(name: 'forceSkipPresubmit', value: true), - booleanParam(name: 'notifyOnGreenBuilds', value: !isPR()), - booleanParam(name: 'USE_CI_SNAPSHOTS', value: true), - string(name: 'ELASTIC_AGENT_VERSION', value: "pr-${env.CHANGE_ID}"), - string(name: 'METRICBEAT_VERSION', value: "pr-${env.CHANGE_ID}"), - string(name: 'runTestsSuites', value: suite), - string(name: 'GITHUB_CHECK_NAME', value: env.GITHUB_CHECK_E2E_TESTS_NAME), - string(name: 'GITHUB_CHECK_REPO', value: env.REPO), - string(name: 'GITHUB_CHECK_SHA1', value: env.GIT_BASE_COMMIT), - ], + parameters: parameters, propagate: false, wait: false ) From 36953dfe6a587c0083cacb350f4d274e09d634a4 Mon Sep 17 00:00:00 2001 From: Dan Roscigno Date: Wed, 7 Oct 2020 09:59:05 -0400 Subject: [PATCH 03/46] Add `add_observer_metadata` `geo.name` to Quickstart (#21501) * Add `add_observer_metadata` `geo.name` to Quickstart The observer location is very important in the Uptime app and the out-of-the-box machine learning job. * Update getting-started.asciidoc The instructions about editing heartbeat.yml were too wordy * tighten it up! Co-authored-by: DeDe Morton Co-authored-by: DeDe Morton --- heartbeat/docs/getting-started.asciidoc | 40 +++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/heartbeat/docs/getting-started.asciidoc b/heartbeat/docs/getting-started.asciidoc index 21da1fb0547..1cd73dbb04f 100644 --- a/heartbeat/docs/getting-started.asciidoc +++ b/heartbeat/docs/getting-started.asciidoc @@ -96,9 +96,43 @@ was started. Heartbeat adds the `@every` keyword to the syntax provided by the include::{libbeat-dir}/shared/config-check.asciidoc[] +[float] +[[configurelocation]] +=== Step 4: Configure the Heartbeat location + +Heartbeat can be deployed in multiple locations so that you can detect +differences in availability and response times across those locations. +Configure the Heartbeat location to allow {kib} to display location-specific +information on Uptime maps and perform Uptime anomaly detection based +on location. + +To configure the location of a Heartbeat instance, modify the +`add_observer_metadata` processor in +{beatname_lc}.yml+. The following +example specifies the `geo.name` of the `add_observer_metadata` processor as +`us-east-1a`: + +[source,yaml] +---------------------------------------------------------------------- +# ============================ Processors ============================ + +processors: + - add_observer_metadata: + # Optional, but recommended geo settings for the location Heartbeat is running in + geo: <1> + # Token describing this location + name: us-east-1a <2> + # Lat, Lon " + #location: "37.926868, -78.024902" <3> +---------------------------------------------------------------------- +<1> Uncomment the `geo` setting. +<2> Uncomment `name` and assign the name of the location of the Heartbeat server. +<3> Optionally uncomment `location` and assign the latitude and longitude. + +include::{libbeat-dir}/shared/config-check.asciidoc[] + [float] [[setup-assets]] -=== Step 4: Set up assets +=== Step 5: Set up assets {beatname_uc} comes with predefined assets for parsing, indexing, and visualizing your data. To load these assets: @@ -128,7 +162,7 @@ environment. If you're using a different output, such as {ls}, see [float] [[start]] -=== Step 5: Start Heartbeat +=== Step 6: Start Heartbeat Before starting {beatname_uc}, modify the user credentials in +{beatname_lc}.yml+ and specify a user who is @@ -145,7 +179,7 @@ events to your defined output. [float] [[view-data]] -=== Step 6: View your data in {kib} +=== Step 7: View your data in {kib} {beatname_uc} comes with pre-built {kib} dashboards and UIs for visualizing the status of your services. The dashboards are available in the From 00d0419b9d1cadc4b2feb3c0676d7661d4786cfa Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Wed, 7 Oct 2020 15:03:22 +0100 Subject: [PATCH 04/46] [CI] Change notification channel (#21559) --- Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index df75ad2ccd1..17041987b27 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -24,7 +24,7 @@ pipeline { PIPELINE_LOG_LEVEL = 'INFO' PYTEST_ADDOPTS = "${params.PYTEST_ADDOPTS}" RUNBLD_DISABLE_NOTIFICATIONS = 'true' - SLACK_CHANNEL = "#beats-ci-builds" + SLACK_CHANNEL = "#beats-build" TERRAFORM_VERSION = "0.12.24" XPACK_MODULE_PATTERN = '^x-pack\\/[a-z0-9]+beat\\/module\\/([^\\/]+)\\/.*' } @@ -122,7 +122,7 @@ pipeline { runbld(stashedTestReports: stashedTestReports, project: env.REPO) } cleanup { - notifyBuildResult(prComment: true, slackComment: true) + notifyBuildResult(prComment: true, slackComment: true, slackNotify: (isBranch() || isTag())) } } } From 030077ce9098bbb419fe92843a2ce3ad25860b20 Mon Sep 17 00:00:00 2001 From: kaiyan-sheng Date: Wed, 7 Oct 2020 08:22:34 -0600 Subject: [PATCH 05/46] [Filebeat S3] Change log.file.path to be nested object (#21624) --- x-pack/filebeat/input/s3/collector.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/x-pack/filebeat/input/s3/collector.go b/x-pack/filebeat/input/s3/collector.go index 2976dd52a5b..bf294f94245 100644 --- a/x-pack/filebeat/input/s3/collector.go +++ b/x-pack/filebeat/input/s3/collector.go @@ -556,8 +556,10 @@ func createEvent(log string, offset int, info s3Info, objectHash string, s3Ctx * Fields: common.MapStr{ "message": log, "log": common.MapStr{ - "offset": int64(offset), - "file.path": constructObjectURL(info), + "offset": int64(offset), + "file": common.MapStr{ + "path": constructObjectURL(info), + }, }, "aws": common.MapStr{ "s3": common.MapStr{ From 048a404985dae344e340b82ddd14698f7fa86bed Mon Sep 17 00:00:00 2001 From: kaiyan-sheng Date: Wed, 7 Oct 2020 08:25:54 -0600 Subject: [PATCH 06/46] Add fips_enabled into all aws filesets (#21626) --- x-pack/filebeat/module/aws/cloudtrail/config/s3.yml | 4 ++++ x-pack/filebeat/module/aws/cloudwatch/config/s3.yml | 4 ++++ x-pack/filebeat/module/aws/ec2/config/s3.yml | 4 ++++ x-pack/filebeat/module/aws/elb/config/s3.yml | 4 ++++ x-pack/filebeat/module/aws/s3access/config/s3.yml | 4 ++++ x-pack/filebeat/module/aws/vpcflow/config/input.yml | 4 ++++ 6 files changed, 24 insertions(+) diff --git a/x-pack/filebeat/module/aws/cloudtrail/config/s3.yml b/x-pack/filebeat/module/aws/cloudtrail/config/s3.yml index 7c455f64f26..ac1caacf21c 100644 --- a/x-pack/filebeat/module/aws/cloudtrail/config/s3.yml +++ b/x-pack/filebeat/module/aws/cloudtrail/config/s3.yml @@ -51,6 +51,10 @@ session_token: {{ .session_token }} role_arn: {{ .role_arn }} {{ end }} +{{ if .fips_enabled }} +fips_enabled: {{ .fips_enabled }} +{{ end }} + tags: {{.tags | tojson}} publisher_pipeline.disable_host: {{ inList .tags "forwarded" }} diff --git a/x-pack/filebeat/module/aws/cloudwatch/config/s3.yml b/x-pack/filebeat/module/aws/cloudwatch/config/s3.yml index 5d2d75dc5d8..bdb0ff350f0 100644 --- a/x-pack/filebeat/module/aws/cloudwatch/config/s3.yml +++ b/x-pack/filebeat/module/aws/cloudwatch/config/s3.yml @@ -37,6 +37,10 @@ session_token: {{ .session_token }} role_arn: {{ .role_arn }} {{ end }} +{{ if .fips_enabled }} +fips_enabled: {{ .fips_enabled }} +{{ end }} + tags: {{.tags | tojson}} publisher_pipeline.disable_host: {{ inList .tags "forwarded" }} diff --git a/x-pack/filebeat/module/aws/ec2/config/s3.yml b/x-pack/filebeat/module/aws/ec2/config/s3.yml index 5d2d75dc5d8..bdb0ff350f0 100644 --- a/x-pack/filebeat/module/aws/ec2/config/s3.yml +++ b/x-pack/filebeat/module/aws/ec2/config/s3.yml @@ -37,6 +37,10 @@ session_token: {{ .session_token }} role_arn: {{ .role_arn }} {{ end }} +{{ if .fips_enabled }} +fips_enabled: {{ .fips_enabled }} +{{ end }} + tags: {{.tags | tojson}} publisher_pipeline.disable_host: {{ inList .tags "forwarded" }} diff --git a/x-pack/filebeat/module/aws/elb/config/s3.yml b/x-pack/filebeat/module/aws/elb/config/s3.yml index 5d2d75dc5d8..bdb0ff350f0 100644 --- a/x-pack/filebeat/module/aws/elb/config/s3.yml +++ b/x-pack/filebeat/module/aws/elb/config/s3.yml @@ -37,6 +37,10 @@ session_token: {{ .session_token }} role_arn: {{ .role_arn }} {{ end }} +{{ if .fips_enabled }} +fips_enabled: {{ .fips_enabled }} +{{ end }} + tags: {{.tags | tojson}} publisher_pipeline.disable_host: {{ inList .tags "forwarded" }} diff --git a/x-pack/filebeat/module/aws/s3access/config/s3.yml b/x-pack/filebeat/module/aws/s3access/config/s3.yml index 5d2d75dc5d8..bdb0ff350f0 100644 --- a/x-pack/filebeat/module/aws/s3access/config/s3.yml +++ b/x-pack/filebeat/module/aws/s3access/config/s3.yml @@ -37,6 +37,10 @@ session_token: {{ .session_token }} role_arn: {{ .role_arn }} {{ end }} +{{ if .fips_enabled }} +fips_enabled: {{ .fips_enabled }} +{{ end }} + tags: {{.tags | tojson}} publisher_pipeline.disable_host: {{ inList .tags "forwarded" }} diff --git a/x-pack/filebeat/module/aws/vpcflow/config/input.yml b/x-pack/filebeat/module/aws/vpcflow/config/input.yml index d0c18047ca4..628196b7d3e 100644 --- a/x-pack/filebeat/module/aws/vpcflow/config/input.yml +++ b/x-pack/filebeat/module/aws/vpcflow/config/input.yml @@ -39,6 +39,10 @@ session_token: {{ .session_token }} role_arn: {{ .role_arn }} {{ end }} +{{ if .fips_enabled }} +fips_enabled: {{ .fips_enabled }} +{{ end }} + {{ else if eq .input "file" }} type: log From cf5fafb157c127c0159e55697ac8a2baf9f1e710 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Wed, 7 Oct 2020 17:56:16 +0200 Subject: [PATCH 07/46] Add openstack ssl provider in add_cloud_metadata (#21590) Add a new provider to query metadata from OpenStack deployments using HTTPS. Add the usual SSL settings available in other features that support TLS. --- CHANGELOG.next.asciidoc | 1 + .../add_cloud_metadata/add_cloud_metadata.go | 16 ++++++++-- .../processors/add_cloud_metadata/config.go | 9 ++++-- .../docs/add_cloud_metadata.asciidoc | 4 +++ .../add_cloud_metadata/http_fetcher.go | 11 +++++-- .../provider_openstack_nova.go | 18 +++++++---- .../provider_openstack_nova_test.go | 30 ++++++++++++++++--- .../add_cloud_metadata/providers.go | 25 +++++++++------- 8 files changed, 87 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 6b4a999e0ec..cc8fb52396a 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -453,6 +453,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Cloud Foundry metadata is cached to disk. {pull}20775[20775] - Add option to select the type of index template to load: legacy, component, index. {pull}21212[21212] - Release `add_cloudfoundry_metadata` as GA. {pull}21525[21525] +- Add support for OpenStack SSL metadata APIs in `add_cloud_metadata`. {pull}21590[21590] *Auditbeat* diff --git a/libbeat/processors/add_cloud_metadata/add_cloud_metadata.go b/libbeat/processors/add_cloud_metadata/add_cloud_metadata.go index de1a8063667..5376d563fca 100644 --- a/libbeat/processors/add_cloud_metadata/add_cloud_metadata.go +++ b/libbeat/processors/add_cloud_metadata/add_cloud_metadata.go @@ -26,6 +26,7 @@ import ( "github.com/elastic/beats/v7/libbeat/beat" "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/common/transport/tlscommon" "github.com/elastic/beats/v7/libbeat/logp" "github.com/elastic/beats/v7/libbeat/processors" jsprocessor "github.com/elastic/beats/v7/libbeat/processors/script/javascript/module/processor" @@ -53,6 +54,7 @@ type addCloudMetadata struct { type initData struct { fetchers []metadataFetcher timeout time.Duration + tlsConfig *tlscommon.TLSConfig overwrite bool } @@ -63,14 +65,24 @@ func New(c *common.Config) (processors.Processor, error) { return nil, errors.Wrap(err, "failed to unpack add_cloud_metadata config") } + tlsConfig, err := tlscommon.LoadTLSConfig(config.TLS) + if err != nil { + return nil, errors.Wrap(err, "TLS configuration load") + } + initProviders := selectProviders(config.Providers, cloudMetaProviders) fetchers, err := setupFetchers(initProviders, c) if err != nil { return nil, err } p := &addCloudMetadata{ - initData: &initData{fetchers, config.Timeout, config.Overwrite}, - logger: logp.NewLogger("add_cloud_metadata"), + initData: &initData{ + fetchers: fetchers, + timeout: config.Timeout, + tlsConfig: tlsConfig, + overwrite: config.Overwrite, + }, + logger: logp.NewLogger("add_cloud_metadata"), } go p.init() diff --git a/libbeat/processors/add_cloud_metadata/config.go b/libbeat/processors/add_cloud_metadata/config.go index 08f4a241483..93a31137592 100644 --- a/libbeat/processors/add_cloud_metadata/config.go +++ b/libbeat/processors/add_cloud_metadata/config.go @@ -20,12 +20,15 @@ package add_cloud_metadata import ( "fmt" "time" + + "github.com/elastic/beats/v7/libbeat/common/transport/tlscommon" ) type config struct { - Timeout time.Duration `config:"timeout"` // Amount of time to wait for responses from the metadata services. - Overwrite bool `config:"overwrite"` // Overwrite if cloud.* fields already exist. - Providers providerList `config:"providers"` // List of providers to probe + Timeout time.Duration `config:"timeout"` // Amount of time to wait for responses from the metadata services. + TLS *tlscommon.Config `config:"ssl"` // TLS configuration + Overwrite bool `config:"overwrite"` // Overwrite if cloud.* fields already exist. + Providers providerList `config:"providers"` // List of providers to probe } type providerList []string diff --git a/libbeat/processors/add_cloud_metadata/docs/add_cloud_metadata.asciidoc b/libbeat/processors/add_cloud_metadata/docs/add_cloud_metadata.asciidoc index 41c0dd6d9f3..44497f31539 100644 --- a/libbeat/processors/add_cloud_metadata/docs/add_cloud_metadata.asciidoc +++ b/libbeat/processors/add_cloud_metadata/docs/add_cloud_metadata.asciidoc @@ -52,12 +52,16 @@ List of names the `providers` setting supports: - "aws", or "ec2" for Amazon Web Services (enabled by default). - "gcp" for Google Copmute Enging (enabled by default). - "openstack", or "nova" for Openstack Nova (enabled by default). +- "openstack-ssl", or "nova-ssl" for Openstack Nova when SSL metadata APIs are enabled (enabled by default). - "tencent", or "qcloud" for Tencent Cloud (disabled by default). The third optional configuration setting is `overwrite`. When `overwrite` is `true`, `add_cloud_metadata` overwrites existing `cloud.*` fields (`false` by default). +The `add_cloud_metadata` processor supports SSL options to configure the http +client used to query cloud metadata. See <> for more information. + The metadata that is added to events varies by hosting provider. Below are examples for each of the supported providers. diff --git a/libbeat/processors/add_cloud_metadata/http_fetcher.go b/libbeat/processors/add_cloud_metadata/http_fetcher.go index e9b18478661..e337edd5be9 100644 --- a/libbeat/processors/add_cloud_metadata/http_fetcher.go +++ b/libbeat/processors/add_cloud_metadata/http_fetcher.go @@ -27,6 +27,7 @@ import ( "github.com/pkg/errors" "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/common/transport/tlscommon" ) type httpMetadataFetcher struct { @@ -127,9 +128,15 @@ func (f *httpMetadataFetcher) fetchRaw( // getMetadataURLs loads config and generates the metadata URLs. func getMetadataURLs(c *common.Config, defaultHost string, metadataURIs []string) ([]string, error) { + return getMetadataURLsWithScheme(c, "http", defaultHost, metadataURIs) +} + +// getMetadataURLsWithScheme loads config and generates the metadata URLs. +func getMetadataURLsWithScheme(c *common.Config, scheme string, defaultHost string, metadataURIs []string) ([]string, error) { var urls []string config := struct { - MetadataHostAndPort string `config:"host"` // Specifies the host and port of the metadata service (for testing purposes only). + MetadataHostAndPort string `config:"host"` // Specifies the host and port of the metadata service (for testing purposes only). + TLSConfig *tlscommon.Config `config:"ssl"` }{ MetadataHostAndPort: defaultHost, } @@ -138,7 +145,7 @@ func getMetadataURLs(c *common.Config, defaultHost string, metadataURIs []string return urls, errors.Wrap(err, "failed to unpack add_cloud_metadata config") } for _, uri := range metadataURIs { - urls = append(urls, "http://"+config.MetadataHostAndPort+uri) + urls = append(urls, scheme+"://"+config.MetadataHostAndPort+uri) } return urls, nil } diff --git a/libbeat/processors/add_cloud_metadata/provider_openstack_nova.go b/libbeat/processors/add_cloud_metadata/provider_openstack_nova.go index 17bc5abf689..7c9a997e0e2 100644 --- a/libbeat/processors/add_cloud_metadata/provider_openstack_nova.go +++ b/libbeat/processors/add_cloud_metadata/provider_openstack_nova.go @@ -32,16 +32,24 @@ const ( // OpenStack Nova Metadata Service // Document https://docs.openstack.org/nova/latest/user/metadata-service.html var openstackNovaMetadataFetcher = provider{ - Name: "openstack-nova", + Name: "openstack-nova", + Local: true, + Create: buildOpenstackNovaCreate("http"), +} - Local: true, +var openstackNovaSSLMetadataFetcher = provider{ + Name: "openstack-nova-ssl", + Local: true, + Create: buildOpenstackNovaCreate("https"), +} - Create: func(provider string, c *common.Config) (metadataFetcher, error) { +func buildOpenstackNovaCreate(scheme string) func(provider string, c *common.Config) (metadataFetcher, error) { + return func(provider string, c *common.Config) (metadataFetcher, error) { osSchema := func(m map[string]interface{}) common.MapStr { return common.MapStr(m) } - urls, err := getMetadataURLs(c, metadataHost, []string{ + urls, err := getMetadataURLsWithScheme(c, scheme, metadataHost, []string{ osMetadataInstanceIDURI, osMetadataInstanceTypeURI, osMetadataHostnameURI, @@ -71,5 +79,5 @@ var openstackNovaMetadataFetcher = provider{ } fetcher := &httpMetadataFetcher{"openstack", nil, responseHandlers, osSchema} return fetcher, nil - }, + } } diff --git a/libbeat/processors/add_cloud_metadata/provider_openstack_nova_test.go b/libbeat/processors/add_cloud_metadata/provider_openstack_nova_test.go index d5c38a8437b..0a63c026cde 100644 --- a/libbeat/processors/add_cloud_metadata/provider_openstack_nova_test.go +++ b/libbeat/processors/add_cloud_metadata/provider_openstack_nova_test.go @@ -29,8 +29,8 @@ import ( "github.com/elastic/beats/v7/libbeat/logp" ) -func initOpenstackNovaTestServer() *httptest.Server { - return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { +func openstackNovaMetadataHandler() http.HandlerFunc { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.RequestURI == osMetadataInstanceIDURI { w.Write([]byte("i-0000ffac")) return @@ -49,13 +49,13 @@ func initOpenstackNovaTestServer() *httptest.Server { } http.Error(w, "not found", http.StatusNotFound) - })) + }) } func TestRetrieveOpenstackNovaMetadata(t *testing.T) { logp.TestingSetup() - server := initOpenstackNovaTestServer() + server := httptest.NewServer(openstackNovaMetadataHandler()) defer server.Close() config, err := common.NewConfigFrom(map[string]interface{}{ @@ -66,6 +66,28 @@ func TestRetrieveOpenstackNovaMetadata(t *testing.T) { t.Fatal(err) } + assertOpenstackNova(t, config) +} + +func TestRetrieveOpenstackNovaMetadataWithHTTPS(t *testing.T) { + logp.TestingSetup() + + server := httptest.NewTLSServer(openstackNovaMetadataHandler()) + defer server.Close() + + config, err := common.NewConfigFrom(map[string]interface{}{ + "host": server.Listener.Addr().String(), + "ssl.verification_mode": "none", + }) + + if err != nil { + t.Fatal(err) + } + + assertOpenstackNova(t, config) +} + +func assertOpenstackNova(t *testing.T, config *common.Config) { p, err := New(config) if err != nil { t.Fatal(err) diff --git a/libbeat/processors/add_cloud_metadata/providers.go b/libbeat/processors/add_cloud_metadata/providers.go index f93e8cf78f4..ea03b64258a 100644 --- a/libbeat/processors/add_cloud_metadata/providers.go +++ b/libbeat/processors/add_cloud_metadata/providers.go @@ -51,17 +51,19 @@ type result struct { } var cloudMetaProviders = map[string]provider{ - "alibaba": alibabaCloudMetadataFetcher, - "ecs": alibabaCloudMetadataFetcher, - "azure": azureVMMetadataFetcher, - "digitalocean": doMetadataFetcher, - "aws": ec2MetadataFetcher, - "ec2": ec2MetadataFetcher, - "gcp": gceMetadataFetcher, - "openstack": openstackNovaMetadataFetcher, - "nova": openstackNovaMetadataFetcher, - "qcloud": qcloudMetadataFetcher, - "tencent": qcloudMetadataFetcher, + "alibaba": alibabaCloudMetadataFetcher, + "ecs": alibabaCloudMetadataFetcher, + "azure": azureVMMetadataFetcher, + "digitalocean": doMetadataFetcher, + "aws": ec2MetadataFetcher, + "ec2": ec2MetadataFetcher, + "gcp": gceMetadataFetcher, + "openstack": openstackNovaMetadataFetcher, + "nova": openstackNovaMetadataFetcher, + "openstack-ssl": openstackNovaSSLMetadataFetcher, + "nova-ssl": openstackNovaSSLMetadataFetcher, + "qcloud": qcloudMetadataFetcher, + "tencent": qcloudMetadataFetcher, } func selectProviders(configList providerList, providers map[string]provider) map[string]provider { @@ -138,6 +140,7 @@ func (p *addCloudMetadata) fetchMetadata() *result { Timeout: p.initData.timeout, KeepAlive: 0, }).DialContext, + TLSClientConfig: p.initData.tlsConfig.ToConfig(), }, } From 7028e2c9779bff69f7d526af69eb804c024e5776 Mon Sep 17 00:00:00 2001 From: Marc Guasch Date: Wed, 7 Oct 2020 18:47:33 +0200 Subject: [PATCH 08/46] Fix cyberark/corepas pipeline (#21643) --- .../cyberark/corepas/ingest/pipeline.yml | 2 +- .../corepas/test/generated.log-expected.json | 394 +++++++++--------- 2 files changed, 198 insertions(+), 198 deletions(-) diff --git a/x-pack/filebeat/module/cyberark/corepas/ingest/pipeline.yml b/x-pack/filebeat/module/cyberark/corepas/ingest/pipeline.yml index ffe90e79f85..4e401931415 100644 --- a/x-pack/filebeat/module/cyberark/corepas/ingest/pipeline.yml +++ b/x-pack/filebeat/module/cyberark/corepas/ingest/pipeline.yml @@ -55,7 +55,7 @@ processors: ignore_missing: true - append: field: related.hosts - value: '{{host.hostname server.domain}}' + value: '{{host.hostname}}' allow_duplicates: false if: ctx?.host?.hostname != null && ctx.host?.hostname != '' - append: diff --git a/x-pack/filebeat/module/cyberark/corepas/test/generated.log-expected.json b/x-pack/filebeat/module/cyberark/corepas/test/generated.log-expected.json index 4056ed473ca..90805d72bcd 100644 --- a/x-pack/filebeat/module/cyberark/corepas/test/generated.log-expected.json +++ b/x-pack/filebeat/module/cyberark/corepas/test/generated.log-expected.json @@ -20,9 +20,9 @@ "10.208.15.216" ], "related.user": [ + "quasiarc", "itv", - "utl", - "quasiarc" + "utl" ], "rsa.db.index": "nes", "rsa.internal.event_desc": "pexe", @@ -67,13 +67,13 @@ "iatnu3810.mail.localdomain" ], "related.ip": [ - "10.92.136.230", - "10.175.75.18" + "10.175.75.18", + "10.92.136.230" ], "related.user": [ - "dolore", + "orev", "nnumqu", - "orev" + "dolore" ], "rsa.db.database": "umdo", "rsa.db.index": "vol", @@ -130,13 +130,13 @@ "anti4454.api.example" ], "related.ip": [ - "10.46.185.46", - "10.51.132.10" + "10.51.132.10", + "10.46.185.46" ], "related.user": [ - "incid", + "serror", "nse", - "serror" + "incid" ], "rsa.db.database": "byC", "rsa.db.index": "tur", @@ -197,9 +197,9 @@ "10.53.192.140" ], "related.user": [ + "psumquia", "atcup", - "ptass", - "psumquia" + "ptass" ], "rsa.db.database": "aperi", "rsa.db.index": "llumd", @@ -253,9 +253,9 @@ "10.81.199.122" ], "related.user": [ + "oremips", "eos", - "giatq", - "oremips" + "giatq" ], "rsa.db.index": "tempo", "rsa.internal.event_desc": "uian", @@ -304,9 +304,9 @@ "10.139.186.201" ], "related.user": [ - "uam", "tcupida", - "aboris" + "aboris", + "uam" ], "rsa.db.database": "isiu", "rsa.db.index": "iatisu", @@ -367,9 +367,9 @@ "10.104.111.129" ], "related.user": [ + "ele", "etconsec", - "ipis", - "ele" + "ipis" ], "rsa.db.database": "riat", "rsa.db.index": "umdolor", @@ -423,9 +423,9 @@ "10.116.120.216" ], "related.user": [ - "quiratio", + "animi", "umdo", - "animi" + "quiratio" ], "rsa.db.index": "oll", "rsa.internal.event_desc": "rumet", @@ -470,13 +470,13 @@ "isqu7224.localdomain" ], "related.ip": [ - "10.57.40.29", - "10.62.54.220" + "10.62.54.220", + "10.57.40.29" ], "related.user": [ "psum", - "rnatura", - "taevi" + "taevi", + "rnatura" ], "rsa.db.database": "emeumfug", "rsa.db.index": "omn", @@ -530,9 +530,9 @@ "10.74.237.180" ], "related.user": [ - "ema", + "tnon", "cup", - "tnon" + "ema" ], "rsa.db.index": "remeumf", "rsa.internal.event_desc": "lup", @@ -618,8 +618,8 @@ "10.74.253.127" ], "related.user": [ - "onproide", "icab", + "onproide", "tema" ], "rsa.db.index": "mqui", @@ -664,8 +664,8 @@ "tlabo6088.www.localdomain" ], "related.ip": [ - "10.189.109.245", - "10.92.8.15" + "10.92.8.15", + "10.189.109.245" ], "related.user": [ "inima", @@ -766,8 +766,8 @@ "10.18.109.121" ], "related.user": [ - "hil", "tatn", + "hil", "pida" ], "rsa.db.index": "quip", @@ -817,9 +817,9 @@ "10.63.37.192" ], "related.user": [ + "reetd", "iunt", - "equep", - "reetd" + "equep" ], "rsa.db.database": "aliqu", "rsa.db.index": "mipsumd", @@ -880,9 +880,9 @@ "10.47.202.102" ], "related.user": [ - "run", + "ice", "ntor", - "ice" + "run" ], "rsa.db.database": "ite", "rsa.db.index": "iquipex", @@ -942,8 +942,8 @@ "10.106.239.55" ], "related.user": [ - "itquiin", - "serunt" + "serunt", + "itquiin" ], "rsa.db.database": "itame", "rsa.db.index": "oluptas", @@ -999,13 +999,13 @@ "etMalor4236.www5.host" ], "related.ip": [ - "10.125.160.129", - "10.53.168.235" + "10.53.168.235", + "10.125.160.129" ], "related.user": [ + "one", "abi", - "ione", - "one" + "ione" ], "rsa.db.database": "sperna", "rsa.db.index": "estia", @@ -1066,8 +1066,8 @@ "10.227.177.121" ], "related.user": [ - "liqui", "tasuntex", + "liqui", "iduntu" ], "rsa.db.database": "rvel", @@ -1125,16 +1125,16 @@ "process.name": "laboree.exe", "process.pid": 6501, "related.hosts": [ - "", + "xeacomm6855.api.corp", "nsecte3304.mail.corp" ], "related.ip": [ - "10.167.85.181", - "10.98.182.220" + "10.98.182.220", + "10.167.85.181" ], "related.user": [ - "fde", - "econs" + "econs", + "fde" ], "rsa.db.database": "equat", "rsa.internal.event_desc": "orpor", @@ -1189,9 +1189,9 @@ "10.89.208.95" ], "related.user": [ - "iciadese", "icabo", - "sintoc" + "sintoc", + "iciadese" ], "rsa.db.index": "eni", "rsa.internal.event_desc": "rcitati", @@ -1240,9 +1240,9 @@ "10.72.148.32" ], "related.user": [ - "uteirure", "tDuisaut", - "luptatev" + "luptatev", + "uteirure" ], "rsa.db.database": "uamest", "rsa.db.index": "uae", @@ -1362,12 +1362,12 @@ "tnonpro7635.localdomain" ], "related.ip": [ - "10.192.34.76", - "10.213.144.249" + "10.213.144.249", + "10.192.34.76" ], "related.user": [ - "lore", "temqu", + "lore", "iquipe" ], "rsa.db.database": "gnamal", @@ -1482,9 +1482,9 @@ "10.143.193.199" ], "related.user": [ - "tqu", + "niamqui", "quid", - "niamqui" + "tqu" ], "rsa.db.index": "inci", "rsa.internal.event_desc": "eroinBCS", @@ -1533,9 +1533,9 @@ "10.65.175.9" ], "related.user": [ + "umqu", "ritatise", - "essequam", - "umqu" + "essequam" ], "rsa.db.database": "ender", "rsa.db.index": "entorev", @@ -1589,9 +1589,9 @@ "10.205.72.243" ], "related.user": [ - "isiuta", "tatn", - "umdolo" + "umdolo", + "isiuta" ], "rsa.db.index": "proide", "rsa.internal.event_desc": "ameiusm", @@ -1633,9 +1633,9 @@ "10.107.9.163" ], "related.user": [ - "sit", + "mac", "mquisno", - "mac" + "sit" ], "rsa.db.index": "sit", "rsa.internal.event_desc": "tdol", @@ -1677,9 +1677,9 @@ "10.80.101.72" ], "related.user": [ + "asiarc", "umSe", - "quidexea", - "asiarc" + "quidexea" ], "rsa.db.index": "veli", "rsa.internal.event_desc": "quatu", @@ -1728,9 +1728,9 @@ "10.39.10.155" ], "related.user": [ - "urExcept", + "ptass", "aboreetd", - "ptass" + "urExcept" ], "rsa.db.database": "teirured", "rsa.db.index": "dolorem", @@ -1828,9 +1828,9 @@ "10.71.238.250" ], "related.user": [ + "reseo", "moenimi", - "aec", - "reseo" + "aec" ], "rsa.db.index": "mac", "rsa.internal.event_desc": "quamest", @@ -1875,13 +1875,13 @@ "rum5798.home" ], "related.ip": [ - "10.226.20.199", - "10.226.101.180" + "10.226.101.180", + "10.226.20.199" ], "related.user": [ - "ritt", "rationev", - "veniamqu" + "veniamqu", + "ritt" ], "rsa.db.database": "conse", "rsa.db.index": "imveniam", @@ -1943,9 +1943,9 @@ "10.134.65.15" ], "related.user": [ - "quaUten", + "cab", "utaliqu", - "cab" + "quaUten" ], "rsa.db.database": "isciv", "rsa.db.index": "nofd", @@ -2002,8 +2002,8 @@ "10.70.147.120" ], "related.user": [ - "cidunt", "tten", + "cidunt", "emqu" ], "rsa.db.index": "eaqu", @@ -2053,9 +2053,9 @@ "10.178.242.100" ], "related.user": [ + "idid", "dqu", - "loi", - "idid" + "loi" ], "rsa.db.database": "tenatuse", "rsa.db.index": "ullamcor", @@ -2109,9 +2109,9 @@ "10.211.179.168" ], "related.user": [ - "ritati", "untincul", - "mmodoc" + "mmodoc", + "ritati" ], "rsa.db.index": "emvele", "rsa.internal.event_desc": "oluptas", @@ -2153,9 +2153,9 @@ "10.30.243.163" ], "related.user": [ - "illu", "mven", - "dolore" + "dolore", + "illu" ], "rsa.db.index": "idol", "rsa.internal.event_desc": "lore", @@ -2200,12 +2200,12 @@ "dictasun3878.internal.localhost" ], "related.ip": [ - "10.212.214.4", - "10.6.79.159" + "10.6.79.159", + "10.212.214.4" ], "related.user": [ - "amvo", "quid", + "amvo", "midestl" ], "rsa.db.database": "urExce", @@ -2267,8 +2267,8 @@ "10.237.170.202" ], "related.user": [ - "liquide", "rcit", + "liquide", "atDu" ], "rsa.db.database": "taedict", @@ -2330,8 +2330,8 @@ "10.228.118.81" ], "related.user": [ - "emoe", "tatemU", + "emoe", "itasper" ], "rsa.db.database": "toditaut", @@ -2389,13 +2389,13 @@ "esseq7889.www.invalid" ], "related.ip": [ - "10.49.71.118", - "10.234.165.130" + "10.234.165.130", + "10.49.71.118" ], "related.user": [ "henderit", - "emip", - "iuntNequ" + "iuntNequ", + "emip" ], "rsa.db.database": "veniamqu", "rsa.db.index": "atquo", @@ -2449,9 +2449,9 @@ "10.199.5.49" ], "related.user": [ - "emip", "turadipi", - "olorema" + "olorema", + "emip" ], "rsa.db.index": "ataevi", "rsa.internal.event_desc": "minim", @@ -2493,9 +2493,9 @@ "10.193.219.34" ], "related.user": [ - "uamei", + "olorem", "utlabo", - "olorem" + "uamei" ], "rsa.db.index": "nse", "rsa.internal.event_desc": "orisni", @@ -2540,12 +2540,12 @@ "tem6815.home" ], "related.ip": [ - "10.174.185.109", - "10.120.167.217" + "10.120.167.217", + "10.174.185.109" ], "related.user": [ - "dolorem", "rsp", + "dolorem", "animid" ], "rsa.db.database": "tsuntinc", @@ -2603,12 +2603,12 @@ "mporainc2064.home" ], "related.ip": [ - "10.141.213.219", - "10.117.137.159" + "10.117.137.159", + "10.141.213.219" ], "related.user": [ - "accusa", "atev", + "accusa", "ate" ], "rsa.db.database": "nibus", @@ -2666,13 +2666,13 @@ "caboNem1043.internal.home" ], "related.ip": [ - "10.166.90.130", - "10.94.224.229" + "10.94.224.229", + "10.166.90.130" ], "related.user": [ "eavol", - "etconsec", - "rem" + "rem", + "etconsec" ], "rsa.db.database": "oditempo", "rsa.db.index": "deF", @@ -2731,13 +2731,13 @@ "tatio6513.www.invalid" ], "related.ip": [ - "10.201.81.46", - "10.38.28.151" + "10.38.28.151", + "10.201.81.46" ], "related.user": [ + "mipsumqu", "incidid", - "tiumto", - "mipsumqu" + "tiumto" ], "rsa.db.database": "abor", "rsa.db.index": "adol", @@ -2796,12 +2796,12 @@ "dolori6232.api.invalid" ], "related.ip": [ - "10.214.245.95", - "10.255.28.56" + "10.255.28.56", + "10.214.245.95" ], "related.user": [ - "umdolors", "rerepre", + "umdolors", "uptatem" ], "rsa.db.database": "odt", @@ -2900,8 +2900,8 @@ "10.141.200.133" ], "related.user": [ - "iame", "ess", + "iame", "enim" ], "rsa.db.index": "nofdeFi", @@ -2945,8 +2945,8 @@ ], "related.user": [ "runtmo", - "illoi", - "ugi" + "ugi", + "illoi" ], "rsa.db.index": "eetdo", "rsa.internal.event_desc": "quaer", @@ -2991,13 +2991,13 @@ "mestq2106.api.host" ], "related.ip": [ - "10.41.89.217", - "10.39.143.155" + "10.39.143.155", + "10.41.89.217" ], "related.user": [ - "sedquiac", "tem", - "tperspic" + "tperspic", + "sedquiac" ], "rsa.db.database": "radipis", "rsa.db.index": "nse", @@ -3058,8 +3058,8 @@ "10.5.5.1" ], "related.user": [ - "minim", "CSe", + "minim", "unt" ], "rsa.db.database": "atu", @@ -3121,9 +3121,9 @@ "10.168.132.175" ], "related.user": [ - "eursinto", + "iamea", "giatquov", - "iamea" + "eursinto" ], "rsa.db.database": "ici", "rsa.db.index": "iquaUt", @@ -3177,9 +3177,9 @@ "10.123.154.17" ], "related.user": [ + "quiac", "dolorsi", - "lmo", - "quiac" + "lmo" ], "rsa.db.index": "idunt", "rsa.internal.event_desc": "usantiu", @@ -3270,9 +3270,9 @@ "10.126.205.76" ], "related.user": [ - "rsitvol", "Nemoenim", - "iati" + "iati", + "rsitvol" ], "rsa.db.index": "eFini", "rsa.internal.event_desc": "acom", @@ -3317,13 +3317,13 @@ "fic5107.home" ], "related.ip": [ - "10.169.101.161", - "10.164.66.154" + "10.164.66.154", + "10.169.101.161" ], "related.user": [ - "orissu", + "eufug", "ine", - "eufug" + "orissu" ], "rsa.db.database": "stquidol", "rsa.db.index": "imadmini", @@ -3377,8 +3377,8 @@ "10.70.83.200" ], "related.user": [ - "metco", "riat", + "metco", "ihilmole" ], "rsa.db.index": "urQuis", @@ -3424,13 +3424,13 @@ "onpr47.api.home" ], "related.ip": [ - "10.207.97.192", - "10.134.55.11" + "10.134.55.11", + "10.207.97.192" ], "related.user": [ + "madminim", "tanimid", - "mmod", - "madminim" + "mmod" ], "rsa.db.database": "tetura", "rsa.db.index": "uptasnul", @@ -3492,8 +3492,8 @@ ], "related.user": [ "eritq", - "oinBCSed", - "texplica" + "texplica", + "oinBCSed" ], "rsa.db.database": "lit", "rsa.db.index": "ritati", @@ -3550,8 +3550,8 @@ "eufugia4481.corp" ], "related.ip": [ - "10.41.232.147", - "10.61.175.217" + "10.61.175.217", + "10.41.232.147" ], "related.user": [ "runtm", @@ -3610,9 +3610,9 @@ "10.150.30.95" ], "related.user": [ - "mini", + "atnonpr", "uisnos", - "atnonpr" + "mini" ], "rsa.db.index": "smod", "rsa.internal.event_desc": "isn", @@ -3654,9 +3654,9 @@ "10.98.71.45" ], "related.user": [ - "fugitse", "CSe", - "onse" + "onse", + "fugitse" ], "rsa.db.index": "Dui", "rsa.internal.event_desc": "isci", @@ -3742,8 +3742,8 @@ "10.197.203.167" ], "related.user": [ - "iumdo", "uta", + "iumdo", "eserun" ], "rsa.db.index": "smo", @@ -3786,9 +3786,9 @@ "10.187.170.23" ], "related.user": [ - "ibusBo", + "enima", "sectetu", - "enima" + "ibusBo" ], "rsa.db.index": "uido", "rsa.internal.event_desc": "lab", @@ -3837,9 +3837,9 @@ "10.250.248.215" ], "related.user": [ - "aevitaed", "tinculpa", - "quaeratv" + "quaeratv", + "aevitaed" ], "rsa.db.database": "lica", "rsa.db.index": "uisnos", @@ -3895,8 +3895,8 @@ "osa3211.www5.example" ], "related.ip": [ - "10.147.154.118", - "10.146.57.23" + "10.146.57.23", + "10.147.154.118" ], "related.user": [ "tateveli", @@ -3997,9 +3997,9 @@ "10.154.172.82" ], "related.user": [ - "onnumqua", + "tetura", "nesci", - "tetura" + "onnumqua" ], "rsa.db.index": "oinBCSed", "rsa.internal.event_desc": "ntor", @@ -4042,8 +4042,8 @@ ], "related.user": [ "tpers", - "midestl", - "expl" + "expl", + "midestl" ], "rsa.db.index": "olu", "rsa.internal.event_desc": "odocons", @@ -4085,8 +4085,8 @@ "10.178.160.245" ], "related.user": [ - "fdeFinib", "turQuis", + "fdeFinib", "olupta" ], "rsa.db.index": "rsint", @@ -4195,13 +4195,13 @@ "nimve2787.mail.test" ], "related.ip": [ - "10.65.207.234", - "10.222.32.183" + "10.222.32.183", + "10.65.207.234" ], "related.user": [ "eve", - "itame", - "eruntmo" + "eruntmo", + "itame" ], "rsa.db.database": "udexerc", "rsa.db.index": "volup", @@ -4255,9 +4255,9 @@ "10.16.181.60" ], "related.user": [ + "olore", "gnama", - "oinven", - "olore" + "oinven" ], "rsa.db.index": "uatu", "rsa.internal.event_desc": "nderiti", @@ -4343,9 +4343,9 @@ "10.204.214.98" ], "related.user": [ + "eprehe", "porissus", - "tdolo", - "eprehe" + "tdolo" ], "rsa.db.index": "abo", "rsa.internal.event_desc": "ecte", @@ -4387,8 +4387,8 @@ "10.223.178.192" ], "related.user": [ - "etc", "evel", + "etc", "moenimip" ], "rsa.db.index": "iarchit", @@ -4434,13 +4434,13 @@ "ama6820.mail.example" ], "related.ip": [ - "10.26.137.126", - "10.26.33.181" + "10.26.33.181", + "10.26.137.126" ], "related.user": [ - "taevit", "ati", - "audant" + "audant", + "taevit" ], "rsa.db.database": "com", "rsa.db.index": "mveni", @@ -4560,13 +4560,13 @@ "lit4112.www.localhost" ], "related.ip": [ - "10.107.24.54", - "10.10.174.253" + "10.10.174.253", + "10.107.24.54" ], "related.user": [ "itinvo", - "uptasn", - "hend" + "hend", + "uptasn" ], "rsa.db.database": "lup", "rsa.db.index": "isau", @@ -4621,9 +4621,9 @@ "10.87.92.17" ], "related.user": [ + "eeufug", "luptate", - "tamr", - "eeufug" + "tamr" ], "rsa.db.index": "oreeufug", "rsa.internal.event_desc": "ura", @@ -4676,9 +4676,9 @@ "10.161.51.135" ], "related.user": [ - "Finibus", "accus", - "asper" + "asper", + "Finibus" ], "rsa.db.database": "litani", "rsa.db.index": "arch", @@ -4732,9 +4732,9 @@ "10.51.17.32" ], "related.user": [ - "itten", "mquido", - "llum" + "llum", + "itten" ], "rsa.db.index": "uscipit", "rsa.internal.event_desc": "llitani", @@ -4777,8 +4777,8 @@ ], "related.user": [ "ollita", - "mmodicon", - "cusa" + "cusa", + "mmodicon" ], "rsa.db.index": "ercitati", "rsa.internal.event_desc": "pteurs", @@ -4824,13 +4824,13 @@ "uidol6868.mail.localdomain" ], "related.ip": [ - "10.114.0.148", - "10.198.187.144" + "10.198.187.144", + "10.114.0.148" ], "related.user": [ "equatD", - "rsitamet", - "ons" + "ons", + "rsitamet" ], "rsa.db.database": "periam", "rsa.db.index": "umiurer", @@ -4888,9 +4888,9 @@ "10.61.140.120" ], "related.user": [ - "loru", + "naaliq", "equa", - "naaliq" + "loru" ], "rsa.db.index": "umfugiat", "rsa.internal.event_desc": "ora", @@ -4935,13 +4935,13 @@ "ptat4878.lan" ], "related.ip": [ - "10.149.238.108", - "10.93.24.151" + "10.93.24.151", + "10.149.238.108" ], "related.user": [ "ite", - "nven", - "sequamn" + "sequamn", + "nven" ], "rsa.db.database": "fugi", "rsa.db.index": "nesciu", @@ -4995,9 +4995,9 @@ "10.101.45.225" ], "related.user": [ - "emi", "uinesc", - "cipitla" + "cipitla", + "emi" ], "rsa.db.index": "caecat", "rsa.internal.event_desc": "tsunt", @@ -5040,9 +5040,9 @@ "10.2.204.161" ], "related.user": [ - "quela", + "eumfugia", "ore", - "eumfugia" + "quela" ], "rsa.db.index": "olup", "rsa.internal.event_desc": "quuntur", @@ -5139,8 +5139,8 @@ "10.151.110.250" ], "related.user": [ - "neavol", "tla", + "neavol", "pidatatn" ], "rsa.db.database": "itaedict", @@ -5258,9 +5258,9 @@ "10.128.102.130" ], "related.user": [ - "que", + "sequatu", "ore", - "sequatu" + "que" ], "rsa.db.index": "exerci", "rsa.internal.event_desc": "olu", @@ -5309,8 +5309,8 @@ "10.200.162.248" ], "related.user": [ - "onnu", "reseo", + "onnu", "doloremi" ], "rsa.db.database": "billo", @@ -5365,8 +5365,8 @@ "10.103.215.159" ], "related.user": [ - "volup", "apa", + "volup", "atatn" ], "rsa.db.index": "atcupi", From 189171723ce69e3a235ef16a80c870db74fba682 Mon Sep 17 00:00:00 2001 From: Blake Rouse Date: Wed, 7 Oct 2020 18:52:51 -0500 Subject: [PATCH 09/46] [Elastic Agent] Reload fleet.kibana.hosts from policy change (#21599) * Update the connected client for kibana from policy change. * Fix vet. * Add changelog. * Add protocol compare. * Rollback protocol and hosts on failure. --- x-pack/elastic-agent/CHANGELOG.next.asciidoc | 1 + .../pkg/agent/application/fleet_acker.go | 4 + .../pkg/agent/application/fleet_gateway.go | 4 + .../handler_action_policy_change.go | 107 +++++++++++++++++- .../handler_action_policy_change_test.go | 45 +++++++- .../pkg/agent/application/managed_mode.go | 16 ++- .../agent/application/managed_mode_test.go | 12 +- .../pkg/agent/storage/storage.go | 8 +- 8 files changed, 182 insertions(+), 15 deletions(-) diff --git a/x-pack/elastic-agent/CHANGELOG.next.asciidoc b/x-pack/elastic-agent/CHANGELOG.next.asciidoc index a0cd6f6291e..73dfe8ff9a4 100644 --- a/x-pack/elastic-agent/CHANGELOG.next.asciidoc +++ b/x-pack/elastic-agent/CHANGELOG.next.asciidoc @@ -33,3 +33,4 @@ - Send updating state {pull}21461[21461] - Add `elastic.agent.id` and `elastic.agent.version` to published events from filebeat and metricbeat {pull}21543[21543] - Add `upgrade` subcommand to perform upgrade of installed Elastic Agent {pull}21425[21425] +- Update `fleet.yml` and Kibana hosts when a policy change updates the Kibana hosts {pull}21599[21599] diff --git a/x-pack/elastic-agent/pkg/agent/application/fleet_acker.go b/x-pack/elastic-agent/pkg/agent/application/fleet_acker.go index ba324f2d7de..4544fa8a772 100644 --- a/x-pack/elastic-agent/pkg/agent/application/fleet_acker.go +++ b/x-pack/elastic-agent/pkg/agent/application/fleet_acker.go @@ -39,6 +39,10 @@ func newActionAcker( }, nil } +func (f *actionAcker) SetClient(client clienter) { + f.client = client +} + func (f *actionAcker) Ack(ctx context.Context, action fleetapi.Action) error { // checkin agentID := f.agentInfo.AgentID() diff --git a/x-pack/elastic-agent/pkg/agent/application/fleet_gateway.go b/x-pack/elastic-agent/pkg/agent/application/fleet_gateway.go index 4bb9d2e6280..21e7bfd4589 100644 --- a/x-pack/elastic-agent/pkg/agent/application/fleet_gateway.go +++ b/x-pack/elastic-agent/pkg/agent/application/fleet_gateway.go @@ -253,3 +253,7 @@ func (f *fleetGateway) stop() { close(f.done) f.wg.Wait() } + +func (f *fleetGateway) SetClient(client clienter) { + f.client = client +} diff --git a/x-pack/elastic-agent/pkg/agent/application/handler_action_policy_change.go b/x-pack/elastic-agent/pkg/agent/application/handler_action_policy_change.go index 81dc1444816..8821700ee74 100644 --- a/x-pack/elastic-agent/pkg/agent/application/handler_action_policy_change.go +++ b/x-pack/elastic-agent/pkg/agent/application/handler_action_policy_change.go @@ -5,17 +5,36 @@ package application import ( + "bytes" "context" "fmt" + "io" + "sort" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/info" + + "gopkg.in/yaml.v2" + + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/configuration" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/storage" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/config" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/logger" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/fleetapi" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/kibana" ) +type clientSetter interface { + SetClient(clienter) +} + type handlerPolicyChange struct { - log *logger.Logger - emitter emitterFunc + log *logger.Logger + emitter emitterFunc + agentInfo *info.AgentInfo + config *configuration.Configuration + store storage.Store + setters []clientSetter } func (h *handlerPolicyChange) Handle(ctx context.Context, a action, acker fleetAcker) error { @@ -31,9 +50,93 @@ func (h *handlerPolicyChange) Handle(ctx context.Context, a action, acker fleetA } h.log.Debugf("handlerPolicyChange: emit configuration for action %+v", a) + err = h.handleKibanaHosts(c) + if err != nil { + return err + } if err := h.emitter(c); err != nil { return err } return acker.Ack(ctx, action) } + +func (h *handlerPolicyChange) handleKibanaHosts(c *config.Config) (err error) { + cfg, err := configuration.NewFromConfig(c) + if err != nil { + return errors.New(err, "could not parse the configuration from the policy", errors.TypeConfig) + } + if kibanaEqual(h.config.Fleet.Kibana, cfg.Fleet.Kibana) { + // already the same hosts + return nil + } + + // only set protocol/hosts as that is all Fleet currently sends + prevProtocol := h.config.Fleet.Kibana.Protocol + prevHosts := h.config.Fleet.Kibana.Hosts + h.config.Fleet.Kibana.Protocol = cfg.Fleet.Kibana.Protocol + h.config.Fleet.Kibana.Hosts = cfg.Fleet.Kibana.Hosts + + // rollback on failure + defer func() { + if err != nil { + h.config.Fleet.Kibana.Protocol = prevProtocol + h.config.Fleet.Kibana.Hosts = prevHosts + } + }() + + client, err := fleetapi.NewAuthWithConfig(h.log, h.config.Fleet.AccessAPIKey, h.config.Fleet.Kibana) + if err != nil { + return errors.New( + err, "fail to create API client with updated hosts", + errors.TypeNetwork, errors.M("hosts", h.config.Fleet.Kibana.Hosts)) + } + reader, err := fleetToReader(h.agentInfo, h.config) + if err != nil { + return errors.New( + err, "fail to persist updated API client hosts", + errors.TypeUnexpected, errors.M("hosts", h.config.Fleet.Kibana.Hosts)) + } + err = h.store.Save(reader) + if err != nil { + return errors.New( + err, "fail to persist updated API client hosts", + errors.TypeFilesystem, errors.M("hosts", h.config.Fleet.Kibana.Hosts)) + } + for _, setter := range h.setters { + setter.SetClient(client) + } + return nil +} + +func kibanaEqual(k1 *kibana.Config, k2 *kibana.Config) bool { + if k1.Protocol != k2.Protocol { + return false + } + + sort.Strings(k1.Hosts) + sort.Strings(k2.Hosts) + if len(k1.Hosts) != len(k2.Hosts) { + return false + } + for i, v := range k1.Hosts { + if v != k2.Hosts[i] { + return false + } + } + return true +} + +func fleetToReader(agentInfo *info.AgentInfo, cfg *configuration.Configuration) (io.Reader, error) { + configToStore := map[string]interface{}{ + "fleet": cfg.Fleet, + "agent": map[string]interface{}{ + "id": agentInfo.AgentID(), + }, + } + data, err := yaml.Marshal(configToStore) + if err != nil { + return nil, err + } + return bytes.NewReader(data), nil +} diff --git a/x-pack/elastic-agent/pkg/agent/application/handler_action_policy_change_test.go b/x-pack/elastic-agent/pkg/agent/application/handler_action_policy_change_test.go index ce4802b68e6..c30f886b0d1 100644 --- a/x-pack/elastic-agent/pkg/agent/application/handler_action_policy_change_test.go +++ b/x-pack/elastic-agent/pkg/agent/application/handler_action_policy_change_test.go @@ -9,6 +9,10 @@ import ( "sync" "testing" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/info" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/configuration" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/storage" + "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -31,6 +35,8 @@ func (m *mockEmitter) Emitter(policy *config.Config) error { func TestPolicyChange(t *testing.T) { log, _ := logger.New("") ack := newNoopAcker() + agentInfo, _ := info.NewAgentInfo() + nullStore := &storage.NullStore{} t.Run("Receive a config change and successfully emits a raw configuration", func(t *testing.T) { emitter := &mockEmitter{} @@ -42,7 +48,14 @@ func TestPolicyChange(t *testing.T) { Policy: conf, } - handler := &handlerPolicyChange{log: log, emitter: emitter.Emitter} + cfg := configuration.DefaultConfiguration() + handler := &handlerPolicyChange{ + log: log, + emitter: emitter.Emitter, + agentInfo: agentInfo, + config: cfg, + store: nullStore, + } err := handler.Handle(context.Background(), action, ack) require.NoError(t, err) @@ -60,7 +73,14 @@ func TestPolicyChange(t *testing.T) { Policy: conf, } - handler := &handlerPolicyChange{log: log, emitter: emitter.Emitter} + cfg := configuration.DefaultConfiguration() + handler := &handlerPolicyChange{ + log: log, + emitter: emitter.Emitter, + agentInfo: agentInfo, + config: cfg, + store: nullStore, + } err := handler.Handle(context.Background(), action, ack) require.Error(t, err) @@ -69,6 +89,9 @@ func TestPolicyChange(t *testing.T) { func TestPolicyAcked(t *testing.T) { log, _ := logger.New("") + agentInfo, _ := info.NewAgentInfo() + nullStore := &storage.NullStore{} + t.Run("Config change should not ACK on error", func(t *testing.T) { tacker := &testAcker{} @@ -83,7 +106,14 @@ func TestPolicyAcked(t *testing.T) { Policy: config, } - handler := &handlerPolicyChange{log: log, emitter: emitter.Emitter} + cfg := configuration.DefaultConfiguration() + handler := &handlerPolicyChange{ + log: log, + emitter: emitter.Emitter, + agentInfo: agentInfo, + config: cfg, + store: nullStore, + } err := handler.Handle(context.Background(), action, tacker) require.Error(t, err) @@ -105,7 +135,14 @@ func TestPolicyAcked(t *testing.T) { Policy: config, } - handler := &handlerPolicyChange{log: log, emitter: emitter.Emitter} + cfg := configuration.DefaultConfiguration() + handler := &handlerPolicyChange{ + log: log, + emitter: emitter.Emitter, + agentInfo: agentInfo, + config: cfg, + store: nullStore, + } err := handler.Handle(context.Background(), action, tacker) require.NoError(t, err) diff --git a/x-pack/elastic-agent/pkg/agent/application/managed_mode.go b/x-pack/elastic-agent/pkg/agent/application/managed_mode.go index 647eae6d4e6..e38685741c3 100644 --- a/x-pack/elastic-agent/pkg/agent/application/managed_mode.go +++ b/x-pack/elastic-agent/pkg/agent/application/managed_mode.go @@ -209,12 +209,17 @@ func newManaged( acker, combinedReporter) + policyChanger := &handlerPolicyChange{ + log: log, + emitter: emit, + agentInfo: agentInfo, + config: cfg, + store: store, + setters: []clientSetter{acker}, + } actionDispatcher.MustRegister( &fleetapi.ActionPolicyChange{}, - &handlerPolicyChange{ - log: log, - emitter: emit, - }, + policyChanger, ) actionDispatcher.MustRegister( @@ -264,6 +269,9 @@ func newManaged( if err != nil { return nil, err } + // add the gateway to setters, so the gateway can be updated + // when the hosts for Kibana are updated by the policy. + policyChanger.setters = append(policyChanger.setters, gateway) managedApplication.gateway = gateway return managedApplication, nil diff --git a/x-pack/elastic-agent/pkg/agent/application/managed_mode_test.go b/x-pack/elastic-agent/pkg/agent/application/managed_mode_test.go index 65cb27547ff..4325c5ce7a6 100644 --- a/x-pack/elastic-agent/pkg/agent/application/managed_mode_test.go +++ b/x-pack/elastic-agent/pkg/agent/application/managed_mode_test.go @@ -9,11 +9,14 @@ import ( "encoding/json" "testing" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/configuration" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/application/info" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/configrequest" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/storage" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/composable" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/logger" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/fleetapi" @@ -34,6 +37,7 @@ func TestManagedModeRouting(t *testing.T) { log, _ := logger.New("") router, _ := newRouter(log, streamFn) agentInfo, _ := info.NewAgentInfo() + nullStore := &storage.NullStore{} composableCtrl, _ := composable.New(log, nil) emit, err := emitter(ctx, log, agentInfo, composableCtrl, router, &configModifiers{Decorators: []decoratorFunc{injectMonitoring}}) require.NoError(t, err) @@ -41,11 +45,15 @@ func TestManagedModeRouting(t *testing.T) { actionDispatcher, err := newActionDispatcher(ctx, log, &handlerDefault{log: log}) require.NoError(t, err) + cfg := configuration.DefaultConfiguration() actionDispatcher.MustRegister( &fleetapi.ActionPolicyChange{}, &handlerPolicyChange{ - log: log, - emitter: emit, + log: log, + emitter: emit, + agentInfo: agentInfo, + config: cfg, + store: nullStore, }, ) diff --git a/x-pack/elastic-agent/pkg/agent/storage/storage.go b/x-pack/elastic-agent/pkg/agent/storage/storage.go index b37c6e06ab3..2435311486a 100644 --- a/x-pack/elastic-agent/pkg/agent/storage/storage.go +++ b/x-pack/elastic-agent/pkg/agent/storage/storage.go @@ -20,7 +20,9 @@ import ( const perms os.FileMode = 0600 -type store interface { +// Store saves the io.Reader. +type Store interface { + // Save the io.Reader. Save(io.Reader) error } @@ -62,12 +64,12 @@ type ReplaceOnSuccessStore struct { target string replaceWith []byte - wrapped store + wrapped Store } // NewReplaceOnSuccessStore takes a target file and a replacement content and will replace the target // file content if the wrapped store execution is done without any error. -func NewReplaceOnSuccessStore(target string, replaceWith []byte, wrapped store) *ReplaceOnSuccessStore { +func NewReplaceOnSuccessStore(target string, replaceWith []byte, wrapped Store) *ReplaceOnSuccessStore { return &ReplaceOnSuccessStore{ target: target, replaceWith: replaceWith, From 53892413e05b63d588e728d3252d145bb4247a59 Mon Sep 17 00:00:00 2001 From: Ivan Fernandez Calvo Date: Thu, 8 Oct 2020 09:54:42 +0200 Subject: [PATCH 10/46] chore: add versions 7.1x (#21670) --- .ci/jobs/apm-beats-update.yml | 5 ++++- .ci/jobs/beats.yml | 5 ++++- .ci/jobs/packaging.yml | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.ci/jobs/apm-beats-update.yml b/.ci/jobs/apm-beats-update.yml index 2ae688ffab7..678a9fbcd67 100644 --- a/.ci/jobs/apm-beats-update.yml +++ b/.ci/jobs/apm-beats-update.yml @@ -18,7 +18,7 @@ discover-pr-forks-trust: 'permission' discover-pr-origin: 'merge-current' discover-tags: true - head-filter-regex: '(master|7\.[x789]|8\.\d+|PR-.*|v\d+\.\d+\.\d+)' + head-filter-regex: '(master|7\.[x789]|7\.1\d|8\.\d+|PR-.*|v\d+\.\d+\.\d+)' disable-pr-notifications: true notification-context: 'apm-beats-update' repo: 'beats' @@ -38,6 +38,9 @@ - regex-name: regex: '7\.[x789]' case-sensitive: true + - regex-name: + regex: '7\.1\d' + case-sensitive: true - regex-name: regex: '8\.\d+' case-sensitive: true diff --git a/.ci/jobs/beats.yml b/.ci/jobs/beats.yml index 6f59a9bcdf8..27095b2fecb 100644 --- a/.ci/jobs/beats.yml +++ b/.ci/jobs/beats.yml @@ -17,7 +17,7 @@ discover-pr-forks-strategy: 'merge-current' discover-pr-forks-trust: 'permission' discover-pr-origin: 'merge-current' - head-filter-regex: '(master|7\.[x789]|8\.\d+|PR-.*|v\d+\.\d+\.\d+)' + head-filter-regex: '(master|7\.[x789]|7\.1\d|8\.\d+|PR-.*|v\d+\.\d+\.\d+)' discover-tags: true notification-context: "beats-ci" repo: 'beats' @@ -38,6 +38,9 @@ - regex-name: regex: '7\.[x789]' case-sensitive: true + - regex-name: + regex: '7\.1\d' + case-sensitive: true - regex-name: regex: '8\.\d+' case-sensitive: true diff --git a/.ci/jobs/packaging.yml b/.ci/jobs/packaging.yml index fd6fb9f90c6..baa3ce45035 100644 --- a/.ci/jobs/packaging.yml +++ b/.ci/jobs/packaging.yml @@ -14,7 +14,7 @@ discover-pr-forks-trust: 'permission' discover-pr-origin: 'merge-current' discover-tags: true - head-filter-regex: '(master|7\.[x789]|8\.\d+|PR-.*|v\d+\.\d+\.\d+)' + head-filter-regex: '(master|7\.[x789]|7\.1\d|8\.\d+|PR-.*|v\d+\.\d+\.\d+)' disable-pr-notifications: true notification-context: 'beats-packaging' repo: 'beats' @@ -34,6 +34,9 @@ - regex-name: regex: '7\.[x789]' case-sensitive: true + - regex-name: + regex: '7\.1\d' + case-sensitive: true - regex-name: regex: '8\.\d+' case-sensitive: true From 47546025421fa6cfb3945799968313b18ad82783 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9mi=20V=C3=A1nyi?= Date: Thu, 8 Oct 2020 11:51:10 +0200 Subject: [PATCH 11/46] Fix flaky FSWatch/FSScanner tests (#21625) ## What does this PR do? Unit tests in for `fswatch.go` do not depend on the order of the returned events anymore. Closes #21489 --- filebeat/input/filestream/fswatch_test.go | 32 ++++--------------- .../filestream/fswatch_test_non_windows.go | 2 +- 2 files changed, 8 insertions(+), 26 deletions(-) diff --git a/filebeat/input/filestream/fswatch_test.go b/filebeat/input/filestream/fswatch_test.go index d6286a273eb..2435fad1500 100644 --- a/filebeat/input/filestream/fswatch_test.go +++ b/filebeat/input/filestream/fswatch_test.go @@ -38,8 +38,6 @@ var ( ) func TestFileScanner(t *testing.T) { - t.Skip("Flaky test: https://github.com/elastic/beats/issues/21489") - testCases := map[string]struct { paths []string excludedFiles []match.Matcher @@ -89,7 +87,7 @@ func TestFileScanner(t *testing.T) { for p, _ := range files { paths = append(paths, p) } - assert.True(t, checkIfSameContents(test.expectedFiles, paths)) + assert.ElementsMatch(t, paths, test.expectedFiles) }) } } @@ -116,26 +114,7 @@ func removeFilesOfScannerTest(t *testing.T) { } } -// only handles sets -func checkIfSameContents(one, other []string) bool { - if len(one) != len(other) { - return false - } - - mustFind := len(one) - for _, oneElem := range one { - for _, otherElem := range other { - if oneElem == otherElem { - mustFind-- - } - } - } - return mustFind == 0 -} - func TestFileWatchNewDeleteModified(t *testing.T) { - t.Skip("Flaky test: https://github.com/elastic/beats/issues/21489") - oldTs := time.Now() newTs := oldTs.Add(5 * time.Second) testCases := map[string]struct { @@ -226,10 +205,13 @@ func TestFileWatchNewDeleteModified(t *testing.T) { go w.watch(context.Background()) - for _, expectedEvent := range test.expectedEvents { - evt := w.Event() - assert.Equal(t, expectedEvent, evt) + count := len(test.expectedEvents) + actual := make([]loginp.FSEvent, count) + for i := 0; i < count; i++ { + actual[i] = w.Event() } + + assert.ElementsMatch(t, actual, test.expectedEvents) }) } } diff --git a/filebeat/input/filestream/fswatch_test_non_windows.go b/filebeat/input/filestream/fswatch_test_non_windows.go index eecfeddf930..3c316efdfc9 100644 --- a/filebeat/input/filestream/fswatch_test_non_windows.go +++ b/filebeat/input/filestream/fswatch_test_non_windows.go @@ -88,7 +88,7 @@ func TestFileScannerSymlinks(t *testing.T) { for p, _ := range files { paths = append(paths, p) } - assert.Equal(t, test.expectedFiles, paths) + assert.ElementsMatch(t, test.expectedFiles, paths) }) } } From 1f29969264b64cbcfa09df86ffc228161d4d2e3f Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Thu, 8 Oct 2020 10:57:41 +0100 Subject: [PATCH 12/46] Docker build resiliance with a retry (#21587) --- dev-tools/mage/integtest_docker.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dev-tools/mage/integtest_docker.go b/dev-tools/mage/integtest_docker.go index 2ed09db711e..8b366bad315 100644 --- a/dev-tools/mage/integtest_docker.go +++ b/dev-tools/mage/integtest_docker.go @@ -27,6 +27,7 @@ import ( "runtime" "strings" "sync" + "time" "github.com/pkg/errors" @@ -246,5 +247,17 @@ func dockerComposeBuildImages() error { os.Stderr, "docker-compose", args..., ) + + // This sleep is to avoid hitting the docker build issues when resources are not available. + if err != nil { + fmt.Println(">> Building docker images again") + time.Sleep(10) + _, err = sh.Exec( + composeEnv, + out, + os.Stderr, + "docker-compose", args..., + ) + } return err } From f344a6769eaa04c6f3fc960551b5b8a8fa8100c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Thu, 8 Oct 2020 13:16:16 +0200 Subject: [PATCH 13/46] [CI: Packaging] fix: push ubi8 images too (#21621) * fix: push ubi8 images too * chore: enhance retries Co-authored-by: Victor Martinez * chore: use variables in log * chore: add "-oss" images Co-authored-by: Victor Martinez --- .ci/packaging.groovy | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/.ci/packaging.groovy b/.ci/packaging.groovy index 26916372b70..81ff5b1994a 100644 --- a/.ci/packaging.groovy +++ b/.ci/packaging.groovy @@ -198,17 +198,30 @@ def tagAndPush(name){ tagName = "pr-${env.CHANGE_ID}" } - def oldName = "${DOCKER_REGISTRY}/beats/${name}:${libbetaVer}" - def newName = "${DOCKER_REGISTRY}/observability-ci/${name}:${tagName}" - def commitName = "${DOCKER_REGISTRY}/observability-ci/${name}:${env.GIT_BASE_COMMIT}" dockerLogin(secret: "${DOCKERELASTIC_SECRET}", registry: "${DOCKER_REGISTRY}") - retry(3){ - sh(label:'Change tag and push', script: """ - docker tag ${oldName} ${newName} - docker push ${newName} - docker tag ${oldName} ${commitName} - docker push ${commitName} - """) + + // supported image flavours + def variants = ["", "-oss", "-ubi8"] + variants.each { variant -> + def oldName = "${DOCKER_REGISTRY}/beats/${name}${variant}:${libbetaVer}" + def newName = "${DOCKER_REGISTRY}/observability-ci/${name}${variant}:${tagName}" + def commitName = "${DOCKER_REGISTRY}/observability-ci/${name}${variant}:${env.GIT_BASE_COMMIT}" + + def iterations = 0 + retryWithSleep(retries: 3, seconds: 5, backoff: true) + iterations++ + def status = sh(label:'Change tag and push', script: """ + docker tag ${oldName} ${newName} + docker push ${newName} + docker tag ${oldName} ${commitName} + docker push ${commitName} + """, returnStatus: true) + if ( status > 0 && iterations < 3) { + error('tag and push failed, retry') + } else if ( status > 0 ) { + log(level: 'WARN', text: "${name} doesn't have ${variant} docker images. See https://github.com/elastic/beats/pull/21621") + } + } } } From d5e89d961ade56b5edf1fe5059e15e64f6426071 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Thu, 8 Oct 2020 12:16:53 +0100 Subject: [PATCH 14/46] backport: add 7.10 branch (#21635) --- .backportrc.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.backportrc.json b/.backportrc.json index 3dce189d671..8bfd891160c 100644 --- a/.backportrc.json +++ b/.backportrc.json @@ -1,6 +1,6 @@ { "upstream": "elastic/beats", - "branches": [{ "name": "7.9"}, { "name": "7.8"}, { "name": "7.7"}, { "name": "7.x"}], + "branches": [ { "name": "7.x", "checked": true }, "7.10", "7.9", "7.8", "7.7"], "labels": ["backport"], "autoAssign": true, "prTitle": "Cherry-pick to {targetBranch}: {commitMessages}" From df03addd29f2d051ea54c55ba10273eaf44d2bfa Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Thu, 8 Oct 2020 14:10:28 +0200 Subject: [PATCH 15/46] Skip publisher flaky tests (#21657) --- libbeat/publisher/pipeline/controller_test.go | 8 +++++--- libbeat/publisher/pipeline/output_test.go | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/libbeat/publisher/pipeline/controller_test.go b/libbeat/publisher/pipeline/controller_test.go index fba98018894..1e400476a30 100644 --- a/libbeat/publisher/pipeline/controller_test.go +++ b/libbeat/publisher/pipeline/controller_test.go @@ -31,7 +31,8 @@ import ( "github.com/elastic/beats/v7/libbeat/publisher" "github.com/elastic/beats/v7/libbeat/publisher/queue" "github.com/elastic/beats/v7/libbeat/publisher/queue/memqueue" - "github.com/elastic/beats/v7/libbeat/tests/resources" + + //"github.com/elastic/beats/v7/libbeat/tests/resources" "github.com/stretchr/testify/require" ) @@ -46,8 +47,9 @@ func TestOutputReload(t *testing.T) { t.Run(name, func(t *testing.T) { testutil.SeedPRNG(t) - goroutines := resources.NewGoroutinesChecker() - defer goroutines.Check(t) + // Flaky check: https://github.com/elastic/beats/issues/21656 + //goroutines := resources.NewGoroutinesChecker() + //defer goroutines.Check(t) err := quick.Check(func(q uint) bool { numEventsToPublish := 15000 + (q % 500) // 15000 to 19999 diff --git a/libbeat/publisher/pipeline/output_test.go b/libbeat/publisher/pipeline/output_test.go index 5be34fa9436..7bde6e137ab 100644 --- a/libbeat/publisher/pipeline/output_test.go +++ b/libbeat/publisher/pipeline/output_test.go @@ -95,6 +95,8 @@ func TestMakeClientWorker(t *testing.T) { } func TestReplaceClientWorker(t *testing.T) { + t.Skip("Flaky test: https://github.com/elastic/beats/issues/17965") + tests := map[string]func(mockPublishFn) outputs.Client{ "client": newMockClient, "network_client": newMockNetworkClient, From 38c6a7026c5eaf419fc2f000f6b3b5cab9105f16 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Thu, 8 Oct 2020 14:08:57 +0100 Subject: [PATCH 16/46] [CI] Support Windows-2016 in pipeline 2.0 (#21337) --- .ci/jobs/beats-windows-mbp.yml | 56 -- .ci/windows.groovy | 874 --------------------------- auditbeat/Jenkinsfile.yml | 11 + filebeat/Jenkinsfile.yml | 1 + heartbeat/Jenkinsfile.yml | 11 + metricbeat/Jenkinsfile.yml | 11 + packetbeat/Jenkinsfile.yml | 11 + winlogbeat/Jenkinsfile.yml | 11 + x-pack/auditbeat/Jenkinsfile.yml | 11 + x-pack/elastic-agent/Jenkinsfile.yml | 11 + x-pack/filebeat/Jenkinsfile.yml | 11 + x-pack/functionbeat/Jenkinsfile.yml | 11 + x-pack/metricbeat/Jenkinsfile.yml | 11 + x-pack/packetbeat/Jenkinsfile.yml | 11 + x-pack/winlogbeat/Jenkinsfile.yml | 11 + 15 files changed, 133 insertions(+), 930 deletions(-) delete mode 100644 .ci/jobs/beats-windows-mbp.yml delete mode 100644 .ci/windows.groovy diff --git a/.ci/jobs/beats-windows-mbp.yml b/.ci/jobs/beats-windows-mbp.yml deleted file mode 100644 index 7ea26c9ba18..00000000000 --- a/.ci/jobs/beats-windows-mbp.yml +++ /dev/null @@ -1,56 +0,0 @@ ---- -- job: - name: Beats/beats-windows-mbp - display-name: Beats Pipeline for Windows - description: Jenkins pipeline for the Beats project running on windows agents. - view: Beats - project-type: multibranch - script-path: .ci/windows.groovy - scm: - - github: - branch-discovery: no-pr - discover-pr-forks-strategy: merge-current - discover-pr-forks-trust: permission - discover-pr-origin: merge-current - discover-tags: false - # Run MBP for the master branch and PRs - head-filter-regex: '(master|PR-.*)' - notification-context: 'beats-ci/windows' - repo: beats - repo-owner: elastic - credentials-id: github-app-beats-ci - ssh-checkout: - credentials: f6c7695a-671e-4f4f-a331-acdce44ff9ba - build-strategies: - - tags: - ignore-tags-older-than: -1 - ignore-tags-newer-than: -1 - - regular-branches: true - - change-request: - ignore-target-only-changes: true - property-strategies: - # Builds for PRs won't be triggered automatically. - # Only the master branch will be triggered automatically. - named-branches: - defaults: - - suppress-scm-triggering: true - exceptions: - - exception: - branch-name: master - properties: - - suppress-scm-triggering: false - clean: - after: true - before: true - prune: true - shallow-clone: true - depth: 10 - do-not-fetch-tags: true - submodule: - disable: false - recursive: true - parent-credentials: true - timeout: 100 - timeout: '15' - use-author: true - wipe-workspace: 'True' diff --git a/.ci/windows.groovy b/.ci/windows.groovy deleted file mode 100644 index 92826e80926..00000000000 --- a/.ci/windows.groovy +++ /dev/null @@ -1,874 +0,0 @@ -#!/usr/bin/env groovy - -@Library('apm@current') _ - -import groovy.transform.Field - -/** - This is required to store the stashed id with the test results to be digested with runbld -*/ -@Field def stashedTestReports = [:] - -/** - List of supported windows versions to be tested with - NOTE: - - 'windows-10' is too slow - - 'windows-2012-r2', 'windows-2008-r2', 'windows-7', 'windows-7-32-bit' are disabled - since we are working on releasing each windows version incrementally. -*/ -@Field def windowsVersions = ['windows-2019', 'windows-2016', 'windows-2012-r2'] - -pipeline { - agent { label 'ubuntu && immutable' } - environment { - BASE_DIR = 'src/github.com/elastic/beats' - GOX_FLAGS = "-arch amd64" - DOCKER_COMPOSE_VERSION = "1.21.0" - TERRAFORM_VERSION = "0.12.24" - PIPELINE_LOG_LEVEL = "INFO" - DOCKERELASTIC_SECRET = 'secret/observability-team/ci/docker-registry/prod' - DOCKER_REGISTRY = 'docker.elastic.co' - AWS_ACCOUNT_SECRET = 'secret/observability-team/ci/elastic-observability-aws-account-auth' - RUNBLD_DISABLE_NOTIFICATIONS = 'true' - JOB_GCS_BUCKET = 'beats-ci-temp' - JOB_GCS_CREDENTIALS = 'beats-ci-gcs-plugin' - } - options { - timeout(time: 2, unit: 'HOURS') - buildDiscarder(logRotator(numToKeepStr: '20', artifactNumToKeepStr: '20', daysToKeepStr: '30')) - timestamps() - ansiColor('xterm') - disableResume() - durabilityHint('PERFORMANCE_OPTIMIZED') - quietPeriod(10) - rateLimitBuilds(throttle: [count: 60, durationName: 'hour', userBoost: true]) - } - triggers { - issueCommentTrigger('(?i).*(?:jenkins\\W+)?run\\W+(?:the\\W+)?tests(?:\\W+please)?.*') - } - parameters { - booleanParam(name: 'runAllStages', defaultValue: false, description: 'Allow to run all stages.') - booleanParam(name: 'windowsTest', defaultValue: true, description: 'Allow Windows stages.') - booleanParam(name: 'macosTest', defaultValue: true, description: 'Allow macOS stages.') - - booleanParam(name: 'allCloudTests', defaultValue: false, description: 'Run all cloud integration tests.') - booleanParam(name: 'awsCloudTests', defaultValue: false, description: 'Run AWS cloud integration tests.') - string(name: 'awsRegion', defaultValue: 'eu-central-1', description: 'Default AWS region to use for testing.') - - booleanParam(name: 'debug', defaultValue: false, description: 'Allow debug logging for Jenkins steps') - booleanParam(name: 'dry_run', defaultValue: false, description: 'Skip build steps, it is for testing pipeline flow') - } - stages { - /** - Checkout the code and stash it, to use it on other stages. - */ - stage('Checkout') { - options { skipDefaultCheckout() } - steps { - pipelineManager([ cancelPreviousRunningBuilds: [ when: 'PR' ] ]) - deleteDir() - gitCheckout(basedir: "${BASE_DIR}", githubNotifyFirstTimeContributor: true) - stashV2(name: 'source', bucket: "${JOB_GCS_BUCKET}", credentialsId: "${JOB_GCS_CREDENTIALS}") - dir("${BASE_DIR}"){ - loadConfigEnvVars() - } - whenTrue(params.debug){ - dumpFilteredEnvironment() - } - } - } - stage('Lint'){ - options { skipDefaultCheckout() } - steps { - // NOTE: commented to run the windows pipeline a bit faster. - // when required then it can be enabled. - // makeTarget("Lint", "check") - echo 'SKIPPED' - } - } - stage('Build and Test Windows'){ - failFast false - parallel { - stage('Elastic Agent x-pack Windows'){ - options { skipDefaultCheckout() } - when { - beforeAgent true - expression { - return params.windowsTest - // NOTE: commented to run all the windows stages. - //return env.BUILD_ELASTIC_AGENT_XPACK != "false" && params.windowsTest - } - } - steps { - mageTargetWin("Elastic Agent x-pack Windows Unit test", "x-pack/elastic-agent", "build unitTest") - } - } - stage('Filebeat Windows'){ - options { skipDefaultCheckout() } - when { - beforeAgent true - expression { - return params.windowsTest - // NOTE: commented to run all the windows stages. - //return env.BUILD_FILEBEAT != "false" && params.windowsTest - } - } - steps { - mageTargetWin("Filebeat oss Windows Unit test", "filebeat", "build unitTest") - } - } - stage('Filebeat x-pack Windows'){ - options { skipDefaultCheckout() } - when { - beforeAgent true - expression { - return params.windowsTest - // NOTE: commented to run all the windows stages. - //return env.BUILD_FILEBEAT_XPACK != "false" && params.windowsTest - } - } - steps { - mageTargetWin("Filebeat x-pack Windows", "x-pack/filebeat", "build unitTest") - } - } - stage('Heartbeat'){ - options { skipDefaultCheckout() } - when { - beforeAgent true - expression { - return params.windowsTest - // NOTE: commented to run all the windows stages. - //return env.BUILD_HEARTBEAT != "false" - } - } - stages { - stage('Heartbeat Windows'){ - options { skipDefaultCheckout() } - when { - beforeAgent true - expression { - return params.windowsTest - } - } - steps { - mageTargetWin("Heartbeat oss Windows Unit test", "heartbeat", "build unitTest") - } - } - } - } - stage('Auditbeat oss Windows'){ - options { skipDefaultCheckout() } - when { - beforeAgent true - expression { - return params.windowsTest - // NOTE: commented to run all the windows stages. - //return env.BUILD_AUDITBEAT != "false" && params.windowsTest - } - } - steps { - mageTargetWin("Auditbeat oss Windows Unit test", "auditbeat", "build unitTest") - } - } - stage('Auditbeat x-pack Windows'){ - options { skipDefaultCheckout() } - when { - beforeAgent true - expression { - return params.windowsTest - // NOTE: commented to run all the windows stages. - //return env.BUILD_AUDITBEAT_XPACK != "false" && params.windowsTest - } - } - steps { - mageTargetWin("Auditbeat x-pack Windows", "x-pack/auditbeat", "build unitTest") - } - } - stage('Metricbeat Windows'){ - options { skipDefaultCheckout() } - when { - beforeAgent true - expression { - return params.windowsTest - // NOTE: commented to run all the windows stages. - //return env.BUILD_METRICBEAT != "false" && params.windowsTest - } - } - steps { - mageTargetWin("Metricbeat Windows Unit test", "metricbeat", "build unitTest") - } - } - stage('Metricbeat x-pack Windows'){ - options { skipDefaultCheckout() } - when { - beforeAgent true - expression { - return params.windowsTest - // NOTE: commented to run all the windows stages. - //return env.BUILD_METRICBEAT_XPACK != "false" && params.windowsTest - } - } - steps { - mageTargetWin("Metricbeat x-pack Windows", "x-pack/metricbeat", "build unitTest") - } - } - stage('Winlogbeat'){ - options { skipDefaultCheckout() } - when { - beforeAgent true - expression { - return params.windowsTest - // NOTE: commented to run all the windows stages. - //return env.BUILD_WINLOGBEAT != "false" - } - } - stages { - stage('Winlogbeat Windows'){ - options { skipDefaultCheckout() } - when { - beforeAgent true - expression { - return params.windowsTest - } - } - steps { - mageTargetWin("Winlogbeat Windows Unit test", "winlogbeat", "build unitTest") - } - } - } - } - stage('Winlogbeat Windows x-pack'){ - options { skipDefaultCheckout() } - when { - beforeAgent true - expression { - return params.windowsTest - // NOTE: commented to run all the windows stages. - //return params.windowsTest && env.BUILD_WINLOGBEAT_XPACK != "false" - } - } - steps { - mageTargetWin("Winlogbeat x-pack Windows", "x-pack/winlogbeat", "build unitTest") - } - } - stage('Functionbeat'){ - options { skipDefaultCheckout() } - when { - beforeAgent true - expression { - return params.windowsTest - // NOTE: commented to run all the windows stages. - //return env.BUILD_FUNCTIONBEAT_XPACK != "false" - } - } - stages { - stage('Functionbeat Windows x-pack'){ - options { skipDefaultCheckout() } - when { - beforeAgent true - expression { - return params.windowsTest - } - } - steps { - mageTargetWin("Functionbeat x-pack Windows Unit test", "x-pack/functionbeat", "build unitTest") - } - } - } - } - } - } - } - post { - always { - runbld() - } - cleanup { - notifyBuildResult(prComment: true) - } - } -} - -def delete() { - dir("${env.BASE_DIR}") { - fixPermissions("${WORKSPACE}") - } - deleteDir() -} - -def fixPermissions(location) { - sh(label: 'Fix permissions', script: """#!/usr/bin/env bash - source ./dev-tools/common.bash - docker_setup - script/fix_permissions.sh ${location}""", returnStatus: true) -} - -def makeTarget(String context, String target, boolean clean = true) { - withGithubNotify(context: "${context}") { - withBeatsEnv(true) { - whenTrue(params.debug) { - dumpFilteredEnvironment() - dumpMage() - } - sh(label: "Make ${target}", script: "make ${target}") - whenTrue(clean) { - fixPermissions("${HOME}") - } - } - } -} - -def mageTarget(String context, String directory, String target) { - withGithubNotify(context: "${context}") { - withBeatsEnv(true) { - whenTrue(params.debug) { - dumpFilteredEnvironment() - dumpMage() - } - - def verboseFlag = params.debug ? "-v" : "" - dir(directory) { - sh(label: "Mage ${target}", script: "mage ${verboseFlag} ${target}") - } - } - } -} - -def mageTargetWin(String context, String directory, String target) { - withGithubNotify(context: "${context}") { - def tasks = [:] - windowsVersions.each { os -> - tasks["${context}-${os}"] = mageTargetWin(context, directory, target, os) - } - parallel(tasks) - } -} - -def mageTargetWin(String context, String directory, String target, String label) { - return { - log(level: 'INFO', text: "context=${context} directory=${directory} target=${target} os=${label}") - def immutable = label.equals('windows-7-32-bit') ? 'windows-immutable-32-bit' : 'windows-immutable' - - // NOTE: skip filebeat with windows-2016/2012-r2 since there are some test failures. - // See https://github.com/elastic/beats/issues/19787 https://github.com/elastic/beats/issues/19641 - if (directory.equals('filebeat') && (label.equals('windows-2016') || label.equals('windows-2012-r2'))) { - log(level: 'WARN', text: "Skipped stage for the 'filebeat' with '${label}' as long as there are test failures to be analysed.") - } else { - node("${immutable} && ${label}"){ - withBeatsEnvWin() { - whenTrue(params.debug) { - dumpFilteredEnvironment() - dumpMageWin() - } - - def verboseFlag = params.debug ? "-v" : "" - dir(directory) { - bat(label: "Mage ${target}", script: "mage ${verboseFlag} ${target}") - } - } - } - } - } -} - -def withBeatsEnv(boolean archive, Closure body) { - def os = goos() - def goRoot = "${env.WORKSPACE}/.gvm/versions/go${GO_VERSION}.${os}.amd64" - - withEnv([ - "HOME=${env.WORKSPACE}", - "GOPATH=${env.WORKSPACE}", - "GOROOT=${goRoot}", - "PATH=${env.WORKSPACE}/bin:${goRoot}/bin:${env.PATH}", - "MAGEFILE_CACHE=${WORKSPACE}/.magefile", - "TEST_COVERAGE=true", - "RACE_DETECTOR=true", - "PYTHON_ENV=${WORKSPACE}/python-env", - "TEST_TAGS=${env.TEST_TAGS},oracle", - "DOCKER_PULL=0", - ]) { - deleteDir() - unstashV2(name: 'source', bucket: "${JOB_GCS_BUCKET}", credentialsId: "${JOB_GCS_CREDENTIALS}") - if(isDockerInstalled()){ - dockerLogin(secret: "${DOCKERELASTIC_SECRET}", registry: "${DOCKER_REGISTRY}") - } - dir("${env.BASE_DIR}") { - installTools() - // TODO (2020-04-07): This is a work-around to fix the Beat generator tests. - // See https://github.com/elastic/beats/issues/17787. - setGitConfig() - try { - if(!params.dry_run){ - body() - } - } finally { - if (archive) { - catchError(buildResult: 'SUCCESS', stageResult: 'UNSTABLE') { - junitAndStore(allowEmptyResults: true, keepLongStdio: true, testResults: "**/build/TEST*.xml") - archiveArtifacts(allowEmptyArchive: true, artifacts: '**/build/TEST*.out') - } - } - reportCoverage() - } - } - } -} - -def withBeatsEnvWin(Closure body) { - final String chocoPath = 'C:\\ProgramData\\chocolatey\\bin' - final String chocoPython3Path = 'C:\\Python38;C:\\Python38\\Scripts' - // NOTE: to support Windows 7 32 bits the arch in the go context path is required. - def arch = is32bit() ? '386' : 'amd64' - def goRoot = "${env.USERPROFILE}\\.gvm\\versions\\go${GO_VERSION}.windows.${arch}" - - withEnv([ - "HOME=${env.WORKSPACE}", - "DEV_ARCH=${arch}", - "DEV_OS=windows", - "GOPATH=${env.WORKSPACE}", - "GOROOT=${goRoot}", - "PATH=${env.WORKSPACE}\\bin;${goRoot}\\bin;${chocoPath};${chocoPython3Path};C:\\tools\\mingw64\\bin;${env.PATH}", - "MAGEFILE_CACHE=${env.WORKSPACE}\\.magefile", - "TEST_COVERAGE=true", - "RACE_DETECTOR=true", - ]){ - deleteDir() - unstashV2(name: 'source', bucket: "${JOB_GCS_BUCKET}", credentialsId: "${JOB_GCS_CREDENTIALS}") - dir("${env.BASE_DIR}"){ - installTools() - try { - if(!params.dry_run){ - body() - } - } finally { - catchError(buildResult: 'SUCCESS', stageResult: 'UNSTABLE') { - junitAndStore(allowEmptyResults: true, keepLongStdio: true, testResults: "**\\build\\TEST*.xml") - archiveArtifacts(allowEmptyArchive: true, artifacts: '**\\build\\TEST*.out') - } - } - } - } -} - -def installTools() { - def i = 2 // Number of retries - if(isUnix()) { - retry(i) { sh(label: "Install Go ${GO_VERSION}", script: ".ci/scripts/install-go.sh") } - retry(i) { sh(label: "Install docker-compose ${DOCKER_COMPOSE_VERSION}", script: ".ci/scripts/install-docker-compose.sh") } - retry(i) { sh(label: "Install Terraform ${TERRAFORM_VERSION}", script: ".ci/scripts/install-terraform.sh") } - retry(i) { sh(label: "Install Mage", script: "make mage") } - } else { - retry(i) { bat(label: "Install Go/Mage/Python ${GO_VERSION}", script: ".ci/scripts/install-tools.bat") } - } -} - -def is32bit(){ - def labels = env.NODE_LABELS - return labels.contains('i386') -} - -def goos(){ - def labels = env.NODE_LABELS - - if (labels.contains('linux')) { - return 'linux' - } else if (labels.contains('windows')) { - return 'windows' - } else if (labels.contains('darwin')) { - return 'darwin' - } - - error("Unhandled OS name in NODE_LABELS: " + labels) -} - -def dumpMage(){ - echo "### MAGE DUMP ###" - sh(label: "Dump mage variables", script: "mage dumpVariables") - echo "### END MAGE DUMP ###" -} - -def dumpMageWin(){ - echo "### MAGE DUMP ###" - bat(label: "Dump mage variables", script: "mage dumpVariables") - echo "### END MAGE DUMP ###" -} - -def dumpFilteredEnvironment(){ - echo "### ENV DUMP ###" - echo "PATH: ${env.PATH}" - echo "HOME: ${env.HOME}" - echo "USERPROFILE: ${env.USERPROFILE}" - echo "BUILD_DIR: ${env.BUILD_DIR}" - echo "COVERAGE_DIR: ${env.COVERAGE_DIR}" - echo "BEATS: ${env.BEATS}" - echo "PROJECTS: ${env.PROJECTS}" - echo "PROJECTS_ENV: ${env.PROJECTS_ENV}" - echo "PYTHON_ENV: ${env.PYTHON_ENV}" - echo "PYTHON_EXE: ${env.PYTHON_EXE}" - echo "PYTHON_ENV_EXE: ${env.PYTHON_ENV_EXE}" - echo "VENV_PARAMS: ${env.VENV_PARAMS}" - echo "FIND: ${env.FIND}" - echo "GOLINT: ${env.GOLINT}" - echo "GOLINT_REPO: ${env.GOLINT_REPO}" - echo "REVIEWDOG: ${env.REVIEWDOG}" - echo "REVIEWDOG_OPTIONS: ${env.REVIEWDOG_OPTIONS}" - echo "REVIEWDOG_REPO: ${env.REVIEWDOG_REPO}" - echo "XPACK_SUFFIX: ${env.XPACK_SUFFIX}" - echo "PKG_BUILD_DIR: ${env.PKG_BUILD_DIR}" - echo "PKG_UPLOAD_DIR: ${env.PKG_UPLOAD_DIR}" - echo "COVERAGE_TOOL: ${env.COVERAGE_TOOL}" - echo "COVERAGE_TOOL_REPO: ${env.COVERAGE_TOOL_REPO}" - echo "TESTIFY_TOOL_REPO: ${env.TESTIFY_TOOL_REPO}" - echo "NOW: ${env.NOW}" - echo "GOBUILD_FLAGS: ${env.GOBUILD_FLAGS}" - echo "GOIMPORTS: ${env.GOIMPORTS}" - echo "GOIMPORTS_REPO: ${env.GOIMPORTS_REPO}" - echo "GOIMPORTS_LOCAL_PREFIX: ${env.GOIMPORTS_LOCAL_PREFIX}" - echo "PROCESSES: ${env.PROCESSES}" - echo "TIMEOUT: ${env.TIMEOUT}" - echo "PYTHON_TEST_FILES: ${env.PYTHON_TEST_FILES}" - echo "PYTEST_ADDOPTS: ${env.PYTEST_ADDOPTS}" - echo "PYTEST_OPTIONS: ${env.PYTEST_OPTIONS}" - echo "TEST_ENVIRONMENT: ${env.TEST_ENVIRONMENT}" - echo "SYSTEM_TESTS: ${env.SYSTEM_TESTS}" - echo "STRESS_TESTS: ${env.STRESS_TESTS}" - echo "STRESS_TEST_OPTIONS: ${env.STRESS_TEST_OPTIONS}" - echo "TEST_TAGS: ${env.TEST_TAGS}" - echo "GOX_OS: ${env.GOX_OS}" - echo "GOX_OSARCH: ${env.GOX_OSARCH}" - echo "GOX_FLAGS: ${env.GOX_FLAGS}" - echo "TESTING_ENVIRONMENT: ${env.TESTING_ENVIRONMENT}" - echo "BEAT_VERSION: ${env.BEAT_VERSION}" - echo "COMMIT_ID: ${env.COMMIT_ID}" - echo "DOCKER_COMPOSE_PROJECT_NAME: ${env.DOCKER_COMPOSE_PROJECT_NAME}" - echo "DOCKER_COMPOSE: ${env.DOCKER_COMPOSE}" - echo "DOCKER_CACHE: ${env.DOCKER_CACHE}" - echo "GOPACKAGES_COMMA_SEP: ${env.GOPACKAGES_COMMA_SEP}" - echo "PIP_INSTALL_PARAMS: ${env.PIP_INSTALL_PARAMS}" - echo "### END ENV DUMP ###" -} - -def k8sTest(versions){ - versions.each{ v -> - stage("k8s ${v}"){ - withEnv(["K8S_VERSION=${v}", "KIND_VERSION=v0.7.0", "KUBECONFIG=${env.WORKSPACE}/kubecfg"]){ - withGithubNotify(context: "K8s ${v}") { - withBeatsEnv(false) { - sh(label: "Install kind", script: ".ci/scripts/install-kind.sh") - sh(label: "Install kubectl", script: ".ci/scripts/install-kubectl.sh") - sh(label: "Setup kind", script: ".ci/scripts/kind-setup.sh") - sh(label: "Integration tests", script: "MODULE=kubernetes make -C metricbeat integration-tests") - sh(label: "Deploy to kubernetes",script: "make -C deploy/kubernetes test") - sh(label: 'Delete cluster', script: 'kind delete cluster') - } - } - } - } - } -} - -def reportCoverage(){ - catchError(buildResult: 'SUCCESS', stageResult: 'UNSTABLE') { - retry(2){ - sh(label: 'Report to Codecov', script: ''' - curl -sSLo codecov https://codecov.io/bash - for i in auditbeat filebeat heartbeat libbeat metricbeat packetbeat winlogbeat journalbeat - do - FILE="${i}/build/coverage/full.cov" - if [ -f "${FILE}" ]; then - bash codecov -f "${FILE}" - fi - done - ''') - } - } -} - -// isChanged treats the patterns as regular expressions. In order to check if -// any file in a directoy is modified use `^/.*`. -def isChanged(patterns){ - return ( - params.runAllStages - || isGitRegionMatch(patterns: patterns, comparator: 'regexp') - ) -} - -def isChangedOSSCode(patterns) { - def allPatterns = [ - "^Jenkinsfile", - "^go.mod", - "^libbeat/.*", - "^testing/.*", - "^dev-tools/.*", - "^\\.ci/scripts/.*", - ] - allPatterns.addAll(patterns) - return isChanged(allPatterns) -} - -def isChangedXPackCode(patterns) { - def allPatterns = [ - "^Jenkinsfile", - "^go.mod", - "^libbeat/.*", - "^dev-tools/.*", - "^testing/.*", - "^x-pack/libbeat/.*", - "^\\.ci/scripts/.*", - ] - allPatterns.addAll(patterns) - return isChanged(allPatterns) -} - -// withCloudTestEnv executes a closure with credentials for cloud test -// environments. -def withCloudTestEnv(Closure body) { - def maskedVars = [] - def testTags = "${env.TEST_TAGS}" - - // AWS - if (params.allCloudTests || params.awsCloudTests) { - testTags = "${testTags},aws" - def aws = getVaultSecret(secret: "${AWS_ACCOUNT_SECRET}").data - if (!aws.containsKey('access_key')) { - error("${AWS_ACCOUNT_SECRET} doesn't contain 'access_key'") - } - if (!aws.containsKey('secret_key')) { - error("${AWS_ACCOUNT_SECRET} doesn't contain 'secret_key'") - } - maskedVars.addAll([ - [var: "AWS_REGION", password: params.awsRegion], - [var: "AWS_ACCESS_KEY_ID", password: aws.access_key], - [var: "AWS_SECRET_ACCESS_KEY", password: aws.secret_key], - ]) - } - - withEnv([ - "TEST_TAGS=${testTags}", - ]) { - withEnvMask(vars: maskedVars) { - body() - } - } -} - -def terraformInit(String directory) { - dir(directory) { - sh(label: "Terraform Init on ${directory}", script: "terraform init") - } -} - -def terraformApply(String directory) { - terraformInit(directory) - dir(directory) { - sh(label: "Terraform Apply on ${directory}", script: "terraform apply -auto-approve") - } -} - -// Start testing environment on cloud using terraform. Terraform files are -// stashed so they can be used by other stages. They are also archived in -// case manual cleanup is needed. -// -// Example: -// startCloudTestEnv('x-pack-metricbeat', [ -// [cond: params.awsCloudTests, dir: 'x-pack/metricbeat/module/aws'], -// ]) -// ... -// terraformCleanup('x-pack-metricbeat', 'x-pack/metricbeat') -def startCloudTestEnv(String name, environments = []) { - withCloudTestEnv() { - withBeatsEnv(false) { - def runAll = params.runAllCloudTests - try { - for (environment in environments) { - if (environment.cond || runAll) { - retry(2) { - terraformApply(environment.dir) - } - } - } - } finally { - // Archive terraform states in case manual cleanup is needed. - archiveArtifacts(allowEmptyArchive: true, artifacts: '**/terraform.tfstate') - } - stash(name: "terraform-${name}", allowEmpty: true, includes: '**/terraform.tfstate,**/.terraform/**') - } - } -} - - -// Looks for all terraform states in directory and runs terraform destroy for them, -// it uses terraform states previously stashed by startCloudTestEnv. -def terraformCleanup(String stashName, String directory) { - stage("Remove cloud scenarios in ${directory}"){ - withCloudTestEnv() { - withBeatsEnv(false) { - unstash("terraform-${stashName}") - retry(2) { - sh(label: "Terraform Cleanup", script: ".ci/scripts/terraform-cleanup.sh ${directory}") - } - } - } - } -} - -def loadConfigEnvVars(){ - def empty = [] - env.GO_VERSION = readFile(".go-version").trim() - - withEnv(["HOME=${env.WORKSPACE}"]) { - retry(2) { sh(label: "Install Go ${env.GO_VERSION}", script: ".ci/scripts/install-go.sh") } - } - - // Libbeat is the core framework of Beats. It has no additional dependencies - // on other projects in the Beats repository. - env.BUILD_LIBBEAT = isChangedOSSCode(empty) - env.BUILD_LIBBEAT_XPACK = isChangedXPackCode(empty) - - // Auditbeat depends on metricbeat as framework, but does not include any of - // the modules from Metricbeat. - // The Auditbeat x-pack build contains all functionality from OSS Auditbeat. - env.BUILD_AUDITBEAT = isChangedOSSCode(getVendorPatterns('auditbeat')) - env.BUILD_AUDITBEAT_XPACK = isChangedXPackCode(getVendorPatterns('x-pack/auditbeat')) - - // Dockerlogbeat is a standalone Beat that only relies on libbeat. - env.BUILD_DOCKERLOGBEAT_XPACK = isChangedXPackCode(getVendorPatterns('x-pack/dockerlogbeat')) - - // Filebeat depends on libbeat only. - // The Filebeat x-pack build contains all functionality from OSS Filebeat. - env.BUILD_FILEBEAT = isChangedOSSCode(getVendorPatterns('filebeat')) - env.BUILD_FILEBEAT_XPACK = isChangedXPackCode(getVendorPatterns('x-pack/filebeat')) - - // Metricbeat depends on libbeat only. - // The Metricbeat x-pack build contains all functionality from OSS Metricbeat. - env.BUILD_METRICBEAT = isChangedOSSCode(getVendorPatterns('metricbeat')) - env.BUILD_METRICBEAT_XPACK = isChangedXPackCode(getVendorPatterns('x-pack/metricbeat')) - - // Functionbeat is a standalone beat that depends on libbeat only. - // Functionbeat is available as x-pack build only. - env.BUILD_FUNCTIONBEAT_XPACK = isChangedXPackCode(getVendorPatterns('x-pack/functionbeat')) - - // Heartbeat depends on libbeat only. - // The Heartbeat x-pack build contains all functionality from OSS Heartbeat. - env.BUILD_HEARTBEAT = isChangedOSSCode(getVendorPatterns('heartbeat')) - env.BUILD_HEARTBEAT_XPACK = isChangedXPackCode(getVendorPatterns('x-pack/heartbeat')) - - // Journalbeat depends on libbeat only. - // The Journalbeat x-pack build contains all functionality from OSS Journalbeat. - env.BUILD_JOURNALBEAT = isChangedOSSCode(getVendorPatterns('journalbeat')) - env.BUILD_JOURNALBEAT_XPACK = isChangedXPackCode(getVendorPatterns('x-pack/journalbeat')) - - // Packetbeat depends on libbeat only. - // The Packetbeat x-pack build contains all functionality from OSS Packetbeat. - env.BUILD_PACKETBEAT = isChangedOSSCode(getVendorPatterns('packetbeat')) - env.BUILD_PACKETBEAT_XPACK = isChangedXPackCode(getVendorPatterns('x-pack/packetbeat')) - - // Winlogbeat depends on libbeat only. - // The Winlogbeat x-pack build contains all functionality from OSS Winlogbeat. - env.BUILD_WINLOGBEAT = isChangedOSSCode(getVendorPatterns('winlogbeat')) - env.BUILD_WINLOGBEAT_XPACK = isChangedXPackCode(getVendorPatterns('x-pack/winlogbeat')) - - // Elastic-agent is a self-contained product, that depends on libbeat only. - // The agent acts as a supervisor for other Beats like Filebeat or Metricbeat. - // The agent is available as x-pack build only. - env.BUILD_ELASTIC_AGENT_XPACK = isChangedXPackCode(getVendorPatterns('x-pack/elastic-agent')) - - // The Kubernetes test use Filebeat and Metricbeat, but only need to be run - // if the deployment scripts have been updated. No Beats specific testing is - // involved. - env.BUILD_KUBERNETES = isChanged(["^deploy/kubernetes/.*"]) - - def generatorPatterns = ['^generator/.*'] - generatorPatterns.addAll(getVendorPatterns('generator/common/beatgen')) - generatorPatterns.addAll(getVendorPatterns('metricbeat/beater')) - env.BUILD_GENERATOR = isChangedOSSCode(generatorPatterns) - - // Skip all the stages for changes only related to the documentation - env.ONLY_DOCS = isDocChangedOnly() - - // Run the ITs by running only if the changeset affects a specific module. - // For such, it's required to look for changes under the module folder and exclude anything else - // such as ascidoc and png files. - env.MODULE = getGitMatchingGroup(pattern: '[a-z0-9]+beat\\/module\\/([^\\/]+)\\/.*', exclude: '^(((?!\\/module\\/).)*$|.*\\.asciidoc|.*\\.png)') -} - -/** - This method verifies if the changeset for the current pull request affect only changes related - to documentation, such as asciidoc and png files. -*/ -def isDocChangedOnly(){ - if (params.runAllStages || !env.CHANGE_ID?.trim()) { - log(level: 'INFO', text: 'Speed build for docs only is disabled for branches/tags or when forcing with the runAllStages parameter.') - return 'false' - } else { - log(level: "INFO", text: 'Check if the speed build for docs is enabled.') - return isGitRegionMatch(patterns: ['.*\\.(asciidoc|png)'], shouldMatchAll: true) - } -} - -/** - This method grab the dependencies of a Go module and transform them on regexp -*/ -def getVendorPatterns(beatName){ - def os = goos() - def goRoot = "${env.WORKSPACE}/.gvm/versions/go${GO_VERSION}.${os}.amd64" - def output = "" - - withEnv([ - "HOME=${env.WORKSPACE}/${env.BASE_DIR}", - "PATH=${env.WORKSPACE}/bin:${goRoot}/bin:${env.PATH}", - ]) { - output = sh(label: 'Get vendor dependency patterns', returnStdout: true, script: """ - go list -deps ./${beatName} \ - | grep 'elastic/beats' \ - | sed -e "s#github.com/elastic/beats/v7/##g" \ - | awk '{print "^" \$1 "/.*"}' - """) - } - return output?.split('\n').collect{ item -> item as String } -} - -def setGitConfig(){ - sh(label: 'check git config', script: ''' - if [ -z "$(git config --get user.email)" ]; then - git config user.email "beatsmachine@users.noreply.github.com" - git config user.name "beatsmachine" - fi - ''') -} - -def isDockerInstalled(){ - return sh(label: 'check for Docker', script: 'command -v docker', returnStatus: true) -} - -def junitAndStore(Map params = [:]){ - junit(params) - // STAGE_NAME env variable could be null in some cases, so let's use the currentmilliseconds - def stageName = env.STAGE_NAME ? env.STAGE_NAME.replaceAll("[\\W]|_",'-') : "uncategorized-${new java.util.Date().getTime()}" - stash(includes: params.testResults, allowEmpty: true, name: stageName, useDefaultExcludes: true) - stashedTestReports[stageName] = stageName -} - -def runbld() { - catchError(buildResult: 'SUCCESS', message: 'runbld post build action failed.') { - if (stashedTestReports) { - dir("${env.BASE_DIR}") { - sh(label: 'Prepare workspace context', - script: 'find . -type f -name "TEST*.xml" -path "*/build/*" -delete') - // Unstash the test reports - stashedTestReports.each { k, v -> - dir(k) { - unstash(v) - } - } - sh(label: 'Process JUnit reports with runbld', - script: '''\ - cat >./runbld-script < Date: Thu, 8 Oct 2020 15:27:14 +0200 Subject: [PATCH 17/46] Fix function that parses from/to/contact headers (#21672) --- packetbeat/protos/sip/plugin.go | 35 +++++++++++++++++++++------- packetbeat/protos/sip/plugin_test.go | 12 +++++++--- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/packetbeat/protos/sip/plugin.go b/packetbeat/protos/sip/plugin.go index e4cc0364d9a..14b56aeda45 100644 --- a/packetbeat/protos/sip/plugin.go +++ b/packetbeat/protos/sip/plugin.go @@ -499,27 +499,46 @@ func populateBodyFields(m *message, pbf *pb.Fields, fields *ProtocolFields) { func parseFromToContact(fromTo common.NetString) (displayInfo, uri common.NetString, params map[string]common.NetString) { params = make(map[string]common.NetString) - pos := bytes.IndexByte(fromTo, '<') - if pos == -1 { - pos = bytes.IndexByte(fromTo, ' ') - } + fromTo = bytes.TrimSpace(fromTo) + + var uriIsWrapped bool + pos := func() int { + // look for the beginning of a url wrapped in <...> + if pos := bytes.IndexByte(fromTo, '<'); pos > -1 { + uriIsWrapped = true + return pos + } + // if there is no < char, it means there is no display info, and + // that the url starts from the beginning + // https://tools.ietf.org/html/rfc3261#section-20.10 + return 0 + }() displayInfo = bytes.Trim(fromTo[:pos], "'\"\t ") endURIPos := func() int { - if fromTo[pos] == '<' { + if uriIsWrapped { return bytes.IndexByte(fromTo, '>') } return bytes.IndexByte(fromTo, ';') }() + // not wrapped and no header params if endURIPos == -1 { - uri = bytes.TrimRight(fromTo[pos:], ">") - return + uri = fromTo[pos:] + return displayInfo, uri, params } - pos += 1 + + // if wrapped, we want to get over the < char + if uriIsWrapped { + pos += 1 + } + + // if wrapped, we will get the string between <...> + // if not wrapped, we will get the value before the header params (until ;) uri = fromTo[pos:endURIPos] + // parse the header params pos = endURIPos + 1 for _, param := range bytes.Split(fromTo[pos:], []byte(";")) { kv := bytes.SplitN(param, []byte("="), 2) diff --git a/packetbeat/protos/sip/plugin_test.go b/packetbeat/protos/sip/plugin_test.go index fc9ee53aff2..d8c09f5b307 100644 --- a/packetbeat/protos/sip/plugin_test.go +++ b/packetbeat/protos/sip/plugin_test.go @@ -65,11 +65,11 @@ func TestParseFromTo(t *testing.T) { assert.Equal(t, common.NetString(nil), params["tag"]) // From - displayInfo, uri, params = parseFromToContact(common.NetString("\"PCMU/8000\" ;tag=1")) + displayInfo, uri, params = parseFromToContact(common.NetString("\"PCMU/8000\" ;tag=1")) assert.Equal(t, common.NetString("PCMU/8000"), displayInfo) assert.Equal(t, common.NetString("sip:sipp@10.0.2.15:5060"), uri) assert.Equal(t, common.NetString("1"), params["tag"]) - displayInfo, uri, params = parseFromToContact(common.NetString("\"Matthew Hodgson\" ;tag=5c7cdb68")) + displayInfo, uri, params = parseFromToContact(common.NetString(" \"Matthew Hodgson\" ;tag=5c7cdb68")) assert.Equal(t, common.NetString("Matthew Hodgson"), displayInfo) assert.Equal(t, common.NetString("sip:matthew@mxtelecom.com"), uri) assert.Equal(t, common.NetString("5c7cdb68"), params["tag"]) @@ -83,7 +83,7 @@ func TestParseFromTo(t *testing.T) { assert.Equal(t, common.NetString(nil), params["tag"]) // Contact - displayInfo, uri, _ = parseFromToContact(common.NetString("")) + displayInfo, uri, _ = parseFromToContact(common.NetString(" ")) assert.Equal(t, common.NetString(nil), displayInfo) assert.Equal(t, common.NetString("sip:test@10.0.2.15:5060;transport=udp"), uri) displayInfo, uri, params = parseFromToContact(common.NetString(";expires=1200;q=0.500")) @@ -100,6 +100,12 @@ func TestParseFromTo(t *testing.T) { assert.Equal(t, common.NetString("Mr. Watson"), displayInfo) assert.Equal(t, common.NetString("mailto:watson@bell-telephone.com"), uri) assert.Equal(t, common.NetString("0.1"), params["q"]) + + // url is not bounded by <...> + displayInfo, uri, params = parseFromToContact(common.NetString(" sip:test@10.0.2.15:5060;transport=udp")) + assert.Equal(t, common.NetString(nil), displayInfo) + assert.Equal(t, common.NetString("sip:test@10.0.2.15:5060"), uri) + assert.Equal(t, common.NetString("udp"), params["transport"]) } func TestParseUDP(t *testing.T) { From 5b69349d02b3fc92605ccfe796b6dd558fa18846 Mon Sep 17 00:00:00 2001 From: Lee Hinman <57081003+leehinman@users.noreply.github.com> Date: Thu, 8 Oct 2020 09:27:33 -0500 Subject: [PATCH 18/46] [Winlogbeat] Remove brittle configuration validation from wineventlog (#21593) * Remove brittle configuration validation from wineventlog - removed config keys checking - update unit tests Closes #21220 --- winlogbeat/eventlog/eventlogging.go | 5 +--- winlogbeat/eventlog/factory.go | 28 +++---------------- winlogbeat/eventlog/wineventlog.go | 6 +--- .../eventlog/wineventlog_expirimental.go | 2 +- winlogbeat/tests/system/test_eventlogging.py | 19 ------------- winlogbeat/tests/system/test_wineventlog.py | 17 ----------- 6 files changed, 7 insertions(+), 70 deletions(-) diff --git a/winlogbeat/eventlog/eventlogging.go b/winlogbeat/eventlog/eventlogging.go index 963797264b2..01465d933fc 100644 --- a/winlogbeat/eventlog/eventlogging.go +++ b/winlogbeat/eventlog/eventlogging.go @@ -40,9 +40,6 @@ const ( eventLoggingAPIName = "eventlogging" ) -var eventLoggingConfigKeys = common.MakeStringSet(append(commonConfigKeys, - "ignore_older", "read_buffer_size", "format_buffer_size")...) - type eventLoggingConfig struct { ConfigCommon `config:",inline"` IgnoreOlder time.Duration `config:"ignore_older"` @@ -284,7 +281,7 @@ func newEventLogging(options *common.Config) (EventLog, error) { ReadBufferSize: win.MaxEventBufferSize, FormatBufferSize: win.MaxFormatMessageBufferSize, } - if err := readConfig(options, &c, eventLoggingConfigKeys); err != nil { + if err := readConfig(options, &c); err != nil { return nil, err } diff --git a/winlogbeat/eventlog/factory.go b/winlogbeat/eventlog/factory.go index f66c158b2f2..9ee8e0d144f 100644 --- a/winlogbeat/eventlog/factory.go +++ b/winlogbeat/eventlog/factory.go @@ -22,14 +22,9 @@ import ( "sort" "strings" - "github.com/joeshaw/multierror" - "github.com/elastic/beats/v7/libbeat/common" ) -var commonConfigKeys = []string{"type", "api", "name", "fields", "fields_under_root", - "tags", "processors", "index", "id", "meta", "revision"} - // ConfigCommon is the common configuration data used to instantiate a new // EventLog. Each implementation is free to support additional configuration // options. @@ -42,33 +37,18 @@ type validator interface { Validate() error } -func readConfig( - c *common.Config, - config interface{}, - validKeys common.StringSet, -) error { +func readConfig(c *common.Config, config interface{}) error { if err := c.Unpack(config); err != nil { return fmt.Errorf("failed unpacking config. %v", err) } - var errs multierror.Errors - if len(validKeys) > 0 { - // Check for invalid keys. - for _, k := range c.GetFields() { - if !validKeys.Has(k) { - errs = append(errs, fmt.Errorf("invalid event log key '%s' "+ - "found. Valid keys are %s", k, strings.Join(validKeys.ToSlice(), ", "))) - } - } - } - if v, ok := config.(validator); ok { if err := v.Validate(); err != nil { - errs = append(errs, err) + return err } } - return errs.Err() + return nil } // Producer produces a new event log instance for reading event log records. @@ -114,7 +94,7 @@ func New(options *common.Config) (EventLog, error) { } var config ConfigCommon - if err := readConfig(options, &config, nil); err != nil { + if err := readConfig(options, &config); err != nil { return nil, err } diff --git a/winlogbeat/eventlog/wineventlog.go b/winlogbeat/eventlog/wineventlog.go index db00f239974..7ee6c62cf18 100644 --- a/winlogbeat/eventlog/wineventlog.go +++ b/winlogbeat/eventlog/wineventlog.go @@ -47,10 +47,6 @@ const ( winEventLogAPIName = "wineventlog" ) -var winEventLogConfigKeys = common.MakeStringSet(append(commonConfigKeys, - "batch_read_size", "ignore_older", "include_xml", "event_id", "forwarded", - "level", "provider", "no_more_events")...) - type winEventLogConfig struct { ConfigCommon `config:",inline"` BatchReadSize int `config:"batch_read_size"` // Maximum number of events that Read will return. @@ -366,7 +362,7 @@ func (l *winEventLog) buildRecordFromXML(x []byte, recoveredErr error) (Record, // using the Windows Event Log. func newWinEventLog(options *common.Config) (EventLog, error) { c := defaultWinEventLogConfig - if err := readConfig(options, &c, winEventLogConfigKeys); err != nil { + if err := readConfig(options, &c); err != nil { return nil, err } diff --git a/winlogbeat/eventlog/wineventlog_expirimental.go b/winlogbeat/eventlog/wineventlog_expirimental.go index 5952aad0f5a..301b2cf4f0b 100644 --- a/winlogbeat/eventlog/wineventlog_expirimental.go +++ b/winlogbeat/eventlog/wineventlog_expirimental.go @@ -243,7 +243,7 @@ func newWinEventLogExp(options *common.Config) (EventLog, error) { cfgwarn.Experimental("The %s event log reader is experimental.", winEventLogExpAPIName) c := winEventLogConfig{BatchReadSize: 512} - if err := readConfig(options, &c, winEventLogConfigKeys); err != nil { + if err := readConfig(options, &c); err != nil { return nil, err } diff --git a/winlogbeat/tests/system/test_eventlogging.py b/winlogbeat/tests/system/test_eventlogging.py index dd763104fa5..0d486656054 100644 --- a/winlogbeat/tests/system/test_eventlogging.py +++ b/winlogbeat/tests/system/test_eventlogging.py @@ -156,25 +156,6 @@ def test_ignore_older(self): self.assertEqual(evts[0]["winlog.event_id"], 10) self.assertEqual(evts[0]["event.code"], 10) - def test_unknown_eventlog_config(self): - """ - eventlogging - Unknown config parameter - """ - self.render_config_template( - event_logs=[ - { - "name": self.providerName, - "api": self.api, - "event_id": "10, 12", - "level": "info", - "provider": ["me"], - "include_xml": True, - } - ] - ) - self.start_beat().check_wait(exit_code=1) - assert self.log_contains("4 errors: invalid event log key") - def test_utf16_characters(self): """ eventlogging - UTF-16 characters diff --git a/winlogbeat/tests/system/test_wineventlog.py b/winlogbeat/tests/system/test_wineventlog.py index ea390475279..363e90edbd2 100644 --- a/winlogbeat/tests/system/test_wineventlog.py +++ b/winlogbeat/tests/system/test_wineventlog.py @@ -311,23 +311,6 @@ def test_query_multi_param(self): self.assertTrue(len(evts), 1) self.assertEqual(evts[0]["message"], "selected") - def test_unknown_eventlog_config(self): - """ - wineventlog - Unknown config parameter - """ - self.render_config_template( - event_logs=[ - { - "name": self.providerName, - "api": self.api, - "forwarded": False, - "invalid": "garbage"} - ] - ) - self.start_beat().check_wait(exit_code=1) - assert self.log_contains( - "1 error: invalid event log key 'invalid' found.") - def test_utf16_characters(self): """ wineventlog - UTF-16 characters From e4df4501bdbf9edb89421746f559a89688fff71e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Thu, 8 Oct 2020 19:46:45 +0200 Subject: [PATCH 19/46] fix: remove extra curly brace in script (#21692) * fix: remove extra curly brace * chore: proper indent --- .ci/packaging.groovy | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.ci/packaging.groovy b/.ci/packaging.groovy index 81ff5b1994a..c65fd7f8b56 100644 --- a/.ci/packaging.groovy +++ b/.ci/packaging.groovy @@ -215,12 +215,12 @@ def tagAndPush(name){ docker push ${newName} docker tag ${oldName} ${commitName} docker push ${commitName} - """, returnStatus: true) - if ( status > 0 && iterations < 3) { - error('tag and push failed, retry') - } else if ( status > 0 ) { - log(level: 'WARN', text: "${name} doesn't have ${variant} docker images. See https://github.com/elastic/beats/pull/21621") - } + """, returnStatus: true) + + if ( status > 0 && iterations < 3) { + error('tag and push failed, retry') + } else if ( status > 0 ) { + log(level: 'WARN', text: "${name} doesn't have ${variant} docker images. See https://github.com/elastic/beats/pull/21621") } } } From 1abe97b20d708d9dd61018163a7d45616b415c59 Mon Sep 17 00:00:00 2001 From: Adrian Serrano Date: Fri, 9 Oct 2020 09:33:35 +0200 Subject: [PATCH 20/46] Make o365audit input cancellable (#21647) PR #21258 introduced a restart mechanism for o365input so that it didn't stop working once a fatal error was found. This updates the restart delay to use a cancellation-context-aware method so that the input doesn't block Filebeat termination. --- x-pack/filebeat/input/o365audit/input.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/x-pack/filebeat/input/o365audit/input.go b/x-pack/filebeat/input/o365audit/input.go index 67013dc6a71..2cf76de9ee3 100644 --- a/x-pack/filebeat/input/o365audit/input.go +++ b/x-pack/filebeat/input/o365audit/input.go @@ -21,14 +21,12 @@ import ( "github.com/elastic/beats/v7/libbeat/logp" "github.com/elastic/beats/v7/x-pack/filebeat/input/o365audit/poll" "github.com/elastic/go-concert/ctxtool" + "github.com/elastic/go-concert/timed" ) const ( pluginName = "o365audit" fieldsPrefix = pluginName - - // How long to retry when a fatal error is encountered in the input. - failureRetryInterval = time.Minute * 5 ) type o365input struct { @@ -126,8 +124,8 @@ func (inp *o365input) Run( } publisher.Publish(event, nil) ctx.Logger.Errorf("Input failed: %v", err) - ctx.Logger.Infof("Restarting in %v", failureRetryInterval) - time.Sleep(failureRetryInterval) + ctx.Logger.Infof("Restarting in %v", inp.config.API.ErrorRetryInterval) + timed.Wait(ctx.Cancelation, inp.config.API.ErrorRetryInterval) } } return nil From 3aceb31568b531063369c191636a36b68f48ae59 Mon Sep 17 00:00:00 2001 From: Andrew Wilkins Date: Mon, 12 Oct 2020 12:34:58 +0800 Subject: [PATCH 21/46] libbeat/logp: introduce Logger.WithOptions (#21671) * libbeat/logp: introduce Logger.WithOptions Add a Logger.WithOptions method, which clones the logger and applies given options. For example, this can be used to obtain a clone of the logger with sampling/rate limiting applied. --- libbeat/logp/logger.go | 6 +++++ libbeat/logp/logger_test.go | 52 +++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 libbeat/logp/logger_test.go diff --git a/libbeat/logp/logger.go b/libbeat/logp/logger.go index 6f1c42fe022..2bbe6b3ce53 100644 --- a/libbeat/logp/logger.go +++ b/libbeat/logp/logger.go @@ -50,6 +50,12 @@ func NewLogger(selector string, options ...LogOption) *Logger { return newLogger(loadLogger().rootLogger, selector, options...) } +// WithOptions returns a clone of l with options applied. +func (l *Logger) WithOptions(options ...LogOption) *Logger { + cloned := l.logger.WithOptions(options...) + return &Logger{cloned, cloned.Sugar()} +} + // With creates a child logger and adds structured context to it. Fields added // to the child don't affect the parent, and vice versa. func (l *Logger) With(args ...interface{}) *Logger { diff --git a/libbeat/logp/logger_test.go b/libbeat/logp/logger_test.go new file mode 100644 index 00000000000..eaf8a1070ce --- /dev/null +++ b/libbeat/logp/logger_test.go @@ -0,0 +1,52 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you 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. + +package logp + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "go.uber.org/zap/zaptest/observer" +) + +func TestLoggerWithOptions(t *testing.T) { + core1, observed1 := observer.New(zapcore.DebugLevel) + core2, observed2 := observer.New(zapcore.DebugLevel) + + logger1 := NewLogger("bo", zap.WrapCore(func(in zapcore.Core) zapcore.Core { + return zapcore.NewTee(in, core1) + })) + logger2 := logger1.WithOptions(zap.WrapCore(func(in zapcore.Core) zapcore.Core { + return zapcore.NewTee(in, core2) + })) + + logger1.Info("hello logger1") // should just go to the first observer + logger2.Info("hello logger1 and logger2") // should go to both observers + + observedEntries1 := observed1.All() + require.Len(t, observedEntries1, 2) + assert.Equal(t, "hello logger1", observedEntries1[0].Message) + assert.Equal(t, "hello logger1 and logger2", observedEntries1[1].Message) + + observedEntries2 := observed2.All() + require.Len(t, observedEntries2, 1) + assert.Equal(t, "hello logger1 and logger2", observedEntries2[0].Message) +} From d35dfb53111a86ed0e484f8450ffcf1474be60e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9mi=20V=C3=A1nyi?= Date: Mon, 12 Oct 2020 10:53:59 +0200 Subject: [PATCH 22/46] Add configuration of filestream input (#21565) --- .../config/filebeat.inputs.reference.yml.tmpl | 140 ++++++++++++++++++ .../_meta/config/filebeat.inputs.yml.tmpl | 29 ++++ filebeat/filebeat.reference.yml | 140 ++++++++++++++++++ filebeat/filebeat.yml | 29 ++++ filebeat/input/filestream/config.go | 18 +-- filebeat/input/filestream/input.go | 16 +- x-pack/filebeat/filebeat.reference.yml | 140 ++++++++++++++++++ x-pack/filebeat/filebeat.yml | 29 ++++ 8 files changed, 524 insertions(+), 17 deletions(-) diff --git a/filebeat/_meta/config/filebeat.inputs.reference.yml.tmpl b/filebeat/_meta/config/filebeat.inputs.reference.yml.tmpl index c920b7dbec8..7eceb559f16 100644 --- a/filebeat/_meta/config/filebeat.inputs.reference.yml.tmpl +++ b/filebeat/_meta/config/filebeat.inputs.reference.yml.tmpl @@ -11,6 +11,7 @@ filebeat.inputs: # # Possible options are: # * log: Reads every line of the log file (default) +# * filestream: Improved version of log input. Experimental. # * stdin: Reads the standard in #------------------------------ Log input -------------------------------- @@ -231,6 +232,145 @@ filebeat.inputs: # Defines if inputs is enabled #enabled: true +#--------------------------- Filestream input ---------------------------- +- type: filestream + + # Change to true to enable this input configuration. + enabled: false + + # Paths that should be crawled and fetched. Glob based paths. + # To fetch all ".log" files from a specific level of subdirectories + # /var/log/*/*.log can be used. + # For each file found under this path, a harvester is started. + # Make sure not file is defined twice as this can lead to unexpected behaviour. + paths: + - /var/log/*.log + #- c:\programdata\elasticsearch\logs\* + + # Configure the file encoding for reading files with international characters + # following the W3C recommendation for HTML5 (http://www.w3.org/TR/encoding). + # Some sample encodings: + # plain, utf-8, utf-16be-bom, utf-16be, utf-16le, big5, gb18030, gbk, + # hz-gb-2312, euc-kr, euc-jp, iso-2022-jp, shift-jis, ... + #encoding: plain + + + # Exclude lines. A list of regular expressions to match. It drops the lines that are + # matching any regular expression from the list. The include_lines is called before + # exclude_lines. By default, no lines are dropped. + #exclude_lines: ['^DBG'] + + # Include lines. A list of regular expressions to match. It exports the lines that are + # matching any regular expression from the list. The include_lines is called before + # exclude_lines. By default, all the lines are exported. + #include_lines: ['^ERR', '^WARN'] + + ### Prospector options + + # How often the input checks for new files in the paths that are specified + # for harvesting. Specify 1s to scan the directory as frequently as possible + # without causing Filebeat to scan too frequently. Default: 10s. + #prospector.scanner.check_interval: 10s + + # Exclude files. A list of regular expressions to match. Filebeat drops the files that + # are matching any regular expression from the list. By default, no files are dropped. + #prospector.scanner.exclude_files: ['.gz$'] + + # Expand "**" patterns into regular glob patterns. + #prospector.scanner.recursive_glob: true + + # If symlinks is enabled, symlinks are opened and harvested. The harvester is opening the + # original for harvesting but will report the symlink name as source. + #prospector.scanner.symlinks: false + + ### State options + + # Files for the modification data is older then clean_inactive the state from the registry is removed + # By default this is disabled. + #clean_inactive: 0 + + # Removes the state for file which cannot be found on disk anymore immediately + #clean_removed: true + + # Method to determine if two files are the same or not. By default + # the Beat considers two files the same if their inode and device id are the same. + #file_identity.native: ~ + + # Optional additional fields. These fields can be freely picked + # to add additional information to the crawled log files for filtering + #fields: + # level: debug + # review: 1 + + # Set to true to publish fields with null values in events. + #keep_null: false + + # By default, all events contain `host.name`. This option can be set to true + # to disable the addition of this field to all events. The default value is + # false. + #publisher_pipeline.disable_host: false + + # Ignore files which were modified more then the defined timespan in the past. + # ignore_older is disabled by default, so no files are ignored by setting it to 0. + # Time strings like 2h (2 hours), 5m (5 minutes) can be used. + #ignore_older: 0 + + # Defines the buffer size every harvester uses when fetching the file + #harvester_buffer_size: 16384 + + # Maximum number of bytes a single log event can have + # All bytes after max_bytes are discarded and not sent. The default is 10MB. + # This is especially useful for multiline log messages which can get large. + #message_max_bytes: 10485760 + + # Characters which separate the lines. Valid values: auto, line_feed, vertical_tab, form_feed, + # carriage_return, carriage_return_line_feed, next_line, line_separator, paragraph_separator. + #line_terminator: auto + + # The Ingest Node pipeline ID associated with this input. If this is set, it + # overwrites the pipeline option from the Elasticsearch output. + #pipeline: + + # Backoff values define how aggressively filebeat crawls new files for updates + # The default values can be used in most cases. Backoff defines how long it is waited + # to check a file again after EOF is reached. Default is 1s which means the file + # is checked every second if new lines were added. This leads to a near real time crawling. + # Every time a new line appears, backoff is reset to the initial value. + #backoff.init: 1s + + # Max backoff defines what the maximum backoff time is. After having backed off multiple times + # from checking the files, the waiting time will never exceed max_backoff independent of the + # backoff factor. Having it set to 10s means in the worst case a new line can be added to a log + # file after having backed off multiple times, it takes a maximum of 10s to read the new line + #backoff.max: 10s + + ### Harvester closing options + + # Close inactive closes the file handler after the predefined period. + # The period starts when the last line of the file was, not the file ModTime. + # Time strings like 2h (2 hours), 5m (5 minutes) can be used. + #close.on_state_change.inactive: 5m + + # Close renamed closes a file handler when the file is renamed or rotated. + # Note: Potential data loss. Make sure to read and understand the docs for this option. + #close.on_state_change.renamed: false + + # When enabling this option, a file handler is closed immediately in case a file can't be found + # any more. In case the file shows up again later, harvesting will continue at the last known position + # after scan_frequency. + #close.on_state_change.removed: true + + # Closes the file handler as soon as the harvesters reaches the end of the file. + # By default this option is disabled. + # Note: Potential data loss. Make sure to read and understand the docs for this option. + #close.reader.eof: false + + # Close timeout closes the harvester after the predefined time. + # This is independent if the harvester did finish reading the file or not. + # By default this option is disabled. + # Note: Potential data loss. Make sure to read and understand the docs for this option. + #close.reader.after_interval: 0 + #----------------------------- Stdin input ------------------------------- # Configuration to use stdin input #- type: stdin diff --git a/filebeat/_meta/config/filebeat.inputs.yml.tmpl b/filebeat/_meta/config/filebeat.inputs.yml.tmpl index a7bd1b5eaa6..70d52cbb9c6 100644 --- a/filebeat/_meta/config/filebeat.inputs.yml.tmpl +++ b/filebeat/_meta/config/filebeat.inputs.yml.tmpl @@ -49,3 +49,32 @@ filebeat.inputs: # that was (not) matched before or after or as long as a pattern is not matched based on negate. # Note: After is the equivalent to previous and before is the equivalent to to next in Logstash #multiline.match: after + +# filestream is an experimental input. It is going to replace log input in the future. +- type: filestream + + # Change to true to enable this input configuration. + enabled: false + + # Paths that should be crawled and fetched. Glob based paths. + paths: + - /var/log/*.log + #- c:\programdata\elasticsearch\logs\* + + # Exclude lines. A list of regular expressions to match. It drops the lines that are + # matching any regular expression from the list. + #exclude_lines: ['^DBG'] + + # Include lines. A list of regular expressions to match. It exports the lines that are + # matching any regular expression from the list. + #include_lines: ['^ERR', '^WARN'] + + # Exclude files. A list of regular expressions to match. Filebeat drops the files that + # are matching any regular expression from the list. By default, no files are dropped. + #prospector.scanner.exclude_files: ['.gz$'] + + # Optional additional fields. These fields can be freely picked + # to add additional information to the crawled log files for filtering + #fields: + # level: debug + # review: 1 diff --git a/filebeat/filebeat.reference.yml b/filebeat/filebeat.reference.yml index bf29e0715ed..3a0d1d5d19c 100644 --- a/filebeat/filebeat.reference.yml +++ b/filebeat/filebeat.reference.yml @@ -398,6 +398,7 @@ filebeat.inputs: # # Possible options are: # * log: Reads every line of the log file (default) +# * filestream: Improved version of log input. Experimental. # * stdin: Reads the standard in #------------------------------ Log input -------------------------------- @@ -618,6 +619,145 @@ filebeat.inputs: # Defines if inputs is enabled #enabled: true +#--------------------------- Filestream input ---------------------------- +- type: filestream + + # Change to true to enable this input configuration. + enabled: false + + # Paths that should be crawled and fetched. Glob based paths. + # To fetch all ".log" files from a specific level of subdirectories + # /var/log/*/*.log can be used. + # For each file found under this path, a harvester is started. + # Make sure not file is defined twice as this can lead to unexpected behaviour. + paths: + - /var/log/*.log + #- c:\programdata\elasticsearch\logs\* + + # Configure the file encoding for reading files with international characters + # following the W3C recommendation for HTML5 (http://www.w3.org/TR/encoding). + # Some sample encodings: + # plain, utf-8, utf-16be-bom, utf-16be, utf-16le, big5, gb18030, gbk, + # hz-gb-2312, euc-kr, euc-jp, iso-2022-jp, shift-jis, ... + #encoding: plain + + + # Exclude lines. A list of regular expressions to match. It drops the lines that are + # matching any regular expression from the list. The include_lines is called before + # exclude_lines. By default, no lines are dropped. + #exclude_lines: ['^DBG'] + + # Include lines. A list of regular expressions to match. It exports the lines that are + # matching any regular expression from the list. The include_lines is called before + # exclude_lines. By default, all the lines are exported. + #include_lines: ['^ERR', '^WARN'] + + ### Prospector options + + # How often the input checks for new files in the paths that are specified + # for harvesting. Specify 1s to scan the directory as frequently as possible + # without causing Filebeat to scan too frequently. Default: 10s. + #prospector.scanner.check_interval: 10s + + # Exclude files. A list of regular expressions to match. Filebeat drops the files that + # are matching any regular expression from the list. By default, no files are dropped. + #prospector.scanner.exclude_files: ['.gz$'] + + # Expand "**" patterns into regular glob patterns. + #prospector.scanner.recursive_glob: true + + # If symlinks is enabled, symlinks are opened and harvested. The harvester is opening the + # original for harvesting but will report the symlink name as source. + #prospector.scanner.symlinks: false + + ### State options + + # Files for the modification data is older then clean_inactive the state from the registry is removed + # By default this is disabled. + #clean_inactive: 0 + + # Removes the state for file which cannot be found on disk anymore immediately + #clean_removed: true + + # Method to determine if two files are the same or not. By default + # the Beat considers two files the same if their inode and device id are the same. + #file_identity.native: ~ + + # Optional additional fields. These fields can be freely picked + # to add additional information to the crawled log files for filtering + #fields: + # level: debug + # review: 1 + + # Set to true to publish fields with null values in events. + #keep_null: false + + # By default, all events contain `host.name`. This option can be set to true + # to disable the addition of this field to all events. The default value is + # false. + #publisher_pipeline.disable_host: false + + # Ignore files which were modified more then the defined timespan in the past. + # ignore_older is disabled by default, so no files are ignored by setting it to 0. + # Time strings like 2h (2 hours), 5m (5 minutes) can be used. + #ignore_older: 0 + + # Defines the buffer size every harvester uses when fetching the file + #harvester_buffer_size: 16384 + + # Maximum number of bytes a single log event can have + # All bytes after max_bytes are discarded and not sent. The default is 10MB. + # This is especially useful for multiline log messages which can get large. + #message_max_bytes: 10485760 + + # Characters which separate the lines. Valid values: auto, line_feed, vertical_tab, form_feed, + # carriage_return, carriage_return_line_feed, next_line, line_separator, paragraph_separator. + #line_terminator: auto + + # The Ingest Node pipeline ID associated with this input. If this is set, it + # overwrites the pipeline option from the Elasticsearch output. + #pipeline: + + # Backoff values define how aggressively filebeat crawls new files for updates + # The default values can be used in most cases. Backoff defines how long it is waited + # to check a file again after EOF is reached. Default is 1s which means the file + # is checked every second if new lines were added. This leads to a near real time crawling. + # Every time a new line appears, backoff is reset to the initial value. + #backoff.init: 1s + + # Max backoff defines what the maximum backoff time is. After having backed off multiple times + # from checking the files, the waiting time will never exceed max_backoff independent of the + # backoff factor. Having it set to 10s means in the worst case a new line can be added to a log + # file after having backed off multiple times, it takes a maximum of 10s to read the new line + #backoff.max: 10s + + ### Harvester closing options + + # Close inactive closes the file handler after the predefined period. + # The period starts when the last line of the file was, not the file ModTime. + # Time strings like 2h (2 hours), 5m (5 minutes) can be used. + #close.on_state_change.inactive: 5m + + # Close renamed closes a file handler when the file is renamed or rotated. + # Note: Potential data loss. Make sure to read and understand the docs for this option. + #close.on_state_change.renamed: false + + # When enabling this option, a file handler is closed immediately in case a file can't be found + # any more. In case the file shows up again later, harvesting will continue at the last known position + # after scan_frequency. + #close.on_state_change.removed: true + + # Closes the file handler as soon as the harvesters reaches the end of the file. + # By default this option is disabled. + # Note: Potential data loss. Make sure to read and understand the docs for this option. + #close.reader.eof: false + + # Close timeout closes the harvester after the predefined time. + # This is independent if the harvester did finish reading the file or not. + # By default this option is disabled. + # Note: Potential data loss. Make sure to read and understand the docs for this option. + #close.reader.after_interval: 0 + #----------------------------- Stdin input ------------------------------- # Configuration to use stdin input #- type: stdin diff --git a/filebeat/filebeat.yml b/filebeat/filebeat.yml index 2a3e678c542..9a29bc76962 100644 --- a/filebeat/filebeat.yml +++ b/filebeat/filebeat.yml @@ -62,6 +62,35 @@ filebeat.inputs: # Note: After is the equivalent to previous and before is the equivalent to to next in Logstash #multiline.match: after +# filestream is an experimental input. It is going to replace log input in the future. +- type: filestream + + # Change to true to enable this input configuration. + enabled: false + + # Paths that should be crawled and fetched. Glob based paths. + paths: + - /var/log/*.log + #- c:\programdata\elasticsearch\logs\* + + # Exclude lines. A list of regular expressions to match. It drops the lines that are + # matching any regular expression from the list. + #exclude_lines: ['^DBG'] + + # Include lines. A list of regular expressions to match. It exports the lines that are + # matching any regular expression from the list. + #include_lines: ['^ERR', '^WARN'] + + # Exclude files. A list of regular expressions to match. Filebeat drops the files that + # are matching any regular expression from the list. By default, no files are dropped. + #prospector.scanner.exclude_files: ['.gz$'] + + # Optional additional fields. These fields can be freely picked + # to add additional information to the crawled log files for filtering + #fields: + # level: debug + # review: 1 + # ============================== Filebeat modules ============================== filebeat.config.modules: diff --git a/filebeat/input/filestream/config.go b/filebeat/input/filestream/config.go index 3ec076196f0..c2b1e838ee5 100644 --- a/filebeat/input/filestream/config.go +++ b/filebeat/input/filestream/config.go @@ -30,10 +30,11 @@ import ( // Config stores the options of a file stream. type config struct { + readerConfig + Paths []string `config:"paths"` Close closerConfig `config:"close"` FileWatcher *common.ConfigNamespace `config:"file_watcher"` - Reader readerConfig `config:"readers"` FileIdentity *common.ConfigNamespace `config:"file_identity"` CleanInactive time.Duration `config:"clean_inactive" validate:"min=0"` CleanRemoved bool `config:"clean_removed"` @@ -47,18 +48,17 @@ type closerConfig struct { } type readerCloserConfig struct { - AfterInterval time.Duration - OnEOF bool + AfterInterval time.Duration `config:"after_interval"` + OnEOF bool `config:"on_eof"` } type stateChangeCloserConfig struct { - CheckInterval time.Duration - Inactive time.Duration - Removed bool - Renamed bool + CheckInterval time.Duration `config:"check_interval" validate:"nonzero"` + Inactive time.Duration `config:"inactive"` + Removed bool `config:"removed"` + Renamed bool `config:"renamed"` } -// TODO should this be inline? type readerConfig struct { Backoff backoffConfig `config:"backoff"` BufferSize int `config:"buffer_size"` @@ -79,9 +79,9 @@ type backoffConfig struct { func defaultConfig() config { return config{ + readerConfig: defaultReaderConfig(), Paths: []string{}, Close: defaultCloserConfig(), - Reader: defaultReaderConfig(), CleanInactive: 0, CleanRemoved: true, HarvesterLimit: 0, diff --git a/filebeat/input/filestream/input.go b/filebeat/input/filestream/input.go index b6c6598c50b..9f715d1183e 100644 --- a/filebeat/input/filestream/input.go +++ b/filebeat/input/filestream/input.go @@ -94,19 +94,19 @@ func configure(cfg *common.Config) (loginp.Prospector, loginp.Harvester, error) return nil, nil, err } - encodingFactory, ok := encoding.FindEncoding(config.Reader.Encoding) + encodingFactory, ok := encoding.FindEncoding(config.Encoding) if !ok || encodingFactory == nil { - return nil, nil, fmt.Errorf("unknown encoding('%v')", config.Reader.Encoding) + return nil, nil, fmt.Errorf("unknown encoding('%v')", config.Encoding) } return prospector, &filestream{ - readerConfig: config.Reader, - bufferSize: config.Reader.BufferSize, + readerConfig: config.readerConfig, + bufferSize: config.BufferSize, encodingFactory: encodingFactory, - lineTerminator: config.Reader.LineTerminator, - excludeLines: config.Reader.ExcludeLines, - includeLines: config.Reader.IncludeLines, - maxBytes: config.Reader.MaxBytes, + lineTerminator: config.LineTerminator, + excludeLines: config.ExcludeLines, + includeLines: config.IncludeLines, + maxBytes: config.MaxBytes, closerConfig: config.Close, }, nil } diff --git a/x-pack/filebeat/filebeat.reference.yml b/x-pack/filebeat/filebeat.reference.yml index 2ffff82135e..49ede1c7d24 100644 --- a/x-pack/filebeat/filebeat.reference.yml +++ b/x-pack/filebeat/filebeat.reference.yml @@ -1783,6 +1783,7 @@ filebeat.inputs: # # Possible options are: # * log: Reads every line of the log file (default) +# * filestream: Improved version of log input. Experimental. # * stdin: Reads the standard in #------------------------------ Log input -------------------------------- @@ -2003,6 +2004,145 @@ filebeat.inputs: # Defines if inputs is enabled #enabled: true +#--------------------------- Filestream input ---------------------------- +- type: filestream + + # Change to true to enable this input configuration. + enabled: false + + # Paths that should be crawled and fetched. Glob based paths. + # To fetch all ".log" files from a specific level of subdirectories + # /var/log/*/*.log can be used. + # For each file found under this path, a harvester is started. + # Make sure not file is defined twice as this can lead to unexpected behaviour. + paths: + - /var/log/*.log + #- c:\programdata\elasticsearch\logs\* + + # Configure the file encoding for reading files with international characters + # following the W3C recommendation for HTML5 (http://www.w3.org/TR/encoding). + # Some sample encodings: + # plain, utf-8, utf-16be-bom, utf-16be, utf-16le, big5, gb18030, gbk, + # hz-gb-2312, euc-kr, euc-jp, iso-2022-jp, shift-jis, ... + #encoding: plain + + + # Exclude lines. A list of regular expressions to match. It drops the lines that are + # matching any regular expression from the list. The include_lines is called before + # exclude_lines. By default, no lines are dropped. + #exclude_lines: ['^DBG'] + + # Include lines. A list of regular expressions to match. It exports the lines that are + # matching any regular expression from the list. The include_lines is called before + # exclude_lines. By default, all the lines are exported. + #include_lines: ['^ERR', '^WARN'] + + ### Prospector options + + # How often the input checks for new files in the paths that are specified + # for harvesting. Specify 1s to scan the directory as frequently as possible + # without causing Filebeat to scan too frequently. Default: 10s. + #prospector.scanner.check_interval: 10s + + # Exclude files. A list of regular expressions to match. Filebeat drops the files that + # are matching any regular expression from the list. By default, no files are dropped. + #prospector.scanner.exclude_files: ['.gz$'] + + # Expand "**" patterns into regular glob patterns. + #prospector.scanner.recursive_glob: true + + # If symlinks is enabled, symlinks are opened and harvested. The harvester is opening the + # original for harvesting but will report the symlink name as source. + #prospector.scanner.symlinks: false + + ### State options + + # Files for the modification data is older then clean_inactive the state from the registry is removed + # By default this is disabled. + #clean_inactive: 0 + + # Removes the state for file which cannot be found on disk anymore immediately + #clean_removed: true + + # Method to determine if two files are the same or not. By default + # the Beat considers two files the same if their inode and device id are the same. + #file_identity.native: ~ + + # Optional additional fields. These fields can be freely picked + # to add additional information to the crawled log files for filtering + #fields: + # level: debug + # review: 1 + + # Set to true to publish fields with null values in events. + #keep_null: false + + # By default, all events contain `host.name`. This option can be set to true + # to disable the addition of this field to all events. The default value is + # false. + #publisher_pipeline.disable_host: false + + # Ignore files which were modified more then the defined timespan in the past. + # ignore_older is disabled by default, so no files are ignored by setting it to 0. + # Time strings like 2h (2 hours), 5m (5 minutes) can be used. + #ignore_older: 0 + + # Defines the buffer size every harvester uses when fetching the file + #harvester_buffer_size: 16384 + + # Maximum number of bytes a single log event can have + # All bytes after max_bytes are discarded and not sent. The default is 10MB. + # This is especially useful for multiline log messages which can get large. + #message_max_bytes: 10485760 + + # Characters which separate the lines. Valid values: auto, line_feed, vertical_tab, form_feed, + # carriage_return, carriage_return_line_feed, next_line, line_separator, paragraph_separator. + #line_terminator: auto + + # The Ingest Node pipeline ID associated with this input. If this is set, it + # overwrites the pipeline option from the Elasticsearch output. + #pipeline: + + # Backoff values define how aggressively filebeat crawls new files for updates + # The default values can be used in most cases. Backoff defines how long it is waited + # to check a file again after EOF is reached. Default is 1s which means the file + # is checked every second if new lines were added. This leads to a near real time crawling. + # Every time a new line appears, backoff is reset to the initial value. + #backoff.init: 1s + + # Max backoff defines what the maximum backoff time is. After having backed off multiple times + # from checking the files, the waiting time will never exceed max_backoff independent of the + # backoff factor. Having it set to 10s means in the worst case a new line can be added to a log + # file after having backed off multiple times, it takes a maximum of 10s to read the new line + #backoff.max: 10s + + ### Harvester closing options + + # Close inactive closes the file handler after the predefined period. + # The period starts when the last line of the file was, not the file ModTime. + # Time strings like 2h (2 hours), 5m (5 minutes) can be used. + #close.on_state_change.inactive: 5m + + # Close renamed closes a file handler when the file is renamed or rotated. + # Note: Potential data loss. Make sure to read and understand the docs for this option. + #close.on_state_change.renamed: false + + # When enabling this option, a file handler is closed immediately in case a file can't be found + # any more. In case the file shows up again later, harvesting will continue at the last known position + # after scan_frequency. + #close.on_state_change.removed: true + + # Closes the file handler as soon as the harvesters reaches the end of the file. + # By default this option is disabled. + # Note: Potential data loss. Make sure to read and understand the docs for this option. + #close.reader.eof: false + + # Close timeout closes the harvester after the predefined time. + # This is independent if the harvester did finish reading the file or not. + # By default this option is disabled. + # Note: Potential data loss. Make sure to read and understand the docs for this option. + #close.reader.after_interval: 0 + #----------------------------- Stdin input ------------------------------- # Configuration to use stdin input #- type: stdin diff --git a/x-pack/filebeat/filebeat.yml b/x-pack/filebeat/filebeat.yml index 2a3e678c542..9a29bc76962 100644 --- a/x-pack/filebeat/filebeat.yml +++ b/x-pack/filebeat/filebeat.yml @@ -62,6 +62,35 @@ filebeat.inputs: # Note: After is the equivalent to previous and before is the equivalent to to next in Logstash #multiline.match: after +# filestream is an experimental input. It is going to replace log input in the future. +- type: filestream + + # Change to true to enable this input configuration. + enabled: false + + # Paths that should be crawled and fetched. Glob based paths. + paths: + - /var/log/*.log + #- c:\programdata\elasticsearch\logs\* + + # Exclude lines. A list of regular expressions to match. It drops the lines that are + # matching any regular expression from the list. + #exclude_lines: ['^DBG'] + + # Include lines. A list of regular expressions to match. It exports the lines that are + # matching any regular expression from the list. + #include_lines: ['^ERR', '^WARN'] + + # Exclude files. A list of regular expressions to match. Filebeat drops the files that + # are matching any regular expression from the list. By default, no files are dropped. + #prospector.scanner.exclude_files: ['.gz$'] + + # Optional additional fields. These fields can be freely picked + # to add additional information to the crawled log files for filtering + #fields: + # level: debug + # review: 1 + # ============================== Filebeat modules ============================== filebeat.config.modules: From f3d18f91c1aeb4d49d2214f519430e02a173b357 Mon Sep 17 00:00:00 2001 From: Blake Rouse Date: Mon, 12 Oct 2020 10:05:51 -0400 Subject: [PATCH 23/46] [Elastic Agent] Fix issue where inputs without processors defined would panic (#21628) * Fix #21581. * Add changelog entry. * Add test for processors as a map. --- x-pack/elastic-agent/CHANGELOG.next.asciidoc | 1 + .../pkg/agent/application/emitter.go | 11 +- .../pkg/agent/application/emitter_test.go | 175 ++++++++++++++++++ 3 files changed, 183 insertions(+), 4 deletions(-) diff --git a/x-pack/elastic-agent/CHANGELOG.next.asciidoc b/x-pack/elastic-agent/CHANGELOG.next.asciidoc index 73dfe8ff9a4..eb98ef39ded 100644 --- a/x-pack/elastic-agent/CHANGELOG.next.asciidoc +++ b/x-pack/elastic-agent/CHANGELOG.next.asciidoc @@ -14,6 +14,7 @@ - Thread safe sorted set {pull}21290[21290] - Copy Action store on upgrade {pull}21298[21298] - Include inputs in action store actions {pull}21298[21298] +- Fix issue where inputs without processors defined would panic {pull}21628[21628] ==== New features diff --git a/x-pack/elastic-agent/pkg/agent/application/emitter.go b/x-pack/elastic-agent/pkg/agent/application/emitter.go index 3bd06043c30..07f8e1f460b 100644 --- a/x-pack/elastic-agent/pkg/agent/application/emitter.go +++ b/x-pack/elastic-agent/pkg/agent/application/emitter.go @@ -219,17 +219,20 @@ func promoteProcessors(dict *transpiler.Dict) *transpiler.Dict { if p == nil { return dict } + var currentList *transpiler.List current, ok := dict.Find("processors") - currentList, isList := current.Value().(*transpiler.List) - if !isList { - return dict + if ok { + currentList, ok = current.Value().(*transpiler.List) + if !ok { + return dict + } } ast, _ := transpiler.NewAST(map[string]interface{}{ "processors": p, }) procs, _ := transpiler.Lookup(ast, "processors") nodes := nodesFromList(procs.Value().(*transpiler.List)) - if ok { + if ok && currentList != nil { nodes = append(nodes, nodesFromList(currentList)...) } dictNodes := dict.Value().([]transpiler.Node) diff --git a/x-pack/elastic-agent/pkg/agent/application/emitter_test.go b/x-pack/elastic-agent/pkg/agent/application/emitter_test.go index 32770eaa5df..b2286552f9f 100644 --- a/x-pack/elastic-agent/pkg/agent/application/emitter_test.go +++ b/x-pack/elastic-agent/pkg/agent/application/emitter_test.go @@ -479,6 +479,181 @@ func TestRenderInputs(t *testing.T) { }), }, }, + "inputs without processors and vars with processors": { + input: transpiler.NewKey("inputs", transpiler.NewList([]transpiler.Node{ + transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("type", transpiler.NewStrVal("logfile")), + transpiler.NewKey("streams", transpiler.NewList([]transpiler.Node{ + transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("paths", transpiler.NewList([]transpiler.Node{ + transpiler.NewStrVal("/var/log/${var1.name}.log"), + })), + }), + })), + }), + })), + expected: transpiler.NewList([]transpiler.Node{ + transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("type", transpiler.NewStrVal("logfile")), + transpiler.NewKey("streams", transpiler.NewList([]transpiler.Node{ + transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("paths", transpiler.NewList([]transpiler.Node{ + transpiler.NewStrVal("/var/log/value1.log"), + })), + }), + })), + transpiler.NewKey("processors", transpiler.NewList([]transpiler.Node{ + transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("add_fields", transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("fields", transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("custom", transpiler.NewStrVal("value1")), + })), + transpiler.NewKey("to", transpiler.NewStrVal("dynamic")), + })), + }), + })), + }), + transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("type", transpiler.NewStrVal("logfile")), + transpiler.NewKey("streams", transpiler.NewList([]transpiler.Node{ + transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("paths", transpiler.NewList([]transpiler.Node{ + transpiler.NewStrVal("/var/log/value2.log"), + })), + }), + })), + transpiler.NewKey("processors", transpiler.NewList([]transpiler.Node{ + transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("add_fields", transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("fields", transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("custom", transpiler.NewStrVal("value2")), + })), + transpiler.NewKey("to", transpiler.NewStrVal("dynamic")), + })), + }), + })), + }), + }), + varsArray: []*transpiler.Vars{ + mustMakeVarsP(map[string]interface{}{ + "var1": map[string]interface{}{ + "name": "value1", + }, + }, + "var1", + []map[string]interface{}{ + { + "add_fields": map[string]interface{}{ + "fields": map[string]interface{}{ + "custom": "value1", + }, + "to": "dynamic", + }, + }, + }), + mustMakeVarsP(map[string]interface{}{ + "var1": map[string]interface{}{ + "name": "value2", + }, + }, + "var1", + []map[string]interface{}{ + { + "add_fields": map[string]interface{}{ + "fields": map[string]interface{}{ + "custom": "value2", + }, + "to": "dynamic", + }, + }, + }), + }, + }, + "processors incorrectly a map": { + input: transpiler.NewKey("inputs", transpiler.NewList([]transpiler.Node{ + transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("type", transpiler.NewStrVal("logfile")), + transpiler.NewKey("streams", transpiler.NewList([]transpiler.Node{ + transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("paths", transpiler.NewList([]transpiler.Node{ + transpiler.NewStrVal("/var/log/${var1.name}.log"), + })), + }), + })), + transpiler.NewKey("processors", transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("add_fields", transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("invalid", transpiler.NewStrVal("value")), + })), + })), + }), + })), + expected: transpiler.NewList([]transpiler.Node{ + transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("type", transpiler.NewStrVal("logfile")), + transpiler.NewKey("streams", transpiler.NewList([]transpiler.Node{ + transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("paths", transpiler.NewList([]transpiler.Node{ + transpiler.NewStrVal("/var/log/value1.log"), + })), + }), + })), + transpiler.NewKey("processors", transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("add_fields", transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("invalid", transpiler.NewStrVal("value")), + })), + })), + }), + transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("type", transpiler.NewStrVal("logfile")), + transpiler.NewKey("streams", transpiler.NewList([]transpiler.Node{ + transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("paths", transpiler.NewList([]transpiler.Node{ + transpiler.NewStrVal("/var/log/value2.log"), + })), + }), + })), + transpiler.NewKey("processors", transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("add_fields", transpiler.NewDict([]transpiler.Node{ + transpiler.NewKey("invalid", transpiler.NewStrVal("value")), + })), + })), + }), + }), + varsArray: []*transpiler.Vars{ + mustMakeVarsP(map[string]interface{}{ + "var1": map[string]interface{}{ + "name": "value1", + }, + }, + "var1", + []map[string]interface{}{ + { + "add_fields": map[string]interface{}{ + "fields": map[string]interface{}{ + "custom": "value1", + }, + "to": "dynamic", + }, + }, + }), + mustMakeVarsP(map[string]interface{}{ + "var1": map[string]interface{}{ + "name": "value2", + }, + }, + "var1", + []map[string]interface{}{ + { + "add_fields": map[string]interface{}{ + "fields": map[string]interface{}{ + "custom": "value2", + }, + "to": "dynamic", + }, + }, + }), + }, + }, } for name, test := range testcases { From cf11c8b5fcae3e3eed12769308f40878b893ef0f Mon Sep 17 00:00:00 2001 From: DeDe Morton Date: Mon, 12 Oct 2020 11:55:28 -0700 Subject: [PATCH 24/46] Fix conditional coding to remove seccomp info from Winlogbeat (#21652) --- libbeat/docs/shared-securing-beat.asciidoc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libbeat/docs/shared-securing-beat.asciidoc b/libbeat/docs/shared-securing-beat.asciidoc index b8dcc3b1957..e1c47d91f2c 100644 --- a/libbeat/docs/shared-securing-beat.asciidoc +++ b/libbeat/docs/shared-securing-beat.asciidoc @@ -29,11 +29,13 @@ For secure communication between APM Server and APM Agents, see <> endif::[] +endif::[] // APM HTTPS information ifdef::beat-specific-security[] @@ -70,5 +72,7 @@ endif::[] // Linux Seccomp ifndef::serverless[] +ifndef::win_only[] include::./security/linux-seccomp.asciidoc[] endif::[] +endif::[] From 9ab0a918237369343cab4e7d17ac09d08e887625 Mon Sep 17 00:00:00 2001 From: Adrian Serrano Date: Tue, 13 Oct 2020 00:43:40 +0200 Subject: [PATCH 25/46] Fix concurrent map read and write in socket dataset (#21690) This fixes a panic caused by a concurrent map read and write in Auditbeat's system/socket dataset. Fixes #21192 --- CHANGELOG.next.asciidoc | 1 + .../auditbeat/module/system/socket/events.go | 4 +-- .../module/system/socket/socket_linux.go | 2 +- .../auditbeat/module/system/socket/state.go | 11 ++++++-- .../module/system/socket/state_test.go | 26 +++++++++++++++++++ 5 files changed, 39 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index cc8fb52396a..f01136c441e 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -195,6 +195,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - auditd: Fix typo in `event.action` of `removed-user-role-from`. {pull}19300[19300] - auditd: Fix typo in `event.action` of `used-suspicious-link`. {pull}19300[19300] - system/socket: Fix kprobe grouping to allow running more than one instance. {pull}20325[20325] +- system/socket: Fixed a crash due to concurrent map read and write. {issue}21192[21192] {pull}21690[21690] *Filebeat* diff --git a/x-pack/auditbeat/module/system/socket/events.go b/x-pack/auditbeat/module/system/socket/events.go index 76d91a8db24..c1937cb0302 100644 --- a/x-pack/auditbeat/module/system/socket/events.go +++ b/x-pack/auditbeat/module/system/socket/events.go @@ -872,8 +872,8 @@ type execveCall struct { creds *commitCreds } -func (e *execveCall) getProcess() process { - p := process{ +func (e *execveCall) getProcess() *process { + p := &process{ pid: e.Meta.PID, path: readCString(e.Path[:]), created: kernelTime(e.Meta.Timestamp), diff --git a/x-pack/auditbeat/module/system/socket/socket_linux.go b/x-pack/auditbeat/module/system/socket/socket_linux.go index 78fdd8ae4ca..11f8a22289e 100644 --- a/x-pack/auditbeat/module/system/socket/socket_linux.go +++ b/x-pack/auditbeat/module/system/socket/socket_linux.go @@ -158,7 +158,7 @@ func (m *MetricSet) Run(r mb.PushReporterV2) { } else { for _, p := range procs { if i, err := p.Info(); err == nil { - process := process{ + process := &process{ name: i.Name, pid: uint32(i.PID), args: i.Args, diff --git a/x-pack/auditbeat/module/system/socket/state.go b/x-pack/auditbeat/module/system/socket/state.go index c7b3ac761a7..fa612b56e1a 100644 --- a/x-pack/auditbeat/module/system/socket/state.go +++ b/x-pack/auditbeat/module/system/socket/state.go @@ -214,6 +214,9 @@ func (f *flow) Timestamp() time.Time { } type process struct { + // RWMutex is used to arbitrate reads and writes to resolvedDomains. + sync.RWMutex + pid uint32 name, path string args []string @@ -229,6 +232,8 @@ type process struct { } func (p *process) addTransaction(tr dns.Transaction) { + p.Lock() + defer p.Unlock() if p.resolvedDomains == nil { p.resolvedDomains = make(map[string]string) } @@ -239,6 +244,8 @@ func (p *process) addTransaction(tr dns.Transaction) { // ResolveIP returns the domain associated with the given IP. func (p *process) ResolveIP(ip net.IP) (domain string, found bool) { + p.RLock() + defer p.RUnlock() domain, found = p.resolvedDomains[ip.String()] return } @@ -542,13 +549,13 @@ func (s *state) ExpireOlder() { s.dns.CleanUp() } -func (s *state) CreateProcess(p process) error { +func (s *state) CreateProcess(p *process) error { if p.pid == 0 { return errors.New("can't create process with PID 0") } s.Lock() defer s.Unlock() - s.processes[p.pid] = &p + s.processes[p.pid] = p if p.createdTime == (time.Time{}) { p.createdTime = s.kernTimestampToTime(p.created) } diff --git a/x-pack/auditbeat/module/system/socket/state_test.go b/x-pack/auditbeat/module/system/socket/state_test.go index 75b73a374d1..89ba0cde9db 100644 --- a/x-pack/auditbeat/module/system/socket/state_test.go +++ b/x-pack/auditbeat/module/system/socket/state_test.go @@ -11,6 +11,7 @@ import ( "fmt" "net" "os" + "sync" "testing" "time" @@ -835,3 +836,28 @@ func TestSocketReuse(t *testing.T) { } assert.Len(t, flows, 1) } + +func TestProcessDNSRace(t *testing.T) { + p := new(process) + var wg sync.WaitGroup + wg.Add(2) + address := func(i byte) net.IP { return net.IPv4(172, 16, 0, i) } + go func() { + for i := byte(255); i > 0; i-- { + p.addTransaction(dns.Transaction{ + Client: net.UDPAddr{IP: net.IPv4(10, 20, 30, 40)}, + Server: net.UDPAddr{IP: net.IPv4(10, 20, 30, 41)}, + Domain: "example.net", + Addresses: []net.IP{address(i)}, + }) + } + wg.Done() + }() + go func() { + for i := byte(255); i > 0; i-- { + p.ResolveIP(address(i)) + } + wg.Done() + }() + wg.Wait() +} From 5e786b383598d252cf200ed22f213d8d5f33ee0d Mon Sep 17 00:00:00 2001 From: Michal Pristas Date: Tue, 13 Oct 2020 09:17:00 +0200 Subject: [PATCH 26/46] [Ingest Manager] Syncing unpacked files (#21706) [Ingest Manager] Syncing unpacked files (#21706) --- .../pkg/artifact/install/tar/tar_installer.go | 6 ++++++ .../pkg/artifact/install/zip/zip_installer.go | 11 +++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/x-pack/elastic-agent/pkg/artifact/install/tar/tar_installer.go b/x-pack/elastic-agent/pkg/artifact/install/tar/tar_installer.go index 5c7f0f593a3..b9e621c71ac 100644 --- a/x-pack/elastic-agent/pkg/artifact/install/tar/tar_installer.go +++ b/x-pack/elastic-agent/pkg/artifact/install/tar/tar_installer.go @@ -102,6 +102,12 @@ func unpack(r io.Reader, dir string) error { if closeErr := wf.Close(); closeErr != nil && err == nil { err = closeErr } + + // sometimes we try executing binary too fast and run into text file busy after unpacking + // syncing prevents this + if syncErr := wf.Sync(); syncErr != nil && err == nil { + err = syncErr + } if err != nil { return fmt.Errorf("TarInstaller: error writing to %s: %v", abs, err) } diff --git a/x-pack/elastic-agent/pkg/artifact/install/zip/zip_installer.go b/x-pack/elastic-agent/pkg/artifact/install/zip/zip_installer.go index ffc90f2dce8..29cdb66f852 100644 --- a/x-pack/elastic-agent/pkg/artifact/install/zip/zip_installer.go +++ b/x-pack/elastic-agent/pkg/artifact/install/zip/zip_installer.go @@ -102,9 +102,16 @@ func (i *Installer) unzip(artifactPath string) error { return err } defer func() { - if cerr := f.Close(); cerr != nil { - err = multierror.Append(err, cerr) + if closeErr := f.Close(); closeErr != nil { + err = multierror.Append(err, closeErr) } + + // sometimes we try executing binary too fast and run into text file busy after unpacking + // syncing prevents this + if syncErr := f.Sync(); syncErr != nil { + err = multierror.Append(err, syncErr) + } + }() if _, err = io.Copy(f, rc); err != nil { From ed772cd8fe1c478318eb5b9622e022412b577df2 Mon Sep 17 00:00:00 2001 From: Michal Pristas Date: Tue, 13 Oct 2020 12:17:29 +0200 Subject: [PATCH 27/46] [Ingest Manager] Change Sync/Close call order (#21735) [Ingest Manager] Change Sync/Close call order (#21735) --- .../pkg/artifact/install/tar/tar_installer.go | 14 +++++++++----- .../pkg/artifact/install/zip/zip_installer.go | 11 ++++------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/x-pack/elastic-agent/pkg/artifact/install/tar/tar_installer.go b/x-pack/elastic-agent/pkg/artifact/install/tar/tar_installer.go index b9e621c71ac..74a74e4c6bc 100644 --- a/x-pack/elastic-agent/pkg/artifact/install/tar/tar_installer.go +++ b/x-pack/elastic-agent/pkg/artifact/install/tar/tar_installer.go @@ -99,15 +99,19 @@ func unpack(r io.Reader, dir string) error { } _, err = io.Copy(wf, tr) + + if err == nil { + // sometimes we try executing binary too fast and run into text file busy after unpacking + // syncing prevents this + if syncErr := wf.Sync(); syncErr != nil { + err = syncErr + } + } + if closeErr := wf.Close(); closeErr != nil && err == nil { err = closeErr } - // sometimes we try executing binary too fast and run into text file busy after unpacking - // syncing prevents this - if syncErr := wf.Sync(); syncErr != nil && err == nil { - err = syncErr - } if err != nil { return fmt.Errorf("TarInstaller: error writing to %s: %v", abs, err) } diff --git a/x-pack/elastic-agent/pkg/artifact/install/zip/zip_installer.go b/x-pack/elastic-agent/pkg/artifact/install/zip/zip_installer.go index 29cdb66f852..b565f630a73 100644 --- a/x-pack/elastic-agent/pkg/artifact/install/zip/zip_installer.go +++ b/x-pack/elastic-agent/pkg/artifact/install/zip/zip_installer.go @@ -105,18 +105,15 @@ func (i *Installer) unzip(artifactPath string) error { if closeErr := f.Close(); closeErr != nil { err = multierror.Append(err, closeErr) } - - // sometimes we try executing binary too fast and run into text file busy after unpacking - // syncing prevents this - if syncErr := f.Sync(); syncErr != nil { - err = multierror.Append(err, syncErr) - } - }() if _, err = io.Copy(f, rc); err != nil { return err } + + // sometimes we try executing binary too fast and run into text file busy after unpacking + // syncing prevents this + f.Sync() } return nil } From 5536fb6cf02480c3db685aa4f27889266cd20a32 Mon Sep 17 00:00:00 2001 From: Chris Mark Date: Tue, 13 Oct 2020 13:28:11 +0300 Subject: [PATCH 28/46] Add istiod metricset (#21519) --- CHANGELOG.next.asciidoc | 1 + .../docs/images/metricbeat-istio-overview.png | Bin 0 -> 187983 bytes metricbeat/docs/modules/istio.asciidoc | 24 +- metricbeat/docs/modules_list.asciidoc | 2 +- x-pack/metricbeat/metricbeat.reference.yml | 7 + .../module/istio/_meta/config.reference.yml | 7 + .../metricbeat/module/istio/_meta/config.yml | 7 + .../module/istio/_meta/docs.asciidoc | 17 +- .../dashboard/Metricbeat-istio-overview.json | 1762 +++++++++++++++++ x-pack/metricbeat/module/istio/fields.go | 2 +- .../module/istio/istiod/_meta/docs.ascoodoc | 4 + .../module/istio/istiod/_meta/fields.yml | 1 + .../istio/istiod/_meta/testdata/config.yml | 5 + .../istiod/_meta/testdata/istiod.v1.7.1.plain | 499 +++++ .../istiod.v1.7.1.plain-expected.json | 843 ++++++++ .../module/istio/istiod/istiod_test.go | 32 + .../module/istio/istiod/manifest.yml | 11 + x-pack/metricbeat/module/istio/module.yaml | 1 - x-pack/metricbeat/module/istio/module.yml | 3 + .../metricbeat/modules.d/istio.yml.disabled | 7 + 20 files changed, 3228 insertions(+), 7 deletions(-) create mode 100644 metricbeat/docs/images/metricbeat-istio-overview.png create mode 100644 x-pack/metricbeat/module/istio/_meta/kibana/7/dashboard/Metricbeat-istio-overview.json create mode 100644 x-pack/metricbeat/module/istio/istiod/_meta/docs.ascoodoc create mode 100644 x-pack/metricbeat/module/istio/istiod/_meta/fields.yml create mode 100644 x-pack/metricbeat/module/istio/istiod/_meta/testdata/config.yml create mode 100644 x-pack/metricbeat/module/istio/istiod/_meta/testdata/istiod.v1.7.1.plain create mode 100644 x-pack/metricbeat/module/istio/istiod/_meta/testdata/istiod.v1.7.1.plain-expected.json create mode 100644 x-pack/metricbeat/module/istio/istiod/istiod_test.go create mode 100644 x-pack/metricbeat/module/istio/istiod/manifest.yml delete mode 100644 x-pack/metricbeat/module/istio/module.yaml create mode 100644 x-pack/metricbeat/module/istio/module.yml diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index f01136c441e..4ca46560ab5 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -453,6 +453,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Add Cloud Foundry tags in related events. {pull}21177[21177] - Cloud Foundry metadata is cached to disk. {pull}20775[20775] - Add option to select the type of index template to load: legacy, component, index. {pull}21212[21212] +- Add istiod metricset. {pull}21519[21519] - Release `add_cloudfoundry_metadata` as GA. {pull}21525[21525] - Add support for OpenStack SSL metadata APIs in `add_cloud_metadata`. {pull}21590[21590] diff --git a/metricbeat/docs/images/metricbeat-istio-overview.png b/metricbeat/docs/images/metricbeat-istio-overview.png new file mode 100644 index 0000000000000000000000000000000000000000..139fe9260d49bd0d0034ad292987f67be37468ce GIT binary patch literal 187983 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�)!0&tU;~T5q=NGJC@b*1d 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 literal 0 HcmV?d00001 diff --git a/metricbeat/docs/modules/istio.asciidoc b/metricbeat/docs/modules/istio.asciidoc index 52be7886e04..bfda2a9588d 100644 --- a/metricbeat/docs/modules/istio.asciidoc +++ b/metricbeat/docs/modules/istio.asciidoc @@ -8,15 +8,28 @@ This file is generated! See scripts/mage/docs_collector.go beta[] -This is the Istio module. The Istio module collects metrics from the +This is the Istio module. +This module is compatible with versions before `1.5` of Istio where microservices architecture is used. If using +versions priot to `1.5` then `mesh`, `mixer`, `pilot`, `galley`, `citadel` metricsets should be used. +wehre the Istio module collects metrics from the Istio https://istio.io/v1.4/docs/tasks/observability/metrics/querying-metrics/#about-the-prometheus-add-on[prometheus exporters endpoints]. +For versions after `1.5`, `istiod` metricset can be used which collects metrics directly from Istio Daemon. The default metricsets are `mesh`, `mixer`, `pilot`, `galley`, `citadel`. [float] === Compatibility -The Istio module is tested with Istio `1.4`. +The Istio module is tested with Istio `1.4` for `mesh`, `mixer`, `pilot`, `galley`, `citadel`. +The Istio module is tested with Istio `1.7` for `istiod`. + +[float] +=== Dashboard + +The Istio module includes a predefined dashboard with overview information about Istio Daemon. +This dashboard is only compatible with versions of Istio after `1.5` which should be monitored with `istiod` metricset. + +image::./images/metricbeat-istio-overview.png[] [float] @@ -62,6 +75,13 @@ metricbeat.modules: period: 10s # use istio-pilot.istio-system:15014, when deploying Metricbeat in a kubernetes cluster as Pod or Daemonset hosts: ["localhost:15014"] + +# Istio istiod to monitor the Istio Daemon for versions after 1.5 of Istio. +- module: istio + metricsets: ['istiod'] + period: 10s + # use istiod.istio-system:15014, when deploying Metricbeat in a kubernetes cluster as Pod or Daemonset + hosts: ['localhost:15014'] ---- [float] diff --git a/metricbeat/docs/modules_list.asciidoc b/metricbeat/docs/modules_list.asciidoc index b37f7f831a3..1bc81071e76 100644 --- a/metricbeat/docs/modules_list.asciidoc +++ b/metricbeat/docs/modules_list.asciidoc @@ -132,7 +132,7 @@ This file is generated! See scripts/mage/docs_collector.go .3+| .3+| |<> beta[] |<> beta[] |<> beta[] -|<> beta[] |image:./images/icon-no.png[No prebuilt dashboards] | +|<> beta[] |image:./images/icon-yes.png[Prebuilt dashboards are available] | .5+| .5+| |<> beta[] |<> beta[] |<> beta[] diff --git a/x-pack/metricbeat/metricbeat.reference.yml b/x-pack/metricbeat/metricbeat.reference.yml index bd99c2cf508..b7b62643353 100644 --- a/x-pack/metricbeat/metricbeat.reference.yml +++ b/x-pack/metricbeat/metricbeat.reference.yml @@ -693,6 +693,13 @@ metricbeat.modules: # use istio-pilot.istio-system:15014, when deploying Metricbeat in a kubernetes cluster as Pod or Daemonset hosts: ["localhost:15014"] +# Istio istiod to monitor the Istio Daemon for versions after 1.5 of Istio. +- module: istio + metricsets: ['istiod'] + period: 10s + # use istiod.istio-system:15014, when deploying Metricbeat in a kubernetes cluster as Pod or Daemonset + hosts: ['localhost:15014'] + #------------------------------- Jolokia Module ------------------------------- - module: jolokia #metricsets: ["jmx"] diff --git a/x-pack/metricbeat/module/istio/_meta/config.reference.yml b/x-pack/metricbeat/module/istio/_meta/config.reference.yml index c3e940f80e5..146728fdfcd 100644 --- a/x-pack/metricbeat/module/istio/_meta/config.reference.yml +++ b/x-pack/metricbeat/module/istio/_meta/config.reference.yml @@ -32,3 +32,10 @@ period: 10s # use istio-pilot.istio-system:15014, when deploying Metricbeat in a kubernetes cluster as Pod or Daemonset hosts: ["localhost:15014"] + +# Istio istiod to monitor the Istio Daemon for versions after 1.5 of Istio. +- module: istio + metricsets: ['istiod'] + period: 10s + # use istiod.istio-system:15014, when deploying Metricbeat in a kubernetes cluster as Pod or Daemonset + hosts: ['localhost:15014'] diff --git a/x-pack/metricbeat/module/istio/_meta/config.yml b/x-pack/metricbeat/module/istio/_meta/config.yml index c3e940f80e5..146728fdfcd 100644 --- a/x-pack/metricbeat/module/istio/_meta/config.yml +++ b/x-pack/metricbeat/module/istio/_meta/config.yml @@ -32,3 +32,10 @@ period: 10s # use istio-pilot.istio-system:15014, when deploying Metricbeat in a kubernetes cluster as Pod or Daemonset hosts: ["localhost:15014"] + +# Istio istiod to monitor the Istio Daemon for versions after 1.5 of Istio. +- module: istio + metricsets: ['istiod'] + period: 10s + # use istiod.istio-system:15014, when deploying Metricbeat in a kubernetes cluster as Pod or Daemonset + hosts: ['localhost:15014'] diff --git a/x-pack/metricbeat/module/istio/_meta/docs.asciidoc b/x-pack/metricbeat/module/istio/_meta/docs.asciidoc index 0a52294d1fc..cfba8ec7ce8 100644 --- a/x-pack/metricbeat/module/istio/_meta/docs.asciidoc +++ b/x-pack/metricbeat/module/istio/_meta/docs.asciidoc @@ -1,9 +1,22 @@ -This is the Istio module. The Istio module collects metrics from the +This is the Istio module. +This module is compatible with versions before `1.5` of Istio where microservices architecture is used. If using +versions priot to `1.5` then `mesh`, `mixer`, `pilot`, `galley`, `citadel` metricsets should be used. +wehre the Istio module collects metrics from the Istio https://istio.io/v1.4/docs/tasks/observability/metrics/querying-metrics/#about-the-prometheus-add-on[prometheus exporters endpoints]. +For versions after `1.5`, `istiod` metricset can be used which collects metrics directly from Istio Daemon. The default metricsets are `mesh`, `mixer`, `pilot`, `galley`, `citadel`. [float] === Compatibility -The Istio module is tested with Istio `1.4`. +The Istio module is tested with Istio `1.4` for `mesh`, `mixer`, `pilot`, `galley`, `citadel`. +The Istio module is tested with Istio `1.7` for `istiod`. + +[float] +=== Dashboard + +The Istio module includes a predefined dashboard with overview information about Istio Daemon. +This dashboard is only compatible with versions of Istio after `1.5` which should be monitored with `istiod` metricset. + +image::./images/metricbeat-istio-overview.png[] diff --git a/x-pack/metricbeat/module/istio/_meta/kibana/7/dashboard/Metricbeat-istio-overview.json b/x-pack/metricbeat/module/istio/_meta/kibana/7/dashboard/Metricbeat-istio-overview.json new file mode 100644 index 00000000000..72b995c2382 --- /dev/null +++ b/x-pack/metricbeat/module/istio/_meta/kibana/7/dashboard/Metricbeat-istio-overview.json @@ -0,0 +1,1762 @@ +{ + "objects": [ + { + "attributes": { + "description": "Overview of the Istiod Service status", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "optionsJSON": { + "hidePanelTitles": false, + "useMargins": true + }, + "panelsJSON": [ + { + "embeddableConfig": { + "title": "Pilot Proxy Queue Time" + }, + "gridData": { + "h": 9, + "i": "cd1bbc4f-95de-4156-a3ef-c091cf6402c0", + "w": 12, + "x": 0, + "y": 0 + }, + "panelIndex": "cd1bbc4f-95de-4156-a3ef-c091cf6402c0", + "panelRefName": "panel_0", + "title": "Pilot Proxy Queue Time", + "version": "7.8.0" + }, + { + "embeddableConfig": { + "title": "Pilot xds Push Time" + }, + "gridData": { + "h": 9, + "i": "06af11e6-e026-48db-a06b-b34b402b535b", + "w": 12, + "x": 12, + "y": 0 + }, + "panelIndex": "06af11e6-e026-48db-a06b-b34b402b535b", + "panelRefName": "panel_1", + "title": "Pilot xds Push Time", + "version": "7.8.0" + }, + { + "embeddableConfig": { + "title": "Pilot xds Pushes" + }, + "gridData": { + "h": 9, + "i": "d9a49bf0-f88b-4d4f-a1e2-74fbd482f77c", + "w": 11, + "x": 24, + "y": 0 + }, + "panelIndex": "d9a49bf0-f88b-4d4f-a1e2-74fbd482f77c", + "panelRefName": "panel_2", + "title": "Pilot xds Pushes", + "version": "7.8.0" + }, + { + "embeddableConfig": { + "title": "Pilot Inbound Updates" + }, + "gridData": { + "h": 9, + "i": "a8e47ef0-03db-419f-890f-0880d674682c", + "w": 13, + "x": 35, + "y": 0 + }, + "panelIndex": "a8e47ef0-03db-419f-890f-0880d674682c", + "panelRefName": "panel_3", + "title": "Pilot Inbound Updates", + "version": "7.8.0" + }, + { + "embeddableConfig": { + "title": "Citadel Cert Issuane" + }, + "gridData": { + "h": 9, + "i": "e708abfa-5a95-483c-9bb2-4470ee913f3c", + "w": 12, + "x": 0, + "y": 9 + }, + "panelIndex": "e708abfa-5a95-483c-9bb2-4470ee913f3c", + "panelRefName": "panel_4", + "title": "Citadel Cert Issuane", + "version": "7.8.0" + }, + { + "embeddableConfig": { + "title": "Galley Validation Failed" + }, + "gridData": { + "h": 9, + "i": "724f0f9e-2186-4ddd-859c-edb2649b8c0f", + "w": 12, + "x": 12, + "y": 9 + }, + "panelIndex": "724f0f9e-2186-4ddd-859c-edb2649b8c0f", + "panelRefName": "panel_5", + "title": "Galley Validation Failed", + "version": "7.8.0" + }, + { + "embeddableConfig": { + "title": "Pods witout IP", + "vis": null + }, + "gridData": { + "h": 9, + "i": "32eaa989-a4f9-4d31-97cb-684f31488aa8", + "w": 8, + "x": 24, + "y": 9 + }, + "panelIndex": "32eaa989-a4f9-4d31-97cb-684f31488aa8", + "panelRefName": "panel_6", + "title": "Pods witout IP", + "version": "7.8.0" + }, + { + "embeddableConfig": { + "title": "Pilot Virtual Services", + "vis": null + }, + "gridData": { + "h": 9, + "i": "6a8463fe-b7cb-4cd8-bf01-f7ca6a185178", + "w": 8, + "x": 32, + "y": 9 + }, + "panelIndex": "6a8463fe-b7cb-4cd8-bf01-f7ca6a185178", + "panelRefName": "panel_7", + "title": "Pilot Virtual Services", + "version": "7.8.0" + }, + { + "embeddableConfig": { + "title": "Pilot Services", + "vis": null + }, + "gridData": { + "h": 9, + "i": "51ecc2f8-3c3f-4a80-b4b6-b52db10e68ad", + "w": 8, + "x": 40, + "y": 9 + }, + "panelIndex": "51ecc2f8-3c3f-4a80-b4b6-b52db10e68ad", + "panelRefName": "panel_8", + "title": "Pilot Services", + "version": "7.8.0" + }, + { + "embeddableConfig": { + "title": "Pilot Conflict Inbound Listener", + "vis": null + }, + "gridData": { + "h": 9, + "i": "0a63d980-8d93-4ce1-b5a1-ab77e589ceec", + "w": 9, + "x": 0, + "y": 18 + }, + "panelIndex": "0a63d980-8d93-4ce1-b5a1-ab77e589ceec", + "panelRefName": "panel_9", + "title": "Pilot Conflict Inbound Listener", + "version": "7.8.0" + }, + { + "embeddableConfig": { + "title": "Pilot eds instances", + "vis": null + }, + "gridData": { + "h": 9, + "i": "9fbfca4c-37b5-4a1a-924e-49fc9ef2294c", + "w": 10, + "x": 9, + "y": 18 + }, + "panelIndex": "9fbfca4c-37b5-4a1a-924e-49fc9ef2294c", + "panelRefName": "panel_10", + "title": "Pilot eds instances", + "version": "7.8.0" + }, + { + "embeddableConfig": { + "title": "Pilot xds endpoints" + }, + "gridData": { + "h": 9, + "i": "d6b2845f-9582-4863-853e-ab753f3d763e", + "w": 14, + "x": 19, + "y": 18 + }, + "panelIndex": "d6b2845f-9582-4863-853e-ab753f3d763e", + "panelRefName": "panel_11", + "title": "Pilot xds endpoints", + "version": "7.8.0" + }, + { + "embeddableConfig": { + "title": "Pilot xds expired" + }, + "gridData": { + "h": 9, + "i": "3d0dec37-26c3-490b-a45f-48b6d1baa160", + "w": 15, + "x": 33, + "y": 18 + }, + "panelIndex": "3d0dec37-26c3-490b-a45f-48b6d1baa160", + "panelRefName": "panel_12", + "title": "Pilot xds expired", + "version": "7.8.0" + } + ], + "timeRestore": false, + "title": "[Metricbeat Istio] Overview", + "version": 1 + }, + "id": "d899d3f0-0883-11eb-a3fd-1b45ec532bb3", + "migrationVersion": { + "dashboard": "7.3.0" + }, + "references": [ + { + "id": "dd1392f0-07d8-11eb-a3fd-1b45ec532bb3", + "name": "panel_0", + "type": "visualization" + }, + { + "id": "b5b3abb0-087c-11eb-a3fd-1b45ec532bb3", + "name": "panel_1", + "type": "visualization" + }, + { + "id": "f858c200-087e-11eb-a3fd-1b45ec532bb3", + "name": "panel_2", + "type": "visualization" + }, + { + "id": "aa997510-087d-11eb-a3fd-1b45ec532bb3", + "name": "panel_3", + "type": "visualization" + }, + { + "id": "506c8490-087f-11eb-a3fd-1b45ec532bb3", + "name": "panel_4", + "type": "visualization" + }, + { + "id": "98b01f00-087f-11eb-a3fd-1b45ec532bb3", + "name": "panel_5", + "type": "visualization" + }, + { + "id": "4275f710-0882-11eb-a3fd-1b45ec532bb3", + "name": "panel_6", + "type": "visualization" + }, + { + "id": "96bfe060-0882-11eb-a3fd-1b45ec532bb3", + "name": "panel_7", + "type": "visualization" + }, + { + "id": "6cfbe3f0-0882-11eb-a3fd-1b45ec532bb3", + "name": "panel_8", + "type": "visualization" + }, + { + "id": "d62a1e60-0881-11eb-a3fd-1b45ec532bb3", + "name": "panel_9", + "type": "visualization" + }, + { + "id": "12cdcce0-0882-11eb-a3fd-1b45ec532bb3", + "name": "panel_10", + "type": "visualization" + }, + { + "id": "e5f3e870-0882-11eb-a3fd-1b45ec532bb3", + "name": "panel_11", + "type": "visualization" + }, + { + "id": "0ed17c80-0883-11eb-a3fd-1b45ec532bb3", + "name": "panel_12", + "type": "visualization" + } + ], + "type": "dashboard", + "updated_at": "2020-10-07T10:23:52.518Z", + "version": "WzQ0OTIsMV0=" + }, + { + "attributes": { + "description": "Time in seconds, a proxy is in the push queue before being dequeued.", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Pilot Proxy Queue Time [Metricbeat Istio]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "7ccbe640-07d8-11eb-985d-2f490d4c2901", + "index_pattern": "metricbeat-*", + "interval": "auto", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "#6092C0", + "fill": 0, + "formatter": "number", + "id": "7ccbe641-07d8-11eb-985d-2f490d4c2901", + "label": "queue_time", + "line_width": 2, + "metrics": [ + { + "field": "prometheus.pilot_proxy_queue_time.histogram.values", + "id": "7ccbe642-07d8-11eb-985d-2f490d4c2901", + "percentiles": [ + { + "id": "88c0d000-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "25" + }, + { + "id": "03ef6580-0887-11eb-876a-9d8e5e94d2f5", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "50" + }, + { + "id": "071fe4f0-0887-11eb-876a-9d8e5e94d2f5", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "75" + }, + { + "id": "0b7164c0-0887-11eb-876a-9d8e5e94d2f5", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "90" + }, + { + "id": "0f611580-0887-11eb-876a-9d8e5e94d2f5", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "95" + }, + { + "id": "136f98e0-0887-11eb-876a-9d8e5e94d2f5", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "99" + } + ], + "type": "percentile" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_mode": "everything", + "stacked": "none", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Pilot Proxy Queue Time [Metricbeat Istio]", + "type": "metrics" + } + }, + "id": "dd1392f0-07d8-11eb-a3fd-1b45ec532bb3", + "migrationVersion": { + "visualization": "7.8.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-10-07T10:23:11.367Z", + "version": "WzQ0ODMsMV0=" + }, + { + "attributes": { + "description": "Total time in seconds Pilot takes to push lds, rds, cds and eds.", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Pilot xds Push Time [Metricbeat Istio]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "7ccbe640-07d8-11eb-985d-2f490d4c2901", + "index_pattern": "metricbeat-*", + "interval": "auto", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "#6092C0", + "fill": 0, + "filter": { + "language": "kuery", + "query": "prometheus.labels.type: \"rds\" OR prometheus.labels.type: \"lds\" OR prometheus.labels.type: \"cds\" OR prometheus.labels.type: \"eds\"" + }, + "formatter": "s,s,", + "id": "7ccbe641-07d8-11eb-985d-2f490d4c2901", + "label": "pilot_xds_push_time", + "line_width": 2, + "metrics": [ + { + "field": "prometheus.pilot_xds_push_time.histogram.values", + "id": "7ccbe642-07d8-11eb-985d-2f490d4c2901", + "percentiles": [ + { + "id": "88c0d000-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "25" + }, + { + "id": "95c750d0-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "50" + }, + { + "id": "9c5ec900-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "75" + }, + { + "id": "9f8e3700-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "95" + }, + { + "id": "a3581040-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "99" + } + ], + "type": "percentile" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_mode": "terms", + "stacked": "none", + "terms_field": "prometheus.labels.type", + "terms_size": "20", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Pilot xds Push Time [Metricbeat Istio]", + "type": "metrics" + } + }, + "id": "b5b3abb0-087c-11eb-a3fd-1b45ec532bb3", + "migrationVersion": { + "visualization": "7.8.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-10-07T10:23:48.176Z", + "version": "WzQ0ODgsMV0=" + }, + { + "attributes": { + "description": "Pilot build and send errors for lds, rds, cds and eds.", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Pilot xds Pushes [Metricbeat Istio]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "7ccbe640-07d8-11eb-985d-2f490d4c2901", + "index_pattern": "metricbeat-*", + "interval": "auto", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "#6092C0", + "fill": 0, + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "7ccbe641-07d8-11eb-985d-2f490d4c2901", + "label": "pilot_xds_pushes", + "line_width": 2, + "metrics": [ + { + "field": "prometheus.pilot_xds_pushes.counter", + "id": "7ccbe642-07d8-11eb-985d-2f490d4c2901", + "percentiles": [ + { + "id": "88c0d000-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "50" + }, + { + "id": "95c750d0-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "25" + }, + { + "id": "9c5ec900-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "75" + }, + { + "id": "9f8e3700-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "95" + }, + { + "id": "a3581040-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "99" + } + ], + "type": "max", + "unit": "" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_mode": "terms", + "stacked": "none", + "terms_field": "prometheus.labels.type", + "terms_order_by": "7ccbe642-07d8-11eb-985d-2f490d4c2901", + "terms_size": "4", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Pilot xds Pushes [Metricbeat Istio]", + "type": "metrics" + } + }, + "id": "f858c200-087e-11eb-a3fd-1b45ec532bb3", + "migrationVersion": { + "visualization": "7.8.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-10-07T09:24:59.040Z", + "version": "WzQzOTAsMV0=" + }, + { + "attributes": { + "description": "Total number of updates received by pilot.", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Pilot Inbound Updates [Metricbeat Istio]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "7ccbe640-07d8-11eb-985d-2f490d4c2901", + "index_pattern": "metricbeat-*", + "interval": "auto", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "#6092C0", + "fill": 0, + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "7ccbe641-07d8-11eb-985d-2f490d4c2901", + "label": "pilot_inbound_updates", + "line_width": 2, + "metrics": [ + { + "field": "prometheus.pilot_inbound_updates.counter", + "id": "7ccbe642-07d8-11eb-985d-2f490d4c2901", + "percentiles": [ + { + "id": "88c0d000-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "50" + }, + { + "id": "95c750d0-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "25" + }, + { + "id": "9c5ec900-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "75" + }, + { + "id": "9f8e3700-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "95" + }, + { + "id": "a3581040-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "99" + } + ], + "type": "max", + "unit": "" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_mode": "terms", + "stacked": "none", + "terms_field": "prometheus.labels.type", + "terms_order_by": "7ccbe642-07d8-11eb-985d-2f490d4c2901", + "terms_size": "10", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Pilot Inbound Updates [Metricbeat Istio]", + "type": "metrics" + } + }, + "id": "aa997510-087d-11eb-a3fd-1b45ec532bb3", + "migrationVersion": { + "visualization": "7.8.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-10-07T09:21:43.250Z", + "version": "WzQzODcsMV0=" + }, + { + "attributes": { + "description": "The number of certificates issuances that have succeeded.", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Citadel Cert Issuance [Metricbeat Istio]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "7ccbe640-07d8-11eb-985d-2f490d4c2901", + "index_pattern": "metricbeat-*", + "interval": "auto", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "#6092C0", + "fill": 0, + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "7ccbe641-07d8-11eb-985d-2f490d4c2901", + "label": "success_cert_issuance", + "line_width": 2, + "metrics": [ + { + "field": "prometheus.citadel_server_success_cert_issuance_count.counter", + "id": "7ccbe642-07d8-11eb-985d-2f490d4c2901", + "percentiles": [ + { + "id": "88c0d000-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "50" + }, + { + "id": "95c750d0-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "25" + }, + { + "id": "9c5ec900-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "75" + }, + { + "id": "9f8e3700-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "95" + }, + { + "id": "a3581040-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "99" + } + ], + "type": "max", + "unit": "" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_mode": "everything", + "stacked": "none", + "terms_field": "prometheus.labels.type", + "terms_order_by": "7ccbe642-07d8-11eb-985d-2f490d4c2901", + "terms_size": "4", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Citadel Cert Issuance [Metricbeat Istio]", + "type": "metrics" + } + }, + "id": "506c8490-087f-11eb-a3fd-1b45ec532bb3", + "migrationVersion": { + "visualization": "7.8.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-10-07T10:15:51.172Z", + "version": "WzQ0NzYsMV0=" + }, + { + "attributes": { + "description": "Resource validation failed.", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Galley Validation Failed [Metricbeat Istio]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "7ccbe640-07d8-11eb-985d-2f490d4c2901", + "index_pattern": "metricbeat-*", + "interval": "auto", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "#6092C0", + "fill": 0, + "filter": { + "language": "kuery", + "query": "" + }, + "formatter": "number", + "id": "7ccbe641-07d8-11eb-985d-2f490d4c2901", + "label": "galley_validation_failed", + "line_width": 2, + "metrics": [ + { + "field": "prometheus.galley_validation_failed.counter", + "id": "7ccbe642-07d8-11eb-985d-2f490d4c2901", + "percentiles": [ + { + "id": "88c0d000-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "50" + }, + { + "id": "95c750d0-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "25" + }, + { + "id": "9c5ec900-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "75" + }, + { + "id": "9f8e3700-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "95" + }, + { + "id": "a3581040-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "99" + } + ], + "type": "max", + "unit": "" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_mode": "terms", + "stacked": "none", + "terms_field": "prometheus.labels.resource", + "terms_order_by": "7ccbe642-07d8-11eb-985d-2f490d4c2901", + "terms_size": "10", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Galley Validation Failed [Metricbeat Istio]", + "type": "metrics" + } + }, + "id": "98b01f00-087f-11eb-a3fd-1b45ec532bb3", + "migrationVersion": { + "visualization": "7.8.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-10-07T09:30:05.212Z", + "version": "WzQzOTksMV0=" + }, + { + "attributes": { + "description": "Pods not found in the endpoint table, possibly invalid.", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.index", + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Pilot Pods without IP [Metricbeat Istio]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [ + { + "enabled": true, + "id": "1", + "params": { + "customLabel": "Pilot No IP pods", + "field": "prometheus.pilot_no_ip.value" + }, + "schema": "metric", + "type": "avg" + } + ], + "params": { + "addLegend": true, + "addTooltip": true, + "gauge": { + "alignment": "automatic", + "backStyle": "Full", + "colorSchema": "Green to Red", + "colorsRange": [ + { + "from": 0, + "to": 50 + }, + { + "from": 50, + "to": 75 + }, + { + "from": 75, + "to": 100 + } + ], + "extendRange": true, + "gaugeColorMode": "Labels", + "gaugeStyle": "Full", + "gaugeType": "Arc", + "invertColors": false, + "labels": { + "color": "black", + "show": true + }, + "orientation": "vertical", + "percentageMode": false, + "scale": { + "color": "rgba(105,112,125,0.2)", + "labels": false, + "show": true + }, + "style": { + "bgColor": true, + "bgFill": "rgba(105,112,125,0.2)", + "bgMask": false, + "bgWidth": 0.9, + "fontSize": 60, + "mask": false, + "maskBars": 50, + "subText": "", + "width": 0.9 + }, + "type": "meter" + }, + "isDisplayWarning": false, + "type": "gauge" + }, + "title": "Pilot Pods without IP [Metricbeat Istio]", + "type": "gauge" + } + }, + "id": "4275f710-0882-11eb-a3fd-1b45ec532bb3", + "migrationVersion": { + "visualization": "7.8.0" + }, + "references": [ + { + "id": "metricbeat-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2020-10-07T09:48:31.873Z", + "version": "WzQ0MjcsMV0=" + }, + { + "attributes": { + "description": "Total virtual services known to pilot.", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.index", + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Pilot Virtual Services [Metricbeat Istio]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [ + { + "enabled": true, + "id": "1", + "params": { + "customLabel": "Pilot Virtual Services", + "field": "prometheus.pilot_virt_services.value" + }, + "schema": "metric", + "type": "avg" + } + ], + "params": { + "addLegend": true, + "addTooltip": true, + "gauge": { + "alignment": "automatic", + "backStyle": "Full", + "colorSchema": "Green to Red", + "colorsRange": [ + { + "from": 0, + "to": 50 + }, + { + "from": 50, + "to": 75 + }, + { + "from": 75, + "to": 100 + } + ], + "extendRange": true, + "gaugeColorMode": "Labels", + "gaugeStyle": "Full", + "gaugeType": "Arc", + "invertColors": false, + "labels": { + "color": "black", + "show": true + }, + "orientation": "vertical", + "percentageMode": false, + "scale": { + "color": "rgba(105,112,125,0.2)", + "labels": false, + "show": true + }, + "style": { + "bgColor": true, + "bgFill": "rgba(105,112,125,0.2)", + "bgMask": false, + "bgWidth": 0.9, + "fontSize": 60, + "mask": false, + "maskBars": 50, + "subText": "", + "width": 0.9 + }, + "type": "meter" + }, + "isDisplayWarning": false, + "type": "gauge" + }, + "title": "Pilot Virtual Services [Metricbeat Istio]", + "type": "gauge" + } + }, + "id": "96bfe060-0882-11eb-a3fd-1b45ec532bb3", + "migrationVersion": { + "visualization": "7.8.0" + }, + "references": [ + { + "id": "metricbeat-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2020-10-07T09:50:53.286Z", + "version": "WzQ0MzMsMV0=" + }, + { + "attributes": { + "description": "Total services known to pilot.", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.index", + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Pilot Services [Metricbeat Istio]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [ + { + "enabled": true, + "id": "1", + "params": { + "customLabel": "Pilot Services", + "field": "prometheus.pilot_services.value" + }, + "schema": "metric", + "type": "avg" + } + ], + "params": { + "addLegend": true, + "addTooltip": true, + "gauge": { + "alignment": "automatic", + "backStyle": "Full", + "colorSchema": "Green to Red", + "colorsRange": [ + { + "from": 0, + "to": 50 + }, + { + "from": 50, + "to": 75 + }, + { + "from": 75, + "to": 100 + } + ], + "extendRange": true, + "gaugeColorMode": "Labels", + "gaugeStyle": "Full", + "gaugeType": "Arc", + "invertColors": false, + "labels": { + "color": "black", + "show": true + }, + "orientation": "vertical", + "percentageMode": false, + "scale": { + "color": "rgba(105,112,125,0.2)", + "labels": false, + "show": true + }, + "style": { + "bgColor": true, + "bgFill": "rgba(105,112,125,0.2)", + "bgMask": false, + "bgWidth": 0.9, + "fontSize": 60, + "mask": false, + "maskBars": 50, + "subText": "", + "width": 0.9 + }, + "type": "meter" + }, + "isDisplayWarning": false, + "type": "gauge" + }, + "title": "Pilot Services [Metricbeat Istio]", + "type": "gauge" + } + }, + "id": "6cfbe3f0-0882-11eb-a3fd-1b45ec532bb3", + "migrationVersion": { + "visualization": "7.8.0" + }, + "references": [ + { + "id": "metricbeat-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2020-10-07T10:04:27.677Z", + "version": "WzQ0NTksMV0=" + }, + { + "attributes": { + "description": "Number of conflicting inbound listeners.", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.index", + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Pilot Conflict Inbound Listener [Metricbeat Istio]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [ + { + "enabled": true, + "id": "1", + "params": { + "customLabel": "Pilot conflict inbound listener", + "field": "prometheus.pilot_conflict_inbound_listener.value" + }, + "schema": "metric", + "type": "avg" + } + ], + "params": { + "addLegend": true, + "addTooltip": true, + "gauge": { + "alignment": "automatic", + "backStyle": "Full", + "colorSchema": "Green to Red", + "colorsRange": [ + { + "from": 0, + "to": 50 + }, + { + "from": 50, + "to": 75 + }, + { + "from": 75, + "to": 100 + } + ], + "extendRange": true, + "gaugeColorMode": "Labels", + "gaugeStyle": "Full", + "gaugeType": "Arc", + "invertColors": false, + "labels": { + "color": "black", + "show": true + }, + "orientation": "vertical", + "percentageMode": false, + "scale": { + "color": "rgba(105,112,125,0.2)", + "labels": false, + "show": true + }, + "style": { + "bgColor": true, + "bgFill": "rgba(105,112,125,0.2)", + "bgMask": false, + "bgWidth": 0.9, + "fontSize": 60, + "mask": false, + "maskBars": 50, + "subText": "", + "width": 0.9 + }, + "type": "meter" + }, + "isDisplayWarning": false, + "type": "gauge" + }, + "title": "Pilot Conflict Inbound Listener [Metricbeat Istio]", + "type": "gauge" + } + }, + "id": "d62a1e60-0881-11eb-a3fd-1b45ec532bb3", + "migrationVersion": { + "visualization": "7.8.0" + }, + "references": [ + { + "id": "metricbeat-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2020-10-07T09:45:30.182Z", + "version": "WzQ0MjAsMV0=" + }, + { + "attributes": { + "description": "Number of clusters without instances.", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.index", + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Pilot eds Instances [Metricbeat Istio]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [ + { + "enabled": true, + "id": "1", + "params": { + "customLabel": "Pilot eds instnaces", + "field": "prometheus.pilot_eds_no_instances.value" + }, + "schema": "metric", + "type": "avg" + } + ], + "params": { + "addLegend": true, + "addTooltip": true, + "gauge": { + "alignment": "automatic", + "backStyle": "Full", + "colorSchema": "Green to Red", + "colorsRange": [ + { + "from": 0, + "to": 50 + }, + { + "from": 50, + "to": 75 + }, + { + "from": 75, + "to": 100 + } + ], + "extendRange": true, + "gaugeColorMode": "Labels", + "gaugeStyle": "Full", + "gaugeType": "Arc", + "invertColors": false, + "labels": { + "color": "black", + "show": true + }, + "orientation": "vertical", + "percentageMode": false, + "scale": { + "color": "rgba(105,112,125,0.2)", + "labels": false, + "show": true + }, + "style": { + "bgColor": true, + "bgFill": "rgba(105,112,125,0.2)", + "bgMask": false, + "bgWidth": 0.9, + "fontSize": 60, + "mask": false, + "maskBars": 50, + "subText": "", + "width": 0.9 + }, + "type": "meter" + }, + "isDisplayWarning": false, + "type": "gauge" + }, + "title": "Pilot eds Instances [Metricbeat Istio]", + "type": "gauge" + } + }, + "id": "12cdcce0-0882-11eb-a3fd-1b45ec532bb3", + "migrationVersion": { + "visualization": "7.8.0" + }, + "references": [ + { + "id": "metricbeat-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2020-10-07T09:47:11.918Z", + "version": "WzQ0MjQsMV0=" + }, + { + "attributes": { + "description": "Number of endpoints connected to this pilot using XDS.", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Pilot XDS endpoints [Metricbeat Istio]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "7ccbe640-07d8-11eb-985d-2f490d4c2901", + "index_pattern": "metricbeat-*", + "interval": "auto", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "#6092C0", + "fill": 0, + "formatter": "number", + "id": "7ccbe641-07d8-11eb-985d-2f490d4c2901", + "label": "pilot_xds", + "line_width": 2, + "metrics": [ + { + "field": "prometheus.pilot_xds.value", + "id": "7ccbe642-07d8-11eb-985d-2f490d4c2901", + "percentiles": [ + { + "id": "88c0d000-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "50" + }, + { + "id": "95c750d0-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "25" + }, + { + "id": "9c5ec900-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "75" + }, + { + "id": "9f8e3700-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "95" + }, + { + "id": "a3581040-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "99" + } + ], + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_mode": "everything", + "stacked": "none", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Pilot XDS endpoints [Metricbeat Istio]", + "type": "metrics" + } + }, + "id": "e5f3e870-0882-11eb-a3fd-1b45ec532bb3", + "migrationVersion": { + "visualization": "7.8.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-10-07T09:53:38.583Z", + "version": "WzQ0NDEsMV0=" + }, + { + "attributes": { + "description": "Total number of XDS requests with an expired nonce.", + "kibanaSavedObjectMeta": { + "searchSourceJSON": { + "filter": [], + "query": { + "language": "kuery", + "query": "" + } + } + }, + "title": "Pilot XDS expired [Metricbeat Istio]", + "uiStateJSON": {}, + "version": 1, + "visState": { + "aggs": [], + "params": { + "axis_formatter": "number", + "axis_min": 0, + "axis_position": "left", + "axis_scale": "normal", + "default_index_pattern": "metricbeat-*", + "default_timefield": "@timestamp", + "filter": { + "language": "kuery", + "query": "" + }, + "id": "7ccbe640-07d8-11eb-985d-2f490d4c2901", + "index_pattern": "metricbeat-*", + "interval": "auto", + "isModelInvalid": false, + "series": [ + { + "axis_position": "right", + "chart_type": "line", + "color": "#6092C0", + "fill": 0, + "formatter": "number", + "id": "7ccbe641-07d8-11eb-985d-2f490d4c2901", + "label": "pilot_xds_expired_nonce", + "line_width": 2, + "metrics": [ + { + "field": "prometheus.pilot_xds_expired_nonce.counter", + "id": "7ccbe642-07d8-11eb-985d-2f490d4c2901", + "percentiles": [ + { + "id": "88c0d000-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "50" + }, + { + "id": "95c750d0-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "25" + }, + { + "id": "9c5ec900-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "75" + }, + { + "id": "9f8e3700-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "95" + }, + { + "id": "a3581040-07d8-11eb-86d1-6521145f6524", + "mode": "line", + "percentile": "", + "shade": 0.2, + "value": "99" + } + ], + "type": "avg" + } + ], + "point_size": 0, + "separate_axis": 0, + "split_mode": "everything", + "stacked": "none", + "type": "timeseries", + "value_template": "{{value}}" + } + ], + "show_grid": 1, + "show_legend": 1, + "time_field": "@timestamp", + "type": "timeseries" + }, + "title": "Pilot XDS expired [Metricbeat Istio]", + "type": "metrics" + } + }, + "id": "0ed17c80-0883-11eb-a3fd-1b45ec532bb3", + "migrationVersion": { + "visualization": "7.8.0" + }, + "references": [], + "type": "visualization", + "updated_at": "2020-10-07T09:54:14.728Z", + "version": "WzQ0NDMsMV0=" + } + ], + "version": "7.8.0" +} diff --git a/x-pack/metricbeat/module/istio/fields.go b/x-pack/metricbeat/module/istio/fields.go index 40a9baed3f5..ccd41e55fe8 100644 --- a/x-pack/metricbeat/module/istio/fields.go +++ b/x-pack/metricbeat/module/istio/fields.go @@ -19,5 +19,5 @@ func init() { // AssetIstio returns asset data. // This is the base64 encoded gzipped contents of module/istio. func AssetIstio() string { - return "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" + return "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" } diff --git a/x-pack/metricbeat/module/istio/istiod/_meta/docs.ascoodoc b/x-pack/metricbeat/module/istio/istiod/_meta/docs.ascoodoc new file mode 100644 index 00000000000..61170c60413 --- /dev/null +++ b/x-pack/metricbeat/module/istio/istiod/_meta/docs.ascoodoc @@ -0,0 +1,4 @@ +This is the istiod metricset of the module istio. +This metricset collects metrics from the Istio Daemon of Istio versions higher than 1.5 + +Tested with Istio 1.7 diff --git a/x-pack/metricbeat/module/istio/istiod/_meta/fields.yml b/x-pack/metricbeat/module/istio/istiod/_meta/fields.yml new file mode 100644 index 00000000000..8033a27f5ac --- /dev/null +++ b/x-pack/metricbeat/module/istio/istiod/_meta/fields.yml @@ -0,0 +1 @@ +- release: beta diff --git a/x-pack/metricbeat/module/istio/istiod/_meta/testdata/config.yml b/x-pack/metricbeat/module/istio/istiod/_meta/testdata/config.yml new file mode 100644 index 00000000000..376691991b5 --- /dev/null +++ b/x-pack/metricbeat/module/istio/istiod/_meta/testdata/config.yml @@ -0,0 +1,5 @@ +type: http +url: "/metrics" +suffix: plain +remove_fields_from_comparison: ["prometheus.labels.instance"] +omit_documented_fields_check: ["prometheus.labels.*"] diff --git a/x-pack/metricbeat/module/istio/istiod/_meta/testdata/istiod.v1.7.1.plain b/x-pack/metricbeat/module/istio/istiod/_meta/testdata/istiod.v1.7.1.plain new file mode 100644 index 00000000000..4ec26bf4a2c --- /dev/null +++ b/x-pack/metricbeat/module/istio/istiod/_meta/testdata/istiod.v1.7.1.plain @@ -0,0 +1,499 @@ +# HELP citadel_server_csr_count The number of CSRs received by Citadel server. +# TYPE citadel_server_csr_count counter +citadel_server_csr_count 8 +# HELP citadel_server_root_cert_expiry_timestamp The unix timestamp, in seconds, when Citadel root cert will expire. A negative time indicates the cert is expired. +# TYPE citadel_server_root_cert_expiry_timestamp gauge +citadel_server_root_cert_expiry_timestamp 1.916309303e+09 +# HELP citadel_server_success_cert_issuance_count The number of certificates issuances that have succeeded. +# TYPE citadel_server_success_cert_issuance_count counter +citadel_server_success_cert_issuance_count 8 +# HELP endpoint_no_pod Endpoints without an associated pod. +# TYPE endpoint_no_pod gauge +endpoint_no_pod 0 +# HELP galley_validation_config_updates k8s webhook configuration updates +# TYPE galley_validation_config_updates counter +galley_validation_config_updates 2 +# HELP galley_validation_failed Resource validation failed +# TYPE galley_validation_failed counter +galley_validation_failed{group="networking.istio.io",reason="invalid_resource",resource="gateways",version="v1alpha3"} 1 +# HELP go_gc_duration_seconds A summary of the GC invocation durations. +# TYPE go_gc_duration_seconds summary +go_gc_duration_seconds{quantile="0"} 8.969e-06 +go_gc_duration_seconds{quantile="0.25"} 8.7539e-05 +go_gc_duration_seconds{quantile="0.5"} 0.000238037 +go_gc_duration_seconds{quantile="0.75"} 0.000783504 +go_gc_duration_seconds{quantile="1"} 0.005515511 +go_gc_duration_seconds_sum 0.013316174 +go_gc_duration_seconds_count 13 +# HELP go_goroutines Number of goroutines that currently exist. +# TYPE go_goroutines gauge +go_goroutines 345 +# HELP go_info Information about the Go environment. +# TYPE go_info gauge +go_info{version="go1.14.7"} 1 +# HELP go_memstats_alloc_bytes Number of bytes allocated and still in use. +# TYPE go_memstats_alloc_bytes gauge +go_memstats_alloc_bytes 3.1723136e+07 +# HELP go_memstats_alloc_bytes_total Total number of bytes allocated, even if freed. +# TYPE go_memstats_alloc_bytes_total counter +go_memstats_alloc_bytes_total 1.106604e+08 +# HELP go_memstats_buck_hash_sys_bytes Number of bytes used by the profiling bucket hash table. +# TYPE go_memstats_buck_hash_sys_bytes gauge +go_memstats_buck_hash_sys_bytes 1.496287e+06 +# HELP go_memstats_frees_total Total number of frees. +# TYPE go_memstats_frees_total counter +go_memstats_frees_total 982627 +# HELP go_memstats_gc_cpu_fraction The fraction of this program's available CPU time used by the GC since the program started. +# TYPE go_memstats_gc_cpu_fraction gauge +go_memstats_gc_cpu_fraction 6.904294960464036e-05 +# HELP go_memstats_gc_sys_bytes Number of bytes used for garbage collection system metadata. +# TYPE go_memstats_gc_sys_bytes gauge +go_memstats_gc_sys_bytes 3.832072e+06 +# HELP go_memstats_heap_alloc_bytes Number of heap bytes allocated and still in use. +# TYPE go_memstats_heap_alloc_bytes gauge +go_memstats_heap_alloc_bytes 3.1723136e+07 +# HELP go_memstats_heap_idle_bytes Number of heap bytes waiting to be used. +# TYPE go_memstats_heap_idle_bytes gauge +go_memstats_heap_idle_bytes 2.1651456e+07 +# HELP go_memstats_heap_inuse_bytes Number of heap bytes that are in use. +# TYPE go_memstats_heap_inuse_bytes gauge +go_memstats_heap_inuse_bytes 4.2508288e+07 +# HELP go_memstats_heap_objects Number of allocated objects. +# TYPE go_memstats_heap_objects gauge +go_memstats_heap_objects 157553 +# HELP go_memstats_heap_released_bytes Number of heap bytes released to OS. +# TYPE go_memstats_heap_released_bytes gauge +go_memstats_heap_released_bytes 1.7825792e+07 +# HELP go_memstats_heap_sys_bytes Number of heap bytes obtained from system. +# TYPE go_memstats_heap_sys_bytes gauge +go_memstats_heap_sys_bytes 6.4159744e+07 +# HELP go_memstats_last_gc_time_seconds Number of seconds since 1970 of last garbage collection. +# TYPE go_memstats_last_gc_time_seconds gauge +go_memstats_last_gc_time_seconds 1.6019028053800597e+09 +# HELP go_memstats_lookups_total Total number of pointer lookups. +# TYPE go_memstats_lookups_total counter +go_memstats_lookups_total 0 +# HELP go_memstats_mallocs_total Total number of mallocs. +# TYPE go_memstats_mallocs_total counter +go_memstats_mallocs_total 1.14018e+06 +# HELP go_memstats_mcache_inuse_bytes Number of bytes in use by mcache structures. +# TYPE go_memstats_mcache_inuse_bytes gauge +go_memstats_mcache_inuse_bytes 6944 +# HELP go_memstats_mcache_sys_bytes Number of bytes used for mcache structures obtained from system. +# TYPE go_memstats_mcache_sys_bytes gauge +go_memstats_mcache_sys_bytes 16384 +# HELP go_memstats_mspan_inuse_bytes Number of bytes in use by mspan structures. +# TYPE go_memstats_mspan_inuse_bytes gauge +go_memstats_mspan_inuse_bytes 485384 +# HELP go_memstats_mspan_sys_bytes Number of bytes used for mspan structures obtained from system. +# TYPE go_memstats_mspan_sys_bytes gauge +go_memstats_mspan_sys_bytes 573440 +# HELP go_memstats_next_gc_bytes Number of heap bytes when next garbage collection will take place. +# TYPE go_memstats_next_gc_bytes gauge +go_memstats_next_gc_bytes 5.3766592e+07 +# HELP go_memstats_other_sys_bytes Number of bytes used for other system allocations. +# TYPE go_memstats_other_sys_bytes gauge +go_memstats_other_sys_bytes 914201 +# HELP go_memstats_stack_inuse_bytes Number of bytes in use by the stack allocator. +# TYPE go_memstats_stack_inuse_bytes gauge +go_memstats_stack_inuse_bytes 2.94912e+06 +# HELP go_memstats_stack_sys_bytes Number of bytes obtained from system for stack allocator. +# TYPE go_memstats_stack_sys_bytes gauge +go_memstats_stack_sys_bytes 2.94912e+06 +# HELP go_memstats_sys_bytes Number of bytes obtained from system. +# TYPE go_memstats_sys_bytes gauge +go_memstats_sys_bytes 7.3941248e+07 +# HELP go_threads Number of OS threads created. +# TYPE go_threads gauge +go_threads 14 +# HELP grpc_server_handled_total Total number of RPCs completed on the server, regardless of success or failure. +# TYPE grpc_server_handled_total counter +grpc_server_handled_total{grpc_code="Aborted",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="Aborted",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Aborted",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Aborted",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Aborted",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Aborted",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="AlreadyExists",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="AlreadyExists",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="AlreadyExists",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="AlreadyExists",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="AlreadyExists",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="AlreadyExists",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Canceled",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="Canceled",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Canceled",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Canceled",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Canceled",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Canceled",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="DataLoss",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="DataLoss",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="DataLoss",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="DataLoss",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="DataLoss",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="DataLoss",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="DeadlineExceeded",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="DeadlineExceeded",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="DeadlineExceeded",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="DeadlineExceeded",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="DeadlineExceeded",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="DeadlineExceeded",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="FailedPrecondition",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="FailedPrecondition",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="FailedPrecondition",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="FailedPrecondition",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="FailedPrecondition",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="FailedPrecondition",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Internal",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="Internal",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Internal",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Internal",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Internal",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Internal",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="InvalidArgument",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="InvalidArgument",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="InvalidArgument",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="InvalidArgument",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="InvalidArgument",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="InvalidArgument",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="NotFound",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="NotFound",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="NotFound",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="NotFound",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="NotFound",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="NotFound",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="OK",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 8 +grpc_server_handled_total{grpc_code="OK",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="OK",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="OK",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="OK",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="OK",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="OutOfRange",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="OutOfRange",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="OutOfRange",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="OutOfRange",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="OutOfRange",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="OutOfRange",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="PermissionDenied",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="PermissionDenied",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="PermissionDenied",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="PermissionDenied",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="PermissionDenied",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="PermissionDenied",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="ResourceExhausted",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="ResourceExhausted",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="ResourceExhausted",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="ResourceExhausted",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="ResourceExhausted",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="ResourceExhausted",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unauthenticated",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="Unauthenticated",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unauthenticated",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unauthenticated",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unauthenticated",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unauthenticated",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unavailable",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="Unavailable",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unavailable",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unavailable",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unavailable",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unavailable",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unimplemented",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="Unimplemented",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unimplemented",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unimplemented",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unimplemented",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unimplemented",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unknown",grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0 +grpc_server_handled_total{grpc_code="Unknown",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unknown",grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unknown",grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unknown",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handled_total{grpc_code="Unknown",grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +# HELP grpc_server_handling_seconds Histogram of response latency (seconds) of gRPC that had been application-level handled by the server. +# TYPE grpc_server_handling_seconds histogram +grpc_server_handling_seconds_bucket{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary",le="0.005"} 7 +grpc_server_handling_seconds_bucket{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary",le="0.01"} 8 +grpc_server_handling_seconds_bucket{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary",le="0.025"} 8 +grpc_server_handling_seconds_bucket{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary",le="0.05"} 8 +grpc_server_handling_seconds_bucket{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary",le="0.1"} 8 +grpc_server_handling_seconds_bucket{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary",le="0.25"} 8 +grpc_server_handling_seconds_bucket{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary",le="0.5"} 8 +grpc_server_handling_seconds_bucket{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary",le="1"} 8 +grpc_server_handling_seconds_bucket{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary",le="2.5"} 8 +grpc_server_handling_seconds_bucket{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary",le="5"} 8 +grpc_server_handling_seconds_bucket{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary",le="10"} 8 +grpc_server_handling_seconds_bucket{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary",le="+Inf"} 8 +grpc_server_handling_seconds_sum{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 0.031283576 +grpc_server_handling_seconds_count{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 8 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.005"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.01"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.025"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.05"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.1"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.25"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="1"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="2.5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="10"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="+Inf"} 0 +grpc_server_handling_seconds_sum{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handling_seconds_count{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.005"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.01"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.025"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.05"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.1"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.25"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="1"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="2.5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="10"} 0 +grpc_server_handling_seconds_bucket{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="+Inf"} 0 +grpc_server_handling_seconds_sum{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handling_seconds_count{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handling_seconds_bucket{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream",le="0.005"} 0 +grpc_server_handling_seconds_bucket{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream",le="0.01"} 0 +grpc_server_handling_seconds_bucket{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream",le="0.025"} 0 +grpc_server_handling_seconds_bucket{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream",le="0.05"} 0 +grpc_server_handling_seconds_bucket{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream",le="0.1"} 0 +grpc_server_handling_seconds_bucket{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream",le="0.25"} 0 +grpc_server_handling_seconds_bucket{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream",le="0.5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream",le="1"} 0 +grpc_server_handling_seconds_bucket{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream",le="2.5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream",le="5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream",le="10"} 0 +grpc_server_handling_seconds_bucket{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream",le="+Inf"} 0 +grpc_server_handling_seconds_sum{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handling_seconds_count{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.005"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.01"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.025"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.05"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.1"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.25"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="1"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="2.5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="10"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream",le="+Inf"} 0 +grpc_server_handling_seconds_sum{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handling_seconds_count{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.005"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.01"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.025"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.05"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.1"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.25"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="0.5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="1"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="2.5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="5"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="10"} 0 +grpc_server_handling_seconds_bucket{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream",le="+Inf"} 0 +grpc_server_handling_seconds_sum{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_handling_seconds_count{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +# HELP grpc_server_msg_received_total Total number of RPC stream messages received on the server. +# TYPE grpc_server_msg_received_total counter +grpc_server_msg_received_total{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 8 +grpc_server_msg_received_total{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_msg_received_total{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_msg_received_total{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_msg_received_total{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_msg_received_total{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +# HELP grpc_server_msg_sent_total Total number of gRPC stream messages sent by the server. +# TYPE grpc_server_msg_sent_total counter +grpc_server_msg_sent_total{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 8 +grpc_server_msg_sent_total{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_msg_sent_total{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_msg_sent_total{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_msg_sent_total{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_msg_sent_total{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +# HELP grpc_server_started_total Total number of RPCs started on the server. +# TYPE grpc_server_started_total counter +grpc_server_started_total{grpc_method="CreateCertificate",grpc_service="istio.v1.auth.IstioCertificateService",grpc_type="unary"} 8 +grpc_server_started_total{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_started_total{grpc_method="DeltaAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_started_total{grpc_method="ServerReflectionInfo",grpc_service="grpc.reflection.v1alpha.ServerReflection",grpc_type="bidi_stream"} 0 +grpc_server_started_total{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v2.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +grpc_server_started_total{grpc_method="StreamAggregatedResources",grpc_service="envoy.service.discovery.v3.AggregatedDiscoveryService",grpc_type="bidi_stream"} 0 +# HELP istio_build Istio component build info +# TYPE istio_build gauge +istio_build{component="pilot",tag="1.7.1"} 1 +# HELP pilot_conflict_inbound_listener Number of conflicting inbound listeners. +# TYPE pilot_conflict_inbound_listener gauge +pilot_conflict_inbound_listener 0 +# HELP pilot_conflict_outbound_listener_http_over_current_tcp Number of conflicting wildcard http listeners with current wildcard tcp listener. +# TYPE pilot_conflict_outbound_listener_http_over_current_tcp gauge +pilot_conflict_outbound_listener_http_over_current_tcp 0 +# HELP pilot_conflict_outbound_listener_tcp_over_current_http Number of conflicting wildcard tcp listeners with current wildcard http listener. +# TYPE pilot_conflict_outbound_listener_tcp_over_current_http gauge +pilot_conflict_outbound_listener_tcp_over_current_http 0 +# HELP pilot_conflict_outbound_listener_tcp_over_current_tcp Number of conflicting tcp listeners with current tcp listener. +# TYPE pilot_conflict_outbound_listener_tcp_over_current_tcp gauge +pilot_conflict_outbound_listener_tcp_over_current_tcp 0 +# HELP pilot_destrule_subsets Duplicate subsets across destination rules for same host +# TYPE pilot_destrule_subsets gauge +pilot_destrule_subsets 0 +# HELP pilot_duplicate_envoy_clusters Duplicate envoy clusters caused by service entries with same hostname +# TYPE pilot_duplicate_envoy_clusters gauge +pilot_duplicate_envoy_clusters 0 +# HELP pilot_eds_no_instances Number of clusters without instances. +# TYPE pilot_eds_no_instances gauge +pilot_eds_no_instances 0 +# HELP pilot_endpoint_not_ready Endpoint found in unready state. +# TYPE pilot_endpoint_not_ready gauge +pilot_endpoint_not_ready 0 +# HELP pilot_inbound_updates Total number of updates received by pilot. +# TYPE pilot_inbound_updates counter +pilot_inbound_updates{type="config"} 90 +pilot_inbound_updates{type="eds"} 54 +pilot_inbound_updates{type="svc"} 22 +# HELP pilot_info Pilot version and build information. +# TYPE pilot_info gauge +pilot_info{version="1.7.1-4e26c697ce460dc8d3b25b25818fb0163ca16394-Clean"} 1 +# HELP pilot_k8s_cfg_events Events from k8s config. +# TYPE pilot_k8s_cfg_events counter +pilot_k8s_cfg_events{event="add",type="DestinationRule"} 4 +pilot_k8s_cfg_events{event="add",type="EnvoyFilter"} 8 +pilot_k8s_cfg_events{event="add",type="Gateway"} 1 +pilot_k8s_cfg_events{event="add",type="VirtualService"} 1 +# HELP pilot_k8s_reg_events Events from k8s registry. +# TYPE pilot_k8s_reg_events counter +pilot_k8s_reg_events{event="add",type="Endpoints"} 12 +pilot_k8s_reg_events{event="add",type="Nodes"} 1 +pilot_k8s_reg_events{event="add",type="Pods"} 22 +pilot_k8s_reg_events{event="add",type="Services"} 11 +pilot_k8s_reg_events{event="update",type="Endpoints"} 30 +pilot_k8s_reg_events{event="update",type="Nodes"} 1 +pilot_k8s_reg_events{event="update",type="Pods"} 61 +pilot_k8s_reg_events{event="updatesame",type="Endpoints"} 185 +# HELP pilot_no_ip Pods not found in the endpoint table, possibly invalid. +# TYPE pilot_no_ip gauge +pilot_no_ip 0 +# HELP pilot_proxy_convergence_time Delay in seconds between config change and a proxy receiving all required configuration. +# TYPE pilot_proxy_convergence_time histogram +pilot_proxy_convergence_time_bucket{le="0.1"} 1 +pilot_proxy_convergence_time_bucket{le="0.5"} 1 +pilot_proxy_convergence_time_bucket{le="1"} 1 +pilot_proxy_convergence_time_bucket{le="3"} 1 +pilot_proxy_convergence_time_bucket{le="5"} 1 +pilot_proxy_convergence_time_bucket{le="10"} 1 +pilot_proxy_convergence_time_bucket{le="20"} 1 +pilot_proxy_convergence_time_bucket{le="30"} 1 +pilot_proxy_convergence_time_bucket{le="+Inf"} 1 +pilot_proxy_convergence_time_sum 0.002034992 +pilot_proxy_convergence_time_count 1 +# HELP pilot_proxy_queue_time Time in seconds, a proxy is in the push queue before being dequeued. +# TYPE pilot_proxy_queue_time histogram +pilot_proxy_queue_time_bucket{le="0.1"} 41 +pilot_proxy_queue_time_bucket{le="1"} 41 +pilot_proxy_queue_time_bucket{le="3"} 41 +pilot_proxy_queue_time_bucket{le="5"} 41 +pilot_proxy_queue_time_bucket{le="10"} 41 +pilot_proxy_queue_time_bucket{le="20"} 41 +pilot_proxy_queue_time_bucket{le="30"} 41 +pilot_proxy_queue_time_bucket{le="+Inf"} 41 +pilot_proxy_queue_time_sum 0.002216352 +pilot_proxy_queue_time_count 41 +# HELP pilot_push_triggers Total number of times a push was triggered, labeled by reason for the push. +# TYPE pilot_push_triggers counter +pilot_push_triggers{type="endpoint"} 40 +pilot_push_triggers{type="proxy"} 1 +# HELP pilot_services Total services known to pilot. +# TYPE pilot_services gauge +pilot_services 11 +# HELP pilot_virt_services Total virtual services known to pilot. +# TYPE pilot_virt_services gauge +pilot_virt_services 1 +# HELP pilot_vservice_dup_domain Virtual services with dup domains. +# TYPE pilot_vservice_dup_domain gauge +pilot_vservice_dup_domain 0 +# HELP pilot_xds Number of endpoints connected to this pilot using XDS. +# TYPE pilot_xds gauge +pilot_xds{version="1.7.1"} 8 +# HELP pilot_xds_push_time Total time in seconds Pilot takes to push lds, rds, cds and eds. +# TYPE pilot_xds_push_time histogram +pilot_xds_push_time_bucket{type="cds",le="0.01"} 9 +pilot_xds_push_time_bucket{type="cds",le="0.1"} 9 +pilot_xds_push_time_bucket{type="cds",le="1"} 9 +pilot_xds_push_time_bucket{type="cds",le="3"} 9 +pilot_xds_push_time_bucket{type="cds",le="5"} 9 +pilot_xds_push_time_bucket{type="cds",le="10"} 9 +pilot_xds_push_time_bucket{type="cds",le="20"} 9 +pilot_xds_push_time_bucket{type="cds",le="30"} 9 +pilot_xds_push_time_bucket{type="cds",le="+Inf"} 9 +pilot_xds_push_time_sum{type="cds"} 0.009549363999999998 +pilot_xds_push_time_count{type="cds"} 9 +pilot_xds_push_time_bucket{type="eds",le="0.01"} 49 +pilot_xds_push_time_bucket{type="eds",le="0.1"} 49 +pilot_xds_push_time_bucket{type="eds",le="1"} 49 +pilot_xds_push_time_bucket{type="eds",le="3"} 49 +pilot_xds_push_time_bucket{type="eds",le="5"} 49 +pilot_xds_push_time_bucket{type="eds",le="10"} 49 +pilot_xds_push_time_bucket{type="eds",le="20"} 49 +pilot_xds_push_time_bucket{type="eds",le="30"} 49 +pilot_xds_push_time_bucket{type="eds",le="+Inf"} 49 +pilot_xds_push_time_sum{type="eds"} 0.008623654 +pilot_xds_push_time_count{type="eds"} 49 +pilot_xds_push_time_bucket{type="lds",le="0.01"} 9 +pilot_xds_push_time_bucket{type="lds",le="0.1"} 9 +pilot_xds_push_time_bucket{type="lds",le="1"} 9 +pilot_xds_push_time_bucket{type="lds",le="3"} 9 +pilot_xds_push_time_bucket{type="lds",le="5"} 9 +pilot_xds_push_time_bucket{type="lds",le="10"} 9 +pilot_xds_push_time_bucket{type="lds",le="20"} 9 +pilot_xds_push_time_bucket{type="lds",le="30"} 9 +pilot_xds_push_time_bucket{type="lds",le="+Inf"} 9 +pilot_xds_push_time_sum{type="lds"} 0.011886486000000002 +pilot_xds_push_time_count{type="lds"} 9 +pilot_xds_push_time_bucket{type="rds",le="0.01"} 9 +pilot_xds_push_time_bucket{type="rds",le="0.1"} 9 +pilot_xds_push_time_bucket{type="rds",le="1"} 9 +pilot_xds_push_time_bucket{type="rds",le="3"} 9 +pilot_xds_push_time_bucket{type="rds",le="5"} 9 +pilot_xds_push_time_bucket{type="rds",le="10"} 9 +pilot_xds_push_time_bucket{type="rds",le="20"} 9 +pilot_xds_push_time_bucket{type="rds",le="30"} 9 +pilot_xds_push_time_bucket{type="rds",le="+Inf"} 9 +pilot_xds_push_time_sum{type="rds"} 0.002646734 +pilot_xds_push_time_count{type="rds"} 9 +# HELP pilot_xds_pushes Pilot build and send errors for lds, rds, cds and eds. +# TYPE pilot_xds_pushes counter +pilot_xds_pushes{type="cds"} 9 +pilot_xds_pushes{type="eds"} 49 +pilot_xds_pushes{type="lds"} 9 +pilot_xds_pushes{type="rds"} 9 +# HELP process_cpu_seconds_total Total user and system CPU time spent in seconds. +# TYPE process_cpu_seconds_total counter +process_cpu_seconds_total 2.35 +# HELP process_max_fds Maximum number of open file descriptors. +# TYPE process_max_fds gauge +process_max_fds 1.048576e+06 +# HELP process_open_fds Number of open file descriptors. +# TYPE process_open_fds gauge +process_open_fds 52 +# HELP process_resident_memory_bytes Resident memory size in bytes. +# TYPE process_resident_memory_bytes gauge +process_resident_memory_bytes 1.08478464e+08 +# HELP process_start_time_seconds Start time of the process since unix epoch in seconds. +# TYPE process_start_time_seconds gauge +process_start_time_seconds 1.60190238401e+09 +# HELP process_virtual_memory_bytes Virtual memory size in bytes. +# TYPE process_virtual_memory_bytes gauge +process_virtual_memory_bytes 8.08730624e+08 +# HELP process_virtual_memory_max_bytes Maximum amount of virtual memory available in bytes. +# TYPE process_virtual_memory_max_bytes gauge +process_virtual_memory_max_bytes -1 +# HELP statsd_metric_mapper_cache_gets_total The count of total metric cache gets. +# TYPE statsd_metric_mapper_cache_gets_total counter +statsd_metric_mapper_cache_gets_total 0 +# HELP statsd_metric_mapper_cache_hits_total The count of total metric cache hits. +# TYPE statsd_metric_mapper_cache_hits_total counter +statsd_metric_mapper_cache_hits_total 0 +# HELP statsd_metric_mapper_cache_length The count of unique metrics currently cached. +# TYPE statsd_metric_mapper_cache_length gauge +statsd_metric_mapper_cache_length 0 diff --git a/x-pack/metricbeat/module/istio/istiod/_meta/testdata/istiod.v1.7.1.plain-expected.json b/x-pack/metricbeat/module/istio/istiod/_meta/testdata/istiod.v1.7.1.plain-expected.json new file mode 100644 index 00000000000..2d96ee6b90e --- /dev/null +++ b/x-pack/metricbeat/module/istio/istiod/_meta/testdata/istiod.v1.7.1.plain-expected.json @@ -0,0 +1,843 @@ +[ + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "event": "add", + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "Gateway" + }, + "pilot_k8s_cfg_events": { + "counter": 1, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "event": "add", + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "Nodes" + }, + "pilot_k8s_reg_events": { + "counter": 1, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "event": "update", + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "Nodes" + }, + "pilot_k8s_reg_events": { + "counter": 1, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "event": "add", + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "DestinationRule" + }, + "pilot_k8s_cfg_events": { + "counter": 4, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "proxy" + }, + "pilot_push_triggers": { + "counter": 1, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "lds" + }, + "pilot_xds_push_time": { + "histogram": { + "counts": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "values": [ + 0.005, + 0.05500000000000001, + 0.55, + 2, + 4, + 7.5, + 15, + 25, + 40 + ] + } + }, + "pilot_xds_pushes": { + "counter": 9, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "svc" + }, + "pilot_inbound_updates": { + "counter": 22, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "eds" + }, + "pilot_inbound_updates": { + "counter": 54, + "rate": 0 + }, + "pilot_xds_push_time": { + "histogram": { + "counts": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "values": [ + 0.005, + 0.05500000000000001, + 0.55, + 2, + 4, + 7.5, + 15, + 25, + 40 + ] + } + }, + "pilot_xds_pushes": { + "counter": 49, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "event": "add", + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "EnvoyFilter" + }, + "pilot_k8s_cfg_events": { + "counter": 8, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "endpoint" + }, + "pilot_push_triggers": { + "counter": 40, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "instance": "127.0.0.1:51143", + "job": "istio", + "version": "1.7.1" + }, + "pilot_xds": { + "value": 8 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "event": "add", + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "Services" + }, + "pilot_k8s_reg_events": { + "counter": 11, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "event": "update", + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "Pods" + }, + "pilot_k8s_reg_events": { + "counter": 61, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "event": "update", + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "Endpoints" + }, + "pilot_k8s_reg_events": { + "counter": 30, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "instance": "127.0.0.1:51143", + "job": "istio", + "version": "1.7.1-4e26c697ce460dc8d3b25b25818fb0163ca16394-Clean" + }, + "pilot_info": { + "value": 1 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "event": "add", + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "Endpoints" + }, + "pilot_k8s_reg_events": { + "counter": 12, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "event": "add", + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "VirtualService" + }, + "pilot_k8s_cfg_events": { + "counter": 1, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "cds" + }, + "pilot_xds_push_time": { + "histogram": { + "counts": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "values": [ + 0.005, + 0.05500000000000001, + 0.55, + 2, + 4, + 7.5, + 15, + 25, + 40 + ] + } + }, + "pilot_xds_pushes": { + "counter": 9, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "event": "add", + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "Pods" + }, + "pilot_k8s_reg_events": { + "counter": 22, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "event": "updatesame", + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "Endpoints" + }, + "pilot_k8s_reg_events": { + "counter": 185, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "config" + }, + "pilot_inbound_updates": { + "counter": 90, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "citadel_server_csr_count": { + "counter": 8, + "rate": 0 + }, + "citadel_server_root_cert_expiry_timestamp": { + "value": 1916309303 + }, + "citadel_server_success_cert_issuance_count": { + "counter": 8, + "rate": 0 + }, + "galley_validation_config_updates": { + "counter": 2, + "rate": 0 + }, + "labels": { + "instance": "127.0.0.1:51143", + "job": "istio" + }, + "pilot_conflict_inbound_listener": { + "value": 0 + }, + "pilot_conflict_outbound_listener_http_over_current_tcp": { + "value": 0 + }, + "pilot_conflict_outbound_listener_tcp_over_current_http": { + "value": 0 + }, + "pilot_conflict_outbound_listener_tcp_over_current_tcp": { + "value": 0 + }, + "pilot_destrule_subsets": { + "value": 0 + }, + "pilot_duplicate_envoy_clusters": { + "value": 0 + }, + "pilot_eds_no_instances": { + "value": 0 + }, + "pilot_endpoint_not_ready": { + "value": 0 + }, + "pilot_no_ip": { + "value": 0 + }, + "pilot_proxy_convergence_time": { + "histogram": { + "counts": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "values": [ + 0.05, + 0.30000000000000004, + 0.75, + 2, + 4, + 7.5, + 15, + 25, + 40 + ] + } + }, + "pilot_proxy_queue_time": { + "histogram": { + "counts": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "values": [ + 0.05, + 0.55, + 2, + 4, + 7.5, + 15, + 25, + 40 + ] + } + }, + "pilot_services": { + "value": 11 + }, + "pilot_virt_services": { + "value": 1 + }, + "pilot_vservice_dup_domain": { + "value": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "labels": { + "instance": "127.0.0.1:51143", + "job": "istio", + "type": "rds" + }, + "pilot_xds_push_time": { + "histogram": { + "counts": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "values": [ + 0.005, + 0.05500000000000001, + 0.55, + 2, + 4, + 7.5, + 15, + 25, + 40 + ] + } + }, + "pilot_xds_pushes": { + "counter": 9, + "rate": 0 + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + }, + { + "event": { + "dataset": "istio.istiod", + "duration": 115000, + "module": "istio" + }, + "metricset": { + "name": "istiod", + "period": 10000 + }, + "prometheus": { + "galley_validation_failed": { + "counter": 1, + "rate": 0 + }, + "labels": { + "group": "networking.istio.io", + "instance": "127.0.0.1:51143", + "job": "istio", + "reason": "invalid_resource", + "resource": "gateways", + "version": "v1alpha3" + } + }, + "service": { + "address": "127.0.0.1:55555", + "type": "istio" + } + } +] \ No newline at end of file diff --git a/x-pack/metricbeat/module/istio/istiod/istiod_test.go b/x-pack/metricbeat/module/istio/istiod/istiod_test.go new file mode 100644 index 00000000000..23ce86ca7f0 --- /dev/null +++ b/x-pack/metricbeat/module/istio/istiod/istiod_test.go @@ -0,0 +1,32 @@ +// 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. + +// +build !integration + +package istiod + +import ( + "os" + "testing" + + "github.com/elastic/beats/v7/libbeat/logp" + "github.com/elastic/beats/v7/metricbeat/mb" + mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" + + // Register input module and metricset + _ "github.com/elastic/beats/v7/x-pack/metricbeat/module/prometheus" + _ "github.com/elastic/beats/v7/x-pack/metricbeat/module/prometheus/collector" +) + +func init() { + // To be moved to some kind of helper + os.Setenv("BEAT_STRICT_PERMS", "false") + mb.Registry.SetSecondarySource(mb.NewLightModulesSource("../../../module")) +} + +func TestEventMapping(t *testing.T) { + logp.TestingSetup() + + mbtest.TestDataFiles(t, "istio", "istiod") +} diff --git a/x-pack/metricbeat/module/istio/istiod/manifest.yml b/x-pack/metricbeat/module/istio/istiod/manifest.yml new file mode 100644 index 00000000000..0a8294d9192 --- /dev/null +++ b/x-pack/metricbeat/module/istio/istiod/manifest.yml @@ -0,0 +1,11 @@ +default: false +input: + module: prometheus + metricset: collector + defaults: + metrics_path: /metrics + metrics_filters: + include: ["citadel_*", "galley_*", "pilot_*"] + exclude: ["^up$"] + use_types: true + rate_counters: true diff --git a/x-pack/metricbeat/module/istio/module.yaml b/x-pack/metricbeat/module/istio/module.yaml deleted file mode 100644 index 8b137891791..00000000000 --- a/x-pack/metricbeat/module/istio/module.yaml +++ /dev/null @@ -1 +0,0 @@ - diff --git a/x-pack/metricbeat/module/istio/module.yml b/x-pack/metricbeat/module/istio/module.yml new file mode 100644 index 00000000000..de29503fca5 --- /dev/null +++ b/x-pack/metricbeat/module/istio/module.yml @@ -0,0 +1,3 @@ +name: istio +metricsets: +- istiod diff --git a/x-pack/metricbeat/modules.d/istio.yml.disabled b/x-pack/metricbeat/modules.d/istio.yml.disabled index 1140d047a09..b65bcdef949 100644 --- a/x-pack/metricbeat/modules.d/istio.yml.disabled +++ b/x-pack/metricbeat/modules.d/istio.yml.disabled @@ -35,3 +35,10 @@ period: 10s # use istio-pilot.istio-system:15014, when deploying Metricbeat in a kubernetes cluster as Pod or Daemonset hosts: ["localhost:15014"] + +# Istio istiod to monitor the Istio Daemon for versions after 1.5 of Istio. +- module: istio + metricsets: ['istiod'] + period: 10s + # use istiod.istio-system:15014, when deploying Metricbeat in a kubernetes cluster as Pod or Daemonset + hosts: ['localhost:15014'] From a3fe79648592169bf81d482f5d3895a5045d140a Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Tue, 13 Oct 2020 13:08:46 +0200 Subject: [PATCH 29/46] Fix leaks with metadata processors (#16349) Add a closer interface for processors so their resources can be released when the processor is not needed anymore. Explicitly close publisher pipelines so their processors are closed. Add closers for add_docker_metadata, add_kubernetes_metadata and add_process_metadata. Script processor will fail if a processor that needs to be closed is used. --- CHANGELOG.next.asciidoc | 1 + libbeat/beat/pipeline.go | 1 + libbeat/cmd/instance/beat.go | 5 + libbeat/common/docker/watcher.go | 7 +- .../add_docker_metadata.go | 12 ++ .../add_docker_metadata_integration_test.go | 120 ++++++++++++++++++ .../add_docker_metadata_test.go | 1 + .../add_kubernetes_metadata/cache.go | 32 +++-- .../add_kubernetes_metadata/kubernetes.go | 10 ++ .../add_process_metadata.go | 46 +++++-- .../add_process_metadata_test.go | 2 +- libbeat/processors/processor.go | 30 +++++ .../javascript/module/processor/chain.go | 11 ++ .../module/processor/processor_test.go | 54 ++++++-- libbeat/publisher/pipeline/client.go | 10 ++ libbeat/publisher/processing/default.go | 12 +- libbeat/publisher/processing/default_test.go | 72 +++++++++++ libbeat/publisher/processing/processing.go | 2 + libbeat/publisher/processing/processors.go | 16 +++ libbeat/tests/docker/docker.go | 20 +++ 20 files changed, 426 insertions(+), 38 deletions(-) create mode 100644 libbeat/processors/add_docker_metadata/add_docker_metadata_integration_test.go diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 4ca46560ab5..576c0062310 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -180,6 +180,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Explicitly detect missing variables in autodiscover configuration, log them at the debug level. {issue}20568[20568] {pull}20898[20898] - Fix `libbeat.output.write.bytes` and `libbeat.output.read.bytes` metrics of the Elasticsearch output. {issue}20752[20752] {pull}21197[21197] - The `o365input` and `o365` module now recover from an authentication problem or other fatal errors, instead of terminating. {pull}21259[21258] +- Orderly close processors when processing pipelines are not needed anymore to release their resources. {pull}16349[16349] *Auditbeat* diff --git a/libbeat/beat/pipeline.go b/libbeat/beat/pipeline.go index 699c96d8b62..f13b3c39ff2 100644 --- a/libbeat/beat/pipeline.go +++ b/libbeat/beat/pipeline.go @@ -138,6 +138,7 @@ type ClientEventer interface { type ProcessorList interface { Processor + Close() error All() []Processor } diff --git a/libbeat/cmd/instance/beat.go b/libbeat/cmd/instance/beat.go index ca58b9b321f..714b266ea84 100644 --- a/libbeat/cmd/instance/beat.go +++ b/libbeat/cmd/instance/beat.go @@ -371,6 +371,11 @@ func (b *Beat) launch(settings Settings, bt beat.Creator) error { if err != nil { return err } + defer func() { + if err := b.processing.Close(); err != nil { + logp.Warn("Failed to close global processing: %v", err) + } + }() // Windows: Mark service as stopped. // After this is run, a Beat service is considered by the OS to be stopped diff --git a/libbeat/common/docker/watcher.go b/libbeat/common/docker/watcher.go index 0543d37e9c3..2421c232eee 100644 --- a/libbeat/common/docker/watcher.go +++ b/libbeat/common/docker/watcher.go @@ -138,6 +138,7 @@ func NewWatcher(log *logp.Logger, host string, tls *TLSConfig, storeShortID bool // Extra check to confirm that Docker is available _, err = client.Info(context.Background()) if err != nil { + client.Close() return nil, err } @@ -395,14 +396,12 @@ func (w *watcher) cleanupWorker() { log := w.log for { - // Wait a full period - time.Sleep(w.cleanupTimeout) - select { case <-w.ctx.Done(): w.stopped.Done() return - default: + // Wait a full period + case <-time.After(w.cleanupTimeout): // Check entries for timeout var toDelete []string timeout := time.Now().Add(-w.cleanupTimeout) diff --git a/libbeat/processors/add_docker_metadata/add_docker_metadata.go b/libbeat/processors/add_docker_metadata/add_docker_metadata.go index cf0fda79d8d..beaca3bb46b 100644 --- a/libbeat/processors/add_docker_metadata/add_docker_metadata.go +++ b/libbeat/processors/add_docker_metadata/add_docker_metadata.go @@ -209,6 +209,18 @@ func (d *addDockerMetadata) Run(event *beat.Event) (*beat.Event, error) { return event, nil } +func (d *addDockerMetadata) Close() error { + if d.cgroups != nil { + d.cgroups.StopJanitor() + } + d.watcher.Stop() + err := processors.Close(d.sourceProcessor) + if err != nil { + return errors.Wrap(err, "closing source processor of add_docker_metadata") + } + return nil +} + func (d *addDockerMetadata) String() string { return fmt.Sprintf("%v=[match_fields=[%v] match_pids=[%v]]", processorName, strings.Join(d.fields, ", "), strings.Join(d.pidFields, ", ")) diff --git a/libbeat/processors/add_docker_metadata/add_docker_metadata_integration_test.go b/libbeat/processors/add_docker_metadata/add_docker_metadata_integration_test.go new file mode 100644 index 00000000000..91d3315a401 --- /dev/null +++ b/libbeat/processors/add_docker_metadata/add_docker_metadata_integration_test.go @@ -0,0 +1,120 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you 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. + +// +build linux darwin windows +// +build integration + +package add_docker_metadata + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/elastic/beats/v7/libbeat/beat" + "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/libbeat/common/docker" + "github.com/elastic/beats/v7/libbeat/logp" + "github.com/elastic/beats/v7/libbeat/processors" + dockertest "github.com/elastic/beats/v7/libbeat/tests/docker" + "github.com/elastic/beats/v7/libbeat/tests/resources" +) + +func TestAddDockerMetadata(t *testing.T) { + goroutines := resources.NewGoroutinesChecker() + defer goroutines.Check(t) + + client, err := docker.NewClient(defaultConfig().Host, nil, nil) + require.NoError(t, err) + + // Docker clients can affect the goroutines checker because they keep + // idle keep-alive connections, so we explicitly close them. + // These idle connections in principle wouldn't represent leaks even if + // the client is not explicitly closed because they are eventually closed. + defer client.Close() + + // Start a container to have some data to enrich events + testClient, err := dockertest.NewClient() + require.NoError(t, err) + // Explicitly close client to don't affect goroutines checker + defer testClient.Close() + + image := "busybox" + cmd := []string{"sleep", "60"} + labels := map[string]string{"label": "foo"} + id, err := testClient.ContainerStart(image, cmd, labels) + require.NoError(t, err) + defer testClient.ContainerRemove(id) + + info, err := testClient.ContainerInspect(id) + require.NoError(t, err) + pid := info.State.Pid + + config, err := common.NewConfigFrom(map[string]interface{}{ + "match_fields": []string{"cid"}, + }) + watcherConstructor := newWatcherWith(client) + processor, err := buildDockerMetadataProcessor(logp.L(), config, watcherConstructor) + require.NoError(t, err) + + t.Run("match container by container id", func(t *testing.T) { + input := &beat.Event{Fields: common.MapStr{ + "cid": id, + }} + result, err := processor.Run(input) + require.NoError(t, err) + + resultLabels, _ := result.Fields.GetValue("container.labels") + expectedLabels := common.MapStr{"label": "foo"} + assert.Equal(t, expectedLabels, resultLabels) + assert.Equal(t, id, result.Fields["cid"]) + }) + + t.Run("match container by process id", func(t *testing.T) { + input := &beat.Event{Fields: common.MapStr{ + "cid": id, + "process.pid": pid, + }} + result, err := processor.Run(input) + require.NoError(t, err) + + resultLabels, _ := result.Fields.GetValue("container.labels") + expectedLabels := common.MapStr{"label": "foo"} + assert.Equal(t, expectedLabels, resultLabels) + assert.Equal(t, id, result.Fields["cid"]) + }) + + t.Run("don't enrich non existing container", func(t *testing.T) { + input := &beat.Event{Fields: common.MapStr{ + "cid": "notexists", + }} + result, err := processor.Run(input) + require.NoError(t, err) + assert.Equal(t, input.Fields, result.Fields) + }) + + err = processors.Close(processor) + require.NoError(t, err) +} + +func newWatcherWith(client docker.Client) docker.WatcherConstructor { + return func(log *logp.Logger, host string, tls *docker.TLSConfig, storeShortID bool) (docker.Watcher, error) { + return docker.NewWatcherWithClient(log, client, 60*time.Second, storeShortID) + } +} diff --git a/libbeat/processors/add_docker_metadata/add_docker_metadata_test.go b/libbeat/processors/add_docker_metadata/add_docker_metadata_test.go index 2d8a5a9e970..f246d597e13 100644 --- a/libbeat/processors/add_docker_metadata/add_docker_metadata_test.go +++ b/libbeat/processors/add_docker_metadata/add_docker_metadata_test.go @@ -16,6 +16,7 @@ // under the License. // +build linux darwin windows +// +build !integration package add_docker_metadata diff --git a/libbeat/processors/add_kubernetes_metadata/cache.go b/libbeat/processors/add_kubernetes_metadata/cache.go index da7492b43a9..5de52efdc1b 100644 --- a/libbeat/processors/add_kubernetes_metadata/cache.go +++ b/libbeat/processors/add_kubernetes_metadata/cache.go @@ -31,6 +31,7 @@ type cache struct { timeout time.Duration deleted map[string]time.Time // key -> when should this obj be deleted metadata map[string]common.MapStr + done chan struct{} } func newCache(cleanupTimeout time.Duration) *cache { @@ -38,6 +39,7 @@ func newCache(cleanupTimeout time.Duration) *cache { timeout: cleanupTimeout, deleted: make(map[string]time.Time), metadata: make(map[string]common.MapStr), + done: make(chan struct{}), } go c.cleanup() return c @@ -67,15 +69,29 @@ func (c *cache) set(key string, data common.MapStr) { } func (c *cache) cleanup() { - ticker := time.Tick(timeout) - for now := range ticker { - c.Lock() - for k, t := range c.deleted { - if now.After(t) { - delete(c.deleted, k) - delete(c.metadata, k) + if timeout <= 0 { + return + } + + ticker := time.NewTicker(timeout) + defer ticker.Stop() + for { + select { + case <-c.done: + return + case now := <-ticker.C: + c.Lock() + for k, t := range c.deleted { + if now.After(t) { + delete(c.deleted, k) + delete(c.metadata, k) + } } + c.Unlock() } - c.Unlock() } } + +func (c *cache) stop() { + close(c.done) +} diff --git a/libbeat/processors/add_kubernetes_metadata/kubernetes.go b/libbeat/processors/add_kubernetes_metadata/kubernetes.go index 2a5f4d2faed..535eb1187ea 100644 --- a/libbeat/processors/add_kubernetes_metadata/kubernetes.go +++ b/libbeat/processors/add_kubernetes_metadata/kubernetes.go @@ -242,6 +242,16 @@ func (k *kubernetesAnnotator) Run(event *beat.Event) (*beat.Event, error) { return event, nil } +func (k *kubernetesAnnotator) Close() error { + if k.watcher != nil { + k.watcher.Stop() + } + if k.cache != nil { + k.cache.stop() + } + return nil +} + func (k *kubernetesAnnotator) addPod(pod *kubernetes.Pod) { metadata := k.indexers.GetMetadata(pod) for _, m := range metadata { diff --git a/libbeat/processors/add_process_metadata/add_process_metadata.go b/libbeat/processors/add_process_metadata/add_process_metadata.go index c41ca9a73d6..01e2cf1e9fe 100644 --- a/libbeat/processors/add_process_metadata/add_process_metadata.go +++ b/libbeat/processors/add_process_metadata/add_process_metadata.go @@ -55,11 +55,12 @@ var ( ) type addProcessMetadata struct { - config config - provider processMetadataProvider - cidProvider cidProvider - log *logp.Logger - mappings common.MapStr + config config + provider processMetadataProvider + cgroupsCache *common.Cache + cidProvider cidProvider + log *logp.Logger + mappings common.MapStr } type processMetadata struct { @@ -81,16 +82,22 @@ type cidProvider interface { } func init() { - processors.RegisterPlugin(processorName, New) + processors.RegisterPlugin(processorName, NewWithCache) jsprocessor.RegisterPlugin("AddProcessMetadata", New) } // New constructs a new add_process_metadata processor. func New(cfg *common.Config) (processors.Processor, error) { - return newProcessMetadataProcessorWithProvider(cfg, &procCache) + return newProcessMetadataProcessorWithProvider(cfg, &procCache, false) } -func newProcessMetadataProcessorWithProvider(cfg *common.Config, provider processMetadataProvider) (proc processors.Processor, err error) { +// NewWithCache construct a new add_process_metadata processor with cache for container IDs. +// Resulting processor implements `Close()` to release the cache resources. +func NewWithCache(cfg *common.Config) (processors.Processor, error) { + return newProcessMetadataProcessorWithProvider(cfg, &procCache, true) +} + +func newProcessMetadataProcessorWithProvider(cfg *common.Config, provider processMetadataProvider, withCache bool) (proc processors.Processor, err error) { // Logging (each processor instance has a unique ID). var ( id = int(instanceID.Inc()) @@ -118,21 +125,25 @@ func newProcessMetadataProcessorWithProvider(cfg *common.Config, provider proces } // don't use cgroup.ProcessCgroupPaths to save it from doing the work when container id disabled if ok := containsValue(mappings, "container.id"); ok { - if config.CgroupCacheExpireTime != 0 { + if withCache && config.CgroupCacheExpireTime != 0 { p.log.Debug("Initializing cgroup cache") evictionListener := func(k common.Key, v common.Value) { p.log.Debugf("Evicted cached cgroups for PID=%v", k) } - cgroupsCache := common.NewCacheWithRemovalListener(config.CgroupCacheExpireTime, 100, evictionListener) - cgroupsCache.StartJanitor(config.CgroupCacheExpireTime) - p.cidProvider = newCidProvider(config.HostPath, config.CgroupPrefixes, config.CgroupRegex, processCgroupPaths, cgroupsCache) + p.cgroupsCache = common.NewCacheWithRemovalListener(config.CgroupCacheExpireTime, 100, evictionListener) + p.cgroupsCache.StartJanitor(config.CgroupCacheExpireTime) + p.cidProvider = newCidProvider(config.HostPath, config.CgroupPrefixes, config.CgroupRegex, processCgroupPaths, p.cgroupsCache) } else { p.cidProvider = newCidProvider(config.HostPath, config.CgroupPrefixes, config.CgroupRegex, processCgroupPaths, nil) } } + if withCache { + return &addProcessMetadataCloser{p}, nil + } + return &p, nil } @@ -253,6 +264,17 @@ func (p *addProcessMetadata) getContainerID(pid int) (string, error) { return cid, nil } +type addProcessMetadataCloser struct { + addProcessMetadata +} + +func (p *addProcessMetadataCloser) Close() error { + if p.addProcessMetadata.cgroupsCache != nil { + p.addProcessMetadata.cgroupsCache.StopJanitor() + } + return nil +} + // String returns the processor representation formatted as a string func (p *addProcessMetadata) String() string { return fmt.Sprintf("%v=[match_pids=%v, mappings=%v, ignore_missing=%v, overwrite_fields=%v, restricted_fields=%v, host_path=%v, cgroup_prefixes=%v]", diff --git a/libbeat/processors/add_process_metadata/add_process_metadata_test.go b/libbeat/processors/add_process_metadata/add_process_metadata_test.go index f9b4aaa681c..493034098cf 100644 --- a/libbeat/processors/add_process_metadata/add_process_metadata_test.go +++ b/libbeat/processors/add_process_metadata/add_process_metadata_test.go @@ -651,7 +651,7 @@ func TestAddProcessMetadata(t *testing.T) { t.Fatal(err) } - proc, err := newProcessMetadataProcessorWithProvider(config, testProcs) + proc, err := newProcessMetadataProcessorWithProvider(config, testProcs, true) if test.initErr == nil { if err != nil { t.Fatal(err) diff --git a/libbeat/processors/processor.go b/libbeat/processors/processor.go index c5002b7cebd..0e772a524da 100644 --- a/libbeat/processors/processor.go +++ b/libbeat/processors/processor.go @@ -20,6 +20,7 @@ package processors import ( "strings" + "github.com/joeshaw/multierror" "github.com/pkg/errors" "github.com/elastic/beats/v7/libbeat/beat" @@ -35,11 +36,29 @@ type Processors struct { log *logp.Logger } +// Processor is the interface that all processors must implement type Processor interface { Run(event *beat.Event) (*beat.Event, error) String() string } +// Closer defines the interface for processors that should be closed after using +// them. +// Close() is not part of the Processor interface because implementing this method +// is also a way to indicate that the processor keeps some resource that needs to +// be released or orderly closed. +type Closer interface { + Close() error +} + +// Close closes a processor if it implements the Closer interface +func Close(p Processor) error { + if closer, ok := p.(Closer); ok { + return closer.Close() + } + return nil +} + // NewList creates a new empty processor list. // Additional processors can be added to the List field. func NewList(log *logp.Logger) *Processors { @@ -153,6 +172,17 @@ func (procs *Processors) All() []beat.Processor { return ret } +func (procs *Processors) Close() error { + var errs multierror.Errors + for _, p := range procs.List { + err := Close(p) + if err != nil { + errs = append(errs, err) + } + } + return errs.Err() +} + // Run executes the all processors serially and returns the event and possibly // an error. If the event has been dropped (canceled) by a processor in the // list then a nil event is returned. diff --git a/libbeat/processors/script/javascript/module/processor/chain.go b/libbeat/processors/script/javascript/module/processor/chain.go index e58aac29372..9ef3da7859e 100644 --- a/libbeat/processors/script/javascript/module/processor/chain.go +++ b/libbeat/processors/script/javascript/module/processor/chain.go @@ -151,6 +151,17 @@ func newNativeProcessor(constructor processors.Constructor, call gojaCall) (proc if err != nil { return nil, err } + + if closer, ok := p.(processors.Closer); ok { + closer.Close() + // Script processor doesn't support releasing resources of stateful processors, + // what can lead to leaks, so prevent use of these processors. They shouldn't + // be registered. If this error happens, a processor that needs to be closed is + // being registered, this should be avoided. + // See https://github.com/elastic/beats/pull/16349 + return nil, errors.Errorf("stateful processor cannot be used in script processor, this is probably a bug: %s", p) + } + return &nativeProcessor{p}, nil } diff --git a/libbeat/processors/script/javascript/module/processor/processor_test.go b/libbeat/processors/script/javascript/module/processor/processor_test.go index 6ea66f409ff..9e958ee7035 100644 --- a/libbeat/processors/script/javascript/module/processor/processor_test.go +++ b/libbeat/processors/script/javascript/module/processor/processor_test.go @@ -23,6 +23,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/elastic/beats/v7/libbeat/beat" "github.com/elastic/beats/v7/libbeat/common" @@ -35,6 +36,7 @@ import ( func init() { RegisterPlugin("Mock", newMock) + RegisterPlugin("MockWithCloser", newMockWithCloser) } func testEvent() *beat.Event { @@ -67,14 +69,10 @@ function process(evt) { logp.TestingSetup() p, err := javascript.NewFromConfig(javascript.Config{Source: script}, nil) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) evt, err := p.Run(testEvent()) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) checkEvent(t, evt, "added", "new_value") } @@ -107,14 +105,10 @@ function process(evt) { logp.TestingSetup() p, err := javascript.NewFromConfig(javascript.Config{Source: script}, nil) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) evt, err := p.Run(testEvent()) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) // checking if hello world is added to the event in different languages checkEvent(t, evt, "helló", "világ") @@ -123,6 +117,22 @@ function process(evt) { checkEvent(t, evt, "hallo", "Welt") } +func TestProcessorWithCloser(t *testing.T) { + const script = ` +var processor = require('processor'); + +var processorWithCloser = new processor.MockWithCloser().Build() + +function process(evt) { + processorWithCloser.Run(evt); +} +` + + logp.TestingSetup() + _, err := javascript.NewFromConfig(javascript.Config{Source: script}, nil) + require.Error(t, err, "processor that implements Closer() shouldn't be allowed") +} + func checkEvent(t *testing.T, evt *beat.Event, key, value string) { s, err := evt.GetValue(key) assert.NoError(t, err) @@ -162,3 +172,23 @@ func (m *mockProcessor) String() string { s, _ := json.Marshal(m.fields) return fmt.Sprintf("mock=%s", s) } + +type mockProcessorWithCloser struct{} + +func newMockWithCloser(c *common.Config) (processors.Processor, error) { + return &mockProcessorWithCloser{}, nil +} + +func (m *mockProcessorWithCloser) Run(event *beat.Event) (*beat.Event, error) { + // Nothing to do, we only want this struct to implement processors.Closer + return event, nil +} + +func (m *mockProcessorWithCloser) Close() error { + // Nothing to do, we only want this struct to implement processors.Closer + return nil +} + +func (m *mockProcessorWithCloser) String() string { + return "mockWithCloser" +} diff --git a/libbeat/publisher/pipeline/client.go b/libbeat/publisher/pipeline/client.go index 2ce792ed887..edb5a3f1eb3 100644 --- a/libbeat/publisher/pipeline/client.go +++ b/libbeat/publisher/pipeline/client.go @@ -24,6 +24,7 @@ import ( "github.com/elastic/beats/v7/libbeat/beat" "github.com/elastic/beats/v7/libbeat/common/atomic" "github.com/elastic/beats/v7/libbeat/logp" + "github.com/elastic/beats/v7/libbeat/processors" "github.com/elastic/beats/v7/libbeat/publisher" "github.com/elastic/beats/v7/libbeat/publisher/queue" ) @@ -164,6 +165,15 @@ func (c *client) Close() error { log.Debug("client: unlink from queue") c.unlink() log.Debug("client: done unlink") + + if c.processors != nil { + log.Debug("client: closing processors") + err := processors.Close(c.processors) + if err != nil { + log.Errorf("client: error closing processors: %v", err) + } + log.Debug("client: done closing processors") + } }) return nil } diff --git a/libbeat/publisher/processing/default.go b/libbeat/publisher/processing/default.go index b2a65642e17..f9eab88fe48 100644 --- a/libbeat/publisher/processing/default.go +++ b/libbeat/publisher/processing/default.go @@ -338,7 +338,10 @@ func (b *builder) Create(cfg beat.ProcessingConfig, drop bool) (beat.Processor, } // setup 8: pipeline processors list - processors.add(b.processors) + if b.processors != nil { + // Add the global pipeline as a function processor, so clients cannot close it + processors.add(newProcessor(b.processors.title, b.processors.Run)) + } // setup 9: time series metadata if b.timeSeries { @@ -358,6 +361,13 @@ func (b *builder) Create(cfg beat.ProcessingConfig, drop bool) (beat.Processor, return processors, nil } +func (b *builder) Close() error { + if b.processors != nil { + return b.processors.Close() + } + return nil +} + func makeClientProcessors( log *logp.Logger, cfg beat.ProcessingConfig, diff --git a/libbeat/publisher/processing/default_test.go b/libbeat/publisher/processing/default_test.go index 56dfa75bdd2..637b38cf44d 100644 --- a/libbeat/publisher/processing/default_test.go +++ b/libbeat/publisher/processing/default_test.go @@ -29,6 +29,7 @@ import ( "github.com/elastic/beats/v7/libbeat/beat" "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/logp" + "github.com/elastic/beats/v7/libbeat/processors" "github.com/elastic/beats/v7/libbeat/processors/actions" "github.com/elastic/ecs/code/go/ecs" ) @@ -317,6 +318,9 @@ func TestNormalization(t *testing.T) { fields.DeepUpdate(test.mod) assert.Equal(t, test.want, actual.Fields) + + err = s.Close() + require.NoError(t, err) }) } } @@ -331,6 +335,9 @@ func TestAlwaysDrop(t *testing.T) { actual, err := prog.Run(&beat.Event{}) require.NoError(t, err) assert.Nil(t, actual) + + err = s.Close() + require.NoError(t, err) } func TestDynamicFields(t *testing.T) { @@ -351,6 +358,52 @@ func TestDynamicFields(t *testing.T) { actual, err = prog.Run(&beat.Event{Fields: common.MapStr{"hello": "world"}}) require.NoError(t, err) assert.Equal(t, common.MapStr{"hello": "world", "dyn": "field"}, actual.Fields) + + err = factory.Close() + require.NoError(t, err) +} + +func TestProcessingClose(t *testing.T) { + factory, err := MakeDefaultSupport(true)(beat.Info{}, logp.L(), common.NewConfig()) + require.NoError(t, err) + + // Inject a processor in the builder that we can check if has been closed. + factoryProcessor := &processorWithClose{} + b := factory.(*builder) + if b.processors == nil { + b.processors = newGroup("global", logp.L()) + } + b.processors.add(factoryProcessor) + + clientProcessor := &processorWithClose{} + g := newGroup("test", logp.L()) + g.add(clientProcessor) + + prog, err := factory.Create(beat.ProcessingConfig{ + Processor: g, + }, false) + require.NoError(t, err) + + // Check that both processors are called + assert.False(t, factoryProcessor.called) + assert.False(t, clientProcessor.called) + _, err = prog.Run(&beat.Event{Fields: common.MapStr{"hello": "world"}}) + require.NoError(t, err) + assert.True(t, factoryProcessor.called) + assert.True(t, clientProcessor.called) + + // Check that closing the client processing pipeline doesn't close the global pipeline + assert.False(t, factoryProcessor.closed) + assert.False(t, clientProcessor.closed) + err = processors.Close(prog) + require.NoError(t, err) + assert.False(t, factoryProcessor.closed) + assert.True(t, clientProcessor.closed) + + // Check that closing the factory closes the processor in the global pipeline + err = factory.Close() + require.NoError(t, err) + assert.True(t, factoryProcessor.closed) } func fromJSON(in string) common.MapStr { @@ -361,3 +414,22 @@ func fromJSON(in string) common.MapStr { } return tmp } + +type processorWithClose struct { + closed bool + called bool +} + +func (p *processorWithClose) Run(e *beat.Event) (*beat.Event, error) { + p.called = true + return e, nil +} + +func (p *processorWithClose) Close() error { + p.closed = true + return nil +} + +func (p *processorWithClose) String() string { + return "processorWithClose" +} diff --git a/libbeat/publisher/processing/processing.go b/libbeat/publisher/processing/processing.go index 4f615fb1422..88feb62c7b2 100644 --- a/libbeat/publisher/processing/processing.go +++ b/libbeat/publisher/processing/processing.go @@ -33,6 +33,8 @@ type SupportFactory func(info beat.Info, log *logp.Logger, cfg *common.Config) ( // will merge the global and local configurations into a common event // processor. // If `drop` is set, then the processor generated must always drop all events. +// A Supporter needs to be closed with `Close()` to release its global resources. type Supporter interface { Create(cfg beat.ProcessingConfig, drop bool) (beat.Processor, error) + Close() error } diff --git a/libbeat/publisher/processing/processors.go b/libbeat/publisher/processing/processors.go index e994eef48cc..3a400d36dad 100644 --- a/libbeat/publisher/processing/processors.go +++ b/libbeat/publisher/processing/processors.go @@ -22,6 +22,8 @@ import ( "strings" "sync" + "github.com/joeshaw/multierror" + "github.com/elastic/beats/v7/libbeat/beat" "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/logp" @@ -77,6 +79,20 @@ func (p *group) add(processor processors.Processor) { } } +func (p *group) Close() error { + if p == nil { + return nil + } + var errs multierror.Errors + for _, processor := range p.list { + err := processors.Close(processor) + if err != nil { + errs = append(errs, err) + } + } + return errs.Err() +} + func (p *group) String() string { var s []string for _, p := range p.list { diff --git a/libbeat/tests/docker/docker.go b/libbeat/tests/docker/docker.go index b81ffa285ea..888347c5cc7 100644 --- a/libbeat/tests/docker/docker.go +++ b/libbeat/tests/docker/docker.go @@ -77,8 +77,28 @@ func (c Client) ContainerWait(ID string) error { return nil } +// ContainerInspect recovers information of the container +func (c Client) ContainerInspect(ID string) (types.ContainerJSON, error) { + ctx := context.Background() + return c.cli.ContainerInspect(ctx, ID) +} + // ContainerKill kills the given container func (c Client) ContainerKill(ID string) error { ctx := context.Background() return c.cli.ContainerKill(ctx, ID, "KILL") } + +// ContainerRemove kills and removed the given container +func (c Client) ContainerRemove(ID string) error { + ctx := context.Background() + return c.cli.ContainerRemove(ctx, ID, types.ContainerRemoveOptions{ + RemoveVolumes: true, + Force: true, + }) +} + +// Close closes the underlying client +func (c *Client) Close() error { + return c.cli.Close() +} From 500e8b5f81af3a9d0cd369182ac879360b39c64b Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Tue, 13 Oct 2020 07:14:59 -0400 Subject: [PATCH 30/46] Remove dot from file.extension value in Auditbeat FIM (#21644) The ECS file.extension field should not include the dot. For example the value should be "png" and not ".png". Relates elastic/ecs#1016 --- CHANGELOG.next.asciidoc | 1 + auditbeat/module/file_integrity/event.go | 2 +- auditbeat/module/file_integrity/event_test.go | 11 +++++++++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 576c0062310..660fa25a10d 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -32,6 +32,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Change event.kind=error to event.kind=event to comply with ECS. {issue}18870[18870] {pull}20685[20685] - Change network.direction values to ECS recommended values (inbound, outbound). {issue}12445[12445] {pull}20695[20695] - Docker container needs to be explicitly run as user root for auditing. {pull}21202[21202] +- File integrity dataset no longer includes the leading dot in `file.extension` values (e.g. it will report "png" instead of ".png") to comply with ECS. {pull}21644[21644] *Filebeat* diff --git a/auditbeat/module/file_integrity/event.go b/auditbeat/module/file_integrity/event.go index 41e4d5a3795..1ee28b7ce35 100644 --- a/auditbeat/module/file_integrity/event.go +++ b/auditbeat/module/file_integrity/event.go @@ -257,7 +257,7 @@ func buildMetricbeatEvent(e *Event, existedBefore bool) mb.Event { if e.Info.Type == FileType { if extension := filepath.Ext(e.Path); extension != "" { - file["extension"] = extension + file["extension"] = strings.TrimLeft(extension, ".") } if mimeType := getMimeType(e.Path); mimeType != "" { file["mime_type"] = mimeType diff --git a/auditbeat/module/file_integrity/event_test.go b/auditbeat/module/file_integrity/event_test.go index 79d1309903d..efaafd02041 100644 --- a/auditbeat/module/file_integrity/event_test.go +++ b/auditbeat/module/file_integrity/event_test.go @@ -28,6 +28,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/elastic/beats/v7/libbeat/common" ) @@ -295,7 +296,11 @@ func TestBuildEvent(t *testing.T) { assertHasKey(t, fields, "event.type") assertHasKey(t, fields, "file.path") - assertHasKey(t, fields, "file.extension") + if assertHasKey(t, fields, "file.extension") { + ext, err := fields.GetValue("file.extension") + require.NoError(t, err) + assert.Equal(t, ext, "txt") + } assertHasKey(t, fields, "file.target_path") assertHasKey(t, fields, "file.inode") assertHasKey(t, fields, "file.uid") @@ -427,10 +432,12 @@ func mustDecodeHex(v string) []byte { return data } -func assertHasKey(t testing.TB, m common.MapStr, key string) { +func assertHasKey(t testing.TB, m common.MapStr, key string) bool { t.Helper() found, err := m.HasKey(key) if err != nil || !found { t.Errorf("key %v not found: %v", key, err) + return false } + return true } From 09234d4e94d4813892160f7d313bc6dec66ca037 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Tue, 13 Oct 2020 13:17:06 +0200 Subject: [PATCH 31/46] fix: update fleet test suite name (#21738) --- .ci/packaging.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.ci/packaging.groovy b/.ci/packaging.groovy index c65fd7f8b56..9087cf48de4 100644 --- a/.ci/packaging.groovy +++ b/.ci/packaging.groovy @@ -230,11 +230,11 @@ def runE2ETestForPackages(){ catchError(buildResult: 'UNSTABLE', message: 'Unable to run e2e tests', stageResult: 'FAILURE') { if ("${env.BEATS_FOLDER}" == "filebeat" || "${env.BEATS_FOLDER}" == "x-pack/filebeat") { - suite = 'helm,ingest-manager' + suite = 'helm,fleet' } else if ("${env.BEATS_FOLDER}" == "metricbeat" || "${env.BEATS_FOLDER}" == "x-pack/metricbeat") { suite = '' } else if ("${env.BEATS_FOLDER}" == "x-pack/elastic-agent") { - suite = 'ingest-manager' + suite = 'fleet' } else { echo("Skipping E2E tests for ${env.BEATS_FOLDER}.") return From 1824e849978f248f4e2da75ae51732963379714a Mon Sep 17 00:00:00 2001 From: EamonnTP Date: Tue, 13 Oct 2020 13:49:48 +0100 Subject: [PATCH 32/46] Update obs app links (#21682) --- libbeat/docs/shared/obs-apps.asciidoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libbeat/docs/shared/obs-apps.asciidoc b/libbeat/docs/shared/obs-apps.asciidoc index 9b5f7354ea0..26698700c21 100644 --- a/libbeat/docs/shared/obs-apps.asciidoc +++ b/libbeat/docs/shared/obs-apps.asciidoc @@ -38,13 +38,13 @@ endif::[] |=== |Elastic apps | Use to -|{kibana-ref}/xpack-infra.html[{metrics-app}] +|{observability-guide}/analyze-metrics.html[{metrics-app}] |Explore metrics about systems and services across your ecosystem -|{kibana-ref}/xpack-logs.html[{logs-app}] +|{observability-guide}/monitor-logs.html[{logs-app}] |Tail related log data in real time -|{kibana-ref}/xpack-uptime.html[{uptime-app}] +|{observability-guide}/monitor-uptime.html[{uptime-app}] |Monitor availability issues across your apps and services |{kibana-ref}/xpack-apm.html[APM app] From 056f0e07b233d3a438b1d21b5182cb2ea49638d2 Mon Sep 17 00:00:00 2001 From: Mariana Dima Date: Tue, 13 Oct 2020 16:01:19 +0200 Subject: [PATCH 33/46] Fix for azure retrieve resource by ids (#21711) * mofidy doc * start work * work * changelog * fix test --- CHANGELOG.next.asciidoc | 1 + x-pack/metricbeat/module/azure/client.go | 6 ++--- x-pack/metricbeat/module/azure/client_test.go | 2 +- .../metricbeat/module/azure/mock_service.go | 4 +-- .../module/azure/monitor_service.go | 26 ++++++++++++++----- .../module/azure/service_interface.go | 2 +- 6 files changed, 27 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 660fa25a10d..41de16190a8 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -366,6 +366,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Fix remote_write flaky test. {pull}21173[21173] - Visualization title fixes in aws, azure and googlecloud compute dashboards. {pull}21098[21098] - Add a switch to the driver definition on SQL module to use pretty names {pull}17378[17378] +- Fix retrieving resources by ID for the azure module. {pull}21711[21711] {issue}21707[21707] - Use timestamp from CloudWatch API when creating events. {pull}21498[21498] *Packetbeat* diff --git a/x-pack/metricbeat/module/azure/client.go b/x-pack/metricbeat/module/azure/client.go index e488fab98b6..dd48f962b59 100644 --- a/x-pack/metricbeat/module/azure/client.go +++ b/x-pack/metricbeat/module/azure/client.go @@ -65,14 +65,14 @@ func (client *Client) InitResources(fn mapResourceMetrics) error { err = errors.Wrap(err, "failed to retrieve resources") return err } - if len(resourceList.Values()) == 0 { + if len(resourceList) == 0 { err = errors.Errorf("failed to retrieve resources: No resources returned using the configuration options resource ID %s, resource group %s, resource type %s, resource query %s", resource.Id, resource.Group, resource.Type, resource.Query) client.Log.Error(err) continue } //map resources to the client - for _, resource := range resourceList.Values() { + for _, resource := range resourceList { if !containsResource(*resource.ID, client.Resources) { client.Resources = append(client.Resources, Resource{ Id: *resource.ID, @@ -84,7 +84,7 @@ func (client *Client) InitResources(fn mapResourceMetrics) error { Subscription: client.Config.SubscriptionId}) } } - resourceMetrics, err := fn(client, resourceList.Values(), resource) + resourceMetrics, err := fn(client, resourceList, resource) if err != nil { return err } diff --git a/x-pack/metricbeat/module/azure/client_test.go b/x-pack/metricbeat/module/azure/client_test.go index 47b88f99cce..6b0df97a370 100644 --- a/x-pack/metricbeat/module/azure/client_test.go +++ b/x-pack/metricbeat/module/azure/client_test.go @@ -50,7 +50,7 @@ func TestInitResources(t *testing.T) { client := NewMockClient() client.Config = resourceQueryConfig m := &MockService{} - m.On("GetResourceDefinitions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(resources.ListResultPage{}, errors.New("invalid resource query")) + m.On("GetResourceDefinitions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]resources.GenericResource{}, errors.New("invalid resource query")) client.AzureMonitorService = m mr := MockReporterV2{} mr.On("Error", mock.Anything).Return(true) diff --git a/x-pack/metricbeat/module/azure/mock_service.go b/x-pack/metricbeat/module/azure/mock_service.go index f6f54c300e0..601f1de5b45 100644 --- a/x-pack/metricbeat/module/azure/mock_service.go +++ b/x-pack/metricbeat/module/azure/mock_service.go @@ -24,9 +24,9 @@ func (client *MockService) GetResourceDefinitionById(id string) (resources.Gener } // GetResourceDefinitions is a mock function for the azure service -func (client *MockService) GetResourceDefinitions(id []string, group []string, rType string, query string) (resources.ListResultPage, error) { +func (client *MockService) GetResourceDefinitions(id []string, group []string, rType string, query string) ([]resources.GenericResource, error) { args := client.Called(id, group, rType, query) - return args.Get(0).(resources.ListResultPage), args.Error(1) + return args.Get(0).([]resources.GenericResource), args.Error(1) } // GetMetricDefinitions is a mock function for the azure service diff --git a/x-pack/metricbeat/module/azure/monitor_service.go b/x-pack/metricbeat/module/azure/monitor_service.go index 053da3db05b..c3ed4e2fa43 100644 --- a/x-pack/metricbeat/module/azure/monitor_service.go +++ b/x-pack/metricbeat/module/azure/monitor_service.go @@ -55,16 +55,24 @@ func NewService(clientId string, clientSecret string, tenantId string, subscript } // GetResourceDefinitions will retrieve the azure resources based on the options entered -func (service MonitorService) GetResourceDefinitions(id []string, group []string, rType string, query string) (resources.ListResultPage, error) { +func (service MonitorService) GetResourceDefinitions(id []string, group []string, rType string, query string) ([]resources.GenericResource, error) { var resourceQuery string + var resourceList []resources.GenericResource if len(id) > 0 { - var filterList []string - // listing resourceID conditions does not seem to work with the API but querying by name or resource types will work + // listing multiple resourceId conditions does not seem to work with the API, extracting the name and resource type does not work as the position of the `resourceType` can move if a parent resource is involved, filtering by resource name and resource group (if extracted) is also not possible as + // different types of resources can contain the same name. for _, id := range id { - filterList = append(filterList, fmt.Sprintf("name eq '%s'", getResourceNameFromId(id))) + resource, err := service.resourceClient.List(service.context, fmt.Sprintf("resourceId eq '%s'", id), "", nil) + if err != nil { + return nil, err + } + if len(resource.Values()) > 0 { + resourceList = append(resourceList, resource.Values()...) + } } - resourceQuery = fmt.Sprintf("(%s) AND resourceType eq '%s'", strings.Join(filterList, " OR "), getResourceTypeFromId(id[0])) - } else if len(group) > 0 { + return resourceList, nil + } + if len(group) > 0 { var filterList []string for _, gr := range group { filterList = append(filterList, fmt.Sprintf("resourceGroup eq '%s'", gr)) @@ -76,7 +84,11 @@ func (service MonitorService) GetResourceDefinitions(id []string, group []string } else if query != "" { resourceQuery = query } - return service.resourceClient.List(service.context, resourceQuery, "", nil) + result, err := service.resourceClient.List(service.context, resourceQuery, "", nil) + if err == nil { + resourceList = result.Values() + } + return resourceList, err } // GetResourceDefinitionById will retrieve the azure resource based on the resource Id diff --git a/x-pack/metricbeat/module/azure/service_interface.go b/x-pack/metricbeat/module/azure/service_interface.go index e8985a7eedd..30430d03100 100644 --- a/x-pack/metricbeat/module/azure/service_interface.go +++ b/x-pack/metricbeat/module/azure/service_interface.go @@ -12,7 +12,7 @@ import ( // Service interface for the azure monitor service and mock for testing type Service interface { GetResourceDefinitionById(id string) (resources.GenericResource, error) - GetResourceDefinitions(id []string, group []string, rType string, query string) (resources.ListResultPage, error) + GetResourceDefinitions(id []string, group []string, rType string, query string) ([]resources.GenericResource, error) GetMetricDefinitions(resourceId string, namespace string) (insights.MetricDefinitionCollection, error) GetMetricNamespaces(resourceId string) (insights.MetricNamespaceCollection, error) GetMetricValues(resourceId string, namespace string, timegrain string, timespan string, metricNames []string, aggregations string, filter string) ([]insights.Metric, string, error) From 32d45264d27acb5c62a272431dff96c47d1dea83 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Tue, 13 Oct 2020 16:36:02 +0200 Subject: [PATCH 34/46] Skip flaky test with oauth2 config in httpjson input (#21749) --- x-pack/filebeat/input/httpjson/config_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x-pack/filebeat/input/httpjson/config_test.go b/x-pack/filebeat/input/httpjson/config_test.go index 85c7c64848d..1986ee7abe9 100644 --- a/x-pack/filebeat/input/httpjson/config_test.go +++ b/x-pack/filebeat/input/httpjson/config_test.go @@ -362,6 +362,7 @@ func TestConfigOauth2Validation(t *testing.T) { "url": "localhost", }, }, + /* Flaky test: https://github.com/elastic/beats/issues/21748 { name: "date_cursor.date_format will fail if invalid", expectedErr: "invalid configuration: date_format is not a valid date layout accessing 'date_cursor'", @@ -370,6 +371,7 @@ func TestConfigOauth2Validation(t *testing.T) { "url": "localhost", }, }, + */ { name: "date_cursor must work with a valid date_format", input: map[string]interface{}{ From f754515e287672405bb1f1b6613deb17471d1f70 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Tue, 13 Oct 2020 18:55:46 +0200 Subject: [PATCH 35/46] Remove kafka partition ISR from Metricbeat docs (#21709) Metricbeat doesn't collect this field directly, it collects for each replica if it is in sync or not. --- metricbeat/docs/fields.asciidoc | 10 ---------- metricbeat/module/kafka/fields.go | 2 +- metricbeat/module/kafka/partition/_meta/fields.yml | 4 ---- 3 files changed, 1 insertion(+), 15 deletions(-) diff --git a/metricbeat/docs/fields.asciidoc b/metricbeat/docs/fields.asciidoc index 7f1d0f69ab9..a193d630564 100644 --- a/metricbeat/docs/fields.asciidoc +++ b/metricbeat/docs/fields.asciidoc @@ -24870,16 +24870,6 @@ type: long -- -*`kafka.partition.partition.isr`*:: -+ --- -List of isr ids. - - -type: keyword - --- - *`kafka.partition.partition.replica`*:: + -- diff --git a/metricbeat/module/kafka/fields.go b/metricbeat/module/kafka/fields.go index 72ee2cdc60c..96c306b5915 100644 --- a/metricbeat/module/kafka/fields.go +++ b/metricbeat/module/kafka/fields.go @@ -32,5 +32,5 @@ func init() { // AssetKafka returns asset data. // This is the base64 encoded gzipped contents of module/kafka. func AssetKafka() string { - return "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" + return "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" } diff --git a/metricbeat/module/kafka/partition/_meta/fields.yml b/metricbeat/module/kafka/partition/_meta/fields.yml index 8f241278589..cf40ad266b9 100644 --- a/metricbeat/module/kafka/partition/_meta/fields.yml +++ b/metricbeat/module/kafka/partition/_meta/fields.yml @@ -33,10 +33,6 @@ type: long description: > Leader id (broker). - - name: isr - type: keyword - description: > - List of isr ids. - name: replica type: long description: > From 7addb4d45502d6f1bc0d5ea9823dc5a926cda9ea Mon Sep 17 00:00:00 2001 From: kaiyan-sheng Date: Tue, 13 Oct 2020 11:36:12 -0600 Subject: [PATCH 36/46] [Filebeat] Add check for context.DeadlineExceeded error (#21732) --- x-pack/filebeat/input/s3/collector.go | 34 ++++++++++++--------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/x-pack/filebeat/input/s3/collector.go b/x-pack/filebeat/input/s3/collector.go index bf294f94245..1b890513284 100644 --- a/x-pack/filebeat/input/s3/collector.go +++ b/x-pack/filebeat/input/s3/collector.go @@ -148,8 +148,7 @@ func (c *s3Collector) processMessage(svcS3 s3iface.ClientAPI, message sqs.Messag // read from s3 object and create event for each log line err = c.handleS3Objects(svcS3, s3Infos, errC) if err != nil { - err = fmt.Errorf("handleS3Objects failed: %w", err) - c.logger.Error(err) + c.logger.Error(fmt.Errorf("handleS3Objects failed: %w", err)) return err } c.logger.Debugf("handleS3Objects succeed") @@ -163,7 +162,12 @@ func (c *s3Collector) processorKeepAlive(svcSQS sqsiface.ClientAPI, message sqs. return nil case err := <-errC: if err != nil { - c.logger.Warn("Processing message failed, updating visibility timeout") + if err == context.DeadlineExceeded { + c.logger.Info("Context deadline exceeded, updating visibility timeout") + } else { + c.logger.Warnf("Processing message failed '%w', updating visibility timeout", err) + } + err := c.changeVisibilityTimeout(queueURL, visibilityTimeout, svcSQS, message.ReceiptHandle) if err != nil { c.logger.Error(fmt.Errorf("SQS ChangeMessageVisibilityRequest failed: %w", err)) @@ -298,8 +302,7 @@ func (c *s3Collector) handleS3Objects(svc s3iface.ClientAPI, s3Infos []s3Info, e c.logger.Debugf("Processing file from s3 bucket \"%s\" with name \"%s\"", info.name, info.key) err := c.createEventsFromS3Info(svc, info, s3Ctx) if err != nil { - err = fmt.Errorf("createEventsFromS3Info failed processing file from s3 bucket \"%s\" with name \"%s\": %w", info.name, info.key, err) - c.logger.Error(err) + c.logger.Error(fmt.Errorf("createEventsFromS3Info failed processing file from s3 bucket \"%s\" with name \"%s\": %w", info.name, info.key, err)) s3Ctx.setError(err) } } @@ -326,8 +329,7 @@ func (c *s3Collector) createEventsFromS3Info(svc s3iface.ClientAPI, info s3Info, // If the SDK can determine the request or retry delay was canceled // by a context the ErrCodeRequestCanceled error will be returned. if awsErr.Code() == awssdk.ErrCodeRequestCanceled { - err = fmt.Errorf("s3 GetObjectRequest canceled for '%s' from S3 bucket '%s': %w", info.key, info.name, err) - c.logger.Error(err) + c.logger.Error(fmt.Errorf("s3 GetObjectRequest canceled for '%s' from S3 bucket '%s': %w", info.key, info.name, err)) return err } @@ -345,16 +347,14 @@ func (c *s3Collector) createEventsFromS3Info(svc s3iface.ClientAPI, info s3Info, isS3ObjGzipped, err := isStreamGzipped(reader) if err != nil { - err = fmt.Errorf("could not determine if S3 object is gzipped: %w", err) - c.logger.Error(err) + c.logger.Error(fmt.Errorf("could not determine if S3 object is gzipped: %w", err)) return err } if isS3ObjGzipped { gzipReader, err := gzip.NewReader(reader) if err != nil { - err = fmt.Errorf("gzip.NewReader failed for '%s' from S3 bucket '%s': %w", info.key, info.name, err) - c.logger.Error(err) + c.logger.Error(fmt.Errorf("gzip.NewReader failed for '%s' from S3 bucket '%s': %w", info.key, info.name, err)) return err } reader = bufio.NewReader(gzipReader) @@ -366,8 +366,7 @@ func (c *s3Collector) createEventsFromS3Info(svc s3iface.ClientAPI, info s3Info, decoder := json.NewDecoder(reader) err := c.decodeJSON(decoder, objectHash, info, s3Ctx) if err != nil { - err = fmt.Errorf("decodeJSONWithKey failed for '%s' from S3 bucket '%s': %w", info.key, info.name, err) - c.logger.Error(err) + c.logger.Error(fmt.Errorf("decodeJSONWithKey failed for '%s' from S3 bucket '%s': %w", info.key, info.name, err)) return err } return nil @@ -383,14 +382,12 @@ func (c *s3Collector) createEventsFromS3Info(svc s3iface.ClientAPI, info s3Info, event := createEvent(log, offset, info, objectHash, s3Ctx) err = c.forwardEvent(event) if err != nil { - err = fmt.Errorf("forwardEvent failed: %w", err) - c.logger.Error(err) + c.logger.Error(fmt.Errorf("forwardEvent failed: %w", err)) return err } return nil } else if err != nil { - err = fmt.Errorf("readStringAndTrimDelimiter failed: %w", err) - c.logger.Error(err) + c.logger.Error(fmt.Errorf("readStringAndTrimDelimiter failed: %w", err)) return err } @@ -403,8 +400,7 @@ func (c *s3Collector) createEventsFromS3Info(svc s3iface.ClientAPI, info s3Info, event := createEvent(log, offset, info, objectHash, s3Ctx) err = c.forwardEvent(event) if err != nil { - err = fmt.Errorf("forwardEvent failed: %w", err) - c.logger.Error(err) + c.logger.Error(fmt.Errorf("forwardEvent failed: %w", err)) return err } } From b0fbfaed277c6c04c23a4be8d30c2a512d915084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9mi=20V=C3=A1nyi?= Date: Tue, 13 Oct 2020 20:44:32 +0200 Subject: [PATCH 37/46] Add missing configuration annotations (#21736) --- filebeat/input/filestream/config.go | 2 +- filebeat/input/filestream/fswatch.go | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/filebeat/input/filestream/config.go b/filebeat/input/filestream/config.go index c2b1e838ee5..5b582ccf6e8 100644 --- a/filebeat/input/filestream/config.go +++ b/filebeat/input/filestream/config.go @@ -34,7 +34,7 @@ type config struct { Paths []string `config:"paths"` Close closerConfig `config:"close"` - FileWatcher *common.ConfigNamespace `config:"file_watcher"` + FileWatcher *common.ConfigNamespace `config:"prospector"` FileIdentity *common.ConfigNamespace `config:"file_identity"` CleanInactive time.Duration `config:"clean_inactive" validate:"min=0"` CleanRemoved bool `config:"clean_removed"` diff --git a/filebeat/input/filestream/fswatch.go b/filebeat/input/filestream/fswatch.go index 1b80971d835..e988fb3cee9 100644 --- a/filebeat/input/filestream/fswatch.go +++ b/filebeat/input/filestream/fswatch.go @@ -57,9 +57,9 @@ type fileScanner struct { type fileWatcherConfig struct { // Interval is the time between two scans. - Interval time.Duration + Interval time.Duration `config:"check_interval"` // Scanner is the configuration of the scanner. - Scanner fileScannerConfig + Scanner fileScannerConfig `config:",inline"` } // fileWatcher gets the list of files from a FSWatcher and creates events by @@ -212,10 +212,9 @@ func (w *fileWatcher) Event() loginp.FSEvent { } type fileScannerConfig struct { - Paths []string - ExcludedFiles []match.Matcher - Symlinks bool - RecursiveGlob bool + ExcludedFiles []match.Matcher `config:"exclude_files"` + Symlinks bool `config:"symlinks"` + RecursiveGlob bool `config:"recursive_glob"` } func defaultFileScannerConfig() fileScannerConfig { From fff5f6a2241e4c9c9513c98c7e821f0a5007d362 Mon Sep 17 00:00:00 2001 From: Michal Pristas Date: Tue, 13 Oct 2020 22:35:02 +0200 Subject: [PATCH 38/46] [Ingest Manager] Agent atomic installer (#21745) [Ingest Manager] Agent atomic installer (#21745) --- .../install/atomic/atomic_installer.go | 62 ++++++++++ .../install/atomic/atomic_installer_test.go | 115 ++++++++++++++++++ .../pkg/artifact/install/installer.go | 8 +- 3 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 x-pack/elastic-agent/pkg/artifact/install/atomic/atomic_installer.go create mode 100644 x-pack/elastic-agent/pkg/artifact/install/atomic/atomic_installer_test.go diff --git a/x-pack/elastic-agent/pkg/artifact/install/atomic/atomic_installer.go b/x-pack/elastic-agent/pkg/artifact/install/atomic/atomic_installer.go new file mode 100644 index 00000000000..5e26436bfc4 --- /dev/null +++ b/x-pack/elastic-agent/pkg/artifact/install/atomic/atomic_installer.go @@ -0,0 +1,62 @@ +// 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 atomic + +import ( + "context" + "io/ioutil" + "os" + "path/filepath" +) + +type embeddedInstaller interface { + Install(ctx context.Context, programName, version, installDir string) error +} + +// Installer installs into temporary destination and moves to correct one after +// successful finish. +type Installer struct { + installer embeddedInstaller +} + +// NewInstaller creates a new AtomicInstaller +func NewInstaller(i embeddedInstaller) (*Installer, error) { + return &Installer{ + installer: i, + }, nil +} + +// Install performs installation of program in a specific version. +func (i *Installer) Install(ctx context.Context, programName, version, installDir string) error { + // tar installer uses Dir of installDir to determine location of unpack + tempDir, err := ioutil.TempDir(os.TempDir(), "elastic-agent-install") + if err != nil { + return err + } + tempInstallDir := filepath.Join(tempDir, filepath.Base(installDir)) + + // cleanup install directory before Install + if _, err := os.Stat(installDir); err == nil || os.IsExist(err) { + os.RemoveAll(installDir) + } + + if _, err := os.Stat(tempInstallDir); err == nil || os.IsExist(err) { + os.RemoveAll(tempInstallDir) + } + + if err := i.installer.Install(ctx, programName, version, tempInstallDir); err != nil { + // cleanup unfinished install + os.RemoveAll(tempInstallDir) + return err + } + + if err := os.Rename(tempInstallDir, installDir); err != nil { + os.RemoveAll(installDir) + os.RemoveAll(tempInstallDir) + return err + } + + return nil +} diff --git a/x-pack/elastic-agent/pkg/artifact/install/atomic/atomic_installer_test.go b/x-pack/elastic-agent/pkg/artifact/install/atomic/atomic_installer_test.go new file mode 100644 index 00000000000..d6266659b7d --- /dev/null +++ b/x-pack/elastic-agent/pkg/artifact/install/atomic/atomic_installer_test.go @@ -0,0 +1,115 @@ +// 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 atomic + +import ( + "context" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "sync" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestOKInstall(t *testing.T) { + sig := make(chan int) + ti := &testInstaller{sig} + var wg sync.WaitGroup + i, err := NewInstaller(ti) + + assert.NoError(t, err) + + ctx := context.Background() + installDir := filepath.Join(os.TempDir(), "install_dir") + + wg.Add(1) + go func() { + err := i.Install(ctx, "a", "b", installDir) + assert.NoError(t, err) + wg.Done() + }() + + // signal to process next files + close(sig) + + wg.Wait() + + assert.DirExists(t, installDir) + files := getFiles() + + for name := range files { + path := filepath.Join(installDir, name) + assert.FileExists(t, path) + } + + os.RemoveAll(installDir) +} + +func TestContextCancelledInstall(t *testing.T) { + sig := make(chan int) + ti := &testInstaller{sig} + var wg sync.WaitGroup + i, err := NewInstaller(ti) + + assert.NoError(t, err) + + ctx, cancel := context.WithCancel(context.Background()) + installDir := filepath.Join(os.TempDir(), "install_dir") + + wg.Add(1) + go func() { + err := i.Install(ctx, "a", "b", installDir) + assert.Error(t, err) + wg.Done() + }() + + // cancel before signaling + cancel() + close(sig) + + wg.Wait() + + assert.NoDirExists(t, installDir) +} + +type testInstaller struct { + signal chan int +} + +func (ti *testInstaller) Install(ctx context.Context, programName, version, installDir string) error { + files := getFiles() + if err := os.MkdirAll(installDir, 0777); err != nil { + return err + } + + for name, content := range files { + if err := ctx.Err(); err != nil { + return err + } + + filename := filepath.Join(installDir, name) + if err := ioutil.WriteFile(filename, content, 0666); err != nil { + return err + } + + // wait for all but last + <-ti.signal + } + + return nil +} + +func getFiles() map[string][]byte { + files := make(map[string][]byte) + fileCount := 3 + for i := 1; i <= fileCount; i++ { + files[fmt.Sprintf("file_%d", i)] = []byte(fmt.Sprintf("content of file %d", i)) + } + + return files +} diff --git a/x-pack/elastic-agent/pkg/artifact/install/installer.go b/x-pack/elastic-agent/pkg/artifact/install/installer.go index f04e7a4238e..c606ada5d65 100644 --- a/x-pack/elastic-agent/pkg/artifact/install/installer.go +++ b/x-pack/elastic-agent/pkg/artifact/install/installer.go @@ -12,6 +12,7 @@ import ( "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/artifact/install/dir" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/artifact" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/artifact/install/atomic" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/artifact/install/hooks" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/artifact/install/tar" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/artifact/install/zip" @@ -60,5 +61,10 @@ func NewInstaller(config *artifact.Config) (InstallerChecker, error) { return nil, err } - return hooks.NewInstallerChecker(installer, dir.NewChecker()) + atomicInstaller, err := atomic.NewInstaller(installer) + if err != nil { + return nil, err + } + + return hooks.NewInstallerChecker(atomicInstaller, dir.NewChecker()) } From a74c74f3e5fdee905c1521edb5ad2d64b2d3a672 Mon Sep 17 00:00:00 2001 From: Michal Pristas Date: Wed, 14 Oct 2020 10:17:56 +0200 Subject: [PATCH 39/46] [Ingest Manager] Atomic installed forgotten changelog #21780 --- x-pack/elastic-agent/CHANGELOG.next.asciidoc | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/elastic-agent/CHANGELOG.next.asciidoc b/x-pack/elastic-agent/CHANGELOG.next.asciidoc index eb98ef39ded..96f036dd15d 100644 --- a/x-pack/elastic-agent/CHANGELOG.next.asciidoc +++ b/x-pack/elastic-agent/CHANGELOG.next.asciidoc @@ -15,6 +15,7 @@ - Copy Action store on upgrade {pull}21298[21298] - Include inputs in action store actions {pull}21298[21298] - Fix issue where inputs without processors defined would panic {pull}21628[21628] +- Partial extracted beat result in failure to spawn beat {issue}21718[21718] ==== New features From e3cf9939f97d77f6efd848b1786cbf2e2a963f74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Wed, 14 Oct 2020 11:23:48 +0200 Subject: [PATCH 40/46] chore: create CI artifacts for DEV usage (#21645) It will create the artifacts with some requirements related to integrity --- .ci/packaging.groovy | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.ci/packaging.groovy b/.ci/packaging.groovy index 9087cf48de4..37eeaa7d223 100644 --- a/.ci/packaging.groovy +++ b/.ci/packaging.groovy @@ -246,8 +246,12 @@ def runE2ETestForPackages(){ def release(){ withBeatsEnv(){ - dir("${env.BEATS_FOLDER}") { - sh(label: "Release ${env.BEATS_FOLDER} ${env.PLATFORMS}", script: 'mage package') + withEnv([ + "DEV=true" + ]) { + dir("${env.BEATS_FOLDER}") { + sh(label: "Release ${env.BEATS_FOLDER} ${env.PLATFORMS}", script: 'mage package') + } } publishPackages("${env.BEATS_FOLDER}") } From 303133990cbe6c001d3242445ab03a2d290c4883 Mon Sep 17 00:00:00 2001 From: Pavel Derendyaev Date: Wed, 14 Oct 2020 12:35:30 +0300 Subject: [PATCH 41/46] typofix for dns timeout configuration option (#21069) --- libbeat/processors/dns/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libbeat/processors/dns/config.go b/libbeat/processors/dns/config.go index 8b0d6c9bd97..a2c2a98955e 100644 --- a/libbeat/processors/dns/config.go +++ b/libbeat/processors/dns/config.go @@ -31,7 +31,7 @@ import ( type Config struct { CacheConfig `config:",inline"` Nameservers []string `config:"nameservers"` // Required on Windows. /etc/resolv.conf is used if none are given. - Timeout time.Duration `conifg:"timeout"` // Per request timeout (with 2 nameservers the total timeout would be 2x). + Timeout time.Duration `config:"timeout"` // Per request timeout (with 2 nameservers the total timeout would be 2x). Type string `config:"type" validate:"required"` // Reverse is the only supported type currently. Action FieldAction `config:"action"` // Append or replace (defaults to append) when target exists. TagOnFailure []string `config:"tag_on_failure"` // Tags to append when a failure occurs. From 6c80fb3b37df033adcaffa190208e5c1d6e88d7d Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Wed, 14 Oct 2020 12:00:07 +0200 Subject: [PATCH 42/46] Increase recommended memory when deploying in Cloud foundry (#21755) --- deploy/cloudfoundry/filebeat/manifest.yml | 2 +- deploy/cloudfoundry/metricbeat/manifest.yml | 2 +- filebeat/docs/running-on-cloudfoundry.asciidoc | 2 +- metricbeat/docs/running-on-cloudfoundry.asciidoc | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/deploy/cloudfoundry/filebeat/manifest.yml b/deploy/cloudfoundry/filebeat/manifest.yml index 26ec5a0b958..5c424823cb3 100644 --- a/deploy/cloudfoundry/filebeat/manifest.yml +++ b/deploy/cloudfoundry/filebeat/manifest.yml @@ -1,6 +1,6 @@ applications: - name: filebeat - memory: 256M + memory: 512M instances: 1 buildpacks: - binary_buildpack diff --git a/deploy/cloudfoundry/metricbeat/manifest.yml b/deploy/cloudfoundry/metricbeat/manifest.yml index 40f0459b8da..1a2bb025683 100644 --- a/deploy/cloudfoundry/metricbeat/manifest.yml +++ b/deploy/cloudfoundry/metricbeat/manifest.yml @@ -1,6 +1,6 @@ applications: - name: metricbeat - memory: 256M + memory: 512M instances: 1 buildpacks: - binary_buildpack diff --git a/filebeat/docs/running-on-cloudfoundry.asciidoc b/filebeat/docs/running-on-cloudfoundry.asciidoc index ae9603dc012..c08efa30c5f 100644 --- a/filebeat/docs/running-on-cloudfoundry.asciidoc +++ b/filebeat/docs/running-on-cloudfoundry.asciidoc @@ -66,7 +66,7 @@ To check the status, run: $ cf apps name requested state instances memory disk urls -filebeat started 1/1 256M 1G +filebeat started 1/1 512M 1G ------------------------------------------------ Log events should start flowing to Elasticsearch. The events are annotated with diff --git a/metricbeat/docs/running-on-cloudfoundry.asciidoc b/metricbeat/docs/running-on-cloudfoundry.asciidoc index 2988e4d3a8b..5fdf20c9f72 100644 --- a/metricbeat/docs/running-on-cloudfoundry.asciidoc +++ b/metricbeat/docs/running-on-cloudfoundry.asciidoc @@ -66,7 +66,7 @@ To check the status, run: $ cf apps name requested state instances memory disk urls -metricbeat started 1/1 256M 1G +metricbeat started 1/1 512M 1G ------------------------------------------------ Metrics should start flowing to Elasticsearch. The events are annotated with From 1685f972d39607203a65eb9208941a3bc9d87a86 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Wed, 14 Oct 2020 11:03:28 +0100 Subject: [PATCH 43/46] [CI] Support skip-ci label (#21377) --- Jenkinsfile | 14 +++++++++----- Jenkinsfile.yml | 9 ++++----- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 17041987b27..30564270125 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -103,13 +103,17 @@ pipeline { script { def mapParallelTasks = [:] def content = readYaml(file: 'Jenkinsfile.yml') - content['projects'].each { projectName -> - generateStages(project: projectName, changeset: content['changeset']).each { k,v -> - mapParallelTasks["${k}"] = v + if (content?.disabled?.when?.labels && beatsWhen(project: 'top-level', content: content?.disabled?.when)) { + error 'Pull Request has been configured to be disabled when there is a skip-ci label match' + } else { + content['projects'].each { projectName -> + generateStages(project: projectName, changeset: content['changeset']).each { k,v -> + mapParallelTasks["${k}"] = v + } } + notifyBuildReason() + parallel(mapParallelTasks) } - notifyBuildReason() - parallel(mapParallelTasks) } } } diff --git a/Jenkinsfile.yml b/Jenkinsfile.yml index 2278ea93735..f7b21e1cbdf 100644 --- a/Jenkinsfile.yml +++ b/Jenkinsfile.yml @@ -40,10 +40,9 @@ changeset: - "^testing/.*" - "^x-pack/libbeat/.*" -## Proposal -## TBC: This will allow to configure what to do based on the PR configuration disabled: when: - labels: ## Skip the GitHub Pull Request builds if there is a GitHub label match - - "skip-ci" - draft: true ## Skip the GitHub Pull Request builds with Draft PRs. + labels: ## Skip the GitHub Pull Request builds if any of the given GitHub labels match with the assigned labels in the PR. + - skip-ci + ## TODO: This will allow to configure what to do based on the PR configuration + draft: true ## Skip the GitHub Pull Request builds with Draft PRs. \ No newline at end of file From 3e5a167809210e9999732d5e50c7e895e10fc389 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Wed, 14 Oct 2020 12:58:48 +0200 Subject: [PATCH 44/46] Make API address and Shard ID required in Cloud Foundry settings (#21759) Having an auto-generated Shard ID leads to duplicated data when trying to scale, increasing the problems of loaded systems. Forcing to set a shard ID makes the user more conscious of the implications of this setting. API address should be always set in a real deployment. --- CHANGELOG.next.asciidoc | 3 +++ deploy/cloudfoundry/filebeat/filebeat.yml | 2 +- deploy/cloudfoundry/metricbeat/metricbeat.yml | 3 ++- .../docs/running-on-cloudfoundry.asciidoc | 8 ------ metricbeat/docs/modules/cloudfoundry.asciidoc | 3 ++- .../docs/running-on-cloudfoundry.asciidoc | 8 ------ .../docs/inputs/input-cloudfoundry.asciidoc | 2 ++ x-pack/libbeat/common/cloudfoundry/config.go | 13 ++-------- .../common/cloudfoundry/config_test.go | 26 +++++++++++++++++-- .../common/cloudfoundry/test/config.go | 9 ++++++- .../add_cloudfoundry_metadata.go | 6 +++++ x-pack/metricbeat/metricbeat.reference.yml | 1 + .../cloudfoundry/_meta/config.reference.yml | 1 + .../module/cloudfoundry/_meta/config.yml | 1 + .../module/cloudfoundry/_meta/docs.asciidoc | 2 +- .../modules.d/cloudfoundry.yml.disabled | 1 + 16 files changed, 55 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 41de16190a8..8ff5fbee069 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -25,6 +25,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Allow embedding of CAs, Certificate of private keys for anything that support TLS in ouputs and inputs https://github.com/elastic/beats/pull/21179 - Update to Golang 1.12.1. {pull}11330[11330] - Disable Alibaba Cloud and Tencent Cloud metadata providers by default. {pull}13812[12812] +- API address is a required setting in `add_cloudfoundry_metadata`. {pull}21759[21759] *Auditbeat* @@ -78,6 +79,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Removed experimental modules `citrix`, `kaspersky`, `rapid7` and `tenable`. {pull}20706[20706] - Add support for GMT timezone offsets in `decode_cef`. {pull}20993[20993] - Fix parsing of Elasticsearch node name by `elasticsearch/slowlog` fileset. {pull}14547[14547] +- API address and shard ID are required settings in the Cloud Foundry input. {pull}21759[21759] *Heartbeat* @@ -95,6 +97,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Fix ECS compliance of user.id field in system/users metricset {pull}19019[19019] - Rename googlecloud stackdriver metricset to metrics. {pull}19718[19718] - Remove "invalid zero" metrics on Windows and Darwin, don't report linux-only memory and diskio metrics when running under agent. {pull}21457[21457] +- API address and shard ID are required settings in the Cloud Foundry module. {pull}21759[21759] *Packetbeat* diff --git a/deploy/cloudfoundry/filebeat/filebeat.yml b/deploy/cloudfoundry/filebeat/filebeat.yml index f4c4943d4bb..f3d58f079a1 100644 --- a/deploy/cloudfoundry/filebeat/filebeat.yml +++ b/deploy/cloudfoundry/filebeat/filebeat.yml @@ -11,7 +11,7 @@ filebeat.inputs: #doppler_address: ${DOPPLER_ADDRESS} #uaa_address: ${UAA_ADDRESS} #rlp_address: ${RLP_ADDRESS} - #shard_id: ${SHARD_ID} + shard_id: ${SHARD_ID} #version: v1 diff --git a/deploy/cloudfoundry/metricbeat/metricbeat.yml b/deploy/cloudfoundry/metricbeat/metricbeat.yml index c89c00ce983..12cfee75f00 100644 --- a/deploy/cloudfoundry/metricbeat/metricbeat.yml +++ b/deploy/cloudfoundry/metricbeat/metricbeat.yml @@ -11,7 +11,8 @@ metricbeat.modules: #doppler_address: ${DOPPLER_ADDRESS} #uaa_address: ${UAA_ADDRESS} #rlp_address: ${RLP_ADDRESS} - #shard_id: ${SHARD_ID} + shard_id: ${SHARD_ID} + #version: v1 #================================ Outputs ===================================== diff --git a/filebeat/docs/running-on-cloudfoundry.asciidoc b/filebeat/docs/running-on-cloudfoundry.asciidoc index c08efa30c5f..5aa3ef95837 100644 --- a/filebeat/docs/running-on-cloudfoundry.asciidoc +++ b/filebeat/docs/running-on-cloudfoundry.asciidoc @@ -71,11 +71,3 @@ filebeat started 1/1 512M 1G Log events should start flowing to Elasticsearch. The events are annotated with metadata added by the <> processor. - - -[WARNING] -======================================= -*Set shard_id to scale:* By default {beatname_uc} will generate a random `shard_id` when it starts. In the case that -{beatname_uc} needs to be scaled passed 1 instance, be sure to set a static `shard_id`. Not setting a static `shard_id` -will result in duplicate events being pushed to Elasticsearch. -======================================= diff --git a/metricbeat/docs/modules/cloudfoundry.asciidoc b/metricbeat/docs/modules/cloudfoundry.asciidoc index 4d153933c23..614c703d155 100644 --- a/metricbeat/docs/modules/cloudfoundry.asciidoc +++ b/metricbeat/docs/modules/cloudfoundry.asciidoc @@ -117,7 +117,7 @@ Client Secret to authenticate with Cloud Foundry. Default: "". === `shard_id` Shard ID for connection to the RLP Gateway. Use the same ID across multiple {beatname_lc} to shard the load of events -from the RLP Gateway. Default: "(generated UUID)". +from the RLP Gateway. [float] ==== `version` @@ -152,6 +152,7 @@ metricbeat.modules: rlp_address: '${CLOUDFOUNDRY_RLP_ADDRESS:""}' client_id: '${CLOUDFOUNDRY_CLIENT_ID:""}' client_secret: '${CLOUDFOUNDRY_CLIENT_SECRET:""}' + shard_id: metricbeat version: v1 ---- diff --git a/metricbeat/docs/running-on-cloudfoundry.asciidoc b/metricbeat/docs/running-on-cloudfoundry.asciidoc index 5fdf20c9f72..59802ba505d 100644 --- a/metricbeat/docs/running-on-cloudfoundry.asciidoc +++ b/metricbeat/docs/running-on-cloudfoundry.asciidoc @@ -71,11 +71,3 @@ metricbeat started 1/1 512M 1G Metrics should start flowing to Elasticsearch. The events are annotated with metadata added by the <> processor. - - -[WARNING] -======================================= -*Set shard_id to scale:* By default {beatname_uc} will generate a random `shard_id` when it starts. In the case that -{beatname_uc} needs to be scaled passed 1 instance, be sure to set a static `shard_id`. Not setting a static `shard_id` -will result in duplicate events being pushed to Elasticsearch. -======================================= diff --git a/x-pack/filebeat/docs/inputs/input-cloudfoundry.asciidoc b/x-pack/filebeat/docs/inputs/input-cloudfoundry.asciidoc index f8d5dd51015..551d0095b3a 100644 --- a/x-pack/filebeat/docs/inputs/input-cloudfoundry.asciidoc +++ b/x-pack/filebeat/docs/inputs/input-cloudfoundry.asciidoc @@ -23,6 +23,7 @@ Example configurations: api_address: https://api.dev.cfdev.sh client_id: uaa-filebeat client_secret: verysecret + shard_id: filebeat ssl: verification_mode: none ---- @@ -34,6 +35,7 @@ Example configurations: api_address: https://api.dev.cfdev.sh client_id: uaa-filebeat client_secret: verysecret + shard_id: filebeat ssl.certificate_authorities: ["/etc/pki/cf/ca.pem"] ssl.certificate: "/etc/pki/cf/cert.pem" ssl.key: "/etc/pki/cf/cert.key" diff --git a/x-pack/libbeat/common/cloudfoundry/config.go b/x-pack/libbeat/common/cloudfoundry/config.go index 0724bdc66e1..2f15d0c7cf9 100644 --- a/x-pack/libbeat/common/cloudfoundry/config.go +++ b/x-pack/libbeat/common/cloudfoundry/config.go @@ -10,8 +10,6 @@ import ( "strings" "time" - "github.com/gofrs/uuid" - "github.com/elastic/beats/v7/libbeat/common/transport/tlscommon" ) @@ -32,14 +30,14 @@ type Config struct { TLS *tlscommon.Config `config:"ssl"` // Override URLs returned from the CF client - APIAddress string `config:"api_address"` + APIAddress string `config:"api_address" validate:"required"` DopplerAddress string `config:"doppler_address"` UaaAddress string `config:"uaa_address"` RlpAddress string `config:"rlp_address"` // ShardID when retrieving events from loggregator, sharing this ID across // multiple filebeats will shard the load of receiving and sending events. - ShardID string `config:"shard_id"` + ShardID string `config:"shard_id" validate:"required"` // Maximum amount of time to cache application objects from CF client. CacheDuration time.Duration `config:"cache_duration"` @@ -50,13 +48,6 @@ type Config struct { // InitDefaults initialize the defaults for the configuration. func (c *Config) InitDefaults() { - // If not provided by the user; subscription ID should be a unique string to avoid clustering by default. - // Default to using a UUID4 string. - uuid, err := uuid.NewV4() - if err != nil { - panic(err) - } - c.ShardID = uuid.String() c.CacheDuration = 120 * time.Second c.CacheRetryDelay = 20 * time.Second c.Version = ConsumerVersionV1 diff --git a/x-pack/libbeat/common/cloudfoundry/config_test.go b/x-pack/libbeat/common/cloudfoundry/config_test.go index bce98a0b642..79ba7d47cc1 100644 --- a/x-pack/libbeat/common/cloudfoundry/config_test.go +++ b/x-pack/libbeat/common/cloudfoundry/config_test.go @@ -22,26 +22,48 @@ func TestValidation(t *testing.T) { var noId Config assert.Error(t, ucfg.MustNewFrom(common.MapStr{ + "api_address": "https://api.dev.cfdev.sh", "client_secret": "client_secret", + "shard_id": "beats-test-1", }).Unpack(&noId)) var noSecret Config assert.Error(t, ucfg.MustNewFrom(common.MapStr{ - "client_id": "client_id", + "api_address": "https://api.dev.cfdev.sh", + "client_id": "client_id", + "shard_id": "beats-test-1", }).Unpack(&noSecret)) + var noAPI Config + assert.Error(t, ucfg.MustNewFrom(common.MapStr{ + "client_id": "client_id", + "client_secret": "client_secret", + "shard_id": "beats-test-1", + }).Unpack(&noAPI)) + + var noShardID Config + assert.Error(t, ucfg.MustNewFrom(common.MapStr{ + "api_address": "https://api.dev.cfdev.sh", + "client_id": "client_id", + "client_secret": "client_secret", + }).Unpack(&noShardID)) + var valid Config assert.NoError(t, ucfg.MustNewFrom(common.MapStr{ + "api_address": "https://api.dev.cfdev.sh", "client_id": "client_id", "client_secret": "client_secret", + "shard_id": "beats-test-1", }).Unpack(&valid)) } func TestInitDefaults(t *testing.T) { var cfCfg Config assert.NoError(t, ucfg.MustNewFrom(common.MapStr{ + "api_address": "https://api.dev.cfdev.sh", "client_id": "client_id", "client_secret": "client_secret", + "shard_id": "beats-test-1", }).Unpack(&cfCfg)) - assert.Len(t, cfCfg.ShardID, 36) + assert.Equal(t, ConsumerVersionV1, cfCfg.Version) } diff --git a/x-pack/libbeat/common/cloudfoundry/test/config.go b/x-pack/libbeat/common/cloudfoundry/test/config.go index f7b9cd18ffb..45059fc559c 100644 --- a/x-pack/libbeat/common/cloudfoundry/test/config.go +++ b/x-pack/libbeat/common/cloudfoundry/test/config.go @@ -7,15 +7,23 @@ package test import ( "os" "testing" + + "github.com/gofrs/uuid" ) func GetConfigFromEnv(t *testing.T) map[string]interface{} { t.Helper() + shardID, err := uuid.NewV4() + if err != nil { + t.Fatalf("Unable to create a random shard ID: %v", err) + } + config := map[string]interface{}{ "api_address": lookupEnv(t, "CLOUDFOUNDRY_API_ADDRESS"), "client_id": lookupEnv(t, "CLOUDFOUNDRY_CLIENT_ID"), "client_secret": lookupEnv(t, "CLOUDFOUNDRY_CLIENT_SECRET"), + "shard_id": shardID.String(), "ssl.verification_mode": "none", } @@ -23,7 +31,6 @@ func GetConfigFromEnv(t *testing.T) map[string]interface{} { optionalConfig(config, "uaa_address", "CLOUDFOUNDRY_UAA_ADDRESS") optionalConfig(config, "rlp_address", "CLOUDFOUNDRY_RLP_ADDRESS") optionalConfig(config, "doppler_address", "CLOUDFOUNDRY_DOPPLER_ADDRESS") - optionalConfig(config, "shard_id", "CLOUDFOUNDRY_SHARD_ID") if t.Failed() { t.FailNow() diff --git a/x-pack/libbeat/processors/add_cloudfoundry_metadata/add_cloudfoundry_metadata.go b/x-pack/libbeat/processors/add_cloudfoundry_metadata/add_cloudfoundry_metadata.go index c178ea04325..a6b8bd16566 100644 --- a/x-pack/libbeat/processors/add_cloudfoundry_metadata/add_cloudfoundry_metadata.go +++ b/x-pack/libbeat/processors/add_cloudfoundry_metadata/add_cloudfoundry_metadata.go @@ -5,6 +5,7 @@ package add_cloudfoundry_metadata import ( + "github.com/gofrs/uuid" "github.com/pkg/errors" "github.com/elastic/beats/v7/libbeat/beat" @@ -32,6 +33,11 @@ const selector = "add_cloudfoundry_metadata" // New constructs a new add_cloudfoundry_metadata processor. func New(cfg *common.Config) (processors.Processor, error) { var config cloudfoundry.Config + + // ShardID is required in cloudfoundry config to consume from the firehose, + // but not for metadata requests, randomly generate one and use it. + config.ShardID = uuid.Must(uuid.NewV4()).String() + if err := cfg.Unpack(&config); err != nil { return nil, errors.Wrapf(err, "fail to unpack the %v configuration", processorName) } diff --git a/x-pack/metricbeat/metricbeat.reference.yml b/x-pack/metricbeat/metricbeat.reference.yml index b7b62643353..41552410a38 100644 --- a/x-pack/metricbeat/metricbeat.reference.yml +++ b/x-pack/metricbeat/metricbeat.reference.yml @@ -398,6 +398,7 @@ metricbeat.modules: rlp_address: '${CLOUDFOUNDRY_RLP_ADDRESS:""}' client_id: '${CLOUDFOUNDRY_CLIENT_ID:""}' client_secret: '${CLOUDFOUNDRY_CLIENT_SECRET:""}' + shard_id: metricbeat version: v1 #----------------------------- CockroachDB Module ----------------------------- diff --git a/x-pack/metricbeat/module/cloudfoundry/_meta/config.reference.yml b/x-pack/metricbeat/module/cloudfoundry/_meta/config.reference.yml index be15db23b65..6299cfc8116 100644 --- a/x-pack/metricbeat/module/cloudfoundry/_meta/config.reference.yml +++ b/x-pack/metricbeat/module/cloudfoundry/_meta/config.reference.yml @@ -10,4 +10,5 @@ rlp_address: '${CLOUDFOUNDRY_RLP_ADDRESS:""}' client_id: '${CLOUDFOUNDRY_CLIENT_ID:""}' client_secret: '${CLOUDFOUNDRY_CLIENT_SECRET:""}' + shard_id: metricbeat version: v1 diff --git a/x-pack/metricbeat/module/cloudfoundry/_meta/config.yml b/x-pack/metricbeat/module/cloudfoundry/_meta/config.yml index a2803b06d40..5ea86f3e8de 100644 --- a/x-pack/metricbeat/module/cloudfoundry/_meta/config.yml +++ b/x-pack/metricbeat/module/cloudfoundry/_meta/config.yml @@ -7,3 +7,4 @@ api_address: '${CLOUDFOUNDRY_API_ADDRESS:""}' client_id: '${CLOUDFOUNDRY_CLIENT_ID:""}' client_secret: '${CLOUDFOUNDRY_CLIENT_SECRET:""}' + shard_id: metricbeat diff --git a/x-pack/metricbeat/module/cloudfoundry/_meta/docs.asciidoc b/x-pack/metricbeat/module/cloudfoundry/_meta/docs.asciidoc index 4d908802358..752b2aaea0c 100644 --- a/x-pack/metricbeat/module/cloudfoundry/_meta/docs.asciidoc +++ b/x-pack/metricbeat/module/cloudfoundry/_meta/docs.asciidoc @@ -107,7 +107,7 @@ Client Secret to authenticate with Cloud Foundry. Default: "". === `shard_id` Shard ID for connection to the RLP Gateway. Use the same ID across multiple {beatname_lc} to shard the load of events -from the RLP Gateway. Default: "(generated UUID)". +from the RLP Gateway. [float] ==== `version` diff --git a/x-pack/metricbeat/modules.d/cloudfoundry.yml.disabled b/x-pack/metricbeat/modules.d/cloudfoundry.yml.disabled index ae540f20cfc..22a600e51d8 100644 --- a/x-pack/metricbeat/modules.d/cloudfoundry.yml.disabled +++ b/x-pack/metricbeat/modules.d/cloudfoundry.yml.disabled @@ -10,3 +10,4 @@ api_address: '${CLOUDFOUNDRY_API_ADDRESS:""}' client_id: '${CLOUDFOUNDRY_CLIENT_ID:""}' client_secret: '${CLOUDFOUNDRY_CLIENT_SECRET:""}' + shard_id: metricbeat From 85b95ee14300324f4950cdca13df1e04e84cc330 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Wed, 14 Oct 2020 13:15:39 +0200 Subject: [PATCH 45/46] Disable writes sync in persistent cache (#21754) With default configuration, every write is synced to disk. For the persistent cache, Beats not need to guarantee that everything is on disk, as the data can be recovered from the source endpoints, but the database was not being properly closed, so there could happen that most of the recent data was lost if the beat is stopped. Now, the processors can close their resources, so the database is properly closed in an normal shutdown, so we can go on without syncs on writes, what gives a much better performance. --- x-pack/libbeat/persistentcache/store.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/x-pack/libbeat/persistentcache/store.go b/x-pack/libbeat/persistentcache/store.go index 589fc724e01..e14b90fedda 100644 --- a/x-pack/libbeat/persistentcache/store.go +++ b/x-pack/libbeat/persistentcache/store.go @@ -36,10 +36,7 @@ func newStore(logger *logp.Logger, dir, name string) (*Store, error) { // Opinionated options for the use of badger as a store for metadata caches in Beats. options := badger.DefaultOptions(dbPath) options.Logger = badgerLogger{logger.Named("badger")} - // TODO: Disabling sync writes gives better performance, and data loss wouldn't - // be a problem for caches. But we are not properly closing processors yet, so let - // sync on writes by now. - // options.SyncWrites = false + options.SyncWrites = false db, err := badger.Open(options) if err != nil { From 355eef4f4bf1081568a0cd15bda16d17cd628fb2 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Wed, 14 Oct 2020 13:59:52 +0200 Subject: [PATCH 46/46] Use badger code from upstream repository (#21705) Issue in badger with 32-bits architectures has been solved in the release branch used by Beats. Update to the version with this fix and stop using the Elastic fork. --- NOTICE.txt | 6 +++--- go.mod | 3 +-- go.sum | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/NOTICE.txt b/NOTICE.txt index b62c4b5d202..349fe58b3d1 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -4473,12 +4473,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- -Dependency : github.com/elastic/badger/v2 -Version: v2.2007.2-beats +Dependency : github.com/dgraph-io/badger/v2 +Version: v2.2007.3-0.20201012072640-f5a7e0a1c83b Licence type (autodetected): Apache-2.0 -------------------------------------------------------------------------------- -Contents of probable licence file $GOMODCACHE/github.com/elastic/badger/v2@v2.2007.2-beats/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/dgraph-io/badger/v2@v2.2007.3-0.20201012072640-f5a7e0a1c83b/LICENSE: Apache License Version 2.0, January 2004 diff --git a/go.mod b/go.mod index 0f0bdb3a422..720690f1f2f 100644 --- a/go.mod +++ b/go.mod @@ -45,7 +45,7 @@ require ( github.com/davecgh/go-xdr v0.0.0-20161123171359-e6a2ba005892 // indirect github.com/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e github.com/devigned/tab v0.1.2-0.20190607222403-0c15cf42f9a2 // indirect - github.com/dgraph-io/badger/v2 v2.2007.2 + github.com/dgraph-io/badger/v2 v2.2007.3-0.20201012072640-f5a7e0a1c83b github.com/dgrijalva/jwt-go v3.2.1-0.20190620180102-5e25c22bd5d6+incompatible // indirect github.com/digitalocean/go-libvirt v0.0.0-20180301200012-6075ea3c39a1 github.com/dlclark/regexp2 v1.1.7-0.20171009020623-7632a260cbaf // indirect @@ -191,7 +191,6 @@ replace ( github.com/Azure/go-autorest => github.com/Azure/go-autorest v12.2.0+incompatible github.com/Shopify/sarama => github.com/elastic/sarama v1.19.1-0.20200629123429-0e7b69039eec github.com/cucumber/godog => github.com/cucumber/godog v0.8.1 - github.com/dgraph-io/badger/v2 => github.com/elastic/badger/v2 v2.2007.2-beats github.com/docker/docker => github.com/docker/engine v0.0.0-20191113042239-ea84732a7725 github.com/docker/go-plugins-helpers => github.com/elastic/go-plugins-helpers v0.0.0-20200207104224-bdf17607b79f github.com/dop251/goja => github.com/andrewkroh/goja v0.0.0-20190128172624-dd2ac4456e20 diff --git a/go.sum b/go.sum index 6f737d7cee2..5c01c612fe3 100644 --- a/go.sum +++ b/go.sum @@ -201,6 +201,8 @@ github.com/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e/go.mod h1:xb github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= github.com/devigned/tab v0.1.2-0.20190607222403-0c15cf42f9a2 h1:6+hM8KeYKV0Z9EIINNqIEDyyIRAcNc2FW+/TUYNmWyw= github.com/devigned/tab v0.1.2-0.20190607222403-0c15cf42f9a2/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= +github.com/dgraph-io/badger/v2 v2.2007.3-0.20201012072640-f5a7e0a1c83b h1:mUDs72Rlzv6A4YN8w3Ra3hU9x/plOQPcQjZYL/1f5SM= +github.com/dgraph-io/badger/v2 v2.2007.3-0.20201012072640-f5a7e0a1c83b/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de h1:t0UHb5vdojIDUqktM6+xJAfScFBsVpXZmqC9dsgJmeA= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= @@ -239,8 +241,6 @@ github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/eclipse/paho.mqtt.golang v1.2.1-0.20200121105743-0d940dd29fd2 h1:DW6WrARxK5J+o8uAKCiACi5wy9EK1UzrsCpGBPsKHAA= github.com/eclipse/paho.mqtt.golang v1.2.1-0.20200121105743-0d940dd29fd2/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= -github.com/elastic/badger/v2 v2.2007.2-beats h1:/rV4bM6fdYvPQhFf2bHHivIb0H4nX8Or7nkWbQ/Q6Ko= -github.com/elastic/badger/v2 v2.2007.2-beats/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= github.com/elastic/dhcp v0.0.0-20200227161230-57ec251c7eb3 h1:lnDkqiRFKm0rxdljqrj3lotWinO9+jFmeDXIC4gvIQs= github.com/elastic/dhcp v0.0.0-20200227161230-57ec251c7eb3/go.mod h1:aPqzac6AYkipvp4hufTyMj5PDIphF3+At8zr7r51xjY= github.com/elastic/ecs v1.6.0 h1:8NmgfnsjmKXh9hVsK3H2tZtfUptepNc3msJOAynhtmc=