manifests-vmc-plugin 0.6.0 → 0.6.1

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.
@@ -98,6 +98,11 @@ module VMCManifests
98
98
 
99
99
  # splits the user's input, resolving paths with the manifest,
100
100
  # into internal/external apps
101
+ #
102
+ # internal apps are returned as their data in the manifest
103
+ #
104
+ # external apps are the strings that the user gave, to be
105
+ # passed along wholesale to the wrapped command
101
106
  def apps_in_manifest(input = nil, use_name = true, &blk)
102
107
  names_or_paths =
103
108
  if input.has?(:apps)
@@ -106,6 +111,8 @@ module VMCManifests
106
111
  input.direct(:apps)
107
112
  elsif input.has?(:app)
108
113
  [input.direct(:app)]
114
+ elsif input.has?(:name)
115
+ [input.direct(:name)]
109
116
  else
110
117
  []
111
118
  end
@@ -126,7 +133,7 @@ module VMCManifests
126
133
  end
127
134
 
128
135
  if !apps.empty?
129
- internal += apps.collect { |app| app[:name] }
136
+ internal += apps
130
137
  else
131
138
  external << x
132
139
  end
@@ -202,6 +209,12 @@ module VMCManifests
202
209
  fail "No applications or manifest to operate on."
203
210
  end
204
211
 
212
+ def warn_reset_changes
213
+ line c("Not applying manifest changes without --reset", :warning)
214
+ line "See `vmc diff` for more details."
215
+ line
216
+ end
217
+
205
218
  def apps_by(attr, val)
206
219
  manifest[:applications].select do |info|
207
220
  info[attr] == val
@@ -3,11 +3,8 @@ module VMCManifests
3
3
  def resolve(manifest, resolver)
4
4
  new = {}
5
5
 
6
- new[:applications] = {}
7
-
8
- manifest[:applications].each do |k, v|
9
- new[:applications][k] =
10
- resolve_lexically(resolver, v, [manifest])
6
+ new[:applications] = manifest[:applications].collect do |app|
7
+ resolve_lexically(resolver, app, [manifest])
11
8
  end
12
9
 
13
10
  resolve_lexically(resolver, new, [new])
@@ -6,12 +6,31 @@ require "manifests-vmc-plugin"
6
6
 
7
7
  class ManifestsPlugin < VMC::App::Base
8
8
  include VMCManifests
9
- include VMC::App::Sync
10
9
 
11
10
  option :manifest, :aliases => "-m", :value => :file,
12
11
  :desc => "Path to manifest file to use"
13
12
 
14
13
 
14
+ [ :start, :restart, :instances, :logs, :env, :health, :stats,
15
+ :scale, :app, :stop, :delete
16
+ ].each do |wrap|
17
+ name_made_optional = change_argument(wrap, :app, :optional)
18
+
19
+ around(wrap) do |cmd, input|
20
+ wrap_with_optional_name(name_made_optional, cmd, input)
21
+ end
22
+ end
23
+
24
+
25
+ add_input :push, :reset, :desc => "Reset to values in the manifest",
26
+ :default => false
27
+
28
+ around(:push) do |push, input|
29
+ wrap_push(push, input)
30
+ end
31
+
32
+ private
33
+
15
34
  def wrap_with_optional_name(name_made_optional, cmd, input)
16
35
  return cmd.call if input[:all]
17
36
 
@@ -30,14 +49,14 @@ class ManifestsPlugin < VMC::App::Base
30
49
 
31
50
  show_manifest_usage
32
51
 
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] }
52
+ if internal.empty? && external.empty?
53
+ internal = current_apps if internal.empty?
54
+ internal = all_apps if internal.empty?
39
55
  end
40
56
 
57
+ internal = internal.collect { |app| app[:name] }
58
+
59
+ apps = internal + external
41
60
  return no_apps if apps.empty?
42
61
 
43
62
  apps.each.with_index do |app, num|
@@ -46,115 +65,80 @@ class ManifestsPlugin < VMC::App::Base
46
65
  end
47
66
  end
48
67
 
49
- # basic commands that, when given no name, act on the
50
- # app(s) described by the manifest, in dependency-order
51
- [ :start, :restart, :instances, :logs, :env,
52
- :health, :stats, :scale, :app
53
- ].each do |wrap|
54
- name_made_optional = change_argument(wrap, :app, :optional)
55
-
56
- around(wrap) do |cmd, input|
57
- wrap_with_optional_name(name_made_optional, cmd, input)
58
- end
68
+ def apply_changes(app, input)
69
+ app.memory = megabytes(input[:memory]) if input.has?(:memory)
70
+ app.total_instances = input[:instances] if input.has?(:instances)
71
+ app.command = input[:command] if input.has?(:command)
72
+ app.production = input[:plan].upcase.start_with?("P") if input.has?(:plan)
73
+ app.framework = input[:framework] if input.has?(:framework)
74
+ app.runtime = input[:runtime] if input.has?(:runtime)
75
+ app.buildpack = input[:buildpack] if input.has?(:buildpack)
59
76
  end
60
77
 
78
+ def wrap_push(push, input)
79
+ unless manifest
80
+ create_and_save_manifest(push, input)
81
+ return
82
+ end
61
83
 
62
- # same as above but in reverse dependency-order
63
- # TODO: do we care about this?
64
- [:stop, :delete].each do |wrap|
65
- around(wrap) do |cmd, input|
66
- next cmd.call if input[:all] || !manifest
84
+ particular, external = apps_in_manifest(input)
67
85
 
68
- show_manifest_usage
86
+ unless external.empty?
87
+ fail "Could not find #{b(external.join(", "))}' in the manifest."
88
+ end
69
89
 
70
- reversed = []
71
- rest =
72
- specific_apps_or_all(input) do |info|
73
- reversed.unshift info[:name]
74
- end
90
+ apps = particular.empty? ? all_apps : particular
75
91
 
76
- unless reversed.empty?
77
- cmd.call(input.without(:apps).merge_given(:apps => reversed))
78
- end
92
+ show_manifest_usage
79
93
 
80
- unless rest.empty?
81
- cmd.call(input.without(:apps).merge(:apps => rest))
82
- end
94
+ spaced(apps) do |app_manifest|
95
+ push_with_manifest(app_manifest, push, input)
83
96
  end
84
97
  end
85
98
 
86
-
87
- def wrap_push(cmd, input)
88
-
99
+ def push_with_manifest(app_manifest, push, input)
100
+ with_filters(
101
+ :push => {
102
+ :create_app => proc { |a|
103
+ setup_env(a, app_manifest)
104
+ a
105
+ },
106
+ :push_app => proc { |a|
107
+ setup_services(a, app_manifest)
108
+ a
109
+ }
110
+ }) do
111
+ app_input = push_input_for(app_manifest, input)
112
+
113
+ push.call(app_input)
114
+ end
89
115
  end
90
116
 
91
- # push and sync meta changes in the manifest
92
- # also sets env data on creation if present in manifest
93
- #
94
- # vmc push [name in manifest] = push that app from its path
95
- # vmc push [name not in manifest] = push new app using given name
96
- # vmc push [path] = push app from its path
97
- change_argument :push, :name, :optional
98
-
99
- add_input :push, :reset, :type => :boolean, :default => false,
100
- :desc => "Reset to values in the manifest"
101
-
102
- around(:push) do |push, input|
103
- particular =
104
- if input.has?(:name)
105
- path = File.expand_path(input[:name])
106
- find_by = File.exists?(path) ? path : input[:name]
107
-
108
- find_apps(find_by)
109
- else
110
- []
111
- end
117
+ def push_input_for(app_manifest, input)
118
+ existing_app = client.app_by_name(app_manifest[:name])
112
119
 
113
- if particular.empty?
114
- particular = find_apps(Dir.pwd)
120
+ if !existing_app || input[:reset]
121
+ input = input.rebase_given(app_manifest)
122
+ else
123
+ warn_reset_changes if manifest_differs?(existing_app, input)
115
124
  end
116
125
 
117
- apps = particular.empty? ? all_apps : particular
126
+ input.merge(
127
+ :path => from_manifest(app_manifest[:path]),
128
+ :name => app_manifest[:name],
129
+ :bind_services => false,
130
+ :create_services => false)
131
+ end
118
132
 
119
- if apps.empty?
120
- with_filters(
121
- :push => {
122
- :push_app =>
123
- proc { |a| ask_to_save(input, a); a }
124
- }) do
125
- push.call
126
- end
127
- else
128
- show_manifest_usage
129
-
130
- spaced(apps) do |app|
131
- with_filters(
132
- :push => {
133
- :create_app => proc { |a|
134
- setup_env(a, app)
135
- a
136
- },
137
- :push_app => proc { |a|
138
- setup_services(a, app)
139
- a
140
- }
141
- }) do
142
- existing_app = client.app_by_name(app[:name])
143
-
144
- # only set inputs if creating app or updating with --reset
145
- if input[:reset] || !existing_app
146
- app_input = input.rebase_given(app)
147
- else
148
- # assign manifest values to detect differences
149
- app_input = input.merge(:path => from_manifest(app[:path]))
150
- end
151
-
152
- push.call(app_input.merge(
153
- :name => app[:name],
154
- :bind_services => false,
155
- :create_services => false))
156
- end
157
- end
133
+ def manifest_differs?(app, input)
134
+ apply_changes(app, input)
135
+ app.changed?
136
+ end
137
+
138
+ def create_and_save_manifest(push, input)
139
+ with_filters(
140
+ :push => { :push_app => proc { |a| ask_to_save(input, a); a } }) do
141
+ push.call
158
142
  end
159
143
  end
160
144
  end
@@ -1,3 +1,3 @@
1
1
  module VMCManifests
2
- VERSION = "0.6.0".freeze
2
+ VERSION = "0.6.1".freeze
3
3
  end
@@ -9,25 +9,29 @@ describe ManifestsPlugin do
9
9
  let(:inputs_hash) { {} }
10
10
  let(:given_hash) { {} }
11
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) }
12
+ let(:command) { nil }
13
+ let(:inputs) { Mothership::Inputs.new(Mothership.commands[:push], nil, inputs_hash, given_hash, global_hash) }
14
+ let(:plugin) { ManifestsPlugin.new(command, inputs) }
15
+
16
+ let(:client) { fake_client }
14
17
 
15
18
  before do
16
19
  stub(plugin).manifest { manifest }
17
20
  stub(plugin).manifest_file { manifest_file } if manifest_file
21
+ stub(plugin).client { client }
18
22
  end
19
23
 
20
24
  describe "#wrap_with_optional_name" do
21
25
  let(:name_made_optional) { true }
22
- let(:command) { mock! }
26
+ let(:wrapped) { mock! }
23
27
 
24
- subject { plugin.wrap_with_optional_name(name_made_optional, command, inputs) }
28
+ subject { plugin.send(:wrap_with_optional_name, name_made_optional, wrapped, inputs) }
25
29
 
26
30
  context "when --all is given" do
27
31
  let(:inputs_hash) { { :all => true } }
28
32
 
29
33
  it "skips all manifest-related logic, and invokes the command" do
30
- mock(command).call
34
+ mock(wrapped).call
31
35
  dont_allow(plugin).show_manifest_usage
32
36
  subject
33
37
  end
@@ -40,7 +44,7 @@ describe ManifestsPlugin do
40
44
  let(:given_hash) { { :app => "foo" } }
41
45
 
42
46
  it "passes through to the command" do
43
- mock(command).call
47
+ mock(wrapped).call
44
48
  dont_allow(plugin).show_manifest_usage
45
49
  subject
46
50
  end
@@ -60,7 +64,7 @@ describe ManifestsPlugin do
60
64
  let(:name_made_optional) { false }
61
65
 
62
66
  it "passes through to the command" do
63
- mock(command).call
67
+ mock(wrapped).call
64
68
  dont_allow(plugin).show_manifest_usage
65
69
  subject
66
70
  end
@@ -80,7 +84,7 @@ describe ManifestsPlugin do
80
84
  let(:manifest) { { :applications => [{ :name => "foo", :path => "/abc/foo" }] } }
81
85
 
82
86
  it "calls the command for only that app" do
83
- mock(command).call(anything) do |inputs|
87
+ mock(wrapped).call(anything) do |inputs|
84
88
  expect(inputs.given[:app]).to eq "foo"
85
89
  end
86
90
 
@@ -95,7 +99,7 @@ describe ManifestsPlugin do
95
99
 
96
100
  it "calls the command for all apps in the manifest" do
97
101
  uncalled_apps = ["foo", "bar"]
98
- mock(command).call(anything).twice do |inputs|
102
+ mock(wrapped).call(anything).twice do |inputs|
99
103
  uncalled_apps.delete inputs.given[:app]
100
104
  end
101
105
 
@@ -116,7 +120,7 @@ describe ManifestsPlugin do
116
120
  mock(plugin).show_manifest_usage
117
121
 
118
122
  uncalled_apps = ["a", "x"]
119
- mock(command).call(anything).twice do |inputs|
123
+ mock(wrapped).call(anything).twice do |inputs|
120
124
  uncalled_apps.delete inputs.given[:app]
121
125
  end
122
126
 
@@ -136,7 +140,7 @@ describe ManifestsPlugin do
136
140
 
137
141
  it "passes through to the original command" do
138
142
  dont_allow(plugin).show_manifest_usage
139
- mock(command).call
143
+ mock(wrapped).call
140
144
  subject
141
145
  end
142
146
  end
@@ -147,7 +151,7 @@ describe ManifestsPlugin do
147
151
  let(:given_hash) { { :app => "foo" } }
148
152
 
149
153
  it "calls the command with that app" do
150
- mock(command).call(anything) do |inputs|
154
+ mock(wrapped).call(anything) do |inputs|
151
155
  expect(inputs.given[:app]).to eq "foo"
152
156
  end
153
157
 
@@ -160,7 +164,7 @@ describe ManifestsPlugin do
160
164
  let(:given_hash) { { :app => "/abc/foo" } }
161
165
 
162
166
  it "calls the command with that app" do
163
- mock(command).call(anything) do |inputs|
167
+ mock(wrapped).call(anything) do |inputs|
164
168
  expect(inputs.given[:app]).to eq "foo"
165
169
  end
166
170
 
@@ -171,12 +175,153 @@ describe ManifestsPlugin do
171
175
  end
172
176
 
173
177
  describe "#wrap_push" do
178
+ let(:wrapped) { mock! }
179
+ let(:command) { Mothership.commands[:push] }
180
+
181
+ subject { plugin.send(:wrap_push, wrapped, inputs) }
182
+
183
+ before do
184
+ stub(plugin).show_manifest_usage
185
+ end
186
+
174
187
  context "with a manifest" do
188
+ let(:manifest_file) { "/abc/manifest.yml" }
189
+
190
+ let(:manifest) do
191
+ { :applications => [
192
+ { :name => "a",
193
+ :path => "/abc/a",
194
+ :instances => "200",
195
+ :memory => "128M"
196
+ }
197
+ ]
198
+ }
199
+ end
200
+
201
+ # vmc push foo
202
+ context "and a name is given" do
203
+ context "and the name is present in the manifest" do
204
+ let(:given_hash) { { :name => "a" } }
205
+
206
+ context "and the app exists" do
207
+ let(:app) { fake :app, :name => "a" }
208
+ let(:client) { fake_client :apps => [app] }
209
+
210
+ context "and --reset was given" do
211
+ let(:inputs_hash) { { :reset => true } }
212
+ let(:given_hash) { { :name => "a", :instances => "100" } }
213
+
214
+ it "rebases their inputs on the manifest's values" do
215
+ mock(wrapped).call(anything) do |inputs|
216
+ expect(inputs.given).to eq(
217
+ :name => "a", :path => "/abc/a", :instances => "100", :memory => "128M")
218
+ end
219
+
220
+ subject
221
+ end
222
+ end
223
+
224
+ context "and --reset was NOT given" do
225
+ let(:given_hash) { { :name => "a", :instances => "100" } }
226
+
227
+ context "and the app settings differ" do
228
+ let(:app) { fake :app, :name => "a", :memory => 256 }
229
+
230
+ it "tells the user to use --reset to apply changes" do
231
+ mock(plugin).warn_reset_changes
232
+ mock(wrapped).call(anything) do |inputs|
233
+ expect(inputs.given).to eq(
234
+ :name => "a", :instances => "100")
235
+ end
236
+ subject
237
+ end
238
+ end
239
+
240
+ it "does not add the manifest's values to the inputs" do
241
+ stub(plugin).warn_reset_changes
242
+ mock(wrapped).call(anything) do |inputs|
243
+ expect(inputs.given).to eq(
244
+ :name => "a", :instances => "100")
245
+ end
246
+
247
+ subject
248
+ end
249
+ end
250
+ end
251
+
252
+ context "and the app does NOT exist" do
253
+ it "pushes a new app with the inputs from the manifest" do
254
+ mock(wrapped).call(anything) do |inputs|
255
+ expect(inputs.given).to eq(
256
+ :name => "a", :path => "/abc/a", :instances => "200", :memory => "128M")
257
+ end
258
+
259
+ subject
260
+ end
261
+ end
262
+ end
263
+
264
+ context "and the name is NOT present in the manifest" do
265
+ let(:given_hash) { { :name => "x" } }
175
266
 
267
+ it "fails, saying that name was not found in the manifest" do
268
+ expect { subject }.to raise_error(VMC::UserError, /Could not find .+ in the manifest./)
269
+ end
270
+ end
271
+ end
272
+
273
+ # vmc push ./abc
274
+ context "and a path is given" do
275
+ context "and there are apps matching that path in the manifest" do
276
+ let(:manifest) do
277
+ { :applications => [
278
+ { :name => "a",
279
+ :path => "/abc/a",
280
+ :instances => "200",
281
+ :memory => "128M"
282
+ },
283
+ { :name => "b",
284
+ :path => "/abc/a",
285
+ :instances => "200",
286
+ :memory => "128M"
287
+ }
288
+ ]
289
+ }
290
+ end
291
+
292
+ let(:given_hash) { { :name => "/abc/a" } }
293
+
294
+ it "pushes the found apps" do
295
+ pushed_apps = []
296
+ mock(wrapped).call(anything).twice do |inputs|
297
+ pushed_apps << inputs[:name]
298
+ end
299
+
300
+ subject
301
+
302
+ expect(pushed_apps).to eq(["a", "b"])
303
+ end
304
+ end
305
+
306
+ context "and there are NOT apps matching that path in the manifest" do
307
+ let(:given_hash) { { :name => "/abc/x" } }
308
+
309
+ it "fails, saying that the path was not found in the manifest" do
310
+ expect { subject }.to raise_error(VMC::UserError, /Path .+ is not present in manifest/)
311
+ end
312
+ end
313
+ end
176
314
  end
177
315
 
178
316
  context "without a manifest" do
179
-
317
+ let(:app) { mock! }
318
+ let(:manifest) { nil }
319
+
320
+ it "asks to save the manifest when uploading the application" do
321
+ mock_ask("Save configuration?", :default => false)
322
+ stub(wrapped).call { plugin.filter(:push_app, app) }
323
+ subject
324
+ end
180
325
  end
181
326
  end
182
327
  end
@@ -224,13 +224,11 @@ describe VMCManifests do
224
224
  end
225
225
 
226
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
227
+ let(:foo_hash) { { :name => "foo", :path => "/abc/foo" } }
228
+ let(:bar_hash) { { :name => "bar", :path => "/abc/bar" } }
229
+ let(:baz_hash) { { :name => "baz", :path => "/abc/baz" } }
230
+
231
+ let(:manifest) { { :applications => [foo_hash, bar_hash, baz_hash] } }
234
232
 
235
233
  subject { cmd.apps_in_manifest(inputs) }
236
234
 
@@ -245,14 +243,14 @@ describe VMCManifests do
245
243
  context "and all of them are in the manifest" do
246
244
  let(:given_hash) { { :apps => ["foo", "bar"] } }
247
245
 
248
- its(:first) { should eq ["foo", "bar"] }
246
+ its(:first) { should eq [foo_hash, bar_hash] }
249
247
  its(:last) { should eq [] }
250
248
  end
251
249
 
252
250
  context "and one of them is in the manifest" do
253
251
  let(:given_hash) { { :apps => ["foo", "xxx"] } }
254
252
 
255
- its(:first) { should eq ["foo"] }
253
+ its(:first) { should eq [foo_hash] }
256
254
  its(:last) { should eq ["xxx"] }
257
255
  end
258
256
 
@@ -268,7 +266,7 @@ describe VMCManifests do
268
266
  context "and the paths are in the manifest" do
269
267
  let(:given_hash) { { :apps => ["/abc/foo"] } }
270
268
 
271
- its(:first) { should eq ["foo"] }
269
+ its(:first) { should eq [foo_hash] }
272
270
  its(:last) { should eq [] }
273
271
  end
274
272
 
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: 7
4
+ hash: 5
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 6
9
- - 0
10
- version: 0.6.0
9
+ - 1
10
+ version: 0.6.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - Alex Suraci