rhc 1.4.8 → 1.5.13
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.
- data/features/application.feature +1 -0
- data/features/lib/rhc_helper/app.rb +5 -3
- data/features/lib/rhc_helper/commandify.rb +2 -1
- data/features/step_definitions/application_steps.rb +2 -1
- data/features/support/env.rb +4 -1
- data/lib/rhc/auth/basic.rb +18 -13
- data/lib/rhc/auth/token.rb +98 -0
- data/lib/rhc/auth/token_store.rb +51 -0
- data/lib/rhc/auth.rb +3 -1
- data/lib/rhc/command_runner.rb +1 -0
- data/lib/rhc/commands/account.rb +47 -1
- data/lib/rhc/commands/alias.rb +2 -4
- data/lib/rhc/commands/app.rb +23 -18
- data/lib/rhc/commands/authorization.rb +93 -0
- data/lib/rhc/commands/base.rb +11 -3
- data/lib/rhc/commands/cartridge.rb +8 -16
- data/lib/rhc/commands/git_clone.rb +2 -3
- data/lib/rhc/commands/port_forward.rb +10 -11
- data/lib/rhc/commands/setup.rb +4 -1
- data/lib/rhc/commands/snapshot.rb +4 -3
- data/lib/rhc/commands/tail.rb +3 -4
- data/lib/rhc/commands/threaddump.rb +1 -2
- data/lib/rhc/commands.rb +37 -3
- data/lib/rhc/config.rb +10 -1
- data/lib/rhc/context_helper.rb +5 -1
- data/lib/rhc/core_ext.rb +10 -0
- data/lib/rhc/exceptions.rb +0 -12
- data/lib/rhc/git_helpers.rb +12 -0
- data/lib/rhc/helpers.rb +31 -1
- data/lib/rhc/output_helpers.rb +19 -3
- data/lib/rhc/rest/api.rb +2 -1
- data/lib/rhc/rest/application.rb +5 -4
- data/lib/rhc/rest/authorization.rb +10 -0
- data/lib/rhc/rest/base.rb +6 -1
- data/lib/rhc/rest/client.rb +243 -122
- data/lib/rhc/rest/domain.rb +0 -15
- data/lib/rhc/rest/gear_group.rb +0 -1
- data/lib/rhc/rest/mock.rb +118 -16
- data/lib/rhc/rest/user.rb +0 -1
- data/lib/rhc/rest.rb +28 -8
- data/lib/rhc/ssh_helpers.rb +5 -2
- data/lib/rhc/tar_gz.rb +16 -5
- data/lib/rhc/usage_templates/help.erb +1 -1
- data/lib/rhc/wizard.rb +54 -10
- data/spec/coverage_helper.rb +9 -0
- data/spec/rhc/auth_spec.rb +229 -22
- data/spec/rhc/cli_spec.rb +15 -0
- data/spec/rhc/command_spec.rb +100 -8
- data/spec/rhc/commands/account_spec.rb +75 -1
- data/spec/rhc/commands/app_spec.rb +23 -5
- data/spec/rhc/commands/authorization_spec.rb +120 -0
- data/spec/rhc/commands/domain_spec.rb +2 -2
- data/spec/rhc/commands/git_clone_spec.rb +24 -0
- data/spec/rhc/commands/port_forward_spec.rb +22 -23
- data/spec/rhc/commands/server_spec.rb +2 -2
- data/spec/rhc/commands/setup_spec.rb +12 -0
- data/spec/rhc/config_spec.rb +7 -3
- data/spec/rhc/helpers_spec.rb +62 -9
- data/spec/rhc/rest_application_spec.rb +24 -0
- data/spec/rhc/rest_client_spec.rb +66 -56
- data/spec/rhc/rest_spec.rb +11 -2
- data/spec/rhc/wizard_spec.rb +61 -12
- data/spec/spec_helper.rb +125 -42
- data/spec/wizard_spec_helper.rb +1 -0
- metadata +9 -3
data/lib/rhc/rest.rb
CHANGED
@@ -4,14 +4,15 @@ module RHC
|
|
4
4
|
autoload :Base, 'rhc/rest/base'
|
5
5
|
autoload :Attributes, 'rhc/rest/attributes'
|
6
6
|
|
7
|
-
autoload :Api,
|
8
|
-
autoload :Application,
|
9
|
-
autoload :
|
10
|
-
autoload :
|
11
|
-
autoload :
|
12
|
-
autoload :
|
13
|
-
autoload :
|
14
|
-
autoload :
|
7
|
+
autoload :Api, 'rhc/rest/api'
|
8
|
+
autoload :Application, 'rhc/rest/application'
|
9
|
+
autoload :Authorization, 'rhc/rest/authorization'
|
10
|
+
autoload :Cartridge, 'rhc/rest/cartridge'
|
11
|
+
autoload :Client, 'rhc/rest/client'
|
12
|
+
autoload :Domain, 'rhc/rest/domain'
|
13
|
+
autoload :Key, 'rhc/rest/key'
|
14
|
+
autoload :User, 'rhc/rest/user'
|
15
|
+
autoload :GearGroup, 'rhc/rest/gear_group'
|
15
16
|
|
16
17
|
class Exception < RuntimeError
|
17
18
|
attr_reader :code
|
@@ -51,6 +52,18 @@ module RHC
|
|
51
52
|
class ResourceNotFoundException < ClientErrorException; end
|
52
53
|
class ApiEndpointNotFound < ResourceNotFoundException; end
|
53
54
|
|
55
|
+
# 404 errors for specific resource types
|
56
|
+
class DomainNotFoundException < ResourceNotFoundException
|
57
|
+
def initialize(msg)
|
58
|
+
super(msg,127)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
class ApplicationNotFoundException < ResourceNotFoundException
|
62
|
+
def initialize(msg)
|
63
|
+
super(msg,101)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
54
67
|
#Exceptions thrown in case of an HTTP 422 is received.
|
55
68
|
class ValidationException < ClientErrorException
|
56
69
|
attr_reader :field
|
@@ -76,6 +89,7 @@ module RHC
|
|
76
89
|
#included Authorization credentials, then the 401 response indicates
|
77
90
|
#that authorization has been refused for those credentials.
|
78
91
|
class UnAuthorizedException < ClientErrorException; end
|
92
|
+
class TokenExpiredOrInvalid < UnAuthorizedException; end
|
79
93
|
|
80
94
|
# DEPRECATED Unreachable host, SSL Exception
|
81
95
|
class ResourceAccessException < Exception; end
|
@@ -97,5 +111,11 @@ module RHC
|
|
97
111
|
class SSLVersionRejected < SSLConnectionFailed; end
|
98
112
|
|
99
113
|
class MultipleCartridgeCreationNotSupported < Exception; end
|
114
|
+
|
115
|
+
class AuthorizationsNotSupported < Exception
|
116
|
+
def initialize(message="The server does not support setting, retrieving, or authenticating with authorization tokens.")
|
117
|
+
super(message, 1)
|
118
|
+
end
|
119
|
+
end
|
100
120
|
end
|
101
121
|
end
|
data/lib/rhc/ssh_helpers.rb
CHANGED
@@ -117,10 +117,13 @@ module RHC
|
|
117
117
|
Net::SSH::KeyFactory.load_public_key(key).fingerprint
|
118
118
|
rescue NoMethodError, NotImplementedError => e
|
119
119
|
ssh_keygen_fallback key
|
120
|
-
|
120
|
+
nil
|
121
121
|
rescue OpenSSL::PKey::PKeyError, Net::SSH::Exception => e
|
122
122
|
error e.message
|
123
|
-
|
123
|
+
nil
|
124
|
+
rescue => e
|
125
|
+
error e.message
|
126
|
+
nil
|
124
127
|
end
|
125
128
|
|
126
129
|
def fingerprint_for_default_key
|
data/lib/rhc/tar_gz.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'stringio'
|
2
|
-
require 'rhc/vendor/zliby'
|
3
2
|
require 'archive/tar/minitar'
|
4
3
|
include Archive::Tar
|
5
4
|
|
@@ -16,7 +15,7 @@ module RHC
|
|
16
15
|
regex = Regexp.new search
|
17
16
|
if RHC::Helpers.windows? or force_ruby
|
18
17
|
begin
|
19
|
-
|
18
|
+
zlib::GzipReader.open(filename) do |gz|
|
20
19
|
Minitar::Reader.open gz do |tar|
|
21
20
|
tar.each_entry do |entry|
|
22
21
|
if entry.full_name =~ regex
|
@@ -25,16 +24,28 @@ module RHC
|
|
25
24
|
end
|
26
25
|
end
|
27
26
|
end
|
28
|
-
rescue
|
29
|
-
|
27
|
+
rescue zlib::GzipFile::Error, zlib::GzipFile::Error
|
28
|
+
false
|
30
29
|
end
|
31
30
|
else
|
32
31
|
# combining STDOUT and STDERR (i.e., 2>&1) does not suppress output
|
33
32
|
# when the specs run via 'bundle exec rake spec'
|
34
|
-
|
33
|
+
system "#{TAR_BIN} --wildcards -tf #{filename} #{regex.source} 2>/dev/null >/dev/null"
|
35
34
|
end
|
36
35
|
end
|
37
36
|
|
37
|
+
private
|
38
|
+
def self.zlib
|
39
|
+
#:nocov:
|
40
|
+
require 'zlib' rescue nil
|
41
|
+
if defined? Zlib::GzipReader
|
42
|
+
Zlib
|
43
|
+
else
|
44
|
+
require 'rhc/vendor/zliby'
|
45
|
+
RHC::Vendor::Zlib
|
46
|
+
end
|
47
|
+
#:nocov:
|
48
|
+
end
|
38
49
|
end
|
39
50
|
|
40
51
|
end
|
@@ -4,7 +4,7 @@ Usage: rhc [--help] [--version] [--debug] <command> [<args>]
|
|
4
4
|
|
5
5
|
<%
|
6
6
|
remaining = Hash[@commands.dup.select{ |name, command| not alias?(name) and not command.summary.blank? }]
|
7
|
-
basic = remaining.slice!('setup', 'app create', 'apps', 'cartridge list', 'cartridge add', 'server')
|
7
|
+
basic = remaining.slice!('setup', 'app create', 'apps', 'cartridge list', 'cartridge add', 'server', 'account logout')
|
8
8
|
begin -%>
|
9
9
|
Getting started:
|
10
10
|
<% for name, command in basic -%>
|
data/lib/rhc/wizard.rb
CHANGED
@@ -7,6 +7,10 @@ module RHC
|
|
7
7
|
class Wizard
|
8
8
|
include HighLine::SystemExtensions
|
9
9
|
|
10
|
+
def self.has_configuration?
|
11
|
+
File.exists? RHC::Config.local_config_path
|
12
|
+
end
|
13
|
+
|
10
14
|
DEFAULT_MAX_LENGTH = 16
|
11
15
|
|
12
16
|
STAGES = [:greeting_stage,
|
@@ -71,15 +75,31 @@ module RHC
|
|
71
75
|
})
|
72
76
|
end
|
73
77
|
|
74
|
-
def
|
75
|
-
@
|
78
|
+
def core_auth
|
79
|
+
@core_auth ||= RHC::Auth::Basic.new(options)
|
76
80
|
end
|
77
81
|
|
78
|
-
def
|
79
|
-
|
82
|
+
def token_auth
|
83
|
+
RHC::Auth::Token.new(options, core_auth, token_store)
|
80
84
|
end
|
81
|
-
|
82
|
-
|
85
|
+
|
86
|
+
def auth(reset=false)
|
87
|
+
@auth = nil if reset
|
88
|
+
@auth ||= begin
|
89
|
+
if options.token
|
90
|
+
token_auth
|
91
|
+
else
|
92
|
+
core_auth
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def token_store
|
98
|
+
@token_store ||= RHC::Auth::TokenStore.new(config.home_conf_path)
|
99
|
+
end
|
100
|
+
|
101
|
+
def username
|
102
|
+
auth.username if auth.respond_to?(:username)
|
83
103
|
end
|
84
104
|
|
85
105
|
def print_dot
|
@@ -109,7 +129,11 @@ module RHC
|
|
109
129
|
end
|
110
130
|
|
111
131
|
def login_stage
|
112
|
-
|
132
|
+
if options.token
|
133
|
+
say "Using an existing token for #{options.rhlogin} to login to #{openshift_server}"
|
134
|
+
elsif options.rhlogin
|
135
|
+
say "Using #{options.rhlogin} to login to #{openshift_server}"
|
136
|
+
end
|
113
137
|
|
114
138
|
self.rest_client = new_client_for_options
|
115
139
|
|
@@ -137,6 +161,22 @@ module RHC
|
|
137
161
|
end
|
138
162
|
|
139
163
|
self.user = rest_client.user
|
164
|
+
|
165
|
+
if rest_client.supports_sessions? && !options.token && options.create_token != false
|
166
|
+
paragraph do
|
167
|
+
info "OpenShift can create and store a token on disk which allows to you to access the server without using your password. The key is stored in your home directory and should be kept secret. You can delete the key at any time by running 'rhc logout'."
|
168
|
+
if options.create_token or agree "Generate a token now? (yes|no) "
|
169
|
+
say "Generating an authorization token for this client ... "
|
170
|
+
token = rest_client.new_session
|
171
|
+
options.token = token.token
|
172
|
+
self.auth(true).save(token.token)
|
173
|
+
self.rest_client = new_client_for_options
|
174
|
+
self.user = rest_client.user
|
175
|
+
|
176
|
+
success "lasts #{distance_of_time_in_words(token.expires_in_seconds)}"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
140
180
|
true
|
141
181
|
end
|
142
182
|
|
@@ -152,7 +192,8 @@ module RHC
|
|
152
192
|
|
153
193
|
changed = Commander::Command::Options.new(options)
|
154
194
|
changed.rhlogin = username
|
155
|
-
changed.password =
|
195
|
+
changed.password = nil
|
196
|
+
changed.use_authorization_tokens = options.create_token != false && !changed.token.nil?
|
156
197
|
|
157
198
|
FileUtils.mkdir_p File.dirname(config.path)
|
158
199
|
config.save!(changed)
|
@@ -211,9 +252,8 @@ module RHC
|
|
211
252
|
return nil
|
212
253
|
end
|
213
254
|
|
214
|
-
hostname = Socket.gethostname.gsub(/\..*\z/,'')
|
215
255
|
userkey = username ? username.gsub(/@.*/, '') : ''
|
216
|
-
pubkey_base_name = "#{userkey}#{hostname}".gsub(/[^A-Za-z0-9]/,'').slice(0,16)
|
256
|
+
pubkey_base_name = "#{userkey}#{hostname.gsub(/\..*\z/,'')}".gsub(/[^A-Za-z0-9]/,'').slice(0,16)
|
217
257
|
default_name = find_unique_key_name(
|
218
258
|
:keys => ssh_keys,
|
219
259
|
:base => pubkey_base_name,
|
@@ -519,6 +559,10 @@ EOF
|
|
519
559
|
@debug
|
520
560
|
end
|
521
561
|
|
562
|
+
def hostname
|
563
|
+
Socket.gethostname
|
564
|
+
end
|
565
|
+
|
522
566
|
protected
|
523
567
|
attr_writer :rest_client
|
524
568
|
end
|
data/spec/coverage_helper.rb
CHANGED
@@ -12,12 +12,21 @@ unless RUBY_VERSION < '1.9'
|
|
12
12
|
end
|
13
13
|
@missed_lines
|
14
14
|
end
|
15
|
+
|
16
|
+
def print_missed_lines
|
17
|
+
@files.each do |file|
|
18
|
+
file.missed_lines.each do |line|
|
19
|
+
puts "MISSED #{file.filename}:#{line.number}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
15
23
|
end
|
16
24
|
|
17
25
|
original_stderr = $stderr # in case helpers don't properly cleanup
|
18
26
|
SimpleCov.at_exit do
|
19
27
|
SimpleCov.result.format!
|
20
28
|
if SimpleCov.result.covered_percent < 100.0
|
29
|
+
SimpleCov.result.print_missed_lines if SimpleCov.result.covered_percent > 98.0
|
21
30
|
original_stderr.puts "Coverage not 100%, build failed."
|
22
31
|
exit 1
|
23
32
|
end
|
data/spec/rhc/auth_spec.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'base64'
|
2
3
|
require 'rhc/commands'
|
3
4
|
|
4
5
|
describe RHC::Auth::Basic do
|
@@ -7,11 +8,13 @@ describe RHC::Auth::Basic do
|
|
7
8
|
let(:auth_hash){ {:user => user, :password => password} }
|
8
9
|
let(:options){ (o = Commander::Command::Options.new).default(default_options); o }
|
9
10
|
let(:default_options){ {} }
|
11
|
+
let(:client){ mock(:supports_sessions? => false) }
|
10
12
|
|
11
13
|
its(:username){ should be_nil }
|
12
14
|
its(:username?){ should be_false }
|
13
15
|
its(:password){ should be_nil }
|
14
|
-
its(:options){
|
16
|
+
its(:options){ should_not be_nil }
|
17
|
+
its(:can_authenticate?){ should be_false }
|
15
18
|
its(:openshift_server){ should == 'openshift.redhat.com' }
|
16
19
|
|
17
20
|
context "with user options" do
|
@@ -28,6 +31,7 @@ describe RHC::Auth::Basic do
|
|
28
31
|
its(:username){ should == user }
|
29
32
|
its(:username?){ should be_true }
|
30
33
|
its(:password){ should == password }
|
34
|
+
its(:can_authenticate?){ should be_true }
|
31
35
|
end
|
32
36
|
|
33
37
|
context "that includes server" do
|
@@ -48,7 +52,7 @@ describe RHC::Auth::Basic do
|
|
48
52
|
its(:username?){ should be_false }
|
49
53
|
it("should not retry") do
|
50
54
|
subject.should_not_receive(:ask_username)
|
51
|
-
subject.retry_auth?(mock(:status => 401)).should be_false
|
55
|
+
subject.retry_auth?(mock(:status => 401), client).should be_false
|
52
56
|
end
|
53
57
|
end
|
54
58
|
end
|
@@ -111,11 +115,6 @@ describe RHC::Auth::Basic do
|
|
111
115
|
it { subject.to_request(request).should equal(request) }
|
112
116
|
it { subject.to_request(request).should == auth_hash }
|
113
117
|
|
114
|
-
context "it should remember cookies" do
|
115
|
-
let(:response){ mock(:cookies => [mock(:name => 'rh_sso', :value => '1')], :status => 200) }
|
116
|
-
it{ subject.retry_auth?(response); subject.to_request(request)[:cookies].should == {:rh_sso => '1'} }
|
117
|
-
end
|
118
|
-
|
119
118
|
context "when the request is lazy" do
|
120
119
|
let(:request){ {:lazy_auth => true} }
|
121
120
|
|
@@ -172,24 +171,21 @@ describe RHC::Auth::Basic do
|
|
172
171
|
context "when the response succeeds" do
|
173
172
|
let(:response){ mock(:cookies => {}, :status => 200) }
|
174
173
|
|
175
|
-
it{ subject.retry_auth?(response).should be_false }
|
176
|
-
after{ subject.cookie.should be_nil }
|
174
|
+
it{ subject.retry_auth?(response, client).should be_false }
|
177
175
|
end
|
178
176
|
context "when the response succeeds with a cookie" do
|
179
177
|
let(:response){ mock(:cookies => [mock(:name => 'rh_sso', :value => '1')], :status => 200) }
|
180
|
-
it{ subject.retry_auth?(response).should be_false }
|
181
|
-
after{ subject.cookie.should == '1' }
|
178
|
+
it{ subject.retry_auth?(response, client).should be_false }
|
182
179
|
end
|
183
180
|
context "when the response requires authentication" do
|
184
181
|
let(:response){ mock(:status => 401) }
|
185
|
-
after{ subject.cookie.should be_nil }
|
186
182
|
|
187
183
|
context "with no user and no password" do
|
188
184
|
subject{ described_class.new(nil, nil) }
|
189
185
|
it("should ask for user and password") do
|
190
186
|
subject.should_receive(:ask_username).and_return(user)
|
191
187
|
subject.should_receive(:ask_password).and_return(password)
|
192
|
-
subject.retry_auth?(response).should be_true
|
188
|
+
subject.retry_auth?(response, client).should be_true
|
193
189
|
end
|
194
190
|
end
|
195
191
|
|
@@ -197,12 +193,12 @@ describe RHC::Auth::Basic do
|
|
197
193
|
subject{ described_class.new(user, nil) }
|
198
194
|
it("should ask for password only") do
|
199
195
|
subject.should_receive(:ask_password).and_return(password)
|
200
|
-
subject.retry_auth?(response).should be_true
|
196
|
+
subject.retry_auth?(response, client).should be_true
|
201
197
|
end
|
202
198
|
it("should ask for password twice") do
|
203
199
|
subject.should_receive(:ask_password).twice.and_return(password)
|
204
|
-
subject.retry_auth?(response).should be_true
|
205
|
-
subject.retry_auth?(response).should be_true
|
200
|
+
subject.retry_auth?(response, client).should be_true
|
201
|
+
subject.retry_auth?(response, client).should be_true
|
206
202
|
end
|
207
203
|
end
|
208
204
|
|
@@ -211,16 +207,227 @@ describe RHC::Auth::Basic do
|
|
211
207
|
it("should not prompt for reauthentication") do
|
212
208
|
subject.should_not_receive(:ask_password)
|
213
209
|
subject.should_receive(:error).with("Username or password is not correct")
|
214
|
-
subject.retry_auth?(response).should be_false
|
210
|
+
subject.retry_auth?(response, client).should be_false
|
215
211
|
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
216
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
217
|
+
describe RHC::Auth::Token do
|
218
|
+
subject{ described_class.new(options) }
|
219
|
+
|
220
|
+
let(:token){ 'a_token' }
|
221
|
+
let(:options){ (o = Commander::Command::Options.new).default(default_options); o }
|
222
|
+
let(:default_options){ {} }
|
223
|
+
let(:client){ mock(:supports_sessions? => false) }
|
224
|
+
let(:auth){ nil }
|
225
|
+
let(:store){ nil }
|
226
|
+
|
227
|
+
its(:username){ should be_nil }
|
228
|
+
its(:options){ should_not be_nil }
|
229
|
+
its(:can_authenticate?){ should be_false }
|
230
|
+
its(:openshift_server){ should == 'openshift.redhat.com' }
|
231
|
+
|
232
|
+
context "with user options" do
|
233
|
+
its(:username){ should be_nil }
|
234
|
+
its(:options){ should equal(options) }
|
235
|
+
|
236
|
+
context "that include token" do
|
237
|
+
let(:default_options){ {:token => token} }
|
238
|
+
its(:can_authenticate?){ should be_true }
|
239
|
+
end
|
240
|
+
|
241
|
+
context "that includes server" do
|
242
|
+
let(:default_options){ {:server => 'test.com'} }
|
243
|
+
its(:openshift_server){ should == 'test.com' }
|
244
|
+
end
|
245
|
+
|
246
|
+
context "with --noprompt" do
|
247
|
+
let(:default_options){ {:noprompt => true} }
|
248
|
+
|
249
|
+
its(:username){ should be_nil }
|
250
|
+
it("should not retry") do
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
context "when initialized with a hash" do
|
256
|
+
subject{ described_class.new({:token => token}) }
|
257
|
+
its(:token){ should == token }
|
258
|
+
end
|
259
|
+
|
260
|
+
context "when initialized with a string" do
|
261
|
+
subject{ described_class.new(token) }
|
262
|
+
its(:token){ should == token }
|
263
|
+
end
|
264
|
+
|
265
|
+
context "when initialized with an auth object" do
|
266
|
+
subject{ described_class.new(nil, auth) }
|
267
|
+
let(:auth){ mock(:username => 'foo') }
|
268
|
+
its(:username){ should == 'foo' }
|
269
|
+
end
|
270
|
+
|
271
|
+
context "when initialized with a store" do
|
272
|
+
subject{ described_class.new(nil, nil, store) }
|
273
|
+
let(:store){ mock }
|
274
|
+
before{ store.should_receive(:get).with(nil, 'openshift.redhat.com').and_return(token) }
|
275
|
+
it("should read the token for the user") do
|
276
|
+
subject.send(:token).should == token
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
describe "#save" do
|
281
|
+
subject{ described_class.new(nil, nil, store) }
|
282
|
+
context "when store is set" do
|
283
|
+
let(:store){ mock(:get => nil) }
|
284
|
+
it("should call put on store") do
|
285
|
+
subject.should_receive(:username).and_return('foo')
|
286
|
+
subject.should_receive(:openshift_server).and_return('bar')
|
287
|
+
store.should_receive(:put).with('foo', 'bar', token)
|
288
|
+
subject.save(token)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
context "when store is nil" do
|
292
|
+
it("should skip calling store"){ subject.save(token) }
|
293
|
+
end
|
294
|
+
after{ subject.instance_variable_get(:@token).should == token }
|
295
|
+
end
|
296
|
+
|
297
|
+
describe "#to_request" do
|
298
|
+
let(:request){ {} }
|
299
|
+
subject{ described_class.new(token, auth) }
|
300
|
+
|
301
|
+
context "when token is provided" do
|
302
|
+
it("should pass bearer token to the server"){ subject.to_request(request).should == {:headers => {'authorization' => "Bearer #{token}"}} }
|
303
|
+
|
304
|
+
context "when the request is lazy" do
|
305
|
+
let(:request){ {:lazy_auth => true} }
|
306
|
+
it("should pass bearer token to the server"){ subject.to_request(request).should == {:lazy_auth => true, :headers => {'authorization' => "Bearer #{token}"}} }
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
context "when token is not provided" do
|
311
|
+
subject{ described_class.new(nil) }
|
312
|
+
|
313
|
+
it("should pass not bearer token to the server"){ subject.to_request(request).should == {} }
|
314
|
+
end
|
315
|
+
|
316
|
+
context "when a parent auth class is passed" do
|
317
|
+
subject{ described_class.new(nil, auth) }
|
318
|
+
let(:auth){ mock }
|
319
|
+
it("should invoke the parent") do
|
320
|
+
auth.should_receive(:to_request).with(request).and_return(request)
|
321
|
+
subject.to_request(request).should == request
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
describe "#retry_auth?" do
|
327
|
+
subject{ described_class.new(token, auth) }
|
328
|
+
|
329
|
+
context "when the response succeeds" do
|
330
|
+
let(:response){ mock(:cookies => {}, :status => 200) }
|
331
|
+
it{ subject.retry_auth?(response, client).should be_false }
|
332
|
+
end
|
333
|
+
|
334
|
+
context "when the response requires authentication" do
|
335
|
+
let(:response){ mock(:status => 401) }
|
336
|
+
|
337
|
+
context "with no token" do
|
338
|
+
subject{ described_class.new(nil, nil) }
|
339
|
+
it("should return false"){ subject.retry_auth?(response, client).should be_false }
|
340
|
+
end
|
341
|
+
|
342
|
+
context "when a nested auth object can't authenticate" do
|
343
|
+
let(:auth){ mock(:can_authenticate? => false) }
|
344
|
+
it("should raise an error"){ expect{ subject.retry_auth?(response, client) }.to raise_error(RHC::Rest::TokenExpiredOrInvalid) }
|
345
|
+
end
|
346
|
+
|
347
|
+
context "with a nested auth object" do
|
348
|
+
let(:auth){ mock('nested_auth', :can_authenticate? => true) }
|
349
|
+
subject{ described_class.new(options, auth) }
|
350
|
+
|
351
|
+
it("should not use token auth") do
|
352
|
+
auth.should_receive(:retry_auth?).with(response, client).and_return true
|
353
|
+
subject.retry_auth?(response, client).should be_true
|
354
|
+
end
|
355
|
+
|
356
|
+
context "when noprompt is requested" do
|
357
|
+
let(:default_options){ {:token => token, :noprompt => true} }
|
358
|
+
it("should raise an error"){ expect{ subject.retry_auth?(response, client) }.to raise_error(RHC::Rest::TokenExpiredOrInvalid) }
|
359
|
+
end
|
360
|
+
|
361
|
+
context "when authorization tokens are enabled locally" do
|
362
|
+
let(:default_options){ {:use_authorization_tokens => true} }
|
363
|
+
|
364
|
+
context "without session support" do
|
365
|
+
let(:default_options){ {:use_authorization_tokens => true, :token => 'foo'} }
|
366
|
+
let(:client){ mock('client', :supports_sessions? => false) }
|
367
|
+
|
368
|
+
it("should invoke raise an error on retry because sessions are not supported") do
|
369
|
+
expect{ subject.retry_auth?(response, client) }.to raise_error RHC::Rest::AuthorizationsNotSupported
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
context "we expect a warning and a call to client" do
|
374
|
+
let(:auth_token){ nil }
|
375
|
+
let(:client){ mock('client', :supports_sessions? => true) }
|
376
|
+
before{ client.should_receive(:new_session).with(:auth => auth).and_return(auth_token) }
|
377
|
+
|
378
|
+
it("should print a message") do
|
379
|
+
subject.should_receive(:info).with("Please sign in to start a new session to #{subject.openshift_server}.")
|
380
|
+
auth.should_receive(:retry_auth?).with(response, client).and_return true
|
381
|
+
subject.retry_auth?(response, client).should be_true
|
382
|
+
end
|
383
|
+
|
384
|
+
context "with a token" do
|
385
|
+
let(:default_options){ {:use_authorization_tokens => true, :token => 'foo'} }
|
386
|
+
it("should invoke raise an error on retry because sessions are not supported") do
|
387
|
+
subject.should_receive(:warn).with("Your authorization token has expired. Please sign in now to continue.")
|
388
|
+
auth.should_receive(:retry_auth?).with(response, client).and_return true
|
389
|
+
subject.retry_auth?(response, client).should be_true
|
390
|
+
#expect{ subject.retry_auth?(response, client) }.to raise_error RHC::Rest::AuthorizationsNotSupported
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
context "when the token request fails" do
|
395
|
+
before{ subject.should_receive(:info).with("Please sign in to start a new session to #{subject.openshift_server}.") }
|
396
|
+
it("should invoke retry on the parent") do
|
397
|
+
auth.should_receive(:retry_auth?).with(response, client).and_return false
|
398
|
+
subject.retry_auth?(response, client).should be_false
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
context "when the token request succeeds" do
|
403
|
+
let(:auth_token){ mock('auth_token', :token => 'bar') }
|
404
|
+
before{ subject.should_receive(:info).with("Please sign in to start a new session to #{subject.openshift_server}.") }
|
405
|
+
it("should save the token and return true") do
|
406
|
+
subject.should_receive(:save).with(auth_token.token).and_return true
|
407
|
+
subject.retry_auth?(response, client).should be_true
|
408
|
+
end
|
409
|
+
end
|
410
|
+
end
|
222
411
|
end
|
223
412
|
end
|
224
413
|
end
|
225
414
|
end
|
226
415
|
end
|
416
|
+
|
417
|
+
describe RHC::Auth::TokenStore do
|
418
|
+
subject{ described_class.new(dir) }
|
419
|
+
let(:dir){ Dir.mktmpdir }
|
420
|
+
|
421
|
+
context "when a key is stored" do
|
422
|
+
before{ subject.put('foo', 'bar', 'token') }
|
423
|
+
it("can be retrieved"){ subject.get('foo', 'bar').should == 'token' }
|
424
|
+
end
|
425
|
+
it("should put a file on disk"){ expect{ subject.put('test', 'server', 'value') }.to change{ Dir.entries(dir).length }.by(1) }
|
426
|
+
|
427
|
+
describe "#clear" do
|
428
|
+
before{ subject.put('test', 'server2', 'value2') }
|
429
|
+
it("should return true"){ subject.clear.should be_true }
|
430
|
+
it("should empty the directory"){ expect{ subject.clear }.to change{ Dir.entries(dir).length }.by_at_least(-1) }
|
431
|
+
after{ Dir.entries(dir).length.should == 2 }
|
432
|
+
end
|
433
|
+
end
|
data/spec/rhc/cli_spec.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'rhc/wizard'
|
2
3
|
|
3
4
|
describe RHC::CLI do
|
4
5
|
|
@@ -11,6 +12,13 @@ describe RHC::CLI do
|
|
11
12
|
it('should mention the help options command') { run_output.should =~ /rhc help options/ }
|
12
13
|
end
|
13
14
|
|
15
|
+
shared_examples_for 'a first run wizard' do
|
16
|
+
let(:arguments) { @arguments or raise "no arguments" }
|
17
|
+
let!(:wizard){ RHC::Wizard.new }
|
18
|
+
before{ RHC::Wizard.should_receive(:new).and_return(wizard) }
|
19
|
+
it('should create and run a new wizard') { expect{ run }.to call(:run).on(wizard) }
|
20
|
+
end
|
21
|
+
|
14
22
|
shared_examples_for 'a help page' do
|
15
23
|
let(:arguments) { @arguments or raise "no arguments" }
|
16
24
|
it('should contain the program description') { run_output.should =~ /Command line interface for OpenShift/ }
|
@@ -52,9 +60,16 @@ describe RHC::CLI do
|
|
52
60
|
end
|
53
61
|
|
54
62
|
describe '#start' do
|
63
|
+
before{ RHC::Wizard.stub(:has_configuration?).and_return(true) }
|
64
|
+
|
55
65
|
context 'with no arguments' do
|
56
66
|
before(:each) { @arguments = [] }
|
57
67
|
it_should_behave_like 'a global help page'
|
68
|
+
|
69
|
+
context "without a config file" do
|
70
|
+
before{ RHC::Wizard.stub(:has_configuration?).and_return(false) }
|
71
|
+
it_should_behave_like 'a first run wizard'
|
72
|
+
end
|
58
73
|
end
|
59
74
|
|
60
75
|
context 'with an ambiguous option' do
|