cfoundry-IronFoundry 0.3.34
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/LICENSE +746 -0
- data/Rakefile +40 -0
- data/lib/cfoundry.rb +2 -0
- data/lib/cfoundry/baseclient.rb +235 -0
- data/lib/cfoundry/chatty_hash.rb +34 -0
- data/lib/cfoundry/client.rb +25 -0
- data/lib/cfoundry/errors.rb +106 -0
- data/lib/cfoundry/uaaclient.rb +111 -0
- data/lib/cfoundry/upload_helpers.rb +100 -0
- data/lib/cfoundry/v1/app.rb +562 -0
- data/lib/cfoundry/v1/base.rb +209 -0
- data/lib/cfoundry/v1/client.rb +232 -0
- data/lib/cfoundry/v1/framework.rb +21 -0
- data/lib/cfoundry/v1/runtime.rb +20 -0
- data/lib/cfoundry/v1/service.rb +25 -0
- data/lib/cfoundry/v1/service_instance.rb +112 -0
- data/lib/cfoundry/v1/user.rb +89 -0
- data/lib/cfoundry/v2/app.rb +328 -0
- data/lib/cfoundry/v2/base.rb +175 -0
- data/lib/cfoundry/v2/client.rb +198 -0
- data/lib/cfoundry/v2/domain.rb +8 -0
- data/lib/cfoundry/v2/framework.rb +12 -0
- data/lib/cfoundry/v2/model.rb +268 -0
- data/lib/cfoundry/v2/organization.rb +13 -0
- data/lib/cfoundry/v2/route.rb +9 -0
- data/lib/cfoundry/v2/runtime.rb +9 -0
- data/lib/cfoundry/v2/service.rb +17 -0
- data/lib/cfoundry/v2/service_auth_token.rb +9 -0
- data/lib/cfoundry/v2/service_binding.rb +8 -0
- data/lib/cfoundry/v2/service_instance.rb +10 -0
- data/lib/cfoundry/v2/service_plan.rb +10 -0
- data/lib/cfoundry/v2/space.rb +14 -0
- data/lib/cfoundry/v2/user.rb +58 -0
- data/lib/cfoundry/version.rb +4 -0
- data/lib/cfoundry/zip.rb +56 -0
- data/spec/Rakefile +14 -0
- data/spec/client_spec.rb +206 -0
- data/spec/helpers.rb +29 -0
- metadata +169 -0
data/Rakefile
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require "rake"
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
4
|
+
require "cfoundry/version"
|
5
|
+
|
6
|
+
task :default => "spec"
|
7
|
+
|
8
|
+
desc "Run specs"
|
9
|
+
task "spec" => ["bundler:install", "test:spec"]
|
10
|
+
|
11
|
+
task :build do
|
12
|
+
sh "gem build cfoundry.gemspec"
|
13
|
+
end
|
14
|
+
|
15
|
+
task :install => :build do
|
16
|
+
sh "gem install --local cfoundry-#{CFoundry::VERSION}"
|
17
|
+
end
|
18
|
+
|
19
|
+
task :uninstall do
|
20
|
+
sh "gem uninstall cfoundry"
|
21
|
+
end
|
22
|
+
|
23
|
+
task :reinstall => [:uninstall, :install]
|
24
|
+
|
25
|
+
task :release => :build do
|
26
|
+
sh "gem push cfoundry-#{CFoundry::VERSION}.gem"
|
27
|
+
end
|
28
|
+
|
29
|
+
namespace "bundler" do
|
30
|
+
desc "Install gems"
|
31
|
+
task "install" do
|
32
|
+
sh("bundle install")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
namespace "test" do
|
37
|
+
task "spec" do |t|
|
38
|
+
sh("cd spec && bundle exec rake spec")
|
39
|
+
end
|
40
|
+
end
|
data/lib/cfoundry.rb
ADDED
@@ -0,0 +1,235 @@
|
|
1
|
+
require "restclient"
|
2
|
+
require "multi_json"
|
3
|
+
|
4
|
+
module CFoundry
|
5
|
+
class BaseClient # :nodoc:
|
6
|
+
attr_accessor :trace, :no_backtrace
|
7
|
+
|
8
|
+
def initialize(target, token = nil)
|
9
|
+
@target = target
|
10
|
+
@token = token
|
11
|
+
@trace = false
|
12
|
+
@no_backtrace = false
|
13
|
+
end
|
14
|
+
|
15
|
+
def request_path(method, path, types = {}, options = {})
|
16
|
+
path = url(path) if path.is_a?(Array)
|
17
|
+
|
18
|
+
unless types.empty?
|
19
|
+
if params = types.delete(:params)
|
20
|
+
options[:params] = params
|
21
|
+
end
|
22
|
+
|
23
|
+
if types.size > 1
|
24
|
+
raise "request types must contain only one Content-Type => Accept"
|
25
|
+
end
|
26
|
+
|
27
|
+
options[:type] = types.keys.first
|
28
|
+
options[:accept] = types.values.first
|
29
|
+
end
|
30
|
+
|
31
|
+
request(method, path, options)
|
32
|
+
end
|
33
|
+
|
34
|
+
# grab the metadata from a token that looks like:
|
35
|
+
#
|
36
|
+
# bearer (base64 ...)
|
37
|
+
def token_data
|
38
|
+
tok = Base64.decode64(@token.sub(/^bearer\s+/, ""))
|
39
|
+
tok.sub!(/\{.+?\}/, "") # clear algo
|
40
|
+
MultiJson.load(tok[/\{.+?\}/], :symbolize_keys => true)
|
41
|
+
|
42
|
+
# normally i don't catch'em all, but can't expect all tokens to be the
|
43
|
+
# proper format, so just silently fail as this is not critical
|
44
|
+
rescue
|
45
|
+
{}
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def parse_json(x)
|
51
|
+
MultiJson.load(x, :symbolize_keys => true)
|
52
|
+
end
|
53
|
+
|
54
|
+
def request(method, path, options = {})
|
55
|
+
accept = options.delete(:accept)
|
56
|
+
type = options.delete(:type)
|
57
|
+
payload = options.delete(:payload)
|
58
|
+
params = options.delete(:params)
|
59
|
+
|
60
|
+
headers = {}
|
61
|
+
headers["Authorization"] = @token if @token
|
62
|
+
headers["Proxy-User"] = @proxy if @proxy
|
63
|
+
|
64
|
+
if accept_type = mimetype(accept)
|
65
|
+
headers["Accept"] = accept_type
|
66
|
+
end
|
67
|
+
|
68
|
+
if content_type = mimetype(type)
|
69
|
+
headers["Content-Type"] = content_type
|
70
|
+
end
|
71
|
+
|
72
|
+
unless payload.is_a?(String)
|
73
|
+
case type
|
74
|
+
when :json
|
75
|
+
payload = MultiJson.dump(payload)
|
76
|
+
when :form
|
77
|
+
payload = encode_params(payload)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
headers["Content-Length"] = payload ? payload.size : 0
|
82
|
+
|
83
|
+
headers.merge!(options[:headers]) if options[:headers]
|
84
|
+
|
85
|
+
if params
|
86
|
+
uri = URI.parse(path)
|
87
|
+
path += (uri.query ? "&" : "?") + encode_params(params)
|
88
|
+
end
|
89
|
+
|
90
|
+
req = options.dup
|
91
|
+
req[:method] = method
|
92
|
+
req[:url] = @target + path
|
93
|
+
req[:headers] = headers
|
94
|
+
req[:payload] = payload
|
95
|
+
|
96
|
+
json = accept == :json
|
97
|
+
|
98
|
+
RestClient::Request.execute(req) do |response, request|
|
99
|
+
print_trace(req, request, response, caller) if @trace
|
100
|
+
handle_response(response, accept)
|
101
|
+
end
|
102
|
+
rescue SocketError, Errno::ECONNREFUSED => e
|
103
|
+
raise TargetRefused, e.message
|
104
|
+
end
|
105
|
+
|
106
|
+
def mimetype(type)
|
107
|
+
case type
|
108
|
+
when String
|
109
|
+
type
|
110
|
+
when :json
|
111
|
+
"application/json"
|
112
|
+
when :form
|
113
|
+
"application/x-www-form-urlencoded"
|
114
|
+
when nil
|
115
|
+
nil
|
116
|
+
# return request headers (not really Accept)
|
117
|
+
when :headers
|
118
|
+
nil
|
119
|
+
else
|
120
|
+
raise "unknown mimetype #{type.inspect}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def encode_params(hash, escape = true)
|
125
|
+
hash.keys.map do |k|
|
126
|
+
v = hash[k]
|
127
|
+
v = MultiJson.dump(v) if v.is_a?(Hash)
|
128
|
+
v = URI.escape(v.to_s, /[^#{URI::PATTERN::UNRESERVED}]/) if escape
|
129
|
+
"#{k}=#{v}"
|
130
|
+
end.join("&")
|
131
|
+
end
|
132
|
+
|
133
|
+
def request_with_types(method, path, options = {})
|
134
|
+
if path.last.is_a?(Hash)
|
135
|
+
types = path.pop
|
136
|
+
end
|
137
|
+
|
138
|
+
request_path(method, url(path), types || {}, options)
|
139
|
+
end
|
140
|
+
|
141
|
+
def get(*path)
|
142
|
+
request_with_types(:get, path)
|
143
|
+
end
|
144
|
+
|
145
|
+
def delete(*path)
|
146
|
+
request_with_types(:delete, path)
|
147
|
+
end
|
148
|
+
|
149
|
+
def post(payload, *path)
|
150
|
+
request_with_types(:post, path, :payload => payload)
|
151
|
+
end
|
152
|
+
|
153
|
+
def put(payload, *path)
|
154
|
+
request_with_types(:put, path, :payload => payload)
|
155
|
+
end
|
156
|
+
|
157
|
+
def url(segments)
|
158
|
+
"/#{safe_path(segments)}"
|
159
|
+
end
|
160
|
+
|
161
|
+
def safe_path(*segments)
|
162
|
+
segments.flatten.collect { |x|
|
163
|
+
URI.encode x.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")
|
164
|
+
}.join("/")
|
165
|
+
end
|
166
|
+
|
167
|
+
def print_trace(req, request, response, locs)
|
168
|
+
$stderr.puts ">>>"
|
169
|
+
$stderr.puts "PROXY: #{RestClient.proxy}" if RestClient.proxy
|
170
|
+
$stderr.puts "REQUEST: #{req[:method]} #{req[:url]}"
|
171
|
+
$stderr.puts "RESPONSE_HEADERS:"
|
172
|
+
response.headers.each do |key, value|
|
173
|
+
$stderr.puts " #{key} : #{value}"
|
174
|
+
end
|
175
|
+
$stderr.puts "REQUEST_HEADERS:"
|
176
|
+
request.headers.each do |key, value|
|
177
|
+
$stderr.puts " #{key} : #{value}"
|
178
|
+
end
|
179
|
+
$stderr.puts "REQUEST_BODY: #{req[:payload]}" if req[:payload]
|
180
|
+
$stderr.puts "RESPONSE: [#{response.code}]"
|
181
|
+
begin
|
182
|
+
parsed_body = MultiJson.load(response.body)
|
183
|
+
$stderr.puts MultiJson.dump(parsed_body, :pretty => true)
|
184
|
+
rescue
|
185
|
+
$stderr.puts "#{response.body}"
|
186
|
+
end
|
187
|
+
$stderr.puts "<<<"
|
188
|
+
|
189
|
+
return if @no_backtrace
|
190
|
+
|
191
|
+
interesting_locs = locs.drop_while { |loc|
|
192
|
+
loc =~ /\/(cfoundry\/|restclient\/|net\/http)/
|
193
|
+
}
|
194
|
+
|
195
|
+
$stderr.puts "--- backtrace:"
|
196
|
+
|
197
|
+
$stderr.puts "... (boring)" unless locs == interesting_locs
|
198
|
+
|
199
|
+
trimmed_locs = interesting_locs[0..5]
|
200
|
+
|
201
|
+
trimmed_locs.each do |loc|
|
202
|
+
$stderr.puts "=== #{loc}"
|
203
|
+
end
|
204
|
+
|
205
|
+
$stderr.puts "... (trimmed)" unless trimmed_locs == interesting_locs
|
206
|
+
end
|
207
|
+
|
208
|
+
def handle_response(response, accept)
|
209
|
+
json = accept == :json
|
210
|
+
|
211
|
+
case response.code
|
212
|
+
when 200, 201, 204, 302
|
213
|
+
if accept == :headers
|
214
|
+
return response.headers
|
215
|
+
end
|
216
|
+
|
217
|
+
if json
|
218
|
+
if response.code == 204
|
219
|
+
raise "Expected JSON response, got 204 No Content"
|
220
|
+
end
|
221
|
+
|
222
|
+
parse_json(response)
|
223
|
+
else
|
224
|
+
response
|
225
|
+
end
|
226
|
+
|
227
|
+
when 404
|
228
|
+
raise CFoundry::NotFound
|
229
|
+
|
230
|
+
else
|
231
|
+
raise CFoundry::BadResponse.new(response.code, response)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module CFoundry
|
2
|
+
class ChattyHash
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
def initialize(callback, hash = {})
|
6
|
+
@callback = callback
|
7
|
+
@hash = hash
|
8
|
+
end
|
9
|
+
|
10
|
+
def [](name)
|
11
|
+
@hash[name]
|
12
|
+
end
|
13
|
+
|
14
|
+
def []=(name, value)
|
15
|
+
@hash[name] = value
|
16
|
+
@callback.call(self)
|
17
|
+
value
|
18
|
+
end
|
19
|
+
|
20
|
+
def each(&blk)
|
21
|
+
@hash.each(&blk)
|
22
|
+
end
|
23
|
+
|
24
|
+
def delete(key)
|
25
|
+
value = @hash.delete(key)
|
26
|
+
@callback.call(self)
|
27
|
+
value
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_json(*args)
|
31
|
+
@hash.to_json(*args)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "cfoundry/baseclient"
|
2
|
+
|
3
|
+
require "cfoundry/v1/client"
|
4
|
+
require "cfoundry/v2/client"
|
5
|
+
|
6
|
+
module CFoundry
|
7
|
+
class Client < BaseClient
|
8
|
+
def self.new(*args)
|
9
|
+
target, _ = args
|
10
|
+
|
11
|
+
base = super(target)
|
12
|
+
|
13
|
+
case base.info[:version]
|
14
|
+
when 2
|
15
|
+
CFoundry::V2::Client.new(*args)
|
16
|
+
else
|
17
|
+
CFoundry::V1::Client.new(*args)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def info
|
22
|
+
get("info", nil => :json)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module CFoundry
|
2
|
+
# Exception representing errors returned by the API.
|
3
|
+
class APIError < RuntimeError
|
4
|
+
class << self
|
5
|
+
# Generic error code for the exception.
|
6
|
+
attr_reader :error_code
|
7
|
+
|
8
|
+
# Generic description for the exception.
|
9
|
+
attr_reader :description
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def setup(code, description = nil)
|
14
|
+
@error_code = code
|
15
|
+
@description = description
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Create an APIError with a given error code and description.
|
20
|
+
def initialize(error_code = nil, description = nil)
|
21
|
+
@error_code = error_code
|
22
|
+
@description = description
|
23
|
+
end
|
24
|
+
|
25
|
+
# A number representing the error.
|
26
|
+
def error_code
|
27
|
+
@error_code || self.class.error_code
|
28
|
+
end
|
29
|
+
|
30
|
+
# A description of the error.
|
31
|
+
def description
|
32
|
+
@description || self.class.description
|
33
|
+
end
|
34
|
+
|
35
|
+
# Exception message.
|
36
|
+
def to_s
|
37
|
+
if error_code
|
38
|
+
"#{error_code}: #{description}"
|
39
|
+
else
|
40
|
+
description
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Generic exception thrown when accessing something that doesn't exist (e.g.
|
46
|
+
# getting info of unknown application).
|
47
|
+
class NotFound < APIError
|
48
|
+
setup(404, "entity not found or inaccessible")
|
49
|
+
end
|
50
|
+
|
51
|
+
# Lower-level exception for when we cannot connect to the target.
|
52
|
+
class TargetRefused < APIError
|
53
|
+
@description = "target refused connection"
|
54
|
+
|
55
|
+
# Error message.
|
56
|
+
attr_reader :message
|
57
|
+
|
58
|
+
# Message varies as this represents various network errors.
|
59
|
+
def initialize(message)
|
60
|
+
@message = message
|
61
|
+
end
|
62
|
+
|
63
|
+
# Exception message.
|
64
|
+
def to_s
|
65
|
+
"#{description} (#{@message})"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Exception raised when an application payload fails to upload.
|
70
|
+
class UploadFailed < APIError
|
71
|
+
setup(402)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Exception raised when access is denied to something, either because the
|
75
|
+
# user is not logged in or is not an administrator.
|
76
|
+
class Denied < APIError
|
77
|
+
# Specific error code.
|
78
|
+
attr_reader :error_code
|
79
|
+
|
80
|
+
# Specific description.
|
81
|
+
attr_reader :description
|
82
|
+
|
83
|
+
# Initialize, with a default error code and message.
|
84
|
+
def initialize(
|
85
|
+
error_code = 200,
|
86
|
+
description = "Operation not permitted")
|
87
|
+
@error_code = error_code
|
88
|
+
@description = description
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Exception raised when the response is unexpected; usually from a server
|
93
|
+
# error.
|
94
|
+
class BadResponse < StandardError
|
95
|
+
# Initialize, with the HTTP response code and body.
|
96
|
+
def initialize(code, body = nil)
|
97
|
+
@code = code
|
98
|
+
@body = body
|
99
|
+
end
|
100
|
+
|
101
|
+
# Exception message.
|
102
|
+
def to_s
|
103
|
+
"target failed to handle our request due to an internal error (#{@code})"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|