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.
@@ -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