manifests-vmc-plugin 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|