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