startapp 0.1.6

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 (156) hide show
  1. checksums.yaml +7 -0
  2. data/COPYRIGHT +1 -0
  3. data/LICENSE +11 -0
  4. data/README.md +95 -0
  5. data/Rakefile +6 -0
  6. data/autocomplete/rhc_bash +1672 -0
  7. data/bin/app +37 -0
  8. data/conf/express.conf +8 -0
  9. data/features/assets/deploy.tar.gz +0 -0
  10. data/features/core_feature.rb +191 -0
  11. data/features/deployments_feature.rb +129 -0
  12. data/features/domains_feature.rb +58 -0
  13. data/features/keys_feature.rb +37 -0
  14. data/features/members_feature.rb +166 -0
  15. data/lib/rhc/auth/basic.rb +64 -0
  16. data/lib/rhc/auth/token.rb +102 -0
  17. data/lib/rhc/auth/token_store.rb +53 -0
  18. data/lib/rhc/auth.rb +5 -0
  19. data/lib/rhc/autocomplete.rb +66 -0
  20. data/lib/rhc/autocomplete_templates/bash.erb +39 -0
  21. data/lib/rhc/cartridge_helpers.rb +118 -0
  22. data/lib/rhc/cli.rb +40 -0
  23. data/lib/rhc/command_runner.rb +185 -0
  24. data/lib/rhc/commands/account.rb +25 -0
  25. data/lib/rhc/commands/alias.rb +124 -0
  26. data/lib/rhc/commands/app.rb +726 -0
  27. data/lib/rhc/commands/apps.rb +20 -0
  28. data/lib/rhc/commands/authorization.rb +115 -0
  29. data/lib/rhc/commands/base.rb +174 -0
  30. data/lib/rhc/commands/cartridge.rb +329 -0
  31. data/lib/rhc/commands/clone.rb +66 -0
  32. data/lib/rhc/commands/configure.rb +20 -0
  33. data/lib/rhc/commands/create.rb +100 -0
  34. data/lib/rhc/commands/delete.rb +19 -0
  35. data/lib/rhc/commands/deploy.rb +32 -0
  36. data/lib/rhc/commands/deployment.rb +82 -0
  37. data/lib/rhc/commands/domain.rb +172 -0
  38. data/lib/rhc/commands/env.rb +142 -0
  39. data/lib/rhc/commands/force_stop.rb +17 -0
  40. data/lib/rhc/commands/git_clone.rb +34 -0
  41. data/lib/rhc/commands/logout.rb +51 -0
  42. data/lib/rhc/commands/logs.rb +21 -0
  43. data/lib/rhc/commands/member.rb +148 -0
  44. data/lib/rhc/commands/port_forward.rb +197 -0
  45. data/lib/rhc/commands/reload.rb +17 -0
  46. data/lib/rhc/commands/restart.rb +17 -0
  47. data/lib/rhc/commands/scp.rb +54 -0
  48. data/lib/rhc/commands/server.rb +40 -0
  49. data/lib/rhc/commands/setup.rb +60 -0
  50. data/lib/rhc/commands/show.rb +43 -0
  51. data/lib/rhc/commands/snapshot.rb +137 -0
  52. data/lib/rhc/commands/ssh.rb +51 -0
  53. data/lib/rhc/commands/sshkey.rb +97 -0
  54. data/lib/rhc/commands/start.rb +17 -0
  55. data/lib/rhc/commands/stop.rb +17 -0
  56. data/lib/rhc/commands/tail.rb +47 -0
  57. data/lib/rhc/commands/threaddump.rb +14 -0
  58. data/lib/rhc/commands/tidy.rb +17 -0
  59. data/lib/rhc/commands.rb +396 -0
  60. data/lib/rhc/config.rb +321 -0
  61. data/lib/rhc/context_helper.rb +121 -0
  62. data/lib/rhc/core_ext.rb +202 -0
  63. data/lib/rhc/coverage_helper.rb +33 -0
  64. data/lib/rhc/deployment_helpers.rb +111 -0
  65. data/lib/rhc/exceptions.rb +256 -0
  66. data/lib/rhc/git_helpers.rb +106 -0
  67. data/lib/rhc/help_formatter.rb +55 -0
  68. data/lib/rhc/helpers.rb +481 -0
  69. data/lib/rhc/highline_extensions.rb +479 -0
  70. data/lib/rhc/json.rb +51 -0
  71. data/lib/rhc/output_helpers.rb +260 -0
  72. data/lib/rhc/rest/activation.rb +11 -0
  73. data/lib/rhc/rest/alias.rb +42 -0
  74. data/lib/rhc/rest/api.rb +87 -0
  75. data/lib/rhc/rest/application.rb +348 -0
  76. data/lib/rhc/rest/attributes.rb +36 -0
  77. data/lib/rhc/rest/authorization.rb +8 -0
  78. data/lib/rhc/rest/base.rb +79 -0
  79. data/lib/rhc/rest/cartridge.rb +162 -0
  80. data/lib/rhc/rest/client.rb +650 -0
  81. data/lib/rhc/rest/deployment.rb +18 -0
  82. data/lib/rhc/rest/domain.rb +98 -0
  83. data/lib/rhc/rest/environment_variable.rb +15 -0
  84. data/lib/rhc/rest/gear_group.rb +16 -0
  85. data/lib/rhc/rest/httpclient.rb +145 -0
  86. data/lib/rhc/rest/key.rb +44 -0
  87. data/lib/rhc/rest/membership.rb +105 -0
  88. data/lib/rhc/rest/mock.rb +1042 -0
  89. data/lib/rhc/rest/user.rb +32 -0
  90. data/lib/rhc/rest.rb +148 -0
  91. data/lib/rhc/scp_helpers.rb +27 -0
  92. data/lib/rhc/ssh_helpers.rb +380 -0
  93. data/lib/rhc/tar_gz.rb +51 -0
  94. data/lib/rhc/usage_templates/command_help.erb +51 -0
  95. data/lib/rhc/usage_templates/command_syntax_help.erb +11 -0
  96. data/lib/rhc/usage_templates/help.erb +61 -0
  97. data/lib/rhc/usage_templates/missing_help.erb +1 -0
  98. data/lib/rhc/usage_templates/options_help.erb +12 -0
  99. data/lib/rhc/vendor/okjson.rb +600 -0
  100. data/lib/rhc/vendor/parseconfig.rb +178 -0
  101. data/lib/rhc/vendor/sshkey.rb +253 -0
  102. data/lib/rhc/vendor/zliby.rb +628 -0
  103. data/lib/rhc/version.rb +5 -0
  104. data/lib/rhc/wizard.rb +637 -0
  105. data/lib/rhc.rb +34 -0
  106. data/spec/coverage_helper.rb +82 -0
  107. data/spec/direct_execution_helper.rb +339 -0
  108. data/spec/keys/example.pem +23 -0
  109. data/spec/keys/example_private.pem +27 -0
  110. data/spec/keys/server.pem +19 -0
  111. data/spec/rest_spec_helper.rb +31 -0
  112. data/spec/rhc/assets/cert.crt +22 -0
  113. data/spec/rhc/assets/cert_key_rsa +27 -0
  114. data/spec/rhc/assets/empty.txt +0 -0
  115. data/spec/rhc/assets/env_vars.txt +7 -0
  116. data/spec/rhc/assets/env_vars_2.txt +1 -0
  117. data/spec/rhc/assets/foo.txt +1 -0
  118. data/spec/rhc/assets/targz_corrupted.tar.gz +1 -0
  119. data/spec/rhc/assets/targz_sample.tar.gz +0 -0
  120. data/spec/rhc/auth_spec.rb +442 -0
  121. data/spec/rhc/cli_spec.rb +186 -0
  122. data/spec/rhc/command_spec.rb +435 -0
  123. data/spec/rhc/commands/account_spec.rb +42 -0
  124. data/spec/rhc/commands/alias_spec.rb +333 -0
  125. data/spec/rhc/commands/app_spec.rb +777 -0
  126. data/spec/rhc/commands/apps_spec.rb +39 -0
  127. data/spec/rhc/commands/authorization_spec.rb +157 -0
  128. data/spec/rhc/commands/cartridge_spec.rb +665 -0
  129. data/spec/rhc/commands/clone_spec.rb +41 -0
  130. data/spec/rhc/commands/deployment_spec.rb +327 -0
  131. data/spec/rhc/commands/domain_spec.rb +401 -0
  132. data/spec/rhc/commands/env_spec.rb +493 -0
  133. data/spec/rhc/commands/git_clone_spec.rb +102 -0
  134. data/spec/rhc/commands/logout_spec.rb +86 -0
  135. data/spec/rhc/commands/member_spec.rb +247 -0
  136. data/spec/rhc/commands/port_forward_spec.rb +217 -0
  137. data/spec/rhc/commands/scp_spec.rb +77 -0
  138. data/spec/rhc/commands/server_spec.rb +69 -0
  139. data/spec/rhc/commands/setup_spec.rb +118 -0
  140. data/spec/rhc/commands/snapshot_spec.rb +179 -0
  141. data/spec/rhc/commands/ssh_spec.rb +163 -0
  142. data/spec/rhc/commands/sshkey_spec.rb +188 -0
  143. data/spec/rhc/commands/tail_spec.rb +81 -0
  144. data/spec/rhc/commands/threaddump_spec.rb +84 -0
  145. data/spec/rhc/config_spec.rb +407 -0
  146. data/spec/rhc/helpers_spec.rb +531 -0
  147. data/spec/rhc/highline_extensions_spec.rb +314 -0
  148. data/spec/rhc/json_spec.rb +30 -0
  149. data/spec/rhc/rest_application_spec.rb +258 -0
  150. data/spec/rhc/rest_client_spec.rb +752 -0
  151. data/spec/rhc/rest_spec.rb +740 -0
  152. data/spec/rhc/targz_spec.rb +55 -0
  153. data/spec/rhc/wizard_spec.rb +756 -0
  154. data/spec/spec_helper.rb +575 -0
  155. data/spec/wizard_spec_helper.rb +330 -0
  156. metadata +469 -0
@@ -0,0 +1,740 @@
1
+ require 'spec_helper'
2
+ require 'rest_spec_helper'
3
+ require 'rhc/rest'
4
+
5
+ module MockRestResponse
6
+ attr_accessor :code, :read
7
+ end
8
+
9
+
10
+ describe RHC::Rest::Cartridge do
11
+ context 'with a name' do
12
+ before{ subject.name = 'foo' }
13
+ its(:display_name){ should == 'foo' }
14
+
15
+ context 'when display name is present' do
16
+ before{ subject.display_name = 'bar' }
17
+ its(:display_name){ should == 'bar' }
18
+ end
19
+ end
20
+ end
21
+
22
+ describe RHC::Rest::Domain do
23
+ subject{ RHC::Rest::Client.new(:server => mock_uri) }
24
+ let(:user_auth){ nil }
25
+ let(:domain) { subject.domains.first }
26
+ context "against a 1.2 server" do
27
+ before{ stub_api_v12; stub_one_domain('bar') }
28
+ before do
29
+ stub_api_request(:post, 'broker/rest/domains/bar/applications', false).
30
+ with(:body => {:name => 'foo', :cartridge => 'bar'}.to_json).
31
+ to_return(:status => 201, :body => {:type => 'application', :data => {:id => '1'}}.to_json)
32
+ end
33
+
34
+ it{ domain.add_application('foo', :cartridges => ['bar']).should be_true }
35
+ it{ expect{ domain.add_application('foo', :cartridges => ['bar', 'other']) }.to raise_error(RHC::Rest::MultipleCartridgeCreationNotSupported) }
36
+ it{ expect{ domain.add_application('foo', :initial_git_url => 'a_url') }.to raise_error(RHC::Rest::InitialGitUrlNotSupported) }
37
+ it{ expect{ domain.add_application('foo', :cartridges => [{:url => 'a_url'}]) }.to raise_error(RHC::Rest::DownloadingCartridgesNotSupported) }
38
+ it{ domain.add_application('foo', :cartridges => 'bar').should be_true }
39
+ it{ domain.add_application('foo', :cartridge => 'bar').should be_true }
40
+ it{ domain.add_application('foo', :cartridge => ['bar']).should be_true }
41
+ end
42
+ context "against a server that supports initial git urls and downloaded carts" do
43
+ let(:cartridges){ ['bar'] }
44
+ before{ stub_api; stub_one_domain('bar', [{:name => 'initial_git_url'},{:name => 'cartridges[][url]'}]) }
45
+ before do
46
+ stub_api_request(:post, 'broker/rest/domains/bar/applications', false).
47
+ with(:body => {:name => 'foo', :cartridges => cartridges}.to_json).
48
+ to_return(:status => 201, :body => {:type => 'application', :data => {:id => '1'}}.to_json)
49
+ end
50
+
51
+ it{ domain.add_application('foo', :cartridges => ['bar']).should be_true }
52
+ it{ domain.add_application('foo', :cartridges => 'bar').should be_true }
53
+ it{ domain.add_application('foo', :cartridge => 'bar').should be_true }
54
+ it{ domain.add_application('foo', :cartridge => ['bar']).should be_true }
55
+
56
+ context "with multiple cartridges" do
57
+ let(:cartridges){ ['bar'] }
58
+ it{ domain.add_application('foo', :cartridges => cartridges).should be_true }
59
+ it{ domain.add_application('foo', :cartridge => cartridges).should be_true }
60
+ end
61
+
62
+ context "with a url" do
63
+ before do
64
+ stub_api_request(:post, 'broker/rest/domains/bar/applications', false).
65
+ with(:body => {:name => 'foo', :initial_git_url => 'a_url', :cartridges => []}.to_json).
66
+ to_return(:status => 201, :body => {:type => 'application', :data => {:id => '1'}}.to_json)
67
+ end
68
+ it{ domain.add_application('foo', :initial_git_url => 'a_url').should be_true }
69
+ end
70
+
71
+ context "with a cartridge url" do
72
+ before do
73
+ stub_api_request(:post, 'broker/rest/domains/bar/applications', false).
74
+ with(:body => {:name => 'foo', :cartridges => [{:url => 'a_url'}]}.to_json).
75
+ to_return(:status => 201, :body => {:type => 'application', :data => {:id => '1'}}.to_json)
76
+ end
77
+ it{ domain.add_application('foo', :cartridges => [{:url => 'a_url'}]).should be_true }
78
+ it{ domain.add_application('foo', :cartridge => RHC::Rest::Cartridge.for_url('a_url')).should be_true }
79
+ it{ domain.add_application('foo', :cartridge => [{'url' => 'a_url'}]).should be_true }
80
+ end
81
+ end
82
+ end
83
+
84
+ module RHC
85
+
86
+ describe Rest do
87
+ subject{ RHC::Rest::Client.new }
88
+
89
+ describe "#default_verify_callback" do
90
+ def invoked_with(is_ok, ctx)
91
+ subject.send(:default_verify_callback).call(is_ok, ctx)
92
+ end
93
+ it{ invoked_with(true, nil).should be_true }
94
+
95
+ it{ expect{ invoked_with(false, nil) }.to raise_error(NoMethodError) }
96
+
97
+ context "with a self signed cert" do
98
+ it{ invoked_with(false, double(:current_cert => double(:issuer => '1', :subject => double(:cmp => 0)))).should be_false }
99
+ after{ subject.send(:self_signed?).should be_true }
100
+ end
101
+
102
+ context "with an intermediate signed cert" do
103
+ it{ invoked_with(false, double(:current_cert => double(:issuer => '2', :subject => double(:cmp => 1)), :error => 1, :error_string => 'a')).should be_false }
104
+ after{ subject.send(:self_signed?).should be_false }
105
+ end
106
+
107
+ end
108
+
109
+ # parse_response function
110
+ describe "#parse_response" do
111
+ context "with no response type" do
112
+ let(:object) {{ :links => { :foo => 'bar' } }}
113
+ it "deserializes to the encapsulated data" do
114
+ json_response = { :data => object }.to_json
115
+ subject.send(:parse_response, json_response).should have_same_attributes_as(object)
116
+ end
117
+ end
118
+
119
+ context "with an application" do
120
+ let(:object) {{
121
+ :domain_id => 'test_domain',
122
+ :name => 'test_app',
123
+ :creation_time => '0000-00-00 00:00:00 -0000',
124
+ :uuid => 'test_app_1234',
125
+ :aliases => ['app_alias_1', 'app_alias_2'],
126
+ :server_identity => 'test_server',
127
+ :links => { :foo => 'bar' }
128
+ }}
129
+ it "deserializes to an application" do
130
+ json_response = { :type => 'application', :data => object, :messages => [{'text' => 'test message'}]}.to_json
131
+ app_obj = RHC::Rest::Application.new(object)
132
+ subject.send(:parse_response, json_response).should have_same_attributes_as(app_obj)
133
+ end
134
+ end
135
+
136
+ context "with two applications" do
137
+ let(:object) {[{ :domain_id => 'test_domain',
138
+ :name => 'test_app',
139
+ :creation_time => '0000-00-00 00:00:00 -0000',
140
+ :uuid => 'test_app_1234',
141
+ :aliases => ['app_alias_1', 'app_alias_2'],
142
+ :server_identity => 'test_server',
143
+ :links => { :foo => 'bar' }
144
+ },
145
+ { :domain_id => 'test_domain_2',
146
+ :name => 'test_app_2',
147
+ :creation_time => '0000-00-00 00:00:00 -0000',
148
+ :uuid => 'test_app_2_1234',
149
+ :aliases => ['app_alias_3', 'app_alias_4'],
150
+ :server_identity => 'test_server_2',
151
+ :links => { :foo => 'bar' }
152
+ }]
153
+ }
154
+ it "deserializes to a list of applications" do
155
+ json_response = { :type => 'applications', :data => object }.to_json
156
+ app_obj_1 = RHC::Rest::Application.new(object[0])
157
+ app_obj_2 = RHC::Rest::Application.new(object[1])
158
+ subject.send(:parse_response, json_response).length.should equal(2)
159
+ subject.send(:parse_response, json_response)[0].should have_same_attributes_as(app_obj_1)
160
+ subject.send(:parse_response, json_response)[1].should have_same_attributes_as(app_obj_2)
161
+ end
162
+ end
163
+
164
+ context "with a cartridge" do
165
+ let(:object) {{
166
+ :name => 'test_cartridge',
167
+ :type => 'test_cartridge_type',
168
+ :links => { :foo => 'bar' }
169
+ }}
170
+
171
+ it "deserializes to a cartridge" do
172
+ json_response = { :type => 'cartridge', :data => object }.to_json
173
+ cart_obj = RHC::Rest::Cartridge.new(object)
174
+ subject.send(:parse_response, json_response).should have_same_attributes_as(cart_obj)
175
+ end
176
+ end
177
+
178
+ context "with two cartridges" do
179
+ let(:object) {[{ :name => 'test_cartridge',
180
+ :type => 'test_cartridge_type',
181
+ :links => { :foo => 'bar' }
182
+ },
183
+ { :name => 'test_cartridge_2',
184
+ :type => 'test_cartridge_type_2',
185
+ :links => { :foo => 'bar' }
186
+ }
187
+ ]}
188
+
189
+ it "deserializes to a list of cartridges" do
190
+ json_response = { :type => 'cartridges', :data => object }.to_json
191
+ cart_obj_1 = RHC::Rest::Cartridge.new(object[0])
192
+ cart_obj_2 = RHC::Rest::Cartridge.new(object[1])
193
+ subject.send(:parse_response, json_response).length.should equal(2)
194
+ subject.send(:parse_response, json_response)[0].should have_same_attributes_as(cart_obj_1)
195
+ subject.send(:parse_response, json_response)[1].should have_same_attributes_as(cart_obj_2)
196
+ end
197
+ end
198
+
199
+ context "with a domain" do
200
+ let(:object) {{
201
+ :id => 'test_domain',
202
+ :links => { :foo => 'bar' }
203
+ }}
204
+
205
+ it "deserializes to a domain" do
206
+ json_response = { :type => 'domain', :data => object }.to_json
207
+ dom_obj = RHC::Rest::Domain.new(object)
208
+ subject.send(:parse_response, json_response).should have_same_attributes_as(dom_obj)
209
+ end
210
+ end
211
+
212
+ context "with two domains" do
213
+ let(:object) {[{ :id => 'test_domain',
214
+ :links => { :foo => 'bar' }
215
+ },
216
+ { :id => 'test_domain_2',
217
+ :links => { :foo => 'bar' }
218
+ }
219
+ ]}
220
+
221
+ it "deserializes to a list of domains" do
222
+ json_response = { :type => 'domains', :data => object }.to_json
223
+ dom_obj_1 = RHC::Rest::Domain.new(object[0])
224
+ dom_obj_2 = RHC::Rest::Domain.new(object[1])
225
+ subject.send(:parse_response, json_response).length.should equal(2)
226
+ subject.send(:parse_response, json_response)[0].should have_same_attributes_as(dom_obj_1)
227
+ subject.send(:parse_response, json_response)[1].should have_same_attributes_as(dom_obj_2)
228
+ end
229
+ end
230
+
231
+ context "with a key" do
232
+ let(:object) {{
233
+ :name => 'test_key',
234
+ :type => 'test_key_type',
235
+ :content => 'test_key_content',
236
+ :links => { :foo => 'bar' }
237
+ }}
238
+
239
+ it "deserializes to a key" do
240
+ json_response = { :type => 'key', :data => object }.to_json
241
+ key_obj = RHC::Rest::Key.new(object)
242
+ subject.send(:parse_response, json_response).should have_same_attributes_as(key_obj)
243
+ end
244
+ end
245
+
246
+ context "with two keys" do
247
+ let(:object) {[{ :name => 'test_key',
248
+ :type => 'test_key_type',
249
+ :content => 'test_key_content',
250
+ :links => { :foo => 'bar' }
251
+ },
252
+ { :name => 'test_key_2',
253
+ :type => 'test_key_type_2',
254
+ :content => 'test_key_content_2',
255
+ :links => { :foo => 'bar' }
256
+ }
257
+ ]}
258
+
259
+ it "deserializes to a list of keys" do
260
+ json_response = { :type => 'keys', :data => object }.to_json
261
+ key_obj_1 = RHC::Rest::Key.new(object[0])
262
+ key_obj_2 = RHC::Rest::Key.new(object[1])
263
+ subject.send(:parse_response, json_response).length.should equal(2)
264
+ subject.send(:parse_response, json_response)[0].should have_same_attributes_as(key_obj_1)
265
+ subject.send(:parse_response, json_response)[1].should have_same_attributes_as(key_obj_2)
266
+ end
267
+ end
268
+
269
+ context "with a user" do
270
+ let(:object) {{
271
+ :login => 'test_user',
272
+ :links => { :foo => 'bar' }
273
+ }}
274
+
275
+ it "deserializes to a user" do
276
+ json_response = { :type => 'user', :data => object }.to_json
277
+ user_obj = RHC::Rest::User.new(object)
278
+ subject.send(:parse_response, json_response).should have_same_attributes_as(user_obj)
279
+ end
280
+ end
281
+
282
+ context "with result messages" do
283
+ let(:object) do
284
+ {
285
+ :login => 'test_user',
286
+ :links => { :foo => 'bar' }
287
+ }
288
+ end
289
+ let(:messages) do
290
+ [
291
+ {:field => nil, :severity => 'info', :text => 'Nil field'},
292
+ {:field => 'result', :severity => 'info', :text => 'Result field'}, # < 1.5 API
293
+ {:field => 'base', :severity => 'result', :text => 'Result severity'}, # >= 1.5 API
294
+ {:field => 'base', :severity => 'info', :text => 'Non-result message' },
295
+ {:field => 'result', :severity => 'debug', :text => 'Debug message' },
296
+ {:field => 'base', :severity => 'warning', :text => 'Warning message' },
297
+ ]
298
+ end
299
+ let(:response) do
300
+ { :type => 'user', :data => object, :messages => messages }.to_json
301
+ end
302
+
303
+ it "copies all non-warning and non-info messages to the object" do
304
+ subject.stub(:debug?).and_return(false)
305
+ subject.send(:parse_response, response).messages.should == ['Result field', 'Result severity']
306
+ end
307
+
308
+ it "includes debug and info when debug true" do
309
+ subject.stub(:debug?).and_return(true)
310
+ subject.send(:parse_response, response).messages.should == ['Nil field', 'Result field', 'Result severity', 'Non-result message', 'Debug message']
311
+ end
312
+ end
313
+ end
314
+
315
+ describe "#new_request" do
316
+ it{ subject.send(:new_request, :api_version => 2.0).last[4]['accept'].should == 'application/json;version=2.0' }
317
+ it{ subject.send(:new_request, :headers => {:accept => :xml}, :api_version => 2.0).last[4]['accept'].should == 'application/xml;version=2.0' }
318
+ context "with the default api version" do
319
+ before{ subject.should_receive(:current_api_version).and_return('1.0') }
320
+ it{ subject.send(:new_request, {}).last[4]['accept'].should == 'application/json;version=1.0' }
321
+ end
322
+ end
323
+
324
+ # request function
325
+ describe "#request" do
326
+ let(:response){ lambda { subject.request(request) } }
327
+ let(:request){ {:url => mock_href, :method => method, :headers => {:accept => :json} } }
328
+ let(:method){ :get }
329
+
330
+ if HTTPClient::SSLConfig.method_defined? :ssl_version
331
+ context "when an invalid ssl version is passed, OpenSSL should reject us" do
332
+ let(:request){ {:url => "https://openshift.redhat.com", :method => :get, :ssl_version => :SSLv3_server} }
333
+ before{ WebMock.allow_net_connect! }
334
+ it("fails to call openssl"){ response.should raise_error(RHC::Rest::SSLConnectionFailed, /called a function you should not call/) }
335
+ after{ WebMock.disable_net_connect! }
336
+ end
337
+ end
338
+
339
+ context "with a successful request" do
340
+ let(:object) {{
341
+ :type => 'domain',
342
+ :data => {
343
+ :id => 'test_domain',
344
+ :links => { :foo => 'bar' }
345
+ }}}
346
+ before do
347
+ return_data = {
348
+ :body => object.to_json,
349
+ :status => 200,
350
+ :headers => { 'Set-Cookie' => "rh_sso=test_ssh_cookie" }
351
+ }
352
+ stub_request(:get, mock_href).with{ |req| req.headers['Accept'].should == 'application/json;version=1.0' }.to_return(return_data)
353
+ end
354
+
355
+ it "sends the response to be deserialized" do
356
+ dom_obj = RHC::Rest::Domain.new(object)
357
+ subject.request(request.merge(:payload => {}, :api_version => '1.0', :timeout => 300)).should have_same_attributes_as(dom_obj)
358
+ end
359
+ end
360
+
361
+ context "with a nil response" do
362
+ before do
363
+ return_data = {
364
+ :body => nil,
365
+ :status => 200,
366
+ :headers => { 'Set-Cookie' => "rh_sso=test_ssh_cookie" }
367
+ }
368
+ stub_request(:get, mock_href).to_return(return_data)
369
+ end
370
+ it "throws an error" do
371
+ response.should raise_error(RHC::Rest::ConnectionException, 'An unexpected error occured: unexpected nil')
372
+ end
373
+ end
374
+
375
+ context "with a 204 (No Content) response" do
376
+ before do
377
+ return_data = {
378
+ :body => nil,
379
+ :status => 204,
380
+ :headers => { 'Set-Cookie' => "rh_sso=test_ssh_cookie" }
381
+ }
382
+ stub_request(:get, mock_href).to_return(return_data)
383
+ end
384
+ it "quietly exits" do
385
+ response.call.should equal(nil)
386
+ end
387
+ end
388
+
389
+ context "with a 502 (Bad Gateway) error" do
390
+ before{ stub_request(method, mock_href).to_return(:status => 502) }
391
+
392
+ context "on a GET request" do
393
+ it("repeats the call"){ response.should raise_error(RHC::Rest::ConnectionException, /communicating with the server.*temporary/i) }
394
+ after{ WebMock.should have_requested(method, mock_href).twice }
395
+ end
396
+
397
+ context "on a POST request" do
398
+ let(:method){ :post }
399
+
400
+ it("does not repeat the call"){ response.should raise_error(RHC::Rest::ConnectionException, /communicating with the server.*temporary/i) }
401
+ after{ WebMock.should have_requested(method, mock_href).once }
402
+ end
403
+ end
404
+
405
+ context "with a GET request" do
406
+ it "serializes payload as query parameters" do
407
+ stub_request(:get, mock_href).with(:query => {:test => '1', :bar => '2'}).to_return(:status => 204)
408
+ subject.request(request.merge(:payload => {:test => '1', :bar => '2'})).should be_nil
409
+ end
410
+ end
411
+ context "with a POST request" do
412
+ let(:method){ :post }
413
+ it "serializes payload as urlencoded body parameters" do
414
+ stub_request(method, mock_href).
415
+ with(:headers => {:accept => 'application/json', :content_type => 'application/json'},
416
+ :body => {:test => '1', :bar => 2}.to_json).
417
+ to_return(:status => 204)
418
+ subject.request(request.merge(:payload => {:test => '1', :bar => 2})).should be_nil
419
+ end
420
+ end
421
+
422
+ context "with a request timeout" do
423
+ before{ stub_request(:get, mock_href).to_timeout }
424
+ it{ response.should raise_error(RHC::Rest::TimeoutException, /Connection to server timed out. It is possible/) }
425
+ end
426
+
427
+ context "with a receive timeout" do
428
+ before{ stub_request(:get, mock_href).to_raise(HTTPClient::ReceiveTimeoutError) }
429
+ it{ response.should raise_error{ |e| e.on_receive?.should be_true } }
430
+ end
431
+
432
+ context "with a send timeout" do
433
+ before{ stub_request(:get, mock_href).to_raise(HTTPClient::SendTimeoutError) }
434
+ it{ response.should raise_error{ |e| e.on_send?.should be_true } }
435
+ end
436
+
437
+ context "with a connect timeout" do
438
+ before{ stub_request(:get, mock_href).to_raise(HTTPClient::ConnectTimeoutError) }
439
+ it{ response.should raise_error{ |e| e.on_connect?.should be_true } }
440
+ end
441
+
442
+ context "with a reset server connection" do
443
+ before{ stub_request(:get, mock_href).to_raise(Errno::ECONNRESET.new('Lost Server Connection')) }
444
+ it{ response.should raise_error(RHC::Rest::ConnectionException, /The server has closed the connection unexpectedly \(Connection reset by peer - Lost Server Connection\)/) }
445
+ end
446
+
447
+
448
+ context "with a broken server connection" do
449
+ before{ stub_request(:get, mock_href).to_raise(EOFError.new('Lost Server Connection')) }
450
+ it{ response.should raise_error(RHC::Rest::ConnectionException, 'Connection to server got interrupted: Lost Server Connection') }
451
+ end
452
+
453
+ #FIXME: the type of this exception should be a subclass of CertificateValidationFailed
454
+ context "with a potentially missing cert store" do
455
+ before{ stub_request(:get, mock_href).to_raise(OpenSSL::SSL::SSLError.new('unable to get local issuer certificate')) }
456
+ it{ response.should raise_error(RHC::Rest::SSLConnectionFailed, /You may need to specify your system CA certificate file/) }
457
+ end
458
+
459
+ context "with a self-signed SSL certificate" do
460
+ before do
461
+ subject.should_receive(:self_signed?).and_return(true)
462
+ stub_request(:get, mock_href).to_raise(OpenSSL::SSL::SSLError.new('Unverified SSL Certificate'))
463
+ end
464
+ it{ response.should raise_error(RHC::Rest::CertificateVerificationFailed, /The server is using a self-signed certificate/) }
465
+ end
466
+
467
+ context "with an unverified SSL certificate" do
468
+ before{ stub_request(:get, mock_href).to_raise(OpenSSL::SSL::SSLError.new('self signed certificate')) }
469
+ it{ response.should raise_error(RHC::Rest::CertificateVerificationFailed, /The server is using a self-signed certificate/) }
470
+ end
471
+
472
+ context "with an failed SSL certificate verification" do
473
+ before{ stub_request(:get, mock_href).to_raise(OpenSSL::SSL::SSLError.new('certificate verify failed')) }
474
+ it{ response.should raise_error(RHC::Rest::CertificateVerificationFailed, /The server's certificate could not be verified.*test\.domain\.com/) }
475
+ end
476
+
477
+ context "with a socket error" do
478
+ before{ stub_request(:get, mock_href).to_raise(SocketError) }
479
+ it{ response.should raise_error(RHC::Rest::ConnectionException, /unable to connect to the server/i) }
480
+ end
481
+
482
+ context "with an SSL connection error" do
483
+ before{ stub_request(:get, mock_href).to_raise(OpenSSL::SSL::SSLError) }
484
+ it{ response.should raise_error(RHC::Rest::SSLConnectionFailed, /a secure connection could not be established/i) }
485
+ end
486
+
487
+ context "with an SSL certificate error" do
488
+ before{ stub_request(:get, mock_href).to_raise(OpenSSL::SSL::SSLError.new('certificate verify failed')) }
489
+ it{ response.should raise_error(RHC::Rest::CertificateVerificationFailed, /the server's certificate could not be verified/i) }
490
+ end
491
+
492
+ context "with an SSL version exception" do
493
+ before{ stub_request(:get, mock_href).to_raise(OpenSSL::SSL::SSLError.new('SSL_connect returned=1 errno=0 state=SSLv2/v3 read server hello A')) }
494
+ it{ response.should raise_error(RHC::Rest::SSLVersionRejected, /connection attempt with an older ssl protocol/i) }
495
+ end
496
+
497
+ context "with a generic exception error" do
498
+ before{ stub_request(:get, mock_href).to_raise(Exception.new('Generic Error')) }
499
+ it{ response.should raise_error(RHC::Rest::ConnectionException, "An unexpected error occured: Generic Error") }
500
+ end
501
+
502
+ context "with an unauthorized request" do
503
+ before do
504
+ return_data = {
505
+ :body => nil,
506
+ :status => 401,
507
+ :headers => { 'Set-Cookie' => "rh_sso=test_ssh_cookie" }
508
+ }
509
+ stub_request(:get, mock_href).to_return(return_data)
510
+ end
511
+ it("raises not authenticated"){ response.should raise_error(RHC::Rest::UnAuthorizedException, 'Not authenticated') }
512
+
513
+ context "when auth will retry forever" do
514
+ let(:auth){ double('auth', :to_request => nil) }
515
+ before{ subject.stub(:auth).and_return(auth); auth.should_receive(:retry_auth?).exactly(4).times.and_return(true) }
516
+ it("raises not authenticated"){ response.should raise_error(RHC::Rest::UnAuthorizedException, 'Not authenticated') }
517
+ after{ WebMock.should have_requested(:get, mock_href).times(RHC::Rest::Client::MAX_RETRIES) }
518
+ end
519
+ end
520
+ end
521
+
522
+ # handle_error! function
523
+ describe "#handle_error!" do
524
+ let(:json){ nil }
525
+ let(:body){ "<html><body>Something failed</body></html>" }
526
+ let(:code){ nil }
527
+ let(:client){ HTTPClient.new(:proxy => proxy) }
528
+ let(:url){ "http://fake.url" }
529
+ let(:proxy){ nil }
530
+ def response
531
+ double(:status => code, :content => json ? RHC::Json.encode(json) : body)
532
+ end
533
+ let(:method) { lambda{ subject.send(:handle_error!, response, url, client) } }
534
+
535
+ context "with a 400 response" do
536
+ let(:code){ 400 }
537
+
538
+ it{ method.should raise_error(RHC::Rest::ServerErrorException) }
539
+
540
+ context "with a formatted JSON response" do
541
+ let(:json){ {:messages => [{ :severity => 'error', :text => 'mock error message' }] } }
542
+ it "raises a client error" do
543
+ method.should raise_error(RHC::Rest::ClientErrorException, 'mock error message')
544
+ end
545
+ end
546
+ end
547
+
548
+ context "with a 401 response" do
549
+ let(:code){ 401 }
550
+ let(:json){ {} }
551
+ it "raises an 'unauthorized exception' error" do
552
+ method.should raise_error(RHC::Rest::UnAuthorizedException, 'Not authenticated')
553
+ end
554
+ end
555
+
556
+ context "with a 403 response" do
557
+ let(:code){ 403 }
558
+
559
+ it "raises a request denied error" do
560
+ method.should raise_error(RHC::Rest::RequestDeniedException)
561
+ end
562
+
563
+ context "with a formatted JSON response" do
564
+ let(:json){ { :messages => [{ :severity => 'error', :text => 'mock error message' }] } }
565
+ it "raises a 'request denied' error" do
566
+ method.should raise_error(RHC::Rest::RequestDeniedException, 'mock error message')
567
+ end
568
+ end
569
+ end
570
+
571
+ context "with a 404 response" do
572
+ let(:code){ 404 }
573
+
574
+ it "raises a Not Found error" do
575
+ method.should raise_error(RHC::Rest::ResourceNotFoundException)
576
+ end
577
+
578
+ context "with a formatted JSON response" do
579
+ let(:json){ { :messages => [{ :severity => 'error', :text => 'mock error message' }] } }
580
+ it "raises a 'resource not found' error" do
581
+ method.should raise_error(RHC::Rest::ResourceNotFoundException, 'mock error message')
582
+ end
583
+ end
584
+ end
585
+
586
+ context "with a 409 response" do
587
+ let(:code){ 409 }
588
+
589
+ it "raises a generic server error" do
590
+ method.should raise_error(RHC::Rest::ServerErrorException)
591
+ end
592
+
593
+ context "with a formatted JSON response" do
594
+ let(:json){ { :messages => [{ :severity => 'error', :text => 'mock error message' }] } }
595
+ it "raises a validation error" do
596
+ method.should raise_error(RHC::Rest::ValidationException, 'mock error message')
597
+ end
598
+ end
599
+ context "with multiple JSON messages" do
600
+ let(:json){ { :messages => [{ :field => 'error', :text => 'mock error message 1' },
601
+ { :field => 'error', :text => 'mock error message 2' }] } }
602
+ it "raises a validation error with concatenated messages" do
603
+ method.should raise_error(RHC::Rest::ValidationException, "The operation did not complete successfully, but the server returned additional information:\n* mock error message 1\n* mock error message 2")
604
+ end
605
+ end
606
+ context "with multiple errors" do
607
+ let(:json){ { :messages => [
608
+ { :severity => 'error', :field => 'error', :text => 'mock 1' },
609
+ { :severity => 'error', :text => 'mock 2' },
610
+ ] } }
611
+ it "raises a validation error with concatenated messages" do
612
+ method.should raise_error(RHC::Rest::ValidationException, "The server reported multiple errors:\n* mock 1\n* mock 2")
613
+ end
614
+ end
615
+ context "with multiple messages and one error" do
616
+ let(:json){ { :messages => [
617
+ { :field => 'error', :text => 'mock 1' },
618
+ { :text => 'mock 2' },
619
+ { :severity => 'error', :text => 'mock 3' },
620
+ ] } }
621
+ it "raises a validation error with concatenated messages" do
622
+ method.should raise_error(RHC::Rest::ValidationException, "mock 3")
623
+ end
624
+ end
625
+ context "with an empty JSON response" do
626
+ let(:json){ {} }
627
+ it "raises a validation error" do
628
+ method.should raise_error(RHC::Rest::ServerErrorException)
629
+ end
630
+ end
631
+ end
632
+
633
+ context "with a 422 response" do
634
+ let(:code){ 422 }
635
+
636
+ it "raises a generic server error" do
637
+ method.should raise_error(RHC::Rest::ServerErrorException)
638
+ end
639
+
640
+ context "with a single JSON message" do
641
+ let(:json){ { :messages => [{ :severity => 'error', :text => 'mock error message' }] } }
642
+ it "raises a validation error" do
643
+ method.should raise_error(RHC::Rest::ValidationException, 'mock error message')
644
+ end
645
+ end
646
+
647
+ context "with an empty JSON response" do
648
+ let(:json){ {} }
649
+ it "raises a validation error" do
650
+ method.should raise_error(RHC::Rest::ServerErrorException)
651
+ end
652
+ end
653
+
654
+ context "with multiple JSON messages" do
655
+ let(:json){ { :messages => [{ :field => 'error', :text => 'mock error message 1' },
656
+ { :field => 'error', :text => 'mock error message 2' }] } }
657
+ it "raises a validation error with concatenated messages" do
658
+ method.should raise_error(RHC::Rest::ValidationException, "The operation did not complete successfully, but the server returned additional information:\n* mock error message 1\n* mock error message 2")
659
+ end
660
+ end
661
+ context "with multiple errors" do
662
+ let(:json){ { :messages => [
663
+ { :severity => 'error', :field => 'error', :text => 'mock 1' },
664
+ { :severity => 'error', :text => 'mock 2' },
665
+ ] } }
666
+ it "raises a validation error with concatenated messages" do
667
+ method.should raise_error(RHC::Rest::ValidationException, "The server reported multiple errors:\n* mock 1\n* mock 2")
668
+ end
669
+ end
670
+ context "with multiple messages and one error" do
671
+ let(:json){ { :messages => [
672
+ { :field => 'error', :text => 'mock 1' },
673
+ { :text => 'mock 2' },
674
+ { :severity => 'error', :text => 'mock 3' },
675
+ ] } }
676
+ it "raises a validation error with concatenated messages" do
677
+ method.should raise_error(RHC::Rest::ValidationException, "mock 3")
678
+ end
679
+ end
680
+ end
681
+
682
+ context "with a 500 response" do
683
+ let(:code){ 500 }
684
+
685
+ it "raises a generic server error" do
686
+ method.should raise_error(RHC::Rest::ServerErrorException, /server did not respond correctly.*verify that you can access the OpenShift server/i)
687
+ end
688
+
689
+ context "when proxy is set" do
690
+ let(:proxy) { 'http://foo.com' }
691
+ it "raises a generic server error with the proxy URL" do
692
+ method.should raise_error(RHC::Rest::ServerErrorException, /foo\.com/i)
693
+ end
694
+ end
695
+
696
+ context "when request url is present" do
697
+ let(:url){ 'foo.bar' }
698
+ it "raises a generic server error with the request URL" do
699
+ method.should raise_error(RHC::Rest::ServerErrorException, /foo\.bar/i)
700
+ end
701
+ end
702
+
703
+ context "with a formatted JSON response" do
704
+ let(:json){ { :messages => [{ :severity => 'error', :text => 'mock error message' }] } }
705
+ it "raises a server error" do
706
+ method.should raise_error(RHC::Rest::ServerErrorException, 'mock error message')
707
+ end
708
+ end
709
+ end
710
+
711
+ context "with a 503 response" do
712
+ let(:code){ 503 }
713
+
714
+ it "raises a 'service unavailable' error" do
715
+ method.should raise_error(RHC::Rest::ServiceUnavailableException)
716
+ end
717
+
718
+ context "with a formatted JSON response" do
719
+ let(:json){ { :messages => [{ :severity => 'error', :text => 'mock error message' }] } }
720
+ it "raises a 'service unavailable' error" do
721
+ method.should raise_error(RHC::Rest::ServiceUnavailableException, 'mock error message')
722
+ end
723
+ end
724
+ end
725
+
726
+ context "with an unhandled response code" do
727
+ let(:code){ 999 }
728
+
729
+ it{ method.should raise_error(RHC::Rest::ServerErrorException) }
730
+
731
+ context "with a formatted JSON response" do
732
+ let(:json){ { :messages => [{ :severity => 'error', :text => 'mock error message' }] } }
733
+ it "raises a resource access error" do
734
+ method.should raise_error(RHC::Rest::ServerErrorException, 'mock error message')
735
+ end
736
+ end
737
+ end
738
+ end
739
+ end
740
+ end