pbox 1.17.2

Sign up to get free protection for your applications and to get access to all the features.
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