inventory-server 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +12 -0
  5. data/Dockerfile +22 -0
  6. data/Dockerfile-passenger +18 -0
  7. data/Dockerfile-unicorn +22 -0
  8. data/Gemfile +4 -0
  9. data/LICENSE.txt +22 -0
  10. data/README.md +32 -0
  11. data/Rakefile +2 -0
  12. data/bin/inventory-server +33 -0
  13. data/circle.yml +26 -0
  14. data/config.ru +8 -0
  15. data/docker/passenger/passenger.conf +15 -0
  16. data/docker/passenger/run-httpd.sh +8 -0
  17. data/docker/unicorn/nginx.conf +45 -0
  18. data/docker/unicorn/run-unicorn.sh +5 -0
  19. data/docker/unicorn/unicorn.conf +31 -0
  20. data/docker/unicorn/unicorn.rb +19 -0
  21. data/fig.yml +18 -0
  22. data/inventory-server.gemspec +45 -0
  23. data/lib/inventory/server.rb +30 -0
  24. data/lib/inventory/server/cli.rb +59 -0
  25. data/lib/inventory/server/config.rb +80 -0
  26. data/lib/inventory/server/email_parser.rb +28 -0
  27. data/lib/inventory/server/http_server.rb +49 -0
  28. data/lib/inventory/server/inventory_error.rb +9 -0
  29. data/lib/inventory/server/loader.rb +60 -0
  30. data/lib/inventory/server/logger.rb +16 -0
  31. data/lib/inventory/server/smtp_server.rb +36 -0
  32. data/lib/inventory/server/version.rb +5 -0
  33. data/plugins/facts_parser.rb +95 -0
  34. data/plugins/index.rb +56 -0
  35. data/plugins/json_schema_validator.rb +33 -0
  36. data/plugins/log_failures_on_disk.rb +35 -0
  37. data/public/.gitignore +0 -0
  38. data/spec/integration/fixtures/facter.xml +4543 -0
  39. data/spec/integration/http_spec.rb +24 -0
  40. data/spec/spec_helper.rb +96 -0
  41. data/spec/unit/cli_spec.rb +54 -0
  42. data/spec/unit/config_spec.rb +65 -0
  43. data/spec/unit/email_parser_spec.rb +53 -0
  44. data/spec/unit/facts_parser_spec.rb +176 -0
  45. data/spec/unit/fixtures/simple_plugin.rb +2 -0
  46. data/spec/unit/http_server_spec.rb +58 -0
  47. data/spec/unit/index_spec.rb +77 -0
  48. data/spec/unit/json_schema_validator_spec.rb +126 -0
  49. data/spec/unit/loader_spec.rb +34 -0
  50. data/spec/unit/log_failures_on_disk_spec.rb +50 -0
  51. data/spec/unit/server_spec.rb +11 -0
  52. data/spec/unit/smtp_server_spec.rb +68 -0
  53. data/tmp/.gitignore +0 -0
  54. metadata +434 -0
@@ -0,0 +1,24 @@
1
+ require 'rspec'
2
+ require 'rest-client'
3
+ require 'parallel'
4
+
5
+ FIXTURES_PATH = File.join File.expand_path(File.dirname(__FILE__)), 'fixtures'
6
+ XML_STR = File.read(File.join FIXTURES_PATH, 'facter.xml')
7
+
8
+ context "HTTP integration tests" do
9
+ before(:all) do
10
+ WebMock.allow_net_connect!
11
+ end
12
+
13
+ it "integrates one document", :integration => true do
14
+ response = RestClient.post "http://localhost/api/v1/facts/MY_UUID", XML_STR
15
+ expect(response.code).to eq 200
16
+ end
17
+
18
+ it "integrates 500 document", :integration => true do
19
+ Parallel.each([*1..500], :in_threads=>20){|i|
20
+ response = RestClient.post "http://localhost/api/v1/facts/MY_UUID_#{i}", XML_STR
21
+ expect(response.code).to eq 200
22
+ }
23
+ end
24
+ end
@@ -0,0 +1,96 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
4
+ # this file to always be loaded, without a need to explicitly require it in any
5
+ # files.
6
+ #
7
+ # Given that it is always loaded, you are encouraged to keep this file as
8
+ # light-weight as possible. Requiring heavyweight dependencies from this file
9
+ # will add to the boot time of your test suite on EVERY test run, even for an
10
+ # individual file that may not need all of that loaded. Instead, consider making
11
+ # a separate helper file that requires the additional dependencies and performs
12
+ # the additional setup, and require it from the spec files that actually need
13
+ # it.
14
+ #
15
+ # The `.rspec` file also contains a few flags that are not defaults but that
16
+ # users commonly want.
17
+ #
18
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
19
+ require "codeclimate-test-reporter"
20
+ require "inventory/server/logger"
21
+ CodeClimate::TestReporter.start
22
+
23
+ Inventory::Server::InventoryLogger.setup '/dev/null'
24
+ RSpec.configure do |config|
25
+ # rspec-expectations config goes here. You can use an alternate
26
+ # assertion/expectation library such as wrong or the stdlib/minitest
27
+ # assertions if you prefer.
28
+ config.expect_with :rspec do |expectations|
29
+ # This option will default to `true` in RSpec 4. It makes the `description`
30
+ # and `failure_message` of custom matchers include text for helper methods
31
+ # defined using `chain`, e.g.:
32
+ # be_bigger_than(2).and_smaller_than(4).description
33
+ # # => "be bigger than 2 and smaller than 4"
34
+ # ...rather than:
35
+ # # => "be bigger than 2"
36
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
37
+ end
38
+
39
+ # rspec-mocks config goes here. You can use an alternate test double
40
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
41
+ config.mock_with :rspec do |mocks|
42
+ # Prevents you from mocking or stubbing a method that does not exist on
43
+ # a real object. This is generally recommended, and will default to
44
+ # `true` in RSpec 4.
45
+ mocks.verify_partial_doubles = true
46
+ end
47
+
48
+ # The settings below are suggested to provide a good initial experience
49
+ # with RSpec, but feel free to customize to your heart's content.
50
+ =begin
51
+ # These two settings work together to allow you to limit a spec run
52
+ # to individual examples or groups you care about by tagging them with
53
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
54
+ # get run.
55
+ config.filter_run :focus
56
+ config.run_all_when_everything_filtered = true
57
+
58
+ # Limits the available syntax to the non-monkey patched syntax that is
59
+ # recommended. For more details, see:
60
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
61
+ # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
62
+ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
63
+ config.disable_monkey_patching!
64
+
65
+ # This setting enables warnings. It's recommended, but in some cases may
66
+ # be too noisy due to issues in dependencies.
67
+ config.warnings = true
68
+
69
+ # Many RSpec users commonly either run the entire suite or an individual
70
+ # file, and it's useful to allow more verbose output when running an
71
+ # individual spec file.
72
+ if config.files_to_run.one?
73
+ # Use the documentation formatter for detailed output,
74
+ # unless a formatter has already been configured
75
+ # (e.g. via a command-line flag).
76
+ config.default_formatter = 'doc'
77
+ end
78
+
79
+ # Print the 10 slowest examples and example groups at the
80
+ # end of the spec run, to help surface which specs are running
81
+ # particularly slow.
82
+ config.profile_examples = 10
83
+
84
+ # Run specs in random order to surface order dependencies. If you find an
85
+ # order dependency and want to debug it, you can fix the order by providing
86
+ # the seed, which is printed after each run.
87
+ # --seed 1234
88
+ config.order = :random
89
+
90
+ # Seed global randomization in this process using the `--seed` CLI option.
91
+ # Setting this allows you to use `--seed` to deterministically reproduce
92
+ # test failures related to randomization by passing the same `--seed` value
93
+ # as the one that triggered the failure.
94
+ Kernel.srand config.seed
95
+ =end
96
+ end
@@ -0,0 +1,54 @@
1
+ require 'inventory/server/cli'
2
+
3
+ RSpec.describe Inventory::Server::CLI do
4
+ before do
5
+ $stdout = StringIO.new
6
+ end
7
+ after(:all) do
8
+ $stdout = STDOUT
9
+ end
10
+
11
+ it "should print usage on -h" do
12
+ expect {
13
+ Inventory::Server::CLI.new().parse! ['-h']
14
+ }.to raise_error SystemExit
15
+ expect($stdout.string).to match(/Usage/)
16
+ end
17
+
18
+ it "should print version on -v" do
19
+ expect {
20
+ Inventory::Server::CLI.new().parse! ['-v']
21
+ }.to raise_error SystemExit
22
+ expect($stdout.string).to match(/\d\.\d\.\d/)
23
+ end
24
+
25
+ it "should set host on --host" do
26
+ options = Inventory::Server::CLI.new().parse! ['--host', '0.0.0.0']
27
+ expect(options[:host]).to eq '0.0.0.0'
28
+ end
29
+
30
+ it "should set smtp port on --smtp_port" do
31
+ options = Inventory::Server::CLI.new().parse! ['--smtp_port', '25']
32
+ expect(options[:smtp_port]).to eq 25
33
+ end
34
+
35
+ it "should set ElasticSearch host on --es_host" do
36
+ options = Inventory::Server::CLI.new().parse! ['--es_host', 'http://localhost:9300']
37
+ expect(options[:es_host]).to eq 'http://localhost:9300'
38
+ end
39
+
40
+ it "should set logger on -o" do
41
+ options = Inventory::Server::CLI.new().parse! ['-o', '/dev/null']
42
+ expect(options[:logger]).to eq '/dev/null'
43
+ end
44
+
45
+ it "should set log level on -l" do
46
+ options = Inventory::Server::CLI.new().parse! ['-l', 'DEBUG']
47
+ expect(options[:log_level]).to eq 'DEBUG'
48
+ end
49
+
50
+ it "should set debug on -d" do
51
+ options = Inventory::Server::CLI.new().parse! ['-d']
52
+ expect(options[:debug]).to eq true
53
+ end
54
+ end
@@ -0,0 +1,65 @@
1
+ require 'inventory/server/config'
2
+
3
+ RSpec.describe Inventory::Server::Config do
4
+ before do
5
+ ENV['INVENTORY_FAILED_FACTS_DIR'] = "./log/"
6
+ end
7
+
8
+ context "without configuration" do
9
+ it "should should use default configuration" do
10
+ config = Inventory::Server::Config.generate({})
11
+ expect(config[:smtp_port]).to eq 2525
12
+ end
13
+ end
14
+
15
+
16
+ context "with ENV configuration" do
17
+ before do
18
+ ENV['INVENTORY_HOST'] = "127.0.0.1"
19
+ ENV['INVENTORY_SMTP_PORT'] = "2626"
20
+ ENV['INVENTORY_DEBUG'] = "false"
21
+ ENV['INVENTORY_LOGGER'] = "stdout"
22
+ ENV['INVENTORY_LOG_LEVEL'] = "DEBUG"
23
+ end
24
+
25
+ after do
26
+ ENV['INVENTORY_HOST'] = nil
27
+ ENV['INVENTORY_SMTP_PORT'] = nil
28
+ ENV['INVENTORY_DEBUG'] = nil
29
+ ENV['INVENTORY_LOGGER'] = nil
30
+ ENV['INVENTORY_LOG_LEVEL'] = nil
31
+ end
32
+
33
+ it "should use the ENV configuration" do
34
+ config = Inventory::Server::Config.generate({})
35
+ expect(config[:host]).to eq '127.0.0.1'
36
+ end
37
+
38
+ it "should should cast integer" do
39
+ config = Inventory::Server::Config.generate({})
40
+ expect(config[:smtp_port]).to eq 2626
41
+ end
42
+
43
+ it "should should cast boolean" do
44
+ config = Inventory::Server::Config.generate({})
45
+ expect(config[:debug]).to eq false
46
+ end
47
+
48
+ it "should should cast stdout/stderr" do
49
+ config = Inventory::Server::Config.generate({})
50
+ expect(config[:logger]).to eq $stdout
51
+ end
52
+
53
+ it "should should cast DEBUG/INFO/WARN/ERROR/FATAL" do
54
+ config = Inventory::Server::Config.generate({})
55
+ expect(config[:log_level]).to eq Logger::DEBUG
56
+ end
57
+
58
+ context "with CLI configuration" do
59
+ it "should use the CLI configuration" do
60
+ config = Inventory::Server::Config.generate({:smtp_port => 25})
61
+ expect(config[:smtp_port]).to eq 25
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,53 @@
1
+ require 'inventory/server/email_parser'
2
+
3
+ require 'mail'
4
+
5
+ RSpec.describe Inventory::Server::EmailParser, '#call' do
6
+
7
+ context "without email subject" do
8
+ mail = Mail.new do
9
+ from 'filirom1@toto.com'
10
+ to 'filirom2@toto.com'
11
+ subject ''
12
+ body '{"key": "value"}'
13
+ end
14
+
15
+ it "should throw an error" do
16
+ expect {
17
+ Inventory::Server::EmailParser.parse(mail.to_s)
18
+ }.to raise_error 'email subject is missing'
19
+ end
20
+ end
21
+
22
+ context "without email body" do
23
+ mail = Mail.new do
24
+ from 'filirom1@toto.com'
25
+ to 'filirom2@toto.com'
26
+ subject 'MY_UUID'
27
+ body ''
28
+ end
29
+
30
+ it "should throw an error" do
31
+ expect {
32
+ Inventory::Server::EmailParser.parse(mail.to_s)
33
+ }.to raise_error 'email body is missing'
34
+ end
35
+ end
36
+
37
+ context "with a valid email" do
38
+ mail = Mail.new do
39
+ from 'filirom1@toto.com'
40
+ to 'filirom2@toto.com'
41
+ subject 'MY_UUID'
42
+ body '{"key": "value"}'
43
+ end
44
+
45
+ it "should parse the email" do
46
+ id, body = Inventory::Server::EmailParser.parse(mail.to_s)
47
+
48
+ expect(id).to eq 'MY_UUID'
49
+ expect(body).to eq '{"key": "value"}'
50
+ end
51
+ end
52
+ end
53
+
@@ -0,0 +1,176 @@
1
+ # encoding: utf-8
2
+ require_relative '../../plugins/facts_parser'
3
+
4
+ noop = lambda {|env|}
5
+
6
+ RSpec.describe Inventory::Server::FactsParser, '#call' do
7
+ context "without body" do
8
+ env = { :body => nil }
9
+ it "should throw an error" do
10
+ expect {
11
+ Inventory::Server::FactsParser.new(noop, {}).call(env)
12
+ }.to raise_error 'body missing'
13
+ end
14
+ end
15
+
16
+ context "with bad format" do
17
+ env = { :body => 'blablayuqsd' }
18
+ it "should throw an error" do
19
+ expect {
20
+ Inventory::Server::FactsParser.new(noop, {}).call(env)
21
+ }.to raise_error 'bad format'
22
+ end
23
+ end
24
+
25
+ context "with bad JSON" do
26
+ env = { :body => '{toto:12}' }
27
+ it "should throw an error" do
28
+ expect {
29
+ Inventory::Server::FactsParser.new(noop, {}).call(env)
30
+ }.to raise_error JSON::ParserError
31
+ end
32
+ end
33
+
34
+ context "with good JSON" do
35
+ env = { :body => '{"key":"value"}' }
36
+ it "should throw an error" do
37
+ Inventory::Server::FactsParser.new(noop, {}).call(env)
38
+ expect(env[:facts]).to eq 'key' => 'value'
39
+ end
40
+ end
41
+
42
+ context "with bad YAML" do
43
+ env = { :body => '''---
44
+ play:
45
+ toto''' }
46
+ it "should throw an error" do
47
+ expect {
48
+ Inventory::Server::FactsParser.new(noop, {}).call(env)
49
+ }.to raise_error Psych::SyntaxError
50
+ end
51
+ end
52
+
53
+ context "with good YAML" do
54
+ env = { :body => '''---
55
+ key: value''' }
56
+ it "should throw an error" do
57
+ Inventory::Server::FactsParser.new(noop, {}).call(env)
58
+ expect(env[:facts]).to eq 'key' => 'value'
59
+ end
60
+ end
61
+
62
+ context "with bad XML" do
63
+ env = { :body => '''<Inventory>blabla</Intory>''' }
64
+ it "should throw an error" do
65
+ expect {
66
+ Inventory::Server::FactsParser.new(noop, {}).call(env)
67
+ }.to raise_error /Invalid XML/
68
+ end
69
+ end
70
+
71
+ context "with an other bad XML" do
72
+ env = { :body => '''
73
+ <inventory>
74
+ <key>value</key>
75
+ text
76
+ </inventory>''' }
77
+ it "should throw an error" do
78
+ expect {
79
+ Inventory::Server::FactsParser.new(noop, {}).call(env)
80
+ }.to raise_error /Invalid XML/
81
+ end
82
+ end
83
+
84
+ context "with good XML" do
85
+ env = { :body => '''
86
+ <inventory>
87
+ <key>value</key>
88
+ </inventory>''' }
89
+ it "should parse it" do
90
+ Inventory::Server::FactsParser.new(noop, {}).call(env)
91
+ expect(env[:facts]).to eq 'key' => 'value'
92
+ end
93
+ end
94
+
95
+ context "with XML using empty node" do
96
+ env = { :body => '''
97
+ <inventory>
98
+ <key></key>
99
+ </inventory>''' }
100
+ it "should return nil" do
101
+ Inventory::Server::FactsParser.new(noop, {}).call(env)
102
+ expect(env[:facts]).to eq 'key' => nil
103
+ end
104
+ end
105
+
106
+
107
+ context "with good XML with base64 in object" do
108
+ env = { :body => '''
109
+ <inventory>
110
+ <key>value</key>
111
+ <obj>
112
+ <key>__base64__dmFsdWU=</key>
113
+ </obj>
114
+ </inventory>''' }
115
+ it "should parse it" do
116
+ Inventory::Server::FactsParser.new(noop, {}).call(env)
117
+ expect(env[:facts]).to eq({ 'key' =>'value', 'obj' => { 'key' => 'value' } })
118
+ end
119
+ end
120
+
121
+ context "with good XML with base64 in array" do
122
+ env = { :body => '''
123
+ <inventory>
124
+ <key>value</key>
125
+ <array>
126
+ <value>__base64__dmFsdWU=</value>
127
+ <value>__base64__dmFsdWU=</value>
128
+ </array>
129
+ </inventory>''' }
130
+ it "should parse it" do
131
+ Inventory::Server::FactsParser.new(noop, {}).call(env)
132
+ expect(env[:facts]).to eq({ 'key' =>'value', 'array' => { 'value' => ['value', 'value']}})
133
+ end
134
+ end
135
+
136
+ context "with XML base64 containing UTF-8 encodings" do
137
+ env = { :body => '''
138
+ <inventory>
139
+ <key>value</key>
140
+ <obj>
141
+ <key>__base64__aMOpaMOp</key>
142
+ </obj>
143
+ </inventory>''' }
144
+ it "should parse it" do
145
+ Inventory::Server::FactsParser.new(noop, {}).call(env)
146
+ expect(env[:facts]).to eq({ 'key' =>'value', 'obj' => { 'key' => "h\xc3\xa9h\xc3\xa9" } })
147
+ end
148
+ end
149
+
150
+ context "with XML base64 containing ISO-8859-1 encodings" do
151
+ env = { :body => '''
152
+ <inventory>
153
+ <key>value</key>
154
+ <obj>
155
+ <key>__base64__aOlo6Q==</key>
156
+ </obj>
157
+ </inventory>''' }
158
+ it "should parse it" do
159
+ Inventory::Server::FactsParser.new(noop, {}).call(env)
160
+ expect(env[:facts]).to eq({ 'key' =>'value', 'obj' => { 'key' => "h\xc3\xa9h\xc3\xa9" } })
161
+ end
162
+ end
163
+
164
+ context "with XML base64 containing Japonese encodings" do
165
+ env = { :body => '''
166
+ <inventory>
167
+ <key>value</key>
168
+ <obj>
169
+ <key>__base64__grGC8YLJgr+CzQ==</key>
170
+ </obj>
171
+ </inventory>''' }
172
+ it "should not throw an error" do
173
+ Inventory::Server::FactsParser.new(noop, {}).call(env)
174
+ end
175
+ end
176
+ end