cfoundry 0.2.2 → 0.3.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 +22 -2
- data/lib/cfoundry/baseclient.rb +206 -0
- data/lib/cfoundry/client.rb +14 -144
- data/lib/cfoundry/uaaclient.rb +103 -0
- data/lib/cfoundry/{app.rb → v1/app.rb} +75 -64
- data/lib/cfoundry/v1/base.rb +206 -0
- data/lib/cfoundry/v1/client.rb +205 -0
- data/lib/cfoundry/v1/framework.rb +16 -0
- data/lib/cfoundry/v1/runtime.rb +14 -0
- data/lib/cfoundry/v1/service.rb +19 -0
- data/lib/cfoundry/{service.rb → v1/service_instance.rb} +15 -15
- data/lib/cfoundry/{user.rb → v1/user.rb} +9 -9
- data/lib/cfoundry/v2/app.rb +119 -0
- data/lib/cfoundry/v2/base.rb +140 -0
- data/lib/cfoundry/v2/client.rb +184 -0
- data/lib/cfoundry/v2/domain.rb +8 -0
- data/lib/cfoundry/v2/framework.rb +18 -0
- data/lib/cfoundry/v2/model.rb +149 -0
- data/lib/cfoundry/v2/organization.rb +15 -0
- data/lib/cfoundry/v2/runtime.rb +9 -0
- data/lib/cfoundry/v2/service.rb +16 -0
- data/lib/cfoundry/v2/service_instance.rb +12 -0
- data/lib/cfoundry/v2/service_plan.rb +10 -0
- data/lib/cfoundry/v2/space.rb +13 -0
- data/lib/cfoundry/v2/user.rb +50 -0
- data/lib/cfoundry/version.rb +1 -1
- metadata +27 -9
- data/lib/cfoundry/restclient.rb +0 -260
data/Rakefile
CHANGED
@@ -1,11 +1,31 @@
|
|
1
|
-
require
|
2
|
-
|
1
|
+
require "rake"
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
4
|
+
require "cfoundry/version"
|
3
5
|
|
4
6
|
task :default => "spec"
|
5
7
|
|
6
8
|
desc "Run specs"
|
7
9
|
task "spec" => ["bundler:install", "test:spec"]
|
8
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
|
+
|
9
29
|
namespace "bundler" do
|
10
30
|
desc "Install gems"
|
11
31
|
task "install" do
|
@@ -0,0 +1,206 @@
|
|
1
|
+
require "restclient"
|
2
|
+
require "json"
|
3
|
+
|
4
|
+
module CFoundry
|
5
|
+
class BaseClient # :nodoc:
|
6
|
+
def initialize(target, token = nil)
|
7
|
+
@target = target
|
8
|
+
@token = token
|
9
|
+
end
|
10
|
+
|
11
|
+
def request_path(method, path, types = {}, options = {})
|
12
|
+
path = url(path) if path.is_a?(Array)
|
13
|
+
|
14
|
+
unless types.empty?
|
15
|
+
if params = types.delete(:params)
|
16
|
+
options[:params] = params
|
17
|
+
end
|
18
|
+
|
19
|
+
if types.size > 1
|
20
|
+
raise "request types must contain only one Content-Type => Accept"
|
21
|
+
end
|
22
|
+
|
23
|
+
options[:type] = types.keys.first
|
24
|
+
options[:accept] = types.values.first
|
25
|
+
end
|
26
|
+
|
27
|
+
request(method, path, options)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def parse_json(x)
|
33
|
+
JSON.parse(x, :symbolize_names => true)
|
34
|
+
end
|
35
|
+
|
36
|
+
def request(method, path, options = {})
|
37
|
+
accept = options.delete(:accept)
|
38
|
+
type = options.delete(:type)
|
39
|
+
payload = options.delete(:payload)
|
40
|
+
params = options.delete(:params)
|
41
|
+
|
42
|
+
headers = {}
|
43
|
+
headers["Authorization"] = @token if @token
|
44
|
+
headers["Proxy-User"] = @proxy if @proxy
|
45
|
+
|
46
|
+
if accept_type = mimetype(accept)
|
47
|
+
headers["Accept"] = accept_type
|
48
|
+
end
|
49
|
+
|
50
|
+
if content_type = mimetype(type)
|
51
|
+
headers["Content-Type"] = content_type
|
52
|
+
end
|
53
|
+
|
54
|
+
unless payload.is_a?(String)
|
55
|
+
case type
|
56
|
+
when :json
|
57
|
+
payload = payload.to_json
|
58
|
+
when :form
|
59
|
+
payload = encode_params(payload, false)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
headers["Content-Length"] = payload ? payload.size : 0
|
64
|
+
|
65
|
+
headers.merge!(options[:headers]) if options[:headers]
|
66
|
+
|
67
|
+
if params
|
68
|
+
path += "?" + encode_params(params)
|
69
|
+
end
|
70
|
+
|
71
|
+
req = options.dup
|
72
|
+
req[:method] = method
|
73
|
+
req[:url] = @target + path
|
74
|
+
req[:headers] = headers
|
75
|
+
req[:payload] = payload
|
76
|
+
|
77
|
+
json = accept == :json
|
78
|
+
|
79
|
+
RestClient::Request.execute(req) do |response, request|
|
80
|
+
if @trace
|
81
|
+
puts '>>>'
|
82
|
+
puts "PROXY: #{RestClient.proxy}" if RestClient.proxy
|
83
|
+
puts "REQUEST: #{req[:method]} #{req[:url]}"
|
84
|
+
puts "RESPONSE_HEADERS:"
|
85
|
+
response.headers.each do |key, value|
|
86
|
+
puts " #{key} : #{value}"
|
87
|
+
end
|
88
|
+
puts "REQUEST_HEADERS:"
|
89
|
+
request.headers.each do |key, value|
|
90
|
+
puts " #{key} : #{value}"
|
91
|
+
end
|
92
|
+
puts "REQUEST_BODY: #{req[:payload]}" if req[:payload]
|
93
|
+
puts "RESPONSE: [#{response.code}]"
|
94
|
+
begin
|
95
|
+
puts JSON.pretty_generate(JSON.parse(response.body))
|
96
|
+
rescue
|
97
|
+
puts "#{response.body}"
|
98
|
+
end
|
99
|
+
puts '<<<'
|
100
|
+
end
|
101
|
+
|
102
|
+
handle_response(response, accept)
|
103
|
+
end
|
104
|
+
rescue SocketError, Errno::ECONNREFUSED => e
|
105
|
+
raise TargetRefused, e.message
|
106
|
+
end
|
107
|
+
|
108
|
+
def mimetype(type)
|
109
|
+
case type
|
110
|
+
when String
|
111
|
+
type
|
112
|
+
when :json
|
113
|
+
"application/json"
|
114
|
+
when :form
|
115
|
+
"application/x-www-form-urlencoded"
|
116
|
+
when nil
|
117
|
+
nil
|
118
|
+
# return request headers (not really Accept)
|
119
|
+
when :headers
|
120
|
+
nil
|
121
|
+
else
|
122
|
+
raise "unknown mimetype #{type.inspect}"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def encode_params(hash, escape = true)
|
127
|
+
hash.keys.map do |k|
|
128
|
+
v = hash[k]
|
129
|
+
|
130
|
+
value =
|
131
|
+
if v.is_a?(Hash)
|
132
|
+
v.to_json
|
133
|
+
elsif escape
|
134
|
+
URI.escape(v.to_s, /[^#{URI::PATTERN::UNRESERVED}]/)
|
135
|
+
else
|
136
|
+
v
|
137
|
+
end
|
138
|
+
|
139
|
+
"#{k}=#{value}"
|
140
|
+
end.join("&")
|
141
|
+
end
|
142
|
+
|
143
|
+
def request_with_types(method, path, options = {})
|
144
|
+
if path.last.is_a?(Hash)
|
145
|
+
types = path.pop
|
146
|
+
end
|
147
|
+
|
148
|
+
request_path(method, url(path), types || {}, options)
|
149
|
+
end
|
150
|
+
|
151
|
+
def get(*path)
|
152
|
+
request_with_types(:get, path)
|
153
|
+
end
|
154
|
+
|
155
|
+
def delete(*path)
|
156
|
+
request_with_types(:delete, path)
|
157
|
+
end
|
158
|
+
|
159
|
+
def post(payload, *path)
|
160
|
+
request_with_types(:post, path, :payload => payload)
|
161
|
+
end
|
162
|
+
|
163
|
+
def put(payload, *path)
|
164
|
+
request_with_types(:put, path, :payload => payload)
|
165
|
+
end
|
166
|
+
|
167
|
+
def url(segments)
|
168
|
+
"/#{safe_path(segments)}"
|
169
|
+
end
|
170
|
+
|
171
|
+
def safe_path(*segments)
|
172
|
+
segments.flatten.collect { |x|
|
173
|
+
URI.encode x.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")
|
174
|
+
}.join("/")
|
175
|
+
end
|
176
|
+
|
177
|
+
private
|
178
|
+
|
179
|
+
def handle_response(response, accept)
|
180
|
+
json = accept == :json
|
181
|
+
|
182
|
+
case response.code
|
183
|
+
when 200, 201, 204, 302
|
184
|
+
if accept == :headers
|
185
|
+
return response.headers
|
186
|
+
end
|
187
|
+
|
188
|
+
if json
|
189
|
+
if response.code == 204
|
190
|
+
raise "Expected JSON response, got 204 No Content"
|
191
|
+
end
|
192
|
+
|
193
|
+
parse_json(response)
|
194
|
+
else
|
195
|
+
response
|
196
|
+
end
|
197
|
+
|
198
|
+
when 404
|
199
|
+
raise CFoundry::NotFound
|
200
|
+
|
201
|
+
else
|
202
|
+
raise CFoundry::BadResponse.new(response.code, response)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
data/lib/cfoundry/client.rb
CHANGED
@@ -1,155 +1,25 @@
|
|
1
|
-
require "cfoundry/
|
2
|
-
require "cfoundry/app"
|
3
|
-
require "cfoundry/service"
|
4
|
-
require "cfoundry/user"
|
1
|
+
require "cfoundry/baseclient"
|
5
2
|
|
3
|
+
require "cfoundry/v1/client"
|
4
|
+
require "cfoundry/v2/client"
|
6
5
|
|
7
6
|
module CFoundry
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
class Client
|
12
|
-
# Internal RESTClient instance. Normally won't be touching this.
|
13
|
-
attr_reader :rest
|
7
|
+
class Client < BaseClient
|
8
|
+
def self.new(*args)
|
9
|
+
target, _ = args
|
14
10
|
|
15
|
-
|
16
|
-
#
|
17
|
-
# A token may also be provided to skip the login step.
|
18
|
-
def initialize(target = "http://api.cloudfoundry.com", token = nil)
|
19
|
-
@rest = RESTClient.new(target, token)
|
20
|
-
end
|
21
|
-
|
22
|
-
# The current target URL of the client.
|
23
|
-
def target
|
24
|
-
@rest.target
|
25
|
-
end
|
26
|
-
|
27
|
-
# Current proxy user. Usually nil.
|
28
|
-
def proxy
|
29
|
-
@rest.proxy
|
30
|
-
end
|
31
|
-
|
32
|
-
# Set the proxy user for the client. Must be authorized as an
|
33
|
-
# administrator for this to have any effect.
|
34
|
-
def proxy=(email)
|
35
|
-
@rest.proxy = email
|
36
|
-
end
|
37
|
-
|
38
|
-
# Is the client tracing API requests?
|
39
|
-
def trace
|
40
|
-
@rest.trace
|
41
|
-
end
|
42
|
-
|
43
|
-
# Set the tracing flag; if true, API requests and responses will be
|
44
|
-
# printed out.
|
45
|
-
def trace=(bool)
|
46
|
-
@rest.trace = bool
|
47
|
-
end
|
48
|
-
|
49
|
-
|
50
|
-
# Retrieve target metadata.
|
51
|
-
def info
|
52
|
-
@rest.info
|
53
|
-
end
|
54
|
-
|
55
|
-
# Retrieve available services. Returned as a Hash from vendor => metadata.
|
56
|
-
def system_services
|
57
|
-
services = {}
|
58
|
-
|
59
|
-
@rest.system_services.each do |type, vendors|
|
60
|
-
vendors.each do |vendor, versions|
|
61
|
-
services[vendor] =
|
62
|
-
{ :type => type,
|
63
|
-
:versions => versions.keys,
|
64
|
-
:description => versions.values[0]["description"],
|
65
|
-
:vendor => vendor
|
66
|
-
}
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
services
|
71
|
-
end
|
72
|
-
|
73
|
-
# Retrieve available runtimes.
|
74
|
-
def system_runtimes
|
75
|
-
@rest.system_runtimes
|
76
|
-
end
|
11
|
+
base = super(target)
|
77
12
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
CFoundry::
|
83
|
-
json["email"],
|
84
|
-
self,
|
85
|
-
{ "email" => json["email"],
|
86
|
-
"admin" => json["admin"] })
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
# Construct a User object. The return value is lazy, and no requests are
|
91
|
-
# made from this alone.
|
92
|
-
#
|
93
|
-
# This should be used for both user creation (after calling User#create!)
|
94
|
-
# and retrieval.
|
95
|
-
def user(email)
|
96
|
-
CFoundry::User.new(email, self)
|
97
|
-
end
|
98
|
-
|
99
|
-
# Create a user on the target and return a User object representing them.
|
100
|
-
def register(email, password)
|
101
|
-
@rest.create_user(:email => email, :password => password)
|
102
|
-
user(email)
|
103
|
-
end
|
104
|
-
|
105
|
-
# Authenticate with the target. Sets the client token.
|
106
|
-
def login(email, password)
|
107
|
-
@rest.token =
|
108
|
-
@rest.create_token({ :password => password }, email)["token"]
|
109
|
-
end
|
110
|
-
|
111
|
-
# Clear client token. No requests are made for this.
|
112
|
-
def logout
|
113
|
-
@rest.token = nil
|
114
|
-
end
|
115
|
-
|
116
|
-
# Is an authentication token set on the client?
|
117
|
-
def logged_in?
|
118
|
-
!!@rest.token
|
119
|
-
end
|
120
|
-
|
121
|
-
|
122
|
-
# Retreive all of the current user's applications.
|
123
|
-
def apps
|
124
|
-
@rest.apps.collect do |json|
|
125
|
-
CFoundry::App.new(json["name"], self, json)
|
13
|
+
case base.info[:version]
|
14
|
+
when 2
|
15
|
+
CFoundry::V2::Client.new(*args)
|
16
|
+
else
|
17
|
+
CFoundry::V1::Client.new(*args)
|
126
18
|
end
|
127
19
|
end
|
128
20
|
|
129
|
-
|
130
|
-
|
131
|
-
#
|
132
|
-
# This should be used for both app creation (after calling App#create!)
|
133
|
-
# and retrieval.
|
134
|
-
def app(name)
|
135
|
-
CFoundry::App.new(name, self)
|
136
|
-
end
|
137
|
-
|
138
|
-
|
139
|
-
# Retrieve all of the current user's services.
|
140
|
-
def services
|
141
|
-
@rest.services.collect do |json|
|
142
|
-
CFoundry::Service.new(json["name"], self, json)
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
# Construct a Service object. The return value is lazy, and no requests are
|
147
|
-
# made from this method alone.
|
148
|
-
#
|
149
|
-
# This should be used for both service creation (after calling
|
150
|
-
# Service#create!) and retrieval.
|
151
|
-
def service(name)
|
152
|
-
CFoundry::Service.new(name, self)
|
21
|
+
def info
|
22
|
+
get("info", nil => :json)
|
153
23
|
end
|
154
24
|
end
|
155
25
|
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require "cfoundry/baseclient"
|
2
|
+
|
3
|
+
module CFoundry
|
4
|
+
class UAAClient < BaseClient
|
5
|
+
attr_accessor :target, :client_id, :scope, :redirect_uri, :token, :trace
|
6
|
+
|
7
|
+
def initialize(
|
8
|
+
target = "https://uaa.cloudfoundry.com",
|
9
|
+
client_id = "vmc")
|
10
|
+
@target = target
|
11
|
+
@client_id = client_id
|
12
|
+
@scope = ["read"]
|
13
|
+
@redirect_uri = "http://uaa.cloudfoundry.com/redirect/vmc"
|
14
|
+
end
|
15
|
+
|
16
|
+
def prompts
|
17
|
+
get("login", nil => :json)[:prompts]
|
18
|
+
end
|
19
|
+
|
20
|
+
def authorize(credentials)
|
21
|
+
query = {
|
22
|
+
:client_id => @client_id,
|
23
|
+
:scope => Array(@scope).join(" "),
|
24
|
+
:response_type => "token",
|
25
|
+
:redirect_uri => @redirect_uri
|
26
|
+
}
|
27
|
+
|
28
|
+
extract_token(
|
29
|
+
post(
|
30
|
+
{ :credentials => credentials },
|
31
|
+
"oauth", "authorize",
|
32
|
+
:form => :headers,
|
33
|
+
:params => query)[:location])
|
34
|
+
end
|
35
|
+
|
36
|
+
def users
|
37
|
+
get("Users", nil => :json)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def handle_response(response, accept)
|
43
|
+
json = accept == :json
|
44
|
+
|
45
|
+
case response.code
|
46
|
+
when 200, 204, 302
|
47
|
+
if accept == :headers
|
48
|
+
return response.headers
|
49
|
+
end
|
50
|
+
|
51
|
+
if json
|
52
|
+
if response.code == 204
|
53
|
+
raise "Expected JSON response, got 204 No Content"
|
54
|
+
end
|
55
|
+
|
56
|
+
parse_json(response)
|
57
|
+
else
|
58
|
+
response
|
59
|
+
end
|
60
|
+
|
61
|
+
when 400, 403
|
62
|
+
info = parse_json(response)
|
63
|
+
raise Denied.new(403, info[:error_description])
|
64
|
+
|
65
|
+
when 401
|
66
|
+
info = parse_json(response)
|
67
|
+
raise Denied.new(401, info[:error_description])
|
68
|
+
|
69
|
+
when 404
|
70
|
+
raise NotFound
|
71
|
+
|
72
|
+
when 409
|
73
|
+
info = parse_json(response)
|
74
|
+
raise CFoundry::Denied.new(409, info[:message])
|
75
|
+
|
76
|
+
when 411, 500, 504
|
77
|
+
begin
|
78
|
+
raise_error(parse_json(response))
|
79
|
+
rescue JSON::ParserError
|
80
|
+
raise BadResponse.new(response.code, response)
|
81
|
+
end
|
82
|
+
|
83
|
+
else
|
84
|
+
raise BadResponse.new(response.code, response)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def extract_token(url)
|
89
|
+
_, params = url.split('#')
|
90
|
+
return unless params
|
91
|
+
|
92
|
+
values = {}
|
93
|
+
params.split("&").each do |pair|
|
94
|
+
key, val = pair.split("=")
|
95
|
+
values[key] = val
|
96
|
+
end
|
97
|
+
|
98
|
+
return unless values["access_token"] && values["token_type"]
|
99
|
+
|
100
|
+
"#{values["token_type"]} #{values["access_token"]}"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|