cfoundry 0.5.0 → 0.5.1.rc1
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/Rakefile +0 -26
- data/lib/cfoundry/auth_token.rb +16 -1
- data/lib/cfoundry/baseclient.rb +26 -5
- data/lib/cfoundry/client.rb +1 -4
- data/lib/cfoundry/rest_client.rb +3 -0
- data/lib/cfoundry/uaaclient.rb +18 -6
- data/lib/cfoundry/upload_helpers.rb +24 -0
- data/lib/cfoundry/v1/base.rb +1 -1
- data/lib/cfoundry/v1/client.rb +13 -1
- data/lib/cfoundry/v1/service.rb +9 -2
- data/lib/cfoundry/v1/service_plan.rb +19 -0
- data/lib/cfoundry/v2/app.rb +19 -3
- data/lib/cfoundry/v2/base.rb +7 -5
- data/lib/cfoundry/v2/client.rb +2 -0
- data/lib/cfoundry/version.rb +1 -1
- data/spec/cfoundry/auth_token_spec.rb +49 -1
- data/spec/cfoundry/baseclient_spec.rb +135 -26
- data/spec/cfoundry/client_spec.rb +15 -0
- data/spec/cfoundry/rest_client_spec.rb +18 -0
- data/spec/cfoundry/uaaclient_spec.rb +48 -9
- data/spec/cfoundry/upload_helpers_spec.rb +48 -3
- data/spec/cfoundry/v2/app_spec.rb +49 -0
- data/spec/cfoundry/v2/base_spec.rb +17 -0
- data/spec/cfoundry/v2/client_spec.rb +5 -0
- data/spec/cfoundry/v2/organization_spec.rb +2 -0
- data/spec/cfoundry/v2/space_spec.rb +2 -0
- data/spec/fixtures/apps/with_dotfiles/xyz +1 -0
- data/spec/fixtures/apps/with_nested_directories/foo/bar/baz/fizz +0 -0
- data/spec/fixtures/apps/with_nested_directories/xyz +0 -0
- data/spec/spec_helper.rb +2 -1
- data/spec/support/fake_helper.rb +12 -1
- metadata +38 -9
data/Rakefile
CHANGED
@@ -36,29 +36,3 @@ namespace :deploy do
|
|
36
36
|
sh "git push origin latest-release"
|
37
37
|
end
|
38
38
|
end
|
39
|
-
|
40
|
-
namespace :release do
|
41
|
-
DEPENDENTS = %w[
|
42
|
-
vmc/vmc.gemspec
|
43
|
-
vmc-plugins/admin/admin-vmc-plugin.gemspec
|
44
|
-
vmc-plugins/console/console-vmc-plugin.gemspec
|
45
|
-
vmc-plugins/manifests/manifests-vmc-plugin.gemspec
|
46
|
-
vmc-plugins/mcf/mcf-vmc-plugin.gemspec
|
47
|
-
vmc-plugins/tunnel/tunnel-vmc-plugin.gemspec
|
48
|
-
].freeze
|
49
|
-
|
50
|
-
def bump_dependent(file, dep, ver)
|
51
|
-
puts "Bumping #{dep} to #{ver} in #{file}"
|
52
|
-
|
53
|
-
old = File.read(file)
|
54
|
-
new = old.sub(/(\.add.+#{dep}\D+)[^'"]+(.+)/, "\\1#{ver}\\2")
|
55
|
-
|
56
|
-
File.open(file, "w") { |io| io.print new }
|
57
|
-
end
|
58
|
-
|
59
|
-
task :bump_dependents do
|
60
|
-
DEPENDENTS.each do |dep|
|
61
|
-
bump_dependent(File.expand_path("../../#{dep}", __FILE__), "cfoundry", CFoundry::VERSION)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
data/lib/cfoundry/auth_token.rb
CHANGED
@@ -21,7 +21,8 @@ module CFoundry
|
|
21
21
|
@refresh_token = refresh_token
|
22
22
|
end
|
23
23
|
|
24
|
-
|
24
|
+
attr_accessor :auth_header
|
25
|
+
attr_reader :refresh_token
|
25
26
|
|
26
27
|
def to_hash
|
27
28
|
{
|
@@ -35,6 +36,7 @@ module CFoundry
|
|
35
36
|
# TODO: rename to #data
|
36
37
|
def token_data
|
37
38
|
return @token_data if @token_data
|
39
|
+
return {} unless @auth_header
|
38
40
|
|
39
41
|
json_hashes = Base64.decode64(@auth_header.split(" ", 2).last)
|
40
42
|
data_json = json_hashes.sub(JSON_HASH, "")[JSON_HASH]
|
@@ -44,5 +46,18 @@ module CFoundry
|
|
44
46
|
rescue MultiJson::DecodeError
|
45
47
|
{}
|
46
48
|
end
|
49
|
+
|
50
|
+
def auth_header=(auth_header)
|
51
|
+
@token_data = nil
|
52
|
+
@auth_header = auth_header
|
53
|
+
end
|
54
|
+
|
55
|
+
def expiration
|
56
|
+
Time.at(token_data[:exp])
|
57
|
+
end
|
58
|
+
|
59
|
+
def expires_soon?
|
60
|
+
(expiration.to_i - Time.now.to_i) < 60
|
61
|
+
end
|
47
62
|
end
|
48
63
|
end
|
data/lib/cfoundry/baseclient.rb
CHANGED
@@ -24,14 +24,21 @@ module CFoundry
|
|
24
24
|
|
25
25
|
def uaa
|
26
26
|
@uaa ||= begin
|
27
|
-
endpoint = info[:authorization_endpoint]
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
if(endpoint = info[:authorization_endpoint])
|
28
|
+
uaa = CFoundry::UAAClient.new(endpoint)
|
29
|
+
uaa.trace = trace
|
30
|
+
uaa.token = token
|
31
|
+
uaa
|
32
|
+
else
|
33
|
+
nil
|
34
|
+
end
|
32
35
|
end
|
33
36
|
end
|
34
37
|
|
38
|
+
def password_score(password)
|
39
|
+
uaa ? uaa.password_score(password) : :unknown
|
40
|
+
end
|
41
|
+
|
35
42
|
def token=(token)
|
36
43
|
if token.is_a?(String)
|
37
44
|
token = CFoundry::AuthToken.new(token)
|
@@ -68,6 +75,11 @@ module CFoundry
|
|
68
75
|
end
|
69
76
|
|
70
77
|
def request(method, *args)
|
78
|
+
if needs_token_refresh?
|
79
|
+
token.auth_header = nil
|
80
|
+
refresh_token!
|
81
|
+
end
|
82
|
+
|
71
83
|
path, options = normalize_arguments(args)
|
72
84
|
request, response = request_raw(method, path, options)
|
73
85
|
handle_response(response, options, request)
|
@@ -77,8 +89,17 @@ module CFoundry
|
|
77
89
|
@rest_client.request(method, path, options)
|
78
90
|
end
|
79
91
|
|
92
|
+
def refresh_token!
|
93
|
+
self.token = uaa.try_to_refresh_token!
|
94
|
+
end
|
95
|
+
|
80
96
|
private
|
81
97
|
|
98
|
+
def needs_token_refresh?
|
99
|
+
token && token.auth_header && token.refresh_token && \
|
100
|
+
token.expires_soon?
|
101
|
+
end
|
102
|
+
|
82
103
|
def status_is_successful?(code)
|
83
104
|
(code >= 200) && (code < 400)
|
84
105
|
end
|
data/lib/cfoundry/client.rb
CHANGED
@@ -6,6 +6,7 @@ require "cfoundry/v1/app"
|
|
6
6
|
require "cfoundry/v1/framework"
|
7
7
|
require "cfoundry/v1/runtime"
|
8
8
|
require "cfoundry/v1/service"
|
9
|
+
require "cfoundry/v1/service_plan"
|
9
10
|
require "cfoundry/v1/service_instance"
|
10
11
|
require "cfoundry/v1/user"
|
11
12
|
require "cfoundry/v1/base"
|
@@ -41,9 +42,5 @@ module CFoundry
|
|
41
42
|
CFoundry::V1::Client.new(*args)
|
42
43
|
end
|
43
44
|
end
|
44
|
-
|
45
|
-
def info
|
46
|
-
get("info", :accept => :json)
|
47
|
-
end
|
48
45
|
end
|
49
46
|
end
|
data/lib/cfoundry/rest_client.rb
CHANGED
data/lib/cfoundry/uaaclient.rb
CHANGED
@@ -2,7 +2,7 @@ require "cfoundry/baseclient"
|
|
2
2
|
require 'uaa'
|
3
3
|
|
4
4
|
module CFoundry
|
5
|
-
class UAAClient
|
5
|
+
class UAAClient
|
6
6
|
attr_accessor :target, :client_id, :token, :trace
|
7
7
|
|
8
8
|
def initialize(target = "https://uaa.cloudfoundry.com", client_id = "vmc")
|
@@ -20,11 +20,12 @@ module CFoundry
|
|
20
20
|
def authorize(username, password)
|
21
21
|
wrap_uaa_errors do
|
22
22
|
begin
|
23
|
-
|
24
|
-
token_issuer.implicit_grant_with_creds(creds)
|
23
|
+
token_issuer.owner_password_grant(username, password)
|
25
24
|
rescue CF::UAA::BadResponse => e
|
26
25
|
status_code = e.message[/\d+/] || 400
|
27
26
|
raise CFoundry::Denied.new("Authorization failed", status_code)
|
27
|
+
rescue CF::UAA::TargetError
|
28
|
+
token_issuer.implicit_grant_with_creds(:username => username, :password => password)
|
28
29
|
end
|
29
30
|
end
|
30
31
|
end
|
@@ -70,18 +71,29 @@ module CFoundry
|
|
70
71
|
end
|
71
72
|
end
|
72
73
|
|
74
|
+
def try_to_refresh_token!
|
75
|
+
wrap_uaa_errors do
|
76
|
+
begin
|
77
|
+
token_info = token_issuer.refresh_token_grant(token.refresh_token)
|
78
|
+
self.token = AuthToken.from_uaa_token_info(token_info)
|
79
|
+
rescue CF::UAA::TargetError
|
80
|
+
self.token
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
73
85
|
private
|
74
86
|
|
75
87
|
def token_issuer
|
76
88
|
@token_issuer ||= CF::UAA::TokenIssuer.new(target, client_id, nil, :symbolize_keys => true)
|
77
|
-
@token_issuer.logger.level = @trace ?
|
89
|
+
@token_issuer.logger.level = @trace ? Logger::Severity::TRACE : 1
|
78
90
|
@token_issuer
|
79
91
|
end
|
80
92
|
|
81
93
|
def scim
|
82
94
|
auth_header = token && token.auth_header
|
83
95
|
scim = CF::UAA::Scim.new(target, auth_header)
|
84
|
-
scim.logger.level = @trace ?
|
96
|
+
scim.logger.level = @trace ? Logger::Severity::TRACE : 1
|
85
97
|
scim
|
86
98
|
end
|
87
99
|
|
@@ -94,7 +106,7 @@ module CFoundry
|
|
94
106
|
rescue CF::UAA::InvalidToken
|
95
107
|
raise CFoundry::Denied
|
96
108
|
rescue CF::UAA::TargetError => e
|
97
|
-
raise CFoundry::UAAError.new(e.info[
|
109
|
+
raise CFoundry::UAAError.new(e.info[:error_description], e.info[:error])
|
98
110
|
end
|
99
111
|
end
|
100
112
|
end
|
@@ -135,9 +135,33 @@ module CFoundry
|
|
135
135
|
resource[:fn].sub!("#{path}/", "")
|
136
136
|
end
|
137
137
|
|
138
|
+
prune_empty_directories(path)
|
139
|
+
|
138
140
|
resources
|
139
141
|
end
|
140
142
|
|
143
|
+
# OK, HERES THE PLAN...
|
144
|
+
#
|
145
|
+
# 1. Get all the directories in the entire file tree.
|
146
|
+
# 2. Sort them by the length of their absolute path.
|
147
|
+
# 3. Go through the list, longest paths first, and remove
|
148
|
+
# the directories that are empty.
|
149
|
+
#
|
150
|
+
# This ensures that directories containing empty directories
|
151
|
+
# are also pruned.
|
152
|
+
def prune_empty_directories(path)
|
153
|
+
all_files = Dir["#{path}/**/{*,.*}"]
|
154
|
+
all_files.reject! { |fn| fn =~ /\/\.+$/ }
|
155
|
+
|
156
|
+
directories = all_files.select { |x| File.directory?(x) }
|
157
|
+
directories.sort! { |a, b| b.size <=> a.size }
|
158
|
+
|
159
|
+
directories.each do |directory|
|
160
|
+
entries = Dir.entries(directory) - %w{. ..}
|
161
|
+
FileUtils.rmdir(directory) if entries.empty?
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
141
165
|
def make_fingerprints(path)
|
142
166
|
fingerprints = []
|
143
167
|
total_size = 0
|
data/lib/cfoundry/v1/base.rb
CHANGED
@@ -49,7 +49,7 @@ module CFoundry::V1
|
|
49
49
|
post("resources", :content => :json, :accept => :json, :payload => fingerprints)
|
50
50
|
end
|
51
51
|
|
52
|
-
def upload_app(name, zipfile, resources = [])
|
52
|
+
def upload_app(name, zipfile = nil, resources = [])
|
53
53
|
payload = {
|
54
54
|
:_method => "put",
|
55
55
|
:resources => MultiJson.dump(resources),
|
data/lib/cfoundry/v1/client.rb
CHANGED
@@ -106,7 +106,8 @@ module CFoundry::V1
|
|
106
106
|
|
107
107
|
services <<
|
108
108
|
Service.new(vendor.to_s, ver.to_s, meta[:description],
|
109
|
-
type.to_s, provider.to_s, state && state.first
|
109
|
+
type.to_s, provider.to_s, state && state.first,
|
110
|
+
generate_plans(meta))
|
110
111
|
end
|
111
112
|
end
|
112
113
|
end
|
@@ -116,6 +117,17 @@ module CFoundry::V1
|
|
116
117
|
services
|
117
118
|
end
|
118
119
|
|
120
|
+
def generate_plans(meta)
|
121
|
+
names = meta[:plans]
|
122
|
+
descriptions = meta[:plan_descriptions]
|
123
|
+
default_name = meta[:default_plan]
|
124
|
+
names.map { |name|
|
125
|
+
description = descriptions[name] if descriptions
|
126
|
+
is_default = name == default_name || names.length == 1
|
127
|
+
ServicePlan.new(name, description, is_default)
|
128
|
+
}
|
129
|
+
end
|
130
|
+
|
119
131
|
# Retrieve available runtimes.
|
120
132
|
def runtimes(options = {})
|
121
133
|
runtimes = []
|
data/lib/cfoundry/v1/service.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
1
|
module CFoundry::V1
|
2
2
|
class Service
|
3
|
-
attr_accessor :label, :version, :description, :type, :provider, :state
|
3
|
+
attr_accessor :label, :version, :description, :type, :provider, :state, :service_plans
|
4
4
|
|
5
5
|
def initialize(label, version = nil, description = nil,
|
6
|
-
type = nil, provider = "core", state = nil
|
6
|
+
type = nil, provider = "core", state = nil,
|
7
|
+
service_plans = [])
|
7
8
|
@label = label
|
8
9
|
@description = description
|
9
10
|
@version = version
|
10
11
|
@type = type
|
11
12
|
@provider = provider
|
12
13
|
@state = state
|
14
|
+
@service_plans = service_plans
|
13
15
|
end
|
14
16
|
|
15
17
|
def eql?(other)
|
@@ -28,5 +30,10 @@ module CFoundry::V1
|
|
28
30
|
def current?
|
29
31
|
@state == :current
|
30
32
|
end
|
33
|
+
|
34
|
+
def default_service_plan
|
35
|
+
service_plans.find(&:default?)
|
36
|
+
end
|
37
|
+
|
31
38
|
end
|
32
39
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module CFoundry::V1
|
2
|
+
|
3
|
+
class ServicePlan
|
4
|
+
|
5
|
+
attr_accessor :name, :description
|
6
|
+
|
7
|
+
def initialize(name, description, is_default)
|
8
|
+
@name = name
|
9
|
+
@description = description
|
10
|
+
@is_default = is_default
|
11
|
+
end
|
12
|
+
|
13
|
+
def default?
|
14
|
+
@is_default
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
data/lib/cfoundry/v2/app.rb
CHANGED
@@ -43,6 +43,10 @@ module CFoundry::V2
|
|
43
43
|
self.cache[:running_instances] = x
|
44
44
|
},
|
45
45
|
|
46
|
+
:instances => proc { |x|
|
47
|
+
self.total_instances = x
|
48
|
+
},
|
49
|
+
|
46
50
|
# TODO: remove these when cc consistently returns nested hashes
|
47
51
|
:framework_guid => proc { |x|
|
48
52
|
if f = self.cache[:framework]
|
@@ -104,13 +108,13 @@ module CFoundry::V2
|
|
104
108
|
end
|
105
109
|
|
106
110
|
def env
|
107
|
-
|
111
|
+
CFoundry::ChattyHash.new(
|
108
112
|
method(:env=),
|
109
|
-
environment_json)
|
113
|
+
stringify(environment_json))
|
110
114
|
end
|
111
115
|
|
112
116
|
def env=(x)
|
113
|
-
self.environment_json = x.to_hash
|
117
|
+
self.environment_json = stringify(x.to_hash)
|
114
118
|
end
|
115
119
|
|
116
120
|
def debug_mode # TODO v2
|
@@ -296,6 +300,18 @@ module CFoundry::V2
|
|
296
300
|
Instance.new(self, "0", @client).stream_file(*path, &blk)
|
297
301
|
end
|
298
302
|
|
303
|
+
private
|
304
|
+
|
305
|
+
def stringify(hash)
|
306
|
+
new = {}
|
307
|
+
|
308
|
+
hash.each do |k, v|
|
309
|
+
new[k.to_s] = v.to_s
|
310
|
+
end
|
311
|
+
|
312
|
+
new
|
313
|
+
end
|
314
|
+
|
299
315
|
class Instance
|
300
316
|
attr_reader :app, :id
|
301
317
|
|
data/lib/cfoundry/v2/base.rb
CHANGED
@@ -13,10 +13,12 @@ module CFoundry::V2
|
|
13
13
|
put("v2", "resource_match", :content => :json, :accept => :json, :payload => fingerprints)
|
14
14
|
end
|
15
15
|
|
16
|
-
def upload_app(guid, zipfile, resources = [])
|
17
|
-
payload = {
|
18
|
-
|
19
|
-
|
16
|
+
def upload_app(guid, zipfile = nil, resources = [])
|
17
|
+
payload = {}
|
18
|
+
payload[:resources] = MultiJson.dump(resources)
|
19
|
+
|
20
|
+
if zipfile
|
21
|
+
payload[:application] =
|
20
22
|
UploadIO.new(
|
21
23
|
if zipfile.is_a? File
|
22
24
|
zipfile
|
@@ -24,7 +26,7 @@ module CFoundry::V2
|
|
24
26
|
File.new(zipfile, "rb")
|
25
27
|
end,
|
26
28
|
"application/zip")
|
27
|
-
|
29
|
+
end
|
28
30
|
|
29
31
|
put("v2", "apps", guid, "bits", :payload => payload)
|
30
32
|
rescue EOFError
|
data/lib/cfoundry/v2/client.rb
CHANGED
data/lib/cfoundry/version.rb
CHANGED
@@ -46,6 +46,14 @@ describe CFoundry::AuthToken do
|
|
46
46
|
let(:access_token) { Base64.encode64('{"algo": "h1234"}{"user_id", "a6", "email": "a@b.com"}random-bytes') }
|
47
47
|
its(:token_data) { should eq({}) }
|
48
48
|
end
|
49
|
+
|
50
|
+
context "when the auth header is nil" do
|
51
|
+
before do
|
52
|
+
subject.auth_header = nil
|
53
|
+
end
|
54
|
+
|
55
|
+
its(:token_data) { should eq({}) }
|
56
|
+
end
|
49
57
|
end
|
50
58
|
end
|
51
59
|
|
@@ -74,4 +82,44 @@ describe CFoundry::AuthToken do
|
|
74
82
|
its(:token_data) { should eq({ :baz => "buzz" }) }
|
75
83
|
end
|
76
84
|
end
|
77
|
-
|
85
|
+
|
86
|
+
describe "#auth_header=" do
|
87
|
+
let(:access_token) { Base64.encode64('{"algo": "h1234"}{"user_id": "a6", "email": "a@b.com"}random-bytes') }
|
88
|
+
let(:other_access_token) { Base64.encode64('{"algo": "h1234"}{"user_id": "b6", "email": "a@b.com"}random-bytes') }
|
89
|
+
|
90
|
+
subject { CFoundry::AuthToken.new("bearer #{access_token}") }
|
91
|
+
|
92
|
+
it "invalidates @token_data" do
|
93
|
+
subject.token_data
|
94
|
+
expect {
|
95
|
+
subject.auth_header = "bearer #{other_access_token}"
|
96
|
+
}.to change { subject.token_data[:user_id] }.from("a6").to("b6")
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "#expires_soon?" do
|
101
|
+
let(:access_token) { Base64.encode64(%Q|{"algo": "h1234"}{"exp":#{expiration.to_i}}random-bytes|) }
|
102
|
+
|
103
|
+
subject { CFoundry::AuthToken.new("bearer #{access_token}") }
|
104
|
+
|
105
|
+
context "when the token expires in less than 1 minute" do
|
106
|
+
let(:expiration) { Time.now + 59 }
|
107
|
+
|
108
|
+
it "returns true" do
|
109
|
+
Timecop.freeze do
|
110
|
+
expect(subject.expires_soon?).to be_true
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
context "when the token expires in greater than or equal to 1 minute" do
|
116
|
+
let(:expiration) { Time.now + 60 }
|
117
|
+
|
118
|
+
it "returns false" do
|
119
|
+
Timecop.freeze do
|
120
|
+
expect(subject.expires_soon?).to be_false
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -30,46 +30,155 @@ describe CFoundry::BaseClient do
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
33
|
+
|
34
|
+
context "when there is a token with an auth_header" do
|
35
|
+
let(:refresh_token) { nil }
|
36
|
+
let(:token) { CFoundry::AuthToken.new("bearer something", refresh_token) }
|
37
|
+
|
38
|
+
before do
|
39
|
+
stub(subject).request_raw
|
40
|
+
subject.token = token
|
41
|
+
stub(token).expires_soon? { expires_soon? }
|
42
|
+
end
|
43
|
+
|
44
|
+
context "and the token is about to expire" do
|
45
|
+
let(:uaa) { Object.new }
|
46
|
+
let(:expires_soon?) { true }
|
47
|
+
|
48
|
+
context "and there is a refresh token" do
|
49
|
+
let(:refresh_token) { "some-refresh-token" }
|
50
|
+
|
51
|
+
it "sets the token's auth header to nil to prevent recursion" do
|
52
|
+
stub(subject).refresh_token!
|
53
|
+
subject.request("GET", "foo")
|
54
|
+
end
|
55
|
+
|
56
|
+
it "refreshes the access token" do
|
57
|
+
mock(subject).refresh_token!
|
58
|
+
subject.request("GET", "foo")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "and there is NOT a refresh token" do
|
63
|
+
let(:refresh_token) { nil }
|
64
|
+
|
65
|
+
it "moves along" do
|
66
|
+
mock(subject).request_raw(anything, anything, anything)
|
67
|
+
dont_allow(subject).refresh_token!
|
68
|
+
subject.request("GET", "foo")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context "and the token is NOT about to expire" do
|
74
|
+
let(:expires_soon?) { nil }
|
75
|
+
|
76
|
+
it "moves along" do
|
77
|
+
mock(subject).request_raw(anything, anything, anything)
|
78
|
+
dont_allow(subject).refresh_token!
|
79
|
+
subject.request("GET", "foo")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "#refresh_token!" do
|
85
|
+
let(:uaa) { stub }
|
86
|
+
let(:access_token) { Base64.encode64(%Q|{"algo": "h1234"}{"a":"b"}random-bytes|) }
|
87
|
+
let(:refresh_token) { "xyz" }
|
88
|
+
let(:new_access_token) { Base64.encode64(%Q|{"algo": "h1234"}{"a":"x"}random-bytes|) }
|
89
|
+
let(:auth_token) { CFoundry::AuthToken.new("bearer #{access_token}", refresh_token) }
|
90
|
+
|
91
|
+
before { stub(subject).uaa { uaa } }
|
92
|
+
|
93
|
+
it "refreshes the token with UAA client and assigns it" do
|
94
|
+
mock(uaa).try_to_refresh_token! {
|
95
|
+
CFoundry::AuthToken.new("bearer #{new_access_token}", auth_token.refresh_token)
|
96
|
+
}
|
97
|
+
|
98
|
+
subject.refresh_token!
|
99
|
+
|
100
|
+
expect(subject.token.auth_header).to eq "bearer #{new_access_token}"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
33
104
|
end
|
34
105
|
|
35
106
|
describe "UAAClient" do
|
36
|
-
|
37
|
-
|
38
|
-
|
107
|
+
context "with a valid uaa endpoint" do
|
108
|
+
before do
|
109
|
+
stub(subject).info { { :authorization_endpoint => "http://uaa.example.com" } }
|
110
|
+
end
|
111
|
+
|
112
|
+
describe "#uaa" do
|
113
|
+
it "creates a UAAClient on the first call" do
|
114
|
+
expect(subject.uaa).to be_a CFoundry::UAAClient
|
115
|
+
end
|
39
116
|
|
40
|
-
|
41
|
-
|
42
|
-
|
117
|
+
it "returns the same object on later calls" do
|
118
|
+
uaa = subject.uaa
|
119
|
+
expect(subject.uaa).to eq uaa
|
120
|
+
end
|
121
|
+
|
122
|
+
it "has the same AuthToken as BaseClient" do
|
123
|
+
token = CFoundry::AuthToken.new(nil)
|
124
|
+
stub(subject).token { token }
|
125
|
+
expect(subject.uaa.token).to eq token
|
126
|
+
end
|
43
127
|
end
|
44
128
|
|
45
|
-
|
46
|
-
|
47
|
-
|
129
|
+
describe "#token=" do
|
130
|
+
it "propagates the change to #uaa" do
|
131
|
+
subject.uaa
|
132
|
+
expect(subject.uaa.token).to eq subject.token
|
133
|
+
subject.token = CFoundry::AuthToken.new(nil)
|
134
|
+
expect(subject.uaa.token).to eq subject.token
|
135
|
+
end
|
48
136
|
end
|
49
137
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
138
|
+
describe "#trace=" do
|
139
|
+
it "propagates the change to #uaa" do
|
140
|
+
subject.uaa
|
141
|
+
subject.trace = true
|
142
|
+
expect(subject.uaa.trace).to eq true
|
143
|
+
subject.trace = false
|
144
|
+
expect(subject.uaa.trace).to eq false
|
145
|
+
end
|
54
146
|
end
|
55
147
|
end
|
56
148
|
|
57
|
-
|
58
|
-
|
59
|
-
subject.
|
60
|
-
expect(subject.uaa.token).to eq subject.token
|
61
|
-
subject.token = CFoundry::AuthToken.new(nil)
|
62
|
-
expect(subject.uaa.token).to eq subject.token
|
149
|
+
context "with no uaa endpoint" do
|
150
|
+
before do
|
151
|
+
stub(subject).info { { :something => "else" } }
|
63
152
|
end
|
153
|
+
|
154
|
+
describe "#uaa" do
|
155
|
+
it "does not return a UAAClient" do
|
156
|
+
expect(subject.uaa).to be_nil
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
64
160
|
end
|
161
|
+
end
|
162
|
+
|
163
|
+
describe "#password_score" do
|
164
|
+
context "with a uaa" do
|
165
|
+
before do
|
166
|
+
stub(subject).info { { :authorization_endpoint => "http://uaa.example.com" } }
|
167
|
+
end
|
168
|
+
|
169
|
+
it "delegates to the uaa's password strength method" do
|
170
|
+
mock(subject.uaa).password_score('password')
|
171
|
+
subject.password_score('password')
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
context "without a uaa" do
|
176
|
+
before do
|
177
|
+
stub(subject).info { { :something => "else" } }
|
178
|
+
end
|
65
179
|
|
66
|
-
|
67
|
-
|
68
|
-
subject.uaa
|
69
|
-
subject.trace = true
|
70
|
-
expect(subject.uaa.trace).to eq true
|
71
|
-
subject.trace = false
|
72
|
-
expect(subject.uaa.trace).to eq false
|
180
|
+
it "returns :unknown" do
|
181
|
+
expect(subject.password_score('password')).to eq(:unknown)
|
73
182
|
end
|
74
183
|
end
|
75
184
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CFoundry::Client do
|
4
|
+
subject { CFoundry::Client.new('http://example.com') }
|
5
|
+
|
6
|
+
it "returns a v1 client when used on a v1 target" do
|
7
|
+
stub_request(:get, "http://example.com/info").to_return(:status => 200, :body => '{"version":1}')
|
8
|
+
subject.should be_a(CFoundry::V1::Client)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "returns a v2 client when used on a v2 target" do
|
12
|
+
stub_request(:get, "http://example.com/info").to_return(:status => 200, :body => '{"version":2}')
|
13
|
+
subject.should be_a(CFoundry::V2::Client)
|
14
|
+
end
|
15
|
+
end
|
@@ -200,6 +200,24 @@ describe CFoundry::RestClient do
|
|
200
200
|
end
|
201
201
|
end
|
202
202
|
|
203
|
+
describe "when the path is a full url" do
|
204
|
+
let(:path) { "http://example.com" }
|
205
|
+
|
206
|
+
it "requests the given url" do
|
207
|
+
stub = stub_request(:get, "http://example.com")
|
208
|
+
subject
|
209
|
+
expect(stub).to have_been_requested
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
describe "when the path is malformed" do
|
214
|
+
let(:path) { "#%&*$(#%&$%)" }
|
215
|
+
|
216
|
+
it "blows up" do
|
217
|
+
expect { subject }.to raise_error(URI::InvalidURIError)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
203
221
|
describe 'trace' do
|
204
222
|
before do
|
205
223
|
rest_client.trace = true
|
@@ -7,6 +7,7 @@ describe CFoundry::UAAClient do
|
|
7
7
|
|
8
8
|
before do
|
9
9
|
uaa.token = CFoundry::AuthToken.new(auth_header)
|
10
|
+
CF::UAA::Util.default_logger.level = 1
|
10
11
|
end
|
11
12
|
|
12
13
|
shared_examples "UAA wrapper" do
|
@@ -61,33 +62,44 @@ EOF
|
|
61
62
|
let(:state) { 'somestate' }
|
62
63
|
let(:redirect_uri) { 'https://uaa.cloudfoundry.com/redirect/vmc' }
|
63
64
|
let(:auth) { Object.new }
|
65
|
+
let(:issuer) { Object.new }
|
64
66
|
|
65
67
|
subject { uaa.authorize(username, password) }
|
66
68
|
|
67
|
-
before
|
69
|
+
before do
|
70
|
+
stub(issuer).owner_password_grant { auth }
|
71
|
+
stub(uaa).token_issuer { issuer }
|
72
|
+
end
|
68
73
|
|
69
74
|
include_examples "UAA wrapper"
|
70
75
|
|
71
76
|
it 'returns the token on successful authentication' do
|
72
|
-
|
77
|
+
mock(issuer).owner_password_grant(username, password) { auth }
|
73
78
|
expect(subject).to eq auth
|
74
79
|
end
|
75
80
|
|
76
81
|
context 'when authorization fails' do
|
77
82
|
context 'in the expected way' do
|
78
83
|
it 'raises a CFoundry::Denied error' do
|
79
|
-
|
80
|
-
|
84
|
+
mock(issuer).owner_password_grant(anything, anything) { raise CF::UAA::BadResponse.new("401: FooBar") }
|
81
85
|
expect { subject }.to raise_error(CFoundry::Denied, "401: Authorization failed")
|
82
86
|
end
|
83
87
|
end
|
84
88
|
|
85
89
|
context 'in an unexpected way' do
|
86
90
|
it 'raises a CFoundry::Denied error' do
|
87
|
-
|
91
|
+
mock(issuer).owner_password_grant(anything, anything) { raise CF::UAA::BadResponse.new("no_status_code") }
|
88
92
|
expect { subject }.to raise_error(CFoundry::Denied, "400: Authorization failed")
|
89
93
|
end
|
90
94
|
end
|
95
|
+
|
96
|
+
context "with a CF::UAA::TargetError" do
|
97
|
+
it "retries with implicit grant" do
|
98
|
+
stub(issuer).owner_password_grant { raise CF::UAA::TargetError.new("useless info") }
|
99
|
+
mock(issuer).implicit_grant_with_creds(:username => username, :password => password)
|
100
|
+
expect { subject }.to_not raise_error
|
101
|
+
end
|
102
|
+
end
|
91
103
|
end
|
92
104
|
end
|
93
105
|
|
@@ -172,7 +184,7 @@ EOF
|
|
172
184
|
it { should == :good }
|
173
185
|
end
|
174
186
|
|
175
|
-
context 'when the score is less than the required
|
187
|
+
context 'when the score is less than the required score' do
|
176
188
|
let(:response) { MultiJson.encode "score" => 1, "requiredScore" => 5 }
|
177
189
|
it { should == :weak }
|
178
190
|
end
|
@@ -259,7 +271,7 @@ EOF
|
|
259
271
|
end
|
260
272
|
|
261
273
|
context "when the block raises CF::UAA::TargetError" do
|
262
|
-
let(:error) { CF::UAA::TargetError.new({
|
274
|
+
let(:error) { CF::UAA::TargetError.new({ :error => "foo", :error_description => "bar" }) }
|
263
275
|
|
264
276
|
it "raises CFoundry::UAAError" do
|
265
277
|
expect { subject }.to raise_exception(CFoundry::UAAError, "foo: bar")
|
@@ -270,7 +282,7 @@ EOF
|
|
270
282
|
describe "#token_issuer" do
|
271
283
|
it "has logging level 0 if #trace is true" do
|
272
284
|
uaa.trace = true
|
273
|
-
expect(uaa.send(:token_issuer).logger.level).to eq
|
285
|
+
expect(uaa.send(:token_issuer).logger.level).to eq -1
|
274
286
|
end
|
275
287
|
|
276
288
|
it "has logging level 1 if #trace is false" do
|
@@ -282,7 +294,7 @@ EOF
|
|
282
294
|
describe "#scim" do
|
283
295
|
it "has logging level 0 if #trace is true" do
|
284
296
|
uaa.trace = true
|
285
|
-
expect(uaa.send(:scim).logger.level).to eq
|
297
|
+
expect(uaa.send(:scim).logger.level).to eq -1
|
286
298
|
end
|
287
299
|
|
288
300
|
it "has logging level 1 if #trace is false" do
|
@@ -290,4 +302,31 @@ EOF
|
|
290
302
|
expect(uaa.send(:scim).logger.level).to eq 1
|
291
303
|
end
|
292
304
|
end
|
305
|
+
|
306
|
+
describe "#try_to_refresh_token!" do
|
307
|
+
it "uses the refresh token to get a new access token" do
|
308
|
+
mock(uaa.send(:token_issuer)).refresh_token_grant(uaa.token.refresh_token) do
|
309
|
+
CF::UAA::TokenInfo.new(
|
310
|
+
:token_type => "bearer",
|
311
|
+
:access_token => "refreshed-token",
|
312
|
+
:refresh_token => "some-refresh-token")
|
313
|
+
end
|
314
|
+
|
315
|
+
uaa.try_to_refresh_token!
|
316
|
+
expect(uaa.token.auth_header).to eq "bearer refreshed-token"
|
317
|
+
expect(uaa.token.refresh_token).to eq "some-refresh-token"
|
318
|
+
end
|
319
|
+
|
320
|
+
context "when the refresh token has expired" do
|
321
|
+
it "returns the current token" do
|
322
|
+
stub(uaa.send(:token_issuer)).refresh_token_grant do
|
323
|
+
raise CF::UAA::TargetError.new
|
324
|
+
end
|
325
|
+
|
326
|
+
expect {
|
327
|
+
uaa.try_to_refresh_token!
|
328
|
+
}.to_not change { uaa.token }
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|
293
332
|
end
|
@@ -28,15 +28,16 @@ describe CFoundry::UploadHelpers do
|
|
28
28
|
end
|
29
29
|
|
30
30
|
before do
|
31
|
+
FileUtils.rm_rf tmpdir
|
32
|
+
|
31
33
|
stub(Dir).tmpdir do
|
32
34
|
FileUtils.mkdir_p tmpdir
|
33
35
|
tmpdir
|
34
36
|
end
|
37
|
+
|
35
38
|
stub(base).upload_app.with_any_args
|
36
39
|
end
|
37
40
|
|
38
|
-
after { FileUtils.rm_rf tmpdir }
|
39
|
-
|
40
41
|
subject { fake_model.upload(path, check_resources) }
|
41
42
|
|
42
43
|
def relative_glob(dir)
|
@@ -125,5 +126,49 @@ describe CFoundry::UploadHelpers do
|
|
125
126
|
subject
|
126
127
|
end
|
127
128
|
end
|
129
|
+
|
130
|
+
context 'when all files match existing resources' do
|
131
|
+
context 'and there are directories' do
|
132
|
+
let(:path) { "#{SPEC_ROOT}/fixtures/apps/with_nested_directories" }
|
133
|
+
|
134
|
+
it 'prunes them before zipping' do
|
135
|
+
stub(fake_model).make_fingerprints(anything) do
|
136
|
+
[[], CFoundry::UploadHelpers::RESOURCE_CHECK_LIMIT + 1]
|
137
|
+
end
|
138
|
+
|
139
|
+
stub(base).resource_match(anything) do
|
140
|
+
%w{ xyz foo/bar/baz/fizz }.map do |path|
|
141
|
+
{ :fn => "#{tmpdir}/.vmc_#{guid}_files/#{path}" }
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
mock(base).upload_app(anything, false, anything)
|
146
|
+
|
147
|
+
fake_model.upload(path)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
context "when only dotfiles don't match existing resources" do
|
153
|
+
let(:path) { "#{SPEC_ROOT}/fixtures/apps/with_dotfiles" }
|
154
|
+
|
155
|
+
it 'does not prune them' do
|
156
|
+
stub(fake_model).make_fingerprints(anything) do
|
157
|
+
[[], CFoundry::UploadHelpers::RESOURCE_CHECK_LIMIT + 1]
|
158
|
+
end
|
159
|
+
|
160
|
+
stub(base).resource_match(anything) do
|
161
|
+
%w{ xyz }.map do |path|
|
162
|
+
{ :fn => "#{tmpdir}/.vmc_#{guid}_files/#{path}" }
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
mock(base).upload_app(anything, anything, anything) do |_, zip, _|
|
167
|
+
expect(zip).to be_a(String)
|
168
|
+
end
|
169
|
+
|
170
|
+
fake_model.upload(path)
|
171
|
+
end
|
172
|
+
end
|
128
173
|
end
|
129
|
-
end
|
174
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe CFoundry::V2::App do
|
4
|
+
let(:client) { fake_client }
|
5
|
+
|
6
|
+
describe "environment" do
|
7
|
+
let(:app) { fake :app, :env => { "FOO" => "1" } }
|
8
|
+
|
9
|
+
it "returns a hash-like object" do
|
10
|
+
expect(app.env["FOO"]).to eq "1"
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "converting keys and values to strings" do
|
14
|
+
let(:app) { fake :app, :env => { :FOO => 1 } }
|
15
|
+
|
16
|
+
it "converts keys and values to strings" do
|
17
|
+
expect(app.env.to_hash).to eq("FOO" => "1")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "when changes are made to the hash-like object" do
|
22
|
+
it "reflects the changes in .env" do
|
23
|
+
expect {
|
24
|
+
app.env["BAR"] = "2"
|
25
|
+
}.to change { app.env.to_hash }.from("FOO" => "1").to("FOO" => "1", "BAR" => "2")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "when the env is set to something else" do
|
30
|
+
it "reflects the changes in .env" do
|
31
|
+
expect {
|
32
|
+
app.env = { "BAR" => "2" }
|
33
|
+
}.to change { app.env.to_hash }.from("FOO" => "1").to("BAR" => "2")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#summarize!" do
|
39
|
+
let(:app) { fake :app }
|
40
|
+
|
41
|
+
it "assigns :instances as #total_instances" do
|
42
|
+
stub(app).summary { { :instances => 4 } }
|
43
|
+
|
44
|
+
app.summarize!
|
45
|
+
|
46
|
+
expect(app.total_instances).to eq(4)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -244,6 +244,23 @@ describe CFoundry::V2::Base do
|
|
244
244
|
base.upload_app(guid, fake_zipfile)
|
245
245
|
expect(stub).to have_been_requested
|
246
246
|
end
|
247
|
+
|
248
|
+
context "when there is no file to upload" do
|
249
|
+
it "does not include 'application' in the request hash" do
|
250
|
+
stub =
|
251
|
+
stub_request(
|
252
|
+
:put,
|
253
|
+
"https://api.cloudfoundry.com/v2/apps/#{guid}/bits"
|
254
|
+
).with { |request|
|
255
|
+
request.body =~ /name="resources"/ &&
|
256
|
+
request.body !~ /name="application"/
|
257
|
+
}.to_return(
|
258
|
+
:body => "{}"
|
259
|
+
)
|
260
|
+
base.upload_app(guid)
|
261
|
+
expect(stub).to have_been_requested
|
262
|
+
end
|
263
|
+
end
|
247
264
|
end
|
248
265
|
|
249
266
|
describe "#stream_file" do
|
@@ -26,6 +26,11 @@ describe CFoundry::V2::Client do
|
|
26
26
|
subject { client.current_user }
|
27
27
|
before { client.token = token }
|
28
28
|
|
29
|
+
context "when there is no token" do
|
30
|
+
let(:token) { nil }
|
31
|
+
it { should eq nil }
|
32
|
+
end
|
33
|
+
|
29
34
|
context "when there is no access_token_data" do
|
30
35
|
let(:token) { CFoundry::AuthToken.new("bearer some-access-token", "some-refresh-token") }
|
31
36
|
it { should eq nil }
|
@@ -0,0 +1 @@
|
|
1
|
+
bar
|
File without changes
|
File without changes
|
data/spec/spec_helper.rb
CHANGED
@@ -4,6 +4,7 @@ require "rspec"
|
|
4
4
|
require "cfoundry"
|
5
5
|
require "webmock/rspec"
|
6
6
|
require "ostruct"
|
7
|
+
require "timecop"
|
7
8
|
|
8
9
|
Dir[File.expand_path('../{support,fakes}/**/*.rb', __FILE__)].each do |file|
|
9
10
|
require file
|
@@ -13,4 +14,4 @@ RSpec.configure do |c|
|
|
13
14
|
c.include Fake::FakeMethods
|
14
15
|
c.include V1Fake::FakeMethods
|
15
16
|
c.mock_with :rr
|
16
|
-
end
|
17
|
+
end
|
data/spec/support/fake_helper.rb
CHANGED
@@ -79,7 +79,7 @@ module CFoundry::V2
|
|
79
79
|
end
|
80
80
|
|
81
81
|
def fake_attributes(attributes)
|
82
|
-
fakes = default_fakes
|
82
|
+
fakes = default_fakes
|
83
83
|
|
84
84
|
# default relationships to other fake objects
|
85
85
|
self.class.to_one_relations.each do |name, opts|
|
@@ -95,6 +95,17 @@ module CFoundry::V2
|
|
95
95
|
end
|
96
96
|
end
|
97
97
|
|
98
|
+
# make sure that the attributes provided are set after the defaults
|
99
|
+
#
|
100
|
+
# we have to do this for cases like environment_json vs. env,
|
101
|
+
# where one would clobber the other
|
102
|
+
attributes.each do |k, _|
|
103
|
+
fakes.delete k
|
104
|
+
end
|
105
|
+
|
106
|
+
fakes = fakes.to_a
|
107
|
+
fakes += attributes.to_a
|
108
|
+
|
98
109
|
fakes
|
99
110
|
end
|
100
111
|
|
metadata
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cfoundry
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 3470363275
|
5
|
+
prerelease: 6
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 5
|
9
|
-
-
|
10
|
-
|
9
|
+
- 1
|
10
|
+
- rc
|
11
|
+
- 1
|
12
|
+
version: 0.5.1.rc1
|
11
13
|
platform: ruby
|
12
14
|
authors:
|
13
15
|
- Cloud Foundry Team
|
@@ -16,7 +18,7 @@ autorequire:
|
|
16
18
|
bindir: bin
|
17
19
|
cert_chain: []
|
18
20
|
|
19
|
-
date: 2013-02-
|
21
|
+
date: 2013-02-20 00:00:00 Z
|
20
22
|
dependencies:
|
21
23
|
- !ruby/object:Gem::Dependency
|
22
24
|
version_requirements: &id001 !ruby/object:Gem::Requirement
|
@@ -153,6 +155,20 @@ dependencies:
|
|
153
155
|
type: :development
|
154
156
|
name: gem-release
|
155
157
|
requirement: *id009
|
158
|
+
- !ruby/object:Gem::Dependency
|
159
|
+
version_requirements: &id010 !ruby/object:Gem::Requirement
|
160
|
+
none: false
|
161
|
+
requirements:
|
162
|
+
- - ">="
|
163
|
+
- !ruby/object:Gem::Version
|
164
|
+
hash: 3
|
165
|
+
segments:
|
166
|
+
- 0
|
167
|
+
version: "0"
|
168
|
+
prerelease: false
|
169
|
+
type: :development
|
170
|
+
name: timecop
|
171
|
+
requirement: *id010
|
156
172
|
description:
|
157
173
|
email:
|
158
174
|
- vcap-dev@googlegroups.com
|
@@ -185,6 +201,7 @@ files:
|
|
185
201
|
- lib/cfoundry/v1/runtime.rb
|
186
202
|
- lib/cfoundry/v1/service.rb
|
187
203
|
- lib/cfoundry/v1/service_instance.rb
|
204
|
+
- lib/cfoundry/v1/service_plan.rb
|
188
205
|
- lib/cfoundry/v1/user.rb
|
189
206
|
- lib/cfoundry/v2/app.rb
|
190
207
|
- lib/cfoundry/v2/base.rb
|
@@ -209,6 +226,7 @@ files:
|
|
209
226
|
- lib/cfoundry.rb
|
210
227
|
- spec/cfoundry/auth_token_spec.rb
|
211
228
|
- spec/cfoundry/baseclient_spec.rb
|
229
|
+
- spec/cfoundry/client_spec.rb
|
212
230
|
- spec/cfoundry/errors_spec.rb
|
213
231
|
- spec/cfoundry/rest_client_spec.rb
|
214
232
|
- spec/cfoundry/trace_helpers_spec.rb
|
@@ -217,6 +235,7 @@ files:
|
|
217
235
|
- spec/cfoundry/v1/base_spec.rb
|
218
236
|
- spec/cfoundry/v1/client_spec.rb
|
219
237
|
- spec/cfoundry/v1/model_magic_spec.rb
|
238
|
+
- spec/cfoundry/v2/app_spec.rb
|
220
239
|
- spec/cfoundry/v2/base_spec.rb
|
221
240
|
- spec/cfoundry/v2/client_spec.rb
|
222
241
|
- spec/cfoundry/v2/model_magic_spec.rb
|
@@ -233,6 +252,9 @@ files:
|
|
233
252
|
- spec/fakes/service_plan_fake.rb
|
234
253
|
- spec/fakes/space_fake.rb
|
235
254
|
- spec/fakes/user_fake.rb
|
255
|
+
- spec/fixtures/apps/with_dotfiles/xyz
|
256
|
+
- spec/fixtures/apps/with_nested_directories/foo/bar/baz/fizz
|
257
|
+
- spec/fixtures/apps/with_nested_directories/xyz
|
236
258
|
- spec/fixtures/apps/with_vmcignore/ignored_dir/file_in_ignored_dir.txt
|
237
259
|
- spec/fixtures/apps/with_vmcignore/ignored_file.txt
|
238
260
|
- spec/fixtures/apps/with_vmcignore/non_ignored_dir/file_in_non_ignored_dir.txt
|
@@ -265,12 +287,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
265
287
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
266
288
|
none: false
|
267
289
|
requirements:
|
268
|
-
- - "
|
290
|
+
- - ">"
|
269
291
|
- !ruby/object:Gem::Version
|
270
|
-
hash:
|
292
|
+
hash: 25
|
271
293
|
segments:
|
272
|
-
-
|
273
|
-
|
294
|
+
- 1
|
295
|
+
- 3
|
296
|
+
- 1
|
297
|
+
version: 1.3.1
|
274
298
|
requirements: []
|
275
299
|
|
276
300
|
rubyforge_project: cfoundry
|
@@ -281,6 +305,7 @@ summary: High-level library for working with the Cloud Foundry API.
|
|
281
305
|
test_files:
|
282
306
|
- spec/cfoundry/auth_token_spec.rb
|
283
307
|
- spec/cfoundry/baseclient_spec.rb
|
308
|
+
- spec/cfoundry/client_spec.rb
|
284
309
|
- spec/cfoundry/errors_spec.rb
|
285
310
|
- spec/cfoundry/rest_client_spec.rb
|
286
311
|
- spec/cfoundry/trace_helpers_spec.rb
|
@@ -289,6 +314,7 @@ test_files:
|
|
289
314
|
- spec/cfoundry/v1/base_spec.rb
|
290
315
|
- spec/cfoundry/v1/client_spec.rb
|
291
316
|
- spec/cfoundry/v1/model_magic_spec.rb
|
317
|
+
- spec/cfoundry/v2/app_spec.rb
|
292
318
|
- spec/cfoundry/v2/base_spec.rb
|
293
319
|
- spec/cfoundry/v2/client_spec.rb
|
294
320
|
- spec/cfoundry/v2/model_magic_spec.rb
|
@@ -305,6 +331,9 @@ test_files:
|
|
305
331
|
- spec/fakes/service_plan_fake.rb
|
306
332
|
- spec/fakes/space_fake.rb
|
307
333
|
- spec/fakes/user_fake.rb
|
334
|
+
- spec/fixtures/apps/with_dotfiles/xyz
|
335
|
+
- spec/fixtures/apps/with_nested_directories/foo/bar/baz/fizz
|
336
|
+
- spec/fixtures/apps/with_nested_directories/xyz
|
308
337
|
- spec/fixtures/apps/with_vmcignore/ignored_dir/file_in_ignored_dir.txt
|
309
338
|
- spec/fixtures/apps/with_vmcignore/ignored_file.txt
|
310
339
|
- spec/fixtures/apps/with_vmcignore/non_ignored_dir/file_in_non_ignored_dir.txt
|