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