stacker_bee 1.0.1 → 2.0.0.pre.pre164

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +13 -5
  2. data/.travis.yml +14 -1
  3. data/README.md +117 -67
  4. data/bin/stacker_bee +4 -3
  5. data/lib/faraday_middleware/response/graylog.rb +35 -0
  6. data/lib/stacker_bee/body_parser.rb +11 -1
  7. data/lib/stacker_bee/client.rb +32 -4
  8. data/lib/stacker_bee/connection.rb +8 -9
  9. data/lib/stacker_bee/dictionary_flattener.rb +12 -12
  10. data/lib/stacker_bee/graylog_faraday_middleware.rb +13 -0
  11. data/lib/stacker_bee/request_error.rb +1 -1
  12. data/lib/stacker_bee/version.rb +1 -1
  13. data/lib/stacker_bee.rb +3 -0
  14. data/spec/cassettes/A_request_sent_to_CloudStack_for_console_access/returns_html_for_console_access.yml +34 -0
  15. data/spec/cassettes/A_response_to_a_request_sent_to_the_CloudStack_API/{a_request_parameter_with_a_Map → a_request_parameter_with_a_map}/can_create_an_object.yml +0 -0
  16. data/spec/integration/configure_middleware_spec.rb +30 -0
  17. data/spec/integration/console_spec.rb +23 -0
  18. data/spec/integration/request_spec.rb +2 -31
  19. data/spec/spec_helper.rb +4 -0
  20. data/spec/units/faraday_graylog_middleware_spec.rb +80 -0
  21. data/spec/units/stacker_bee/client_spec.rb +52 -9
  22. data/spec/units/stacker_bee/connection_spec.rb +14 -5
  23. data/spec/units/stacker_bee/console_spec.rb +0 -0
  24. data/spec/units/stacker_bee/graylog_faraday_middleware_spec.rb +51 -0
  25. data/spec/units/stacker_bee/request_error_spec.rb +7 -3
  26. data/spec/units/stacker_bee/response_spec.rb +18 -4
  27. data/stacker_bee.gemspec +9 -2
  28. metadata +58 -15
  29. data/lib/stacker_bee/middleware/logger.rb +0 -47
  30. data/spec/cassettes/A_response_to_a_request_sent_to_the_CloudStack_API/a_request_parameter_with_a_Map/object.yml +0 -153
  31. data/spec/units/stacker_bee/middleware/logger_spec.rb +0 -55
@@ -0,0 +1,23 @@
1
+ require "spec_helper"
2
+
3
+ describe "A request sent to CloudStack for console access", :vcr do
4
+ let(:config_hash) do
5
+ {
6
+ url: CONFIG['url'],
7
+ api_key: CONFIG['api_key'],
8
+ secret_key: CONFIG['secret_key']
9
+ }
10
+ end
11
+
12
+ let(:client) do
13
+ StackerBee::Client.new(config_hash)
14
+ end
15
+
16
+ let(:vm) { "36f9c08b-f17a-4d0e-ac9b-d45ce2d34fcd" }
17
+
18
+ subject(:console_access) { client.console_access(vm: vm) }
19
+
20
+ it "returns html for console access" do
21
+ expect(console_access).to match %r{<html>.*<frame src=.*</html>}
22
+ end
23
+ end
@@ -2,16 +2,12 @@ require "spec_helper"
2
2
  require "logger"
3
3
 
4
4
  describe "A response to a request sent to the CloudStack API", :vcr do
5
- let(:io) { StringIO.new }
6
- let(:logger) { Logger.new(io) }
7
- let(:log_string) { io.string }
8
- let(:url) { CONFIG["url"] }
5
+ let(:url) { CONFIG["url"] }
9
6
  let(:config_hash) do
10
7
  {
11
8
  url: url,
12
9
  api_key: CONFIG["api_key"],
13
10
  secret_key: CONFIG["secret_key"],
14
- logger: logger,
15
11
  apis_path: File.join(File.dirname(__FILE__), '../fixtures/4.2.json')
16
12
  }
17
13
  end
@@ -31,36 +27,11 @@ describe "A response to a request sent to the CloudStack API", :vcr do
31
27
  its(["account_type"]) { should be_a Numeric }
32
28
  end
33
29
 
34
- it "should log request" do
35
- subject
36
- log_string.should include "listAccounts"
37
- end
38
-
39
- it "should not log response as error" do
40
- subject
41
- log_string.should_not include "ERROR"
42
- end
43
-
44
- context "containing an error" do
45
- subject do
46
- client.deploy_virtual_machine
47
- end
48
- it { expect { subject }.to raise_error StackerBee::ClientError }
49
-
50
- it "should log response as error" do
51
- begin
52
- subject
53
- rescue StackerBee::ClientError
54
- log_string.should include "ERROR"
55
- end
56
- end
57
- end
58
-
59
30
  context "failing to connect" do
60
31
  let(:url) { "http://127.0.0.1:666/client/api" }
61
32
  it "should raise helpful exception" do
62
33
  klass = StackerBee::ConnectionError
63
- expect{ subject }.to raise_error klass, /#{url}/
34
+ expect { subject }.to raise_error klass, /#{url}/
64
35
  end
65
36
  end
66
37
 
data/spec/spec_helper.rb CHANGED
@@ -41,6 +41,10 @@ VCR.configure do |c|
41
41
  c.filter_sensitive_data('<CLOUD_STACK_URL>') do
42
42
  CONFIG["url"]
43
43
  end
44
+ c.filter_sensitive_data('<CLOUD_STACK_HOST>') do
45
+ uri = URI.parse(CONFIG["url"])
46
+ "#{uri.scheme}://#{uri.host}:#{uri.port}"
47
+ end
44
48
  c.filter_sensitive_data('<CLOUD_STACK_API_KEY>') do
45
49
  CONFIG["api_key"]
46
50
  end
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+
3
+ describe FaradayMiddleware::Graylog do
4
+ subject { log_data }
5
+
6
+ class DummyLogger
7
+ attr_accessor :data
8
+ alias_method :notify, :data=
9
+ end
10
+
11
+ let(:log_data) { logger.data }
12
+ let(:logger) { DummyLogger.new }
13
+
14
+ let(:dummy_adapter) { ->(env) { Faraday::Response.new(env) } }
15
+ let(:middleware) do
16
+ described_class.new(dummy_adapter, logger, facility: facility)
17
+ end
18
+
19
+ let(:env) do
20
+ {
21
+ body: "DATA",
22
+ response_headers: {},
23
+ response: {},
24
+ status: status
25
+ }
26
+ end
27
+ let(:facility) { nil }
28
+
29
+ before { middleware.call(env) }
30
+
31
+ context "a basic request" do
32
+ let(:status) { 200 }
33
+
34
+ its([:facility]) { should eq "faraday-middleware-graylog" }
35
+ its([:short_message]) { should eq "faraday-middleware-graylog Request" }
36
+ its([:_data]) do
37
+ should eq(body: "DATA", response_headers: {}, status: status)
38
+ end
39
+ end
40
+
41
+ context "given a facility" do
42
+ let(:status) { 200 }
43
+ let(:facility) { "stacker_bee" }
44
+
45
+ its([:facility]) { should eq "stacker_bee" }
46
+ end
47
+
48
+ context "a successful request" do
49
+ let(:status) { 200 }
50
+ its([:level]) { should eq FaradayMiddleware::Graylog::INFO }
51
+ end
52
+
53
+ context "a failed request" do
54
+ let(:status) { 500 }
55
+ its([:level]) { should eq FaradayMiddleware::Graylog::ERROR }
56
+ end
57
+
58
+ describe "a subclass" do
59
+ class GrayLogSubclass < FaradayMiddleware::Graylog
60
+ def short_message(env)
61
+ "Short message: #{env[:status]}"
62
+ end
63
+
64
+ def level(env)
65
+ env[:status]
66
+ end
67
+ end
68
+
69
+ let(:middleware) { GrayLogSubclass.new(dummy_adapter, logger) }
70
+ let(:status) { 401 }
71
+
72
+ it "can override the level determining logic" do
73
+ log_data[:level].should eq 401
74
+ end
75
+
76
+ it "can override the short_message logic" do
77
+ log_data[:short_message].should eq "Short message: 401"
78
+ end
79
+ end
80
+ end
@@ -13,7 +13,7 @@ describe StackerBee::Client, ".api" do
13
13
  end
14
14
 
15
15
  describe StackerBee::Client, "calling endpoint" do
16
- let(:url) { "cloud-stack.com" }
16
+ let(:url) { "http://cloud-stack.com" }
17
17
  let(:api_key) { "cloud-stack-api-key" }
18
18
  let(:secret_key) { "cloud-stack-secret-key" }
19
19
  let(:config_hash) do
@@ -42,7 +42,7 @@ describe StackerBee::Client, "calling endpoint" do
42
42
  ) do
43
43
  request
44
44
  end
45
- connection.stub(:get).with(request) { raw_response }
45
+ connection.stub(:get).with(request, "") { raw_response }
46
46
  StackerBee::Response.stub(:new).with(raw_response) { response }
47
47
  end
48
48
 
@@ -59,7 +59,7 @@ describe StackerBee::Client, "#request" do
59
59
  let(:endpoint) { "listVirtualMachines" }
60
60
  let(:params) { { list: :all } }
61
61
 
62
- let(:url) { "cloud-stack.com" }
62
+ let(:url) { "http://cloud-stack.com" }
63
63
  let(:api_key) { "cloud-stack-api-key" }
64
64
  let(:secret_key) { "cloud-stack-secret-key" }
65
65
  let(:config_hash) do
@@ -80,7 +80,7 @@ describe StackerBee::Client, "#request" do
80
80
  StackerBee::Request.should_receive(:new).with(endpoint, api_key, params) do
81
81
  request
82
82
  end
83
- connection.should_receive(:get).with(request) { raw_response }
83
+ connection.should_receive(:get).with(request, "") { raw_response }
84
84
  StackerBee::Response.should_receive(:new).with(raw_response) { response }
85
85
  end
86
86
 
@@ -93,7 +93,7 @@ describe StackerBee::Client, "#request" do
93
93
  end
94
94
 
95
95
  describe StackerBee::Client, "configuration" do
96
- let(:default_url) { "default_cloud-stack.com" }
96
+ let(:default_url) { "http://default_cloud-stack.com" }
97
97
  let(:default_api_key) { "default-cloud-stack-api-key" }
98
98
  let(:default_secret_key) { "default-cloud-stack-secret-key" }
99
99
  let(:default_config_hash) do
@@ -101,13 +101,14 @@ describe StackerBee::Client, "configuration" do
101
101
  url: default_url,
102
102
  api_key: default_api_key,
103
103
  secret_key: default_secret_key,
104
- allow_empty_string_params: false
104
+ allow_empty_string_params: false,
105
+ middlewares: ->(*) {}
105
106
  }
106
107
  end
107
108
  let!(:default_configuration) do
108
109
  StackerBee::Configuration.new(default_config_hash)
109
110
  end
110
- let(:instance_url) { "instance-cloud-stack.com" }
111
+ let(:instance_url) { "http://instance-cloud-stack.com" }
111
112
  let(:instance_api_key) { "instance-cloud-stack-api-key" }
112
113
  let(:instance_secret_key) { "instance-cloud-stack-secret-key" }
113
114
  let(:instance_config_hash) do
@@ -115,7 +116,8 @@ describe StackerBee::Client, "configuration" do
115
116
  url: instance_url,
116
117
  api_key: instance_api_key,
117
118
  secret_key: instance_secret_key,
118
- allow_empty_string_params: false
119
+ allow_empty_string_params: false,
120
+ middlewares: ->(*) {}
119
121
  }
120
122
  end
121
123
  let!(:instance_configuration) do
@@ -161,7 +163,7 @@ describe StackerBee::Client, "configuration" do
161
163
  end
162
164
 
163
165
  describe "#url" do
164
- let(:other_url) { "other-cloud-stack.com" }
166
+ let(:other_url) { "http://other-cloud-stack.com" }
165
167
  before { subject.url = other_url }
166
168
  its(:url) { should eq other_url }
167
169
  end
@@ -179,3 +181,44 @@ describe StackerBee::Client, "configuration" do
179
181
  end
180
182
  end
181
183
  end
184
+
185
+ describe StackerBee::Client, "#console_access" do
186
+ let(:config_hash) do
187
+ {
188
+ url: CONFIG["url"],
189
+ api_key: CONFIG["api_key"],
190
+ secret_key: CONFIG["secret_key"]
191
+ }
192
+ end
193
+
194
+ let(:client) do
195
+ StackerBee::Client.new(config_hash)
196
+ end
197
+
198
+ let(:vm) { "36f9c08b-f17a-4d0e-ac9b-d45ce2d34fcd" }
199
+
200
+ let(:body) { "<body></body>" }
201
+ let(:headers) { { 'content-type' => 'text/html' } }
202
+ let(:response_stub) do
203
+ double(StackerBee::Response, success?: true, body: body, headers: headers)
204
+ end
205
+
206
+ subject(:console_access) { client.console_access(vm: vm) }
207
+
208
+ it "makes a request with the consoleAccess endpoint" do
209
+ expect(client).to receive(:request).with(
210
+ "consoleAccess", cmd: 'access', vm: vm
211
+ )
212
+
213
+ console_access
214
+ end
215
+
216
+ it "makes a get request to the connection" do
217
+ expect(client.send(:connection)).to receive(:get) do |request, path|
218
+ expect(request.endpoint).to eq("consoleAccess")
219
+ expect(path).to eq("/client/console")
220
+ end.and_return(response_stub)
221
+
222
+ expect(console_access).to eq(body)
223
+ end
224
+ end
@@ -2,6 +2,7 @@ require "spec_helper"
2
2
 
3
3
  describe StackerBee::Connection do
4
4
  let(:url) { "http://test.com:1234/foo/bar/" }
5
+ let(:path) { "/foo/bar" }
5
6
  let(:secret_key) { "shhh" }
6
7
  let(:configuration) { double url: url, secret_key: secret_key }
7
8
  let(:query_params) { [[:foo, :bar]] }
@@ -9,18 +10,18 @@ describe StackerBee::Connection do
9
10
  let(:response) { double }
10
11
  let(:faraday) { double get: response }
11
12
  let(:connection) { StackerBee::Connection.new configuration }
12
- subject { connection.get request }
13
+ subject(:get) { connection.get request, path }
13
14
  before do
14
15
  Faraday.stub(:new) { faraday }
15
16
  end
16
17
 
17
18
  context "successfully connecting" do
18
19
  before do
19
- faraday.should_receive(:get).with('/foo/bar/', query_params) { response }
20
+ faraday.should_receive(:get).with('/foo/bar', query_params) { response }
20
21
  end
21
22
  it { should be response }
22
23
  it "specifies url without path when creating connection" do
23
- subject
24
+ get
24
25
  Faraday.should have_received(:new).with(url: "http://test.com:1234")
25
26
  end
26
27
  end
@@ -31,7 +32,7 @@ describe StackerBee::Connection do
31
32
  end
32
33
  it "should raise helpful exception" do
33
34
  klass = StackerBee::ConnectionError
34
- expect(-> { subject }).to raise_error klass, /#{url}/
35
+ expect { get }.to raise_error klass, /#{url}/
35
36
  end
36
37
  end
37
38
 
@@ -39,7 +40,15 @@ describe StackerBee::Connection do
39
40
  let(:url) { "wrong.com" }
40
41
  it "should raise helpful exception" do
41
42
  klass = StackerBee::ConnectionError
42
- expect(-> { subject }).to raise_error klass, /no protocol/
43
+ expect { get }.to raise_error klass, /no protocol/
44
+ end
45
+ end
46
+
47
+ context "when given a path" do
48
+ let(:path) { '/baz' }
49
+ it "makes a request to the correct path" do
50
+ expect(faraday).to receive(:get).with(path, query_params)
51
+ connection.get request, path
43
52
  end
44
53
  end
45
54
  end
File without changes
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ describe StackerBee::GraylogFaradayMiddleware do
4
+ subject { log_data }
5
+
6
+ class DummyLogger
7
+ attr_accessor :data
8
+ alias_method :notify, :data=
9
+ end
10
+
11
+ let(:log_data) { logger.data }
12
+ let(:logger) { DummyLogger.new }
13
+
14
+ let(:dummy_adapter) { ->(env) { Faraday::Response.new(env) } }
15
+ let(:middleware) { described_class.new(dummy_adapter, logger) }
16
+ let(:status) { 200 }
17
+
18
+ let(:env) do
19
+ {
20
+ body: "DATA",
21
+ response_headers: {},
22
+ response: {},
23
+ status: status,
24
+ url: URI.parse("http://a.b/?key=KEY&command=listVirtualMachines&val=val")
25
+ }
26
+ end
27
+
28
+ before { middleware.call(env) }
29
+
30
+ it "sets a custom short message" do
31
+ log_data[:short_message].should eq "StackerBee listVirtualMachines"
32
+ end
33
+
34
+ its([:facility]) { should == "stacker-bee" }
35
+
36
+ context "without a command in the url" do
37
+ let(:env) do
38
+ {
39
+ body: "DATA",
40
+ response_headers: {},
41
+ response: {},
42
+ status: status,
43
+ url: URI.parse("http://a.b/?key=KEY&&val=val")
44
+ }
45
+ end
46
+
47
+ it "sets a custom short message" do
48
+ log_data[:short_message].should eq "StackerBee"
49
+ end
50
+ end
51
+ end
@@ -1,7 +1,9 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe StackerBee::RequestError, ".for" do
4
- let(:raw_response) { double status: status, body: '{"foo": "bar"}' }
4
+ let(:raw_response) do
5
+ double status: status, body: '{"foo": "bar"}', headers: {}
6
+ end
5
7
  subject { StackerBee::RequestError.for raw_response }
6
8
 
7
9
  context "HTTP status in the 400s" do
@@ -21,7 +23,7 @@ end
21
23
  describe StackerBee::RequestError do
22
24
  let(:http_status) { 431 }
23
25
  let(:message) do
24
- "Unable to execute API command deployvirtualmachine " +
26
+ "Unable to execute API command deployvirtualmachine " \
25
27
  "due to missing parameter zoneid"
26
28
  end
27
29
  let(:raw_body) do
@@ -36,7 +38,9 @@ describe StackerBee::RequestError do
36
38
  }
37
39
  EOS
38
40
  end
39
- let(:raw_response) { double body: raw_body, :success? => false, status: 431 }
41
+ let(:raw_response) do
42
+ double body: raw_body, :success? => false, status: 431, headers: {}
43
+ end
40
44
  let(:client_error) { StackerBee::ClientError.new raw_response }
41
45
  subject { client_error }
42
46
  its(:status) { should eq http_status }
@@ -2,8 +2,12 @@ require "spec_helper"
2
2
 
3
3
  describe StackerBee::Response do
4
4
  let(:raw_body) { '{ "json": "here" }' }
5
- let(:raw_response) { double body: raw_body, :success? => true }
6
- let(:response) { StackerBee::Response.new raw_response }
5
+ let(:raw_response) do
6
+ double(body: raw_body, success?: true, headers: headers)
7
+ end
8
+ let(:response) { StackerBee::Response.new raw_response }
9
+ let(:headers) { { 'content-type' => content_type } }
10
+ let(:content_type) { "application/json; charset=UTF-8" }
7
11
  subject { response }
8
12
  its(:body) { should == 'here' }
9
13
 
@@ -44,7 +48,8 @@ describe StackerBee::Response do
44
48
  double(
45
49
  :body => '{ "foo": "bar" }',
46
50
  :success? => false,
47
- :status => 431
51
+ :status => 431,
52
+ :headers => {}
48
53
  )
49
54
  end
50
55
  it { expect { subject }.to raise_exception StackerBee::ClientError }
@@ -62,7 +67,8 @@ describe StackerBee::Response do
62
67
  { "createprojectresponse" :
63
68
  {"uuidList":[],"errorcode":401,"errortext":"#{message}"} } ],
64
69
  success?: false,
65
- status: 401
70
+ status: 401,
71
+ headers: {}
66
72
  )
67
73
  end
68
74
  let(:client_error) { StackerBee::AuthenticationError.new raw_response }
@@ -76,4 +82,12 @@ describe StackerBee::Response do
76
82
  end
77
83
  end
78
84
 
85
+ context "with an html response" do
86
+ let(:raw_body) { "<html><body>hi</body><html>" }
87
+ let(:content_type) { "text/html" }
88
+
89
+ it "returns the raw html without parsing" do
90
+ expect(response.body).to eq(raw_body)
91
+ end
92
+ end
79
93
  end
data/stacker_bee.gemspec CHANGED
@@ -19,8 +19,11 @@ Gem::Specification.new do |spec|
19
19
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
20
  spec.require_paths = ["lib"]
21
21
 
22
- spec.add_runtime_dependency "faraday", "~> 0.8"
23
- spec.add_runtime_dependency 'multi_json', "~> 1.8"
22
+ spec.add_runtime_dependency "faraday", "~> 0.8", "< 0.9"
23
+ spec.add_runtime_dependency 'multi_json', "~> 1.8"
24
+
25
+ # this is a dependency for FaradayMiddleware::Graylog
26
+ spec.add_runtime_dependency "faraday_middleware", "~> 0.9"
24
27
 
25
28
  spec.add_development_dependency "bundler", "~> 1.3"
26
29
  spec.add_development_dependency "rake", "~> 10.0"
@@ -28,4 +31,8 @@ Gem::Specification.new do |spec|
28
31
  spec.add_development_dependency "webmock", "~> 1.15"
29
32
  spec.add_development_dependency "vcr", "~> 2.6"
30
33
  spec.add_development_dependency "rubocop"
34
+ spec.add_development_dependency "pry"
35
+
36
+ # Release every merge to master as a prerelease
37
+ spec.version = "#{spec.version}-pre#{ENV['TRAVIS_BUILD_NUMBER']}" if ENV['TRAVIS']
31
38
  end