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
data/bin/pbox ADDED
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rhc/coverage_helper'
3
+
4
+ def get_args
5
+ ARGV.shift
6
+ args = ""
7
+ ARGV.each do|a|
8
+ if ( a.to_s.strip.length == 0 || a.to_s.strip.match(/\s/) ); a = "'#{a}'" end
9
+ args += " #{a}"
10
+ end
11
+ args
12
+ end
13
+
14
+ begin
15
+ Signal.trap("PIPE", "EXIT") if Signal.list["PIPE"]
16
+
17
+ retcode = begin
18
+ require 'rhc/cli'
19
+ RHC::CLI.set_terminal
20
+ RHC::CLI.start(ARGV)
21
+ rescue Interrupt
22
+ puts "Interrupted"
23
+ 128 + 2
24
+ rescue SystemExit => e
25
+ puts
26
+ e.status
27
+ end
28
+
29
+ if retcode == nil
30
+ retcode = 1
31
+
32
+ # return codes for uncaught signals are 128 + the signal code
33
+ retcode = 128 + $?.termsig if $?.signaled? and !$?.termsig.nil?
34
+ end
35
+
36
+ exit retcode
37
+ end
@@ -0,0 +1,8 @@
1
+ # Remote API server
2
+ protonbox_server = 'api.protonbox.com'
3
+
4
+ # Logging
5
+ debug = 'false'
6
+
7
+ # Timeout
8
+ #timeout = '10'
Binary file
@@ -0,0 +1,178 @@
1
+ require 'spec_helper'
2
+ require 'direct_execution_helper'
3
+
4
+ describe "pbox core scenarios" do
5
+
6
+ it "reports a version" do
7
+ r = pbox '--version'
8
+ r.status.should == 0
9
+ r.stdout.should match /pbox \d+\.\d+\.\d+\b/
10
+ end
11
+
12
+ it "displays help" do
13
+ r = pbox 'help'
14
+ r.status.should == 0
15
+ r.stdout.should match "Command line interface for ProtonBox"
16
+ r.stdout.should match "Usage: pbox"
17
+ r.stdout.should match "Getting started"
18
+ r.stdout.should match "See 'pbox help options' for a list"
19
+ end
20
+
21
+ context "with a clean configuration" do
22
+ before{ use_clean_config }
23
+
24
+ it "walks through a configuration" do
25
+ r = pbox :setup, :with => setup_args
26
+ r.stdout.should match 'ProtonBox Client Tools'
27
+ r.stdout.should match 'Checking for git ...'
28
+ r.stdout.should match 'Checking for applications ...'
29
+ r.stdout.should match 'Your client tools are now configured.'
30
+ r.status.should == 0
31
+
32
+ r = pbox :account
33
+ r.stdout.should match "on #{ENV['PROTONBOX_SERVER']}"
34
+ r.stdout.should match 'Gears Allowed'
35
+ r.stdout.should match 'Allowed Gear Sizes'
36
+ r.stdout.should match 'Gears Used'
37
+ r.stdout.should match 'SSL Certificates'
38
+ end
39
+
40
+ it "starts the wizard on default invocation" do
41
+ r = pbox
42
+ r.stdout.should match "ProtonBox Client Tools"
43
+ end
44
+ end
45
+
46
+ context "when creating an app" do
47
+ when_running 'create-app', 'test1', a_web_cartridge
48
+ before{ no_applications }
49
+ it "returns the proper info and is in the rest api" do
50
+ status.should == 0
51
+ output.should match "Your application 'test1' is now available"
52
+ output.should match /Gear Size: .*default/
53
+ output.should match /Scaling: .*no/
54
+ output.should match %r(URL: .*http://test1-)
55
+ output.should match "Cloned to"
56
+
57
+ apps = client.applications
58
+ apps.should_not be_empty
59
+ apps.should include{ |app| app.name == 'test1' }
60
+ end
61
+ end
62
+
63
+ context "with an existing app" do
64
+ before(:all) do
65
+ standard_config
66
+ @app = has_an_application
67
+ end
68
+
69
+ let(:app){ @app }
70
+
71
+ it "should display domain list" do
72
+ r = pbox 'domains'
73
+ r.status.should == 0
74
+ r.stdout.should match "Domain #{app.domain_id}"
75
+ end
76
+
77
+ it "should show app state" do
78
+ r = pbox 'app-show', app.name, '--state'
79
+ r.status.should == 0
80
+ r.stdout.should match "Cartridge #{a_web_cartridge} is started"
81
+ end
82
+
83
+ it "should stop and start the app" do
84
+ r = pbox 'stop-app', app.name
85
+ r.status.should == 0
86
+ r.stdout.should match "#{app.name} stopped"
87
+ r = pbox 'start-app', app.name
88
+ r.status.should == 0
89
+ r.stdout.should match "#{app.name} started"
90
+ end
91
+
92
+ it "should show gear status" do
93
+ r = pbox 'app-show', app.name, '--gears'
94
+ r.status.should == 0
95
+ r.stdout.lines.to_a.length.should == 3
96
+ r.stdout.should match app.ssh_string
97
+ app.cartridges.map(&:name).each do |c|
98
+ r.stdout.should match c
99
+ end
100
+ r.stdout.should match "started"
101
+ end
102
+
103
+ it "should show gear ssh strings" do
104
+ r = pbox 'app-show', app.name, '--gears', 'ssh'
105
+ r.status.should == 0
106
+ r.stdout.lines.to_a.length.should == 1
107
+ r.stdout.chomp.should == app.ssh_string
108
+ end
109
+
110
+ context "when the app is cloned" do
111
+ before(:all) do
112
+ pbox('git-clone', @app.name).status.should == 0
113
+ Dir.exists?(@app.name).should be_true
114
+ Dir.chdir @app.name
115
+ end
116
+ let(:git_config){ `git config --list` }
117
+
118
+ it "will set Git config values" do
119
+ git_config.should match "pbox.app-id=#{app.id}"
120
+ git_config.should match "pbox.app-name=#{app.name}"
121
+ git_config.should match "pbox.domain-name=#{app.domain_name}"
122
+ end
123
+
124
+ it "will infer the current app from the git repository" do
125
+ r = pbox 'show-app'
126
+ r.stdout.should match app.name
127
+ r.stdout.should match app.id
128
+ r.stdout.should match app.ssh_string
129
+ r.stdout.should match app.app_url
130
+ (app.cartridges.map(&:name) + app.cartridges.map(&:display_name)).each{ |n| r.stdout.should match n }
131
+ r.status.should == 0
132
+ end
133
+
134
+ it "will fetch the quotas from the app" do
135
+ r = pbox 'show-app', '--gears', 'quota'
136
+ r.stdout.chomp.lines.count.should == (app.gear_count + 2)
137
+ app.cartridges.map(&:name).each{ |n| r.stdout.should match n }
138
+ app.cartridges.map(&:gear_storage).each{ |n| r.stdout.should match(RHC::Helpers.human_size(n)) }
139
+ r.status.should == 0
140
+ end
141
+
142
+ it "will ssh to the app and run a command" do
143
+ r = pbox 'ssh', '--', '--ssh', ENV['GIT_SSH'], 'echo $PROTONBOX_APP_NAME'
144
+ r.stdout.should match app.name
145
+ r.status.should == 0
146
+ end
147
+ end
148
+ end
149
+
150
+ context "when adding a cartridge" do
151
+ context "with a scalable app" do
152
+ before(:all) do
153
+ standard_config
154
+ @app = has_a_scalable_application
155
+ end
156
+
157
+ after(:all) do
158
+ pbox 'app-delete', @app.name
159
+ end
160
+
161
+ let(:app){ @app }
162
+
163
+ it "should add a cartridge with small gear size" do
164
+ r = pbox 'add-cartridge', an_addon_cartridge, '-a', app.name, '--gear-size', 'small'
165
+ r.stdout.should match /#{an_addon_cartridge}/
166
+ r.stdout.should match /Gears:\s+1 small/
167
+ r.status.should == 0
168
+ end
169
+
170
+ it "should fail for a cartridge with not allowed gear size" do
171
+ r = pbox 'add-cartridge', an_addon_cartridge, '-a', app.name, '--gear-size', 'medium'
172
+ r.stdout.should match "The gear size 'medium' is not valid for this domain. Allowed sizes: small."
173
+ r.status.should_not == 0
174
+ end
175
+ end
176
+
177
+ end
178
+ end
@@ -0,0 +1,127 @@
1
+ require 'spec_helper'
2
+ require 'direct_execution_helper'
3
+ require 'httpclient'
4
+ require 'fileutils'
5
+
6
+ DEPLOYMENT_LIST_ITEM = /([0-2]?[0-9]:[0-5][0-9] (AM|PM), deployment [a-f0-9]{8})/
7
+
8
+ describe "pbox deployment scenarios" do
9
+ context "with an existing app" do
10
+ before(:all) do
11
+ standard_config
12
+ @app = has_an_application
13
+ end
14
+
15
+ let(:app){ @app }
16
+
17
+ it "should display deployment list" do
18
+ r = list_deployments
19
+ r.stdout.should match DEPLOYMENT_LIST_ITEM
20
+ end
21
+
22
+ it "should configure the app for a git ref deployment" do
23
+ r = configure_app_for_manual_git_deployment
24
+ r.stdout.should match /Deployment:\s+manual/
25
+ r.stdout.should match /Keep Deployments:\s+10/
26
+ r.stdout.should match /Deployment Type:\s+git/
27
+ r.stdout.should match /Deployment Branch:\s+master/
28
+ end
29
+
30
+ it "should configure the app for a binary deployment" do
31
+ r = configure_app_for_manual_binary_deployment
32
+ r.stdout.should match /Deployment:\s+manual/
33
+ r.stdout.should match /Keep Deployments:\s+10/
34
+ r.stdout.should match /Deployment Type:\s+binary/
35
+ r.stdout.should match /Deployment Branch:\s+master/
36
+ end
37
+
38
+ it "should deploy a git ref" do
39
+ configure_app_for_manual_git_deployment
40
+ r = deploy_master
41
+ r.stdout.should match /Deployment of git ref 'master' in progress for application #{app.name}/
42
+ r.stdout.should match /Success/
43
+ r = list_deployments
44
+ r.stdout.should match DEPLOYMENT_LIST_ITEM
45
+ r.stdout.scan(DEPLOYMENT_LIST_ITEM).length.should > 1
46
+ end
47
+
48
+ it "should perform a complete deploy workflow" do
49
+ configure_app_for_manual_git_deployment
50
+ edit_simple_change 'Welcome to Test'
51
+ app_page_content.should match /Welcome to ProtonBox/
52
+ app_page_content.should_not match /Welcome to Test/
53
+ deploy_master
54
+ app_page_content.should match /Welcome to Test/
55
+ app_page_content.should_not match /Welcome to ProtonBox/
56
+ deployment_id = find_inactive_deployment
57
+ deployment_id.should_not be_nil
58
+ activate deployment_id
59
+ app_page_content.should match /Welcome to ProtonBox/
60
+ app_page_content.should_not match /Welcome to Test/
61
+ end
62
+
63
+ private
64
+ def configure_app_for_manual_git_deployment
65
+ ensure_command 'configure-app', app.name, '--no-auto-deploy', '--keep-deployments', 10, '--deployment-type', 'git'
66
+ end
67
+
68
+ def configure_app_for_manual_binary_deployment
69
+ ensure_command 'configure-app', app.name, '--no-auto-deploy', '--keep-deployments', 10, '--deployment-type', 'binary'
70
+ end
71
+
72
+ def list_deployments
73
+ ensure_command 'deployments', app.name
74
+ end
75
+
76
+ def deploy(ref)
77
+ ensure_command 'deploy', ref, '-a', app.name
78
+ end
79
+
80
+ def deploy_master
81
+ deploy 'master'
82
+ end
83
+
84
+ def activate(deployment_id)
85
+ ensure_command 'activate-deployment', deployment_id, '-a', app.name
86
+ end
87
+
88
+ def snapshot_deployment
89
+ ensure_command 'save-snapshot', app.name, '--deployment'
90
+ end
91
+
92
+ def git_clone
93
+ ensure_command 'git-clone', app.name, '-r', git_directory
94
+ Dir.exists?(git_directory).should be_true
95
+ end
96
+
97
+ def edit_simple_change(content)
98
+ FileUtils.rm_rf git_directory
99
+ git_clone
100
+ Dir.chdir git_directory
101
+ `sed -i "s/Welcome to ProtonBox/#{content}/" php/index.php`
102
+ `git commit -a -m "Commit from Feature Tests"`
103
+ `git push origin master`
104
+ Dir.chdir '../'
105
+ FileUtils.rm_rf git_directory
106
+ end
107
+
108
+ def app_page_content
109
+ HTTPClient.new.get_content(app.app_url)
110
+ end
111
+
112
+ def git_directory
113
+ "#{app.name}_feature_tests_repo"
114
+ end
115
+
116
+ def find_inactive_deployment
117
+ r = list_deployments
118
+ r.stdout.match(/deployment ([a-f0-9]{8})/)[1]
119
+ end
120
+
121
+ def ensure_command(*args)
122
+ r = pbox *args
123
+ r.status.should == 0
124
+ r
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+ require 'direct_execution_helper'
3
+
4
+ describe "pbox domain scenarios" do
5
+ context "with an existing domain" do
6
+ before(:all) do
7
+ standard_config
8
+ @domain = has_a_domain
9
+ end
10
+ let(:domain){ @domain }
11
+
12
+ it "should display the domain configuration" do
13
+ r = pbox 'configure-domain', domain.name
14
+ r.status.should == 0
15
+ if domain.allowed_gear_sizes
16
+ r.stdout.should match "Allowed Gear Sizes:\s+#{domain.allowed_gear_sizes.join(", ")}"
17
+ else
18
+ r.stdout.should_not match "Allowed Gear Sizes:"
19
+ end
20
+ end
21
+
22
+ it "should change the domain configuration" do
23
+ r = pbox 'configure-domain', domain.name, '--no-allowed-gear-sizes'
24
+ r.status.should == 0
25
+ r.stdout.should match "Allowed Gear Sizes:\s+<none>$"
26
+ client.reset.find_domain(domain.name).allowed_gear_sizes.should == []
27
+
28
+ all_sizes = client.user.capabilities.gear_sizes
29
+ r = pbox 'configure-domain', domain.name, '--allowed-gear-sizes', all_sizes.join(',')
30
+ r.status.should == 0
31
+ r.stdout.should match "Allowed Gear Sizes:\s+#{all_sizes.join(', ')}$"
32
+ client.reset.find_domain(domain.name).allowed_gear_sizes.should == all_sizes
33
+ end
34
+
35
+ it "should reject invalid gear size configuration changes" do
36
+ all_sizes = client.user.capabilities.gear_sizes
37
+
38
+ r = pbox 'configure-domain', domain.name, '--allowed-gear-sizes', '_not_a_size_'
39
+ r.status.should_not == 1
40
+ r.stdout.should match "Updating domain configuration.*The following gear sizes are invalid: _not_a_size_"
41
+ client.reset.find_domain(domain.name).allowed_gear_sizes.should == all_sizes
42
+
43
+ r = pbox 'configure-domain', domain.name, '--allowed-gear-sizes'
44
+ r.status.should_not == 1
45
+ r.stdout.should match "invalid option: Provide a comma delimited .* --allowed-gear-sizes"
46
+ client.reset.find_domain(domain.name).allowed_gear_sizes.should == all_sizes
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+ require 'direct_execution_helper'
3
+
4
+ describe "pbox sshkey scenarios" do
5
+ context "with an existing domain" do
6
+ before(:all) do
7
+ standard_config
8
+ @domain = has_a_domain
9
+ end
10
+
11
+ let(:domain){ @domain }
12
+
13
+ context "with an application" do
14
+ before{ has_an_application }
15
+
16
+ it "should add and remove kerberos keys on gear" do
17
+ app = @domain.applications.first
18
+ keyname = "key#{rand(1000000000000)}"
19
+ keycontent = "principal#{rand(1000000000000)}"
20
+
21
+ r = pbox 'sshkey', 'add', keyname, '--type', 'krb5-principal', '--content', keycontent
22
+ r.status.should == 0
23
+
24
+ r = pbox 'ssh', app.name, '-n', domain.name, '--ssh', ssh_exec_for_env, '--', 'if [ -f .k5login ]; then cat .k5login; fi'
25
+ r.status.should == 0
26
+ r.stdout.should match(Regexp.new("#{keyname}\n#{keycontent}"))
27
+
28
+ r = pbox 'sshkey', 'remove', keyname
29
+ r.status.should == 0
30
+
31
+ r = pbox 'ssh', app.name, '-n', domain.name, '--ssh', ssh_exec_for_env, '--', 'if [ -f .k5login ]; then cat .k5login; fi'
32
+ r.status.should == 0
33
+ r.stdout.should_not match(Regexp.new("#{keyname}\n#{keycontent}"))
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,166 @@
1
+ require 'spec_helper'
2
+ require 'direct_execution_helper'
3
+
4
+ describe "pbox member scenarios" do
5
+ context "with an existing domain" do
6
+ before(:all) do
7
+ standard_config
8
+ @domain = has_a_domain
9
+ end
10
+
11
+ let(:domain){ @domain }
12
+
13
+ context "with no users" do
14
+ before{ no_members(domain) }
15
+
16
+ it "should not show members in the domain" do
17
+ r = pbox 'show-domain', domain.name
18
+ r.status.should == 0
19
+ r.stdout.should_not match "Members:"
20
+ r.stdout.should match "owned by #{domain.owner}"
21
+ end
22
+
23
+ it "should prevent leaving the domain for the owner" do
24
+ r = pbox 'leave-domain', domain.name
25
+ r.status.should_not == 1
26
+ r.stdout.should match "Leaving domain.*You are the owner of this domain and cannot leave"
27
+ end
28
+
29
+ it "should add and remove a member" do
30
+ user = other_users.keys.take(1).first
31
+ r = pbox 'add-member', user, '-n', domain.name
32
+ r.status.should == 0
33
+ r.stdout.should match "Adding 1 editor to domain"
34
+ r.stdout.should match "done"
35
+ client.find_domain(domain.name).members.any?{ |m| m.id == other_users[user].id && m.editor? }.should be_true
36
+
37
+ r = pbox 'show-domain', domain.name
38
+ r.status.should == 0
39
+ r.stdout.should match "Members:"
40
+ r.stdout.should match "#{user} \\(edit\\)"
41
+
42
+ r = pbox 'remove-member', user, '-n', domain.name
43
+ r.status.should == 0
44
+ r.stdout.should match "Removing 1 member from domain"
45
+ client.find_domain(domain.name).members.none?{ |m| m.id == other_users[user].id }.should be_true
46
+ end
47
+
48
+ it "should add and remove two members" do
49
+ user1, user2 = other_users.keys.take(2)
50
+ r = pbox 'add-member', user1, user2, '-n', domain.name
51
+ r.status.should == 0
52
+ r.stdout.should match "Adding 2 editors to domain"
53
+ r.stdout.should match "done"
54
+ members = client.find_domain(domain.name).members
55
+ members.any?{ |m| m.id == other_users[user1].id && m.editor? }.should be_true
56
+ members.any?{ |m| m.id == other_users[user2].id && m.editor? }.should be_true
57
+
58
+ r = pbox 'show-domain', domain.name
59
+ r.status.should == 0
60
+ r.stdout.should match "Members:"
61
+ r.stdout.should match "#{user1} \\(edit\\)"
62
+ r.stdout.should match "#{user2} \\(edit\\)"
63
+
64
+ r = pbox 'remove-member', user1, user2, '-n', domain.name
65
+ r.status.should == 0
66
+ r.stdout.should match "Removing 2 members from domain"
67
+ client.find_domain(domain.name).members.none?{ |m| m.id == other_users[user1].id }.should be_true
68
+ client.find_domain(domain.name).members.none?{ |m| m.id == other_users[user2].id }.should be_true
69
+ end
70
+
71
+ it "should add a view and an admin member. and allow users to leave the domain" do
72
+ user1, user2 = other_users.keys.take(2)
73
+
74
+ r = pbox 'add-member', user1, '--role', 'admin', '-n', domain.name
75
+ r.status.should == 0
76
+ r.stdout.should match "Adding 1 administrator to domain"
77
+ r.stdout.should match "done"
78
+ client.find_domain(domain.name).members.any?{ |m| m.id == other_users[user1].id && m.admin? }.should be_true
79
+
80
+ r = pbox 'add-member', user2, '--role', 'view', '-n', domain.name
81
+ r.status.should == 0
82
+ r.stdout.should match "Adding 1 viewer to domain"
83
+ r.stdout.should match "done"
84
+ client.find_domain(domain.name).members.any?{ |m| m.id == other_users[user2].id && m.viewer? }.should be_true
85
+
86
+ r = pbox 'show-domain', domain.name
87
+ r.status.should == 0
88
+ r.stdout.should match "Members:"
89
+ r.stdout.should match "#{user1} \\(admin\\)"
90
+ r.stdout.should match "#{user2} \\(view\\)"
91
+
92
+ r = pbox 'leave-domain', domain.name, :as => other_users[user2]
93
+ r.status.should == 0
94
+ r.stdout.should match "Leaving domain.*done"
95
+ end
96
+
97
+ it "should remove all non owners" do
98
+ user1, user2 = other_users.keys.take(2)
99
+ r = pbox 'add-member', user1, user2, '-n', domain.name
100
+ r.status.should == 0
101
+ r.stdout.should match "Adding 2 editors to domain"
102
+ r.stdout.should match "done"
103
+ members = client.find_domain(domain.name).members
104
+ members.any?{ |m| m.id == other_users[user1].id && m.editor? }.should be_true
105
+ members.any?{ |m| m.id == other_users[user2].id && m.editor? }.should be_true
106
+
107
+ r = pbox 'remove-member', domain.name, '--all'
108
+ r.status.should == 0
109
+ r.stdout.should match "Removing all members from domain.*done"
110
+ members = client.find_domain(domain.name).members
111
+ members.select(&:owner).should == members
112
+ end
113
+
114
+ it "should reject a non-existent user" do
115
+ r = pbox 'add-member', 'not-a-user', '-n', domain.name
116
+ r.status.to_i.should == 256
117
+ r.stdout.should match "There is no account with login not-a-user."
118
+ client.find_domain(domain.name).members.length.should == 1
119
+ end
120
+
121
+ it "should add a user by id" do
122
+ user = other_users.values.take(1).first
123
+ r = pbox 'add-member', user.id, '--ids', '-n', domain.name
124
+ r.status.should == 0
125
+ r.stdout.should match "Adding 1 editor to domain"
126
+ r.stdout.should match "done"
127
+ client.find_domain(domain.name).members.any?{ |m| m.id == user.id && m.editor? }.should be_true
128
+ end
129
+ end
130
+
131
+ context "with an application" do
132
+ let(:other_user){ other_users.values.first }
133
+ before{ has_an_application }
134
+ before{ has_local_ssh_key(other_user) }
135
+
136
+ it "should allow SSH only for admin and edit roles" do
137
+ user = other_user.login
138
+ name = @domain.applications.first.name
139
+
140
+ r = pbox 'add-member', user, '--role', 'admin', '-n', domain.name
141
+ r.status.should == 0
142
+
143
+ with_environment(other_user) do
144
+ r = pbox 'ssh', name, '-n', domain.name, '--ssh', ssh_exec_for_env
145
+ r.status.should == 0
146
+ end
147
+
148
+ r = pbox 'add-member', user, '--role', 'view', '-n', domain.name
149
+ r.status.should == 0
150
+
151
+ with_environment(other_user) do
152
+ r = pbox 'ssh', name, '-n', domain.name, '--ssh', ssh_exec_for_env
153
+ r.status.to_i.should_not == 0
154
+ end
155
+
156
+ r = pbox 'add-member', user, '--role', 'edit', '-n', domain.name
157
+ r.status.should == 0
158
+
159
+ with_environment(other_user) do
160
+ r = pbox 'ssh', name, '-n', domain.name, '--ssh', ssh_exec_for_env
161
+ r.status.should == 0
162
+ end
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,64 @@
1
+ module RHC::Auth
2
+ class Basic
3
+ def initialize(*args)
4
+ if args[0].is_a?(String) or args.length > 1
5
+ @username, @password = args
6
+ else
7
+ @options = args[0] || Commander::Command::Options.new
8
+ @username = options[:pblogin]
9
+ @password = options[:password]
10
+ @no_interactive = options[:noprompt]
11
+ end
12
+ @skip_interactive = !@password.nil?
13
+ end
14
+
15
+ def to_request(request)
16
+ request[:user] ||=
17
+ lambda{ username || (request[:lazy_auth] != true && ask_username) || nil }
18
+ request[:password] ||=
19
+ lambda{ password || (username? && request[:lazy_auth] != true && ask_password) || nil }
20
+ request
21
+ end
22
+
23
+ def retry_auth?(response, client)
24
+ if response.status == 401
25
+ credentials_rejected
26
+ else
27
+ false
28
+ end
29
+ end
30
+
31
+ def can_authenticate?
32
+ username? and not (password.nil? and @skip_interactive and @no_interactive)
33
+ end
34
+
35
+ attr_reader :username
36
+
37
+ protected
38
+ include RHC::Helpers
39
+ attr_reader :options, :password
40
+
41
+ def credentials_rejected
42
+ error "Username or password is not correct" if username? && password
43
+ unless @skip_interactive or @no_interactive
44
+ ask_username unless username?
45
+ ask_password
46
+ true
47
+ end
48
+ end
49
+
50
+ def ask_username
51
+ @username = ask("Login to #{protonbox_server}: ") unless @no_interactive
52
+ end
53
+ def ask_password
54
+ @password = ask("Password: ") { |q|
55
+ q.echo = '*'
56
+ q.whitespace = :chomp
57
+ } unless @no_interactive
58
+ end
59
+
60
+ def username?
61
+ username.present?
62
+ end
63
+ end
64
+ end