pbox 1.17.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/COPYRIGHT +1 -0
- data/LICENSE +11 -0
- data/README.md +40 -0
- data/Rakefile +6 -0
- data/autocomplete/pbox_bash +1639 -0
- data/bin/pbox +37 -0
- data/conf/protonbox.conf +8 -0
- data/features/assets/deploy.tar.gz +0 -0
- data/features/core_feature.rb +178 -0
- data/features/deployments_feature.rb +127 -0
- data/features/domains_feature.rb +49 -0
- data/features/keys_feature.rb +37 -0
- data/features/members_feature.rb +166 -0
- data/lib/rhc/auth/basic.rb +64 -0
- data/lib/rhc/auth/token.rb +102 -0
- data/lib/rhc/auth/token_store.rb +53 -0
- data/lib/rhc/auth.rb +5 -0
- data/lib/rhc/autocomplete.rb +66 -0
- data/lib/rhc/autocomplete_templates/bash.erb +39 -0
- data/lib/rhc/cartridge_helpers.rb +118 -0
- data/lib/rhc/cli.rb +40 -0
- data/lib/rhc/command_runner.rb +186 -0
- data/lib/rhc/commands/account.rb +25 -0
- data/lib/rhc/commands/alias.rb +124 -0
- data/lib/rhc/commands/app.rb +701 -0
- data/lib/rhc/commands/apps.rb +20 -0
- data/lib/rhc/commands/authorization.rb +96 -0
- data/lib/rhc/commands/base.rb +174 -0
- data/lib/rhc/commands/cartridge.rb +326 -0
- data/lib/rhc/commands/deployment.rb +82 -0
- data/lib/rhc/commands/domain.rb +167 -0
- data/lib/rhc/commands/env.rb +142 -0
- data/lib/rhc/commands/git_clone.rb +29 -0
- data/lib/rhc/commands/logout.rb +51 -0
- data/lib/rhc/commands/member.rb +148 -0
- data/lib/rhc/commands/port_forward.rb +197 -0
- data/lib/rhc/commands/server.rb +40 -0
- data/lib/rhc/commands/setup.rb +60 -0
- data/lib/rhc/commands/snapshot.rb +137 -0
- data/lib/rhc/commands/ssh.rb +51 -0
- data/lib/rhc/commands/sshkey.rb +97 -0
- data/lib/rhc/commands/tail.rb +47 -0
- data/lib/rhc/commands/threaddump.rb +14 -0
- data/lib/rhc/commands.rb +396 -0
- data/lib/rhc/config.rb +320 -0
- data/lib/rhc/context_helper.rb +121 -0
- data/lib/rhc/core_ext.rb +202 -0
- data/lib/rhc/coverage_helper.rb +33 -0
- data/lib/rhc/deployment_helpers.rb +88 -0
- data/lib/rhc/exceptions.rb +232 -0
- data/lib/rhc/git_helpers.rb +91 -0
- data/lib/rhc/help_formatter.rb +55 -0
- data/lib/rhc/helpers.rb +477 -0
- data/lib/rhc/highline_extensions.rb +479 -0
- data/lib/rhc/json.rb +51 -0
- data/lib/rhc/output_helpers.rb +260 -0
- data/lib/rhc/rest/activation.rb +11 -0
- data/lib/rhc/rest/alias.rb +42 -0
- data/lib/rhc/rest/api.rb +87 -0
- data/lib/rhc/rest/application.rb +332 -0
- data/lib/rhc/rest/attributes.rb +36 -0
- data/lib/rhc/rest/authorization.rb +8 -0
- data/lib/rhc/rest/base.rb +79 -0
- data/lib/rhc/rest/cartridge.rb +154 -0
- data/lib/rhc/rest/client.rb +650 -0
- data/lib/rhc/rest/deployment.rb +18 -0
- data/lib/rhc/rest/domain.rb +98 -0
- data/lib/rhc/rest/environment_variable.rb +15 -0
- data/lib/rhc/rest/gear_group.rb +16 -0
- data/lib/rhc/rest/httpclient.rb +145 -0
- data/lib/rhc/rest/key.rb +44 -0
- data/lib/rhc/rest/membership.rb +105 -0
- data/lib/rhc/rest/mock.rb +1024 -0
- data/lib/rhc/rest/user.rb +32 -0
- data/lib/rhc/rest.rb +148 -0
- data/lib/rhc/ssh_helpers.rb +378 -0
- data/lib/rhc/tar_gz.rb +51 -0
- data/lib/rhc/usage_templates/command_help.erb +51 -0
- data/lib/rhc/usage_templates/command_syntax_help.erb +11 -0
- data/lib/rhc/usage_templates/help.erb +35 -0
- data/lib/rhc/usage_templates/missing_help.erb +1 -0
- data/lib/rhc/usage_templates/options_help.erb +12 -0
- data/lib/rhc/vendor/okjson.rb +600 -0
- data/lib/rhc/vendor/parseconfig.rb +178 -0
- data/lib/rhc/vendor/sshkey.rb +253 -0
- data/lib/rhc/vendor/zliby.rb +628 -0
- data/lib/rhc/version.rb +5 -0
- data/lib/rhc/wizard.rb +633 -0
- data/lib/rhc.rb +34 -0
- data/spec/coverage_helper.rb +89 -0
- data/spec/direct_execution_helper.rb +338 -0
- data/spec/keys/example.pem +23 -0
- data/spec/keys/example_private.pem +27 -0
- data/spec/keys/server.pem +19 -0
- data/spec/rest_spec_helper.rb +31 -0
- data/spec/rhc/assets/cert.crt +22 -0
- data/spec/rhc/assets/cert_key_rsa +27 -0
- data/spec/rhc/assets/empty.txt +0 -0
- data/spec/rhc/assets/env_vars.txt +7 -0
- data/spec/rhc/assets/env_vars_2.txt +1 -0
- data/spec/rhc/assets/foo.txt +1 -0
- data/spec/rhc/assets/targz_corrupted.tar.gz +1 -0
- data/spec/rhc/assets/targz_sample.tar.gz +0 -0
- data/spec/rhc/auth_spec.rb +442 -0
- data/spec/rhc/cli_spec.rb +188 -0
- data/spec/rhc/command_spec.rb +435 -0
- data/spec/rhc/commands/account_spec.rb +42 -0
- data/spec/rhc/commands/alias_spec.rb +333 -0
- data/spec/rhc/commands/app_spec.rb +754 -0
- data/spec/rhc/commands/apps_spec.rb +39 -0
- data/spec/rhc/commands/authorization_spec.rb +145 -0
- data/spec/rhc/commands/cartridge_spec.rb +641 -0
- data/spec/rhc/commands/deployment_spec.rb +286 -0
- data/spec/rhc/commands/domain_spec.rb +383 -0
- data/spec/rhc/commands/env_spec.rb +493 -0
- data/spec/rhc/commands/git_clone_spec.rb +80 -0
- data/spec/rhc/commands/logout_spec.rb +86 -0
- data/spec/rhc/commands/member_spec.rb +228 -0
- data/spec/rhc/commands/port_forward_spec.rb +217 -0
- data/spec/rhc/commands/server_spec.rb +69 -0
- data/spec/rhc/commands/setup_spec.rb +118 -0
- data/spec/rhc/commands/snapshot_spec.rb +179 -0
- data/spec/rhc/commands/ssh_spec.rb +163 -0
- data/spec/rhc/commands/sshkey_spec.rb +188 -0
- data/spec/rhc/commands/tail_spec.rb +81 -0
- data/spec/rhc/commands/threaddump_spec.rb +84 -0
- data/spec/rhc/config_spec.rb +407 -0
- data/spec/rhc/helpers_spec.rb +524 -0
- data/spec/rhc/highline_extensions_spec.rb +314 -0
- data/spec/rhc/json_spec.rb +30 -0
- data/spec/rhc/rest_application_spec.rb +248 -0
- data/spec/rhc/rest_client_spec.rb +752 -0
- data/spec/rhc/rest_spec.rb +740 -0
- data/spec/rhc/targz_spec.rb +55 -0
- data/spec/rhc/wizard_spec.rb +756 -0
- data/spec/spec_helper.rb +575 -0
- data/spec/wizard_spec_helper.rb +330 -0
- 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
|