manifests-vmc-plugin 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+