manifests-vmc-plugin 0.4.13 → 0.4.14
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/lib/manifests-vmc-plugin/loader/builder.rb +34 -0
- data/lib/manifests-vmc-plugin/loader/normalizer.rb +112 -0
- data/lib/manifests-vmc-plugin/loader/resolver.rb +75 -0
- data/lib/manifests-vmc-plugin/loader.rb +32 -0
- data/lib/manifests-vmc-plugin/plugin.rb +11 -23
- data/lib/manifests-vmc-plugin/version.rb +1 -1
- data/lib/manifests-vmc-plugin.rb +82 -269
- data/spec/normalizer_spec.rb +125 -0
- data/spec/spec_helper.rb +2 -0
- metadata +58 -6
@@ -0,0 +1,34 @@
|
|
1
|
+
module VMCManifests
|
2
|
+
module Builder
|
3
|
+
# parse a manifest and merge with its inherited manifests
|
4
|
+
def build(file)
|
5
|
+
manifest = YAML.load_file file
|
6
|
+
|
7
|
+
Array(manifest["inherit"]).each do |path|
|
8
|
+
manifest = merge_parent(path, manifest)
|
9
|
+
end
|
10
|
+
|
11
|
+
manifest
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
# merge the manifest at `parent_path' into the `child'
|
17
|
+
def merge_parent(parent_path, child)
|
18
|
+
merge_manifest(build(from_manifest(parent_path)), child)
|
19
|
+
end
|
20
|
+
|
21
|
+
# deep hash merge
|
22
|
+
def merge_manifest(parent, child)
|
23
|
+
merge = proc do |_, old, new|
|
24
|
+
if new.is_a?(Hash) && old.is_a?(Hash)
|
25
|
+
old.merge(new, &merge)
|
26
|
+
else
|
27
|
+
new
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
parent.merge(child, &merge)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module VMCManifests
|
2
|
+
module Normalizer
|
3
|
+
MANIFEST_META = ["applications", "properties"]
|
4
|
+
|
5
|
+
def normalize!(manifest)
|
6
|
+
toplevel = toplevel_attributes(manifest)
|
7
|
+
|
8
|
+
apps = manifest["applications"]
|
9
|
+
apps ||= [{}]
|
10
|
+
|
11
|
+
default_paths_to_keys!(apps)
|
12
|
+
|
13
|
+
apps = convert_from_array(apps)
|
14
|
+
|
15
|
+
merge_toplevel!(toplevel, manifest, apps)
|
16
|
+
normalize_apps!(apps)
|
17
|
+
|
18
|
+
manifest["applications"] = apps
|
19
|
+
|
20
|
+
normalize_paths!(apps)
|
21
|
+
|
22
|
+
keyval = normalize_key_val(manifest)
|
23
|
+
manifest.clear.merge!(keyval)
|
24
|
+
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def normalize_paths!(apps)
|
31
|
+
apps.each do |_, app|
|
32
|
+
app["path"] = from_manifest(app["path"])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def convert_from_array(apps)
|
37
|
+
return apps unless apps.is_a?(Array)
|
38
|
+
|
39
|
+
apps_hash = {}
|
40
|
+
apps.each.with_index do |a, i|
|
41
|
+
apps_hash[i.to_s] = a
|
42
|
+
end
|
43
|
+
|
44
|
+
apps_hash
|
45
|
+
end
|
46
|
+
|
47
|
+
def default_paths_to_keys!(apps)
|
48
|
+
return if apps.is_a?(Array)
|
49
|
+
|
50
|
+
apps.each do |tag, app|
|
51
|
+
app["path"] ||= tag
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def normalize_apps!(apps)
|
56
|
+
apps.each_value do |app|
|
57
|
+
normalize_app!(app)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def merge_toplevel!(toplevel, manifest, apps)
|
62
|
+
return if toplevel.empty?
|
63
|
+
|
64
|
+
apps.each do |t, a|
|
65
|
+
apps[t] = toplevel.merge(a)
|
66
|
+
end
|
67
|
+
|
68
|
+
toplevel.each do |k, _|
|
69
|
+
manifest.delete k
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def normalize_app!(app)
|
74
|
+
if app["framework"].is_a?(Hash)
|
75
|
+
app["framework"] = app["framework"]["name"]
|
76
|
+
end
|
77
|
+
|
78
|
+
if app.key?("mem")
|
79
|
+
app["memory"] = app.delete("mem")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def toplevel_attributes(manifest)
|
84
|
+
top =
|
85
|
+
manifest.reject { |k, _|
|
86
|
+
MANIFEST_META.include? k
|
87
|
+
}
|
88
|
+
|
89
|
+
# implicit toplevel path of .
|
90
|
+
top["path"] ||= "."
|
91
|
+
|
92
|
+
top
|
93
|
+
end
|
94
|
+
|
95
|
+
def normalize_key_val(val)
|
96
|
+
case val
|
97
|
+
when Hash
|
98
|
+
stringified = {}
|
99
|
+
|
100
|
+
val.each do |k, v|
|
101
|
+
stringified[k.to_sym] = normalize_key_val(v)
|
102
|
+
end
|
103
|
+
|
104
|
+
stringified
|
105
|
+
when Array
|
106
|
+
val.collect { |x| normalize_key_val(x) }
|
107
|
+
else
|
108
|
+
val.to_s
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module VMCManifests
|
2
|
+
module Resolver
|
3
|
+
def resolve!(manifest, resolver)
|
4
|
+
manifest[:applications].each_value do |v|
|
5
|
+
resolve_lexically(resolver, v, [manifest])
|
6
|
+
end
|
7
|
+
|
8
|
+
resolve_lexically(resolver, manifest, [manifest])
|
9
|
+
|
10
|
+
nil
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
# resolve symbols, with hashes introducing new lexical symbols
|
16
|
+
def resolve_lexically(resolver, val, ctx)
|
17
|
+
case val
|
18
|
+
when Hash
|
19
|
+
val.each_value do |v|
|
20
|
+
resolve_lexically(resolver, v, [val] + ctx)
|
21
|
+
end
|
22
|
+
when Array
|
23
|
+
val.each do |v|
|
24
|
+
resolve_lexically(resolver, v, ctx)
|
25
|
+
end
|
26
|
+
when String
|
27
|
+
val.gsub!(/\$\{([^\}]+)\}/) do
|
28
|
+
resolve(resolver, $1, ctx)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
|
35
|
+
# resolve a symbol to its value, and then resolve that value
|
36
|
+
def resolve(resolver, sym, ctx)
|
37
|
+
if found = find_symbol(sym.to_sym, ctx)
|
38
|
+
resolve_lexically(resolver, found, ctx)
|
39
|
+
found
|
40
|
+
elsif dynamic = resolver.resolve_symbol(sym)
|
41
|
+
dynamic
|
42
|
+
else
|
43
|
+
fail("Unknown symbol in manifest: #{sym}")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# search for a symbol introduced in the lexical context
|
48
|
+
def find_symbol(sym, ctx)
|
49
|
+
ctx.each do |h|
|
50
|
+
if val = resolve_in(h, sym)
|
51
|
+
return val
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
nil
|
56
|
+
end
|
57
|
+
|
58
|
+
# find a value, searching in explicit properties first
|
59
|
+
def resolve_in(hash, *where)
|
60
|
+
find_in_hash(hash, [:properties] + where) ||
|
61
|
+
find_in_hash(hash, where)
|
62
|
+
end
|
63
|
+
|
64
|
+
# helper for following a path of values in a hash
|
65
|
+
def find_in_hash(hash, where)
|
66
|
+
what = hash
|
67
|
+
where.each do |x|
|
68
|
+
return nil unless what.is_a?(Hash)
|
69
|
+
what = what[x]
|
70
|
+
end
|
71
|
+
|
72
|
+
what
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "manifests-vmc-plugin/loader/builder"
|
2
|
+
require "manifests-vmc-plugin/loader/normalizer"
|
3
|
+
require "manifests-vmc-plugin/loader/resolver"
|
4
|
+
|
5
|
+
module VMCManifests
|
6
|
+
class Loader
|
7
|
+
include Builder
|
8
|
+
include Normalizer
|
9
|
+
include Resolver
|
10
|
+
|
11
|
+
def initialize(file, resolver)
|
12
|
+
@file = file
|
13
|
+
@resolver = resolver
|
14
|
+
end
|
15
|
+
|
16
|
+
def manifest
|
17
|
+
info = build(@file)
|
18
|
+
normalize! info
|
19
|
+
resolve! info, @resolver
|
20
|
+
info
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
# expand a path relative to the manifest file's directory
|
26
|
+
def from_manifest(path)
|
27
|
+
return path unless @file
|
28
|
+
|
29
|
+
File.expand_path(path, File.dirname(@file))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -4,29 +4,13 @@ require "vmc/plugin"
|
|
4
4
|
require "manifests-vmc-plugin"
|
5
5
|
|
6
6
|
|
7
|
-
class Manifests < VMC::
|
7
|
+
class Manifests < VMC::App::Base
|
8
8
|
include VMCManifests
|
9
9
|
|
10
|
-
@@showed_manifest_usage = false
|
11
|
-
|
12
10
|
option :manifest, :aliases => "-m", :value => :file,
|
13
11
|
:desc => "Path to manifest file to use"
|
14
12
|
|
15
13
|
|
16
|
-
def no_apps
|
17
|
-
fail "No applications or manifest to operate on."
|
18
|
-
end
|
19
|
-
|
20
|
-
def show_manifest_usage
|
21
|
-
return if @@showed_manifest_usage
|
22
|
-
|
23
|
-
path = Pathname.new(manifest_file).relative_path_from(Pathname.pwd)
|
24
|
-
line "Using manifest file #{c(path, :name)}"
|
25
|
-
line
|
26
|
-
|
27
|
-
@@showed_manifest_usage = true
|
28
|
-
end
|
29
|
-
|
30
14
|
# basic commands that, when given no name, act on the
|
31
15
|
# app(s) described by the manifest, in dependency-order
|
32
16
|
[ :start, :restart, :instances, :logs, :env,
|
@@ -104,17 +88,21 @@ class Manifests < VMC::CLI
|
|
104
88
|
:desc => "Reset to values in the manifest"
|
105
89
|
|
106
90
|
around(:push) do |push, input|
|
107
|
-
|
91
|
+
particular =
|
108
92
|
if input.given?(:name)
|
109
93
|
path = File.expand_path(input[:name])
|
110
94
|
find_by = File.exists?(path) ? path : input[:name]
|
111
95
|
|
112
|
-
|
96
|
+
find_apps(find_by)
|
97
|
+
else
|
98
|
+
[]
|
113
99
|
end
|
114
100
|
|
115
|
-
|
101
|
+
if particular.empty?
|
102
|
+
particular = find_apps(Dir.pwd)
|
103
|
+
end
|
116
104
|
|
117
|
-
apps =
|
105
|
+
apps = particular.empty? ? all_apps : particular
|
118
106
|
|
119
107
|
if apps.empty?
|
120
108
|
with_filters(
|
@@ -127,7 +115,7 @@ class Manifests < VMC::CLI
|
|
127
115
|
else
|
128
116
|
show_manifest_usage
|
129
117
|
|
130
|
-
apps
|
118
|
+
spaced(apps) do |app|
|
131
119
|
with_filters(
|
132
120
|
:push => {
|
133
121
|
:create_app => proc { |a|
|
@@ -141,7 +129,7 @@ class Manifests < VMC::CLI
|
|
141
129
|
}) do
|
142
130
|
# only set inputs if creating app or updating with --reset
|
143
131
|
if input[:reset] || !client.app_by_name(app[:name])
|
144
|
-
app_input = input.
|
132
|
+
app_input = input.rebase_given(app)
|
145
133
|
else
|
146
134
|
app_input = input.merge(:path => from_manifest(app[:path]))
|
147
135
|
end
|
data/lib/manifests-vmc-plugin.rb
CHANGED
@@ -1,9 +1,14 @@
|
|
1
1
|
require "yaml"
|
2
2
|
require "set"
|
3
3
|
|
4
|
+
require "manifests-vmc-plugin/loader"
|
5
|
+
|
6
|
+
|
4
7
|
module VMCManifests
|
5
8
|
MANIFEST_FILE = "manifest.yml"
|
6
9
|
|
10
|
+
@@showed_manifest_usage = false
|
11
|
+
|
7
12
|
def manifest
|
8
13
|
return @manifest if @manifest
|
9
14
|
|
@@ -13,7 +18,7 @@ module VMCManifests
|
|
13
18
|
end
|
14
19
|
|
15
20
|
def save_manifest(save_to = manifest_file)
|
16
|
-
|
21
|
+
fail "No manifest to save!" unless @manifest
|
17
22
|
|
18
23
|
File.open(save_to, "w") do |io|
|
19
24
|
YAML.dump(@manifest, io)
|
@@ -44,250 +49,67 @@ module VMCManifests
|
|
44
49
|
@manifest_file = File.expand_path(path)
|
45
50
|
end
|
46
51
|
|
47
|
-
# convert any deprecated structuring to the modern format
|
48
|
-
def simplify_info(info)
|
49
|
-
if info["framework"].is_a?(Hash)
|
50
|
-
info["framework"] = info["framework"]["name"]
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
52
|
# load and resolve a given manifest file
|
55
53
|
def load_manifest(file)
|
56
|
-
|
57
|
-
resolve_manifest(manifest)
|
58
|
-
|
59
|
-
# single-app manifest
|
60
|
-
simplify_info(manifest)
|
61
|
-
|
62
|
-
if apps = manifest["applications"]
|
63
|
-
apps.each do |path, info|
|
64
|
-
simplify_info(info)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
manifest
|
69
|
-
end
|
70
|
-
|
71
|
-
# parse a manifest and merge with its inherited manifests
|
72
|
-
def build_manifest(file)
|
73
|
-
manifest = YAML.load_file file
|
74
|
-
|
75
|
-
Array(manifest["inherit"]).each do |p|
|
76
|
-
manifest = merge_parent(manifest, p)
|
77
|
-
end
|
78
|
-
|
79
|
-
manifest
|
80
|
-
end
|
81
|
-
|
82
|
-
# merge the manifest at `path' into the `child'
|
83
|
-
def merge_parent(child, path)
|
84
|
-
merge_manifest(child, build_manifest(from_manifest(path)))
|
85
|
-
end
|
86
|
-
|
87
|
-
# deep hash merge
|
88
|
-
def merge_manifest(child, parent)
|
89
|
-
merge = proc do |_, old, new|
|
90
|
-
if new.is_a?(Hash) and old.is_a?(Hash)
|
91
|
-
old.merge(new, &merge)
|
92
|
-
else
|
93
|
-
new
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
parent.merge(child, &merge)
|
98
|
-
end
|
99
|
-
|
100
|
-
# resolve symbols in a manifest
|
101
|
-
def resolve_manifest(manifest)
|
102
|
-
if apps = manifest["applications"]
|
103
|
-
apps.each_value do |v|
|
104
|
-
resolve_lexically(v, [manifest])
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
resolve_lexically(manifest, [manifest])
|
109
|
-
|
110
|
-
nil
|
54
|
+
Loader.new(file, self).manifest
|
111
55
|
end
|
112
56
|
|
113
|
-
#
|
114
|
-
def
|
115
|
-
case val
|
116
|
-
when Hash
|
117
|
-
val.each_value do |v|
|
118
|
-
resolve_lexically(v, [val] + ctx)
|
119
|
-
end
|
120
|
-
when Array
|
121
|
-
val.each do |v|
|
122
|
-
resolve_lexically(v, ctx)
|
123
|
-
end
|
124
|
-
when String
|
125
|
-
val.gsub!(/\$\{([^\}]+)\}/) do
|
126
|
-
resolve_symbol($1, ctx)
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
nil
|
131
|
-
end
|
132
|
-
|
133
|
-
# resolve a symbol to its value, and then resolve that value
|
134
|
-
def resolve_symbol(sym, ctx)
|
57
|
+
# dynamic symbol resolution
|
58
|
+
def resolve_symbol(sym)
|
135
59
|
case sym
|
136
60
|
when "target-url"
|
137
|
-
|
61
|
+
client_target
|
138
62
|
|
139
63
|
when "target-base"
|
140
|
-
|
64
|
+
client_target.sub(/^[^\.]+\./, "")
|
141
65
|
|
142
66
|
when "random-word"
|
143
|
-
"%04x"
|
67
|
+
sprintf("%04x", rand(0x0100000))
|
144
68
|
|
145
69
|
when /^ask (.+)/
|
146
70
|
ask($1)
|
147
|
-
|
148
|
-
else
|
149
|
-
found = find_symbol(sym, ctx)
|
150
|
-
|
151
|
-
if found
|
152
|
-
resolve_lexically(found, ctx)
|
153
|
-
found
|
154
|
-
else
|
155
|
-
raise("Unknown symbol in manifest: #{sym}")
|
156
|
-
end
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
# get the target url from either the manifest or the current client
|
161
|
-
def target_url(ctx = [])
|
162
|
-
find_symbol("target", ctx) || client_target
|
163
|
-
end
|
164
|
-
|
165
|
-
def target_base(ctx = [])
|
166
|
-
target_url(ctx).sub(/^[^\.]+\./, "")
|
167
|
-
end
|
168
|
-
|
169
|
-
# search for a symbol introduced in the lexical context
|
170
|
-
def find_symbol(sym, ctx)
|
171
|
-
ctx.each do |h|
|
172
|
-
if val = resolve_in(h, sym)
|
173
|
-
return val
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
nil
|
178
|
-
end
|
179
|
-
|
180
|
-
# find a value, searching in explicit properties first
|
181
|
-
def resolve_in(hash, *where)
|
182
|
-
find_in_hash(hash, ["properties"] + where) ||
|
183
|
-
find_in_hash(hash, where)
|
184
|
-
end
|
185
|
-
|
186
|
-
# helper for following a path of values in a hash
|
187
|
-
def find_in_hash(hash, where)
|
188
|
-
what = hash
|
189
|
-
where.each do |x|
|
190
|
-
return nil unless what.is_a?(Hash)
|
191
|
-
what = what[x]
|
192
|
-
end
|
193
|
-
|
194
|
-
what
|
195
|
-
end
|
196
|
-
|
197
|
-
MANIFEST_META = ["applications", "properties"]
|
198
|
-
|
199
|
-
def toplevel_attributes
|
200
|
-
if m = manifest
|
201
|
-
m.reject do |k, _|
|
202
|
-
MANIFEST_META.include? k
|
203
|
-
end
|
204
71
|
end
|
205
72
|
end
|
206
73
|
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
if apps = manifest["applications"]
|
211
|
-
manifest["applications"].find do |path, info|
|
212
|
-
info["name"] == name
|
213
|
-
end
|
214
|
-
elsif name == manifest["name"]
|
215
|
-
[".", toplevel_attributes]
|
216
|
-
end
|
74
|
+
# find an app by its unique tag
|
75
|
+
def app_by_tag(tag)
|
76
|
+
manifest[:applications][tag]
|
217
77
|
end
|
218
78
|
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
full_path = from_manifest(find_path)
|
224
|
-
|
225
|
-
manifest["applications"].find do |path, info|
|
226
|
-
from_manifest(path) == full_path
|
227
|
-
end
|
228
|
-
elsif find_path == "."
|
229
|
-
[".", toplevel_attributes]
|
79
|
+
# find apps by an identifier, which may be either a tag, a name, or a path
|
80
|
+
def find_apps(identifier)
|
81
|
+
if app = app_by_tag(identifier)
|
82
|
+
return [app]
|
230
83
|
end
|
231
|
-
end
|
232
|
-
|
233
|
-
def app_info(path_or_name, input = nil)
|
234
|
-
path, info = app_by_name(path_or_name) || app_by_path(path_or_name)
|
235
|
-
return unless info
|
236
84
|
|
237
|
-
|
238
|
-
|
239
|
-
toplevel_attributes.merge(info).each do |k, v|
|
240
|
-
name = k.to_sym
|
241
|
-
|
242
|
-
if name == :mem
|
243
|
-
name = :memory
|
244
|
-
end
|
85
|
+
apps = apps_by(:name, identifier)
|
245
86
|
|
246
|
-
|
87
|
+
if apps.empty?
|
88
|
+
apps = apps_by(:path, from_manifest(identifier))
|
247
89
|
end
|
248
90
|
|
249
|
-
|
250
|
-
|
251
|
-
data
|
91
|
+
apps
|
252
92
|
end
|
253
93
|
|
254
94
|
# call a block for each app in a manifest (in dependency order), setting
|
255
95
|
# inputs for each app
|
256
|
-
def each_app(
|
257
|
-
|
258
|
-
use_inputs = all_apps.size == 1
|
259
|
-
|
260
|
-
ordered_by_deps(all_apps).each do |path|
|
261
|
-
yield app_info(path, use_inputs && input)
|
262
|
-
end
|
263
|
-
|
264
|
-
true
|
265
|
-
|
266
|
-
# manually created or legacy single-app manifest
|
267
|
-
elsif toplevel_attributes
|
268
|
-
yield app_info(".", input)
|
269
|
-
|
270
|
-
true
|
96
|
+
def each_app(&blk)
|
97
|
+
return unless manifest
|
271
98
|
|
272
|
-
|
273
|
-
false
|
274
|
-
end
|
99
|
+
ordered_by_deps(manifest[:applications]).each(&blk)
|
275
100
|
end
|
276
101
|
|
277
|
-
|
102
|
+
# return all the apps described by the manifest, in dependency order
|
103
|
+
def all_apps
|
278
104
|
apps = []
|
279
105
|
|
280
|
-
each_app
|
106
|
+
each_app do |app|
|
281
107
|
apps << app
|
282
108
|
end
|
283
109
|
|
284
110
|
apps
|
285
111
|
end
|
286
112
|
|
287
|
-
def no_apps
|
288
|
-
fail "No applications or manifest to operate on."
|
289
|
-
end
|
290
|
-
|
291
113
|
# like each_app, but only acts on apps specified as paths instead of names
|
292
114
|
#
|
293
115
|
# returns the names that were not paths
|
@@ -307,10 +129,12 @@ module VMCManifests
|
|
307
129
|
in_manifest = []
|
308
130
|
|
309
131
|
if names_or_paths.empty?
|
310
|
-
|
311
|
-
|
132
|
+
specific = find_apps(Dir.pwd)
|
133
|
+
|
134
|
+
if !specific.empty?
|
135
|
+
in_manifest += apps
|
312
136
|
else
|
313
|
-
each_app(
|
137
|
+
each_app(&blk)
|
314
138
|
return []
|
315
139
|
end
|
316
140
|
end
|
@@ -320,8 +144,10 @@ module VMCManifests
|
|
320
144
|
if x.is_a?(String)
|
321
145
|
path = File.expand_path(x)
|
322
146
|
|
323
|
-
|
324
|
-
|
147
|
+
apps = find_apps(File.exists?(path) ? path : x)
|
148
|
+
|
149
|
+
if !apps.empty?
|
150
|
+
in_manifest += apps
|
325
151
|
elsif app = client.app_by_name(x)
|
326
152
|
external << app
|
327
153
|
else
|
@@ -339,9 +165,33 @@ module VMCManifests
|
|
339
165
|
external
|
340
166
|
end
|
341
167
|
|
342
|
-
|
343
168
|
private
|
344
169
|
|
170
|
+
def show_manifest_usage
|
171
|
+
return if @@showed_manifest_usage
|
172
|
+
|
173
|
+
path = Pathname.new(manifest_file).relative_path_from(Pathname.pwd)
|
174
|
+
line "Using manifest file #{c(path, :name)}"
|
175
|
+
line
|
176
|
+
|
177
|
+
@@showed_manifest_usage = true
|
178
|
+
end
|
179
|
+
|
180
|
+
def no_apps
|
181
|
+
fail "No applications or manifest to operate on."
|
182
|
+
end
|
183
|
+
|
184
|
+
def apps_by(attr, val)
|
185
|
+
found = []
|
186
|
+
manifest[:applications].each do |tag, info|
|
187
|
+
if info[attr] == val
|
188
|
+
found << info
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
found
|
193
|
+
end
|
194
|
+
|
345
195
|
# expand a path relative to the manifest file's directory
|
346
196
|
def from_manifest(path)
|
347
197
|
File.expand_path(path, File.dirname(manifest_file))
|
@@ -349,35 +199,26 @@ module VMCManifests
|
|
349
199
|
|
350
200
|
# sort applications in dependency order
|
351
201
|
# e.g. if A depends on B, B will be listed before A
|
352
|
-
def ordered_by_deps(apps,
|
353
|
-
unless abspaths
|
354
|
-
abspaths = {}
|
355
|
-
apps.each do |p, i|
|
356
|
-
abspaths[from_manifest(p)] = i
|
357
|
-
end
|
358
|
-
end
|
359
|
-
|
202
|
+
def ordered_by_deps(apps, processed = Set[])
|
360
203
|
ordered = []
|
361
|
-
apps.each do |
|
362
|
-
|
204
|
+
apps.each do |tag, info|
|
205
|
+
next if processed.include?(tag)
|
363
206
|
|
364
|
-
if deps = info["depends-on"]
|
207
|
+
if deps = Array(info[:"depends-on"])
|
365
208
|
dep_apps = {}
|
366
209
|
deps.each do |dep|
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
dep_apps[dep] = abspaths[edep]
|
210
|
+
dep = dep.to_sym
|
211
|
+
fail "Circular dependency detected." if processed.include? dep
|
212
|
+
dep_apps[dep] = apps[dep]
|
372
213
|
end
|
373
214
|
|
374
|
-
processed.add(
|
215
|
+
processed.add(tag)
|
375
216
|
|
376
|
-
ordered += ordered_by_deps(dep_apps,
|
377
|
-
ordered <<
|
378
|
-
|
379
|
-
ordered <<
|
380
|
-
processed.add(
|
217
|
+
ordered += ordered_by_deps(dep_apps, processed)
|
218
|
+
ordered << info
|
219
|
+
else
|
220
|
+
ordered << info
|
221
|
+
processed.add(tag)
|
381
222
|
end
|
382
223
|
end
|
383
224
|
|
@@ -431,7 +272,7 @@ module VMCManifests
|
|
431
272
|
with_progress("Saving to #{c("manifest.yml", :name)}") do
|
432
273
|
File.open("manifest.yml", "w") do |io|
|
433
274
|
YAML.dump(
|
434
|
-
{ "applications" =>
|
275
|
+
{ "applications" => [meta] },
|
435
276
|
io)
|
436
277
|
end
|
437
278
|
end
|
@@ -470,19 +311,19 @@ module VMCManifests
|
|
470
311
|
to_bind << instance
|
471
312
|
else
|
472
313
|
service = services.find { |s|
|
473
|
-
s.label == (svc[
|
474
|
-
(!svc[
|
475
|
-
(s.provider == (svc[
|
314
|
+
s.label == (svc[:label] || svc[:type] || svc[:vendor]) &&
|
315
|
+
(!svc[:version] || s.version == svc[:version]) &&
|
316
|
+
(s.provider == (svc[:provider] || "core"))
|
476
317
|
}
|
477
318
|
|
478
319
|
fail "Unknown service: #{svc.inspect}." unless service
|
479
320
|
|
480
321
|
if v2?
|
481
322
|
plan = service.service_plans.find { |p|
|
482
|
-
p.name == (svc[
|
323
|
+
p.name == (svc[:plan] || "D100")
|
483
324
|
}
|
484
325
|
|
485
|
-
fail "Unknown service plan: #{svc[
|
326
|
+
fail "Unknown service plan: #{svc[:plan]}." unless plan
|
486
327
|
end
|
487
328
|
|
488
329
|
invoke :create_service,
|
@@ -500,32 +341,4 @@ module VMCManifests
|
|
500
341
|
:instance => i
|
501
342
|
end
|
502
343
|
end
|
503
|
-
|
504
|
-
def megabytes(str)
|
505
|
-
if str =~ /T$/i
|
506
|
-
str.to_i * 1024 * 1024
|
507
|
-
elsif str =~ /G$/i
|
508
|
-
str.to_i * 1024
|
509
|
-
elsif str =~ /M$/i
|
510
|
-
str.to_i
|
511
|
-
elsif str =~ /K$/i
|
512
|
-
str.to_i / 1024
|
513
|
-
else # assume megabytes
|
514
|
-
str.to_i
|
515
|
-
end
|
516
|
-
end
|
517
|
-
|
518
|
-
def human_size(num, precision = 1)
|
519
|
-
sizes = ["G", "M", "K"]
|
520
|
-
sizes.each.with_index do |suf, i|
|
521
|
-
pow = sizes.size - i
|
522
|
-
unit = 1024 ** pow
|
523
|
-
if num >= unit
|
524
|
-
return format("%.#{precision}f%s", num / unit, suf)
|
525
|
-
end
|
526
|
-
end
|
527
|
-
|
528
|
-
format("%.#{precision}fB", num)
|
529
|
-
end
|
530
|
-
|
531
344
|
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
require "manifests-vmc-plugin/loader"
|
4
|
+
|
5
|
+
|
6
|
+
describe VMCManifests::Normalizer do
|
7
|
+
let(:manifest) { {} }
|
8
|
+
let(:loader) { VMCManifests::Loader.new(nil, nil) }
|
9
|
+
|
10
|
+
describe '#normalize!' do
|
11
|
+
subject do
|
12
|
+
loader.normalize!(manifest)
|
13
|
+
manifest
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'with a manifest where the applications have no path set' do
|
17
|
+
let(:manifest) { { "applications" => { "." => { "name" => "foo" } } } }
|
18
|
+
|
19
|
+
it "sets the path to their tag, assuming it's a path" do
|
20
|
+
expect(subject).to eq(
|
21
|
+
:applications => { :"." => { :name => "foo", :path => "." } })
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'with a manifest with toplevel attributes' do
|
26
|
+
context 'and properties' do
|
27
|
+
let(:manifest) {
|
28
|
+
{ "name" => "foo", "properties" => { "fizz" => "buzz" } }
|
29
|
+
}
|
30
|
+
|
31
|
+
it 'keeps the properties at the toplevel' do
|
32
|
+
expect(subject).to eq(
|
33
|
+
:applications => { :"0" => { :name => "foo", :path => "." } },
|
34
|
+
:properties => { :fizz => "buzz" })
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'and no applications' do
|
39
|
+
context 'and no path' do
|
40
|
+
let(:manifest) { { "name" => "foo" } }
|
41
|
+
|
42
|
+
it 'adds it as an application with path .' do
|
43
|
+
expect(subject).to eq(
|
44
|
+
:applications => { :"0" => { :name => "foo", :path => "." } })
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'and a path' do
|
49
|
+
let(:manifest) { { "name" => "foo", "path" => "./foo" } }
|
50
|
+
|
51
|
+
it 'adds it as an application with the proper tag and path' do
|
52
|
+
expect(subject).to eq(
|
53
|
+
:applications => {
|
54
|
+
:"0" => { :name => "foo", :path => "./foo" }
|
55
|
+
})
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'and applications' do
|
61
|
+
let(:manifest) {
|
62
|
+
{ "runtime" => "ruby19",
|
63
|
+
"applications" => {
|
64
|
+
"./foo" => { "name" => "foo" },
|
65
|
+
"./bar" => { "name" => "bar" },
|
66
|
+
"./baz" => { "name" => "baz", "runtime" => "ruby18" }
|
67
|
+
}
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
it "merges the toplevel attributes into the applications" do
|
72
|
+
expect(subject).to eq(
|
73
|
+
:applications => {
|
74
|
+
:"./foo" =>
|
75
|
+
{ :name => "foo", :path => "./foo", :runtime => "ruby19" },
|
76
|
+
|
77
|
+
:"./bar" =>
|
78
|
+
{ :name => "bar", :path => "./bar", :runtime => "ruby19" },
|
79
|
+
|
80
|
+
:"./baz" =>
|
81
|
+
{ :name => "baz", :path => "./baz", :runtime => "ruby18" }
|
82
|
+
})
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'with a manifest where applications is an array' do
|
88
|
+
let(:manifest) { { "applications" => [{ "name" => "foo" }] } }
|
89
|
+
|
90
|
+
it 'converts the array to a hash, with the path as .' do
|
91
|
+
expect(subject).to eq(
|
92
|
+
:applications => { :"0" => { :name => "foo", :path => "." } })
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe '#normalize_app!' do
|
98
|
+
subject do
|
99
|
+
loader.send(:normalize_app!, manifest)
|
100
|
+
manifest
|
101
|
+
end
|
102
|
+
|
103
|
+
context 'with framework as a hash' do
|
104
|
+
let(:manifest) {
|
105
|
+
{ "name" => "foo",
|
106
|
+
"framework" => { "name" => "ruby19", "mem" => "64M" }
|
107
|
+
}
|
108
|
+
}
|
109
|
+
|
110
|
+
it 'sets the framework to just the name' do
|
111
|
+
expect(subject).to eq(
|
112
|
+
"name" => "foo",
|
113
|
+
"framework" => "ruby19")
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'with mem instead of memory' do
|
118
|
+
let(:manifest) { { "name" => "foo", "mem" => "128M" } }
|
119
|
+
|
120
|
+
it 'renames mem to memory' do
|
121
|
+
expect(subject).to eq("name" => "foo", "memory" => "128M")
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: manifests-vmc-plugin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 4
|
9
|
-
-
|
10
|
-
version: 0.4.
|
9
|
+
- 14
|
10
|
+
version: 0.4.14
|
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-11-
|
18
|
+
date: 2012-11-30 00:00:00 -08:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -34,6 +34,51 @@ dependencies:
|
|
34
34
|
version: 0.4.0
|
35
35
|
type: :runtime
|
36
36
|
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: rake
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 3
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
version: "0"
|
49
|
+
type: :development
|
50
|
+
version_requirements: *id002
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
name: rspec
|
53
|
+
prerelease: false
|
54
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ~>
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 3
|
60
|
+
segments:
|
61
|
+
- 2
|
62
|
+
- 0
|
63
|
+
version: "2.0"
|
64
|
+
type: :development
|
65
|
+
version_requirements: *id003
|
66
|
+
- !ruby/object:Gem::Dependency
|
67
|
+
name: vmc
|
68
|
+
prerelease: false
|
69
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
hash: 15
|
75
|
+
segments:
|
76
|
+
- 0
|
77
|
+
- 4
|
78
|
+
- 0
|
79
|
+
version: 0.4.0
|
80
|
+
type: :development
|
81
|
+
version_requirements: *id004
|
37
82
|
description:
|
38
83
|
email:
|
39
84
|
- asuraci@vmware.com
|
@@ -48,7 +93,13 @@ files:
|
|
48
93
|
- lib/manifests-vmc-plugin/version.rb
|
49
94
|
- lib/manifests-vmc-plugin/errors.rb
|
50
95
|
- lib/manifests-vmc-plugin/plugin.rb
|
96
|
+
- lib/manifests-vmc-plugin/loader.rb
|
97
|
+
- lib/manifests-vmc-plugin/loader/normalizer.rb
|
98
|
+
- lib/manifests-vmc-plugin/loader/resolver.rb
|
99
|
+
- lib/manifests-vmc-plugin/loader/builder.rb
|
51
100
|
- lib/manifests-vmc-plugin.rb
|
101
|
+
- spec/spec_helper.rb
|
102
|
+
- spec/normalizer_spec.rb
|
52
103
|
has_rdoc: true
|
53
104
|
homepage: http://cloudfoundry.com/
|
54
105
|
licenses: []
|
@@ -83,5 +134,6 @@ rubygems_version: 1.6.2
|
|
83
134
|
signing_key:
|
84
135
|
specification_version: 3
|
85
136
|
summary: Cloud Foundry automation via manifest documents.
|
86
|
-
test_files:
|
87
|
-
|
137
|
+
test_files:
|
138
|
+
- spec/spec_helper.rb
|
139
|
+
- spec/normalizer_spec.rb
|