manifests-vmc-plugin 0.6.0 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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