cfoundry 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/cfoundry/app.rb +166 -23
- data/lib/cfoundry/client.rb +45 -10
- data/lib/cfoundry/errors.rb +23 -7
- data/lib/cfoundry/restclient.rb +1 -1
- data/lib/cfoundry/service.rb +42 -5
- data/lib/cfoundry/user.rb +27 -5
- data/lib/cfoundry/version.rb +3 -2
- data/lib/cfoundry/zip.rb +9 -1
- metadata +4 -4
data/lib/cfoundry/app.rb
CHANGED
@@ -6,23 +6,67 @@ require "tmpdir"
|
|
6
6
|
require "cfoundry/zip"
|
7
7
|
|
8
8
|
module CFoundry
|
9
|
+
# Class for representing a user's application on a given target (via
|
10
|
+
# Client).
|
11
|
+
#
|
12
|
+
# Goes not guarantee that the app exists; used for both app creation and
|
13
|
+
# retrieval, as the attributes are all lazily retrieved. Setting attributes
|
14
|
+
# does not perform any requests; use #update! to commit your changes.
|
9
15
|
class App
|
16
|
+
# Application name.
|
10
17
|
attr_reader :name
|
11
18
|
|
19
|
+
# Application instance count.
|
20
|
+
attr_accessor :total_instances
|
21
|
+
|
22
|
+
# Services bound to the application.
|
23
|
+
attr_accessor :services
|
24
|
+
|
25
|
+
# Application environment variables.
|
26
|
+
attr_accessor :env
|
27
|
+
|
28
|
+
# Application memory limit.
|
29
|
+
attr_accessor :memory
|
30
|
+
|
31
|
+
# Application framework.
|
32
|
+
attr_accessor :framework
|
33
|
+
|
34
|
+
# Application runtime.
|
35
|
+
attr_accessor :runtime
|
36
|
+
|
37
|
+
# Application startup command.
|
38
|
+
#
|
39
|
+
# Used for standalone apps.
|
40
|
+
attr_accessor :command
|
41
|
+
|
42
|
+
# Application debug mode.
|
43
|
+
attr_accessor :debug_mode
|
44
|
+
|
45
|
+
# Application state.
|
46
|
+
attr_accessor :state
|
47
|
+
alias_method :status, :state
|
48
|
+
|
49
|
+
# URIs mapped to the application.
|
50
|
+
attr_accessor :uris
|
51
|
+
alias_method :urls, :uris
|
52
|
+
|
53
|
+
|
54
|
+
# Create an App object.
|
55
|
+
#
|
56
|
+
# You'll usually call Client#app instead
|
12
57
|
def initialize(name, client, manifest = nil)
|
13
58
|
@name = name
|
14
59
|
@client = client
|
15
60
|
@manifest = manifest
|
16
61
|
end
|
17
62
|
|
18
|
-
def inspect
|
63
|
+
def inspect # :nodoc:
|
19
64
|
"#<App '#@name'>"
|
20
65
|
end
|
21
66
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
67
|
+
# Delete the application from the target.
|
68
|
+
#
|
69
|
+
# Keeps the metadata, but clears target-specific state from it.
|
26
70
|
def delete!
|
27
71
|
@client.rest.delete_app(@name)
|
28
72
|
|
@@ -33,11 +77,15 @@ module CFoundry
|
|
33
77
|
end
|
34
78
|
end
|
35
79
|
|
80
|
+
# Create the application on the target.
|
81
|
+
#
|
82
|
+
# Call this after setting the various attributes.
|
36
83
|
def create!
|
37
84
|
@client.rest.create_app(@manifest.merge("name" => @name))
|
38
85
|
@manifest = nil
|
39
86
|
end
|
40
87
|
|
88
|
+
# Check if the application exists on the target.
|
41
89
|
def exists?
|
42
90
|
@client.rest.app(@name)
|
43
91
|
true
|
@@ -45,16 +93,19 @@ module CFoundry
|
|
45
93
|
false
|
46
94
|
end
|
47
95
|
|
96
|
+
# Retrieve all of the instances of the app, as Instance objects.
|
48
97
|
def instances
|
49
98
|
@client.rest.instances(@name).collect do |m|
|
50
99
|
Instance.new(@name, m["index"], @client, m)
|
51
100
|
end
|
52
101
|
end
|
53
102
|
|
103
|
+
# Retrieve application statistics, e.g. CPU load and memory usage.
|
54
104
|
def stats
|
55
105
|
@client.rest.stats(@name)
|
56
106
|
end
|
57
107
|
|
108
|
+
# Update application attributes. Does not restart the application.
|
58
109
|
def update!(what = {})
|
59
110
|
# TODO: hacky; can we not just set in meta field?
|
60
111
|
# we write to manifest["debug"] but read from manifest["meta"]["debug"]
|
@@ -64,19 +115,28 @@ module CFoundry
|
|
64
115
|
@manifest = nil
|
65
116
|
end
|
66
117
|
|
118
|
+
# Stop the application.
|
67
119
|
def stop!
|
68
120
|
update! "state" => "STOPPED"
|
69
121
|
end
|
70
122
|
|
123
|
+
# Start the application.
|
71
124
|
def start!
|
72
125
|
update! "state" => "STARTED"
|
73
126
|
end
|
74
127
|
|
128
|
+
# Restart the application.
|
75
129
|
def restart!
|
76
130
|
stop!
|
77
131
|
start!
|
78
132
|
end
|
79
133
|
|
134
|
+
# Determine application health.
|
135
|
+
#
|
136
|
+
# If all instances are running, returns "RUNNING". If only some are
|
137
|
+
# started, returns the precentage of them that are healthy.
|
138
|
+
#
|
139
|
+
# Otherwise, returns application's status.
|
80
140
|
def health
|
81
141
|
s = state
|
82
142
|
if s == "STARTED"
|
@@ -97,20 +157,26 @@ module CFoundry
|
|
97
157
|
end
|
98
158
|
end
|
99
159
|
|
160
|
+
# Check that all application instances are running.
|
100
161
|
def healthy?
|
101
162
|
# invalidate cache so the check is fresh
|
102
163
|
@manifest = nil
|
103
164
|
health == "RUNNING"
|
104
165
|
end
|
166
|
+
alias_method :running?, :healthy?
|
105
167
|
|
168
|
+
# Is the application stopped?
|
106
169
|
def stopped?
|
107
170
|
state == "STOPPED"
|
108
171
|
end
|
109
172
|
|
173
|
+
# Is the application started?
|
174
|
+
#
|
175
|
+
# Note that this does not imply that all instances are running. See
|
176
|
+
# #healthy?
|
110
177
|
def started?
|
111
178
|
state == "STARTED"
|
112
179
|
end
|
113
|
-
alias_method :running?, :started?
|
114
180
|
|
115
181
|
{ :total_instances => "instances",
|
116
182
|
:state => "state",
|
@@ -130,10 +196,12 @@ module CFoundry
|
|
130
196
|
end
|
131
197
|
end
|
132
198
|
|
199
|
+
# Shortcut for uris[0]
|
133
200
|
def uri
|
134
201
|
uris[0]
|
135
202
|
end
|
136
203
|
|
204
|
+
# Shortcut for uris = [x]
|
137
205
|
def uri=(x)
|
138
206
|
self.uris = [x]
|
139
207
|
end
|
@@ -141,12 +209,12 @@ module CFoundry
|
|
141
209
|
alias :url :uri
|
142
210
|
alias :url= :uri=
|
143
211
|
|
144
|
-
def framework
|
212
|
+
def framework # :nodoc:
|
145
213
|
manifest["staging"]["framework"] ||
|
146
214
|
manifest["staging"]["model"]
|
147
215
|
end
|
148
216
|
|
149
|
-
def framework=(v)
|
217
|
+
def framework=(v) # :nodoc:
|
150
218
|
@manifest ||= {}
|
151
219
|
@manifest["staging"] ||= {}
|
152
220
|
|
@@ -157,12 +225,12 @@ module CFoundry
|
|
157
225
|
end
|
158
226
|
end
|
159
227
|
|
160
|
-
def runtime
|
228
|
+
def runtime # :nodoc:
|
161
229
|
manifest["staging"]["runtime"] ||
|
162
230
|
manifest["staging"]["stack"]
|
163
231
|
end
|
164
232
|
|
165
|
-
def runtime=(v)
|
233
|
+
def runtime=(v) # :nodoc:
|
166
234
|
@manifest ||= {}
|
167
235
|
@manifest["staging"] ||= {}
|
168
236
|
|
@@ -173,39 +241,47 @@ module CFoundry
|
|
173
241
|
end
|
174
242
|
end
|
175
243
|
|
176
|
-
|
244
|
+
|
245
|
+
def command # :nodoc:
|
177
246
|
manifest["staging"]["command"]
|
178
247
|
end
|
179
248
|
|
180
|
-
def command=(v)
|
249
|
+
def command=(v) # :nodoc:
|
181
250
|
@manifest ||= {}
|
182
251
|
@manifest["staging"] ||= {}
|
183
252
|
@manifest["staging"]["command"] = v
|
184
253
|
end
|
185
254
|
|
186
|
-
|
255
|
+
|
256
|
+
def memory # :nodoc:
|
187
257
|
manifest["resources"]["memory"]
|
188
258
|
end
|
189
259
|
|
190
|
-
def memory=(v)
|
260
|
+
def memory=(v) # :nodoc:
|
191
261
|
@manifest ||= {}
|
192
262
|
@manifest["resources"] ||= {}
|
193
263
|
@manifest["resources"]["memory"] = v
|
194
264
|
end
|
195
265
|
|
196
|
-
|
197
|
-
|
266
|
+
|
267
|
+
def debug_mode # :nodoc:
|
268
|
+
manifest.fetch("debug") do
|
269
|
+
manifest["meta"] && manifest["meta"]["debug"]
|
270
|
+
end
|
198
271
|
end
|
199
272
|
|
200
|
-
def debug_mode=(v)
|
273
|
+
def debug_mode=(v) # :nodoc:
|
201
274
|
@manifest ||= {}
|
202
275
|
@manifest["debug"] = v
|
203
276
|
end
|
204
277
|
|
278
|
+
|
279
|
+
# Bind services to application.
|
205
280
|
def bind(*service_names)
|
206
281
|
update!("services" => services + service_names)
|
207
282
|
end
|
208
283
|
|
284
|
+
# Unbind services from application.
|
209
285
|
def unbind(*service_names)
|
210
286
|
update!("services" =>
|
211
287
|
services.reject { |s|
|
@@ -213,16 +289,46 @@ module CFoundry
|
|
213
289
|
})
|
214
290
|
end
|
215
291
|
|
292
|
+
# Retrieve file listing under path for the first instance of the application.
|
293
|
+
#
|
294
|
+
# [path]
|
295
|
+
# A sequence of strings representing path segments.
|
296
|
+
#
|
297
|
+
# For example, <code>files("foo", "bar")</code> for +foo/bar+.
|
216
298
|
def files(*path)
|
217
299
|
Instance.new(@name, 0, @client).files(*path)
|
218
300
|
end
|
219
301
|
|
302
|
+
# Retrieve file contents for the first instance of the application.
|
303
|
+
#
|
304
|
+
# [path]
|
305
|
+
# A sequence of strings representing path segments.
|
306
|
+
#
|
307
|
+
# For example, <code>files("foo", "bar")</code> for +foo/bar+.
|
220
308
|
def file(*path)
|
221
309
|
Instance.new(@name, 0, @client).file(*path)
|
222
310
|
end
|
223
311
|
|
312
|
+
# Default paths to exclude from upload payload.
|
313
|
+
#
|
314
|
+
# Value: .git, _darcs, .svn
|
224
315
|
UPLOAD_EXCLUDE = %w{.git _darcs .svn}
|
225
316
|
|
317
|
+
# Upload application's code to target. Do this after #create! and before
|
318
|
+
# #start!
|
319
|
+
#
|
320
|
+
# [path]
|
321
|
+
# A path pointing to either a directory, or a .jar, .war, or .zip
|
322
|
+
# file.
|
323
|
+
#
|
324
|
+
# If a .vmcignore file is detected under the given path, it will be used
|
325
|
+
# to exclude paths from the payload, similar to a .gitignore.
|
326
|
+
#
|
327
|
+
# [check_resources]
|
328
|
+
# If set to `false`, the entire payload will be uploaded
|
329
|
+
# without checking the resource cache.
|
330
|
+
#
|
331
|
+
# Only do this if you know what you're doing.
|
226
332
|
def upload(path, check_resources = true)
|
227
333
|
unless File.exist? path
|
228
334
|
raise "invalid application path '#{path}'"
|
@@ -248,6 +354,10 @@ module CFoundry
|
|
248
354
|
|
249
355
|
private
|
250
356
|
|
357
|
+
def manifest
|
358
|
+
@manifest ||= @client.rest.app(@name)
|
359
|
+
end
|
360
|
+
|
251
361
|
def prepare_package(path, to)
|
252
362
|
if path =~ /\.(jar|war|zip)$/
|
253
363
|
CFoundry::Zip.unpack(path, to)
|
@@ -291,6 +401,9 @@ module CFoundry
|
|
291
401
|
end
|
292
402
|
end
|
293
403
|
|
404
|
+
# Minimum size for an application payload to bother checking resources.
|
405
|
+
#
|
406
|
+
# Value: 64kb
|
294
407
|
RESOURCE_CHECK_LIMIT = 64 * 1024
|
295
408
|
|
296
409
|
def determine_resources(path)
|
@@ -349,9 +462,17 @@ module CFoundry
|
|
349
462
|
files && files.select { |f| File.socket? f }
|
350
463
|
end
|
351
464
|
|
465
|
+
# Class represnting a running instance of an application.
|
352
466
|
class Instance
|
353
|
-
|
467
|
+
# The application this instance belongs to.
|
468
|
+
attr_reader :app
|
354
469
|
|
470
|
+
# Application instance number.
|
471
|
+
attr_reader :index
|
472
|
+
|
473
|
+
# Create an Instance object.
|
474
|
+
#
|
475
|
+
# You'll usually call App#instances instead
|
355
476
|
def initialize(appname, index, client, manifest = {})
|
356
477
|
@app = appname
|
357
478
|
@index = index
|
@@ -359,33 +480,43 @@ module CFoundry
|
|
359
480
|
@manifest = manifest
|
360
481
|
end
|
361
482
|
|
362
|
-
def inspect
|
483
|
+
def inspect # :nodoc:
|
363
484
|
"#<App::Instance '#@app' \##@index>"
|
364
485
|
end
|
365
486
|
|
487
|
+
# Instance state.
|
366
488
|
def state
|
367
489
|
@manifest["state"]
|
368
490
|
end
|
369
491
|
alias_method :status, :state
|
370
492
|
|
493
|
+
# Instance start time.
|
371
494
|
def since
|
372
495
|
Time.at(@manifest["since"])
|
373
496
|
end
|
374
497
|
|
498
|
+
# Instance debugger data. If instance is in debug mode, returns a hash
|
499
|
+
# containing :ip and :port keys.
|
375
500
|
def debugger
|
376
501
|
return unless @manifest["debug_ip"] and @manifest["debug_port"]
|
377
|
-
|
378
|
-
|
502
|
+
|
503
|
+
{ :ip => @manifest["debug_ip"],
|
504
|
+
:port => @manifest["debug_port"]
|
379
505
|
}
|
380
506
|
end
|
381
507
|
|
508
|
+
# Instance console data. If instance has a console, returns a hash
|
509
|
+
# containing :ip and :port keys.
|
382
510
|
def console
|
383
511
|
return unless @manifest["console_ip"] and @manifest["console_port"]
|
384
|
-
|
385
|
-
|
512
|
+
|
513
|
+
{ :ip => @manifest["console_ip"],
|
514
|
+
:port => @manifest["console_port"]
|
386
515
|
}
|
387
516
|
end
|
388
517
|
|
518
|
+
# True if instance is starting or running, false if it's down or
|
519
|
+
# flapping.
|
389
520
|
def healthy?
|
390
521
|
case state
|
391
522
|
when "STARTING", "RUNNING"
|
@@ -395,12 +526,24 @@ module CFoundry
|
|
395
526
|
end
|
396
527
|
end
|
397
528
|
|
529
|
+
# Retrieve file listing under path for this instance.
|
530
|
+
#
|
531
|
+
# [path]
|
532
|
+
# A sequence of strings representing path segments.
|
533
|
+
#
|
534
|
+
# For example, <code>files("foo", "bar")</code> for +foo/bar+.
|
398
535
|
def files(*path)
|
399
536
|
@client.rest.files(@app, @index, *path).split("\n").collect do |entry|
|
400
537
|
path + [entry.split(/\s+/, 2)[0]]
|
401
538
|
end
|
402
539
|
end
|
403
540
|
|
541
|
+
# Retrieve file contents for this instance.
|
542
|
+
#
|
543
|
+
# [path]
|
544
|
+
# A sequence of strings representing path segments.
|
545
|
+
#
|
546
|
+
# For example, <code>files("foo", "bar")</code> for +foo/bar+.
|
404
547
|
def file(*path)
|
405
548
|
@client.rest.files(@app, @index, *path)
|
406
549
|
end
|
data/lib/cfoundry/client.rb
CHANGED
@@ -5,39 +5,53 @@ require "cfoundry/user"
|
|
5
5
|
|
6
6
|
|
7
7
|
module CFoundry
|
8
|
+
# The primary API entrypoint. Wraps RESTClient to provide nicer return
|
9
|
+
# values. Initialize with the target and, optionally, an auth token. These
|
10
|
+
# are the only two internal states.
|
8
11
|
class Client
|
9
|
-
attr_reader :rest
|
12
|
+
attr_reader :rest #:nodoc:
|
10
13
|
|
11
|
-
|
14
|
+
# Create a new Client for interfacing with the given target.
|
15
|
+
#
|
16
|
+
# A token may also be provided to skip the login step.
|
17
|
+
def initialize(target = "http://api.cloudfoundry.com", token = nil)
|
12
18
|
@rest = RESTClient.new(*args)
|
13
19
|
end
|
14
20
|
|
21
|
+
# The current target URL of the client.
|
15
22
|
def target
|
16
23
|
@rest.target
|
17
24
|
end
|
18
25
|
|
26
|
+
# Current proxy user. Usually nil.
|
19
27
|
def proxy
|
20
28
|
@rest.proxy
|
21
29
|
end
|
22
30
|
|
23
|
-
|
24
|
-
|
31
|
+
# Set the proxy user for the client. Must be authorized as an
|
32
|
+
# administrator for this to have any effect.
|
33
|
+
def proxy=(email)
|
34
|
+
@rest.proxy = email
|
25
35
|
end
|
26
36
|
|
37
|
+
# Is the client tracing API requests?
|
27
38
|
def trace
|
28
39
|
@rest.trace
|
29
40
|
end
|
30
41
|
|
31
|
-
|
32
|
-
|
42
|
+
# Set the tracing flag; if true, API requests and responses will be
|
43
|
+
# printed out.
|
44
|
+
def trace=(bool)
|
45
|
+
@rest.trace = bool
|
33
46
|
end
|
34
47
|
|
35
48
|
|
36
|
-
#
|
49
|
+
# Retrieve target metadata.
|
37
50
|
def info
|
38
51
|
@rest.info
|
39
52
|
end
|
40
53
|
|
54
|
+
# Retrieve available services. Returned as a Hash from vendor => metadata.
|
41
55
|
def system_services
|
42
56
|
services = {}
|
43
57
|
|
@@ -55,12 +69,13 @@ module CFoundry
|
|
55
69
|
services
|
56
70
|
end
|
57
71
|
|
72
|
+
# Retrieve available runtimes.
|
58
73
|
def system_runtimes
|
59
74
|
@rest.system_runtimes
|
60
75
|
end
|
61
76
|
|
62
77
|
|
63
|
-
#
|
78
|
+
# Retrieve user list. Admin-only.
|
64
79
|
def users
|
65
80
|
@rest.users.collect do |json|
|
66
81
|
CFoundry::User.new(
|
@@ -71,47 +86,67 @@ module CFoundry
|
|
71
86
|
end
|
72
87
|
end
|
73
88
|
|
89
|
+
# Construct a User object. The return value is lazy, and no requests are
|
90
|
+
# made from this alone.
|
91
|
+
#
|
92
|
+
# This should be used for both user creation (after calling User#create!)
|
93
|
+
# and retrieval.
|
74
94
|
def user(email)
|
75
95
|
CFoundry::User.new(email, self)
|
76
96
|
end
|
77
97
|
|
98
|
+
# Create a user on the target and return a User object representing them.
|
78
99
|
def register(email, password)
|
79
100
|
@rest.create_user(:email => email, :password => password)
|
80
101
|
user(email)
|
81
102
|
end
|
82
103
|
|
104
|
+
# Authenticate with the target. Sets the client token.
|
83
105
|
def login(email, password)
|
84
106
|
@rest.token =
|
85
107
|
@rest.create_token({ :password => password }, email)["token"]
|
86
108
|
end
|
87
109
|
|
110
|
+
# Clear client token. No requests are made for this.
|
88
111
|
def logout
|
89
112
|
@rest.token = nil
|
90
113
|
end
|
91
114
|
|
115
|
+
# Is an authentication token set on the client?
|
92
116
|
def logged_in?
|
93
117
|
!!@rest.token
|
94
118
|
end
|
95
119
|
|
96
120
|
|
97
|
-
#
|
121
|
+
# Retreive all of the current user's applications.
|
98
122
|
def apps
|
99
123
|
@rest.apps.collect do |json|
|
100
124
|
CFoundry::App.new(json["name"], self, json)
|
101
125
|
end
|
102
126
|
end
|
103
127
|
|
128
|
+
# Construct an App object. The return value is lazy, and no requests are
|
129
|
+
# made from this method alone.
|
130
|
+
#
|
131
|
+
# This should be used for both app creation (after calling App#create!)
|
132
|
+
# and retrieval.
|
104
133
|
def app(name)
|
105
134
|
CFoundry::App.new(name, self)
|
106
135
|
end
|
107
136
|
|
108
|
-
|
137
|
+
|
138
|
+
# Retrieve all of the current user's services.
|
109
139
|
def services
|
110
140
|
@rest.services.collect do |json|
|
111
141
|
CFoundry::Service.new(json["name"], self, json)
|
112
142
|
end
|
113
143
|
end
|
114
144
|
|
145
|
+
# Construct a Service object. The return value is lazy, and no requests are
|
146
|
+
# made from this method alone.
|
147
|
+
#
|
148
|
+
# This should be used for both service creation (after calling
|
149
|
+
# Service#create!) and retrieval.
|
115
150
|
def service(name)
|
116
151
|
CFoundry::Service.new(name, self)
|
117
152
|
end
|
data/lib/cfoundry/errors.rb
CHANGED
@@ -1,28 +1,32 @@
|
|
1
1
|
module CFoundry
|
2
|
+
# Exception representing errors returned by the API.
|
2
3
|
class APIError < RuntimeError
|
3
|
-
class << self
|
4
|
-
attr_reader :error_code, :description
|
4
|
+
class << self # :nodoc:
|
5
|
+
attr_reader :error_code, :description # :nodoc:
|
5
6
|
|
6
|
-
def setup(code, description = nil)
|
7
|
+
def setup(code, description = nil) # :nodoc:
|
7
8
|
@error_code = code
|
8
9
|
@description = description
|
9
10
|
end
|
10
11
|
end
|
11
12
|
|
13
|
+
# Create an APIError with a given error code and description.
|
12
14
|
def initialize(error_code = nil, description = nil)
|
13
15
|
@error_code = error_code
|
14
16
|
@description = description
|
15
17
|
end
|
16
18
|
|
19
|
+
# A number representing the error.
|
17
20
|
def error_code
|
18
21
|
@error_code || self.class.error_code
|
19
22
|
end
|
20
23
|
|
24
|
+
# A description of the error.
|
21
25
|
def description
|
22
26
|
@description || self.class.description
|
23
27
|
end
|
24
28
|
|
25
|
-
def to_s
|
29
|
+
def to_s # :nodoc:
|
26
30
|
if error_code
|
27
31
|
"#{error_code}: #{description}"
|
28
32
|
else
|
@@ -31,31 +35,40 @@ module CFoundry
|
|
31
35
|
end
|
32
36
|
end
|
33
37
|
|
38
|
+
# Generic exception thrown when accessing something that doesn't exist (e.g.
|
39
|
+
# getting info of unknown application).
|
34
40
|
class NotFound < APIError
|
35
41
|
setup(404, "entity not found or inaccessible")
|
36
42
|
end
|
37
43
|
|
44
|
+
# Lower-level exception for when we cannot connect to the target.
|
38
45
|
class TargetRefused < APIError
|
39
46
|
@description = "target refused connection"
|
40
47
|
|
48
|
+
# Error message.
|
41
49
|
attr_reader :message
|
42
50
|
|
51
|
+
# Message varies as this represents various network errors.
|
43
52
|
def initialize(message)
|
44
53
|
@message = message
|
45
54
|
end
|
46
55
|
|
47
|
-
def to_s
|
56
|
+
def to_s # :nodoc:
|
48
57
|
"#{description} (#{@message})"
|
49
58
|
end
|
50
59
|
end
|
51
60
|
|
61
|
+
# Exception raised when an application payload fails to upload.
|
52
62
|
class UploadFailed < APIError
|
53
63
|
setup(402)
|
54
64
|
end
|
55
65
|
|
66
|
+
# Exception raised when access is denied to something, either because the
|
67
|
+
# user is not logged in or is not an administrator.
|
56
68
|
class Denied < APIError
|
57
|
-
attr_reader :error_code, :description
|
69
|
+
attr_reader :error_code, :description # :nodoc:
|
58
70
|
|
71
|
+
# Initialize, with a default error code and message.
|
59
72
|
def initialize(
|
60
73
|
error_code = 200,
|
61
74
|
description = "Operation not permitted")
|
@@ -64,13 +77,16 @@ module CFoundry
|
|
64
77
|
end
|
65
78
|
end
|
66
79
|
|
80
|
+
# Exception raised when the response is unexpected; usually from a server
|
81
|
+
# error.
|
67
82
|
class BadResponse < StandardError
|
83
|
+
# Initialize, with the HTTP response code and body.
|
68
84
|
def initialize(code, body = nil)
|
69
85
|
@code = code
|
70
86
|
@body = body
|
71
87
|
end
|
72
88
|
|
73
|
-
def to_s
|
89
|
+
def to_s # :nodoc:
|
74
90
|
"target failed to handle our request due to an internal error (#{@code})"
|
75
91
|
end
|
76
92
|
end
|
data/lib/cfoundry/restclient.rb
CHANGED
data/lib/cfoundry/service.rb
CHANGED
@@ -1,29 +1,58 @@
|
|
1
1
|
module CFoundry
|
2
|
+
# Class for representing a user's service on a given target (via Client).
|
3
|
+
#
|
4
|
+
# Goes 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.
|
2
8
|
class Service
|
9
|
+
# Service name.
|
3
10
|
attr_reader :name
|
4
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.
|
5
33
|
def initialize(name, client, manifest = nil)
|
6
34
|
@name = name
|
7
35
|
@client = client
|
8
36
|
@manifest = manifest
|
9
37
|
end
|
10
38
|
|
11
|
-
def inspect
|
39
|
+
def inspect # :nodoc:
|
12
40
|
"#<Service '#@name'>"
|
13
41
|
end
|
14
42
|
|
15
|
-
|
16
|
-
@manifest ||= @client.rest.service(@name)
|
17
|
-
end
|
18
|
-
|
43
|
+
# Delete the service from the target.
|
19
44
|
def delete!
|
20
45
|
@client.rest.delete_service(@name)
|
21
46
|
end
|
22
47
|
|
48
|
+
# Create the service on the target.
|
49
|
+
#
|
50
|
+
# Call this after setting the various attributes.
|
23
51
|
def create!
|
24
52
|
@client.rest.create_service(@manifest.merge("name" => @name))
|
25
53
|
end
|
26
54
|
|
55
|
+
# Check if the service exists on the target.
|
27
56
|
def exists?
|
28
57
|
@client.rest.service(@name)
|
29
58
|
true
|
@@ -31,10 +60,12 @@ module CFoundry
|
|
31
60
|
false
|
32
61
|
end
|
33
62
|
|
63
|
+
# Timestamp of when the service was created.
|
34
64
|
def created
|
35
65
|
Time.at(meta["created"])
|
36
66
|
end
|
37
67
|
|
68
|
+
# Timestamp of when the service was last updated.
|
38
69
|
def updated
|
39
70
|
Time.at(meta["updated"])
|
40
71
|
end
|
@@ -55,5 +86,11 @@ module CFoundry
|
|
55
86
|
@manifest[attr] = v
|
56
87
|
end
|
57
88
|
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def manifest
|
93
|
+
@manifest ||= @client.rest.service(@name)
|
94
|
+
end
|
58
95
|
end
|
59
96
|
end
|
data/lib/cfoundry/user.rb
CHANGED
@@ -1,34 +1,46 @@
|
|
1
1
|
module CFoundry
|
2
|
+
# Class for representing a user on a given target (via Client).
|
3
|
+
#
|
4
|
+
# Goes 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.
|
2
7
|
class User
|
8
|
+
# User email.
|
3
9
|
attr_reader :email
|
4
10
|
|
11
|
+
|
12
|
+
# Create a User object.
|
13
|
+
#
|
14
|
+
# You'll usually call Client#user instead
|
5
15
|
def initialize(email, client, manifest = nil)
|
6
16
|
@email = email
|
7
17
|
@client = client
|
8
18
|
@manifest = manifest
|
9
19
|
end
|
10
20
|
|
11
|
-
def inspect
|
21
|
+
def inspect # :nodoc:
|
12
22
|
"#<User '#@email'>"
|
13
23
|
end
|
14
24
|
|
15
|
-
|
16
|
-
@manifest ||= @client.rest.user(@email)
|
17
|
-
end
|
18
|
-
|
25
|
+
# Delete the user from the target.
|
19
26
|
def delete!
|
20
27
|
@client.rest.delete_user(@email)
|
21
28
|
end
|
22
29
|
|
30
|
+
# Create the user on the target.
|
31
|
+
#
|
32
|
+
# Call this after setting the various attributes.
|
23
33
|
def create!
|
24
34
|
@client.rest.create_user(@manifest.merge("email" => @email))
|
25
35
|
end
|
26
36
|
|
37
|
+
# Update user attributes.
|
27
38
|
def update!(what = {})
|
28
39
|
@client.rest.update_user(@email, manifest.merge(what))
|
29
40
|
@manifest = nil
|
30
41
|
end
|
31
42
|
|
43
|
+
# Check if the user exists on the target.
|
32
44
|
def exists?
|
33
45
|
@client.rest.user(@email)
|
34
46
|
true
|
@@ -36,12 +48,22 @@ module CFoundry
|
|
36
48
|
false
|
37
49
|
end
|
38
50
|
|
51
|
+
# Check if the user is an administrator.
|
39
52
|
def admin?
|
40
53
|
manifest["admin"]
|
41
54
|
end
|
42
55
|
|
56
|
+
# Set the user's password.
|
57
|
+
#
|
58
|
+
# Call #update! after using this.
|
43
59
|
def password=(str)
|
44
60
|
manifest["password"] = str
|
45
61
|
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def manifest
|
66
|
+
@manifest ||= @client.rest.user(@email)
|
67
|
+
end
|
46
68
|
end
|
47
69
|
end
|
data/lib/cfoundry/version.rb
CHANGED
data/lib/cfoundry/zip.rb
CHANGED
@@ -1,11 +1,16 @@
|
|
1
|
-
require
|
1
|
+
require "zip/zipfilesystem"
|
2
2
|
|
3
3
|
module CFoundry
|
4
|
+
# Generic Zpi API. Uses rubyzip underneath, but may be changed in the future
|
5
|
+
# to use system zip command if necessary.
|
4
6
|
module Zip
|
7
|
+
# Directory entries to exclude from packing.
|
5
8
|
PACK_EXCLUSION_GLOBS = ['..', '.', '*~', '#*#', '*.log']
|
6
9
|
|
7
10
|
module_function
|
8
11
|
|
12
|
+
# Get the entries in the zip file. Returns an array of the entire
|
13
|
+
# contents, recursively (not just top-level).
|
9
14
|
def entry_lines(file)
|
10
15
|
entries = []
|
11
16
|
::Zip::ZipFile.foreach(file) do |zentry|
|
@@ -14,6 +19,7 @@ module CFoundry
|
|
14
19
|
entries
|
15
20
|
end
|
16
21
|
|
22
|
+
# Unpack a zip +file+ to directory +dest+.
|
17
23
|
def unpack(file, dest)
|
18
24
|
::Zip::ZipFile.foreach(file) do |zentry|
|
19
25
|
epath = "#{dest}/#{zentry}"
|
@@ -23,6 +29,7 @@ module CFoundry
|
|
23
29
|
end
|
24
30
|
end
|
25
31
|
|
32
|
+
# Determine what files in +dir+ to pack.
|
26
33
|
def files_to_pack(dir)
|
27
34
|
Dir.glob("#{dir}/**/*", File::FNM_DOTMATCH).select do |f|
|
28
35
|
File.exists?(f) &&
|
@@ -32,6 +39,7 @@ module CFoundry
|
|
32
39
|
end
|
33
40
|
end
|
34
41
|
|
42
|
+
# Package directory +dir+ as file +zipfile+.
|
35
43
|
def pack(dir, zipfile)
|
36
44
|
files = files_to_pack(dir)
|
37
45
|
return false if files.empty?
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cfoundry
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 21
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 2
|
9
|
-
-
|
10
|
-
version: 0.2.
|
9
|
+
- 1
|
10
|
+
version: 0.2.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Alex Suraci
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-
|
18
|
+
date: 2012-06-02 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: rest-client
|