pbox 1.17.2

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 (139) hide show
  1. checksums.yaml +7 -0
  2. data/COPYRIGHT +1 -0
  3. data/LICENSE +11 -0
  4. data/README.md +40 -0
  5. data/Rakefile +6 -0
  6. data/autocomplete/pbox_bash +1639 -0
  7. data/bin/pbox +37 -0
  8. data/conf/protonbox.conf +8 -0
  9. data/features/assets/deploy.tar.gz +0 -0
  10. data/features/core_feature.rb +178 -0
  11. data/features/deployments_feature.rb +127 -0
  12. data/features/domains_feature.rb +49 -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 +186 -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 +701 -0
  27. data/lib/rhc/commands/apps.rb +20 -0
  28. data/lib/rhc/commands/authorization.rb +96 -0
  29. data/lib/rhc/commands/base.rb +174 -0
  30. data/lib/rhc/commands/cartridge.rb +326 -0
  31. data/lib/rhc/commands/deployment.rb +82 -0
  32. data/lib/rhc/commands/domain.rb +167 -0
  33. data/lib/rhc/commands/env.rb +142 -0
  34. data/lib/rhc/commands/git_clone.rb +29 -0
  35. data/lib/rhc/commands/logout.rb +51 -0
  36. data/lib/rhc/commands/member.rb +148 -0
  37. data/lib/rhc/commands/port_forward.rb +197 -0
  38. data/lib/rhc/commands/server.rb +40 -0
  39. data/lib/rhc/commands/setup.rb +60 -0
  40. data/lib/rhc/commands/snapshot.rb +137 -0
  41. data/lib/rhc/commands/ssh.rb +51 -0
  42. data/lib/rhc/commands/sshkey.rb +97 -0
  43. data/lib/rhc/commands/tail.rb +47 -0
  44. data/lib/rhc/commands/threaddump.rb +14 -0
  45. data/lib/rhc/commands.rb +396 -0
  46. data/lib/rhc/config.rb +320 -0
  47. data/lib/rhc/context_helper.rb +121 -0
  48. data/lib/rhc/core_ext.rb +202 -0
  49. data/lib/rhc/coverage_helper.rb +33 -0
  50. data/lib/rhc/deployment_helpers.rb +88 -0
  51. data/lib/rhc/exceptions.rb +232 -0
  52. data/lib/rhc/git_helpers.rb +91 -0
  53. data/lib/rhc/help_formatter.rb +55 -0
  54. data/lib/rhc/helpers.rb +477 -0
  55. data/lib/rhc/highline_extensions.rb +479 -0
  56. data/lib/rhc/json.rb +51 -0
  57. data/lib/rhc/output_helpers.rb +260 -0
  58. data/lib/rhc/rest/activation.rb +11 -0
  59. data/lib/rhc/rest/alias.rb +42 -0
  60. data/lib/rhc/rest/api.rb +87 -0
  61. data/lib/rhc/rest/application.rb +332 -0
  62. data/lib/rhc/rest/attributes.rb +36 -0
  63. data/lib/rhc/rest/authorization.rb +8 -0
  64. data/lib/rhc/rest/base.rb +79 -0
  65. data/lib/rhc/rest/cartridge.rb +154 -0
  66. data/lib/rhc/rest/client.rb +650 -0
  67. data/lib/rhc/rest/deployment.rb +18 -0
  68. data/lib/rhc/rest/domain.rb +98 -0
  69. data/lib/rhc/rest/environment_variable.rb +15 -0
  70. data/lib/rhc/rest/gear_group.rb +16 -0
  71. data/lib/rhc/rest/httpclient.rb +145 -0
  72. data/lib/rhc/rest/key.rb +44 -0
  73. data/lib/rhc/rest/membership.rb +105 -0
  74. data/lib/rhc/rest/mock.rb +1024 -0
  75. data/lib/rhc/rest/user.rb +32 -0
  76. data/lib/rhc/rest.rb +148 -0
  77. data/lib/rhc/ssh_helpers.rb +378 -0
  78. data/lib/rhc/tar_gz.rb +51 -0
  79. data/lib/rhc/usage_templates/command_help.erb +51 -0
  80. data/lib/rhc/usage_templates/command_syntax_help.erb +11 -0
  81. data/lib/rhc/usage_templates/help.erb +35 -0
  82. data/lib/rhc/usage_templates/missing_help.erb +1 -0
  83. data/lib/rhc/usage_templates/options_help.erb +12 -0
  84. data/lib/rhc/vendor/okjson.rb +600 -0
  85. data/lib/rhc/vendor/parseconfig.rb +178 -0
  86. data/lib/rhc/vendor/sshkey.rb +253 -0
  87. data/lib/rhc/vendor/zliby.rb +628 -0
  88. data/lib/rhc/version.rb +5 -0
  89. data/lib/rhc/wizard.rb +633 -0
  90. data/lib/rhc.rb +34 -0
  91. data/spec/coverage_helper.rb +89 -0
  92. data/spec/direct_execution_helper.rb +338 -0
  93. data/spec/keys/example.pem +23 -0
  94. data/spec/keys/example_private.pem +27 -0
  95. data/spec/keys/server.pem +19 -0
  96. data/spec/rest_spec_helper.rb +31 -0
  97. data/spec/rhc/assets/cert.crt +22 -0
  98. data/spec/rhc/assets/cert_key_rsa +27 -0
  99. data/spec/rhc/assets/empty.txt +0 -0
  100. data/spec/rhc/assets/env_vars.txt +7 -0
  101. data/spec/rhc/assets/env_vars_2.txt +1 -0
  102. data/spec/rhc/assets/foo.txt +1 -0
  103. data/spec/rhc/assets/targz_corrupted.tar.gz +1 -0
  104. data/spec/rhc/assets/targz_sample.tar.gz +0 -0
  105. data/spec/rhc/auth_spec.rb +442 -0
  106. data/spec/rhc/cli_spec.rb +188 -0
  107. data/spec/rhc/command_spec.rb +435 -0
  108. data/spec/rhc/commands/account_spec.rb +42 -0
  109. data/spec/rhc/commands/alias_spec.rb +333 -0
  110. data/spec/rhc/commands/app_spec.rb +754 -0
  111. data/spec/rhc/commands/apps_spec.rb +39 -0
  112. data/spec/rhc/commands/authorization_spec.rb +145 -0
  113. data/spec/rhc/commands/cartridge_spec.rb +641 -0
  114. data/spec/rhc/commands/deployment_spec.rb +286 -0
  115. data/spec/rhc/commands/domain_spec.rb +383 -0
  116. data/spec/rhc/commands/env_spec.rb +493 -0
  117. data/spec/rhc/commands/git_clone_spec.rb +80 -0
  118. data/spec/rhc/commands/logout_spec.rb +86 -0
  119. data/spec/rhc/commands/member_spec.rb +228 -0
  120. data/spec/rhc/commands/port_forward_spec.rb +217 -0
  121. data/spec/rhc/commands/server_spec.rb +69 -0
  122. data/spec/rhc/commands/setup_spec.rb +118 -0
  123. data/spec/rhc/commands/snapshot_spec.rb +179 -0
  124. data/spec/rhc/commands/ssh_spec.rb +163 -0
  125. data/spec/rhc/commands/sshkey_spec.rb +188 -0
  126. data/spec/rhc/commands/tail_spec.rb +81 -0
  127. data/spec/rhc/commands/threaddump_spec.rb +84 -0
  128. data/spec/rhc/config_spec.rb +407 -0
  129. data/spec/rhc/helpers_spec.rb +524 -0
  130. data/spec/rhc/highline_extensions_spec.rb +314 -0
  131. data/spec/rhc/json_spec.rb +30 -0
  132. data/spec/rhc/rest_application_spec.rb +248 -0
  133. data/spec/rhc/rest_client_spec.rb +752 -0
  134. data/spec/rhc/rest_spec.rb +740 -0
  135. data/spec/rhc/targz_spec.rb +55 -0
  136. data/spec/rhc/wizard_spec.rb +756 -0
  137. data/spec/spec_helper.rb +575 -0
  138. data/spec/wizard_spec_helper.rb +330 -0
  139. metadata +435 -0
@@ -0,0 +1,754 @@
1
+ require 'spec_helper'
2
+ require 'rest_spec_helper'
3
+ require 'rhc/commands/app'
4
+ require 'rhc/config'
5
+ require 'resolv'
6
+
7
+ describe RHC::Commands::App do
8
+ let!(:rest_client){ MockRestClient.new }
9
+ let!(:config){ user_config }
10
+ before{ RHC::Config.stub(:home_dir).and_return('/home/mock_user') }
11
+ before do
12
+ FakeFS.activate!
13
+ FakeFS::FileSystem.clear
14
+ RHC::Helpers.send(:remove_const, :MAX_RETRIES) rescue nil
15
+ RHC::Helpers.const_set(:MAX_RETRIES, 3)
16
+ @instance = RHC::Commands::App.new
17
+ RHC::Commands::App.stub(:new) do
18
+ @instance.stub(:git_config_get) { "" }
19
+ @instance.stub(:git_config_set) { "" }
20
+ Kernel.stub(:sleep) { }
21
+ @instance.stub(:git_clone_repo) do |git_url, repo_dir|
22
+ $terminal.instance_variable_get(:@output).puts "Cloning into..."
23
+ raise RHC::GitException, "Error in git clone" if repo_dir == "giterrorapp"
24
+ Dir::mkdir(repo_dir)
25
+ File.expand_path(repo_dir)
26
+ end
27
+ @instance.stub(:host_exists?) do |host|
28
+ host.match("dnserror") ? false : true
29
+ end
30
+ @instance
31
+ end
32
+ end
33
+
34
+ after(:each) do
35
+ FakeFS.deactivate!
36
+ end
37
+
38
+ describe 'app default' do
39
+ before do
40
+ FakeFS.deactivate!
41
+ end
42
+
43
+ context 'app' do
44
+ let(:arguments) { ['app'] }
45
+ it { run_output.should match('Usage:') }
46
+ it { run_output.should match('List of Actions') }
47
+ it { run_output.should_not match('Options') }
48
+ end
49
+ end
50
+
51
+ describe '#gear_group_state' do
52
+ it("shows single state"){ subject.send(:gear_group_state, ['started']).should == 'started' }
53
+ it("shows unique states"){ subject.send(:gear_group_state, ['idle', 'idle']).should == 'idle' }
54
+ it("shows number of started"){ subject.send(:gear_group_state, ['started', 'idle']).should == '1/2 started' }
55
+ end
56
+
57
+ describe '#check_domain!' do
58
+ let(:rest_client){ double('RestClient') }
59
+ let(:domain){ double('Domain', :name => 'test') }
60
+ before{ subject.stub(:rest_client).and_return(rest_client) }
61
+ let(:interactive){ false }
62
+ before{ subject.stub(:interactive?).and_return(interactive) }
63
+
64
+ context "when no options are provided and there is one domain" do
65
+ before{ rest_client.should_receive(:domains).twice.and_return([domain]) }
66
+ it("should load the first domain"){ subject.send(:check_domain!).should == domain }
67
+ after{ subject.send(:options).namespace.should == domain.name }
68
+ end
69
+
70
+ context "when no options are provided and there are no domains" do
71
+ before{ rest_client.should_receive(:domains).and_return([]) }
72
+ it("should load the first domain"){ expect{ subject.send(:check_domain!) }.to raise_error(RHC::Rest::DomainNotFoundException) }
73
+ after{ subject.send(:options).namespace.should be_nil }
74
+ end
75
+
76
+ context "when valid namespace is provided" do
77
+ before{ subject.send(:options)[:namespace] = 'test' }
78
+ before{ rest_client.should_receive(:find_domain).with('test').and_return(domain) }
79
+ it("should load the requested domain"){ subject.send(:check_domain!).should == domain }
80
+ after{ subject.send(:options).namespace.should == 'test' }
81
+ end
82
+
83
+ context "when interactive and no domains" do
84
+ let(:interactive){ true }
85
+ before{ rest_client.should_receive(:domains).twice.and_return([]) }
86
+ before{ RHC::DomainWizard.should_receive(:new).and_return(double(:run => true)) }
87
+ it("should raise if the wizard doesn't set the option"){ expect{ subject.send(:check_domain!) }.to raise_error(RHC::Rest::DomainNotFoundException) }
88
+ after{ subject.send(:options).namespace.should be_nil }
89
+ end
90
+ end
91
+
92
+ describe 'app create' do
93
+ before{ rest_client.add_domain("mockdomain") }
94
+
95
+ context "when we ask for help with the alias" do
96
+ before{ FakeFS.deactivate! }
97
+ context do
98
+ let(:arguments) { ['help', 'create-app'] }
99
+ it{ run_output.should match "Usage: pbox app-create <name>" }
100
+ end
101
+ context do
102
+ let(:arguments) { ['create-app', '-h'] }
103
+ it{ run_output.should match "Usage: pbox app-create <name>" }
104
+ end
105
+ end
106
+
107
+ context "when run with no arguments" do
108
+ before{ FakeFS.deactivate! }
109
+ let(:arguments){ ['create-app'] }
110
+ it{ run_output.should match "Usage: pbox app-create <name>" }
111
+ it{ run_output.should match "When creating an application, you must provide a name and a cartridge from the list below:" }
112
+ it{ run_output.should match "mock_standalone_cart-1" }
113
+ it{ run_output.should match "Please specify the name of the application" }
114
+ end
115
+
116
+ context "when dealing with config" do
117
+ subject{ described_class.new(Commander::Command::Options.new(options)) }
118
+ let(:wizard){ s = double('Wizard'); RHC::EmbeddedWizard.should_receive(:new).and_return(s); s }
119
+ let(:options){ nil }
120
+ let(:interactive){ true }
121
+ before{ subject.should_receive(:interactive?).at_least(1).times.and_return(interactive) }
122
+ before{ subject.stub(:check_sshkeys!) }
123
+
124
+ it("should run the wizard"){ expect{ subject.create('name', ['mock_standalone_cart-1']) }.to call(:run).on(wizard).and_stop }
125
+
126
+ context "when has config" do
127
+ let(:options){ {:server => 'test', :pblogin => 'foo'} }
128
+ before{ subject.send(:config).should_receive(:has_local_config?).and_return(true) }
129
+ it("should not run the wizard"){ expect{ subject.create('name', ['mock_standalone_cart-1']) }.to not_call(:new).on(RHC::EmbeddedWizard) }
130
+ end
131
+
132
+ context "when has no config" do
133
+ before{ subject.send(:config).should_receive(:has_local_config?).and_return(false) }
134
+ it("should run the wizard"){ expect{ subject.create('name', ['mock_standalone_cart-1']) }.to call(:new).on(RHC::EmbeddedWizard).and_stop }
135
+ end
136
+
137
+ context "when not interactive" do
138
+ let(:interactive){ false }
139
+ it("should not run the wizard"){ expect{ subject.create('name', ['mock_standalone_cart-1']) }.to not_call(:new).on(RHC::EmbeddedWizard) }
140
+ end
141
+ end
142
+
143
+ context "when dealing with ssh keys" do
144
+ before(:all){ mock_terminal }
145
+ subject{ described_class.new(options) }
146
+ let(:wizard){ s = double('Wizard'); RHC::SSHWizard.should_receive(:new).and_return(s); s }
147
+ let(:options){ Commander::Command::Options.new(:server => 'foo.com', :pblogin => 'test') }
148
+ let(:interactive){ true }
149
+ before{ subject.should_receive(:interactive?).at_least(1).times.and_return(interactive) }
150
+ before{ subject.should_receive(:check_config!) }
151
+
152
+ it("should run the wizard"){ expect{ subject.create('name', ['mock_standalone_cart-1']) }.to call(:run).on(wizard).and_stop }
153
+
154
+ context "when not interactive" do
155
+ let(:interactive){ false }
156
+ it("should not run the wizard"){ expect{ subject.create('name', ['mock_standalone_cart-1']) }.to not_call(:new).on(RHC::SSHWizard) }
157
+ end
158
+ end
159
+
160
+ context "when in full interactive mode with no keys, domain, or config" do
161
+ let!(:config){ base_config }
162
+ before{ RHC::Config.any_instance.stub(:has_local_config?).and_return(false) }
163
+ before{ described_class.any_instance.stub(:interactive?).and_return(true) }
164
+ before{ rest_client.domains.clear }
165
+ let(:arguments) { ['app', 'create', 'app1', 'mock_standalone_cart-1'] }
166
+ # skips login stage and insecure check because of mock rest client, doesn't check keys
167
+ it { run_output(['mydomain', 'y', 'mykey']).should match(/This wizard.*Checking for a domain.*You will not be able to create an application without completing this step.*Your domain 'mydomain' has been successfully created.*Creating application.*Your public SSH key.*Uploading key 'mykey'.*Your application 'app1' is now available.*Cloned to/m) }
168
+ end
169
+
170
+ context 'when run without a cart' do
171
+ before{ FakeFS.deactivate! }
172
+ let(:arguments) { ['app', 'create', 'app1'] }
173
+ it { run_output.should match(/mock_standalone_cart-1.*Every application needs a web cartridge/m) }
174
+ end
175
+
176
+ context 'when run with a valid cart' do
177
+ let(:arguments) { ['app', 'create', 'app1', 'mock_standalone_cart-1'] }
178
+ it { expect { run }.to exit_with_code(0) }
179
+ it { run_output.should match("Success") }
180
+ it { run_output.should match("Cartridges: mock_standalone_cart-1\n") }
181
+ it { run_output.should_not match(/Environment Variables:/) }
182
+ end
183
+
184
+ context 'when Hosts resolver raises an Exception' do
185
+ let(:arguments) { ['app', 'create', 'app1', 'mock_standalone_cart-1'] }
186
+ before :each do
187
+ resolver = Object.new
188
+ Resolv::Hosts.should_receive(:new).and_return(resolver)
189
+ resolver.should_receive(:getaddress).with('app1-mockdomain.fake.foo').and_raise(ArgumentError)
190
+ end
191
+
192
+ it { expect { run }.to exit_with_code(0) }
193
+ it { run_output.should match("Success") }
194
+ end
195
+
196
+ context 'when run with multiple carts' do
197
+ let(:arguments) { ['app', 'create', 'app1', 'mock_standalone_cart-1', 'mock_cart-1'] }
198
+ it { expect { run }.to exit_with_code(0) }
199
+ it { run_output.should match("Success") }
200
+ it { run_output.should match("Cartridges: mock_standalone_cart-1, mock_cart-1\n") }
201
+ it { run_output.should_not match(/Environment Variables:/) }
202
+ after{ rest_client.domains.first.applications.first.cartridges.find{ |c| c.name == 'mock_cart-1' }.should be_true }
203
+ end
204
+
205
+ context 'when run with a cart URL' do
206
+ let(:arguments) { ['app', 'create', 'app1', 'http://foo.com', 'mock_cart-1'] }
207
+ it { expect { run }.to exit_with_code(0) }
208
+ it { run_output.should match("Success") }
209
+ it { run_output.should match("Cartridges: http://foo.com, mock_cart-1\n") }
210
+ it { run_output.should_not match(/Environment Variables:/) }
211
+ after{ rest_client.domains.first.applications.first.cartridges.find{ |c| c.url == 'http://foo.com' }.should be_true }
212
+ end
213
+
214
+ context 'when run with a git url' do
215
+ let(:arguments) { ['app', 'create', 'app1', 'mock_standalone_cart-1', '--from', 'git://url'] }
216
+ it { expect { run }.to exit_with_code(0) }
217
+ it { run_output.should match("Success") }
218
+ it { run_output.should match("Git remote: git:fake.foo/git/app1.git\n") }
219
+ it { run_output.should match("Source Code: git://url\n") }
220
+ after{ rest_client.domains.first.applications.first.initial_git_url.should == 'git://url' }
221
+ end
222
+
223
+ context 'when no cartridges are returned' do
224
+ before do
225
+ domain = rest_client.domains.first
226
+ end
227
+ context 'without trace' do
228
+ let(:arguments) { ['app', 'create', 'app1', 'nomatch_cart'] }
229
+ it("should display the list of cartridges") { run_output.should match(/Short Name.*mock_standalone_cart-2/m) }
230
+ end
231
+ context 'with trace' do
232
+ let(:arguments) { ['app', 'create', 'app1', 'nomatch_cart', '--trace'] }
233
+ it { expect { run }.to raise_error(RHC::CartridgeNotFoundException, "There are no cartridges that match 'nomatch_cart'.") }
234
+ end
235
+ end
236
+
237
+ end
238
+
239
+ describe 'cart matching behavior' do
240
+ before{ rest_client.add_domain("mockdomain") }
241
+
242
+ context 'multiple web matches' do
243
+ let(:arguments) { ['app', 'create', 'app1', 'mock_standalone_cart', '--trace', '--noprompt'] }
244
+ it { expect { run }.to raise_error(RHC::MultipleCartridgesException) }
245
+ end
246
+ context 'when only a single cart can match' do
247
+ let(:arguments) { ['app', 'create', 'app1', 'unique', '--trace', '--noprompt'] }
248
+ it('picks the cart') { run_output.should match('Using mock_unique_standalone_cart-1') }
249
+ end
250
+ context 'when I pick a web cart and an ambiguous non web cart' do
251
+ let(:arguments) { ['app', 'create', 'app1', 'mock_standalone_cart-1', 'unique', '--trace', '--noprompt'] }
252
+ it('picks the non web cart') { run_output.should match('Using unique_mock_cart-1') }
253
+ end
254
+ context 'when I pick very ambiguous carts' do
255
+ let(:arguments) { ['app', 'create', 'app1', 'mock', 'embcart-', '--noprompt'] }
256
+ it('shows only web carts') { run_output.should match("There are multiple cartridges matching 'mock'") }
257
+ end
258
+ context 'when I pick only embedded carts' do
259
+ let(:arguments) { ['app', 'create', 'app1', 'mock_cart', '--trace', '--noprompt'] }
260
+ it { expect { run }.to raise_error(RHC::CartridgeNotFoundException, /Every application needs a web cartridge/) }
261
+ end
262
+ context 'when I pick multiple embedded carts' do
263
+ let(:arguments) { ['app', 'create', 'app1', 'unique_standalone', 'mock_cart', '--trace', '--noprompt'] }
264
+ it { expect { run }.to raise_error(RHC::MultipleCartridgesException, /There are multiple cartridges matching 'mock_cart'/) }
265
+ end
266
+ context 'when I pick multiple standalone carts' do
267
+ let(:arguments) { ['app', 'create', 'app1', 'unique_standalone', 'mock_standalone_cart', '--trace', '--noprompt'] }
268
+ it { expect { run }.to raise_error(RHC::MultipleCartridgesException, /There are multiple cartridges matching 'mock_standalone_cart'/) }
269
+ end
270
+ context 'when I pick a custom URL cart' do
271
+ let(:arguments) { ['app', 'create', 'app1', 'http://foo.com', '--trace', '--noprompt'] }
272
+ it('tells me about custom carts') { run_output.should match("The cartridge 'http://foo.com' will be downloaded") }
273
+ it('lists the cart using the short_name') { run_output.should match(%r(Cartridges:\s+http://foo.com$)) }
274
+ end
275
+ context 'when I pick a custom URL cart and a web cart' do
276
+ let(:arguments) { ['app', 'create', 'app1', 'http://foo.com', 'embcart-1', '--trace', '--noprompt'] }
277
+ it('tells me about custom carts') { run_output.should match("The cartridge 'http://foo.com' will be downloaded") }
278
+ it('lists the carts using the short_name') { run_output.should match(%r(Cartridges:\s+http://foo.com, embcart-1$)) }
279
+ end
280
+ end
281
+
282
+ describe 'app create enable-jenkins' do
283
+ let(:arguments) { ['app', 'create', 'app1', '--trace', 'mock_unique_standalone_cart', '--enable-jenkins'] }
284
+
285
+ context 'when run' do
286
+ before do
287
+ @domain = rest_client.add_domain("mockdomain")
288
+ end
289
+ it "should create a jenkins app and a regular app with an embedded jenkins client" do
290
+ #puts run_output
291
+ expect { run }.to exit_with_code(0)
292
+ jenkins_app = rest_client.find_application(@domain.name,"jenkins")
293
+ jenkins_app.cartridges[0].name.should == "jenkins-1"
294
+ app = rest_client.find_application(@domain.name,"app1")
295
+ app.find_cartridge("jenkins-client-1")
296
+ end
297
+ end
298
+ end
299
+
300
+ describe 'app create enable-jenkins with --no-dns' do
301
+ let(:arguments) { ['app', 'create', 'app1', 'mock_unique_standalone_cart', '--trace', '--enable-jenkins', '--no-dns'] }
302
+
303
+ context 'when run' do
304
+ before do
305
+ domain = rest_client.add_domain("mockdomain")
306
+ end
307
+ it { expect { run }.to_not raise_error }
308
+ end
309
+ end
310
+
311
+ describe 'app create enable-jenkins with same name as app' do
312
+ let(:arguments) { ['app', 'create', 'app1', 'mock_unique_standalone_cart', '--trace', '--enable-jenkins', 'app1'] }
313
+
314
+ context 'when run' do
315
+ before do
316
+ domain = rest_client.add_domain("mockdomain")
317
+ end
318
+ it { expect { run }.to raise_error(ArgumentError, /You have named both your main application and your Jenkins application/) }
319
+ end
320
+ end
321
+
322
+ describe 'app create enable-jenkins with existing jenkins' do
323
+ let(:arguments) { ['app', 'create', 'app1', 'mock_unique_standalone_cart', '--trace', '--enable-jenkins', 'jenkins2'] }
324
+
325
+ context 'when run' do
326
+ before do
327
+ @domain = rest_client.add_domain("mockdomain")
328
+ @domain.add_application("jenkins", "jenkins-1")
329
+ end
330
+ it "should use existing jenkins" do
331
+ expect { run }.to exit_with_code(0)
332
+ expect { rest_client.find_application(@domain.name,"jenkins") }.to_not raise_error
333
+ expect { rest_client.find_application(@domain.name,"jenkins2") }.to raise_error(RHC::Rest::ApplicationNotFoundException)
334
+ end
335
+ end
336
+ end
337
+
338
+ describe 'app create jenkins fails to install warnings' do
339
+ let(:arguments) { ['app', 'create', 'app1', 'mock_unique_standalone_cart', '--enable-jenkins'] }
340
+
341
+ before do
342
+ @domain = rest_client.add_domain("mockdomain")
343
+ end
344
+
345
+ context 'when run with error in jenkins setup' do
346
+ before do
347
+ @instance.stub(:add_jenkins_app) { raise Exception }
348
+ end
349
+ it "should print out jenkins warning" do
350
+ run_output.should match("Jenkins failed to install")
351
+ end
352
+ end
353
+
354
+ context 'when run with error in jenkins-client setup' do
355
+ before do
356
+ @instance.stub(:add_jenkins_cartridge) { raise Exception }
357
+ end
358
+ it "should print out jenkins warning" do
359
+ run_output.should match("Jenkins client failed to install")
360
+ end
361
+ end
362
+
363
+ context 'when run without jenkins cartridge available on server' do
364
+ before do
365
+ @instance.stub(:all_cartridges) { rest_client.cartridges.delete_if { |item| item.name =~ /\Ajenkins/i } }
366
+ end
367
+ it "should exit with jenkins error" do
368
+ run_output.should match("There is no installed cartridge that exposes Jenkins")
369
+ end
370
+ end
371
+ end
372
+
373
+ describe 'app create jenkins install with retries' do
374
+ let(:arguments) { ['app', 'create', 'app1', 'mock_unique_standalone_cart', '--enable-jenkins'] }
375
+
376
+ context 'when run with server error in jenkins-client setup' do
377
+ before do
378
+ @domain = rest_client.add_domain("mockdomain")
379
+ @instance.stub(:add_jenkins_cartridge) { raise RHC::Rest::ServerErrorException.new("Server error", 157) }
380
+ end
381
+ it "should fail embedding jenkins cartridge" do
382
+ Kernel.should_receive(:sleep).and_return(true)
383
+ run_output.should match("Jenkins client failed to install")
384
+ end
385
+ end
386
+ end
387
+
388
+ describe 'dns app create warnings' do
389
+ let(:arguments) { ['app', 'create', 'app1', 'mock_unique_standalone_cart'] }
390
+
391
+ context 'when run' do
392
+ before do
393
+ @domain = rest_client.add_domain("dnserror")
394
+ end
395
+ it { run_output.should match("unable to lookup your hostname") }
396
+ end
397
+ end
398
+
399
+ describe 'app create git warnings' do
400
+ let(:arguments) { ['app', 'create', 'app1', 'mock_unique_standalone_cart'] }
401
+
402
+ before do
403
+ @domain = rest_client.add_domain("mockdomain")
404
+ @instance.stub(:git_clone_application) { raise RHC::GitException }
405
+ @instance.stub(:check_sshkeys!)
406
+ end
407
+
408
+ context 'when run with error in git clone' do
409
+ it "should print out git warning" do
410
+ run_output.should match("We were unable to clone your application's git repo")
411
+ end
412
+ end
413
+
414
+ context 'when run with windows and no nslookup bug' do
415
+ before do
416
+ RHC::Helpers.stub(:windows?) { true }
417
+ @instance.stub(:run_nslookup) { true }
418
+ @instance.stub(:run_ping) { true }
419
+ end
420
+ it "should print out git warning" do
421
+ run_output.should match(" We were unable to clone your application's git repo")
422
+ end
423
+ end
424
+
425
+ context 'when run with windows nslookup bug' do
426
+ before do
427
+ RHC::Helpers.stub(:windows?) { true }
428
+ @instance.stub(:run_nslookup) { true }
429
+ @instance.stub(:run_ping) { false }
430
+ end
431
+ it "should print out windows warning" do
432
+ run_output.should match("This may also be related to an issue with Winsock on Windows")
433
+ end
434
+ end
435
+ end
436
+
437
+ describe 'app create prompt for sshkeys' do
438
+ let(:arguments) { ['app', 'create', 'app1', 'mock_unique_standalone_cart', '--config', '/tmp/test.conf', '-l', 'test@test.foo', '-p', 'password'] }
439
+
440
+ before (:each) do
441
+ @domain = rest_client.add_domain("mockdomain")
442
+ # fakefs is activated
443
+ Dir.mkdir('/tmp/')
444
+ File.open('/tmp/test.conf', 'w') do |f|
445
+ f.write("pblogin=test@test.foo")
446
+ end
447
+
448
+ # don't run wizard here because we test this elsewhere
449
+ wizard_instance = RHC::SSHWizard.new(rest_client, RHC::Config.new, Commander::Command::Options.new)
450
+ wizard_instance.stub(:ssh_key_uploaded?) { true }
451
+ RHC::SSHWizard.stub(:new) { wizard_instance }
452
+ RHC::Config.stub(:should_run_ssh_wizard?) { false }
453
+ end
454
+
455
+ context 'when run' do
456
+ it { expect { run }.to exit_with_code(0) }
457
+ end
458
+ end
459
+
460
+ describe 'app delete' do
461
+ let(:arguments) { ['app', 'delete', '--trace', '-a', 'app1', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] }
462
+
463
+ context 'when run' do
464
+ before{ @domain = rest_client.add_domain("mockdomain") }
465
+
466
+ it "should raise cartridge not found exception when no apps exist" do
467
+ expect { run }.to raise_error RHC::Rest::ApplicationNotFoundException
468
+ end
469
+
470
+ context "with an app" do
471
+ before{ @app = @domain.add_application("app1", "mock_type") }
472
+
473
+ it "should not remove app when no is sent as input" do
474
+ expect { run(["no"]) }.to raise_error(RHC::ConfirmationError)
475
+ @domain.applications.length.should == 1
476
+ @domain.applications[0] == @app
477
+ end
478
+
479
+ it "should remove app when yes is sent as input" do
480
+ expect { run(["yes"]) }.to exit_with_code(0)
481
+ @domain.applications.length.should == 0
482
+ end
483
+
484
+ context "with --noprompt but without --confirm" do
485
+ let(:arguments) { ['app', 'delete', 'app1', '--noprompt', '--trace'] }
486
+ it "should not remove the app" do
487
+ expect { run(["no"]) }.to raise_error(RHC::ConfirmationError)
488
+ @domain.applications.length.should == 1
489
+ end
490
+ end
491
+ context "with --noprompt and --confirm" do
492
+ let(:arguments) { ['app', 'delete', 'app1', '--noprompt', '--confirm'] }
493
+ it "should remove the app" do
494
+ expect { run }.to exit_with_code(0)
495
+ @domain.applications.length.should == 0
496
+ end
497
+ end
498
+ end
499
+ end
500
+
501
+ context "against a 1.5 server" do
502
+ let!(:rest_client){ nil }
503
+ let(:username){ mock_user }
504
+ let(:password){ 'password' }
505
+ let(:server){ mock_uri }
506
+ let(:arguments){ ['delete-app', 'foo', '--confirm', '--trace'] }
507
+ before do
508
+ stub_api
509
+ challenge{ stub_one_domain('test') }
510
+ stub_one_application('test', 'foo')
511
+ end
512
+ before do
513
+ stub_api_request(:delete, "broker/rest/domains/test/applications/foo").
514
+ to_return({
515
+ :body => {
516
+ :type => nil,
517
+ :data => nil,
518
+ :messages => [
519
+ {:exit_code => 0, :field => nil, :severity => 'info', :text => 'Removed foo'},
520
+ {:exit_code => 0, :field => nil, :severity => 'result', :text => 'Job URL changed'},
521
+ ]
522
+ }.to_json,
523
+ :status => 200
524
+ })
525
+ end
526
+
527
+ it("should display info returned by the server"){ run_output.should match "Deleting application 'foo'" }
528
+ it("should display results returned by the server"){ run_output.should match "Job URL changed" }
529
+ it('should exit successfully'){ expect{ run }.to exit_with_code(0) }
530
+ end
531
+ end
532
+
533
+ describe 'app show' do
534
+ let(:arguments) { ['app', 'show', 'app1'] }
535
+
536
+ context 'when run with the same case as created' do
537
+ before do
538
+ FakeFS.deactivate!
539
+ @domain = rest_client.add_domain("mockdomain")
540
+ @domain.add_application("app1", "mock_type")
541
+ end
542
+ it("should output an app") { run_output.should match("app1 @ https://app1-mockdomain.fake.foo/") }
543
+ it { run_output.should match(/Gears:\s+1 small/) }
544
+ end
545
+
546
+ context 'when run with scaled app' do
547
+ before do
548
+ @domain = rest_client.add_domain("mockdomain")
549
+ app = @domain.add_application("app1", "mock_type", true)
550
+ cart1 = app.add_cartridge('mock_cart-1')
551
+ cart2 = app.add_cartridge('mock_cart-2')
552
+ cart2.gear_profile = 'medium'
553
+ end
554
+ it { run_output.should match("app1 @ https://app1-mockdomain.fake.foo/") }
555
+ it { run_output.should match(/Scaling:.*x2/) }
556
+ it { run_output.should match(/Gears:\s+Located with mock_type/) }
557
+ it { run_output.should match(/Gears:\s+1 medium/) }
558
+ end
559
+
560
+ context 'when run with custom app' do
561
+ before do
562
+ @domain = rest_client.add_domain("mockdomain")
563
+ app = @domain.add_application("app1", "mock_type", true)
564
+ cart1 = app.add_cartridge('mock_cart-1')
565
+ cart1.url = 'https://foo.bar.com'
566
+ end
567
+ it { run_output.should match("app1 @ https://app1-mockdomain.fake.foo/") }
568
+ it { run_output.should match(/Scaling:.*x2/) }
569
+ it { run_output.should match(/Gears:\s+Located with mock_type/) }
570
+ it { run_output.should match(/Gears:\s+1 small/) }
571
+ it { run_output.should match(%r(From:\s+ https://foo.bar.com)) }
572
+ end
573
+ end
574
+
575
+ describe 'app show' do
576
+ let(:arguments) { ['app', 'show', 'APP1'] }
577
+
578
+ context 'when run with the different case from created' do
579
+ before do
580
+ @rc = MockRestClient.new
581
+ @domain = @rc.add_domain("mockdomain")
582
+ @domain.add_application("app1", "mock_type")
583
+ end
584
+ it { run_output.should match("app1 @ https://app1-mockdomain.fake.foo/") }
585
+ end
586
+ end
587
+
588
+ describe 'app show --state' do
589
+ let(:arguments) { ['app', 'show', 'app1', '--state'] }
590
+
591
+ context 'when run' do
592
+ before do
593
+ @domain = rest_client.add_domain("mockdomain")
594
+ @domain.add_application("app1", "mock_type")
595
+ end
596
+ it { run_output.should match("started") }
597
+ end
598
+ end
599
+
600
+ describe 'app show --gears' do
601
+ let(:arguments) { ['app', 'show', 'app1', '--gears'] }
602
+
603
+ context 'when run' do
604
+ before do
605
+ @domain = rest_client.add_domain("mockdomain")
606
+ @domain.add_application("app1", "mock_type")
607
+ end
608
+ it { run_output.should match("fakegearid0 started mock_type small fakegearid0@fakesshurl.com") }
609
+ it { expect{ run }.to exit_with_code(0) }
610
+ end
611
+ end
612
+
613
+ describe 'app show --gears quota' do
614
+ let(:arguments) { ['app', 'show', 'app1', '--gears', 'quota'] }
615
+
616
+ context 'when run' do
617
+ before do
618
+ @domain = rest_client.add_domain("mockdomain")
619
+ @domain.add_application("app1", "mock_type", true)
620
+ expect_multi_ssh('echo "$(du --block-size=1 -s 2>/dev/null | cut -f 1)"', 'fakegearid0@fakesshurl.com' => '1734334', 'fakegearid1@fakesshurl.com' => '1934348')
621
+ end
622
+ it { run_output.should match(/Gear.*Cartridges.*Used.*fakegearid0.*1\.7 MB.*1 GB.*fakegearid1.*1\.9 MB/m) }
623
+ it { expect{ run }.to exit_with_code(0) }
624
+ end
625
+ end
626
+
627
+ describe 'app show --gears ssh' do
628
+ let(:arguments) { ['app', 'show', 'app1', '--gears', 'ssh'] }
629
+
630
+ context 'when run' do
631
+ before do
632
+ @domain = rest_client.add_domain("mockdomain")
633
+ @domain.add_application("app1", "mock_type", true)
634
+ end
635
+ it { run_output.should == "fakegearid0@fakesshurl.com\nfakegearid1@fakesshurl.com\n\n" }
636
+ it { expect{ run }.to exit_with_code(0) }
637
+ end
638
+ end
639
+
640
+ describe 'app show --gears badcommand' do
641
+ let(:arguments) { ['app', 'show', 'app1', '--gears', 'badcommand'] }
642
+
643
+ context 'when run' do
644
+ before{ rest_client.add_domain("mockdomain").add_application("app1", "mock_type", true) }
645
+ it { run_output.should match(/The operation badcommand is not supported/m) }
646
+ it { expect{ run }.to exit_with_code(1) }
647
+ end
648
+ end
649
+
650
+ describe 'app actions' do
651
+ before do
652
+ domain = rest_client.add_domain("mockdomain")
653
+ app = domain.add_application("app1", "mock_type")
654
+ app.add_cartridge('mock_cart-1')
655
+ end
656
+
657
+ context 'app start' do
658
+ let(:arguments) { ['app', 'start', '-a', 'app1'] }
659
+ it { run_output.should match('start') }
660
+ it { expect{ run }.to exit_with_code(0) }
661
+ end
662
+
663
+ context 'app stop' do
664
+ let(:arguments) { ['app', 'stop', 'app1'] }
665
+
666
+ it { run_output.should match('stop') }
667
+ it { expect{ run }.to exit_with_code(0) }
668
+ end
669
+
670
+ context 'app force stop' do
671
+ let(:arguments) { ['app', 'force-stop', 'app1'] }
672
+
673
+ it { run_output.should match('force') }
674
+ it { expect{ run }.to exit_with_code(0) }
675
+ end
676
+
677
+ context 'app restart' do
678
+ let(:arguments) { ['app', 'restart', 'app1'] }
679
+ it { run_output.should match('restart') }
680
+ it { expect{ run }.to exit_with_code(0) }
681
+ end
682
+
683
+ context 'app reload' do
684
+ let(:arguments) { ['app', 'reload', 'app1'] }
685
+ it { run_output.should match('reload') }
686
+ it { expect{ run }.to exit_with_code(0) }
687
+ end
688
+
689
+ context 'app tidy' do
690
+ let(:arguments) { ['app', 'tidy', 'app1'] }
691
+ it { run_output.should match('cleaned') }
692
+ it { expect{ run }.to exit_with_code(0) }
693
+ end
694
+ end
695
+
696
+ describe "#create_app" do
697
+ it("should list cartridges when a server error happens") do
698
+ subject.should_receive(:list_cartridges)
699
+ domain = double
700
+ domain.stub(:add_application).and_raise(RHC::Rest::ValidationException.new('Foo', :cartridges, 109))
701
+ expect{ subject.send(:create_app, 'name', 'jenkins-1', domain) }.to raise_error(RHC::Rest::ValidationException)
702
+ end
703
+ end
704
+
705
+ describe 'create app with env vars' do
706
+ before{ @domain = rest_client.add_domain("mockdomain") }
707
+
708
+ [['app', 'create', 'app1', 'mock_standalone_cart-1', '-e', 'FOO=BAR'],
709
+ ['app', 'create', 'app1', 'mock_standalone_cart-1', '--env', 'FOO=BAR'],
710
+ ['app', 'create', 'app1', 'mock_standalone_cart-1', 'FOO=BAR']
711
+ ].each_with_index do |args, i|
712
+ context "when run with single env var #{i}" do
713
+ let(:arguments) { args }
714
+ before { @domain.should_receive(:has_param?).with('ADD_APPLICATION','environment_variables').and_return(true) }
715
+ it { expect { run }.to exit_with_code(0) }
716
+ it { run_output.should match("Success") }
717
+ it { run_output.should match(/Cartridges:\s+mock_standalone_cart-1\n/) }
718
+ it { run_output.should match(/Environment Variables:\s+FOO=BAR\n/) }
719
+ end
720
+ end
721
+
722
+ [['app', 'create', 'app1', 'mock_standalone_cart-1', '-e', 'VAR1=VAL1', '-e', 'VAR2=VAL2', '-e', 'VAR3=VAL3'],
723
+ ['app', 'create', 'app1', 'mock_standalone_cart-1', '--env', 'VAR1=VAL1', '--env', 'VAR2=VAL2', '--env', 'VAR3=VAL3'],
724
+ ['app', 'create', 'app1', 'mock_standalone_cart-1', 'VAR1=VAL1', 'VAR2=VAL2', 'VAR3=VAL3'],
725
+ ['app', 'create', 'app1', 'mock_standalone_cart-1', 'VAR1=VAL1', 'VAR2=VAL2', '-e', 'VAR3=VAL3'],
726
+ ['app', 'create', 'app1', 'mock_standalone_cart-1', 'VAR1=VAL1', '--env', 'VAR2=VAL2', '-e', 'VAR3=VAL3']
727
+ ].each_with_index do |args, i|
728
+ context "when run with multiple env vars #{i}" do
729
+ let(:arguments) { args }
730
+ before { @domain.should_receive(:has_param?).with('ADD_APPLICATION','environment_variables').and_return(true) }
731
+ it { expect { run }.to exit_with_code(0) }
732
+ it { run_output.should match("Success") }
733
+ it { run_output.should match(/Cartridges:\s+mock_standalone_cart-1\n/) }
734
+ it { run_output.should match(/Environment Variables:\s+VAR1=VAL1, VAR2=VAL2, VAR3=VAL3\n/) }
735
+ end
736
+ end
737
+
738
+ [['app', 'create', 'app1', 'mock_standalone_cart-1', '-e', 'FOO=BAR'],
739
+ ['app', 'create', 'app1', 'mock_standalone_cart-1', '--env', 'FOO=BAR'],
740
+ ['app', 'create', 'app1', 'mock_standalone_cart-1', 'FOO=BAR']
741
+ ].each_with_index do |args, i|
742
+ context "when run against a server without env vars support #{i}" do
743
+ let(:arguments) { args }
744
+ before { @domain.should_receive(:has_param?).with('ADD_APPLICATION','environment_variables').and_return(false) }
745
+ it { expect { run }.to exit_with_code(0) }
746
+ it { run_output.should match("Success") }
747
+ it { run_output.should match(/Cartridges:\s+mock_standalone_cart-1\n/) }
748
+ it { run_output.should match("Server does not support environment variables") }
749
+ it { run_output.should_not match(/Environment Variables:\s+FOO=BAR\n/) }
750
+ end
751
+ end
752
+
753
+ end
754
+ end