cfoundry-IronFoundry 0.3.34
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,20 @@
|
|
1
|
+
module CFoundry::V1
|
2
|
+
class Runtime
|
3
|
+
attr_accessor :name, :description, :debug_modes
|
4
|
+
|
5
|
+
def initialize(name, description = nil, debug_modes = nil)
|
6
|
+
@name = name
|
7
|
+
@description = description
|
8
|
+
@debug_modes = debug_modes
|
9
|
+
end
|
10
|
+
|
11
|
+
def eql?(other)
|
12
|
+
other.is_a?(self.class) && other.name == @name
|
13
|
+
end
|
14
|
+
alias :== :eql?
|
15
|
+
|
16
|
+
def apps
|
17
|
+
[] # not supported by v1
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module CFoundry::V1
|
2
|
+
class Service
|
3
|
+
attr_accessor :label, :version, :description, :type
|
4
|
+
|
5
|
+
def initialize(label, version = nil, description = nil, type = nil)
|
6
|
+
@label = label
|
7
|
+
@description = description
|
8
|
+
@version = version
|
9
|
+
@type = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def eql?(other)
|
13
|
+
other.is_a?(self.class) && other.label == @label
|
14
|
+
end
|
15
|
+
alias :== :eql?
|
16
|
+
|
17
|
+
def provider
|
18
|
+
"core"
|
19
|
+
end
|
20
|
+
|
21
|
+
def active
|
22
|
+
true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module CFoundry::V1
|
2
|
+
# Class for representing a user's service on a given target (via Client).
|
3
|
+
#
|
4
|
+
# Does not guarantee that the service exists; used for both service creation
|
5
|
+
# and retrieval, as the attributes are all lazily retrieved. Setting
|
6
|
+
# attributes does not perform any requests; use #update! to commit your
|
7
|
+
# changes.
|
8
|
+
class ServiceInstance
|
9
|
+
# Service name.
|
10
|
+
attr_accessor :name
|
11
|
+
|
12
|
+
# Service type (e.g. key-value).
|
13
|
+
attr_accessor :type
|
14
|
+
|
15
|
+
# Service vendor (redis, mysql, etc.).
|
16
|
+
attr_accessor :vendor
|
17
|
+
|
18
|
+
# Service version.
|
19
|
+
attr_accessor :version
|
20
|
+
|
21
|
+
# Service properties.
|
22
|
+
attr_accessor :properties
|
23
|
+
|
24
|
+
# Service tier. Usually "free" for now.
|
25
|
+
attr_accessor :tier
|
26
|
+
|
27
|
+
# Service metadata.
|
28
|
+
attr_accessor :meta
|
29
|
+
|
30
|
+
# Create a Service object.
|
31
|
+
#
|
32
|
+
# You'll usually call Client#service instead.
|
33
|
+
def initialize(name, client, manifest = nil)
|
34
|
+
@name = name
|
35
|
+
@client = client
|
36
|
+
@manifest = manifest
|
37
|
+
end
|
38
|
+
|
39
|
+
# Show string representing the service.
|
40
|
+
def inspect
|
41
|
+
"#<ServiceInstance '#@name'>"
|
42
|
+
end
|
43
|
+
|
44
|
+
# Basic equality test by name.
|
45
|
+
def eql?(other)
|
46
|
+
other.is_a?(self.class) && other.name == @name
|
47
|
+
end
|
48
|
+
alias :== :eql?
|
49
|
+
|
50
|
+
# Delete the service from the target.
|
51
|
+
def delete!
|
52
|
+
@client.base.delete_service(@name)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Create the service on the target.
|
56
|
+
#
|
57
|
+
# Call this after setting the various attributes.
|
58
|
+
def create!
|
59
|
+
@client.base.create_service(@manifest.merge(:name => @name))
|
60
|
+
end
|
61
|
+
|
62
|
+
# Check if the service exists on the target.
|
63
|
+
def exists?
|
64
|
+
@client.base.service(@name)
|
65
|
+
true
|
66
|
+
rescue CFoundry::NotFound
|
67
|
+
false
|
68
|
+
end
|
69
|
+
|
70
|
+
# Timestamp of when the service was created.
|
71
|
+
def created
|
72
|
+
Time.at(meta[:created])
|
73
|
+
end
|
74
|
+
|
75
|
+
# Timestamp of when the service was last updated.
|
76
|
+
def updated
|
77
|
+
Time.at(meta[:updated])
|
78
|
+
end
|
79
|
+
|
80
|
+
def invalidate!
|
81
|
+
@manifest = nil
|
82
|
+
end
|
83
|
+
|
84
|
+
def eql?(other)
|
85
|
+
other.is_a?(self.class) && @name == other.name
|
86
|
+
end
|
87
|
+
alias :== :eql?
|
88
|
+
|
89
|
+
{ :type => :type,
|
90
|
+
:vendor => :vendor,
|
91
|
+
:version => :version,
|
92
|
+
:properties => :properties,
|
93
|
+
:tier => :tier,
|
94
|
+
:meta => :meta
|
95
|
+
}.each do |meth, attr|
|
96
|
+
define_method(meth) do
|
97
|
+
manifest[attr]
|
98
|
+
end
|
99
|
+
|
100
|
+
define_method(:"#{meth}=") do |v|
|
101
|
+
@manifest ||= {}
|
102
|
+
@manifest[attr] = v
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def manifest
|
109
|
+
@manifest ||= @client.base.service(@name)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module CFoundry::V1
|
2
|
+
# Class for representing a user on a given target (via Client).
|
3
|
+
#
|
4
|
+
# Does not guarantee that the user exists; used for both user creation and
|
5
|
+
# retrieval, as the attributes are all lazily retrieved. Setting attributes
|
6
|
+
# does not perform any requests; use #update! to commit your changes.
|
7
|
+
class User
|
8
|
+
# User email.
|
9
|
+
attr_reader :email
|
10
|
+
|
11
|
+
|
12
|
+
# Create a User object.
|
13
|
+
#
|
14
|
+
# You'll usually call Client#user instead
|
15
|
+
def initialize(email, client, manifest = nil)
|
16
|
+
@email = email
|
17
|
+
@client = client
|
18
|
+
@manifest = manifest
|
19
|
+
end
|
20
|
+
|
21
|
+
# Show string representing the user.
|
22
|
+
def inspect
|
23
|
+
"#<User '#@email'>"
|
24
|
+
end
|
25
|
+
|
26
|
+
# Basic equality test by email.
|
27
|
+
def eql?(other)
|
28
|
+
other.is_a?(self.class) && other.email == @email
|
29
|
+
end
|
30
|
+
alias :== :eql?
|
31
|
+
|
32
|
+
# Delete the user from the target.
|
33
|
+
def delete!
|
34
|
+
@client.base.delete_user(@email)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Create the user on the target.
|
38
|
+
#
|
39
|
+
# Call this after setting the various attributes.
|
40
|
+
def create!
|
41
|
+
@client.base.create_user(@manifest.merge(:email => @email))
|
42
|
+
end
|
43
|
+
|
44
|
+
# Update user attributes.
|
45
|
+
def update!(what = {})
|
46
|
+
@client.base.update_user(@email, manifest.merge(what))
|
47
|
+
@manifest = nil
|
48
|
+
end
|
49
|
+
|
50
|
+
# Check if the user exists on the target.
|
51
|
+
def exists?
|
52
|
+
@client.base.user(@email)
|
53
|
+
true
|
54
|
+
rescue CFoundry::Denied
|
55
|
+
false
|
56
|
+
end
|
57
|
+
|
58
|
+
# Check if the user is an administrator.
|
59
|
+
def admin?
|
60
|
+
manifest[:admin]
|
61
|
+
end
|
62
|
+
|
63
|
+
# Set the user's password.
|
64
|
+
#
|
65
|
+
# Call #update! after using this.
|
66
|
+
def password=(str)
|
67
|
+
manifest[:password] = str
|
68
|
+
end
|
69
|
+
|
70
|
+
def guid
|
71
|
+
@guid ||= @client.base.token_data[:user_id]
|
72
|
+
end
|
73
|
+
|
74
|
+
def change_password!(new, old)
|
75
|
+
if @client.base.uaa
|
76
|
+
@client.base.uaa.change_password(guid, new, old)
|
77
|
+
else
|
78
|
+
self.password = new
|
79
|
+
update!
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def manifest
|
86
|
+
@manifest ||= @client.base.user(@email)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,328 @@
|
|
1
|
+
require "tmpdir"
|
2
|
+
require "multi_json"
|
3
|
+
|
4
|
+
require "cfoundry/zip"
|
5
|
+
require "cfoundry/upload_helpers"
|
6
|
+
require "cfoundry/chatty_hash"
|
7
|
+
|
8
|
+
require "cfoundry/v2/model"
|
9
|
+
|
10
|
+
module CFoundry::V2
|
11
|
+
# Class for representing a user's application on a given target (via
|
12
|
+
# Client).
|
13
|
+
#
|
14
|
+
# Does not guarantee that the app exists; used for both app creation and
|
15
|
+
# retrieval, as the attributes are all lazily retrieved. Setting attributes
|
16
|
+
# does not perform any requests; use #update! to commit your changes.
|
17
|
+
class App < Model
|
18
|
+
include CFoundry::UploadHelpers
|
19
|
+
|
20
|
+
attribute :name, :string
|
21
|
+
attribute :production, :boolean, :default => false
|
22
|
+
to_one :space
|
23
|
+
to_one :runtime
|
24
|
+
to_one :framework
|
25
|
+
attribute :environment_json, :hash, :default => {}
|
26
|
+
attribute :memory, :integer, :default => 256
|
27
|
+
attribute :instances, :integer, :default => 1
|
28
|
+
attribute :file_descriptors, :integer, :default => 256
|
29
|
+
attribute :disk_quota, :integer, :default => 256
|
30
|
+
attribute :state, :integer, :default => "STOPPED"
|
31
|
+
to_many :service_bindings
|
32
|
+
to_many :routes
|
33
|
+
|
34
|
+
alias :total_instances :instances
|
35
|
+
alias :total_instances= :instances=
|
36
|
+
|
37
|
+
private :environment_json
|
38
|
+
|
39
|
+
def instances
|
40
|
+
@client.base.instances(@guid).collect do |i, m|
|
41
|
+
Instance.new(self, i.to_s, @client, m)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def stats
|
46
|
+
@client.base.stats(@guid)
|
47
|
+
end
|
48
|
+
|
49
|
+
def services
|
50
|
+
service_bindings.collect(&:service_instance)
|
51
|
+
end
|
52
|
+
|
53
|
+
def env
|
54
|
+
@env ||= CFoundry::ChattyHash.new(
|
55
|
+
method(:env=),
|
56
|
+
environment_json)
|
57
|
+
end
|
58
|
+
|
59
|
+
alias :env= :environment_json=
|
60
|
+
|
61
|
+
def command # TODO v2
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
|
65
|
+
def debug_mode # TODO v2
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
|
69
|
+
def console # TODO v2
|
70
|
+
nil
|
71
|
+
end
|
72
|
+
|
73
|
+
def uris
|
74
|
+
routes.collect do |r|
|
75
|
+
"#{r.host}.#{r.domain.name}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
alias :urls :uris
|
79
|
+
|
80
|
+
def uris=(uris)
|
81
|
+
raise "App#uris= is invalid against V2 APIs. Use add/remove_route."
|
82
|
+
end
|
83
|
+
alias :urls= :uris=
|
84
|
+
|
85
|
+
def create_routes(*uris)
|
86
|
+
uris.each do |uri|
|
87
|
+
host, domain_name = uri.split(".", 2)
|
88
|
+
|
89
|
+
domain =
|
90
|
+
@client.current_space.domains.find { |d|
|
91
|
+
d.name == domain_name
|
92
|
+
}
|
93
|
+
|
94
|
+
raise "Invalid domain '#{domain_name}'" unless domain
|
95
|
+
|
96
|
+
route = @client.routes.find { |r|
|
97
|
+
r.host == host && r.domain == domain
|
98
|
+
}
|
99
|
+
|
100
|
+
unless route
|
101
|
+
route = @client.route
|
102
|
+
route.host = host
|
103
|
+
route.domain = domain
|
104
|
+
route.organization = @client.current_organization
|
105
|
+
route.create!
|
106
|
+
end
|
107
|
+
|
108
|
+
add_route(route)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
alias :create_route :create_routes
|
112
|
+
|
113
|
+
def uri
|
114
|
+
uris[0]
|
115
|
+
end
|
116
|
+
alias :url :uri
|
117
|
+
|
118
|
+
def uri=(x)
|
119
|
+
self.uris = [x]
|
120
|
+
end
|
121
|
+
alias :url= :uri=
|
122
|
+
|
123
|
+
# Stop the application.
|
124
|
+
def stop!
|
125
|
+
update! :state => "STOPPED"
|
126
|
+
end
|
127
|
+
|
128
|
+
# Start the application.
|
129
|
+
def start!
|
130
|
+
update! :state => "STARTED"
|
131
|
+
end
|
132
|
+
|
133
|
+
# Restart the application.
|
134
|
+
def restart!
|
135
|
+
stop!
|
136
|
+
start!
|
137
|
+
end
|
138
|
+
|
139
|
+
# Determine application health.
|
140
|
+
#
|
141
|
+
# If all instances are running, returns "RUNNING". If only some are
|
142
|
+
# started, returns the precentage of them that are healthy.
|
143
|
+
#
|
144
|
+
# Otherwise, returns application's status.
|
145
|
+
def health
|
146
|
+
state
|
147
|
+
end
|
148
|
+
|
149
|
+
# Check that all application instances are running.
|
150
|
+
def healthy?
|
151
|
+
# invalidate cache so the check is fresh
|
152
|
+
@manifest = nil
|
153
|
+
|
154
|
+
case health
|
155
|
+
when "RUNNING", "STARTED"
|
156
|
+
true
|
157
|
+
end
|
158
|
+
end
|
159
|
+
alias_method :running?, :healthy?
|
160
|
+
|
161
|
+
# Is the application stopped?
|
162
|
+
def stopped?
|
163
|
+
state == "STOPPED"
|
164
|
+
end
|
165
|
+
|
166
|
+
# Is the application started?
|
167
|
+
#
|
168
|
+
# Note that this does not imply that all instances are running. See
|
169
|
+
# #healthy?
|
170
|
+
def started?
|
171
|
+
state == "STARTED"
|
172
|
+
end
|
173
|
+
|
174
|
+
# Bind services to application.
|
175
|
+
def bind(*instances)
|
176
|
+
instances.each do |i|
|
177
|
+
binding = @client.service_binding
|
178
|
+
binding.app = self
|
179
|
+
binding.service_instance = i
|
180
|
+
binding.create!
|
181
|
+
end
|
182
|
+
|
183
|
+
self
|
184
|
+
end
|
185
|
+
|
186
|
+
# Unbind services from application.
|
187
|
+
def unbind(*instances)
|
188
|
+
service_bindings.each do |b|
|
189
|
+
if instances.include? b.service_instance
|
190
|
+
b.delete!
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
self
|
195
|
+
end
|
196
|
+
|
197
|
+
def binds?(instance)
|
198
|
+
service_bindings.any? { |b|
|
199
|
+
b.service_instance == instance
|
200
|
+
}
|
201
|
+
end
|
202
|
+
|
203
|
+
# Upload application's code to target. Do this after #create! and before
|
204
|
+
# #start!
|
205
|
+
#
|
206
|
+
# [path]
|
207
|
+
# A path pointing to either a directory, or a .jar, .war, or .zip
|
208
|
+
# file.
|
209
|
+
#
|
210
|
+
# If a .vmcignore file is detected under the given path, it will be used
|
211
|
+
# to exclude paths from the payload, similar to a .gitignore.
|
212
|
+
#
|
213
|
+
# [check_resources]
|
214
|
+
# If set to `false`, the entire payload will be uploaded
|
215
|
+
# without checking the resource cache.
|
216
|
+
#
|
217
|
+
# Only do this if you know what you're doing.
|
218
|
+
def upload(path, check_resources = true)
|
219
|
+
unless File.exist? path
|
220
|
+
raise "invalid application path '#{path}'"
|
221
|
+
end
|
222
|
+
|
223
|
+
zipfile = "#{Dir.tmpdir}/#{@guid}.zip"
|
224
|
+
tmpdir = "#{Dir.tmpdir}/.vmc_#{@guid}_files"
|
225
|
+
|
226
|
+
FileUtils.rm_f(zipfile)
|
227
|
+
FileUtils.rm_rf(tmpdir)
|
228
|
+
|
229
|
+
prepare_package(path, tmpdir)
|
230
|
+
|
231
|
+
resources = determine_resources(tmpdir) if check_resources
|
232
|
+
|
233
|
+
packed = CFoundry::Zip.pack(tmpdir, zipfile)
|
234
|
+
|
235
|
+
@client.base.upload_app(@guid, packed && zipfile, resources || [])
|
236
|
+
ensure
|
237
|
+
FileUtils.rm_f(zipfile) if zipfile
|
238
|
+
FileUtils.rm_rf(tmpdir) if tmpdir
|
239
|
+
end
|
240
|
+
|
241
|
+
def files(*path)
|
242
|
+
Instance.new(self, "0", @client).files(*path)
|
243
|
+
end
|
244
|
+
|
245
|
+
def file(*path)
|
246
|
+
Instance.new(self, "0", @client).file(*path)
|
247
|
+
end
|
248
|
+
|
249
|
+
class Instance
|
250
|
+
attr_reader :app, :id
|
251
|
+
|
252
|
+
def initialize(app, id, client, manifest = {})
|
253
|
+
@app = app
|
254
|
+
@id = id
|
255
|
+
@client = client
|
256
|
+
@manifest = manifest
|
257
|
+
end
|
258
|
+
|
259
|
+
def inspect
|
260
|
+
"#<App::Instance '#{@app.name}' \##@id>"
|
261
|
+
end
|
262
|
+
|
263
|
+
def state
|
264
|
+
@manifest[:state]
|
265
|
+
end
|
266
|
+
alias_method :status, :state
|
267
|
+
|
268
|
+
def since
|
269
|
+
Time.at(@manifest[:since])
|
270
|
+
end
|
271
|
+
|
272
|
+
def debugger
|
273
|
+
return unless @manifest[:debug_ip] and @manifest[:debug_port]
|
274
|
+
|
275
|
+
{ :ip => @manifest[:debug_ip],
|
276
|
+
:port => @manifest[:debug_port]
|
277
|
+
}
|
278
|
+
end
|
279
|
+
|
280
|
+
def console
|
281
|
+
return unless @manifest[:console_ip] and @manifest[:console_port]
|
282
|
+
|
283
|
+
{ :ip => @manifest[:console_ip],
|
284
|
+
:port => @manifest[:console_port]
|
285
|
+
}
|
286
|
+
end
|
287
|
+
|
288
|
+
def healthy?
|
289
|
+
case state
|
290
|
+
when "STARTING", "RUNNING"
|
291
|
+
true
|
292
|
+
when "DOWN", "FLAPPING"
|
293
|
+
false
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
def files(*path)
|
298
|
+
@client.base.files(@app.guid, @id, *path).split("\n").collect do |entry|
|
299
|
+
path + [entry.split(/\s+/, 2)[0]]
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
def file(*path)
|
304
|
+
@client.base.files(@app.guid, @id, *path)
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
private
|
309
|
+
|
310
|
+
# Minimum size for an application payload to bother checking resources.
|
311
|
+
RESOURCE_CHECK_LIMIT = 64 * 1024
|
312
|
+
|
313
|
+
def determine_resources(path)
|
314
|
+
fingerprints, total_size = make_fingerprints(path)
|
315
|
+
|
316
|
+
return if total_size <= RESOURCE_CHECK_LIMIT
|
317
|
+
|
318
|
+
resources = @client.base.resource_match(fingerprints)
|
319
|
+
|
320
|
+
resources.each do |resource|
|
321
|
+
FileUtils.rm_f resource[:fn]
|
322
|
+
resource[:fn].sub!("#{path}/", "")
|
323
|
+
end
|
324
|
+
|
325
|
+
resources
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|