splunk-sdk-ruby 0.1.0 → 0.8.1
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 +15 -0
- data/CHANGELOG.md +28 -0
- data/README.md +5 -17
- data/examples/6_work_with_modular_inputs.rb +96 -0
- data/examples/example_modular_inputs.spl +0 -0
- data/lib/splunk-sdk-ruby/atomfeed.rb +1 -1
- data/lib/splunk-sdk-ruby/collection.rb +112 -109
- data/lib/splunk-sdk-ruby/collection/input_kinds.rb +136 -0
- data/lib/splunk-sdk-ruby/collection/jobs.rb +9 -1
- data/lib/splunk-sdk-ruby/context.rb +7 -4
- data/lib/splunk-sdk-ruby/entity.rb +37 -29
- data/lib/splunk-sdk-ruby/entity/job.rb +12 -4
- data/lib/splunk-sdk-ruby/entity/modular_input_kind.rb +47 -0
- data/lib/splunk-sdk-ruby/resultsreader.rb +71 -17
- data/lib/splunk-sdk-ruby/service.rb +37 -5
- data/lib/splunk-sdk-ruby/version.rb +1 -1
- data/lib/splunk-sdk-ruby/xml_shim.rb +11 -0
- data/splunk-sdk-ruby.gemspec +3 -2
- data/test/data/atom_test_data.json +457 -0
- data/test/{export_test_data.json → data/export_test_data.json} +302 -182
- data/test/data/resultsreader_test_data.json +1693 -0
- data/test/test_atomfeed.rb +20 -9
- data/test/test_configuration_file.rb +22 -0
- data/test/test_context.rb +1 -1
- data/test/test_helper.rb +27 -15
- data/test/test_index.rb +2 -3
- data/test/test_inputs.rb +211 -0
- data/test/test_jobs.rb +73 -2
- data/test/test_modular_input_kinds.rb +46 -0
- data/test/test_restarts.rb +10 -0
- data/test/test_resultsreader.rb +22 -7
- data/test/test_users.rb +8 -0
- data/test/test_xml_shim.rb +10 -0
- metadata +104 -101
- data/test/atom_test_data.rb +0 -472
- data/test/resultsreader_test_data.json +0 -1119
data/test/test_atomfeed.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require_relative "test_helper"
|
2
2
|
require "splunk-sdk-ruby"
|
3
3
|
|
4
|
+
require 'json'
|
5
|
+
|
4
6
|
include Splunk
|
5
7
|
|
6
8
|
# URI's classes compare by object identity, which is exactly what we
|
@@ -37,33 +39,42 @@ class TestAtomFeed < Test::Unit::TestCase
|
|
37
39
|
puts "Nokogiri not installed. Skipping."
|
38
40
|
end
|
39
41
|
|
40
|
-
test_cases =
|
42
|
+
test_cases = JSON::parse(open("test/data/atom_test_data.json").read())
|
41
43
|
|
42
44
|
xml_libraries.each do |xml_library|
|
43
45
|
test_cases.each_entry do |filename, expected|
|
44
46
|
define_method("test_#{xml_library}_#{filename}".intern()) do
|
45
47
|
file = File.open("test/data/atom/#{filename}.xml")
|
48
|
+
Splunk::require_xml_library(xml_library)
|
46
49
|
feed = Splunk::AtomFeed.new(file)
|
47
50
|
|
51
|
+
# In the assert statements below, the output of the code is first,
|
52
|
+
# the expected data second, which is breaking convention, but has
|
53
|
+
# to be this way since we need to match URLs which are URI objects
|
54
|
+
# in the output of the code to URLs which are strings in the expected
|
55
|
+
# data from the JSON file. URI has been patched to make this
|
56
|
+
# work...but only if the == method is called on the URI object,
|
57
|
+
# not the string. == is not commutative.
|
58
|
+
|
48
59
|
# To make debugging easy, test the metadata a key at
|
49
60
|
# a time, since Test::Unit doesn't display diffs.
|
50
61
|
# Then test the whole thing at the end to make sure it all matches.
|
51
|
-
expected[
|
52
|
-
assert_equal([filename, key,
|
53
|
-
[filename, key,
|
62
|
+
expected["metadata"].each_entry do |key, value|
|
63
|
+
assert_equal([filename, key, feed.metadata[key]],
|
64
|
+
[filename, key, value])
|
54
65
|
end
|
55
|
-
assert_equal(expected[
|
66
|
+
assert_equal(feed.metadata, expected["metadata"])
|
56
67
|
|
57
68
|
# To make debugging easy, test each key of each entry
|
58
69
|
# separately, since Test::Unit doesn't display diffs.
|
59
70
|
# Then test the whole thing at the end to make sure it all matches.
|
60
|
-
expected[
|
71
|
+
expected["entries"].each_with_index do |entry, index|
|
61
72
|
entry.each_entry do |key, value|
|
62
|
-
assert_equal([filename, index, key,
|
63
|
-
[filename, index, key,
|
73
|
+
assert_equal([filename, index, key, feed.entries[index][key]],
|
74
|
+
[filename, index, key, value])
|
64
75
|
end
|
65
76
|
end
|
66
|
-
assert_equal(expected[
|
77
|
+
assert_equal(feed.entries, expected["entries"])
|
67
78
|
end
|
68
79
|
end
|
69
80
|
end
|
@@ -68,6 +68,28 @@ class ConfigurationFileTestCase < TestCaseWithSplunkConnection
|
|
68
68
|
assert_equal(created_conf.name, bracket_fetched_conf.name)
|
69
69
|
end
|
70
70
|
|
71
|
+
##
|
72
|
+
# Check that fetching nonexistent configuration files returns nil.
|
73
|
+
#
|
74
|
+
def test_fetch_nonexistent
|
75
|
+
nonexistent_name = temporary_name()
|
76
|
+
|
77
|
+
assert_nil(@confs[nonexistent_name])
|
78
|
+
assert_nil(@confs.fetch(nonexistent_name))
|
79
|
+
end
|
80
|
+
|
81
|
+
##
|
82
|
+
# Check that server errors during fetch are encapsulated sensibly.
|
83
|
+
#
|
84
|
+
def test_error_from_fetch
|
85
|
+
new_service = Splunk::Service.new(@splunkrc)
|
86
|
+
new_confs = new_service.confs()
|
87
|
+
new_service.logout()
|
88
|
+
assert_raise(SplunkHTTPError) do
|
89
|
+
new_confs[temporary_name()]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
71
93
|
def test_each_and_values
|
72
94
|
each_names = []
|
73
95
|
@confs.each() { |entity| each_names << entity.name }
|
data/test/test_context.rb
CHANGED
@@ -97,7 +97,7 @@ class TestContext < TestCaseWithSplunkConnection
|
|
97
97
|
|
98
98
|
def test_server_accepting_connections?
|
99
99
|
values = @splunkrc.clone()
|
100
|
-
values[:port] =
|
100
|
+
values[:port] = 8000
|
101
101
|
service = Context.new(values)
|
102
102
|
assert_false(service.server_accepting_connections?)
|
103
103
|
|
data/test/test_helper.rb
CHANGED
@@ -79,14 +79,24 @@ class TestCaseWithSplunkConnection < Test::Unit::TestCase
|
|
79
79
|
fail("Test left server in a state requiring restart.")
|
80
80
|
end
|
81
81
|
|
82
|
+
# Are we on Windows or Unix? We need this below.
|
83
|
+
splunk_home = @service.settings["SPLUNK_HOME"]
|
84
|
+
is_windows = splunk_home.include?("\\") == true
|
85
|
+
|
82
86
|
if @service.splunk_version[0..1] != [4,2]
|
83
87
|
@installed_apps.each() do |app_name|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
if
|
89
|
-
|
88
|
+
# There is a bug in Python on Windows which results in the
|
89
|
+
# sleep_command not deleting properly because there are still
|
90
|
+
# hung jobs that haven't terminated when we reach this point.
|
91
|
+
# We skip deleting the sleep_command app on Windows.
|
92
|
+
if app_name != 'sleep_command' or !is_windows
|
93
|
+
@service.apps.delete(app_name)
|
94
|
+
assert_eventually_true() do
|
95
|
+
!@service.apps.has_key?(app_name)
|
96
|
+
end
|
97
|
+
if @service.server_requires_restart?
|
98
|
+
clear_restart_message(@service)
|
99
|
+
end
|
90
100
|
end
|
91
101
|
end
|
92
102
|
end
|
@@ -113,13 +123,15 @@ class TestCaseWithSplunkConnection < Test::Unit::TestCase
|
|
113
123
|
end
|
114
124
|
|
115
125
|
def assert_not_logged_in(service)
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
126
|
+
if service.server_accepting_connections?
|
127
|
+
begin
|
128
|
+
service.request(:method=>:GET,
|
129
|
+
:resource=>["data", "indexes"])
|
130
|
+
rescue SplunkHTTPError => err
|
131
|
+
assert_equal(401, err.code, "Expected HTTP status code 401, found: #{err.code}")
|
132
|
+
else
|
133
|
+
fail("Context is logged in.")
|
134
|
+
end
|
123
135
|
end
|
124
136
|
end
|
125
137
|
|
@@ -143,7 +155,7 @@ class TestCaseWithSplunkConnection < Test::Unit::TestCase
|
|
143
155
|
end
|
144
156
|
end
|
145
157
|
|
146
|
-
def
|
158
|
+
def has_test_data?(service)
|
147
159
|
collection_name = 'sdk-app-collection'
|
148
160
|
return service.apps.has_key?(collection_name)
|
149
161
|
end
|
@@ -247,4 +259,4 @@ class TestCaseWithSplunkConnection < Test::Unit::TestCase
|
|
247
259
|
def unchecked_restart(service)
|
248
260
|
service.restart(DEFAULT_RESTART_TIMEOUT)
|
249
261
|
end
|
250
|
-
end
|
262
|
+
end
|
data/test/test_index.rb
CHANGED
@@ -74,9 +74,8 @@ class IndexTestCase < TestCaseWithSplunkConnection
|
|
74
74
|
end
|
75
75
|
|
76
76
|
def test_upload
|
77
|
-
if
|
78
|
-
|
79
|
-
return
|
77
|
+
if not has_test_data?(@service)
|
78
|
+
fail("Install the SDK test data to test uploads.")
|
80
79
|
end
|
81
80
|
|
82
81
|
install_app_from_collection("file_to_upload")
|
data/test/test_inputs.rb
ADDED
@@ -0,0 +1,211 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
require 'splunk-sdk-ruby'
|
3
|
+
|
4
|
+
include Splunk
|
5
|
+
|
6
|
+
class InputsTest < TestCaseWithSplunkConnection
|
7
|
+
def setup
|
8
|
+
super
|
9
|
+
@ports_to_delete = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def teardown
|
13
|
+
@ports_to_delete.each do |spec|
|
14
|
+
resource, name = spec
|
15
|
+
inputs = @service.inputs
|
16
|
+
resource.each do |r|
|
17
|
+
inputs = inputs[r]
|
18
|
+
end
|
19
|
+
if inputs.has_key?(name)
|
20
|
+
inputs.delete(name)
|
21
|
+
end
|
22
|
+
assert !inputs.has_key?(name)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def get_free_port(input_collection)
|
27
|
+
port_names = input_collection.map() {|ent| ent.name}
|
28
|
+
proper_port_names = port_names.select() {|name| name != nil}
|
29
|
+
ports = proper_port_names.map() do |name|
|
30
|
+
if name.include?(":")
|
31
|
+
name.split(":")[1]
|
32
|
+
else
|
33
|
+
name
|
34
|
+
end
|
35
|
+
end
|
36
|
+
highest_existing_port = ports.map() {|p| Integer(p)}.max()
|
37
|
+
|
38
|
+
if highest_existing_port == nil
|
39
|
+
port = "10000"
|
40
|
+
else
|
41
|
+
port = (highest_existing_port + 1).to_s()
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_create_and_delete_tcp_raw
|
46
|
+
tcp_inputs = @service.inputs["tcp"]["raw"]
|
47
|
+
all_inputs = @service.inputs["all"]
|
48
|
+
|
49
|
+
port = get_free_port(tcp_inputs)
|
50
|
+
@ports_to_delete << [["tcp", "raw"], port]
|
51
|
+
|
52
|
+
input = tcp_inputs.create(port)
|
53
|
+
assert_equal(port, input.name)
|
54
|
+
assert_true(tcp_inputs.has_key?(port))
|
55
|
+
if @service.splunk_version[0] >= 5
|
56
|
+
assert_true(all_inputs.has_key?(port))
|
57
|
+
end
|
58
|
+
|
59
|
+
tcp_inputs.delete(port)
|
60
|
+
assert_false(tcp_inputs.has_key?(port))
|
61
|
+
if @service.splunk_version[0] >= 5
|
62
|
+
assert_false(all_inputs.has_key?(port))
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
##
|
67
|
+
# Check that fetching with namespaces provided works.
|
68
|
+
#
|
69
|
+
def test_fetch_with_namespaces
|
70
|
+
user_ns = Splunk::namespace(
|
71
|
+
:sharing => "user",
|
72
|
+
:app => "search",
|
73
|
+
:owner => @splunkrc[:username]
|
74
|
+
)
|
75
|
+
begin
|
76
|
+
user_udp_inputs = @service.inputs.fetch("udp", namespace=user_ns)
|
77
|
+
port = get_free_port(user_udp_inputs)
|
78
|
+
user_udp_inputs.create(port.to_s, :namespace => user_ns)
|
79
|
+
user_udp_inputs.fetch(port.to_s, namespace=user_ns)
|
80
|
+
ensure
|
81
|
+
@service.inputs.fetch("udp", namespace=user_ns).delete(port.to_s)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
##
|
86
|
+
# Check that fetching a nonexistent input kind returns nil.
|
87
|
+
#
|
88
|
+
def test_fetch_nonexistent_input_kind
|
89
|
+
assert_nil(@service.inputs[temporary_name()])
|
90
|
+
end
|
91
|
+
|
92
|
+
##
|
93
|
+
# Test that fetch resulting in server error raises SplunkHTTPError.
|
94
|
+
def test_fetch_with_server_error
|
95
|
+
new_service = Splunk::Service.new(@splunkrc)
|
96
|
+
new_inputs = new_service.inputs()
|
97
|
+
new_service.logout()
|
98
|
+
assert_raise(SplunkHTTPError) do
|
99
|
+
new_inputs["tcp"]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_create_and_delete_tcp_raw_with_restrictToHost
|
104
|
+
tcp_inputs = @service.inputs["tcp"]["raw"]
|
105
|
+
|
106
|
+
port = get_free_port(tcp_inputs)
|
107
|
+
name = "localhost:" + port
|
108
|
+
@ports_to_delete << [["tcp", "raw"], name]
|
109
|
+
|
110
|
+
input = tcp_inputs.create(port, :restrictToHost => "localhost")
|
111
|
+
assert_equal(name, input.name)
|
112
|
+
assert_equal("localhost", input["restrictToHost"])
|
113
|
+
assert_true(tcp_inputs.has_key?(name))
|
114
|
+
assert_false(tcp_inputs.has_key?(port))
|
115
|
+
|
116
|
+
if @service.splunk_version[0] >= 5
|
117
|
+
all_inputs = @service.inputs["all"]
|
118
|
+
assert_true(all_inputs.has_key?(name))
|
119
|
+
assert_false(all_inputs.has_key?(port))
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
tcp_inputs.delete(name)
|
124
|
+
|
125
|
+
assert_false(tcp_inputs.has_key?(name))
|
126
|
+
if @service.splunk_version[0] >= 5
|
127
|
+
assert_false(all_inputs.has_key?(name))
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_update_on_restrictToHost_does_not_clear
|
132
|
+
tcp_inputs = @service.inputs["tcp"]["raw"]
|
133
|
+
|
134
|
+
port = get_free_port(tcp_inputs)
|
135
|
+
name = "localhost:" + port
|
136
|
+
@ports_to_delete << [["tcp", "raw"], name]
|
137
|
+
|
138
|
+
input = tcp_inputs.create(port, :restrictToHost => "localhost")
|
139
|
+
|
140
|
+
input.update({:sourcetype => "boris"})
|
141
|
+
input.refresh()
|
142
|
+
assert_equal("localhost", input["restrictToHost"])
|
143
|
+
assert_true(tcp_inputs.has_key?(name))
|
144
|
+
|
145
|
+
if @service.splunk_version[0] >= 5
|
146
|
+
all_inputs = @service.inputs["all"]
|
147
|
+
assert_true(all_inputs.has_key?(name))
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def test_create_and_delete_udp
|
152
|
+
udp_inputs = @service.inputs["udp"]
|
153
|
+
|
154
|
+
port = get_free_port(udp_inputs)
|
155
|
+
@ports_to_delete << [["udp"], port]
|
156
|
+
|
157
|
+
input = udp_inputs.create(port)
|
158
|
+
assert_equal(port, input.name)
|
159
|
+
assert_true(udp_inputs.has_key?(port))
|
160
|
+
|
161
|
+
if @service.splunk_version[0] >= 5
|
162
|
+
all_inputs = @service.inputs["all"]
|
163
|
+
assert_true(all_inputs.has_key?(port))
|
164
|
+
end
|
165
|
+
|
166
|
+
udp_inputs.delete(port)
|
167
|
+
assert_false(udp_inputs.has_key?(port))
|
168
|
+
|
169
|
+
if @service.splunk_version[0] >= 5
|
170
|
+
assert_false(all_inputs.has_key?(port))
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def test_oneshot_input
|
175
|
+
if not has_test_data?(@service)
|
176
|
+
fail("Install the SDK test data to test oneshot inputs.")
|
177
|
+
return
|
178
|
+
end
|
179
|
+
|
180
|
+
install_app_from_collection("file_to_upload")
|
181
|
+
|
182
|
+
index_name = temporary_name()
|
183
|
+
index = @service.indexes.create(index_name)
|
184
|
+
begin
|
185
|
+
assert_eventually_true do
|
186
|
+
index.refresh()
|
187
|
+
index["disabled"] == "0"
|
188
|
+
end
|
189
|
+
|
190
|
+
event_count = Integer(index['totalEventCount'])
|
191
|
+
path = path_in_app("file_to_upload", ["log.txt"])
|
192
|
+
@service.inputs["oneshot"].create(path, :index => index_name)
|
193
|
+
|
194
|
+
assert_eventually_true do
|
195
|
+
index.refresh()
|
196
|
+
Integer(index['totalEventCount']) == event_count + 4
|
197
|
+
end
|
198
|
+
ensure
|
199
|
+
if @service.splunk_version[0] >= 5
|
200
|
+
index.delete()
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def test_oneshot_on_nonexistant_file
|
206
|
+
name = temporary_name()
|
207
|
+
assert_raises(Splunk::SplunkHTTPError) do
|
208
|
+
@service.inputs["oneshot"].create(name)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
data/test/test_jobs.rb
CHANGED
@@ -27,9 +27,9 @@ class JobsTestCase < TestCaseWithSplunkConnection
|
|
27
27
|
# There is a convenience method on service to create an asynchronous
|
28
28
|
# search job. Test it the same way.
|
29
29
|
#
|
30
|
-
def
|
30
|
+
def test_service_create_and_idempotent_cancel
|
31
31
|
jobs = @service.jobs
|
32
|
-
job = @service.
|
32
|
+
job = @service.create_search(QUERY)
|
33
33
|
assert_true(jobs.has_key?(job.sid))
|
34
34
|
job.cancel()
|
35
35
|
assert_eventually_true() { !jobs.has_key?(job.sid) }
|
@@ -102,6 +102,77 @@ class JobsTestCase < TestCaseWithSplunkConnection
|
|
102
102
|
assert_true(3 >= results.length())
|
103
103
|
end
|
104
104
|
|
105
|
+
##
|
106
|
+
# Test that oneshot jobs have no <sg> elements in the XML they return
|
107
|
+
# by default.
|
108
|
+
#
|
109
|
+
def test_oneshot_has_no_segmentation_by_default
|
110
|
+
omit_if(@service.splunk_version[0] == 4)
|
111
|
+
stream = @service.create_oneshot("search index=_internal GET | head 3")
|
112
|
+
assert_false(stream.include?("<sg"))
|
113
|
+
end
|
114
|
+
|
115
|
+
##
|
116
|
+
# Are <sg> elements returned in the XML from a oneshot job when we pass
|
117
|
+
# the option segmentation=raw?
|
118
|
+
#
|
119
|
+
def test_oneshot_has_forced_segmentation
|
120
|
+
omit_if(@service.splunk_version[0] == 4)
|
121
|
+
stream = @service.create_oneshot("search index=_internal GET | head 3",
|
122
|
+
:segmentation => "raw")
|
123
|
+
assert_true(stream.include?("<sg"))
|
124
|
+
end
|
125
|
+
|
126
|
+
##
|
127
|
+
# Test that export jobs have no <sg> elements in the XML they return by
|
128
|
+
# default.
|
129
|
+
#
|
130
|
+
def test_export_has_no_segmentation_by_default
|
131
|
+
omit_if(@service.splunk_version[0] == 4)
|
132
|
+
stream = @service.create_export("search index=_internal GET | head 3")
|
133
|
+
assert_false(stream.include?("<sg"))
|
134
|
+
end
|
135
|
+
|
136
|
+
##
|
137
|
+
# Export jobs should have <sg> elements in the XML they return when a
|
138
|
+
# value is passed to the segmentation argument to make it so.
|
139
|
+
#
|
140
|
+
def test_export_has_forced_segmentation
|
141
|
+
omit_if(@service.splunk_version[0] == 4)
|
142
|
+
|
143
|
+
stream = @service.create_export("search index=_internal GET | head 3",
|
144
|
+
:segmentation => "raw")
|
145
|
+
assert_true(stream.include?("<sg"))
|
146
|
+
end
|
147
|
+
|
148
|
+
##
|
149
|
+
# Results and preview on a search job should have no segmentation
|
150
|
+
# by default.
|
151
|
+
#
|
152
|
+
def test_asynchronous_job_has_no_segmentation_by_default
|
153
|
+
omit_if(@service.splunk_version[0] == 4)
|
154
|
+
job = @service.jobs.create("search index=_internal GET | head 3")
|
155
|
+
until job.is_done?()
|
156
|
+
sleep(0.1)
|
157
|
+
end
|
158
|
+
assert_false(job.events().include?("<sg"))
|
159
|
+
assert_false(job.preview().include?("<sg"))
|
160
|
+
end
|
161
|
+
|
162
|
+
##
|
163
|
+
# Results and preview on a search job should have segmentation when
|
164
|
+
# it is forced.
|
165
|
+
#
|
166
|
+
def test_asynchronous_job_has_segmentation_when_forced
|
167
|
+
omit_if(@service.splunk_version[0] == 4)
|
168
|
+
job = @service.jobs.create("search index=_internal GET | head 3")
|
169
|
+
until job.is_done?()
|
170
|
+
sleep(0.1)
|
171
|
+
end
|
172
|
+
assert_true(job.events(:segmentation => "raw").include?("<sg"))
|
173
|
+
assert_true(job.preview(:segmentation => "raw").include?("<sg"))
|
174
|
+
end
|
175
|
+
|
105
176
|
def test_each_and_values
|
106
177
|
jobs = Jobs.new(@service)
|
107
178
|
|