manifests-vmc-plugin 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/manifests-vmc-plugin/loader/normalizer.rb +34 -11
- data/lib/manifests-vmc-plugin/plugin.rb +47 -32
- data/lib/manifests-vmc-plugin/version.rb +1 -1
- data/lib/manifests-vmc-plugin.rb +33 -88
- data/spec/{loader → manifests-vmc-plugin/loader}/normalizer_spec.rb +43 -24
- data/spec/manifests-vmc-plugin/loader/plugin_spec.rb +182 -0
- data/spec/manifests-vmc-plugin_spec.rb +201 -10
- data/spec/spec_helper.rb +15 -2
- metadata +11 -9
@@ -10,7 +10,7 @@ module VMCManifests
|
|
10
10
|
|
11
11
|
default_paths_to_keys!(apps)
|
12
12
|
|
13
|
-
apps =
|
13
|
+
apps = convert_to_array(apps)
|
14
14
|
|
15
15
|
merge_toplevel!(toplevel, manifest, apps)
|
16
16
|
normalize_apps!(apps)
|
@@ -28,20 +28,43 @@ module VMCManifests
|
|
28
28
|
private
|
29
29
|
|
30
30
|
def normalize_paths!(apps)
|
31
|
-
apps.each do |
|
31
|
+
apps.each do |app|
|
32
32
|
app["path"] = from_manifest(app["path"])
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
def
|
37
|
-
return apps
|
36
|
+
def convert_to_array(apps)
|
37
|
+
return apps if apps.is_a?(Array)
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
ordered_by_deps(apps)
|
40
|
+
end
|
41
|
+
|
42
|
+
# sort applications in dependency order
|
43
|
+
# e.g. if A depends on B, B will be listed before A
|
44
|
+
def ordered_by_deps(apps, processed = Set[])
|
45
|
+
ordered = []
|
46
|
+
apps.each do |tag, info|
|
47
|
+
next if processed.include?(tag)
|
48
|
+
|
49
|
+
if deps = Array(info["depends-on"])
|
50
|
+
dep_apps = {}
|
51
|
+
deps.each do |dep|
|
52
|
+
dep_apps[dep] = apps[dep]
|
53
|
+
end
|
54
|
+
|
55
|
+
processed.add(tag)
|
56
|
+
|
57
|
+
ordered += ordered_by_deps(dep_apps, processed)
|
58
|
+
ordered << info
|
59
|
+
else
|
60
|
+
ordered << info
|
61
|
+
processed.add(tag)
|
62
|
+
end
|
42
63
|
end
|
43
64
|
|
44
|
-
|
65
|
+
ordered.each { |app| app.delete("depends-on") }
|
66
|
+
|
67
|
+
ordered
|
45
68
|
end
|
46
69
|
|
47
70
|
def default_paths_to_keys!(apps)
|
@@ -53,7 +76,7 @@ module VMCManifests
|
|
53
76
|
end
|
54
77
|
|
55
78
|
def normalize_apps!(apps)
|
56
|
-
apps.
|
79
|
+
apps.each do |app|
|
57
80
|
normalize_app!(app)
|
58
81
|
end
|
59
82
|
end
|
@@ -61,8 +84,8 @@ module VMCManifests
|
|
61
84
|
def merge_toplevel!(toplevel, manifest, apps)
|
62
85
|
return if toplevel.empty?
|
63
86
|
|
64
|
-
apps.
|
65
|
-
|
87
|
+
apps.collect! do |a|
|
88
|
+
toplevel.merge(a)
|
66
89
|
end
|
67
90
|
|
68
91
|
toplevel.each do |k, _|
|
@@ -4,55 +4,63 @@ require "vmc/plugin"
|
|
4
4
|
require "manifests-vmc-plugin"
|
5
5
|
|
6
6
|
|
7
|
-
class
|
7
|
+
class ManifestsPlugin < VMC::App::Base
|
8
8
|
include VMCManifests
|
9
|
+
include VMC::App::Sync
|
9
10
|
|
10
11
|
option :manifest, :aliases => "-m", :value => :file,
|
11
12
|
:desc => "Path to manifest file to use"
|
12
13
|
|
13
14
|
|
15
|
+
def wrap_with_optional_name(name_made_optional, cmd, input)
|
16
|
+
return cmd.call if input[:all]
|
17
|
+
|
18
|
+
unless manifest
|
19
|
+
# if the command knows how to handle this
|
20
|
+
if input.has?(:app) || !name_made_optional
|
21
|
+
return cmd.call
|
22
|
+
else
|
23
|
+
return no_apps
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
internal, external = apps_in_manifest(input)
|
28
|
+
|
29
|
+
return cmd.call if internal.empty? && !external.empty?
|
30
|
+
|
31
|
+
show_manifest_usage
|
32
|
+
|
33
|
+
apps = internal + external
|
34
|
+
|
35
|
+
if apps.empty?
|
36
|
+
apps = current_apps if apps.empty?
|
37
|
+
apps = all_apps if apps.empty?
|
38
|
+
apps = apps.collect { |app| app[:name] }
|
39
|
+
end
|
40
|
+
|
41
|
+
return no_apps if apps.empty?
|
42
|
+
|
43
|
+
apps.each.with_index do |app, num|
|
44
|
+
line unless quiet? || num == 0
|
45
|
+
cmd.call(input.without(:apps).merge_given(:app => app))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
14
49
|
# basic commands that, when given no name, act on the
|
15
50
|
# app(s) described by the manifest, in dependency-order
|
16
51
|
[ :start, :restart, :instances, :logs, :env,
|
17
52
|
:health, :stats, :scale, :app
|
18
53
|
].each do |wrap|
|
19
|
-
|
54
|
+
name_made_optional = change_argument(wrap, :app, :optional)
|
20
55
|
|
21
56
|
around(wrap) do |cmd, input|
|
22
|
-
|
23
|
-
|
24
|
-
unless manifest
|
25
|
-
if optional_name && !input.has?(:app)
|
26
|
-
no_apps
|
27
|
-
else
|
28
|
-
next cmd.call
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
show_manifest_usage
|
33
|
-
|
34
|
-
num = 0
|
35
|
-
rest =
|
36
|
-
specific_apps_or_all(input) do |info|
|
37
|
-
puts "" unless quiet? || num == 0
|
38
|
-
cmd.call(input.without(:apps).merge_given(:app => info[:name]))
|
39
|
-
num += 1
|
40
|
-
end
|
41
|
-
|
42
|
-
if rest
|
43
|
-
rest.each do |name|
|
44
|
-
cmd.call(input.without(:apps).merge(:app => name))
|
45
|
-
end
|
46
|
-
|
47
|
-
# fail manually for commands whose name we made optional
|
48
|
-
elsif optional_name
|
49
|
-
no_apps
|
50
|
-
end
|
57
|
+
wrap_with_optional_name(name_made_optional, cmd, input)
|
51
58
|
end
|
52
59
|
end
|
53
60
|
|
54
61
|
|
55
62
|
# same as above but in reverse dependency-order
|
63
|
+
# TODO: do we care about this?
|
56
64
|
[:stop, :delete].each do |wrap|
|
57
65
|
around(wrap) do |cmd, input|
|
58
66
|
next cmd.call if input[:all] || !manifest
|
@@ -76,6 +84,10 @@ class Manifests < VMC::App::Base
|
|
76
84
|
end
|
77
85
|
|
78
86
|
|
87
|
+
def wrap_push(cmd, input)
|
88
|
+
|
89
|
+
end
|
90
|
+
|
79
91
|
# push and sync meta changes in the manifest
|
80
92
|
# also sets env data on creation if present in manifest
|
81
93
|
#
|
@@ -127,10 +139,13 @@ class Manifests < VMC::App::Base
|
|
127
139
|
a
|
128
140
|
}
|
129
141
|
}) do
|
142
|
+
existing_app = client.app_by_name(app[:name])
|
143
|
+
|
130
144
|
# only set inputs if creating app or updating with --reset
|
131
|
-
if input[:reset] || !
|
145
|
+
if input[:reset] || !existing_app
|
132
146
|
app_input = input.rebase_given(app)
|
133
147
|
else
|
148
|
+
# assign manifest values to detect differences
|
134
149
|
app_input = input.merge(:path => from_manifest(app[:path]))
|
135
150
|
end
|
136
151
|
|
data/lib/manifests-vmc-plugin.rb
CHANGED
@@ -71,19 +71,10 @@ module VMCManifests
|
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
74
|
-
# find an app by its unique tag
|
75
|
-
def app_by_tag(tag)
|
76
|
-
manifest[:applications][tag]
|
77
|
-
end
|
78
|
-
|
79
74
|
# find apps by an identifier, which may be either a tag, a name, or a path
|
80
75
|
def find_apps(identifier)
|
81
76
|
return [] unless manifest
|
82
77
|
|
83
|
-
if app = app_by_tag(identifier)
|
84
|
-
return [app]
|
85
|
-
end
|
86
|
-
|
87
78
|
apps = apps_by(:name, identifier)
|
88
79
|
|
89
80
|
if apps.empty?
|
@@ -93,78 +84,58 @@ module VMCManifests
|
|
93
84
|
apps
|
94
85
|
end
|
95
86
|
|
96
|
-
#
|
97
|
-
# inputs for each app
|
98
|
-
def each_app(&blk)
|
99
|
-
return unless manifest
|
100
|
-
|
101
|
-
ordered_by_deps(manifest[:applications]).each(&blk)
|
102
|
-
end
|
103
|
-
|
104
|
-
# return all the apps described by the manifest, in dependency order
|
87
|
+
# return all the apps described by the manifest
|
105
88
|
def all_apps
|
106
|
-
|
89
|
+
manifest[:applications]
|
90
|
+
end
|
107
91
|
|
108
|
-
|
109
|
-
|
92
|
+
def current_apps
|
93
|
+
manifest[:applications].select do |app|
|
94
|
+
next unless app[:path]
|
95
|
+
from_manifest(app[:path]) == Dir.pwd
|
110
96
|
end
|
111
|
-
|
112
|
-
apps
|
113
97
|
end
|
114
98
|
|
115
|
-
#
|
116
|
-
#
|
117
|
-
|
118
|
-
def specific_apps_or_all(input = nil, use_name = true, &blk)
|
99
|
+
# splits the user's input, resolving paths with the manifest,
|
100
|
+
# into internal/external apps
|
101
|
+
def apps_in_manifest(input = nil, use_name = true, &blk)
|
119
102
|
names_or_paths =
|
120
103
|
if input.has?(:apps)
|
121
104
|
# names may be given but be [], which will still cause
|
122
105
|
# interaction, so use #direct instead of #[] here
|
123
106
|
input.direct(:apps)
|
124
107
|
elsif input.has?(:app)
|
125
|
-
[input
|
108
|
+
[input.direct(:app)]
|
126
109
|
else
|
127
110
|
[]
|
128
111
|
end
|
129
112
|
|
130
|
-
|
131
|
-
in_manifest = []
|
132
|
-
|
133
|
-
if names_or_paths.empty?
|
134
|
-
apps = find_apps(Dir.pwd)
|
135
|
-
|
136
|
-
if !apps.empty?
|
137
|
-
in_manifest += apps
|
138
|
-
else
|
139
|
-
each_app(&blk)
|
140
|
-
return []
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
113
|
+
internal = []
|
144
114
|
external = []
|
115
|
+
|
145
116
|
names_or_paths.each do |x|
|
146
117
|
if x.is_a?(String)
|
147
|
-
|
118
|
+
if x =~ %r([/\\])
|
119
|
+
apps = find_apps(File.expand_path(x))
|
148
120
|
|
149
|
-
|
121
|
+
if apps.empty?
|
122
|
+
fail("Path #{b(x)} is not present in manifest #{b(relative_manifest_file)}.")
|
123
|
+
end
|
124
|
+
else
|
125
|
+
apps = find_apps(x)
|
126
|
+
end
|
150
127
|
|
151
128
|
if !apps.empty?
|
152
|
-
|
153
|
-
elsif app = client.app_by_name(x)
|
154
|
-
external << app
|
129
|
+
internal += apps.collect { |app| app[:name] }
|
155
130
|
else
|
156
|
-
|
131
|
+
external << x
|
157
132
|
end
|
158
133
|
else
|
159
134
|
external << x
|
160
135
|
end
|
161
136
|
end
|
162
137
|
|
163
|
-
|
164
|
-
blk.call app
|
165
|
-
end
|
166
|
-
|
167
|
-
external
|
138
|
+
[internal, external]
|
168
139
|
end
|
169
140
|
|
170
141
|
def create_manifest_for(app, path)
|
@@ -213,10 +184,14 @@ module VMCManifests
|
|
213
184
|
|
214
185
|
private
|
215
186
|
|
187
|
+
def relative_manifest_file
|
188
|
+
Pathname.new(manifest_file).relative_path_from(Pathname.pwd)
|
189
|
+
end
|
190
|
+
|
216
191
|
def show_manifest_usage
|
217
192
|
return if @@showed_manifest_usage
|
218
193
|
|
219
|
-
path =
|
194
|
+
path = relative_manifest_file
|
220
195
|
line "Using manifest file #{c(path, :name)}"
|
221
196
|
line
|
222
197
|
|
@@ -228,14 +203,9 @@ module VMCManifests
|
|
228
203
|
end
|
229
204
|
|
230
205
|
def apps_by(attr, val)
|
231
|
-
|
232
|
-
|
233
|
-
if info[attr] == val
|
234
|
-
found << info
|
235
|
-
end
|
206
|
+
manifest[:applications].select do |info|
|
207
|
+
info[attr] == val
|
236
208
|
end
|
237
|
-
|
238
|
-
found
|
239
209
|
end
|
240
210
|
|
241
211
|
# expand a path relative to the manifest file's directory
|
@@ -243,33 +213,6 @@ module VMCManifests
|
|
243
213
|
File.expand_path(path, File.dirname(manifest_file))
|
244
214
|
end
|
245
215
|
|
246
|
-
# sort applications in dependency order
|
247
|
-
# e.g. if A depends on B, B will be listed before A
|
248
|
-
def ordered_by_deps(apps, processed = Set[])
|
249
|
-
ordered = []
|
250
|
-
apps.each do |tag, info|
|
251
|
-
next if processed.include?(tag)
|
252
|
-
|
253
|
-
if deps = Array(info[:"depends-on"])
|
254
|
-
dep_apps = {}
|
255
|
-
deps.each do |dep|
|
256
|
-
dep = dep.to_sym
|
257
|
-
fail "Circular dependency detected." if processed.include? dep
|
258
|
-
dep_apps[dep] = apps[dep]
|
259
|
-
end
|
260
|
-
|
261
|
-
processed.add(tag)
|
262
|
-
|
263
|
-
ordered += ordered_by_deps(dep_apps, processed)
|
264
|
-
ordered << info
|
265
|
-
else
|
266
|
-
ordered << info
|
267
|
-
processed.add(tag)
|
268
|
-
end
|
269
|
-
end
|
270
|
-
|
271
|
-
ordered
|
272
|
-
end
|
273
216
|
|
274
217
|
def ask_to_save(input, app)
|
275
218
|
return if manifest_file
|
@@ -344,6 +287,8 @@ module VMCManifests
|
|
344
287
|
end
|
345
288
|
|
346
289
|
to_bind.each do |s|
|
290
|
+
next if app.binds?(s)
|
291
|
+
|
347
292
|
# TODO: splat
|
348
293
|
invoke :bind_service, :app => app, :service => s
|
349
294
|
end
|
@@ -18,7 +18,7 @@ describe VMCManifests::Normalizer do
|
|
18
18
|
|
19
19
|
it "sets the path to their tag, assuming it's a path" do
|
20
20
|
expect(subject).to eq(
|
21
|
-
:applications => { :
|
21
|
+
:applications => [{ :name => "foo", :path => "." }])
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
@@ -27,9 +27,7 @@ describe VMCManifests::Normalizer do
|
|
27
27
|
|
28
28
|
it "sets it to none" do
|
29
29
|
expect(subject).to eq(
|
30
|
-
:applications => {
|
31
|
-
:"." => { :path => ".", :url => "none" }
|
32
|
-
})
|
30
|
+
:applications => [{ :path => ".", :url => "none" }])
|
33
31
|
end
|
34
32
|
end
|
35
33
|
|
@@ -41,7 +39,7 @@ describe VMCManifests::Normalizer do
|
|
41
39
|
|
42
40
|
it 'keeps the properties at the toplevel' do
|
43
41
|
expect(subject).to eq(
|
44
|
-
:applications => { :
|
42
|
+
:applications => [{ :name => "foo", :path => "." }],
|
45
43
|
:properties => { :fizz => "buzz" })
|
46
44
|
end
|
47
45
|
end
|
@@ -52,7 +50,7 @@ describe VMCManifests::Normalizer do
|
|
52
50
|
|
53
51
|
it 'adds it as an application with path .' do
|
54
52
|
expect(subject).to eq(
|
55
|
-
:applications => { :
|
53
|
+
:applications => [{ :name => "foo", :path => "." }])
|
56
54
|
end
|
57
55
|
end
|
58
56
|
|
@@ -61,9 +59,7 @@ describe VMCManifests::Normalizer do
|
|
61
59
|
|
62
60
|
it 'adds it as an application with the proper tag and path' do
|
63
61
|
expect(subject).to eq(
|
64
|
-
:applications => {
|
65
|
-
:"0" => { :name => "foo", :path => "./foo" }
|
66
|
-
})
|
62
|
+
:applications => [{ :name => "foo", :path => "./foo" }])
|
67
63
|
end
|
68
64
|
end
|
69
65
|
end
|
@@ -80,27 +76,50 @@ describe VMCManifests::Normalizer do
|
|
80
76
|
}
|
81
77
|
|
82
78
|
it "merges the toplevel attributes into the applications" do
|
83
|
-
expect(subject).to
|
84
|
-
:
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
:"./bar" =>
|
89
|
-
{ :name => "bar", :path => "./bar", :runtime => "ruby19" },
|
90
|
-
|
91
|
-
:"./baz" =>
|
92
|
-
{ :name => "baz", :path => "./baz", :runtime => "ruby18" }
|
93
|
-
})
|
79
|
+
expect(subject[:applications]).to match_array [
|
80
|
+
{ :name => "foo", :path => "./foo", :runtime => "ruby19" },
|
81
|
+
{ :name => "bar", :path => "./bar", :runtime => "ruby19" },
|
82
|
+
{ :name => "baz", :path => "./baz", :runtime => "ruby18" }
|
83
|
+
]
|
94
84
|
end
|
95
85
|
end
|
96
86
|
end
|
97
87
|
|
98
|
-
context 'with a manifest where applications is
|
99
|
-
let(:manifest) { { "applications" =>
|
88
|
+
context 'with a manifest where applications is a hash' do
|
89
|
+
let(:manifest) { { "applications" => { "foo" => { "name" => "foo" } } } }
|
100
90
|
|
101
|
-
it 'converts the array to a hash, with the path as
|
91
|
+
it 'converts the array to a hash, with the path as the key' do
|
102
92
|
expect(subject).to eq(
|
103
|
-
:applications => { :
|
93
|
+
:applications => [{ :name => "foo", :path => "foo" }])
|
94
|
+
end
|
95
|
+
|
96
|
+
context "and the applications had dependencies" do
|
97
|
+
let(:manifest) do
|
98
|
+
{ "applications" => {
|
99
|
+
"bar" => { "name" => "bar", "depends-on" => "foo" },
|
100
|
+
"foo" => { "name" => "foo" }
|
101
|
+
}
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
it "converts using dependency order" do
|
106
|
+
expect(subject).to eq(
|
107
|
+
:applications => [{ :name => "foo", :path => "foo" }, { :name => "bar", :path => "bar" }])
|
108
|
+
end
|
109
|
+
|
110
|
+
context "and there's a circular dependency" do
|
111
|
+
let(:manifest) do
|
112
|
+
{ "applications" => {
|
113
|
+
"bar" => { "name" => "bar", "depends-on" => "foo" },
|
114
|
+
"foo" => { "name" => "foo", "depends-on" => "bar" }
|
115
|
+
}
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
119
|
+
it "doesn't blow up" do
|
120
|
+
expect(subject).to be_true
|
121
|
+
end
|
122
|
+
end
|
104
123
|
end
|
105
124
|
end
|
106
125
|
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
require "manifests-vmc-plugin/plugin"
|
4
|
+
|
5
|
+
|
6
|
+
describe ManifestsPlugin do
|
7
|
+
let(:manifest) { {} }
|
8
|
+
let(:manifest_file) { nil }
|
9
|
+
let(:inputs_hash) { {} }
|
10
|
+
let(:given_hash) { {} }
|
11
|
+
let(:global_hash) { { :quiet => true } }
|
12
|
+
let(:inputs) { Mothership::Inputs.new(nil, nil, inputs_hash, given_hash, global_hash) }
|
13
|
+
let(:plugin) { ManifestsPlugin.new(nil, inputs) }
|
14
|
+
|
15
|
+
before do
|
16
|
+
stub(plugin).manifest { manifest }
|
17
|
+
stub(plugin).manifest_file { manifest_file } if manifest_file
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#wrap_with_optional_name" do
|
21
|
+
let(:name_made_optional) { true }
|
22
|
+
let(:command) { mock! }
|
23
|
+
|
24
|
+
subject { plugin.wrap_with_optional_name(name_made_optional, command, inputs) }
|
25
|
+
|
26
|
+
context "when --all is given" do
|
27
|
+
let(:inputs_hash) { { :all => true } }
|
28
|
+
|
29
|
+
it "skips all manifest-related logic, and invokes the command" do
|
30
|
+
mock(command).call
|
31
|
+
dont_allow(plugin).show_manifest_usage
|
32
|
+
subject
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "when there is no manifest" do
|
37
|
+
let(:manifest) { nil }
|
38
|
+
|
39
|
+
context "and an app is given" do
|
40
|
+
let(:given_hash) { { :app => "foo" } }
|
41
|
+
|
42
|
+
it "passes through to the command" do
|
43
|
+
mock(command).call
|
44
|
+
dont_allow(plugin).show_manifest_usage
|
45
|
+
subject
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "and an app is NOT given" do
|
50
|
+
let(:inputs_hash) { {} }
|
51
|
+
|
52
|
+
context "and we made it optional" do
|
53
|
+
it "fails manually" do
|
54
|
+
mock(plugin).no_apps
|
55
|
+
subject
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "and we did NOT make it optional" do
|
60
|
+
let(:name_made_optional) { false }
|
61
|
+
|
62
|
+
it "passes through to the command" do
|
63
|
+
mock(command).call
|
64
|
+
dont_allow(plugin).show_manifest_usage
|
65
|
+
subject
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context "when there is a manifest" do
|
72
|
+
let(:manifest_file) { "/abc/manifest.yml" }
|
73
|
+
|
74
|
+
before do
|
75
|
+
stub(plugin).show_manifest_usage
|
76
|
+
end
|
77
|
+
|
78
|
+
context "when no apps are given" do
|
79
|
+
context "and the user's working directory matches a particular app in the manifest" do
|
80
|
+
let(:manifest) { { :applications => [{ :name => "foo", :path => "/abc/foo" }] } }
|
81
|
+
|
82
|
+
it "calls the command for only that app" do
|
83
|
+
mock(command).call(anything) do |inputs|
|
84
|
+
expect(inputs.given[:app]).to eq "foo"
|
85
|
+
end
|
86
|
+
|
87
|
+
stub(Dir).pwd { "/abc/foo" }
|
88
|
+
|
89
|
+
subject
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context "and the user's working directory isn't in the manifest" do
|
94
|
+
let(:manifest) { { :applications => [{ :name => "foo" }, { :name => "bar" }] } }
|
95
|
+
|
96
|
+
it "calls the command for all apps in the manifest" do
|
97
|
+
uncalled_apps = ["foo", "bar"]
|
98
|
+
mock(command).call(anything).twice do |inputs|
|
99
|
+
uncalled_apps.delete inputs.given[:app]
|
100
|
+
end
|
101
|
+
|
102
|
+
subject
|
103
|
+
|
104
|
+
expect(uncalled_apps).to be_empty
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context "when any of the given apps are not in the manifest" do
|
110
|
+
let(:manifest) { { :applications => [{ :name => "a" }, { :name => "b" }] } }
|
111
|
+
|
112
|
+
context "and --apps is given" do
|
113
|
+
let(:given_hash) { { :apps => ["x", "a"] } }
|
114
|
+
|
115
|
+
it "passes through to the original command" do
|
116
|
+
mock(plugin).show_manifest_usage
|
117
|
+
|
118
|
+
uncalled_apps = ["a", "x"]
|
119
|
+
mock(command).call(anything).twice do |inputs|
|
120
|
+
uncalled_apps.delete inputs.given[:app]
|
121
|
+
end
|
122
|
+
|
123
|
+
subject
|
124
|
+
|
125
|
+
expect(uncalled_apps).to be_empty
|
126
|
+
subject
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context "when none of the given apps are in the manifest" do
|
132
|
+
let(:manifest) { { :applications => [{ :name => "a" }, { :name => "b" }] } }
|
133
|
+
|
134
|
+
context "and --apps is given" do
|
135
|
+
let(:given_hash) { { :apps => ["x", "y"] } }
|
136
|
+
|
137
|
+
it "passes through to the original command" do
|
138
|
+
dont_allow(plugin).show_manifest_usage
|
139
|
+
mock(command).call
|
140
|
+
subject
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
context "when an app name that's in the manifest is given" do
|
146
|
+
let(:manifest) { { :applications => [{ :name => "foo" }] } }
|
147
|
+
let(:given_hash) { { :app => "foo" } }
|
148
|
+
|
149
|
+
it "calls the command with that app" do
|
150
|
+
mock(command).call(anything) do |inputs|
|
151
|
+
expect(inputs.given[:app]).to eq "foo"
|
152
|
+
end
|
153
|
+
|
154
|
+
subject
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
context "when a path to an app that's in the manifest is given" do
|
159
|
+
let(:manifest) { { :applications => [{ :name => "foo", :path => "/abc/foo" }] } }
|
160
|
+
let(:given_hash) { { :app => "/abc/foo" } }
|
161
|
+
|
162
|
+
it "calls the command with that app" do
|
163
|
+
mock(command).call(anything) do |inputs|
|
164
|
+
expect(inputs.given[:app]).to eq "foo"
|
165
|
+
end
|
166
|
+
|
167
|
+
subject
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
describe "#wrap_push" do
|
174
|
+
context "with a manifest" do
|
175
|
+
|
176
|
+
end
|
177
|
+
|
178
|
+
context "without a manifest" do
|
179
|
+
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
@@ -1,20 +1,39 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'manifests-vmc-plugin'
|
3
3
|
|
4
|
-
require 'cfoundry/test_support'
|
5
|
-
|
6
4
|
describe VMCManifests do
|
5
|
+
let(:inputs_hash) { {} }
|
6
|
+
let(:given_hash) { {} }
|
7
|
+
let(:global_hash) { {} }
|
8
|
+
let(:inputs) { Mothership::Inputs.new(nil, nil, inputs_hash, given_hash, global_hash) }
|
9
|
+
|
7
10
|
let(:cmd) do
|
8
|
-
manifest = VMC::App::Push.new
|
11
|
+
manifest = VMC::App::Push.new(nil, inputs)
|
9
12
|
manifest.extend VMCManifests
|
13
|
+
stub(manifest).client { client }
|
10
14
|
manifest
|
11
15
|
end
|
12
16
|
|
13
17
|
let(:target_base) { "some-cloud.com" }
|
14
18
|
|
19
|
+
let(:foo) { fake(:app, :name => "foo") }
|
20
|
+
let(:bar) { fake(:app, :name => "bar") }
|
21
|
+
let(:baz) { fake(:app, :name => "baz") }
|
22
|
+
let(:xxx) { fake(:app, :name => "xxx") }
|
23
|
+
let(:yyy) { fake(:app, :name => "yyy") }
|
24
|
+
|
25
|
+
let(:client) do
|
26
|
+
fake_client :apps => [foo, bar, baz, xxx, yyy]
|
27
|
+
end
|
28
|
+
|
29
|
+
let(:manifest_file) { "/abc/manifest.yml" }
|
30
|
+
|
15
31
|
before do
|
16
32
|
stub(cmd).target_base { target_base }
|
17
33
|
stub(cmd).v2? { true }
|
34
|
+
|
35
|
+
stub(cmd).manifest { manifest }
|
36
|
+
stub(cmd).manifest_file { manifest_file }
|
18
37
|
end
|
19
38
|
|
20
39
|
describe '#find_apps' do
|
@@ -32,7 +51,7 @@ describe VMCManifests do
|
|
32
51
|
:framework => fake(:framework),
|
33
52
|
:runtime => fake(:runtime),
|
34
53
|
:memory => 2048,
|
35
|
-
:
|
54
|
+
:total_instances => 2,
|
36
55
|
:command => "ruby main.rb",
|
37
56
|
:routes => [
|
38
57
|
fake(:route,
|
@@ -66,10 +85,9 @@ describe VMCManifests do
|
|
66
85
|
its(["command"]) { should eq "ruby main.rb" }
|
67
86
|
|
68
87
|
it "contains the service information" do
|
69
|
-
|
70
|
-
expect(manifest["services"]).to be_a Hash
|
88
|
+
expect(subject["services"]).to be_a Hash
|
71
89
|
|
72
|
-
services =
|
90
|
+
services = subject["services"]
|
73
91
|
app.service_bindings.each do |b|
|
74
92
|
service = b.service_instance
|
75
93
|
|
@@ -97,7 +115,7 @@ describe VMCManifests do
|
|
97
115
|
:framework => fake(:framework),
|
98
116
|
:runtime => fake(:runtime),
|
99
117
|
:memory => 2048,
|
100
|
-
:
|
118
|
+
:total_instances => 2
|
101
119
|
}
|
102
120
|
|
103
121
|
its(["url"]) { should eq "none" }
|
@@ -109,7 +127,7 @@ describe VMCManifests do
|
|
109
127
|
:framework => fake(:framework),
|
110
128
|
:runtime => fake(:runtime),
|
111
129
|
:memory => 2048,
|
112
|
-
:
|
130
|
+
:total_instances => 2
|
113
131
|
}
|
114
132
|
|
115
133
|
it { should_not include "command" }
|
@@ -121,10 +139,183 @@ describe VMCManifests do
|
|
121
139
|
:framework => fake(:framework),
|
122
140
|
:runtime => fake(:runtime),
|
123
141
|
:memory => 2048,
|
124
|
-
:
|
142
|
+
:total_instances => 2
|
125
143
|
}
|
126
144
|
|
127
145
|
it { should_not include "services" }
|
128
146
|
end
|
129
147
|
end
|
148
|
+
|
149
|
+
describe "#setup_services" do
|
150
|
+
let(:service_bindings) { [] }
|
151
|
+
let(:app) { fake :app, :service_bindings => service_bindings }
|
152
|
+
|
153
|
+
before do
|
154
|
+
dont_allow_ask(anything, anything)
|
155
|
+
end
|
156
|
+
|
157
|
+
context "when services are defined in the manifest" do
|
158
|
+
let(:info) {
|
159
|
+
{ :services => { "service-1" => { :label => "mysql", :plan => "100" } } }
|
160
|
+
}
|
161
|
+
|
162
|
+
let(:service_1) { fake(:service_instance, :name => "service-1") }
|
163
|
+
|
164
|
+
let(:plan_100) { fake :service_plan, :name => "100" }
|
165
|
+
|
166
|
+
let(:mysql) {
|
167
|
+
fake(
|
168
|
+
:service,
|
169
|
+
:label => "mysql",
|
170
|
+
:provider => "core",
|
171
|
+
:service_plans => [plan_100])
|
172
|
+
}
|
173
|
+
|
174
|
+
let(:service_instances) { [] }
|
175
|
+
|
176
|
+
let(:client) {
|
177
|
+
fake_client :services => [mysql], :service_instances => service_instances
|
178
|
+
}
|
179
|
+
|
180
|
+
context "and the services exist" do
|
181
|
+
let(:service_instances) { [service_1] }
|
182
|
+
|
183
|
+
context "and are already bound" do
|
184
|
+
let(:service_bindings) { [fake(:service_binding, :service_instance => service_1)] }
|
185
|
+
|
186
|
+
it "does neither create nor bind the service again" do
|
187
|
+
dont_allow(cmd).invoke :create_service, anything
|
188
|
+
dont_allow(cmd).invoke :bind_service, anything
|
189
|
+
cmd.send(:setup_services, app, info)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
context "but are not bound" do
|
194
|
+
it "does not create the services" do
|
195
|
+
dont_allow(cmd).invoke :create_service, anything
|
196
|
+
stub(cmd).invoke :bind_service, anything
|
197
|
+
cmd.send(:setup_services, app, info)
|
198
|
+
end
|
199
|
+
|
200
|
+
it "binds the service" do
|
201
|
+
mock(cmd).invoke :bind_service, :app => app, :service => service_1
|
202
|
+
cmd.send(:setup_services, app, info)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
context "and the services do not exist" do
|
208
|
+
it "creates the services" do
|
209
|
+
mock(cmd).invoke :create_service, :app => app,
|
210
|
+
:name => service_1.name, :offering => mysql, :plan => plan_100
|
211
|
+
dont_allow(cmd).invoke :bind_service, anything
|
212
|
+
cmd.send(:setup_services, app, info)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
context "when there are no services defined" do
|
218
|
+
let(:info) { {} }
|
219
|
+
|
220
|
+
it "does not ask anything" do
|
221
|
+
cmd.send(:setup_services, app, info)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
describe "#apps_in_manifest" do
|
227
|
+
let(:manifest) do
|
228
|
+
{:applications => [
|
229
|
+
{:name => "foo", :path => "/abc/foo"},
|
230
|
+
{:name => "bar", :path => "/abc/bar"},
|
231
|
+
{:name => "baz", :path => "/abc/baz"}
|
232
|
+
]}
|
233
|
+
end
|
234
|
+
|
235
|
+
subject { cmd.apps_in_manifest(inputs) }
|
236
|
+
|
237
|
+
context "when no apps are passed" do
|
238
|
+
let(:given_hash) { {} }
|
239
|
+
|
240
|
+
its(:first) { should eq [] }
|
241
|
+
its(:last) { should eq [] }
|
242
|
+
end
|
243
|
+
|
244
|
+
context "when app names are passed" do
|
245
|
+
context "and all of them are in the manifest" do
|
246
|
+
let(:given_hash) { { :apps => ["foo", "bar"] } }
|
247
|
+
|
248
|
+
its(:first) { should eq ["foo", "bar"] }
|
249
|
+
its(:last) { should eq [] }
|
250
|
+
end
|
251
|
+
|
252
|
+
context "and one of them is in the manifest" do
|
253
|
+
let(:given_hash) { { :apps => ["foo", "xxx"] } }
|
254
|
+
|
255
|
+
its(:first) { should eq ["foo"] }
|
256
|
+
its(:last) { should eq ["xxx"] }
|
257
|
+
end
|
258
|
+
|
259
|
+
context "and none of them are in the manifest" do
|
260
|
+
let(:given_hash) { { :apps => ["xxx", "yyy"] } }
|
261
|
+
|
262
|
+
its(:first) { should eq [] }
|
263
|
+
its(:last) { should eq ["xxx", "yyy"] }
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
context "when apps are passed as paths" do
|
268
|
+
context "and the paths are in the manifest" do
|
269
|
+
let(:given_hash) { { :apps => ["/abc/foo"] } }
|
270
|
+
|
271
|
+
its(:first) { should eq ["foo"] }
|
272
|
+
its(:last) { should eq [] }
|
273
|
+
end
|
274
|
+
|
275
|
+
context "and any path is not in the manifest" do
|
276
|
+
let(:given_hash) { { :apps => ["/abc/xxx"] } }
|
277
|
+
|
278
|
+
it "fails with a manifest-specific method (i.e. path not in manifest)" do
|
279
|
+
expect { subject }.to raise_error(VMC::UserError, /Path .+ is not present in manifest/)
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
describe "#all_apps" do
|
286
|
+
let(:applications) do
|
287
|
+
[
|
288
|
+
{:name => "foo", :path => "/abc"},
|
289
|
+
{:name => "bar", :path => "/abc"},
|
290
|
+
{:name => "baz", :path => "/abc/baz"}
|
291
|
+
]
|
292
|
+
end
|
293
|
+
|
294
|
+
let(:manifest) do
|
295
|
+
{ :applications => applications }
|
296
|
+
end
|
297
|
+
|
298
|
+
subject { cmd.all_apps }
|
299
|
+
|
300
|
+
it "returns all of the apps described in the manifest, as hashes" do
|
301
|
+
expect(subject).to eq applications
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
describe "#current_apps" do
|
306
|
+
let(:manifest) do
|
307
|
+
{:applications => [
|
308
|
+
{:name => "foo", :path => "/abc"},
|
309
|
+
{:name => "bar", :path => "/abc"},
|
310
|
+
{:name => "baz", :path => "/abc/baz"}
|
311
|
+
]}
|
312
|
+
end
|
313
|
+
|
314
|
+
subject { cmd.current_apps }
|
315
|
+
|
316
|
+
it "returns the applications with the cwd as their path" do
|
317
|
+
stub(Dir).pwd { "/abc" }
|
318
|
+
expect(subject).to eq [{ :name => "foo", :path => "/abc"}, { :name => "bar", :path => "/abc" }]
|
319
|
+
end
|
320
|
+
end
|
130
321
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
-
|
1
|
+
SPEC_ROOT = File.dirname(__FILE__).freeze
|
2
|
+
|
3
|
+
require "rspec"
|
2
4
|
require "vmc"
|
3
|
-
require "
|
5
|
+
require "cfoundry"
|
6
|
+
require "webmock"
|
7
|
+
require "cfoundry/test_support"
|
8
|
+
require "vmc/test_support"
|
9
|
+
|
10
|
+
WebMock.disable_net_connect!
|
11
|
+
|
12
|
+
RSpec.configure do |c|
|
13
|
+
c.include Fake::FakeMethods
|
14
|
+
c.include VMC::TestSupport::InteractHelper
|
15
|
+
c.mock_with :rr
|
16
|
+
end
|
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: 7
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 6
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.6.0
|
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: 2013-
|
18
|
+
date: 2013-02-06 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: cfoundry
|
@@ -25,12 +25,12 @@ dependencies:
|
|
25
25
|
requirements:
|
26
26
|
- - ~>
|
27
27
|
- !ruby/object:Gem::Version
|
28
|
-
hash:
|
28
|
+
hash: 11
|
29
29
|
segments:
|
30
30
|
- 0
|
31
|
-
-
|
31
|
+
- 5
|
32
32
|
- 0
|
33
|
-
version: 0.
|
33
|
+
version: 0.5.0
|
34
34
|
type: :runtime
|
35
35
|
version_requirements: *id001
|
36
36
|
- !ruby/object:Gem::Dependency
|
@@ -112,7 +112,8 @@ files:
|
|
112
112
|
- lib/manifests-vmc-plugin/plugin.rb
|
113
113
|
- lib/manifests-vmc-plugin/version.rb
|
114
114
|
- lib/manifests-vmc-plugin.rb
|
115
|
-
- spec/loader/normalizer_spec.rb
|
115
|
+
- spec/manifests-vmc-plugin/loader/normalizer_spec.rb
|
116
|
+
- spec/manifests-vmc-plugin/loader/plugin_spec.rb
|
116
117
|
- spec/manifests-vmc-plugin_spec.rb
|
117
118
|
- spec/spec_helper.rb
|
118
119
|
homepage: http://cloudfoundry.com/
|
@@ -149,6 +150,7 @@ signing_key:
|
|
149
150
|
specification_version: 3
|
150
151
|
summary: Cloud Foundry automation via manifest documents.
|
151
152
|
test_files:
|
152
|
-
- spec/loader/normalizer_spec.rb
|
153
|
+
- spec/manifests-vmc-plugin/loader/normalizer_spec.rb
|
154
|
+
- spec/manifests-vmc-plugin/loader/plugin_spec.rb
|
153
155
|
- spec/manifests-vmc-plugin_spec.rb
|
154
156
|
- spec/spec_helper.rb
|