manifests-vmc-plugin 0.1.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/LICENSE ADDED
@@ -0,0 +1,30 @@
1
+ Copyright (c)2012, Alex Suraci
2
+
3
+ All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ * Redistributions of source code must retain the above copyright
9
+ notice, this list of conditions and the following disclaimer.
10
+
11
+ * Redistributions in binary form must reproduce the above
12
+ copyright notice, this list of conditions and the following
13
+ disclaimer in the documentation and/or other materials provided
14
+ with the distribution.
15
+
16
+ * Neither the name of Alex Suraci nor the names of other
17
+ contributors may be used to endorse or promote products derived
18
+ from this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,410 @@
1
+ require "yaml"
2
+ require "set"
3
+
4
+ module VMCManifests
5
+ MANIFEST_FILE = "manifest.yml"
6
+
7
+ def manifest
8
+ return @manifest if @manifest
9
+
10
+ if manifest_file && File.exists?(manifest_file)
11
+ @manifest = load_manifest(manifest_file)
12
+ end
13
+ end
14
+
15
+ def save_manifest(save_to = manifest_file)
16
+ err "No manifest to save!" unless @manifest
17
+
18
+ File.open(save_to, "w") do |io|
19
+ YAML.dump(@manifest, io)
20
+ end
21
+ end
22
+
23
+ # find the manifest file to work with
24
+ def manifest_file
25
+ return options[:manifest] if options[:manifest]
26
+ return @manifest_file if @manifest_file
27
+
28
+ where = Dir.pwd
29
+ while true
30
+ if File.exists?(File.join(where, MANIFEST_FILE))
31
+ @manifest_file = File.join(where, MANIFEST_FILE)
32
+ break
33
+ elsif File.basename(where) == "/"
34
+ @manifest_file = nil
35
+ break
36
+ else
37
+ where = File.expand_path("../", where)
38
+ end
39
+ end
40
+
41
+ @manifest_file
42
+ end
43
+
44
+ # load and resolve a given manifest file
45
+ def load_manifest(file)
46
+ manifest = build_manifest(file)
47
+ resolve_manifest(manifest)
48
+ manifest
49
+ end
50
+
51
+ # parse a manifest and merge with its inherited manifests
52
+ def build_manifest(file)
53
+ manifest = YAML.load_file file
54
+
55
+ Array(manifest["inherit"]).each do |p|
56
+ manifest = merge_parent(manifest, p)
57
+ end
58
+
59
+ manifest
60
+ end
61
+
62
+ # merge the manifest at `path' into the `child'
63
+ def merge_parent(child, path)
64
+ file = File.expand_path("../" + path, manifest_file)
65
+ merge_manifest(child, build_manifest(file))
66
+ end
67
+
68
+ # deep hash merge
69
+ def merge_manifest(child, parent)
70
+ merge = proc do |_, old, new|
71
+ if new.is_a?(Hash) and old.is_a?(Hash)
72
+ old.merge(new, &merge)
73
+ else
74
+ new
75
+ end
76
+ end
77
+
78
+ parent.merge(child, &merge)
79
+ end
80
+
81
+ # resolve symbols in a manifest
82
+ def resolve_manifest(manifest)
83
+ if apps = manifest["applications"]
84
+ apps.each_value do |v|
85
+ resolve_lexically(v, [manifest])
86
+ end
87
+ end
88
+
89
+ resolve_lexically(manifest, [manifest])
90
+
91
+ nil
92
+ end
93
+
94
+ # resolve symbols, with hashes introducing new lexical symbols
95
+ def resolve_lexically(val, ctx)
96
+ case val
97
+ when Hash
98
+ val.each_value do |v|
99
+ resolve_lexically(v, [val] + ctx)
100
+ end
101
+ when Array
102
+ val.each do |v|
103
+ resolve_lexically(v, ctx)
104
+ end
105
+ when String
106
+ val.gsub!(/\$\{([[:alnum:]\-]+)\}/) do
107
+ resolve_symbol($1, ctx)
108
+ end
109
+ end
110
+
111
+ nil
112
+ end
113
+
114
+ # resolve a symbol to its value, and then resolve that value
115
+ def resolve_symbol(sym, ctx)
116
+ case sym
117
+ when "target-url"
118
+ target_url(ctx)
119
+
120
+ when "target-base"
121
+ target_url(ctx).sub(/^[^\.]+\./, "")
122
+
123
+ when "random-word"
124
+ "%04x" % [rand(0x0100000)]
125
+
126
+ else
127
+ found = find_symbol(sym, ctx)
128
+
129
+ if found
130
+ resolve_lexically(found, ctx)
131
+ found
132
+ else
133
+ err("Unknown symbol in manifest: #{sym}")
134
+ end
135
+ end
136
+ end
137
+
138
+ # get the target url from either the manifest or the current client
139
+ def target_url(ctx = [])
140
+ find_symbol("target", ctx) || client_target
141
+ end
142
+
143
+ # search for a symbol introduced in the lexical context
144
+ def find_symbol(sym, ctx)
145
+ ctx.each do |h|
146
+ if val = resolve_in(h, sym)
147
+ return val
148
+ end
149
+ end
150
+
151
+ nil
152
+ end
153
+
154
+ # find a value, searching in explicit properties first
155
+ def resolve_in(hash, *where)
156
+ find_in_hash(hash, ["properties"] + where) ||
157
+ find_in_hash(hash, where)
158
+ end
159
+
160
+ # helper for following a path of values in a hash
161
+ def find_in_hash(hash, where)
162
+ what = hash
163
+ where.each do |x|
164
+ return nil unless what.is_a?(Hash)
165
+ what = what[x]
166
+ end
167
+
168
+ what
169
+ end
170
+
171
+ MANIFEST_META = ["applications", "properties"]
172
+
173
+ def toplevel_attributes
174
+ if m = manifest
175
+ info =
176
+ m.reject do |k, _|
177
+ MANIFEST_META.include? k
178
+ end
179
+
180
+ if info["framework"].is_a?(Hash)
181
+ info["framework"] = info["framework"]["name"]
182
+ end
183
+
184
+ info
185
+ end
186
+ end
187
+
188
+ def app_info(find_path)
189
+ return unless manifest and manifest["applications"]
190
+
191
+ manifest["applications"].each do |path, info|
192
+ if info["framework"].is_a?(Hash)
193
+ info["framework"] = info["framework"]["name"]
194
+ end
195
+
196
+ app = File.expand_path("../" + path, manifest_file)
197
+ if find_path == app
198
+ return toplevel_attributes.merge info
199
+ end
200
+ end
201
+
202
+ nil
203
+ end
204
+
205
+ # call a block for each app in a manifest (in dependency order), setting
206
+ # inputs for each app
207
+ def each_app
208
+ given_path = passed_value(:path)
209
+
210
+ if manifest and all_apps = manifest["applications"]
211
+ # given a specific application
212
+ if given_path
213
+ full_path = File.expand_path(given_path)
214
+
215
+ if info = app_info(full_path)
216
+ with_app(full_path, info) do
217
+ yield info
218
+ end
219
+ else
220
+ raise "Path #{given_path} is not described by the manifest."
221
+ end
222
+ else
223
+ # all apps in the manifest
224
+ ordered_by_deps(all_apps).each do |path|
225
+ app = File.expand_path("../" + path, manifest_file)
226
+ info = app_info(app)
227
+
228
+ with_app(app, info) do
229
+ yield info
230
+ end
231
+ end
232
+ end
233
+
234
+ true
235
+
236
+ # manually created or legacy single-app manifest
237
+ elsif single = toplevel_attributes
238
+ with_app(full_path || ".", single) do
239
+ yield single
240
+ end
241
+
242
+ true
243
+
244
+ else
245
+ false
246
+ end
247
+ end
248
+
249
+ private
250
+
251
+ def inputs
252
+ @inputs ||= {}
253
+ end
254
+
255
+ # call the block as if the app info and path were given as flags
256
+ def with_app(path, info)
257
+ before_path = inputs[:path]
258
+ before_info = {}
259
+
260
+ inputs[:path] = path
261
+
262
+ info.each do |k, v|
263
+ if k == "mem"
264
+ k = "memory"
265
+ end
266
+
267
+ before_info[k.to_sym] = inputs[k.to_sym]
268
+ inputs[k.to_sym] = v
269
+ end
270
+
271
+ yield
272
+ ensure
273
+ if before_path.nil?
274
+ inputs.delete :path
275
+ else
276
+ inputs[:path] = before_path
277
+ end
278
+
279
+ before_info.each do |k, v|
280
+ if v.nil?
281
+ inputs.delete k
282
+ else
283
+ inputs[k] = v
284
+ end
285
+ end
286
+ end
287
+
288
+ # sort applications in dependency order
289
+ # e.g. if A depends on B, B will be listed before A
290
+ def ordered_by_deps(apps, abspaths = nil, processed = Set[])
291
+ unless abspaths
292
+ abspaths = {}
293
+ apps.each do |p, i|
294
+ ep = File.expand_path("../" + p, manifest_file)
295
+ abspaths[ep] = i
296
+ end
297
+ end
298
+
299
+ ordered = []
300
+ apps.each do |path, info|
301
+ epath = File.expand_path("../" + path, manifest_file)
302
+
303
+ if deps = info["depends-on"]
304
+ dep_apps = {}
305
+ deps.each do |dep|
306
+ edep = File.expand_path("../" + dep, manifest_file)
307
+
308
+ err "Circular dependency detected." if processed.include? edep
309
+
310
+ dep_apps[dep] = abspaths[edep]
311
+ end
312
+
313
+ processed.add(epath)
314
+
315
+ ordered += ordered_by_deps(dep_apps, abspaths, processed)
316
+ ordered << path
317
+ elsif not processed.include? epath
318
+ ordered << path
319
+ processed.add(epath)
320
+ end
321
+ end
322
+
323
+ ordered
324
+ end
325
+
326
+ # detect changes in app info, and update the app if necessary.
327
+ #
328
+ # redeploys the app if necessary (after prompting the user), e.g. for
329
+ # runtime/framework change
330
+ def sync_changes(info)
331
+ app = client.app(info["name"])
332
+ return unless app.exists?
333
+
334
+ diff = {}
335
+ need_restage = []
336
+ info.each do |k, v|
337
+ case k
338
+ when /ur[li]s?/
339
+ old = app.urls
340
+ if old != Array(v)
341
+ diff[k] = [old, v]
342
+ app.urls = Array(v)
343
+ end
344
+ when "env"
345
+ old = app.env
346
+ if old != v
347
+ diff[k] = [old, v]
348
+ app.env = v
349
+ end
350
+ when "framework", "runtime"
351
+ old = app.send(k)
352
+ if old != v
353
+ diff[k] = [old, v]
354
+ app.send(:"#{k}=", v)
355
+ need_restage << k
356
+ end
357
+ when "instances"
358
+ old = app.total_instances
359
+ if old != v
360
+ diff[k] = [old, v]
361
+ app.total_instances = v
362
+ end
363
+ when "mem", "memory"
364
+ old = app.memory
365
+ new = megabytes(v)
366
+ if old != new
367
+ diff["memory"] = [old, new]
368
+ app.memory = new
369
+ end
370
+ end
371
+ end
372
+
373
+ return if diff.empty?
374
+
375
+ unless simple_output?
376
+ puts "Detected the following changes to #{c(app.name, :blue)}:"
377
+ diff.each do |k, d|
378
+ old, new = d
379
+ label = c(k, need_restage.include?(k) ? :red : :green)
380
+ puts " #{label}: #{old.inspect} #{c("->", :black)} #{new.inspect}"
381
+ end
382
+
383
+ puts ""
384
+ end
385
+
386
+ if need_restage.empty?
387
+ with_progress("Updating #{c(app.name, :blue)}") do
388
+ app.update!
389
+ end
390
+ else
391
+ unless simple_output?
392
+ puts "The following changes require the app to be recreated:"
393
+ need_restage.each do |n|
394
+ puts " #{c(n, :magenta)}"
395
+ end
396
+ puts ""
397
+ end
398
+
399
+ if force? || ask("Redeploy?", :default => false)
400
+ with_progress("Deleting #{c(app.name, :blue)}") do
401
+ app.delete!
402
+ end
403
+
404
+ with_progress("Recreating #{c(app.name, :blue)}") do
405
+ app.create!
406
+ end
407
+ end
408
+ end
409
+ end
410
+ end
@@ -0,0 +1,21 @@
1
+ module VMCManifests
2
+ class CircularDependency < RuntimeError
3
+ def initialize(app)
4
+ @app = app
5
+ end
6
+
7
+ def to_s
8
+ "Circular dependency in application '#@app'"
9
+ end
10
+ end
11
+
12
+ class UnknownSymbol < RuntimeError
13
+ def initialize(sym)
14
+ @sym = sym
15
+ end
16
+
17
+ def to_s
18
+ "Undefined symbol in manifest: '#@sym'"
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,222 @@
1
+ require "yaml"
2
+ require "set"
3
+
4
+ module VMCManifests
5
+ class Manifest
6
+ def initialize(file)
7
+ @file = file
8
+ end
9
+
10
+ def body
11
+ @body ||= load
12
+ end
13
+
14
+ # load and resolve a given manifest file
15
+ def load
16
+ manifest = build_manifest(@file)
17
+ resolve_manifest(manifest)
18
+ manifest
19
+ end
20
+
21
+ def save(dest = @file)
22
+ File.open(save_to, "w") do |io|
23
+ YAML.dump(@body, io)
24
+ end
25
+ end
26
+
27
+ MANIFEST_META = ["applications", "properties"]
28
+
29
+ def toplevel_attributes
30
+ info =
31
+ body.reject do |k, _|
32
+ MANIFEST_META.include? k
33
+ end
34
+
35
+ if info["framework"].is_a?(Hash)
36
+ info["framework"] = info["framework"]["name"]
37
+ end
38
+
39
+ info
40
+ end
41
+
42
+ def app_info(find_path)
43
+ return unless body["applications"]
44
+
45
+ body["applications"].each do |path, info|
46
+ if info["framework"].is_a?(Hash)
47
+ info["framework"] = info["framework"]["name"]
48
+ end
49
+
50
+ app = File.expand_path("../" + path, manifest_file)
51
+ if find_path == app
52
+ return toplevel_attributes.merge info
53
+ end
54
+ end
55
+
56
+ nil
57
+ end
58
+
59
+ # sort applications in dependency order
60
+ # e.g. if A depends on B, B will be listed before A
61
+ def applications(
62
+ apps = body["applications"],
63
+ abspaths = nil,
64
+ processed = Set[])
65
+ unless abspaths
66
+ abspaths = {}
67
+ apps.each do |p, i|
68
+ ep = File.expand_path("../" + p, manifest_file)
69
+ abspaths[ep] = i
70
+ end
71
+ end
72
+
73
+ ordered = []
74
+ apps.each do |path, info|
75
+ epath = File.expand_path("../" + path, manifest_file)
76
+
77
+ if deps = info["depends-on"]
78
+ dep_apps = {}
79
+ deps.each do |dep|
80
+ edep = File.expand_path("../" + dep, manifest_file)
81
+
82
+ raise CircularDependency.new(edep) if processed.include?(edep)
83
+
84
+ dep_apps[dep] = abspaths[edep]
85
+ end
86
+
87
+ processed.add(epath)
88
+
89
+ ordered += applications(dep_apps, abspaths, processed)
90
+ ordered << path
91
+ elsif not processed.include? epath
92
+ ordered << path
93
+ processed.add(epath)
94
+ end
95
+ end
96
+
97
+ ordered
98
+ end
99
+
100
+ private
101
+
102
+ # parse a manifest and merge with its inherited manifests
103
+ def build_manifest(file)
104
+ manifest = YAML.load_file file
105
+
106
+ Array(manifest["inherit"]).each do |p|
107
+ manifest = merge_parent(manifest, p)
108
+ end
109
+
110
+ manifest
111
+ end
112
+
113
+ # merge the manifest at `path' into the `child'
114
+ def merge_parent(child, path)
115
+ file = File.expand_path("../" + path, @file)
116
+ deep_merge(child, build_manifest(file))
117
+ end
118
+
119
+ # resolve symbols in a manifest
120
+ def resolve_manifest(manifest)
121
+ if apps = manifest["applications"]
122
+ apps.each_value do |v|
123
+ resolve_lexically(v, [manifest])
124
+ end
125
+ end
126
+
127
+ resolve_lexically(manifest, [manifest])
128
+
129
+ nil
130
+ end
131
+
132
+ # resolve symbols, with hashes introducing new lexical symbols
133
+ def resolve_lexically(val, ctx)
134
+ case val
135
+ when Hash
136
+ val.each_value do |v|
137
+ resolve_lexically(v, [val] + ctx)
138
+ end
139
+ when Array
140
+ val.each do |v|
141
+ resolve_lexically(v, ctx)
142
+ end
143
+ when String
144
+ val.gsub!(/\$\{([[:alnum:]\-]+)\}/) do
145
+ resolve_symbol($1, ctx)
146
+ end
147
+ end
148
+
149
+ nil
150
+ end
151
+
152
+ # resolve a symbol to its value, and then resolve that value
153
+ def resolve_symbol(sym, ctx)
154
+ case sym
155
+ when "target-url"
156
+ target_url(ctx)
157
+
158
+ when "target-base"
159
+ target_url(ctx).sub(/^[^\.]+\./, "")
160
+
161
+ when "random-word"
162
+ "%04x" % [rand(0x0100000)]
163
+
164
+ else
165
+ found = find_symbol(sym, ctx)
166
+
167
+ if found
168
+ resolve_lexically(found, ctx)
169
+ found
170
+ else
171
+ raise UnknownSymbol.new(sym)
172
+ end
173
+ end
174
+ end
175
+
176
+ # get the target url from either the manifest or the current client
177
+ def target_url(ctx = [])
178
+ find_symbol("target", ctx) || client_target
179
+ end
180
+
181
+ # search for a symbol introduced in the lexical context
182
+ def find_symbol(sym, ctx)
183
+ ctx.each do |h|
184
+ if val = resolve_in(h, sym)
185
+ return val
186
+ end
187
+ end
188
+
189
+ nil
190
+ end
191
+
192
+ # find a value, searching in explicit properties first
193
+ def resolve_in(hash, *where)
194
+ find_in_hash(hash, ["properties"] + where) ||
195
+ find_in_hash(hash, where)
196
+ end
197
+
198
+ # helper for following a path of values in a hash
199
+ def find_in_hash(hash, where)
200
+ what = hash
201
+ where.each do |x|
202
+ return nil unless what.is_a?(Hash)
203
+ what = what[x]
204
+ end
205
+
206
+ what
207
+ end
208
+
209
+ # deep hash merge
210
+ def deep_merge(child, parent)
211
+ merge = proc do |_, old, new|
212
+ if new.is_a?(Hash) and old.is_a?(Hash)
213
+ old.merge(new, &merge)
214
+ else
215
+ new
216
+ end
217
+ end
218
+
219
+ parent.merge(child, &merge)
220
+ end
221
+ end
222
+ end
@@ -0,0 +1,112 @@
1
+ require "cf/plugin"
2
+ require File.expand_path("../../manifests-vmc-plugin", __FILE__)
3
+
4
+ CF.Plugin do
5
+ class_option :manifest,
6
+ :aliases => "-m", :desc => "Manifest file"
7
+
8
+ class_option :path,
9
+ :aliases => "-p", :desc => "Application path"
10
+ end
11
+
12
+ CF.Plugin(CF::App) do
13
+ include VMCManifests
14
+
15
+ # basic commands that, when given no args, act on the
16
+ # app(s) described by the manifest, in dependency-order
17
+ [:start, :instances, :logs].each do |wrap|
18
+ around(wrap) do |cmd, args|
19
+ if args.empty?
20
+ each_app do |a|
21
+ cmd.call(a["name"])
22
+ puts "" unless simple_output?
23
+ end || err("No applications to act on.")
24
+ else
25
+ cmd.call(args)
26
+ end
27
+ end
28
+ end
29
+
30
+ # same as above but in reverse dependency-order
31
+ [:stop, :delete].each do |wrap|
32
+ around(wrap) do |cmd, args|
33
+ if args.empty?
34
+ reversed = []
35
+ each_app do |a|
36
+ reversed.unshift a["name"]
37
+ end || err("No applications to act on.")
38
+
39
+ reversed.each do |name|
40
+ cmd.call(name)
41
+ puts "" unless simple_output?
42
+ end
43
+ else
44
+ cmd.call(args)
45
+ end
46
+ end
47
+ end
48
+
49
+ # stop apps in reverse dependency order,
50
+ # and then start in dependency order
51
+ around(:restart) do |cmd, args|
52
+ if args.empty?
53
+ reversed = []
54
+ forwards = []
55
+ each_app do |a|
56
+ reversed.unshift a["name"]
57
+ forwards << a["name"]
58
+ end || err("No applications to act on.")
59
+
60
+ reversed.each do |name|
61
+ stop(name)
62
+ end
63
+
64
+ puts "" unless simple_output?
65
+
66
+ forwards.each do |name|
67
+ start(name)
68
+ end
69
+ else
70
+ cmd.call(args)
71
+ end
72
+ end
73
+
74
+ # push and sync meta changes in the manifest
75
+ # also sets env data on creation if present in manifest
76
+ around(:push) do |push, args|
77
+ if args.empty?
78
+ all_pushed =
79
+ each_app do |a|
80
+ app = client.app(a["name"])
81
+ updating = app.exists?
82
+
83
+ start = input(:start)
84
+
85
+ begin
86
+ inputs[:start] = false
87
+
88
+ sync_changes(a)
89
+ push.call(a["name"])
90
+
91
+ unless updating
92
+ app.env = a["env"]
93
+
94
+ if start
95
+ start(a["name"])
96
+ else
97
+ app.update!
98
+ end
99
+ end
100
+ ensure
101
+ inputs[:start] = start
102
+ end
103
+
104
+ puts "" unless simple_output?
105
+ end
106
+
107
+ push.call unless all_pushed
108
+ else
109
+ push.call(args)
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,3 @@
1
+ module VMCManifests
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: manifests-vmc-plugin
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Alex Suraci
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-05-01 00:00:00 Z
19
+ dependencies: []
20
+
21
+ description:
22
+ email:
23
+ - asuraci@vmware.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - LICENSE
32
+ - Rakefile
33
+ - lib/manifests-vmc-plugin/errors.rb
34
+ - lib/manifests-vmc-plugin/manifest.rb
35
+ - lib/manifests-vmc-plugin/plugin.rb
36
+ - lib/manifests-vmc-plugin/version.rb
37
+ - lib/manifests-vmc-plugin.rb
38
+ homepage: http://cloudfoundry.com/
39
+ licenses: []
40
+
41
+ post_install_message:
42
+ rdoc_options: []
43
+
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ hash: 3
52
+ segments:
53
+ - 0
54
+ version: "0"
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ hash: 3
61
+ segments:
62
+ - 0
63
+ version: "0"
64
+ requirements: []
65
+
66
+ rubyforge_project: manifests-vmc-plugin
67
+ rubygems_version: 1.8.23
68
+ signing_key:
69
+ specification_version: 3
70
+ summary: Cloud Foundry automation via manifest documents.
71
+ test_files: []
72
+