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,228 @@
1
+ require 'spec_helper'
2
+ require 'rest_spec_helper'
3
+ require 'rhc/commands/member'
4
+
5
+ describe RHC::Commands::Member do
6
+
7
+ before{ user_config }
8
+
9
+ describe 'help' do
10
+ let(:arguments) { ['member', '--help'] }
11
+
12
+ it "should display help" do
13
+ expect { run }.to exit_with_code(0)
14
+ end
15
+ it('should output usage') { run_output.should match "Usage: pbox member" }
16
+ it('should output info about roles') { run_output.should match "Teams of developers can collaborate" }
17
+ end
18
+
19
+ let(:username){ 'test_user' }
20
+ let(:password){ 'test_password' }
21
+ let(:server){ 'test.domain.com' }
22
+
23
+ def with_mock_rest_client
24
+ @rest_client ||= MockRestClient.new
25
+ end
26
+ def with_mock_domain
27
+ @domain ||= with_mock_rest_client.add_domain("mock-domain-0")
28
+ end
29
+ def with_mock_app
30
+ @app ||= begin
31
+ app = with_mock_domain.add_application("mock-app-0", "ruby-1.8.7")
32
+ app.stub(:ssh_url).and_return("ssh://user@test.domain.com")
33
+ app.stub(:supports_members?).and_return(supports_members)
34
+ app
35
+ end
36
+ end
37
+
38
+ let(:owner){ RHC::Rest::Membership::Member.new(:id => '1', :role => 'admin', :owner => true, :login => 'alice') }
39
+ let(:other_admin){ RHC::Rest::Membership::Member.new(:id => '2', :role => 'admin', :login => 'Bob') }
40
+ let(:other_editor){ RHC::Rest::Membership::Member.new(:id => '3', :role => 'editor', :name => 'Carol', :login => 'carol') }
41
+ let(:other_viewer){ RHC::Rest::Membership::Member.new(:id => '4', :role => 'viewer', :name => 'Doug', :login => 'doug@doug.com') }
42
+
43
+ describe 'list-member' do
44
+ context 'on a domain' do
45
+ let(:arguments) { ['members', '-n', 'mock-domain-0'] }
46
+ let(:supports_members){ true }
47
+ before{ with_mock_domain.add_member(owner) }
48
+ it { expect { run }.to exit_with_code(0) }
49
+ it { run_output.should =~ /alice\s+admin \(owner\)/ }
50
+ it("should not show the name column") { run_output.should =~ /^Login\s+Role$/ }
51
+ end
52
+
53
+ context 'on an application' do
54
+ let(:arguments) { ['members', 'mock-domain-0/mock-app-0'] }
55
+ let(:supports_members){ false }
56
+ before{ with_mock_app }
57
+
58
+ it { expect { run }.to exit_with_code(1) }
59
+ it { run_output.should =~ /The server does not support adding or removing members/ }
60
+
61
+ context "with only owner" do
62
+ let(:supports_members){ true }
63
+ before{ with_mock_app.add_member(owner) }
64
+ it { expect { run }.to exit_with_code(0) }
65
+ it { run_output.should =~ /alice\s+admin \(owner\)/ }
66
+ it("should not show the name column") { run_output.should =~ /^Login\s+Role$/ }
67
+
68
+ context "with ids" do
69
+ let(:arguments) { ['members', 'mock-domain-0/mock-app-0', '--ids'] }
70
+ it { expect { run }.to exit_with_code(0) }
71
+ it { run_output.should =~ /alice\s+admin \(owner\) 1/ }
72
+ it("should not show the name column") { run_output.should =~ /^Login\s+Role\s+ID$/ }
73
+ end
74
+ end
75
+
76
+ context "with several members" do
77
+ let(:supports_members){ true }
78
+ before{ with_mock_app.add_member(owner).add_member(other_editor).add_member(other_admin).add_member(other_viewer) }
79
+ it { expect { run }.to exit_with_code(0) }
80
+ it { run_output.should =~ /alice\s+admin \(owner\)/ }
81
+ it { run_output.should =~ /Bob\s+admin/ }
82
+ it { run_output.should =~ /carol\s+editor/ }
83
+ it { run_output.should =~ /doug\.com\s+viewer/ }
84
+ it("should order the members by role") { run_output.should =~ /admin.*owner.*admin.*edit.*view/m }
85
+ it("should include the login value") { run_output.should =~ /alice.*Bob.*carol.*doug@doug\.com/m }
86
+ it("should show the name column") { run_output.should =~ /^Name\s+Login\s+Role$/ }
87
+ end
88
+ end
89
+ end
90
+
91
+ describe 'add-member' do
92
+ before do
93
+ stub_api
94
+ challenge{ stub_one_domain('test', nil, mock_user_auth) }
95
+ end
96
+
97
+ context "when the resource doesn't support membership changes" do
98
+ let(:arguments) { ['add-member', 'testuser', '-n', 'test'] }
99
+ it { expect { run }.to exit_with_code(1) }
100
+ it { run_output.should =~ /Adding 1 editor to domain .*The server does not support adding or removing members/ }
101
+ end
102
+
103
+ context "with supported membership" do
104
+ let(:supports_members?){ true }
105
+
106
+ context 'with a valid user' do
107
+ let(:arguments) { ['add-member', 'testuser', '-n', 'test'] }
108
+ before do
109
+ stub_api_request(:patch, "broker/rest/domains/test/members").
110
+ with(:body => {:members => [{'login' => 'testuser', 'role' => 'edit'}]}).
111
+ to_return({:body => {:type => 'members', :data => [], :messages => [{:exit_code => 0, :field => 'login', :index => 0, :severity => 'info', :text => 'Added 1 member'},]}.to_json, :status => 200})
112
+ end
113
+ it { expect { run }.to exit_with_code(0) }
114
+ it { run_output.should =~ /Adding 1 editor to domain .*done/ }
115
+ end
116
+
117
+ context 'with an invalid role' do
118
+ let(:arguments) { ['add-member', 'testuser', '-n', 'test', '--role', 'missing'] }
119
+ it { expect { run }.to exit_with_code(1) }
120
+ it { run_output.should =~ /The provided role 'missing' is not valid\. Supported values: .*admin/ }
121
+ end
122
+
123
+ context 'with a missing user' do
124
+ let(:arguments) { ['add-member', 'testuser', '-n', 'test'] }
125
+ before do
126
+ stub_api_request(:patch, "broker/rest/domains/test/members").
127
+ with(:body => {:members => [{'login' => 'testuser', 'role' => 'edit'}]}).
128
+ to_return({:body => {:messages => [{:exit_code => 132, :field => 'login', :index => 0, :severity => 'error', :text => 'There is no user with a login testuser'},]}.to_json, :status => 422})
129
+ end
130
+ it { expect { run }.to exit_with_code(1) }
131
+ it { run_output.should =~ /Adding 1 editor to domain.*There is no user with a login testuser/ }
132
+ end
133
+
134
+ context 'with a missing user id and role' do
135
+ let(:arguments) { ['add-member', '123', '-n', 'test', '--ids', '--role', 'admin'] }
136
+ before do
137
+ stub_api_request(:patch, "broker/rest/domains/test/members").
138
+ with(:body => {:members => [{'id' => '123', 'role' => 'admin'}]}).
139
+ to_return({:body => {:messages => [{:exit_code => 132, :field => 'id', :index => 0, :severity => 'error', :text => 'There is no user with the id 123'},]}.to_json, :status => 422})
140
+ end
141
+ it { expect { run }.to exit_with_code(1) }
142
+ it { run_output.should =~ /Adding 1 administrator to domain.*There is no user with the id 123/ }
143
+ end
144
+ end
145
+ end
146
+
147
+ describe 'remove-member' do
148
+ context "when the resource doesn't support membership changes" do
149
+ before{ stub_api }
150
+
151
+ context "when adjusting a domain" do
152
+ let(:arguments) { ['remove-member', 'testuser', '-n', 'test'] }
153
+ before{ challenge{ stub_one_domain('test', nil, mock_user_auth) } }
154
+ it { expect { run }.to exit_with_code(1) }
155
+ it { run_output.should =~ /Removing 1 member from domain .*The server does not support adding or removing members/ }
156
+ end
157
+ end
158
+
159
+ context "with supported membership" do
160
+ let(:supports_members?){ true }
161
+ before do
162
+ stub_api
163
+ challenge{ stub_one_domain('test', nil, mock_user_auth) }
164
+ end
165
+ =begin Scenario removed
166
+ context "when adjusting an app" do
167
+ let(:arguments) { ['remove-member', 'testuser', '-n', 'test', '-a', 'app'] }
168
+ before{ challenge{ stub_one_application('test', 'app') } }
169
+ it { expect { run }.to exit_with_code(1) }
170
+ it { run_output.should =~ /You can only add or remove members on a domain/ }
171
+ end
172
+ =end
173
+ context 'with a valid member' do
174
+ let(:arguments) { ['remove-member', 'testuser', '-n', 'test'] }
175
+ before do
176
+ stub_api_request(:patch, "broker/rest/domains/test/members").
177
+ with(:body => {:members => [{'login' => 'testuser', 'role' => 'none'}]}).
178
+ to_return({:body => {:type => 'members', :data => [], :messages => [{:exit_code => 0, :field => 'login', :index => 0, :severity => 'info', :text => 'Removed 1 member'},]}.to_json, :status => 200})
179
+ end
180
+ it { expect { run }.to exit_with_code(0) }
181
+ it { run_output.should =~ /Removing 1 member from domain .*done/ }
182
+ end
183
+
184
+ context 'with --all' do
185
+ let(:arguments) { ['remove-member', '--all', '-n', 'test'] }
186
+ before do
187
+ stub_api_request(:delete, "broker/rest/domains/test/members").
188
+ to_return({:body => {:type => 'members', :data => [], :messages => [{:exit_code => 0, :field => 'login', :index => 0, :severity => 'info', :text => 'Removed everyone except owner.'},]}.to_json, :status => 200})
189
+ end
190
+ it { expect { run }.to exit_with_code(0) }
191
+ it { run_output.should =~ /Removing all members from domain .*done/ }
192
+ end
193
+
194
+ context 'with a missing user' do
195
+ let(:arguments) { ['remove-member', 'testuser', '-n', 'test'] }
196
+ before do
197
+ stub_api_request(:patch, "broker/rest/domains/test/members").
198
+ with(:body => {:members => [{'login' => 'testuser', 'role' => 'none'}]}).
199
+ to_return({:body => {:messages => [{:exit_code => 132, :field => 'login', :index => 0, :severity => 'error', :text => 'There is no user with a login testuser'},]}.to_json, :status => 422})
200
+ end
201
+ it { expect { run }.to exit_with_code(1) }
202
+ it { run_output.should =~ /Removing 1 member from domain.*There is no user with a login testuser/ }
203
+ end
204
+
205
+ context 'with a missing user id and role' do
206
+ let(:arguments) { ['remove-member', '123', '-n', 'test', '--ids'] }
207
+ before do
208
+ stub_api_request(:patch, "broker/rest/domains/test/members").
209
+ with(:body => {:members => [{'id' => '123', 'role' => 'none'}]}).
210
+ to_return({:body => {:messages => [{:exit_code => 132, :field => 'id', :index => 0, :severity => 'error', :text => 'There is no user with the id 123'},]}.to_json, :status => 422})
211
+ end
212
+ it { expect { run }.to exit_with_code(1) }
213
+ it { run_output.should =~ /Removing 1 member from domain.*There is no user with the id 123/ }
214
+ end
215
+
216
+ context 'when the user isn''t a member' do
217
+ let(:arguments) { ['remove-member', 'testuser', '-n', 'test'] }
218
+ before do
219
+ stub_api_request(:patch, "broker/rest/domains/test/members").
220
+ with(:body => {:members => [{'login' => 'testuser', 'role' => 'none'}]}).
221
+ to_return({:body => {:type => 'members', :data => [], :messages => [{:exit_code => 0, :field => 'login', :index => 0, :severity => 'warning', :text => 'testuser is not a member of this domain.'},]}.to_json, :status => 200})
222
+ end
223
+ it { expect { run }.to exit_with_code(0) }
224
+ it { run_output.should =~ /Removing 1 member from domain.*testuser is not a member of this domain.*done/m }
225
+ end
226
+ end
227
+ end
228
+ end
@@ -0,0 +1,217 @@
1
+ require 'spec_helper'
2
+ require 'rest_spec_helper'
3
+ require 'rhc/commands/port_forward'
4
+
5
+ describe RHC::Commands::PortForward do
6
+
7
+ let!(:rest_client){ MockRestClient.new }
8
+ before{ user_config }
9
+
10
+ describe 'run' do
11
+ let(:arguments) { ['port-forward', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password', '--app', 'mockapp'] }
12
+
13
+ before :each do
14
+ @domain = rest_client.add_domain("mockdomain")
15
+ @app = @domain.add_application 'mockapp', 'mock-1.0'
16
+ @uri = URI.parse @app.ssh_url
17
+ @ssh = double(Net::SSH)
18
+ end
19
+
20
+ context 'when port forwarding for a down appl' do
21
+ before(:each) do
22
+ Net::SSH.should_receive(:start).with(@uri.host, @uri.user).and_yield(@ssh)
23
+ @ssh.should_receive(:exec!).with("pbox-list-ports").and_yield(nil, :stderr, '127.0.0.1:3306')
24
+ @gg = MockRestGearGroup.new(rest_client)
25
+ @app.should_receive(:gear_groups).and_return([@gg])
26
+ @gg.should_receive(:gears).and_return([{'state' => 'stopped', 'id' => 'fakegearid'}])
27
+ end
28
+ it "should error out and suggest restarting the application" do
29
+ expect { run }.to exit_with_code(1)
30
+ end
31
+ it { run_output.should match(/none.*The application is stopped\..*restart/m) }
32
+ end
33
+
34
+ context 'when port forwarding an app without ports to forward' do
35
+ before(:each) do
36
+ Net::SSH.should_receive(:start).with(@uri.host, @uri.user).and_yield(@ssh)
37
+ @ssh.should_receive(:exec!).with("pbox-list-ports").and_yield(nil, :stderr, '127.0.0.1:3306')
38
+ end
39
+ it "should error out as no ports to forward" do
40
+ expect { run }.to exit_with_code(102)
41
+ rest_client.domains[0].name.should == 'mockdomain'
42
+ rest_client.domains[0].applications.size.should == 1
43
+ rest_client.domains[0].applications[0].name.should == 'mockapp'
44
+ end
45
+ it("should report no ports") { run_output.should match("no available ports to forward.") }
46
+ end
47
+
48
+ context 'when port forwarding an app with permission denied ports' do
49
+ before(:each) do
50
+ Net::SSH.should_receive(:start).with(@uri.host, @uri.user).and_yield(@ssh)
51
+ @ssh.should_receive(:exec!).with("pbox-list-ports").and_yield(nil, :stderr, 'permission denied')
52
+ end
53
+ it "should error out as permission denied" do
54
+ expect { run }.to exit_with_code(129)
55
+ rest_client.domains[0].name.should == 'mockdomain'
56
+ rest_client.domains[0].applications.size.should == 1
57
+ rest_client.domains[0].applications[0].name.should == 'mockapp'
58
+ end
59
+ it { run_output.should match("Permission denied") }
60
+ end
61
+
62
+ context 'when port forwarding an app with ports to forward' do
63
+ before(:each) do
64
+ Net::SSH.should_receive(:start).with(@uri.host, @uri.user).and_yield(@ssh).twice
65
+ @ssh.should_receive(:exec!).with("pbox-list-ports").and_yield(nil, :stderr, 'mysql -> 127.0.0.1:3306')
66
+ forward = double(Net::SSH::Service::Forward)
67
+ @ssh.should_receive(:forward).and_return(forward)
68
+ forward.should_receive(:local).with(3306, '127.0.0.1', 3306)
69
+ @ssh.should_receive(:loop)
70
+ end
71
+ it "should run successfully" do
72
+ expect { run }.to exit_with_code(0)
73
+ rest_client.domains[0].name.should == 'mockdomain'
74
+ rest_client.domains[0].applications.size.should == 1
75
+ rest_client.domains[0].applications[0].name.should == 'mockapp'
76
+ end
77
+ it { run_output.should match(/Forwarding ports.*Press CTRL-C/m) }
78
+ end
79
+
80
+ context 'when host is unreachable' do
81
+ before(:each) do
82
+ Net::SSH.should_receive(:start).and_raise(Errno::EHOSTUNREACH)
83
+ end
84
+ it "should error out" do
85
+ expect { run }.to exit_with_code(1)
86
+ rest_client.domains[0].name.should == 'mockdomain'
87
+ rest_client.domains[0].applications.size.should == 1
88
+ rest_client.domains[0].applications[0].name.should == 'mockapp'
89
+ end
90
+ it { run_output.should include("Error trying to forward ports.") }
91
+ end
92
+
93
+ context 'when REST client connection times out' do
94
+ before(:each) do
95
+ rest_client.should_receive(:find_domain).and_raise(RHC::Rest::ConnectionException)
96
+ end
97
+ it("should error out") { expect { run }.to exit_with_code(1) }
98
+ it{ run_output.should match("Connection.*failed:") }
99
+ end
100
+
101
+ context 'when port forwarding an app with ports to forward' do
102
+ before(:each) do
103
+ Net::SSH.should_receive(:start).with(@uri.host, @uri.user).and_yield(@ssh).twice
104
+ @ssh.should_receive(:exec!).with("pbox-list-ports").and_yield(nil, :stderr, 'mysql -> 127.0.0.1:3306')
105
+ forward = double(Net::SSH::Service::Forward)
106
+ @ssh.should_receive(:forward).and_return(forward)
107
+ forward.should_receive(:local).with(3306, '127.0.0.1', 3306)
108
+ @ssh.should_receive(:loop).and_raise(Interrupt.new)
109
+ end
110
+ it "should exit when user interrupts" do
111
+ expect { run }.to exit_with_code(0)
112
+ rest_client.domains[0].name.should == 'mockdomain'
113
+ rest_client.domains[0].applications.size.should == 1
114
+ rest_client.domains[0].applications[0].name.should == 'mockapp'
115
+ end
116
+ it { run_output.should include("Ending port forward") }
117
+ end
118
+
119
+ context 'when local port is already bound' do
120
+ before(:each) do
121
+ Net::SSH.should_receive(:start).with(@uri.host, @uri.user).and_yield(@ssh).twice
122
+ @ssh.should_receive(:exec!).with("pbox-list-ports").and_yield(nil, :stderr, 'mysql -> 127.0.0.1:3306')
123
+ forward = double(Net::SSH::Service::Forward)
124
+ @ssh.should_receive(:forward).at_least(2).and_return(forward)
125
+ forward.should_receive(:local).with(3306, '127.0.0.1', 3306).and_raise(Errno::EACCES)
126
+ forward.should_receive(:local).with(3307, '127.0.0.1', 3306)
127
+ @ssh.should_receive(:loop).and_raise(Interrupt.new)
128
+ end
129
+ it 'should bind to a higher port' do
130
+ run_output.should include("3307")
131
+ end
132
+ end
133
+
134
+ context 'when host refuses connection' do
135
+ before(:each) do
136
+ Net::SSH.should_receive(:start).with(@uri.host, @uri.user).and_yield(@ssh).twice
137
+ @ssh.should_receive(:exec!).with("pbox-list-ports").and_yield(nil, :stderr, 'mysql -> 127.0.0.1:3306')
138
+ forward = double(Net::SSH::Service::Forward)
139
+ @ssh.should_receive(:forward).and_raise(Errno::ECONNREFUSED)
140
+ end
141
+ it "should error out" do
142
+ expect { run }.to exit_with_code(0)
143
+ end
144
+ it { run_output.should include("ssh -N") }
145
+ it { run_output.should include("Error forwarding") }
146
+ end
147
+
148
+ context 'when port forwarding a scaled app with ports to forward' do
149
+ let(:haproxy_host_1) { '127.0.0.1' }
150
+ let(:haproxy_host_2) { '127.0.0.2' }
151
+ let(:mongo_host) { '51125bb94a-test907742.dev.protonbox.me' }
152
+ let(:ipv6_host) { '::1' }
153
+ before(:each) do
154
+ Net::SSH.should_receive(:start).with(@uri.host, @uri.user).and_yield(@ssh).twice
155
+ @ssh.should_receive(:exec!).with("pbox-list-ports").
156
+ and_yield(nil, :stderr, "httpd -> #{haproxy_host_1}:8080\nhttpd -> #{haproxy_host_2}:8080\nmongodb -> #{mongo_host}:35541\nmysqld -> #{ipv6_host}:3306")
157
+ forward = double(Net::SSH::Service::Forward)
158
+ @ssh.should_receive(:forward).at_least(3).times.and_return(forward)
159
+ forward.should_receive(:local).with(8080, haproxy_host_1, 8080)
160
+ forward.should_receive(:local).with(8080, haproxy_host_2, 8080).and_raise(Errno::EADDRINUSE)
161
+ forward.should_receive(:local).with(8081, haproxy_host_2, 8080)
162
+ forward.should_receive(:local).with(35541, mongo_host, 35541)
163
+ forward.should_receive(:local).with(3306, ipv6_host, 3306)
164
+ @ssh.should_receive(:loop).and_raise(Interrupt.new)
165
+ end
166
+ it "should exit when user interrupts" do
167
+ expect { run }.to exit_with_code(0)
168
+ rest_client.domains[0].name.should == 'mockdomain'
169
+ rest_client.domains[0].applications.size.should == 1
170
+ rest_client.domains[0].applications[0].name.should == 'mockapp'
171
+ end
172
+ it { run_output.should include("Ending port forward") }
173
+ end
174
+
175
+ context 'when port forwarding a single gear on a scaled app' do
176
+ let(:gear_host) { 'fakesshurl.com' }
177
+ let(:gear_user) { 'fakegearid0' }
178
+ let(:arguments) { ['port-forward', '--noprompt', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password', '--app', 'mockapp', '--gear', @gear_id] }
179
+
180
+ it 'should run successfully' do
181
+ @gear_id = 'fakegearid0'
182
+ Net::SSH.should_receive(:start).with(gear_host, gear_user).and_yield(@ssh).twice
183
+
184
+ @ssh.should_receive(:exec!).with("pbox-list-ports --exclude-remote").
185
+ and_yield(nil, :stderr, "mongodb -> #{gear_host}:35541")
186
+ forward = double(Net::SSH::Service::Forward)
187
+ @ssh.should_receive(:forward).and_return(forward)
188
+ forward.should_receive(:local).with(35541, gear_host, 35541)
189
+ @ssh.should_receive(:loop).and_raise(Interrupt.new)
190
+
191
+ expect { run }.to exit_with_code(0)
192
+ end
193
+
194
+ it 'should fail if the specified gear is missing' do
195
+ @gear_id = 'notarealgearxxxxx'
196
+
197
+ expect { run }.to exit_with_code(1)
198
+ run_output.should include('Gear notarealgearxxxxx not found')
199
+ end
200
+
201
+ it 'should fail if the specified gear has no ssh info' do
202
+ @gear_id = 'fakegearid0'
203
+
204
+ # Given - gears in gear group should not have ssh info
205
+ gg = MockRestGearGroup.new(rest_client)
206
+ @app.stub(:gear_groups).and_return([gg])
207
+ gg.stub(:gears).and_return([{'state' => 'started', 'id' => 'fakegearid0'}])
208
+
209
+ expect { run }.to exit_with_code(1)
210
+ run_output.should match('The server does not support operations on individual gears.')
211
+ end
212
+
213
+ end
214
+
215
+ end
216
+ end
217
+
@@ -0,0 +1,69 @@
1
+ require 'spec_helper'
2
+ require 'rest_spec_helper'
3
+ require 'rhc/commands/server'
4
+ require 'rhc/config'
5
+
6
+ describe RHC::Commands::Server do
7
+ before(:each){ user_config }
8
+ let(:rest_client) { MockRestClient.new }
9
+
10
+ describe 'run against a different server' do
11
+ let(:arguments) { ['server', '--server', 'foo.com', '-l', 'person', '-p', ''] }
12
+
13
+ context 'when server refuses connection' do
14
+ before { stub_request(:get, 'https://foo.com/broker/rest/api').with(&user_agent_header).to_raise(SocketError) }
15
+ it('should output an error') { run_output.should =~ /Connected to foo.com.*Unable to connect to the server/m }
16
+ it { expect { run }.to exit_with_code(1) }
17
+ end
18
+
19
+ context 'when API is missing' do
20
+ before { stub_request(:get, 'https://foo.com/broker/rest/api').with(&user_agent_header).to_return(:status => 404) }
21
+ it('should output an error') { run_output.should =~ /Connected to foo.com.*server is not responding correctly/m }
22
+ it { expect { run }.to exit_with_code(1) }
23
+ end
24
+
25
+ context 'when API is at version 1.2' do
26
+ before do
27
+ rest_client.stub(:api_version_negotiated).and_return('1.2')
28
+ end
29
+ it('should output an error') { run_output.should =~ /Connected to foo.com.*Using API version 1.2/m }
30
+ it { expect { run }.to exit_with_code(0) }
31
+ end
32
+ end
33
+
34
+ describe 'run against an invalid server url' do
35
+ let(:arguments) { ['server', '--server', 'invalid_uri', '-l', 'person', '-p', ''] }
36
+ it('should output an invalid URI error') { run_output.should match('Invalid URI specified: invalid_uri') }
37
+ end
38
+
39
+ describe 'run' do
40
+ let(:arguments) { ['server'] }
41
+ before{ rest_client.stub(:auth).and_return(nil) }
42
+
43
+ context 'when no issues' do
44
+ before { stub_request(:get, 'https://api.protonbox.com/app/status/status.json').with(&user_agent_header).to_return(:body => {'issues' => []}.to_json) }
45
+ it('should output success') { run_output.should =~ /All systems running fine/ }
46
+ it { expect { run }.to exit_with_code(0) }
47
+ end
48
+
49
+ context 'when 1 issue' do
50
+ before do
51
+ stub_request(:get, 'https://api.protonbox.com/app/status/status.json').with(&user_agent_header).to_return(:body =>
52
+ {'open' => [
53
+ {'issue' => {
54
+ 'created_at' => '2011-05-22T17:31:32-04:00',
55
+ 'id' => 11,
56
+ 'title' => 'Root cause',
57
+ 'updates' => [{
58
+ 'created_at' => '2012-05-22T13:48:20-04:00',
59
+ 'description' => 'Working on update'
60
+ }]
61
+ }}]}.to_json)
62
+ end
63
+ it { expect { run }.to exit_with_code(1) }
64
+ it('should output message') { run_output.should =~ /1 open issue/ }
65
+ it('should output title') { run_output.should =~ /Root cause/ }
66
+ it('should contain update') { run_output.should =~ /Working on update/ }
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,118 @@
1
+ require 'spec_helper'
2
+ require 'rest_spec_helper'
3
+ require 'rhc/commands/setup'
4
+
5
+ # just test the command runner as we already have extensive wizard tests
6
+ describe RHC::Commands::Setup do
7
+ subject{ RHC::Commands::Setup }
8
+ let(:instance){ subject.new }
9
+ let!(:config){ base_config }
10
+ before{ described_class.send(:public, *described_class.protected_instance_methods) }
11
+ before{ FakeFS::FileSystem.clear }
12
+ before{ RHC::Config.stub(:home_dir).and_return('/home/mock_user') }
13
+
14
+ describe '#run' do
15
+ it{ expects_running('setup').should call(:run).on(instance).with(no_args) }
16
+
17
+ let(:arguments) { ['setup', '--config', 'test.conf', '-l', 'test@test.foo', '-p', 'password'] }
18
+
19
+ before(:each) do
20
+ @wizard = double('wizard')
21
+ @wizard.stub(:run).and_return(true)
22
+ RHC::RerunWizard.stub(:new){ @wizard }
23
+ end
24
+
25
+ context 'when no issues' do
26
+ it "should exit 0" do
27
+ expect { run }.to exit_with_code(0)
28
+ end
29
+ end
30
+
31
+ context 'when there is an issue' do
32
+ it "should exit 1" do
33
+ @wizard.stub(:run).and_return(false)
34
+ expect { run }.to exit_with_code(1)
35
+ end
36
+ end
37
+ end
38
+
39
+ it{ expects_running('setup').should call(:run).on(instance).with(no_args) }
40
+ it{ command_for('setup', '--clean').options.clean.should be_true }
41
+
42
+ it{ command_for('setup').options.server.should == 'api.protonbox.com' }
43
+ it{ command_for('setup', '--server', 'foo.com').options.server.should == 'foo.com' }
44
+ it{ command_for('setup', '--no-create-token').options.create_token.should == false }
45
+ it{ command_for('setup', '--create-token').options.create_token.should == true }
46
+
47
+ context "when config has use_authorization_tokens=false" do
48
+ let!(:config){ base_config{ |c, d| d.add('use_authorization_tokens', 'false') } }
49
+ it{ command_for('setup').options.use_authorization_tokens.should == false }
50
+ end
51
+ context "when config has use_authorization_tokens=true" do
52
+ let!(:config){ base_config{ |c, d| d.add('use_authorization_tokens', 'true') } }
53
+ it{ command_for('setup').options.use_authorization_tokens.should == true }
54
+ end
55
+
56
+ =begin context 'when protonbox_server is set' do
57
+ before{ ENV.stub(:[]).with('PROTONBOX_SERVER').and_return('bar.com') }
58
+ it{ command_for('setup').config['protonbox_server'].should == 'bar.com' }
59
+ it{ command_for('setup').options.server.should == 'bar.com' }
60
+ it{ command_for('setup', '--server', 'foo.com').options.server.should == 'foo.com' }
61
+ =end end
62
+
63
+ context 'when --clean is used' do
64
+ let!(:config){ base_config{ |config, defaults| defaults.add 'protonbox_server', 'test.com' } }
65
+
66
+ it("should ignore a config value"){ command_for('setup', '--clean').options.server.should == 'api.protonbox.com' }
67
+ end
68
+
69
+ context 'when -d is passed' do
70
+ let(:arguments) { ['setup', '-d', '-l', 'test@test.foo'] }
71
+ # 'y' for the password prompt
72
+ let(:input) { ['', 'y', '', ''] }
73
+ let!(:rest_client){ MockRestClient.new }
74
+
75
+ it("succeeds"){ FakeFS{ expect { run input }.to exit_with_code 0 } }
76
+ it("the output includes debug output") do
77
+ FakeFS{ run_output( input ).should match 'DEBUG' }
78
+ end
79
+ end
80
+
81
+ context 'when -l is used to specify the user name' do
82
+ let(:arguments) { ['setup', '-l', 'test@test.foo'] }
83
+ # 'y' for the password prompt
84
+ let(:input) { ['', 'y', '', ''] }
85
+ let!(:rest_client){ MockRestClient.new }
86
+
87
+ it("succeeds"){ FakeFS{ expect { run input }.to exit_with_code 0 } }
88
+ it("sets the user name to the value given by the command line") do
89
+ FakeFS{ run_output( input ).should match 'test@test.foo' }
90
+ end
91
+ end
92
+
93
+ describe 'help' do
94
+ let(:arguments) { ['setup', '--help'] }
95
+
96
+ context 'help is run' do
97
+ it "should display help" do
98
+ @wizard.stub(:run).and_return(true)
99
+ expect { run }.to exit_with_code(0)
100
+ end
101
+ it('should output usage') { run_output.should match("Connects to an ProtonBox server to get you started. Will") }
102
+ end
103
+ end
104
+
105
+ describe '--autocomplete' do
106
+ let(:arguments) { ['setup', '--autocomplete'] }
107
+ before do
108
+ path = File.join(Gem.loaded_specs['pbox'].full_gem_path, "autocomplete")
109
+ FakeFS::FileUtils.mkdir_p(path)
110
+ FakeFS::FileUtils.touch(File.join(path, "pbox_bash"))
111
+ end
112
+
113
+ context 'is passed' do
114
+ it('should output information') { FakeFS{ run_output.should match("To enable tab-completion") } }
115
+ it('should output the gem path') { FakeFS{ run_output.should match File.join(RHC::Config.home_conf_dir, 'bash_autocomplete') } }
116
+ end
117
+ end
118
+ end