cfoundry 0.4.21 → 0.5.0
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 +47 -24
- data/lib/cfoundry/auth_token.rb +48 -0
- data/lib/cfoundry/baseclient.rb +96 -277
- data/lib/cfoundry/client.rb +2 -0
- data/lib/cfoundry/concerns/login_helpers.rb +13 -0
- data/lib/cfoundry/errors.rb +21 -13
- data/lib/cfoundry/rest_client.rb +290 -0
- data/lib/cfoundry/test_support.rb +3 -0
- data/lib/cfoundry/trace_helpers.rb +9 -9
- data/lib/cfoundry/uaaclient.rb +66 -74
- data/lib/cfoundry/upload_helpers.rb +2 -0
- data/lib/cfoundry/v1/app.rb +2 -2
- data/lib/cfoundry/v1/base.rb +4 -51
- data/lib/cfoundry/v1/client.rb +7 -30
- data/lib/cfoundry/v1/model.rb +22 -5
- data/lib/cfoundry/v1/model_magic.rb +30 -15
- data/lib/cfoundry/v2/app.rb +2 -5
- data/lib/cfoundry/v2/base.rb +10 -74
- data/lib/cfoundry/v2/client.rb +19 -30
- data/lib/cfoundry/v2/domain.rb +1 -4
- data/lib/cfoundry/v2/model.rb +1 -3
- data/lib/cfoundry/v2/model_magic.rb +13 -23
- data/lib/cfoundry/version.rb +1 -1
- data/lib/cfoundry/zip.rb +1 -1
- data/spec/cfoundry/auth_token_spec.rb +77 -0
- data/spec/cfoundry/baseclient_spec.rb +54 -30
- data/spec/cfoundry/errors_spec.rb +10 -13
- data/spec/cfoundry/rest_client_spec.rb +238 -0
- data/spec/cfoundry/trace_helpers_spec.rb +10 -5
- data/spec/cfoundry/uaaclient_spec.rb +141 -114
- data/spec/cfoundry/upload_helpers_spec.rb +129 -0
- data/spec/cfoundry/v1/base_spec.rb +2 -2
- data/spec/cfoundry/v1/client_spec.rb +17 -0
- data/spec/cfoundry/v1/model_magic_spec.rb +43 -0
- data/spec/cfoundry/v2/base_spec.rb +256 -33
- data/spec/cfoundry/v2/client_spec.rb +68 -0
- data/spec/cfoundry/v2/model_magic_spec.rb +49 -0
- data/spec/fixtures/apps/with_vmcignore/ignored_dir/file_in_ignored_dir.txt +1 -0
- data/spec/fixtures/apps/with_vmcignore/ignored_file.txt +1 -0
- data/spec/fixtures/apps/with_vmcignore/non_ignored_dir/file_in_non_ignored_dir.txt +1 -0
- data/spec/fixtures/apps/with_vmcignore/non_ignored_dir/ignored_file.txt +1 -0
- data/spec/fixtures/apps/with_vmcignore/non_ignored_file.txt +1 -0
- data/spec/fixtures/empty_file +0 -0
- data/spec/spec_helper.rb +4 -4
- data/spec/support/randoms.rb +3 -0
- data/spec/support/shared_examples/client_login_examples.rb +46 -0
- data/spec/support/{summaries.rb → shared_examples/model_summary_examples.rb} +0 -0
- data/spec/support/v1_fake_helper.rb +144 -0
- metadata +101 -37
- data/lib/cfoundry/spec_helper.rb +0 -1
data/lib/cfoundry/v1/app.rb
CHANGED
@@ -10,8 +10,8 @@ module CFoundry::V1
|
|
10
10
|
attribute :name, :string, :guid => true
|
11
11
|
attribute :instances, :integer
|
12
12
|
attribute :state, :string
|
13
|
-
attribute :created, :integer, :at => [:meta, :created]
|
14
|
-
attribute :version, :integer, :at => [:meta, :version]
|
13
|
+
attribute :created, :integer, :at => [:meta, :created], :read_only => true
|
14
|
+
attribute :version, :integer, :at => [:meta, :version], :read_only => true
|
15
15
|
attribute :framework, :string, :at => [:staging, :model]
|
16
16
|
attribute :runtime, :string, :at => [:staging, :stack]
|
17
17
|
attribute :command, :string, :at => [:staging, :command]
|
data/lib/cfoundry/v1/base.rb
CHANGED
@@ -9,36 +9,6 @@ module CFoundry::V1
|
|
9
9
|
class Base < CFoundry::BaseClient
|
10
10
|
include BaseClientMethods
|
11
11
|
|
12
|
-
attr_accessor :target, :token, :proxy, :trace, :backtrace, :log
|
13
|
-
|
14
|
-
def initialize(
|
15
|
-
target = "https://api.cloudfoundry.com",
|
16
|
-
token = nil)
|
17
|
-
super
|
18
|
-
end
|
19
|
-
|
20
|
-
|
21
|
-
# The UAA used for this client.
|
22
|
-
#
|
23
|
-
# `false` if no UAA (legacy)
|
24
|
-
def uaa
|
25
|
-
return @uaa unless @uaa.nil?
|
26
|
-
|
27
|
-
endpoint = info[:authorization_endpoint]
|
28
|
-
return @uaa = false unless endpoint
|
29
|
-
|
30
|
-
@uaa = CFoundry::UAAClient.new(endpoint)
|
31
|
-
@uaa.trace = @trace
|
32
|
-
@uaa.token = @token
|
33
|
-
@uaa
|
34
|
-
end
|
35
|
-
|
36
|
-
|
37
|
-
# Cloud metadata
|
38
|
-
def info
|
39
|
-
get("info", :accept => :json)
|
40
|
-
end
|
41
|
-
|
42
12
|
def system_services
|
43
13
|
get("services", "v1", "offerings", :content => :json, :accept => :json)
|
44
14
|
end
|
@@ -50,12 +20,11 @@ module CFoundry::V1
|
|
50
20
|
# Users
|
51
21
|
def create_user(payload)
|
52
22
|
# no JSON response
|
53
|
-
post(
|
23
|
+
post("users", :content => :json, :payload => payload)
|
54
24
|
end
|
55
25
|
|
56
26
|
def create_token(payload, email)
|
57
|
-
post(
|
58
|
-
:content => :json, :accept => :json)
|
27
|
+
post("users", email, "tokens", :content => :json, :accept => :json, :payload => payload)
|
59
28
|
end
|
60
29
|
|
61
30
|
# Applications
|
@@ -77,7 +46,7 @@ module CFoundry::V1
|
|
77
46
|
end
|
78
47
|
|
79
48
|
def resource_match(fingerprints)
|
80
|
-
post(
|
49
|
+
post("resources", :content => :json, :accept => :json, :payload => fingerprints)
|
81
50
|
end
|
82
51
|
|
83
52
|
def upload_app(name, zipfile, resources = [])
|
@@ -94,25 +63,9 @@ module CFoundry::V1
|
|
94
63
|
"application/zip")
|
95
64
|
}
|
96
65
|
|
97
|
-
post(
|
66
|
+
post("apps", name, "application", :payload => payload)
|
98
67
|
rescue EOFError
|
99
68
|
retry
|
100
69
|
end
|
101
|
-
|
102
|
-
private
|
103
|
-
|
104
|
-
def handle_response(response, accept, request)
|
105
|
-
# this is a copy paste of v2
|
106
|
-
return super if response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPRedirection)
|
107
|
-
|
108
|
-
info = parse_json(response.body)
|
109
|
-
return super unless info[:code]
|
110
|
-
|
111
|
-
cls = CFoundry::APIError.error_classes[info[:code]]
|
112
|
-
|
113
|
-
raise (cls || CFoundry::APIError).new(request, response, info[:description], info[:code])
|
114
|
-
rescue MultiJson::DecodeError
|
115
|
-
super
|
116
|
-
end
|
117
70
|
end
|
118
71
|
end
|
data/lib/cfoundry/v1/client.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
+
require File.expand_path("../../concerns/login_helpers", __FILE__)
|
2
|
+
|
1
3
|
module CFoundry::V1
|
2
4
|
# The primary API entrypoint. Wraps a BaseClient to provide nicer return
|
3
5
|
# values. Initialize with the target and, optionally, an auth token. These
|
4
6
|
# are the only two internal states.
|
5
7
|
class Client
|
6
|
-
include ClientMethods
|
8
|
+
include ClientMethods, CFoundry::LoginHelpers
|
7
9
|
|
8
10
|
attr_reader :base
|
9
11
|
|
@@ -14,6 +16,10 @@ module CFoundry::V1
|
|
14
16
|
@base = Base.new(target, token)
|
15
17
|
end
|
16
18
|
|
19
|
+
def version
|
20
|
+
1
|
21
|
+
end
|
22
|
+
|
17
23
|
# The current target URL of the client.
|
18
24
|
def target
|
19
25
|
@base.target
|
@@ -162,35 +168,6 @@ module CFoundry::V1
|
|
162
168
|
user(email)
|
163
169
|
end
|
164
170
|
|
165
|
-
# Login prompts
|
166
|
-
def login_prompts
|
167
|
-
if @base.uaa
|
168
|
-
@base.uaa.prompts
|
169
|
-
else
|
170
|
-
{ :username => ["text", "Email"],
|
171
|
-
:password => ["password", "Password"]
|
172
|
-
}
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
# Authenticate with the target. Sets the client token.
|
177
|
-
#
|
178
|
-
# Credentials is a hash, typically containing :username and :password
|
179
|
-
# keys.
|
180
|
-
#
|
181
|
-
# The values in the hash should mirror the prompts given by
|
182
|
-
# `login_prompts`.
|
183
|
-
def login(credentials)
|
184
|
-
@base.token =
|
185
|
-
if @base.uaa
|
186
|
-
@base.uaa.authorize(credentials)
|
187
|
-
else
|
188
|
-
@base.create_token(
|
189
|
-
{ :password => credentials[:password] },
|
190
|
-
credentials[:username])[:token]
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
171
|
# Clear client token. No requests are made for this.
|
195
172
|
def logout
|
196
173
|
@base.token = nil
|
data/lib/cfoundry/v1/model.rb
CHANGED
@@ -5,6 +5,8 @@ require "cfoundry/v1/model_magic"
|
|
5
5
|
|
6
6
|
module CFoundry::V1
|
7
7
|
class Model
|
8
|
+
@@objects = {}
|
9
|
+
|
8
10
|
extend ModelMagic
|
9
11
|
|
10
12
|
class << self
|
@@ -28,6 +30,15 @@ module CFoundry::V1
|
|
28
30
|
def plural_base_object_name
|
29
31
|
"#{base_object_name}s"
|
30
32
|
end
|
33
|
+
|
34
|
+
def objects
|
35
|
+
@@objects
|
36
|
+
end
|
37
|
+
|
38
|
+
def inherited(klass)
|
39
|
+
@@objects[klass.object_name] = klass
|
40
|
+
super
|
41
|
+
end
|
31
42
|
end
|
32
43
|
|
33
44
|
attr_accessor :guid, :changes
|
@@ -79,8 +90,6 @@ module CFoundry::V1
|
|
79
90
|
def update!
|
80
91
|
@client.base.send(:"update_#{base_object_name}", @guid, write_manifest)
|
81
92
|
|
82
|
-
invalidate!
|
83
|
-
|
84
93
|
true
|
85
94
|
end
|
86
95
|
|
@@ -108,6 +117,13 @@ module CFoundry::V1
|
|
108
117
|
read[name] = val if found
|
109
118
|
end
|
110
119
|
|
120
|
+
self.class.write_locations.each do |name, where|
|
121
|
+
next if self.class.read_only_attributes.include? name
|
122
|
+
|
123
|
+
found, val = find_path(manifest, where)
|
124
|
+
read[name] = val if found
|
125
|
+
end
|
126
|
+
|
111
127
|
read[guid_name] = @guid
|
112
128
|
|
113
129
|
read
|
@@ -129,10 +145,11 @@ module CFoundry::V1
|
|
129
145
|
end
|
130
146
|
|
131
147
|
def write_manifest(body = read_manifest, onto = {})
|
132
|
-
onto[guid_name] = @guid
|
148
|
+
onto[guid_name] = @guid if guid_name
|
133
149
|
|
134
|
-
self.class.write_locations.each do |
|
135
|
-
|
150
|
+
self.class.write_locations.each do |name, where|
|
151
|
+
next if self.class.read_only_attributes.include? name
|
152
|
+
put(body[name], onto, where) if body.key?(name)
|
136
153
|
end
|
137
154
|
|
138
155
|
onto
|
@@ -18,6 +18,18 @@ module CFoundry::V1
|
|
18
18
|
@write_locations ||= {}
|
19
19
|
end
|
20
20
|
|
21
|
+
def read_only_attributes
|
22
|
+
@read_only_attributes ||= []
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_client(&blk)
|
26
|
+
ClientMethods.module_eval(&blk)
|
27
|
+
end
|
28
|
+
|
29
|
+
def on_base_client(&blk)
|
30
|
+
BaseClientMethods.module_eval(&blk)
|
31
|
+
end
|
32
|
+
|
21
33
|
def define_client_methods(klass = self)
|
22
34
|
singular = klass.object_name
|
23
35
|
plural = klass.plural_object_name
|
@@ -25,13 +37,13 @@ module CFoundry::V1
|
|
25
37
|
base_singular = klass.base_object_name
|
26
38
|
base_plural = klass.plural_base_object_name
|
27
39
|
|
28
|
-
|
40
|
+
on_base_client do
|
29
41
|
define_method(base_singular) do |guid|
|
30
42
|
get(base_plural, guid, :accept => :json)
|
31
43
|
end
|
32
44
|
|
33
45
|
define_method(:"create_#{base_singular}") do |payload|
|
34
|
-
post(
|
46
|
+
post(base_plural, :content => :json, :accept => :json, :payload => payload)
|
35
47
|
end
|
36
48
|
|
37
49
|
define_method(:"delete_#{base_singular}") do |guid|
|
@@ -40,7 +52,7 @@ module CFoundry::V1
|
|
40
52
|
end
|
41
53
|
|
42
54
|
define_method(:"update_#{base_singular}") do |guid, payload|
|
43
|
-
put(
|
55
|
+
put(base_plural, guid, :content => :json, :payload => payload)
|
44
56
|
end
|
45
57
|
|
46
58
|
define_method(base_plural) do |*args|
|
@@ -48,7 +60,7 @@ module CFoundry::V1
|
|
48
60
|
end
|
49
61
|
end
|
50
62
|
|
51
|
-
|
63
|
+
on_client do
|
52
64
|
if klass.guid_name
|
53
65
|
define_method(:"#{singular}_by_#{klass.guid_name}") do |guid|
|
54
66
|
obj = send(singular, guid)
|
@@ -79,22 +91,20 @@ module CFoundry::V1
|
|
79
91
|
write_only = opts[:write_only]
|
80
92
|
has_default = opts.key?(:default)
|
81
93
|
|
82
|
-
read_locations[name] = Array(opts[:read] || opts[:at] || name)
|
83
|
-
write_locations[name] = Array(opts[:write] || opts[:at] || name)
|
94
|
+
read_locations[name] = Array(opts[:read] || opts[:at] || name) unless write_only
|
95
|
+
write_locations[name] = Array(opts[:write] || opts[:at] || name) unless read_only
|
96
|
+
|
97
|
+
read_only_attributes << name if read_only
|
84
98
|
|
85
99
|
self.guid_name = name if is_guid
|
86
100
|
|
87
|
-
|
88
|
-
|
89
|
-
return @guid if @guid && is_guid
|
101
|
+
define_method(name) do
|
102
|
+
return @guid if @guid && is_guid
|
90
103
|
|
91
|
-
|
92
|
-
|
93
|
-
end
|
104
|
+
read = read_manifest
|
105
|
+
read.key?(name) ? read[name] : default
|
94
106
|
end
|
95
107
|
|
96
|
-
return if read_only
|
97
|
-
|
98
108
|
define_method(:"#{name}=") do |val|
|
99
109
|
unless has_default && val == default
|
100
110
|
CFoundry::Validator.validate_type(val, type)
|
@@ -107,8 +117,13 @@ module CFoundry::V1
|
|
107
117
|
old = read_manifest[name]
|
108
118
|
@changes[name] = [old, val] if old != val
|
109
119
|
|
110
|
-
|
120
|
+
write_to = read_only ? self.class.read_locations : self.class.write_locations
|
121
|
+
|
122
|
+
put(val, @manifest, write_to[name])
|
111
123
|
end
|
124
|
+
|
125
|
+
private name if write_only
|
126
|
+
private :"#{name}=" if read_only
|
112
127
|
end
|
113
128
|
end
|
114
129
|
end
|
data/lib/cfoundry/v2/app.rb
CHANGED
@@ -24,12 +24,12 @@ module CFoundry::V2
|
|
24
24
|
to_one :framework
|
25
25
|
attribute :environment_json, :hash, :default => {}
|
26
26
|
attribute :memory, :integer, :default => 256
|
27
|
-
attribute :
|
28
|
-
attribute :file_descriptors, :integer, :default => 256
|
27
|
+
attribute :total_instances, :integer, :default => 1, :at => :instances
|
29
28
|
attribute :disk_quota, :integer, :default => 256
|
30
29
|
attribute :state, :string, :default => "STOPPED"
|
31
30
|
attribute :command, :string, :default => nil
|
32
31
|
attribute :console, :boolean, :default => false
|
32
|
+
attribute :buildpack, :string, :default => nil
|
33
33
|
to_many :service_bindings
|
34
34
|
to_many :routes
|
35
35
|
|
@@ -75,9 +75,6 @@ module CFoundry::V2
|
|
75
75
|
end
|
76
76
|
}
|
77
77
|
|
78
|
-
alias :total_instances :instances
|
79
|
-
alias :total_instances= :instances=
|
80
|
-
|
81
78
|
private :environment_json
|
82
79
|
|
83
80
|
def instances
|
data/lib/cfoundry/v2/base.rb
CHANGED
@@ -9,39 +9,8 @@ module CFoundry::V2
|
|
9
9
|
class Base < CFoundry::BaseClient
|
10
10
|
include BaseClientMethods
|
11
11
|
|
12
|
-
attr_accessor :target, :token, :proxy, :trace, :backtrace, :log
|
13
|
-
|
14
|
-
def initialize(
|
15
|
-
target = "https://api.cloudfoundry.com",
|
16
|
-
token = nil)
|
17
|
-
super
|
18
|
-
end
|
19
|
-
|
20
|
-
|
21
|
-
# The UAA used for this client.
|
22
|
-
#
|
23
|
-
# `false` if no UAA (legacy)
|
24
|
-
def uaa
|
25
|
-
return @uaa unless @uaa.nil?
|
26
|
-
|
27
|
-
endpoint = info[:authorization_endpoint]
|
28
|
-
return @uaa = false unless endpoint
|
29
|
-
|
30
|
-
@uaa = CFoundry::UAAClient.new(endpoint)
|
31
|
-
@uaa.trace = @trace
|
32
|
-
@uaa.token = @token
|
33
|
-
@uaa
|
34
|
-
end
|
35
|
-
|
36
|
-
|
37
|
-
# Cloud metadata
|
38
|
-
def info
|
39
|
-
get("info", :accept => :json)
|
40
|
-
end
|
41
|
-
|
42
12
|
def resource_match(fingerprints)
|
43
|
-
put(
|
44
|
-
:content => :json, :accept => :json)
|
13
|
+
put("v2", "resource_match", :content => :json, :accept => :json, :payload => fingerprints)
|
45
14
|
end
|
46
15
|
|
47
16
|
def upload_app(guid, zipfile, resources = [])
|
@@ -57,7 +26,7 @@ module CFoundry::V2
|
|
57
26
|
"application/zip")
|
58
27
|
}
|
59
28
|
|
60
|
-
put(
|
29
|
+
put("v2", "apps", guid, "bits", :payload => payload)
|
61
30
|
rescue EOFError
|
62
31
|
retry
|
63
32
|
end
|
@@ -68,27 +37,22 @@ module CFoundry::V2
|
|
68
37
|
alias :file :files
|
69
38
|
|
70
39
|
def stream_file(guid, instance, *path)
|
71
|
-
|
72
|
-
|
73
|
-
Net::HTTP::Get,
|
74
|
-
["v2", "apps", guid, "instances", instance, "files", *path],
|
75
|
-
:return_response => true)
|
40
|
+
path_and_options = path + [{:return_response => true, :follow_redirects => false}]
|
41
|
+
redirect = get("v2", "apps", guid, "instances", instance, "files", *path_and_options)
|
76
42
|
|
77
|
-
if
|
78
|
-
uri = URI.parse(
|
43
|
+
if location = redirect[:headers]["location"]
|
44
|
+
uri = URI.parse(location + "&tail")
|
79
45
|
|
80
46
|
Net::HTTP.start(uri.host, uri.port) do |http|
|
81
47
|
req = Net::HTTP::Get.new(uri.request_uri)
|
82
|
-
req["Authorization"] =
|
48
|
+
req["Authorization"] = token.auth_header if token
|
83
49
|
|
84
50
|
http.request(req) do |response|
|
85
|
-
response.read_body
|
86
|
-
yield chunk
|
87
|
-
end
|
51
|
+
response.read_body { |chunk| yield chunk }
|
88
52
|
end
|
89
53
|
end
|
90
54
|
else
|
91
|
-
yield redirect
|
55
|
+
yield redirect[:body]
|
92
56
|
end
|
93
57
|
end
|
94
58
|
|
@@ -104,43 +68,15 @@ module CFoundry::V2
|
|
104
68
|
get("v2", "apps", guid, "stats", :accept => :json)
|
105
69
|
end
|
106
70
|
|
107
|
-
|
108
71
|
def all_pages(paginated)
|
109
72
|
payload = paginated[:resources]
|
110
73
|
|
111
74
|
while next_page = paginated[:next_url]
|
112
|
-
paginated =
|
75
|
+
paginated = get(next_page, :accept => :json)
|
113
76
|
payload += paginated[:resources]
|
114
77
|
end
|
115
78
|
|
116
79
|
payload
|
117
80
|
end
|
118
|
-
|
119
|
-
private
|
120
|
-
|
121
|
-
def handle_response(response, accept, request)
|
122
|
-
# this is a copy paste of v1
|
123
|
-
return super if response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPRedirection)
|
124
|
-
|
125
|
-
info = parse_json(response.body)
|
126
|
-
return super unless info[:code]
|
127
|
-
|
128
|
-
cls = CFoundry::APIError.error_classes[info[:code]]
|
129
|
-
|
130
|
-
raise (cls || CFoundry::APIError).new(request, response, info[:description], info[:code])
|
131
|
-
rescue MultiJson::DecodeError
|
132
|
-
super
|
133
|
-
end
|
134
|
-
|
135
|
-
def log_line(io, data)
|
136
|
-
io.printf(
|
137
|
-
"[%s] %0.3fs %s %6s -> %d %s\n",
|
138
|
-
Time.now.strftime("%F %T"),
|
139
|
-
data[:time],
|
140
|
-
data[:response][:headers]["x-vcap-request-id"],
|
141
|
-
data[:request][:method].to_s.upcase,
|
142
|
-
data[:response][:code],
|
143
|
-
data[:request][:url])
|
144
|
-
end
|
145
81
|
end
|
146
82
|
end
|