diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index a788d5f7fb1..a05591b3da0 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -462,6 +462,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Enable `add_observer_metadata` processor in default config. {pull}11394[11394] - Record HTTP body metadata and optionally contents in `http.response.body.*` fields. {pull}13022[13022] +- Add `monitor.timespan` field for optimized queries in kibana. {pull}13672[13672] - Allow `hosts` to be used to configure http monitors {pull}13703[13703] - google-pubsub input: ACK pub/sub message when acknowledged by publisher. {issue}13346[13346] {pull}14715[14715] - Remove Beta label from google-pubsub input. {issue}13346[13346] {pull}14715[14715] diff --git a/heartbeat/_meta/fields.common.yml b/heartbeat/_meta/fields.common.yml index 7cd6393278b..28a721494f9 100644 --- a/heartbeat/_meta/fields.common.yml +++ b/heartbeat/_meta/fields.common.yml @@ -63,6 +63,11 @@ description: > A token unique to a simultaneously invoked group of checks as in the case where multiple IPs are checked for a single DNS entry. + - name: timespan + type: date_range + description: > + Time range this ping reported starting at the instant the check was started, ending at the start of the next scheduled check. + - key: summary title: "Monitor summary" description: diff --git a/heartbeat/docs/fields.asciidoc b/heartbeat/docs/fields.asciidoc index c02828a41e7..91be9bc8c08 100644 --- a/heartbeat/docs/fields.asciidoc +++ b/heartbeat/docs/fields.asciidoc @@ -296,6 +296,16 @@ type: keyword -- +*`monitor.timespan`*:: ++ +-- +Time range this ping reported starting at the instant the check was started, ending at the start of the next scheduled check. + + +type: date_range + +-- + [[exported-fields-docker-processor]] == Docker fields diff --git a/heartbeat/hbtest/hbtestutil.go b/heartbeat/hbtest/hbtestutil.go index 277da7432ce..05f09d0f1e9 100644 --- a/heartbeat/hbtest/hbtestutil.go +++ b/heartbeat/hbtest/hbtestutil.go @@ -30,6 +30,8 @@ import ( "strings" "testing" + "github.com/elastic/beats/heartbeat/hbtestllext" + "github.com/stretchr/testify/require" "github.com/elastic/beats/heartbeat/monitors/wrappers" @@ -124,17 +126,21 @@ func BaseChecks(ip string, status string, typ string) validator.Validator { } else { ipCheck = isdef.Optional(isdef.IsEqual(ip)) } - return lookslike.MustCompile(map[string]interface{}{ - "monitor": map[string]interface{}{ - "ip": ipCheck, - "duration.us": isdef.IsDuration, - "status": status, - "id": isdef.IsNonEmptyString, - "name": isdef.IsString, - "type": typ, - "check_group": isdef.IsString, - }, - }) + + return lookslike.Compose( + lookslike.MustCompile(map[string]interface{}{ + "monitor": map[string]interface{}{ + "ip": ipCheck, + "status": status, + "duration.us": isdef.IsDuration, + "id": isdef.IsNonEmptyString, + "name": isdef.IsString, + "type": typ, + "check_group": isdef.IsString, + }, + }), + hbtestllext.MonitorTimespanValidator, + ) } // SummaryChecks validates the "summary" field and its subfields. diff --git a/heartbeat/hbtestllext/isdefs.go b/heartbeat/hbtestllext/isdefs.go new file mode 100644 index 00000000000..b913c6c40da --- /dev/null +++ b/heartbeat/hbtestllext/isdefs.go @@ -0,0 +1,35 @@ +// 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 hbtestllext + +import ( + "time" + + "github.com/elastic/go-lookslike/isdef" + "github.com/elastic/go-lookslike/llpath" + "github.com/elastic/go-lookslike/llresult" +) + +// IsTime checks that the value is a time.Time instance. +var IsTime = isdef.Is("time", func(path llpath.Path, v interface{}) *llresult.Results { + _, ok := v.(time.Time) + if !ok { + return llresult.SimpleResult(path, false, "expected a time.Time") + } + return llresult.ValidResult(path) +}) diff --git a/heartbeat/hbtestllext/validators.go b/heartbeat/hbtestllext/validators.go new file mode 100644 index 00000000000..23a9df5d5cf --- /dev/null +++ b/heartbeat/hbtestllext/validators.go @@ -0,0 +1,32 @@ +// 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 hbtestllext + +import ( + "github.com/elastic/go-lookslike" +) + +// MonitorTimespanValidator is tests for the `next_run` and `next_run_in.us` keys. +var MonitorTimespanValidator = lookslike.MustCompile(map[string]interface{}{ + "monitor": map[string]interface{}{ + "timespan": map[string]interface{}{ + "gte": IsTime, + "lt": IsTime, + }, + }, +}) diff --git a/heartbeat/include/fields.go b/heartbeat/include/fields.go index 8c2cbeaa053..a0cecd709e5 100644 --- a/heartbeat/include/fields.go +++ b/heartbeat/include/fields.go @@ -32,5 +32,5 @@ func init() { // AssetFieldsYml returns asset data. // This is the base64 encoded gzipped contents of fields.yml. func AssetFieldsYml() string { - return "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" + return "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" } diff --git a/heartbeat/monitors/active/http/http_test.go b/heartbeat/monitors/active/http/http_test.go index 98339385a0c..9066306de86 100644 --- a/heartbeat/monitors/active/http/http_test.go +++ b/heartbeat/monitors/active/http/http_test.go @@ -35,6 +35,7 @@ import ( "github.com/elastic/beats/heartbeat/hbtest" "github.com/elastic/beats/heartbeat/monitors/wrappers" + schedule "github.com/elastic/beats/heartbeat/scheduler/schedule" "github.com/elastic/beats/libbeat/beat" "github.com/elastic/beats/libbeat/common" "github.com/elastic/beats/libbeat/common/file" @@ -74,7 +75,8 @@ func testTLSRequest(t *testing.T, testURL string, useUrls bool, extraConfig map[ jobs, endpoints, err := create("tls", config) require.NoError(t, err) - job := wrappers.WrapCommon(jobs, "tls", "", "http")[0] + sched, _ := schedule.Parse("@every 1s") + job := wrappers.WrapCommon(jobs, "tls", "", "http", sched, time.Duration(0))[0] event := &beat.Event{} _, err = job(event) @@ -301,7 +303,8 @@ func TestLargeResponse(t *testing.T) { jobs, _, err := create("largeresp", config) require.NoError(t, err) - job := wrappers.WrapCommon(jobs, "test", "", "http")[0] + sched, _ := schedule.Parse("@every 1s") + job := wrappers.WrapCommon(jobs, "test", "", "http", sched, time.Duration(0))[0] event := &beat.Event{} _, err = job(event) @@ -472,7 +475,8 @@ func TestRedirect(t *testing.T) { jobs, _, err := create("redirect", config) require.NoError(t, err) - job := wrappers.WrapCommon(jobs, "test", "", "http")[0] + sched, _ := schedule.Parse("@every 1s") + job := wrappers.WrapCommon(jobs, "test", "", "http", sched, time.Duration(0))[0] event := &beat.Event{} _, err = job(event) diff --git a/heartbeat/monitors/active/tcp/tcp_test.go b/heartbeat/monitors/active/tcp/tcp_test.go index 83230ef7762..22d42b0173f 100644 --- a/heartbeat/monitors/active/tcp/tcp_test.go +++ b/heartbeat/monitors/active/tcp/tcp_test.go @@ -27,19 +27,19 @@ import ( "os" "strconv" "testing" - - "github.com/elastic/go-lookslike/isdef" + "time" "github.com/stretchr/testify/require" - "github.com/elastic/go-lookslike" - "github.com/elastic/go-lookslike/testslike" - "github.com/elastic/beats/heartbeat/hbtest" "github.com/elastic/beats/heartbeat/monitors/wrappers" + "github.com/elastic/beats/heartbeat/scheduler/schedule" "github.com/elastic/beats/libbeat/beat" "github.com/elastic/beats/libbeat/common" btesting "github.com/elastic/beats/libbeat/testing" + "github.com/elastic/go-lookslike" + "github.com/elastic/go-lookslike/isdef" + "github.com/elastic/go-lookslike/testslike" ) func testTCPCheck(t *testing.T, host string, port uint16) *beat.Event { @@ -58,7 +58,8 @@ func testTCPConfigCheck(t *testing.T, configMap common.MapStr, host string, port jobs, endpoints, err := create("tcp", config) require.NoError(t, err) - job := wrappers.WrapCommon(jobs, "test", "", "tcp")[0] + sched, _ := schedule.Parse("@every 1s") + job := wrappers.WrapCommon(jobs, "test", "", "tcp", sched, time.Duration(0))[0] event := &beat.Event{} _, err = job(event) @@ -81,7 +82,8 @@ func testTLSTCPCheck(t *testing.T, host string, port uint16, certFileName string jobs, endpoints, err := create("tcp", config) require.NoError(t, err) - job := wrappers.WrapCommon(jobs, "test", "", "tcp")[0] + sched, _ := schedule.Parse("@every 1s") + job := wrappers.WrapCommon(jobs, "test", "", "tcp", sched, time.Duration(0))[0] event := &beat.Event{} _, err = job(event) diff --git a/heartbeat/monitors/mocks_test.go b/heartbeat/monitors/mocks_test.go index 4c71775fe3f..6fa1793d4f7 100644 --- a/heartbeat/monitors/mocks_test.go +++ b/heartbeat/monitors/mocks_test.go @@ -23,19 +23,18 @@ import ( "sync" "testing" - "github.com/elastic/go-lookslike/isdef" - "github.com/elastic/go-lookslike/validator" - "github.com/stretchr/testify/require" - "github.com/elastic/go-lookslike" - "github.com/elastic/beats/heartbeat/eventext" "github.com/elastic/beats/heartbeat/hbtest" + "github.com/elastic/beats/heartbeat/hbtestllext" "github.com/elastic/beats/heartbeat/monitors/jobs" "github.com/elastic/beats/libbeat/beat" "github.com/elastic/beats/libbeat/common" "github.com/elastic/beats/libbeat/monitoring" + "github.com/elastic/go-lookslike" + "github.com/elastic/go-lookslike/isdef" + "github.com/elastic/go-lookslike/validator" ) type MockBeatClient struct { @@ -116,6 +115,7 @@ func mockEventMonitorValidator(id string) validator.Validator { "check_group": isdef.IsString, }, }), + hbtestllext.MonitorTimespanValidator, hbtest.SummaryChecks(1, 0), lookslike.MustCompile(mockEventCustomFields()), )) diff --git a/heartbeat/monitors/monitor.go b/heartbeat/monitors/monitor.go index c11fecca2c0..01213f2ad2d 100644 --- a/heartbeat/monitors/monitor.go +++ b/heartbeat/monitors/monitor.go @@ -163,7 +163,7 @@ func newMonitorUnsafe( } rawJobs, endpoints, err := monitorPlugin.create(config) - wrappedJobs := wrappers.WrapCommon(rawJobs, m.id, m.name, m.typ) + wrappedJobs := wrappers.WrapCommon(rawJobs, m.id, m.name, m.typ, mpi.Schedule, mpi.Timeout) m.endpoints = endpoints if err != nil { diff --git a/heartbeat/monitors/pluginconf.go b/heartbeat/monitors/pluginconf.go index 26df3252eed..20aace700fd 100644 --- a/heartbeat/monitors/pluginconf.go +++ b/heartbeat/monitors/pluginconf.go @@ -18,8 +18,11 @@ package monitors import ( + "time" + "github.com/pkg/errors" + "github.com/elastic/beats/heartbeat/scheduler/schedule" "github.com/elastic/beats/libbeat/common" ) @@ -28,10 +31,12 @@ var ErrPluginDisabled = errors.New("Monitor not loaded, plugin is disabled") // MonitorPluginInfo represents the generic configuration options around a monitor plugin. type MonitorPluginInfo struct { - ID string `config:"id"` - Name string `config:"name"` - Type string `config:"type" validate:"required"` - Enabled bool `config:"enabled"` + ID string `config:"id"` + Name string `config:"name"` + Type string `config:"type" validate:"required"` + Schedule *schedule.Schedule `config:"schedule" validate:"required"` + Timeout time.Duration `config:"timeout"` + Enabled bool `config:"enabled"` } func pluginInfo(config *common.Config) (MonitorPluginInfo, error) { diff --git a/heartbeat/monitors/wrappers/monitors.go b/heartbeat/monitors/wrappers/monitors.go index 4de60250582..c565d7481c0 100644 --- a/heartbeat/monitors/wrappers/monitors.go +++ b/heartbeat/monitors/wrappers/monitors.go @@ -22,6 +22,8 @@ import ( "sync" "time" + "github.com/elastic/beats/heartbeat/scheduler/schedule" + "github.com/gofrs/uuid" "github.com/mitchellh/hashstructure" "github.com/pkg/errors" @@ -35,23 +37,24 @@ import ( ) // WrapCommon applies the common wrappers that all monitor jobs get. -func WrapCommon(js []jobs.Job, id string, name string, typ string) []jobs.Job { +func WrapCommon(js []jobs.Job, id string, name string, typ string, sched *schedule.Schedule, timeout time.Duration) []jobs.Job { return jobs.WrapAllSeparately( jobs.WrapAll( js, addMonitorStatus, addMonitorDuration, ), func() jobs.JobWrapper { - return addMonitorMeta(id, name, typ, len(js) > 1) + return addMonitorMeta(id, name, typ, len(js) > 1, sched, timeout) }, func() jobs.JobWrapper { return makeAddSummary() }) } // addMonitorMeta adds the id, name, and type fields to the monitor. -func addMonitorMeta(id string, name string, typ string, isMulti bool) jobs.JobWrapper { +func addMonitorMeta(id string, name string, typ string, isMulti bool, sched *schedule.Schedule, timeout time.Duration) jobs.JobWrapper { return func(job jobs.Job) jobs.Job { return func(event *beat.Event) ([]jobs.Job, error) { + started := time.Now() cont, e := job(event) thisID := id @@ -69,9 +72,10 @@ func addMonitorMeta(id string, name string, typ string, isMulti bool) jobs.JobWr event, common.MapStr{ "monitor": common.MapStr{ - "id": thisID, - "name": name, - "type": typ, + "id": thisID, + "name": name, + "type": typ, + "timespan": timespan(started, sched, timeout), }, }, ) @@ -81,6 +85,19 @@ func addMonitorMeta(id string, name string, typ string, isMulti bool) jobs.JobWr } } +func timespan(started time.Time, sched *schedule.Schedule, timeout time.Duration) common.MapStr { + maxEnd := sched.Next(started) + + if maxEnd.Sub(started) < timeout { + maxEnd = started.Add(timeout) + } + + return common.MapStr{ + "gte": started, + "lt": maxEnd, + } +} + // addMonitorStatus wraps the given Job's execution such that any error returned // by the original Job will be set as a field. The original error will not be // passed through as a return value. Errors may still be present but only if there diff --git a/heartbeat/monitors/wrappers/monitors_test.go b/heartbeat/monitors/wrappers/monitors_test.go index 8f4d558b6fc..51e17890e49 100644 --- a/heartbeat/monitors/wrappers/monitors_test.go +++ b/heartbeat/monitors/wrappers/monitors_test.go @@ -20,22 +20,23 @@ package wrappers import ( "fmt" "net/url" + "reflect" "testing" - - "github.com/elastic/go-lookslike/isdef" - - "github.com/elastic/go-lookslike/validator" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/elastic/go-lookslike" - "github.com/elastic/go-lookslike/testslike" - "github.com/elastic/beats/heartbeat/eventext" + "github.com/elastic/beats/heartbeat/hbtestllext" "github.com/elastic/beats/heartbeat/monitors/jobs" + "github.com/elastic/beats/heartbeat/scheduler/schedule" "github.com/elastic/beats/libbeat/beat" "github.com/elastic/beats/libbeat/common" + "github.com/elastic/go-lookslike" + "github.com/elastic/go-lookslike/isdef" + "github.com/elastic/go-lookslike/testslike" + "github.com/elastic/go-lookslike/validator" ) type fields struct { @@ -54,7 +55,8 @@ type testDef struct { func testCommonWrap(t *testing.T, tt testDef) { t.Run(tt.name, func(t *testing.T) { - wrapped := WrapCommon(tt.jobs, tt.fields.id, tt.fields.name, tt.fields.typ) + schedule, _ := schedule.Parse("@every 1s") + wrapped := WrapCommon(tt.jobs, tt.fields.id, tt.fields.name, tt.fields.typ, schedule, time.Duration(0)) results, err := jobs.ExecJobsAndConts(t, wrapped) assert.NoError(t, err) @@ -93,6 +95,7 @@ func TestSimpleJob(t *testing.T) { "check_group": isdef.IsString, }, }), + hbtestllext.MonitorTimespanValidator, summaryValidator(1, 0), )}, nil, @@ -127,6 +130,7 @@ func TestErrorJob(t *testing.T) { []validator.Validator{ lookslike.Compose( errorJobValidator, + hbtestllext.MonitorTimespanValidator, summaryValidator(0, 1), )}, nil, @@ -151,6 +155,7 @@ func TestMultiJobNoConts(t *testing.T) { "check_group": uniqScope.IsUniqueTo("check_group"), }, }), + hbtestllext.MonitorTimespanValidator, summaryValidator(1, 0), ) } @@ -199,6 +204,7 @@ func TestMultiJobConts(t *testing.T) { "check_group": uniqScope.IsUniqueTo(u), }, }), + hbtestllext.MonitorTimespanValidator, ) } @@ -258,6 +264,7 @@ func TestMultiJobContsCancelledEvents(t *testing.T) { "check_group": uniqScope.IsUniqueTo(u), }, }), + hbtestllext.MonitorTimespanValidator, ) } @@ -316,3 +323,44 @@ func summaryValidator(up int, down int) validator.Validator { }, }) } + +func TestTimespan(t *testing.T) { + now := time.Now() + sched10s, err := schedule.Parse("@every 10s") + require.NoError(t, err) + + type args struct { + started time.Time + sched *schedule.Schedule + timeout time.Duration + } + tests := []struct { + name string + args args + want common.MapStr + }{ + { + "interval longer than timeout", + args{now, sched10s, time.Second}, + common.MapStr{ + "gte": now, + "lt": now.Add(time.Second * 10), + }, + }, + { + "timeout longer than interval", + args{now, sched10s, time.Second * 20}, + common.MapStr{ + "gte": now, + "lt": now.Add(time.Second * 20), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := timespan(tt.args.started, tt.args.sched, tt.args.timeout); !reflect.DeepEqual(got, tt.want) { + t.Errorf("timespan() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/heartbeat/scheduler/schedule/schedule.go b/heartbeat/scheduler/schedule/schedule.go index f7c73129a81..696a6b8fa00 100644 --- a/heartbeat/scheduler/schedule/schedule.go +++ b/heartbeat/scheduler/schedule/schedule.go @@ -29,6 +29,7 @@ type Schedule struct { scheduler.Schedule } +// intervalScheduler defines a schedule that runs at fixed intervals. type intervalScheduler struct { interval time.Duration } diff --git a/libbeat/mapping/field.go b/libbeat/mapping/field.go index dd226201a76..66cde47e834 100644 --- a/libbeat/mapping/field.go +++ b/libbeat/mapping/field.go @@ -132,6 +132,8 @@ func (f *Field) validateType() error { return dateType.validate(f.Format) case "geo_point": return geoPointType.validate(f.Format) + case "date_range": + return dateRangeType.validate(f.Format) case "boolean", "binary", "ip", "alias", "array": if f.Format != "" { return fmt.Errorf("no format expected for field %s, found: %s", f.Name, f.Format) @@ -158,10 +160,11 @@ type fieldTypeGroup struct { } var ( - stringType = fieldTypeGroup{"string", []string{"string", "url"}} - numberType = fieldTypeGroup{"number", []string{"string", "url", "bytes", "duration", "number", "percent", "color"}} - dateType = fieldTypeGroup{"date", []string{"string", "url", "date"}} - geoPointType = fieldTypeGroup{"geo_point", []string{"geo_point"}} + stringType = fieldTypeGroup{"string", []string{"string", "url"}} + numberType = fieldTypeGroup{"number", []string{"string", "url", "bytes", "duration", "number", "percent", "color"}} + dateType = fieldTypeGroup{"date", []string{"string", "url", "date"}} + geoPointType = fieldTypeGroup{"geo_point", []string{"geo_point"}} + dateRangeType = fieldTypeGroup{"date_range", []string{"date_range"}} ) func (g *fieldTypeGroup) validate(formatter string) error { diff --git a/libbeat/tests/system/beat/beat.py b/libbeat/tests/system/beat/beat.py index d26ac75a071..d2f7f5b83fe 100644 --- a/libbeat/tests/system/beat/beat.py +++ b/libbeat/tests/system/beat/beat.py @@ -683,7 +683,9 @@ def is_documented(key, docs): for key in flat.keys(): metaKey = key.startswith('@metadata.') - if not(is_documented(key, expected_fields) or metaKey): + # Range keys as used in 'date_range' etc will not have docs of course + isRangeKey = key.split('.')[-1] in ['gte', 'gt', 'lte', 'lt'] + if not(is_documented(key, expected_fields) or metaKey or isRangeKey): raise Exception("Key '{}' found in event is not documented!".format(key)) if is_documented(key, aliases): raise Exception("Key '{}' found in event is documented as an alias!".format(key))