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
@@ -0,0 +1,140 @@
|
|
1
|
+
require "json"
|
2
|
+
require "base64"
|
3
|
+
|
4
|
+
require "cfoundry/baseclient"
|
5
|
+
require "cfoundry/uaaclient"
|
6
|
+
|
7
|
+
require "cfoundry/errors"
|
8
|
+
|
9
|
+
module CFoundry::V2
|
10
|
+
class Base < CFoundry::BaseClient
|
11
|
+
attr_accessor :target, :token, :proxy, :trace
|
12
|
+
|
13
|
+
def initialize(
|
14
|
+
target = "https://api.cloudfoundry.com",
|
15
|
+
token = nil)
|
16
|
+
@target = target
|
17
|
+
@token = token
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
# invalidate token data when changing token
|
22
|
+
def token=(t)
|
23
|
+
@token = t
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
# The UAA used for this client.
|
28
|
+
#
|
29
|
+
# `false` if no UAA (legacy)
|
30
|
+
def uaa
|
31
|
+
return @uaa unless @uaa.nil?
|
32
|
+
|
33
|
+
endpoint = info[:authorization_endpoint]
|
34
|
+
return @uaa = false unless endpoint
|
35
|
+
|
36
|
+
@uaa = CFoundry::UAAClient.new(endpoint)
|
37
|
+
@uaa.trace = @trace
|
38
|
+
@uaa.token = @token
|
39
|
+
@uaa
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
# Cloud metadata
|
44
|
+
def info
|
45
|
+
get("info", nil => :json)
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
[:app, :organization, :app_space, :service, :service_instance,
|
50
|
+
:user, :runtime, :framework].each do |obj|
|
51
|
+
plural = "#{obj}s"
|
52
|
+
|
53
|
+
define_method(obj) do |guid, *args|
|
54
|
+
depth, _ = args
|
55
|
+
depth ||= 1
|
56
|
+
|
57
|
+
params = { :"inline-relations-depth" => depth }
|
58
|
+
|
59
|
+
get("v2", plural, guid, nil => :json, :params => params)
|
60
|
+
end
|
61
|
+
|
62
|
+
define_method(:"create_#{obj}") do |payload|
|
63
|
+
post(payload, "v2", plural, :json => :json)
|
64
|
+
end
|
65
|
+
|
66
|
+
define_method(:"delete_#{obj}") do |guid|
|
67
|
+
delete("v2", plural, guid, nil => nil)
|
68
|
+
true
|
69
|
+
end
|
70
|
+
|
71
|
+
define_method(:"update_#{obj}") do |guid, payload|
|
72
|
+
put(payload, "v2", plural, guid, :json => :json)
|
73
|
+
end
|
74
|
+
|
75
|
+
define_method(plural) do |*args|
|
76
|
+
depth, query = args
|
77
|
+
depth ||= 1
|
78
|
+
|
79
|
+
params = { :"inline-relations-depth" => depth }
|
80
|
+
|
81
|
+
if query
|
82
|
+
params[:q] = "#{query.keys.first}:#{query.values.first}"
|
83
|
+
end
|
84
|
+
|
85
|
+
get("v2", plural, nil => :json, :params => params)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
alias :spaces :app_spaces
|
90
|
+
alias :space :app_space
|
91
|
+
alias :update_space :update_app_space
|
92
|
+
alias :delete_space :delete_app_space
|
93
|
+
alias :create_space :create_app_space
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def handle_response(response, accept)
|
98
|
+
json = accept == :json
|
99
|
+
|
100
|
+
case response.code
|
101
|
+
when 200, 201, 204, 302
|
102
|
+
if accept == :headers
|
103
|
+
return response.headers
|
104
|
+
end
|
105
|
+
|
106
|
+
if json
|
107
|
+
if response.code == 204
|
108
|
+
raise "Expected JSON response, got 204 No Content"
|
109
|
+
end
|
110
|
+
|
111
|
+
parse_json(response)
|
112
|
+
else
|
113
|
+
response
|
114
|
+
end
|
115
|
+
|
116
|
+
when 400
|
117
|
+
info = parse_json(response)
|
118
|
+
raise CFoundry::APIError.new(info[:code], info[:description])
|
119
|
+
|
120
|
+
when 401
|
121
|
+
info = parse_json(response)
|
122
|
+
raise CFoundry::Denied.new(info[:code], info[:description])
|
123
|
+
|
124
|
+
when 404
|
125
|
+
raise CFoundry::NotFound
|
126
|
+
|
127
|
+
when 411, 500, 504
|
128
|
+
begin
|
129
|
+
info = parse_json(response)
|
130
|
+
raise CFoundry::APIError.new(info[:code], info[:description])
|
131
|
+
rescue JSON::ParserError
|
132
|
+
raise CFoundry::BadResponse.new(response.code, response)
|
133
|
+
end
|
134
|
+
|
135
|
+
else
|
136
|
+
raise CFoundry::BadResponse.new(response.code, response)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
require "cfoundry/v2/base"
|
2
|
+
|
3
|
+
require "cfoundry/v2/app"
|
4
|
+
require "cfoundry/v2/framework"
|
5
|
+
require "cfoundry/v2/organization"
|
6
|
+
require "cfoundry/v2/runtime"
|
7
|
+
require "cfoundry/v2/service"
|
8
|
+
require "cfoundry/v2/service_instance"
|
9
|
+
require "cfoundry/v2/space"
|
10
|
+
require "cfoundry/v2/user"
|
11
|
+
|
12
|
+
module CFoundry::V2
|
13
|
+
# The primary API entrypoint. Wraps a BaseClient to provide nicer return
|
14
|
+
# values. Initialize with the target and, optionally, an auth token. These
|
15
|
+
# are the only two internal states.
|
16
|
+
class Client
|
17
|
+
# Internal BaseClient instance. Normally won't be touching this.
|
18
|
+
attr_reader :base
|
19
|
+
|
20
|
+
# [Organization] Currently targeted organization.
|
21
|
+
attr_accessor :current_organization
|
22
|
+
|
23
|
+
# [Space] Currently targeted space.
|
24
|
+
attr_accessor :current_space
|
25
|
+
|
26
|
+
|
27
|
+
# Create a new Client for interfacing with the given target.
|
28
|
+
#
|
29
|
+
# A token may also be provided to skip the login step.
|
30
|
+
def initialize(target = "http://api.cloudfoundry.com", token = nil)
|
31
|
+
@base = Base.new(target, token)
|
32
|
+
end
|
33
|
+
|
34
|
+
# The current target URL of the client.
|
35
|
+
def target
|
36
|
+
@base.target
|
37
|
+
end
|
38
|
+
|
39
|
+
# Current proxy user. Usually nil.
|
40
|
+
def proxy
|
41
|
+
@base.proxy
|
42
|
+
end
|
43
|
+
|
44
|
+
# Set the proxy user for the client. Must be authorized as an
|
45
|
+
# administrator for this to have any effect.
|
46
|
+
def proxy=(email)
|
47
|
+
@base.proxy = email
|
48
|
+
end
|
49
|
+
|
50
|
+
# Is the client tracing API requests?
|
51
|
+
def trace
|
52
|
+
@base.trace
|
53
|
+
end
|
54
|
+
|
55
|
+
# Set the tracing flag; if true, API requests and responses will be
|
56
|
+
# printed out.
|
57
|
+
def trace=(bool)
|
58
|
+
@base.trace = bool
|
59
|
+
end
|
60
|
+
|
61
|
+
# The currently authenticated user.
|
62
|
+
def current_user
|
63
|
+
if user = info[:user]
|
64
|
+
user(user)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Cloud metadata
|
69
|
+
def info
|
70
|
+
@base.info
|
71
|
+
end
|
72
|
+
|
73
|
+
# Login prompts
|
74
|
+
def login_prompts
|
75
|
+
if @base.uaa
|
76
|
+
@base.uaa.prompts
|
77
|
+
else
|
78
|
+
{ :username => ["text", "Email"],
|
79
|
+
:password => ["password", "Password"]
|
80
|
+
}
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Authenticate with the target. Sets the client token.
|
85
|
+
#
|
86
|
+
# Credentials is a hash, typically containing :username and :password
|
87
|
+
# keys.
|
88
|
+
#
|
89
|
+
# The values in the hash should mirror the prompts given by
|
90
|
+
# `login_prompts`.
|
91
|
+
def login(credentials)
|
92
|
+
@current_organization = nil
|
93
|
+
@current_space = nil
|
94
|
+
|
95
|
+
@base.token =
|
96
|
+
if @base.uaa
|
97
|
+
@base.uaa.authorize(credentials)
|
98
|
+
else
|
99
|
+
@base.create_token(
|
100
|
+
{ :password => credentials[:password] },
|
101
|
+
credentials[:username])[:token]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Clear client token. No requests are made for this.
|
106
|
+
def logout
|
107
|
+
@base.token = nil
|
108
|
+
end
|
109
|
+
|
110
|
+
# Is an authentication token set on the client?
|
111
|
+
def logged_in?
|
112
|
+
!!@base.token
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
[:app, :organization, :app_space, :service, :service_binding,
|
117
|
+
:service_instance, :user, :runtime, :framework].each do |singular|
|
118
|
+
klass = singular.to_s.capitalize.gsub(/(.)_(.)/) do
|
119
|
+
$1 + $2.upcase
|
120
|
+
end
|
121
|
+
|
122
|
+
plural = :"#{singular}s"
|
123
|
+
|
124
|
+
define_method(singular) do |*args|
|
125
|
+
id, _ = args
|
126
|
+
CFoundry::V2.const_get(klass).new(id, self)
|
127
|
+
end
|
128
|
+
|
129
|
+
define_method(plural) do |*args|
|
130
|
+
depth, query = args
|
131
|
+
depth ||= 1
|
132
|
+
|
133
|
+
@base.send(plural, depth, query)[:resources].collect do |json|
|
134
|
+
send(:"make_#{singular}", json)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
define_method(:"#{singular}_from") do |path|
|
139
|
+
uri = URI.parse(path)
|
140
|
+
|
141
|
+
if uri.query
|
142
|
+
uri.query += "&inline-relations-depth=1"
|
143
|
+
else
|
144
|
+
uri.query = "inline-relations-depth=1"
|
145
|
+
end
|
146
|
+
|
147
|
+
send(
|
148
|
+
:"make_#{singular}",
|
149
|
+
@base.request_path(
|
150
|
+
:get,
|
151
|
+
uri.to_s,
|
152
|
+
nil => :json))
|
153
|
+
end
|
154
|
+
|
155
|
+
define_method(:"#{plural}_from") do |path|
|
156
|
+
uri = URI.parse(path)
|
157
|
+
|
158
|
+
if uri.query
|
159
|
+
uri.query += "&inline-relations-depth=1"
|
160
|
+
else
|
161
|
+
uri.query = "inline-relations-depth=1"
|
162
|
+
end
|
163
|
+
|
164
|
+
@base.request_path(
|
165
|
+
:get,
|
166
|
+
uri.to_s,
|
167
|
+
nil => :json)[:resources].collect do |json|
|
168
|
+
send(:"make_#{singular}", json)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
define_method(:"make_#{singular}") do |json|
|
173
|
+
CFoundry::V2.const_get(klass).new(json[:metadata][:guid], self, json)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
alias :spaces :app_spaces
|
178
|
+
alias :space :app_space
|
179
|
+
|
180
|
+
alias :system_frameworks :frameworks
|
181
|
+
alias :system_runtimes :runtimes
|
182
|
+
alias :system_services :services
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "cfoundry/v2/model"
|
2
|
+
|
3
|
+
module CFoundry::V2
|
4
|
+
class Framework < Model
|
5
|
+
attribute :name
|
6
|
+
attribute :description
|
7
|
+
to_many :apps
|
8
|
+
|
9
|
+
def detection
|
10
|
+
nil # TODO for v2?
|
11
|
+
end
|
12
|
+
|
13
|
+
def runtimes
|
14
|
+
[] # TODO for v2?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,149 @@
|
|
1
|
+
module CFoundry::V2
|
2
|
+
class Model
|
3
|
+
class << self
|
4
|
+
def defaults
|
5
|
+
@defaults ||= {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def attribute(name, opts = {})
|
9
|
+
default = opts[:default] || nil
|
10
|
+
defaults[name] = default if default
|
11
|
+
|
12
|
+
define_method(name) {
|
13
|
+
manifest[:entity][name] || default
|
14
|
+
}
|
15
|
+
|
16
|
+
define_method(:"#{name}=") { |val|
|
17
|
+
@manifest ||= {}
|
18
|
+
@manifest[:entity] ||= {}
|
19
|
+
@manifest[:entity][name] = val
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_one(name, opts = {})
|
24
|
+
obj = opts[:as] || name
|
25
|
+
|
26
|
+
define_method(name) {
|
27
|
+
if manifest[:entity].key? name
|
28
|
+
@client.send(:"make_#{obj}", manifest[:entity][name])
|
29
|
+
else
|
30
|
+
@client.send(:"#{name}_from", send("#{obj}_url"))
|
31
|
+
end
|
32
|
+
}
|
33
|
+
|
34
|
+
define_method(:"#{name}_url") {
|
35
|
+
manifest[:entity][:"#{name}_url"]
|
36
|
+
}
|
37
|
+
|
38
|
+
define_method(:"#{name}=") { |x|
|
39
|
+
@manifest ||= {}
|
40
|
+
@manifest[:entity] ||= {}
|
41
|
+
@manifest[:entity][:"#{name}_guid"] = x.id
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_many(plural, opts = {})
|
46
|
+
singular = plural.to_s.sub(/s$/, "").to_sym
|
47
|
+
object = opts[:as] || singular
|
48
|
+
|
49
|
+
define_method(plural) {
|
50
|
+
if manifest[:entity].key? plural
|
51
|
+
manifest[:entity][plural].collect do |json|
|
52
|
+
@client.send(:"make_#{object}", json)
|
53
|
+
end
|
54
|
+
else
|
55
|
+
@client.send(:"#{plural}_from", send("#{plural}_url"))
|
56
|
+
end
|
57
|
+
}
|
58
|
+
|
59
|
+
define_method(:"#{plural}_url") {
|
60
|
+
manifest[:entity][:"#{plural}_url"]
|
61
|
+
}
|
62
|
+
|
63
|
+
# TODO: these are hacky
|
64
|
+
define_method(:"add_#{singular}") { |x|
|
65
|
+
@client.base.request_path(
|
66
|
+
:put,
|
67
|
+
["v2", "#{object_name}s", @id, plural, x.id],
|
68
|
+
nil => :json)
|
69
|
+
}
|
70
|
+
|
71
|
+
define_method(:"remove_#{singular}") {
|
72
|
+
@client.base.request_path(
|
73
|
+
:delete,
|
74
|
+
["v2", "#{object_name}s", @id, plural, x.id],
|
75
|
+
nil => :json)
|
76
|
+
}
|
77
|
+
|
78
|
+
define_method(:"#{plural}=") { |xs|
|
79
|
+
@manifest ||= {}
|
80
|
+
@manifest[:entity] ||= {}
|
81
|
+
@manifest[:entity][:"#{singular}_guids"] = xs.collect(&:id)
|
82
|
+
}
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
attr_reader :id
|
87
|
+
|
88
|
+
def initialize(id, client, manifest = nil)
|
89
|
+
@id = id
|
90
|
+
@client = client
|
91
|
+
@manifest = manifest
|
92
|
+
end
|
93
|
+
|
94
|
+
def manifest
|
95
|
+
# inline depth of 2 for fewer requests
|
96
|
+
@manifest ||= @client.base.send(object_name, @id, 2)
|
97
|
+
end
|
98
|
+
|
99
|
+
def inspect
|
100
|
+
"\#<#{self.class.name} '#@id'>"
|
101
|
+
end
|
102
|
+
|
103
|
+
def object_name
|
104
|
+
@object_name ||=
|
105
|
+
self.class.name.split("::").last.gsub(
|
106
|
+
/([a-z])([A-Z])/,
|
107
|
+
'\1_\2').downcase
|
108
|
+
end
|
109
|
+
|
110
|
+
def create!
|
111
|
+
@manifest =
|
112
|
+
@client.base.send(
|
113
|
+
:"create_#{object_name}",
|
114
|
+
@manifest[:entity].merge(self.class.defaults))
|
115
|
+
|
116
|
+
@id = @manifest[:metadata][:guid]
|
117
|
+
|
118
|
+
true
|
119
|
+
end
|
120
|
+
|
121
|
+
def update!(diff = nil)
|
122
|
+
@client.base.send(
|
123
|
+
:"update_#{object_name}",
|
124
|
+
@id,
|
125
|
+
diff || manifest[:entity])
|
126
|
+
|
127
|
+
@manifest = nil
|
128
|
+
end
|
129
|
+
|
130
|
+
def delete!
|
131
|
+
@client.base.send(:"delete_#{object_name}", @id)
|
132
|
+
|
133
|
+
if @manifest
|
134
|
+
@manifest.delete :metadata
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def exists?
|
139
|
+
@client.base.send(object_name, @id)
|
140
|
+
true
|
141
|
+
rescue CFoundry::APIError # TODO: NotFound would be better
|
142
|
+
false
|
143
|
+
end
|
144
|
+
|
145
|
+
def ==(other)
|
146
|
+
other.is_a?(self.class) && @id == other.id
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|