wavefront-cli 8.2.0 → 8.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/HISTORY.md +21 -1
- data/lib/wavefront-cli/base.rb +3 -0
- data/lib/wavefront-cli/commands/event.rb +8 -6
- data/lib/wavefront-cli/commands/proxy.rb +1 -0
- data/lib/wavefront-cli/commands/serviceaccount.rb +6 -4
- data/lib/wavefront-cli/controller.rb +9 -0
- data/lib/wavefront-cli/display/proxy.rb +4 -0
- data/lib/wavefront-cli/display/serviceaccount.rb +12 -4
- data/lib/wavefront-cli/event.rb +50 -166
- data/lib/wavefront-cli/event_store.rb +177 -0
- data/lib/wavefront-cli/proxy.rb +4 -0
- data/lib/wavefront-cli/serviceaccount.rb +16 -6
- data/lib/wavefront-cli/version.rb +1 -1
- data/spec/spec_helper.rb +0 -1
- data/spec/wavefront-cli/controller_spec.rb +12 -0
- data/spec/wavefront-cli/event_spec.rb +69 -109
- data/spec/wavefront-cli/event_store_spec.rb +186 -0
- data/spec/wavefront-cli/proxy_spec.rb +13 -0
- data/spec/wavefront-cli/serviceaccount_spec.rb +53 -19
- data/wavefront-cli.gemspec +1 -1
- metadata +13 -4
@@ -0,0 +1,177 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'etc'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'open3'
|
6
|
+
require 'json'
|
7
|
+
require_relative 'constants'
|
8
|
+
require_relative 'exception'
|
9
|
+
|
10
|
+
module WavefrontCli
|
11
|
+
#
|
12
|
+
# Encapsulation of everything needed to manage the locally stored state of
|
13
|
+
# events opened by the CLI. This is our own addition, entirely separate from
|
14
|
+
# Wavefront's API.
|
15
|
+
#
|
16
|
+
# When the user creates an open-ended event (i.e. one that does not have and
|
17
|
+
# end time, and is not instantaneous) a state file is created in a local
|
18
|
+
# directory. (*)
|
19
|
+
#
|
20
|
+
# That directory is defined by the EVENT_STATE_DIR constant, but may be
|
21
|
+
# overriden with an option in the constructor. The tests do this.
|
22
|
+
#
|
23
|
+
# (*) The user may specifically request that no state file be created with
|
24
|
+
# the --nostate flag.
|
25
|
+
#
|
26
|
+
class EventStore
|
27
|
+
include WavefrontCli::Constants
|
28
|
+
|
29
|
+
attr_reader :dir, :options
|
30
|
+
|
31
|
+
# @param state_dir [Pathname] override the default dir for testing
|
32
|
+
#
|
33
|
+
def initialize(options, state_dir = nil)
|
34
|
+
@options = options
|
35
|
+
@dir = event_state_dir(state_dir) + (Etc.getlogin || 'notty')
|
36
|
+
create_dir(dir)
|
37
|
+
end
|
38
|
+
|
39
|
+
def state_file_needed?
|
40
|
+
!(options[:nostate] || options[:end] || options[:instant])
|
41
|
+
end
|
42
|
+
|
43
|
+
def event_file(id)
|
44
|
+
id =~ /^\d{13}:.+/ ? dir + id : nil
|
45
|
+
end
|
46
|
+
|
47
|
+
# We can override the temp directory with the WF_EVENT_STATE_DIR env var.
|
48
|
+
# This is primarily for testing, though someone may find a valid use for
|
49
|
+
# it.
|
50
|
+
#
|
51
|
+
def event_state_dir(state_dir = nil)
|
52
|
+
if ENV['WF_EVENT_STATE_DIR']
|
53
|
+
Pathname.new(ENV['WF_EVENT_STATE_DIR'])
|
54
|
+
elsif state_dir.nil?
|
55
|
+
EVENT_STATE_DIR
|
56
|
+
else
|
57
|
+
Pathname.new(state_dir)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# @param id [String,Nil] if this is falsey, returns the event on the top
|
62
|
+
# of the state stack, removing its state file. If it's an exact event
|
63
|
+
# ID, simply pass that ID back, NOT removing the state file. This is
|
64
|
+
# okay: the state file is cleaned up by WavefrontCli::Event when an
|
65
|
+
# event is closed. If it's a name but not an ID, return the ID of the
|
66
|
+
# most recent event with the given name.
|
67
|
+
# @return [String] the name of the most recent suitable event from the
|
68
|
+
# local stack directory.
|
69
|
+
#
|
70
|
+
def event(id)
|
71
|
+
if !id
|
72
|
+
pop_event!
|
73
|
+
elsif id =~ /^\d{13}:.+:\d+/
|
74
|
+
id
|
75
|
+
else
|
76
|
+
pop_event!(id)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# List events on the local stack
|
81
|
+
#
|
82
|
+
def list
|
83
|
+
events = dir.children
|
84
|
+
abort 'No locally recorded events.' if events.empty?
|
85
|
+
|
86
|
+
events
|
87
|
+
rescue Errno::ENOENT
|
88
|
+
raise(WavefrontCli::Exception::SystemError,
|
89
|
+
'There is no event state directory on this host.')
|
90
|
+
end
|
91
|
+
|
92
|
+
# Run a command, stream stderr and stdout to the screen (they
|
93
|
+
# get combined -- could be an issue for someone somewhere) and
|
94
|
+
# return the command's exit code
|
95
|
+
#
|
96
|
+
def run_wrapped_cmd(cmd)
|
97
|
+
separator = '-' * (TW - 4)
|
98
|
+
|
99
|
+
puts "Command output follows, on STDERR:\n#{separator}"
|
100
|
+
ret = nil
|
101
|
+
|
102
|
+
Open3.popen2e(cmd) do |_in, out, thr|
|
103
|
+
# rubocop:disable Lint/AssignmentInCondition
|
104
|
+
while l = out.gets do warn l end
|
105
|
+
# rubocop:enable Lint/AssignmentInCondition
|
106
|
+
ret = thr.value.exitstatus
|
107
|
+
end
|
108
|
+
|
109
|
+
puts separator
|
110
|
+
ret
|
111
|
+
end
|
112
|
+
|
113
|
+
# Write a state file. We put the hosts bound to the event into the file.
|
114
|
+
# These aren't currently used by anything in the CLI, but they might be
|
115
|
+
# useful to someone, somewhere, someday.
|
116
|
+
# @return [Nil]
|
117
|
+
#
|
118
|
+
def create!(id)
|
119
|
+
return unless state_file_needed?
|
120
|
+
|
121
|
+
fname = dir + id
|
122
|
+
File.open(fname, 'w') { |fh| fh.puts(event_file_data) }
|
123
|
+
puts "Event state recorded at #{fname}."
|
124
|
+
rescue StandardError
|
125
|
+
puts 'NOTICE: event was created but state file was not.'
|
126
|
+
end
|
127
|
+
|
128
|
+
# Record event data in the state file. We don't currently use it, but it
|
129
|
+
# might be useful to someone someday.
|
130
|
+
# @return [String]
|
131
|
+
#
|
132
|
+
def event_file_data
|
133
|
+
{ hosts: options[:host],
|
134
|
+
description: options[:desc],
|
135
|
+
severity: options[:severity],
|
136
|
+
tags: options[:evtag] }.to_json
|
137
|
+
end
|
138
|
+
|
139
|
+
def create_dir(state_dir)
|
140
|
+
FileUtils.mkdir_p(state_dir)
|
141
|
+
raise unless state_dir.exist? &&
|
142
|
+
state_dir.directory? &&
|
143
|
+
state_dir.writable?
|
144
|
+
rescue StandardError
|
145
|
+
raise(WavefrontCli::Exception::SystemError,
|
146
|
+
"Cannot create writable system directory at '#{state_dir}'.")
|
147
|
+
end
|
148
|
+
|
149
|
+
# Get the last event this script created. If you supply a name, you get
|
150
|
+
# the last event with that name. If not, you get the last event. Note the
|
151
|
+
# '!': this method (potentially) has side effects.
|
152
|
+
# @param name [String] name of event. This is the middle part of the real
|
153
|
+
# event name: the only part supplied by the user.
|
154
|
+
# @return [Array[timestamp, event_name]]
|
155
|
+
#
|
156
|
+
def pop_event!(name = nil)
|
157
|
+
return false unless dir.exist?
|
158
|
+
|
159
|
+
list = local_events_with_name(name)
|
160
|
+
return false if list.empty?
|
161
|
+
|
162
|
+
ev_file = list.max
|
163
|
+
File.unlink(ev_file)
|
164
|
+
ev_file.basename.to_s
|
165
|
+
end
|
166
|
+
|
167
|
+
# Event names are of the form `1609860826095:name:0`
|
168
|
+
# @param name [String] the user-specified (middle) portion of an event ID
|
169
|
+
# @return [Array[String]] list of matching events
|
170
|
+
#
|
171
|
+
def local_events_with_name(name = nil)
|
172
|
+
return list unless name
|
173
|
+
|
174
|
+
list.select { |f| f.basename.to_s.split(':')[1] == name }
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
data/lib/wavefront-cli/proxy.rb
CHANGED
@@ -32,6 +32,10 @@ module WavefrontCli
|
|
32
32
|
version_info(raw).sort_by { |p| Gem::Version.new(p[:version]) }.reverse
|
33
33
|
end
|
34
34
|
|
35
|
+
def do_shutdown
|
36
|
+
wf.shutdown(options[:'<id>'])
|
37
|
+
end
|
38
|
+
|
35
39
|
def version_info(raw)
|
36
40
|
raw.response.items.map do |i|
|
37
41
|
{ id: i.id, version: i.version, name: i.name }
|
@@ -23,7 +23,8 @@ module WavefrontCli
|
|
23
23
|
end
|
24
24
|
|
25
25
|
alias do_groups do_describe
|
26
|
-
alias
|
26
|
+
alias do_ingestionpolicy do_describe
|
27
|
+
alias do_roles do_describe
|
27
28
|
|
28
29
|
def do_create
|
29
30
|
wf_user_id?(options[:'<id>'])
|
@@ -95,7 +96,7 @@ module WavefrontCli
|
|
95
96
|
def extra_validation
|
96
97
|
validate_groups
|
97
98
|
validate_tokens
|
98
|
-
|
99
|
+
validate_ingestion_policy
|
99
100
|
end
|
100
101
|
|
101
102
|
def validator_exception
|
@@ -157,15 +158,18 @@ module WavefrontCli
|
|
157
158
|
!options[:inactive]
|
158
159
|
end
|
159
160
|
|
161
|
+
# rubocop:disable Metrics/AbcSize
|
160
162
|
def user_body
|
161
163
|
{ identifier: options[:'<id>'],
|
162
164
|
active: active_account?,
|
163
|
-
|
165
|
+
ingestionPolicyId: options[:policy],
|
164
166
|
tokens: options[:usertoken],
|
165
|
-
|
167
|
+
roles: options[:role],
|
168
|
+
userGroups: options[:group] }.compact.tap do |b|
|
166
169
|
b[:description] = options[:desc] if options[:desc]
|
167
170
|
end
|
168
171
|
end
|
172
|
+
# rubocop:enable Metrics/AbcSize
|
169
173
|
|
170
174
|
def item_dump_call
|
171
175
|
wf.list.response
|
@@ -175,12 +179,18 @@ module WavefrontCli
|
|
175
179
|
options[:group].each { |g| wf_usergroup_id?(g) }
|
176
180
|
end
|
177
181
|
|
182
|
+
def validate_roles
|
183
|
+
options[:role].each { |r| wf_role_id?(r) }
|
184
|
+
end
|
185
|
+
|
178
186
|
def validate_tokens
|
179
187
|
options[:usertoken].each { |t| wf_apitoken_id?(t) }
|
180
188
|
end
|
181
189
|
|
182
|
-
def
|
183
|
-
options[:
|
190
|
+
def validate_ingestion_policy
|
191
|
+
return true unless options[:policy]
|
192
|
+
|
193
|
+
wf_ingestionpolicy_id?(options[:policy])
|
184
194
|
end
|
185
195
|
|
186
196
|
def descriptive_name
|
data/spec/spec_helper.rb
CHANGED
@@ -22,6 +22,18 @@ class WavefrontCliHelpTest < MiniTest::Test
|
|
22
22
|
assert_match(/^ \w+ --help$/, e.message)
|
23
23
|
end
|
24
24
|
|
25
|
+
def test_commands_no_args
|
26
|
+
SupportedCommands.new.all.each do |cmd|
|
27
|
+
_test_command_no_args(cmd)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def _test_command_no_args(cmd)
|
32
|
+
capture_io { WavefrontCliController.new([cmd]) }
|
33
|
+
rescue SystemExit => e
|
34
|
+
assert e.message.end_with?("wf #{cmd} --help")
|
35
|
+
end
|
36
|
+
|
25
37
|
def test_version
|
26
38
|
capture_io { WavefrontCliController.new(%w[--version]) }
|
27
39
|
rescue SystemExit => e
|
@@ -3,6 +3,7 @@
|
|
3
3
|
|
4
4
|
require 'tmpdir'
|
5
5
|
require_relative '../support/command_base'
|
6
|
+
require_relative '../test_mixins/tag'
|
6
7
|
require_relative '../../lib/wavefront-cli/event'
|
7
8
|
require 'wavefront-sdk/support/mixins'
|
8
9
|
|
@@ -15,12 +16,11 @@ class EventEndToEndTest < EndToEndTest
|
|
15
16
|
attr_reader :test_state_dir
|
16
17
|
|
17
18
|
include Wavefront::Mixins
|
18
|
-
include WavefrontCliTest::Describe
|
19
|
-
include WavefrontCliTest::Delete
|
20
|
-
# Ones above work, ones below don't
|
19
|
+
# include WavefrontCliTest::Describe
|
20
|
+
# include WavefrontCliTest::Delete
|
21
21
|
# include WavefrontCliTest::Search
|
22
|
-
# include WavefrontCliTest::Set
|
23
|
-
# include WavefrontCliTest::
|
22
|
+
# #include WavefrontCliTest::Set
|
23
|
+
# include WavefrontCliTest::Tag
|
24
24
|
|
25
25
|
def before_setup
|
26
26
|
@test_state_dir = Pathname.new(Dir.mktmpdir)
|
@@ -31,11 +31,6 @@ class EventEndToEndTest < EndToEndTest
|
|
31
31
|
FileUtils.rm_r(test_state_dir)
|
32
32
|
end
|
33
33
|
|
34
|
-
def cmd_instance
|
35
|
-
cmd_class.new(event_state_dir: TEST_EVENT_DIR)
|
36
|
-
puts cmd_class
|
37
|
-
end
|
38
|
-
|
39
34
|
def test_list_no_options
|
40
35
|
str = '/api/v2/event\?' \
|
41
36
|
'earliestStartTimeEpochMillis=\d{13}+&' \
|
@@ -65,6 +60,22 @@ class EventEndToEndTest < EndToEndTest
|
|
65
60
|
end
|
66
61
|
end
|
67
62
|
|
63
|
+
def test_show_with_no_local_events
|
64
|
+
assert_exits_with('No locally recorded events.', 'show')
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_show
|
68
|
+
setup_test_state_dir
|
69
|
+
|
70
|
+
out, err = capture_io do
|
71
|
+
assert_raises(SystemExit) { wf.new("event show -c #{CF}".split) }
|
72
|
+
end
|
73
|
+
|
74
|
+
assert_empty(err)
|
75
|
+
assert_equal("1568133440530:ev3:0\n1568133440520:ev2:0\n" \
|
76
|
+
"1568133440515:ev1:1\n1568133440510:ev1:0\n", out)
|
77
|
+
end
|
78
|
+
|
68
79
|
def test_create
|
69
80
|
mock_id = "#{start_time}:#{event_name}:1"
|
70
81
|
state_file = state_dir + mock_id
|
@@ -100,8 +111,8 @@ class EventEndToEndTest < EndToEndTest
|
|
100
111
|
refute state_file.exist?
|
101
112
|
|
102
113
|
out, err = capture_io do
|
103
|
-
assert_cmd_posts('create -d reason -H host1 -H host2 -g ' \
|
104
|
-
"
|
114
|
+
assert_cmd_posts('create -d reason -H host1 -H host2 -g mytag ' \
|
115
|
+
"#{event_name}",
|
105
116
|
'/api/v2/event',
|
106
117
|
{ name: event_name,
|
107
118
|
startTime: a_ms_timestamp,
|
@@ -148,15 +159,6 @@ class EventEndToEndTest < EndToEndTest
|
|
148
159
|
assert_match(/\ntags tag1\n tag2\n/, out)
|
149
160
|
end
|
150
161
|
|
151
|
-
def test_close_named_event
|
152
|
-
quietly do
|
153
|
-
assert_cmd_posts('close 1568133440520:ev2:0',
|
154
|
-
'/api/v2/event/1568133440520:ev2:0/close')
|
155
|
-
end
|
156
|
-
|
157
|
-
assert_abort_on_missing_creds("close #{id}")
|
158
|
-
end
|
159
|
-
|
160
162
|
def test_close_with_no_local_events
|
161
163
|
quietly { assert_cmd_posts("close #{id}", "/api/v2/event/#{id}/close") }
|
162
164
|
assert_exits_with('No locally recorded events.', 'close')
|
@@ -212,26 +214,47 @@ class EventEndToEndTest < EndToEndTest
|
|
212
214
|
end
|
213
215
|
end
|
214
216
|
|
215
|
-
def
|
216
|
-
|
217
|
+
def test_window_start
|
218
|
+
wfse = WavefrontCli::Event.new(start: wall_time[0], end: wall_time[1])
|
219
|
+
assert_kind_of(Numeric, wfse.window_start)
|
220
|
+
assert_equal(epoch_ms_time[0], wfse.window_start)
|
221
|
+
end
|
217
222
|
|
218
|
-
|
219
|
-
|
220
|
-
|
223
|
+
def test_window_end
|
224
|
+
wfse = WavefrontCli::Event.new(start: wall_time[0], end: wall_time[1])
|
225
|
+
assert_kind_of(Numeric, wfse.window_end)
|
226
|
+
assert_equal(epoch_ms_time[1], wfse.window_end)
|
227
|
+
end
|
221
228
|
|
222
|
-
|
223
|
-
|
224
|
-
|
229
|
+
def test_list_args_defaults
|
230
|
+
wfe = WavefrontCli::Event.new({})
|
231
|
+
x = wfe.list_args
|
232
|
+
assert_instance_of(Array, x)
|
233
|
+
assert_equal(4, x.size)
|
234
|
+
assert_in_delta(((Time.now - 600).to_i * 1000), x[0], 1000)
|
235
|
+
assert_in_delta((Time.now.to_i * 1000), x[1], 1000)
|
236
|
+
assert_equal(100, x[2])
|
237
|
+
assert_nil(x[3])
|
238
|
+
end
|
239
|
+
|
240
|
+
def test_list_args_options
|
241
|
+
wfse = WavefrontCli::Event.new(limit: 55,
|
242
|
+
start: wall_time[0],
|
243
|
+
cursor: id,
|
244
|
+
end: wall_time[1])
|
245
|
+
x = wfse.list_args
|
246
|
+
assert_instance_of(Array, x)
|
247
|
+
assert_equal(4, x.size)
|
248
|
+
assert_equal(epoch_ms_time[0], x[0])
|
249
|
+
assert_equal(epoch_ms_time[1], x[1])
|
250
|
+
assert_equal(55, x[2])
|
251
|
+
assert_equal(id, x[3])
|
225
252
|
end
|
226
253
|
|
227
254
|
private
|
228
255
|
|
229
256
|
def id
|
230
|
-
'1481553823153:testev'
|
231
|
-
end
|
232
|
-
|
233
|
-
def event_name
|
234
|
-
'test_event'
|
257
|
+
'1481553823153:testev:0'
|
235
258
|
end
|
236
259
|
|
237
260
|
def invalid_id
|
@@ -242,18 +265,24 @@ class EventEndToEndTest < EndToEndTest
|
|
242
265
|
'event'
|
243
266
|
end
|
244
267
|
|
268
|
+
def event_name
|
269
|
+
'test_event'
|
270
|
+
end
|
271
|
+
|
245
272
|
def start_time
|
246
273
|
1_481_553_823_153
|
247
274
|
end
|
248
275
|
|
249
|
-
def
|
250
|
-
|
276
|
+
def epoch_ms_time
|
277
|
+
wall_time.map { |t| (t.to_i * 1000) }
|
251
278
|
end
|
252
279
|
|
253
280
|
def state_dir
|
254
281
|
test_state_dir + (Etc.getlogin || 'notty')
|
255
282
|
end
|
256
283
|
|
284
|
+
# Puts some test events in the state directory
|
285
|
+
#
|
257
286
|
def setup_test_state_dir
|
258
287
|
FileUtils.mkdir_p(state_dir)
|
259
288
|
|
@@ -285,79 +314,10 @@ class EventEndToEndTest < EndToEndTest
|
|
285
314
|
"https://#{perm[:endpoint]}/api/v2/event/#{mock_id}/close")
|
286
315
|
.with(body: 'null')
|
287
316
|
end
|
288
|
-
end
|
289
|
-
|
290
|
-
# Unit tests for class methods
|
291
|
-
#
|
292
|
-
class EventMethodTests < Minitest::Test
|
293
|
-
attr_reader :wf, :wfse
|
294
|
-
|
295
|
-
def setup
|
296
|
-
@wf = WavefrontCli::Event.new({})
|
297
|
-
@wfse = WavefrontCli::Event.new(start: wall_time[0],
|
298
|
-
end: wall_time[1],
|
299
|
-
limit: 55,
|
300
|
-
cursor: '1481553823153:testev')
|
301
|
-
end
|
302
|
-
|
303
|
-
def test_create_dir_ok
|
304
|
-
base = Pathname.new(Dir.mktmpdir)
|
305
|
-
dir = base + 'testdir'
|
306
|
-
refute dir.exist?
|
307
|
-
wf.create_dir(dir)
|
308
|
-
assert dir.exist?
|
309
|
-
dir.unlink
|
310
|
-
base.unlink
|
311
|
-
end
|
312
|
-
|
313
|
-
def test_create_dir_fail
|
314
|
-
spy = Spy.on(FileUtils, :mkdir_p).and_return(false)
|
315
|
-
|
316
|
-
assert_raises(WavefrontCli::Exception::SystemError) do
|
317
|
-
wf.create_dir(Pathname.new('/any/old/directory'))
|
318
|
-
end
|
319
|
-
|
320
|
-
assert spy.has_been_called?
|
321
|
-
spy.unhook
|
322
|
-
end
|
323
|
-
|
324
|
-
def test_list_args_defaults
|
325
|
-
x = wf.list_args
|
326
|
-
assert_instance_of(Array, x)
|
327
|
-
assert_equal(4, x.size)
|
328
|
-
assert_in_delta(((Time.now - 600).to_i * 1000), x[0], 1000)
|
329
|
-
assert_in_delta((Time.now.to_i * 1000), x[1], 1000)
|
330
|
-
assert_equal(100, x[2])
|
331
|
-
assert_nil(x[3])
|
332
|
-
end
|
333
|
-
|
334
|
-
def test_list_args_options
|
335
|
-
x = wfse.list_args
|
336
|
-
assert_instance_of(Array, x)
|
337
|
-
assert_equal(4, x.size)
|
338
|
-
assert_equal(epoch_ms_time[0], x[0])
|
339
|
-
assert_equal(epoch_ms_time[1], x[1])
|
340
|
-
assert_equal(55, x[2])
|
341
|
-
assert_equal('1481553823153:testev', x[3])
|
342
|
-
end
|
343
|
-
|
344
|
-
def test_window_start
|
345
|
-
assert_kind_of(Numeric, wf.window_start)
|
346
|
-
assert_equal(epoch_ms_time[0], wfse.window_start)
|
347
|
-
end
|
348
|
-
|
349
|
-
def test_window_end
|
350
|
-
assert_kind_of(Numeric, wf.window_end)
|
351
|
-
assert_equal(epoch_ms_time[1], wfse.window_end)
|
352
|
-
end
|
353
317
|
|
354
|
-
|
355
|
-
|
356
|
-
def
|
357
|
-
|
358
|
-
end
|
359
|
-
|
360
|
-
def epoch_ms_time
|
361
|
-
wall_time.map { |t| (t.to_i * 1000) }
|
318
|
+
# Event searching uses a cursor, not an offset
|
319
|
+
#
|
320
|
+
def cannot_handle_offsets
|
321
|
+
true
|
362
322
|
end
|
363
323
|
end
|