cfoundry-IronFoundry 0.3.34
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/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
|