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.
@@ -10,7 +10,7 @@ module VMCManifests
10
10
 
11
11
  default_paths_to_keys!(apps)
12
12
 
13
- apps = convert_from_array(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 |_, app|
31
+ apps.each do |app|
32
32
  app["path"] = from_manifest(app["path"])
33
33
  end
34
34
  end
35
35
 
36
- def convert_from_array(apps)
37
- return apps unless apps.is_a?(Array)
36
+ def convert_to_array(apps)
37
+ return apps if apps.is_a?(Array)
38
38
 
39
- apps_hash = {}
40
- apps.each.with_index do |a, i|
41
- apps_hash[i.to_s] = a
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
- apps_hash
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.each_value do |app|
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.each do |t, a|
65
- apps[t] = toplevel.merge(a)
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 Manifests < VMC::App::Base
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
- optional_name = change_argument(wrap, :app, :optional)
54
+ name_made_optional = change_argument(wrap, :app, :optional)
20
55
 
21
56
  around(wrap) do |cmd, input|
22
- next cmd.call if input[:all]
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] || !client.app_by_name(app[:name])
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
 
@@ -1,3 +1,3 @@
1
1
  module VMCManifests
2
- VERSION = "0.5.0".freeze
2
+ VERSION = "0.6.0".freeze
3
3
  end
@@ -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
- # call a block for each app in a manifest (in dependency order), setting
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
- apps = []
89
+ manifest[:applications]
90
+ end
107
91
 
108
- each_app do |app|
109
- apps << app
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
- # like each_app, but only acts on apps specified as paths instead of names
116
- #
117
- # returns the names that were not paths
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[:app]]
108
+ [input.direct(:app)]
126
109
  else
127
110
  []
128
111
  end
129
112
 
130
- input = input.without(:app, :apps)
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
- path = File.expand_path(x)
118
+ if x =~ %r([/\\])
119
+ apps = find_apps(File.expand_path(x))
148
120
 
149
- apps = find_apps(File.exists?(path) ? path : x)
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
- in_manifest += apps
153
- elsif app = client.app_by_name(x)
154
- external << app
129
+ internal += apps.collect { |app| app[:name] }
155
130
  else
156
- fail("Unknown app '#{x}'")
131
+ external << x
157
132
  end
158
133
  else
159
134
  external << x
160
135
  end
161
136
  end
162
137
 
163
- in_manifest.each do |app|
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 = Pathname.new(manifest_file).relative_path_from(Pathname.pwd)
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
- found = []
232
- manifest[:applications].each do |tag, info|
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 => { :"." => { :name => "foo", :path => "." } })
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 => { :"0" => { :name => "foo", :path => "." } },
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 => { :"0" => { :name => "foo", :path => "." } })
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 eq(
84
- :applications => {
85
- :"./foo" =>
86
- { :name => "foo", :path => "./foo", :runtime => "ruby19" },
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 an array' do
99
- let(:manifest) { { "applications" => [{ "name" => "foo" }] } }
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 .' do
91
+ it 'converts the array to a hash, with the path as the key' do
102
92
  expect(subject).to eq(
103
- :applications => { :"0" => { :name => "foo", :path => "." } })
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
- :instances => 2,
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
- manifest = subject
70
- expect(manifest["services"]).to be_a Hash
88
+ expect(subject["services"]).to be_a Hash
71
89
 
72
- services = manifest["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
- :instances => 2
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
- :instances => 2
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
- :instances => 2
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
- require "cfoundry"
1
+ SPEC_ROOT = File.dirname(__FILE__).freeze
2
+
3
+ require "rspec"
2
4
  require "vmc"
3
- require "vmc/spec_helper"
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: 11
4
+ hash: 7
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 5
8
+ - 6
9
9
  - 0
10
- version: 0.5.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-01-15 00:00:00 Z
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: 15
28
+ hash: 11
29
29
  segments:
30
30
  - 0
31
- - 4
31
+ - 5
32
32
  - 0
33
- version: 0.4.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