cloulu 0.2.6 → 0.3.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.
- checksums.yaml +15 -0
- data/lib/manifests-vmc-plugin/lib/manifests-vmc-plugin/errors.rb +33 -0
- data/lib/manifests-vmc-plugin/lib/manifests-vmc-plugin/loader/builder.rb +37 -0
- data/lib/manifests-vmc-plugin/lib/manifests-vmc-plugin/loader/normalizer.rb +149 -0
- data/lib/manifests-vmc-plugin/lib/manifests-vmc-plugin/loader/resolver.rb +79 -0
- data/lib/manifests-vmc-plugin/lib/manifests-vmc-plugin/loader.rb +31 -0
- data/lib/manifests-vmc-plugin/lib/manifests-vmc-plugin/plugin.rb +145 -0
- data/lib/manifests-vmc-plugin/lib/manifests-vmc-plugin/version.rb +3 -0
- data/lib/manifests-vmc-plugin/lib/manifests-vmc-plugin.rb +317 -0
- data/lib/manifests-vmc-plugin/spec/manifests-vmc-plugin/errors_spec.rb +29 -0
- data/lib/manifests-vmc-plugin/spec/manifests-vmc-plugin/loader/builder_spec.rb +84 -0
- data/lib/manifests-vmc-plugin/spec/manifests-vmc-plugin/loader/normalizer_spec.rb +176 -0
- data/lib/manifests-vmc-plugin/spec/manifests-vmc-plugin/plugin_spec.rb +365 -0
- data/lib/manifests-vmc-plugin/spec/manifests-vmc-plugin_spec.rb +321 -0
- data/lib/manifests-vmc-plugin/spec/spec_helper.rb +17 -0
- data/lib/tunnel-vmc-plugin/plugin.rb +178 -0
- data/lib/tunnel-vmc-plugin/spec/plugin_spec.rb +29 -0
- data/lib/tunnel-vmc-plugin/spec/spec_helper.rb +15 -0
- data/lib/tunnel-vmc-plugin/tunnel.rb +308 -0
- data/lib/tunnel-vmc-plugin/version.rb +3 -0
- data/lib/vmc/plugin.rb +8 -23
- data/lib/vmc/version.rb +1 -1
- metadata +57 -32
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
require "yaml"
|
|
2
|
+
require "set"
|
|
3
|
+
|
|
4
|
+
require "manifests-vmc-plugin/loader"
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
module VMCManifests
|
|
8
|
+
MANIFEST_FILE = "manifest.yml"
|
|
9
|
+
|
|
10
|
+
@@showed_manifest_usage = false
|
|
11
|
+
|
|
12
|
+
def manifest
|
|
13
|
+
return @manifest if @manifest
|
|
14
|
+
|
|
15
|
+
if manifest_file && File.exists?(manifest_file)
|
|
16
|
+
@manifest = load_manifest(manifest_file)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def save_manifest(save_to = manifest_file)
|
|
21
|
+
fail "No manifest to save!" unless @manifest
|
|
22
|
+
|
|
23
|
+
File.open(save_to, "w") do |io|
|
|
24
|
+
YAML.dump(@manifest, io)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# find the manifest file to work with
|
|
29
|
+
def manifest_file
|
|
30
|
+
return @manifest_file if @manifest_file
|
|
31
|
+
|
|
32
|
+
unless path = input[:manifest]
|
|
33
|
+
where = Dir.pwd
|
|
34
|
+
while true
|
|
35
|
+
if File.exists?(File.join(where, MANIFEST_FILE))
|
|
36
|
+
path = File.join(where, MANIFEST_FILE)
|
|
37
|
+
break
|
|
38
|
+
elsif File.basename(where) == "/"
|
|
39
|
+
path = nil
|
|
40
|
+
break
|
|
41
|
+
else
|
|
42
|
+
where = File.expand_path("../", where)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
return unless path
|
|
48
|
+
|
|
49
|
+
@manifest_file = File.expand_path(path)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# load and resolve a given manifest file
|
|
53
|
+
def load_manifest(file)
|
|
54
|
+
Loader.new(file, self).manifest
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# dynamic symbol resolution
|
|
58
|
+
def resolve_symbol(sym)
|
|
59
|
+
case sym
|
|
60
|
+
when "target-url"
|
|
61
|
+
client_target
|
|
62
|
+
|
|
63
|
+
when "target-base"
|
|
64
|
+
target_base
|
|
65
|
+
|
|
66
|
+
when "random-word"
|
|
67
|
+
sprintf("%04x", rand(0x0100000))
|
|
68
|
+
|
|
69
|
+
when /^ask (.+)/
|
|
70
|
+
ask($1)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# find apps by an identifier, which may be either a tag, a name, or a path
|
|
75
|
+
def find_apps(identifier)
|
|
76
|
+
return [] unless manifest
|
|
77
|
+
|
|
78
|
+
apps = apps_by(:name, identifier)
|
|
79
|
+
|
|
80
|
+
if apps.empty?
|
|
81
|
+
apps = apps_by(:path, from_manifest(identifier))
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
apps
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# return all the apps described by the manifest
|
|
88
|
+
def all_apps
|
|
89
|
+
manifest[:applications]
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def current_apps
|
|
93
|
+
manifest[:applications].select do |app|
|
|
94
|
+
next unless app[:path]
|
|
95
|
+
from_manifest(app[:path]) == Dir.pwd
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# splits the user's input, resolving paths with the manifest,
|
|
100
|
+
# into internal/external apps
|
|
101
|
+
#
|
|
102
|
+
# internal apps are returned as their data in the manifest
|
|
103
|
+
#
|
|
104
|
+
# external apps are the strings that the user gave, to be
|
|
105
|
+
# passed along wholesale to the wrapped command
|
|
106
|
+
def apps_in_manifest(input = nil, use_name = true, &blk)
|
|
107
|
+
names_or_paths =
|
|
108
|
+
if input.has?(:apps)
|
|
109
|
+
# names may be given but be [], which will still cause
|
|
110
|
+
# interaction, so use #direct instead of #[] here
|
|
111
|
+
input.direct(:apps)
|
|
112
|
+
elsif input.has?(:app)
|
|
113
|
+
[input.direct(:app)]
|
|
114
|
+
elsif input.has?(:name)
|
|
115
|
+
[input.direct(:name)]
|
|
116
|
+
else
|
|
117
|
+
[]
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
internal = []
|
|
121
|
+
external = []
|
|
122
|
+
|
|
123
|
+
names_or_paths.each do |x|
|
|
124
|
+
if x.is_a?(String)
|
|
125
|
+
if x =~ %r([/\\])
|
|
126
|
+
apps = find_apps(File.expand_path(x))
|
|
127
|
+
|
|
128
|
+
if apps.empty?
|
|
129
|
+
fail("Path #{b(x)} is not present in manifest #{b(relative_manifest_file)}.")
|
|
130
|
+
end
|
|
131
|
+
else
|
|
132
|
+
apps = find_apps(x)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
if !apps.empty?
|
|
136
|
+
internal += apps
|
|
137
|
+
else
|
|
138
|
+
external << x
|
|
139
|
+
end
|
|
140
|
+
else
|
|
141
|
+
external << x
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
[internal, external]
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def create_manifest_for(app, path)
|
|
149
|
+
meta = {
|
|
150
|
+
"name" => app.name,
|
|
151
|
+
"framework" => app.framework.name,
|
|
152
|
+
"runtime" => app.runtime.name,
|
|
153
|
+
"memory" => human_size(app.memory * 1024 * 1024, 0),
|
|
154
|
+
"instances" => app.total_instances,
|
|
155
|
+
"url" => app.url ? app.url.sub(target_base, '${target-base}') : "none",
|
|
156
|
+
"path" => path
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
services = app.services
|
|
160
|
+
|
|
161
|
+
unless services.empty?
|
|
162
|
+
meta["services"] = {}
|
|
163
|
+
|
|
164
|
+
services.each do |i|
|
|
165
|
+
if v2?
|
|
166
|
+
p = i.service_plan
|
|
167
|
+
s = p.service
|
|
168
|
+
|
|
169
|
+
meta["services"][i.name] = {
|
|
170
|
+
"label" => s.label,
|
|
171
|
+
"provider" => s.provider,
|
|
172
|
+
"version" => s.version,
|
|
173
|
+
"plan" => p.name
|
|
174
|
+
}
|
|
175
|
+
else
|
|
176
|
+
meta["services"][i.name] = {
|
|
177
|
+
"vendor" => i.vendor,
|
|
178
|
+
"version" => i.version,
|
|
179
|
+
"tier" => i.tier
|
|
180
|
+
}
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
if cmd = app.command
|
|
186
|
+
meta["command"] = cmd
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
if buildpack = app.buildpack
|
|
190
|
+
meta["buildpack"] = buildpack
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
meta
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
private
|
|
197
|
+
|
|
198
|
+
def relative_manifest_file
|
|
199
|
+
Pathname.new(manifest_file).relative_path_from(Pathname.pwd)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def show_manifest_usage
|
|
203
|
+
return if @@showed_manifest_usage
|
|
204
|
+
|
|
205
|
+
path = relative_manifest_file
|
|
206
|
+
line "Using manifest file #{c(path, :name)}"
|
|
207
|
+
line
|
|
208
|
+
|
|
209
|
+
@@showed_manifest_usage = true
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def no_apps
|
|
213
|
+
fail "No applications or manifest to operate on."
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def warn_reset_changes
|
|
217
|
+
line c("Not applying manifest changes without --reset", :warning)
|
|
218
|
+
line "See `vmc diff` for more details."
|
|
219
|
+
line
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def apps_by(attr, val)
|
|
223
|
+
manifest[:applications].select do |info|
|
|
224
|
+
info[attr] == val
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# expand a path relative to the manifest file's directory
|
|
229
|
+
def from_manifest(path)
|
|
230
|
+
File.expand_path(path, File.dirname(manifest_file))
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def ask_to_save(input, app)
|
|
235
|
+
return if manifest_file
|
|
236
|
+
return unless ask("Save configuration?", :default => false)
|
|
237
|
+
|
|
238
|
+
manifest = create_manifest_for(app, input[:path])
|
|
239
|
+
|
|
240
|
+
with_progress("Saving to #{c("manifest.yml", :name)}") do
|
|
241
|
+
File.open("manifest.yml", "w") do |io|
|
|
242
|
+
YAML.dump(
|
|
243
|
+
{ "applications" => [manifest] },
|
|
244
|
+
io)
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def env_hash(val)
|
|
250
|
+
if val.is_a?(Hash)
|
|
251
|
+
val
|
|
252
|
+
else
|
|
253
|
+
hash = {}
|
|
254
|
+
|
|
255
|
+
val.each do |pair|
|
|
256
|
+
name, val = pair.split("=", 2)
|
|
257
|
+
hash[name] = val
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
hash
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def setup_env(app, info)
|
|
265
|
+
return unless info[:env]
|
|
266
|
+
app.env = env_hash(info[:env])
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def setup_services(app, info)
|
|
270
|
+
return if !info[:services] || info[:services].empty?
|
|
271
|
+
|
|
272
|
+
offerings = client.services
|
|
273
|
+
|
|
274
|
+
to_bind = []
|
|
275
|
+
|
|
276
|
+
info[:services].each do |name, svc|
|
|
277
|
+
name = name.to_s
|
|
278
|
+
|
|
279
|
+
if instance = client.service_instance_by_name(name)
|
|
280
|
+
to_bind << instance
|
|
281
|
+
else
|
|
282
|
+
offering = offerings.find { |o|
|
|
283
|
+
o.label == (svc[:label] || svc[:type] || svc[:vendor]) &&
|
|
284
|
+
(!svc[:version] || o.version == svc[:version]) &&
|
|
285
|
+
(o.provider == (svc[:provider] || "core"))
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
fail "Unknown service offering: #{svc.inspect}." unless offering
|
|
289
|
+
|
|
290
|
+
if v2?
|
|
291
|
+
plan = offering.service_plans.find { |p|
|
|
292
|
+
p.name == (svc[:plan] || "D100")
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
fail "Unknown service plan: #{svc[:plan]}." unless plan
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
invoke :create_service,
|
|
299
|
+
:name => name,
|
|
300
|
+
:offering => offering,
|
|
301
|
+
:plan => plan,
|
|
302
|
+
:app => app
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
to_bind.each do |s|
|
|
307
|
+
next if app.binds?(s)
|
|
308
|
+
|
|
309
|
+
# TODO: splat
|
|
310
|
+
invoke :bind_service, :app => app, :service => s
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def target_base
|
|
315
|
+
client_target.sub(/^[^\.]+\./, "")
|
|
316
|
+
end
|
|
317
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
require "manifests-vmc-plugin/errors"
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
describe VMCManifests::InvalidManifest do
|
|
7
|
+
let(:file) { "/path/to/file" }
|
|
8
|
+
|
|
9
|
+
subject { described_class.new(file) }
|
|
10
|
+
|
|
11
|
+
describe "#initialize" do
|
|
12
|
+
it "is initialized with a file" do
|
|
13
|
+
described_class.new(file)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe "#to_s" do
|
|
18
|
+
it "says the file is malformed" do
|
|
19
|
+
expect(subject.to_s).to eq(
|
|
20
|
+
"Manifest file '#{file}' is malformed.")
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
describe "#file" do
|
|
25
|
+
it "returns the file it was initialized with" do
|
|
26
|
+
expect(subject.file).to eq(file)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
require "manifests-vmc-plugin/loader"
|
|
4
|
+
require "manifests-vmc-plugin/errors"
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
describe VMCManifests::Builder do
|
|
8
|
+
subject { VMCManifests::Loader.new(nil, nil) }
|
|
9
|
+
|
|
10
|
+
describe "#build" do
|
|
11
|
+
let(:file) { "manifest.yml" }
|
|
12
|
+
|
|
13
|
+
before do
|
|
14
|
+
FakeFS.activate!
|
|
15
|
+
|
|
16
|
+
File.open(file, "w") do |io|
|
|
17
|
+
io.write manifest
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
after do
|
|
22
|
+
FakeFS.deactivate!
|
|
23
|
+
FakeFS::FileSystem.clear
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
context "with a simple manifest" do
|
|
27
|
+
let(:manifest) do
|
|
28
|
+
<<EOF
|
|
29
|
+
---
|
|
30
|
+
foo: bar
|
|
31
|
+
EOF
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "loads the manifest YAML" do
|
|
35
|
+
expect(subject.build(file)).to eq("foo" => "bar")
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
context "with a manifest that inherits another" do
|
|
40
|
+
let(:manifest) do
|
|
41
|
+
<<EOF
|
|
42
|
+
---
|
|
43
|
+
inherit: other-manifest.yml
|
|
44
|
+
foo:
|
|
45
|
+
baz: c
|
|
46
|
+
EOF
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
before do
|
|
50
|
+
FakeFS.activate!
|
|
51
|
+
|
|
52
|
+
File.open("other-manifest.yml", "w") do |io|
|
|
53
|
+
io.write <<OTHER
|
|
54
|
+
---
|
|
55
|
+
foo:
|
|
56
|
+
bar: a
|
|
57
|
+
baz: b
|
|
58
|
+
OTHER
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "merges itself into the parent, by depth" do
|
|
63
|
+
manifest = subject.build(file)
|
|
64
|
+
expect(manifest).to include(
|
|
65
|
+
"foo" => { "bar" => "a", "baz" => "c" })
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "does not include the 'inherit' attribute" do
|
|
69
|
+
manifest = subject.build(file)
|
|
70
|
+
expect(manifest).to_not include("inherit")
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
context "with an invalid manifest" do
|
|
75
|
+
let(:manifest) { "" }
|
|
76
|
+
|
|
77
|
+
it "raises an error" do
|
|
78
|
+
expect {
|
|
79
|
+
subject.build(file)
|
|
80
|
+
}.to raise_error(VMCManifests::InvalidManifest)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,176 @@
|
|
|
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 where the url is nil' do
|
|
26
|
+
let(:manifest) { { "applications" => { "." => { "url" => nil } } } }
|
|
27
|
+
|
|
28
|
+
it "sets it to none" do
|
|
29
|
+
expect(subject).to eq(
|
|
30
|
+
:applications => [{ :path => ".", :url => "none" }]
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
context 'with a manifest with a subdomain attribute' do
|
|
36
|
+
let(:manifest) { { "applications" => { "." => { "subdomain" => "use-this-for-host" } } } }
|
|
37
|
+
|
|
38
|
+
it "sets the subdomain key to be host" do
|
|
39
|
+
expect(subject).to eq(
|
|
40
|
+
:applications => [{ :path => ".", :host => "use-this-for-host" }]
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
context "when the host attribute is also set" do
|
|
45
|
+
let(:manifest) { { "applications" => { "." => { "subdomain" => "dont-use-this-for-host", "host" => "canonical-attribute" } } } }
|
|
46
|
+
|
|
47
|
+
it 'does not overwrite an explicit host attribute' do
|
|
48
|
+
expect(subject).to eq(
|
|
49
|
+
:applications => [{ :path => ".", :host => "canonical-attribute" }]
|
|
50
|
+
)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
context 'with a manifest with toplevel attributes' do
|
|
56
|
+
context 'and properties' do
|
|
57
|
+
let(:manifest) {
|
|
58
|
+
{ "name" => "foo", "properties" => { "fizz" => "buzz" } }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
it 'keeps the properties at the toplevel' do
|
|
62
|
+
expect(subject).to eq(
|
|
63
|
+
:applications => [{ :name => "foo", :path => "." }],
|
|
64
|
+
:properties => { :fizz => "buzz" })
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
context 'and no applications' do
|
|
69
|
+
context 'and no path' do
|
|
70
|
+
let(:manifest) { { "name" => "foo" } }
|
|
71
|
+
|
|
72
|
+
it 'adds it as an application with path .' do
|
|
73
|
+
expect(subject).to eq(
|
|
74
|
+
:applications => [{ :name => "foo", :path => "." }])
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
context 'and a path' do
|
|
79
|
+
let(:manifest) { { "name" => "foo", "path" => "./foo" } }
|
|
80
|
+
|
|
81
|
+
it 'adds it as an application with the proper tag and path' do
|
|
82
|
+
expect(subject).to eq(
|
|
83
|
+
:applications => [{ :name => "foo", :path => "./foo" }])
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
context 'and applications' do
|
|
89
|
+
let(:manifest) {
|
|
90
|
+
{ "runtime" => "ruby19",
|
|
91
|
+
"applications" => {
|
|
92
|
+
"./foo" => { "name" => "foo" },
|
|
93
|
+
"./bar" => { "name" => "bar" },
|
|
94
|
+
"./baz" => { "name" => "baz", "runtime" => "ruby18" }
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
it "merges the toplevel attributes into the applications" do
|
|
100
|
+
expect(subject[:applications]).to match_array [
|
|
101
|
+
{ :name => "foo", :path => "./foo", :runtime => "ruby19" },
|
|
102
|
+
{ :name => "bar", :path => "./bar", :runtime => "ruby19" },
|
|
103
|
+
{ :name => "baz", :path => "./baz", :runtime => "ruby18" }
|
|
104
|
+
]
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
context 'with a manifest where applications is a hash' do
|
|
110
|
+
let(:manifest) { { "applications" => { "foo" => { "name" => "foo" } } } }
|
|
111
|
+
|
|
112
|
+
it 'converts the array to a hash, with the path as the key' do
|
|
113
|
+
expect(subject).to eq(
|
|
114
|
+
:applications => [{ :name => "foo", :path => "foo" }])
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
context "and the applications had dependencies" do
|
|
118
|
+
let(:manifest) do
|
|
119
|
+
{ "applications" => {
|
|
120
|
+
"bar" => { "name" => "bar", "depends-on" => "foo" },
|
|
121
|
+
"foo" => { "name" => "foo" }
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it "converts using dependency order" do
|
|
127
|
+
expect(subject).to eq(
|
|
128
|
+
:applications => [{ :name => "foo", :path => "foo" }, { :name => "bar", :path => "bar" }])
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
context "and there's a circular dependency" do
|
|
132
|
+
let(:manifest) do
|
|
133
|
+
{ "applications" => {
|
|
134
|
+
"bar" => { "name" => "bar", "depends-on" => "foo" },
|
|
135
|
+
"foo" => { "name" => "foo", "depends-on" => "bar" }
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
it "doesn't blow up" do
|
|
141
|
+
expect(subject).to be_true
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
describe '#normalize_app!' do
|
|
149
|
+
subject do
|
|
150
|
+
loader.send(:normalize_app!, manifest)
|
|
151
|
+
manifest
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
context 'with framework as a hash' do
|
|
155
|
+
let(:manifest) {
|
|
156
|
+
{ "name" => "foo",
|
|
157
|
+
"framework" => { "name" => "ruby19", "mem" => "64M" }
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
it 'sets the framework to just the name' do
|
|
162
|
+
expect(subject).to eq(
|
|
163
|
+
"name" => "foo",
|
|
164
|
+
"framework" => "ruby19")
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
context 'with mem instead of memory' do
|
|
169
|
+
let(:manifest) { { "name" => "foo", "mem" => "128M" } }
|
|
170
|
+
|
|
171
|
+
it 'renames mem to memory' do
|
|
172
|
+
expect(subject).to eq("name" => "foo", "memory" => "128M")
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|