stacker_bee 1.0.1 → 2.0.0.pre.pre164

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.
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