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,777 @@
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: app app-create <name>" }
100
+ end
101
+ context do
102
+ let(:arguments) { ['create-app', '-h'] }
103
+ it{ run_output.should match "Usage: app 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: app 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', :rhlogin => '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', :rhlogin => '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 without git installed' do
415
+ before do
416
+ @instance.stub(:has_git?) { false }
417
+ end
418
+ it "should print out git warning" do
419
+ run_output.should match("You do not have git installed")
420
+ end
421
+ end
422
+
423
+
424
+ context 'when run with windows and no nslookup bug' do
425
+ before do
426
+ RHC::Helpers.stub(:windows?) { true }
427
+ @instance.stub(:run_nslookup) { true }
428
+ @instance.stub(:run_ping) { true }
429
+ end
430
+ it "should print out git warning" do
431
+ run_output.should match(" We were unable to clone your application's git repo")
432
+ end
433
+ end
434
+
435
+ context 'when run with windows nslookup bug' do
436
+ before do
437
+ RHC::Helpers.stub(:windows?) { true }
438
+ @instance.stub(:run_nslookup) { true }
439
+ @instance.stub(:run_ping) { false }
440
+ end
441
+ it "should print out windows warning" do
442
+ run_output.should match("This may also be related to an issue with Winsock on Windows")
443
+ end
444
+ end
445
+ end
446
+
447
+ describe 'app create prompt for sshkeys' do
448
+ let(:arguments) { ['app', 'create', 'app1', 'mock_unique_standalone_cart', '--config', '/tmp/test.conf', '-l', 'test@test.foo', '-p', 'password'] }
449
+
450
+ before (:each) do
451
+ @domain = rest_client.add_domain("mockdomain")
452
+ # fakefs is activated
453
+ Dir.mkdir('/tmp/')
454
+ File.open('/tmp/test.conf', 'w') do |f|
455
+ f.write("rhlogin=test@test.foo")
456
+ end
457
+
458
+ # don't run wizard here because we test this elsewhere
459
+ wizard_instance = RHC::SSHWizard.new(rest_client, RHC::Config.new, Commander::Command::Options.new)
460
+ wizard_instance.stub(:ssh_key_uploaded?) { true }
461
+ RHC::SSHWizard.stub(:new) { wizard_instance }
462
+ RHC::Config.stub(:should_run_ssh_wizard?) { false }
463
+ end
464
+
465
+ context 'when run' do
466
+ it { expect { run }.to exit_with_code(0) }
467
+ end
468
+ end
469
+
470
+ describe 'app delete' do
471
+ let(:arguments) { ['app', 'delete', '--trace', '-a', 'app1', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] }
472
+
473
+ context 'when run' do
474
+ before{ @domain = rest_client.add_domain("mockdomain") }
475
+
476
+ it "should raise cartridge not found exception when no apps exist" do
477
+ expect { run }.to raise_error RHC::Rest::ApplicationNotFoundException
478
+ end
479
+
480
+ context "with an app" do
481
+ before{ @app = @domain.add_application("app1", "mock_type") }
482
+
483
+ it "should not remove app when no is sent as input" do
484
+ expect { run(["no"]) }.to raise_error(RHC::ConfirmationError)
485
+ @domain.applications.length.should == 1
486
+ @domain.applications[0] == @app
487
+ end
488
+
489
+ it "should remove app when yes is sent as input" do
490
+ expect { run(["yes"]) }.to exit_with_code(0)
491
+ @domain.applications.length.should == 0
492
+ end
493
+
494
+ context "with --noprompt but without --confirm" do
495
+ let(:arguments) { ['app', 'delete', 'app1', '--noprompt', '--trace'] }
496
+ it "should not remove the app" do
497
+ expect { run(["no"]) }.to raise_error(RHC::ConfirmationError)
498
+ @domain.applications.length.should == 1
499
+ end
500
+ end
501
+ context "with --noprompt and --confirm" do
502
+ let(:arguments) { ['app', 'delete', 'app1', '--noprompt', '--confirm'] }
503
+ it "should remove the app" do
504
+ expect { run }.to exit_with_code(0)
505
+ @domain.applications.length.should == 0
506
+ end
507
+ end
508
+ end
509
+ end
510
+
511
+ context "against a 1.5 server" do
512
+ let!(:rest_client){ nil }
513
+ let(:username){ mock_user }
514
+ let(:password){ 'password' }
515
+ let(:server){ mock_uri }
516
+ let(:arguments){ ['delete-app', 'foo', '--confirm', '--trace'] }
517
+ before do
518
+ stub_api
519
+ challenge{ stub_one_domain('test') }
520
+ stub_one_application('test', 'foo')
521
+ end
522
+ before do
523
+ stub_api_request(:delete, "broker/rest/domains/test/applications/foo").
524
+ to_return({
525
+ :body => {
526
+ :type => nil,
527
+ :data => nil,
528
+ :messages => [
529
+ {:exit_code => 0, :field => nil, :severity => 'info', :text => 'Removed foo'},
530
+ {:exit_code => 0, :field => nil, :severity => 'result', :text => 'Job URL changed'},
531
+ ]
532
+ }.to_json,
533
+ :status => 200
534
+ })
535
+ end
536
+
537
+ it("should display info returned by the server"){ run_output.should match "Deleting application 'foo'" }
538
+ it("should display results returned by the server"){ run_output.should match "Job URL changed" }
539
+ it('should exit successfully'){ expect{ run }.to exit_with_code(0) }
540
+ end
541
+ end
542
+
543
+ describe 'app show' do
544
+ let(:arguments) { ['app', 'show', 'app1'] }
545
+
546
+ context 'when run with the same case as created' do
547
+ before do
548
+ FakeFS.deactivate!
549
+ @domain = rest_client.add_domain("mockdomain")
550
+ @domain.add_application("app1", "mock_type")
551
+ end
552
+ it("should output an app") { run_output.should match("app1 @ https://app1-mockdomain.fake.foo/") }
553
+ it { run_output.should match(/Gears:\s+1 small/) }
554
+ end
555
+
556
+ context 'when run with scaled app' do
557
+ before do
558
+ @domain = rest_client.add_domain("mockdomain")
559
+ app = @domain.add_application("app1", "mock_type", true)
560
+ cart1 = app.add_cartridge('mock_cart-1')
561
+ cart2 = app.add_cartridge('mock_cart-2')
562
+ cart2.gear_profile = 'medium'
563
+ end
564
+ it { run_output.should match("app1 @ https://app1-mockdomain.fake.foo/") }
565
+ it { run_output.should match(/Scaling:.*x2/) }
566
+ it { run_output.should match(/Gears:\s+Located with mock_type/) }
567
+ it { run_output.should match(/Gears:\s+1 medium/) }
568
+ end
569
+
570
+ context 'when run with custom app' do
571
+ before do
572
+ @domain = rest_client.add_domain("mockdomain")
573
+ app = @domain.add_application("app1", "mock_type", true)
574
+ cart1 = app.add_cartridge('mock_cart-1')
575
+ cart1.url = 'https://foo.bar.com'
576
+ end
577
+ it { run_output.should match("app1 @ https://app1-mockdomain.fake.foo/") }
578
+ it { run_output.should match(/Scaling:.*x2/) }
579
+ it { run_output.should match(/Gears:\s+Located with mock_type/) }
580
+ it { run_output.should match(/Gears:\s+1 small/) }
581
+ it { run_output.should match(%r(From:\s+ https://foo.bar.com)) }
582
+ end
583
+ end
584
+
585
+ describe 'app show' do
586
+ let(:arguments) { ['app', 'show', 'APP1'] }
587
+
588
+ context 'when run with the different case from created' do
589
+ before do
590
+ @rc = MockRestClient.new
591
+ @domain = @rc.add_domain("mockdomain")
592
+ @domain.add_application("app1", "mock_type")
593
+ end
594
+ it { run_output.should match("app1 @ https://app1-mockdomain.fake.foo/") }
595
+ end
596
+ end
597
+
598
+ describe 'app show --state' do
599
+ let(:arguments) { ['app', 'show', 'app1', '--state'] }
600
+
601
+ context 'when run' do
602
+ before do
603
+ @domain = rest_client.add_domain("mockdomain")
604
+ @domain.add_application("app1", "mock_type")
605
+ end
606
+ it { run_output.should match("started") }
607
+ end
608
+ end
609
+
610
+ describe 'app show --gears' do
611
+ let(:arguments) { ['app', 'show', 'app1', '--gears'] }
612
+
613
+ context 'when run' do
614
+ before do
615
+ @domain = rest_client.add_domain("mockdomain")
616
+ @domain.add_application("app1", "mock_type")
617
+ end
618
+ it { run_output.should match("fakegearid0 started mock_type small fakegearid0@fakesshurl.com") }
619
+ it { expect{ run }.to exit_with_code(0) }
620
+ end
621
+ end
622
+
623
+ describe 'app show --gears quota' do
624
+ let(:arguments) { ['app', 'show', 'app1', '--gears', 'quota'] }
625
+
626
+ context 'when run' do
627
+ before do
628
+ @domain = rest_client.add_domain("mockdomain")
629
+ @domain.add_application("app1", "mock_type", true)
630
+ expect_multi_ssh('echo "$(du --block-size=1 -s 2>/dev/null | cut -f 1)"', 'fakegearid0@fakesshurl.com' => '1734334', 'fakegearid1@fakesshurl.com' => '1934348')
631
+ end
632
+ it { run_output.should match(/Gear.*Cartridges.*Used.*fakegearid0.*1\.7 MB.*1 GB.*fakegearid1.*1\.9 MB/m) }
633
+ it { expect{ run }.to exit_with_code(0) }
634
+ end
635
+ end
636
+
637
+ describe 'app show --gears ssh' do
638
+ let(:arguments) { ['app', 'show', 'app1', '--gears', 'ssh'] }
639
+
640
+ context 'when run' do
641
+ before do
642
+ @domain = rest_client.add_domain("mockdomain")
643
+ @domain.add_application("app1", "mock_type", true)
644
+ end
645
+ it { run_output.should == "fakegearid0@fakesshurl.com\nfakegearid1@fakesshurl.com\n\n" }
646
+ it { expect{ run }.to exit_with_code(0) }
647
+ end
648
+ end
649
+
650
+ describe 'app show --gears badcommand' do
651
+ let(:arguments) { ['app', 'show', 'app1', '--gears', 'badcommand'] }
652
+
653
+ context 'when run' do
654
+ before{ rest_client.add_domain("mockdomain").add_application("app1", "mock_type", true) }
655
+ it { run_output.should match(/The operation badcommand is not supported/m) }
656
+ it { expect{ run }.to exit_with_code(1) }
657
+ end
658
+ end
659
+
660
+ describe 'app actions' do
661
+ before do
662
+ domain = rest_client.add_domain("mockdomain")
663
+ app = domain.add_application("app1", "mock_type")
664
+ app.add_cartridge('mock_cart-1')
665
+ end
666
+
667
+ context 'app start' do
668
+ let(:arguments) { ['app', 'start', '-a', 'app1'] }
669
+ it { run_output.should match('start') }
670
+ it { expect{ run }.to exit_with_code(0) }
671
+ end
672
+
673
+ context 'app scale-up' do
674
+ let(:arguments) { ['app', 'scale-up', '-a', 'app1'] }
675
+ it { run_output.should match('scaled up') }
676
+ it { expect{ run }.to exit_with_code(0) }
677
+ end
678
+
679
+ context 'app scale-down' do
680
+ let(:arguments) { ['app', 'scale-down', '-a', 'app1'] }
681
+ it { run_output.should match('scaled down') }
682
+ it { expect{ run }.to exit_with_code(0) }
683
+ end
684
+
685
+
686
+ context 'app stop' do
687
+ let(:arguments) { ['app', 'stop', 'app1'] }
688
+
689
+ it { run_output.should match('stop') }
690
+ it { expect{ run }.to exit_with_code(0) }
691
+ end
692
+
693
+ context 'app force stop' do
694
+ let(:arguments) { ['app', 'force-stop', 'app1'] }
695
+
696
+ it { run_output.should match('force') }
697
+ it { expect{ run }.to exit_with_code(0) }
698
+ end
699
+
700
+ context 'app restart' do
701
+ let(:arguments) { ['app', 'restart', 'app1'] }
702
+ it { run_output.should match('restart') }
703
+ it { expect{ run }.to exit_with_code(0) }
704
+ end
705
+
706
+ context 'app reload' do
707
+ let(:arguments) { ['app', 'reload', 'app1'] }
708
+ it { run_output.should match('reload') }
709
+ it { expect{ run }.to exit_with_code(0) }
710
+ end
711
+
712
+ context 'app tidy' do
713
+ let(:arguments) { ['app', 'tidy', 'app1'] }
714
+ it { run_output.should match('cleaned') }
715
+ it { expect{ run }.to exit_with_code(0) }
716
+ end
717
+ end
718
+
719
+ describe "#create_app" do
720
+ it("should list cartridges when a server error happens") do
721
+ subject.should_receive(:list_cartridges)
722
+ domain = double
723
+ domain.stub(:add_application).and_raise(RHC::Rest::ValidationException.new('Foo', :cartridges, 109))
724
+ expect{ subject.send(:create_app, 'name', 'jenkins-1', domain) }.to raise_error(RHC::Rest::ValidationException)
725
+ end
726
+ end
727
+
728
+ describe 'create app with env vars' do
729
+ before{ @domain = rest_client.add_domain("mockdomain") }
730
+
731
+ [['app', 'create', 'app1', 'mock_standalone_cart-1', '-e', 'FOO=BAR'],
732
+ ['app', 'create', 'app1', 'mock_standalone_cart-1', '--env', 'FOO=BAR'],
733
+ ['app', 'create', 'app1', 'mock_standalone_cart-1', 'FOO=BAR']
734
+ ].each_with_index do |args, i|
735
+ context "when run with single env var #{i}" do
736
+ let(:arguments) { args }
737
+ before { @domain.should_receive(:has_param?).with('ADD_APPLICATION','environment_variables').and_return(true) }
738
+ it { expect { run }.to exit_with_code(0) }
739
+ it { run_output.should match("Success") }
740
+ it { run_output.should match(/Cartridges:\s+mock_standalone_cart-1\n/) }
741
+ it { run_output.should match(/Environment Variables:\s+FOO=BAR\n/) }
742
+ end
743
+ end
744
+
745
+ [['app', 'create', 'app1', 'mock_standalone_cart-1', '-e', 'VAR1=VAL1', '-e', 'VAR2=VAL2', '-e', 'VAR3=VAL3'],
746
+ ['app', 'create', 'app1', 'mock_standalone_cart-1', '--env', 'VAR1=VAL1', '--env', 'VAR2=VAL2', '--env', 'VAR3=VAL3'],
747
+ ['app', 'create', 'app1', 'mock_standalone_cart-1', 'VAR1=VAL1', 'VAR2=VAL2', 'VAR3=VAL3'],
748
+ ['app', 'create', 'app1', 'mock_standalone_cart-1', 'VAR1=VAL1', 'VAR2=VAL2', '-e', 'VAR3=VAL3'],
749
+ ['app', 'create', 'app1', 'mock_standalone_cart-1', 'VAR1=VAL1', '--env', 'VAR2=VAL2', '-e', 'VAR3=VAL3']
750
+ ].each_with_index do |args, i|
751
+ context "when run with multiple env vars #{i}" do
752
+ let(:arguments) { args }
753
+ before { @domain.should_receive(:has_param?).with('ADD_APPLICATION','environment_variables').and_return(true) }
754
+ it { expect { run }.to exit_with_code(0) }
755
+ it { run_output.should match("Success") }
756
+ it { run_output.should match(/Cartridges:\s+mock_standalone_cart-1\n/) }
757
+ it { run_output.should match(/Environment Variables:\s+VAR1=VAL1, VAR2=VAL2, VAR3=VAL3\n/) }
758
+ end
759
+ end
760
+
761
+ [['app', 'create', 'app1', 'mock_standalone_cart-1', '-e', 'FOO=BAR'],
762
+ ['app', 'create', 'app1', 'mock_standalone_cart-1', '--env', 'FOO=BAR'],
763
+ ['app', 'create', 'app1', 'mock_standalone_cart-1', 'FOO=BAR']
764
+ ].each_with_index do |args, i|
765
+ context "when run against a server without env vars support #{i}" do
766
+ let(:arguments) { args }
767
+ before { @domain.should_receive(:has_param?).with('ADD_APPLICATION','environment_variables').and_return(false) }
768
+ it { expect { run }.to exit_with_code(0) }
769
+ it { run_output.should match("Success") }
770
+ it { run_output.should match(/Cartridges:\s+mock_standalone_cart-1\n/) }
771
+ it { run_output.should match("Server does not support environment variables") }
772
+ it { run_output.should_not match(/Environment Variables:\s+FOO=BAR\n/) }
773
+ end
774
+ end
775
+
776
+ end
777
+ end