vanagon 0.5.2 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8a3414d151dd4f8f7162a06b6dfe3a5dc8c9b066
4
- data.tar.gz: 944acf2e2cfa169c60f3072d05a5add5c03ac25c
3
+ metadata.gz: fd0b8d155b61fe71dc4b21216f3cd8674734c790
4
+ data.tar.gz: b3b1554b32d14049b08eb56c89206f9b71e120ff
5
5
  SHA512:
6
- metadata.gz: d88e78e71ccdd7f878141dc0b26d741bbef71b9f9c21e8cc8f766bf241bc8c317c6090e9781508f812d17dddc46e8af42f39d38274618934d4871de498eecf45
7
- data.tar.gz: 0152bcaa044f2c8f939a7c8bb4434801d023397d7d156692f6648a55bceca6958a3182577fd6b4492999712cb07ce7eaef017e7c7150cf2205153ddd318750e7
6
+ metadata.gz: f9646e8ab2503dfe9433e91f25af53c2a7debcadb276d9d4338a4f8ff11749a3125f7ec04a007c855bf7e6a5b9878e5bba9227fb42e2aa73d14e9d8e9bcf5462
7
+ data.tar.gz: 945246b7da4cc69b58a919c6f91df69db236e9be28039c7b1c0e78acf90e2b285c268b3730204a03050026901a4f890245fec9e89918098026fa6067eaf8ff47
@@ -184,7 +184,11 @@ class Vanagon
184
184
  # Return here because there is no file to install, just a string read in
185
185
  return
186
186
  when "windows"
187
- @component.service = OpenStruct.new(:id => "#{service_name.gsub(/[^A-Za-z0-9]/, '').upcase}", :service_file => service_file)
187
+ @component.service = OpenStruct.new(\
188
+ :bindir_id => "#{service_name.gsub(/[^A-Za-z0-9]/, '').upcase}BINDIR", \
189
+ :service_file => service_file, \
190
+ :component_group_id => "#{service_name.gsub(/[^A-Za-z0-9]/, '')}Component"\
191
+ )
188
192
  # return here as we are just collecting the name of the service file to put into the harvest filter list.
189
193
  return
190
194
  else
@@ -11,6 +11,7 @@ class Vanagon
11
11
  class Driver
12
12
  include Vanagon::Utilities
13
13
  attr_accessor :platform, :project, :target, :workdir, :verbose, :preserve
14
+ attr_accessor :timeout, :retry_count
14
15
 
15
16
  def initialize(platform, project, options = { :configdir => nil, :target => nil, :engine => nil, :components => nil, :skipcheck => false })
16
17
  @verbose = false
@@ -24,8 +25,7 @@ class Vanagon
24
25
  @platform = Vanagon::Platform.load_platform(platform, File.join(@@configdir, "platforms"))
25
26
  @project = Vanagon::Project.load_project(project, File.join(@@configdir, "projects"), @platform, components)
26
27
  @project.settings[:skipcheck] = options[:skipcheck]
27
- @@logger = Logger.new('vanagon_hosts.log')
28
- @@logger.progname = 'vanagon'
28
+ loginit('vanagon_hosts.log')
29
29
 
30
30
  # If a target has been given, we don't want to make any assumptions about how to tear it down.
31
31
  engine = 'base' if target
@@ -76,14 +76,13 @@ class Vanagon
76
76
  @engine.startup(@workdir)
77
77
 
78
78
  puts "Target is #{@engine.target}"
79
-
80
- install_build_dependencies
79
+ retry_task { install_build_dependencies }
81
80
  @project.fetch_sources(@workdir)
82
81
  @project.make_makefile(@workdir)
83
82
  @project.make_bill_of_materials(@workdir)
84
83
  @project.generate_packaging_artifacts(@workdir)
85
84
  @engine.ship_workdir(@workdir)
86
- @engine.dispatch("(cd #{@engine.remote_workdir}; #{@platform.make})")
85
+ retry_task { @engine.dispatch("(cd #{@engine.remote_workdir}; #{@platform.make})") }
87
86
  @engine.retrieve_built_artifact
88
87
  @engine.teardown unless @preserve
89
88
  cleanup_workdir unless @preserve
@@ -95,7 +94,7 @@ class Vanagon
95
94
  if @engine.name == "hardware"
96
95
  @engine.teardown
97
96
  end
98
- end
97
+ end
99
98
 
100
99
  def prepare(workdir = nil)
101
100
  @workdir = workdir ? FileUtils.mkdir_p(workdir).first : Dir.mktmpdir
@@ -115,5 +114,22 @@ class Vanagon
115
114
  puts e.backtrace.join("\n")
116
115
  raise e
117
116
  end
117
+
118
+ # Retry the provided block, use the retry count and timeout
119
+ # values from the project, if available, otherwise use some
120
+ # sane defaults.
121
+ def retry_task(&block)
122
+ @timeout = @project.timeout || 3600
123
+ @retry_count = @project.retry_count || 3
124
+ Vanagon::Utilities.retry_with_timeout(@retry_count, @timeout) { yield }
125
+ end
126
+ private :retry_task
127
+
128
+ # Initialize the logging instance
129
+ def loginit(logfile)
130
+ @@logger = Logger.new(logfile)
131
+ @@logger.progname = 'vanagon'
132
+ end
133
+ private :loginit
118
134
  end
119
135
  end
@@ -289,6 +289,105 @@ class Vanagon
289
289
  File.join("windows", target_repo, @architecture)
290
290
  end
291
291
 
292
+ # Generate correctly formatted wix elements that match the
293
+ # structure of the directory input
294
+ #
295
+ # @param services, Array of components services
296
+ # and optionally:
297
+ # @return [string] correctly formatted wix element string
298
+ def generate_wix_dirs(services)
299
+ directories = []
300
+ services.map { |svc| directories.push({ :path => svc.service_file, :bindir_id => svc.bindir_id }) }
301
+ # root refers to the root of an n-ary tree (which we are about to make)
302
+ root = { :children => [] }
303
+ # iterate over all paths specified and break each one
304
+ # in to its specific directories. This will generate_wix_dirs
305
+ # an n-ary tree structure matching the specs from the input
306
+ directories.each do |dir|
307
+ # Always start at the beginning
308
+ curr = root
309
+ names = strip_path(dir[:path])
310
+ # The last entry in this list will be the actual file,
311
+ # which we do not want, we only want it's base path
312
+ names.pop
313
+ names.each do |name|
314
+ curr = insert_child(curr, name)
315
+ end
316
+ # at this point, curr will be the top dir, override the id if
317
+ # id exists
318
+ curr[:bindir_ids].push(dir[:bindir_id])
319
+ end
320
+ return generate_wix_from_graph(root)
321
+ end
322
+
323
+ # insert a new object with the name "name" if it doesn't already
324
+ # exist. Then assign curr to either the new child or the one that
325
+ # already exists here
326
+ #
327
+ # @param [HASH] curr, current object we are on
328
+ # @param [string] name, name of new object we are to search for and
329
+ # create if necessary
330
+ def insert_child(curr, name)
331
+ #The Id field will default to name, but be overridden later
332
+ new_obj = { :name => name, :id => name, :bindir_ids => [], :children => [] }
333
+ if (child_index = includes_child(new_obj, curr[:children]))
334
+ curr = curr[:children][child_index]
335
+ else
336
+ curr[:children].push(new_obj)
337
+ curr = new_obj
338
+ end
339
+ return curr
340
+ end
341
+
342
+ # strip and split the directory path into single names
343
+ # @param [string] path string of directory
344
+ def strip_path(path)
345
+ if path.include?("/") || path.include?("\\")
346
+ # The regex in the last part of this if warrants some
347
+ # explanation. Specifically it matches any combinations
348
+ # of any letters, then the : char, then finally either
349
+ # the char / or the char \. it's meant to parse out drive
350
+ # roots on windows
351
+ if path.start_with?("/") || path.start_with?("\\") || path.start_with?("SourceDir") || path =~ (/([A-Za-z])*\:(\/|\\)/)
352
+ path = path.sub(/\/|\\|([A-Za-z])*\:(\/|\\)|(\/|\\)?(SourceDir)(\/|\\)?/, '')
353
+ end
354
+ names = path.split(/\/|\\/)
355
+ end
356
+ return names
357
+ end
358
+
359
+ # Find if child element is the same as one of
360
+ # the old_children elements, return that child
361
+ def includes_child(new_child, old_children)
362
+ old_children.each_with_index do |curr_old_child, index|
363
+ return index if curr_old_child[:name] == new_child[:name]
364
+ end unless old_children.empty?
365
+ return nil
366
+ end
367
+
368
+
369
+ # Recursively generate wix element structure
370
+ #
371
+ # @param root, the (empty) root of an n-ary tree containing the
372
+ # structure of directories
373
+ def generate_wix_from_graph(root)
374
+ string = ''
375
+ unless root[:children].empty?
376
+ root[:children].each do |child|
377
+ string += ("<Directory Name=\"#{child[:name]}\" Id=\"#{child[:id]}\">\n")
378
+ unless child[:bindir_ids].empty?
379
+ child[:bindir_ids].each do |bindir_id|
380
+ string += ("<Directory Id=\"#{bindir_id}\" />\n")
381
+ end
382
+ end
383
+ string += generate_wix_from_graph(child)
384
+ string += ("</Directory>\n")
385
+ end
386
+ return string
387
+ end
388
+ return ''
389
+ end
390
+
292
391
  # Constructor. Sets up some defaults for the windows platform and calls the parent constructor
293
392
  #
294
393
  # Mingw varies on where it is installed based on architecture. We want to use which ever is on the system.
@@ -11,7 +11,7 @@ class Vanagon
11
11
  attr_accessor :version, :directories, :license, :description, :vendor
12
12
  attr_accessor :homepage, :requires, :user, :repo, :noarch, :identifier
13
13
  attr_accessor :cleanup, :version_file, :release, :replaces, :provides
14
- attr_accessor :bill_of_materials
14
+ attr_accessor :bill_of_materials, :retry_count, :timeout
15
15
 
16
16
  # Loads a given project from the configdir
17
17
  #
@@ -101,6 +101,22 @@ class Vanagon
101
101
  replaces.flatten.uniq
102
102
  end
103
103
 
104
+ # Grabs a specific service based on which name is passed in
105
+ # note that if the name is wrong or there was no
106
+ # @component.install_service call in the component, this
107
+ # will return nil
108
+ #
109
+ # @param [string] name of service to grab
110
+ # @return [@component.service obj] specific service
111
+ def get_service(name)
112
+ @components.each do |component|
113
+ if component.name == name
114
+ return component.service
115
+ end
116
+ end
117
+ return nil
118
+ end
119
+
104
120
  # Collects all of the provides for the project and its components
105
121
  #
106
122
  # @return [Array] array of package level provides for the project
@@ -35,3 +35,8 @@ fi
35
35
  fi
36
36
  <%- end -%>
37
37
  <%- end -%>
38
+
39
+ <%- get_services.each do |service| -%>
40
+ # Restarting service <%= service.name %>
41
+ /usr/sbin/svcadm restart <%= service.name %> || true
42
+ <%- end -%>
@@ -53,8 +53,11 @@ set name=org.opensolaris.smf.fmri <%= get_services.map {|service| "value=svc:/#{
53
53
  <%- end -%>
54
54
 
55
55
  <%- get_configfiles.each do |config| -%>
56
- # Preserve the old conf file on upgrade
56
+ # Preserve the old conf file on upgrade, restart services on config file change
57
57
  <transform file path=<%= strip_and_escape(config.path) %>$ -> add preserve renamenew>
58
+ <%- get_services.each do |service| -%>
59
+ <transform file path=<%= strip_and_escape(config.path) %>$ -> add restart_fmri <%= "svc:/#{service.name}:*" %> >
60
+ <%- end -%>
58
61
  <%- end -%>
59
62
 
60
63
  # Set any required owner, group or mode transformations
@@ -2,10 +2,8 @@
2
2
  <Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
3
3
 
4
4
  <Fragment>
5
- <ComponentGroup Id="ServiceListGroup">
6
- <%- get_services.each do |service| -%>
7
- <ComponentGroupRef Id="Service_<%= service.id %>" />
8
- <%- end -%>
9
- </ComponentGroup>
5
+ <DirectoryRef Id='TARGETDIR'>
6
+ <%= @platform.generate_wix_dirs(self.get_services) %>
7
+ </DirectoryRef>
10
8
  </Fragment>
11
9
  </Wix>
@@ -15,8 +15,10 @@
15
15
  Description="<%= "#{settings[:product_id]}#{@platform.architecture == "x64" ? " (64-bit)" : ""}" %> Installer"
16
16
  Comments="<%= @homepage %>"
17
17
  Compressed="yes"
18
- Platform="<%= @platform.architecture %>"
19
- />
18
+ Platform="<%= @platform.architecture %>" />
19
+
20
+ <!-- We are discussing a MAINT PR to move the componentfilegroup to seperate file
21
+ This would allow project to specify additional fragments if needed -->
20
22
 
21
23
  <MajorUpgrade AllowDowngrades="yes" />
22
24
  <Media Id="1" Cabinet="<%= settings[:product_id] %>.cab" EmbedCab="yes" CompressionLevel="high" />
@@ -25,7 +27,13 @@
25
27
  <!-- We can add all components by referencing this one thing -->
26
28
  <ComponentGroupRef Id="ProductComponentGroup" />
27
29
  <ComponentGroupRef Id="RegistryComponentGroup" />
28
- <ComponentGroupRef Id="ServiceListGroup" />
30
+ <%- get_services.each do |service| -%>
31
+ <ComponentGroupRef Id="<%= service.component_group_id %>" />
32
+ <%- end -%>
33
+ <!-- All of these Include refs are expected to be present -->
34
+ <ComponentGroupRef Id="FragmentProperties" />
35
+ <ComponentGroupRef Id="FragmentSequences" />
36
+ <ComponentGroupRef Id="FragmentCustomActions" />
29
37
  </Feature>
30
38
  <!-- We will use DirectoryRef at the project level to hook in the project directory structure -->
31
39
  <Directory Id='TARGETDIR' Name='SourceDir' />
File without changes
@@ -1,4 +1,7 @@
1
1
  require 'vanagon/platform'
2
+ require 'vanagon/project'
3
+ require 'vanagon/common'
4
+
2
5
 
3
6
  # These constants are defined for the purpose of the project/generic file merge tests
4
7
  # to point these directories to test areas under the /tmp directory.
@@ -26,14 +29,17 @@ describe "Vanagon::Platform::Windows" do
26
29
  :output_dir_with_target => "windows/thing/x64",
27
30
  :target_user => "Administrator",
28
31
  :projname => "test-proj",
29
- :block => %Q[ platform "windows-2012r2-x64" do |plat| end ]
32
+ :block => %Q[ platform "windows-2012r2-x64" do |plat| plat.servicetype 'windows' end ]
30
33
  },
31
34
  ]
32
35
 
33
36
  platforms.each do |plat|
34
- context "on #{plat[:name]} we should behave ourselves" do
37
+ context "on #{plat[:name]}" do
35
38
  let(:platform) { plat }
36
39
  let(:cur_plat) { Vanagon::Platform::DSL.new(plat[:name]) }
40
+ let (:project_block) {
41
+ "project 'test-fixture' do |proj|
42
+ end" }
37
43
 
38
44
  before do
39
45
  cur_plat.instance_eval(plat[:block])
@@ -160,6 +166,123 @@ describe "Vanagon::Platform::Windows" do
160
166
  expect(File).not_to exist("#{WORKDIR}/wix/file-3.wxs.erb")
161
167
  expect(File).not_to exist("#{WORKDIR}/wix/file-4.wxs.erb")
162
168
  end
169
+
170
+
171
+ describe "generate_wix_dirs" do
172
+
173
+ it "returns one directory with install_service defaults" do
174
+ cur_plat.instance_eval(plat[:block])
175
+ comp = Vanagon::Component::DSL.new('service-test', {}, cur_plat._platform)
176
+ comp.install_service('/opt/bin.exe')
177
+ expect(cur_plat._platform.generate_wix_dirs([comp._component.service].flatten.compact)).to eq( \
178
+ <<-HERE
179
+ <Directory Name="opt" Id="opt">
180
+ <Directory Id="SERVICETESTBINDIR" />
181
+ </Directory>
182
+ HERE
183
+ )
184
+ end
185
+
186
+ it "returns one directory with non-default name" do
187
+ cur_plat.instance_eval(plat[:block])
188
+ comp = Vanagon::Component::DSL.new('service-test', {}, cur_plat._platform)
189
+ comp.install_service('/opt/bin.exe', nil, "service-test-2")
190
+ expect(cur_plat._platform.generate_wix_dirs([comp._component.service].flatten.compact)).to eq( \
191
+ <<-HERE
192
+ <Directory Name="opt" Id="opt">
193
+ <Directory Id="SERVICETEST2BINDIR" />
194
+ </Directory>
195
+ HERE
196
+ )
197
+ end
198
+
199
+ it "returns nested directory correctly with \\" do
200
+ cur_plat.instance_eval(plat[:block])
201
+ comp = Vanagon::Component::DSL.new('service-test', {}, cur_plat._platform)
202
+ comp.install_service('root\\programfiles\\bin.exe')
203
+ expect(cur_plat._platform.generate_wix_dirs([comp._component.service].flatten.compact)).to eq( \
204
+ <<-HERE
205
+ <Directory Name="root" Id="root">
206
+ <Directory Name="programfiles" Id="programfiles">
207
+ <Directory Id="SERVICETESTBINDIR" />
208
+ </Directory>
209
+ </Directory>
210
+ HERE
211
+ )
212
+ end
213
+
214
+ it "removes any drive roots" do
215
+ cur_plat.instance_eval(plat[:block])
216
+ comp = Vanagon::Component::DSL.new('service-test', {}, cur_plat._platform)
217
+ comp.install_service('C:\\programfiles\\bin.exe')
218
+ expect(cur_plat._platform.generate_wix_dirs([comp._component.service].flatten.compact)).to eq( \
219
+ <<-HERE
220
+ <Directory Name="programfiles" Id="programfiles">
221
+ <Directory Id="SERVICETESTBINDIR" />
222
+ </Directory>
223
+ HERE
224
+ )
225
+ end
226
+
227
+ it "removes SourceDir" do
228
+ cur_plat.instance_eval(plat[:block])
229
+ comp = Vanagon::Component::DSL.new('service-test', {}, cur_plat._platform)
230
+ comp.install_service('SourceDir\\programfiles\\bin.exe')
231
+ expect(cur_plat._platform.generate_wix_dirs([comp._component.service].flatten.compact)).to eq( \
232
+ <<-HERE
233
+ <Directory Name="programfiles" Id="programfiles">
234
+ <Directory Id="SERVICETESTBINDIR" />
235
+ </Directory>
236
+ HERE
237
+ )
238
+ end
239
+
240
+
241
+ it "adds a second directory for the same input but different components" do
242
+ cur_plat.instance_eval(plat[:block])
243
+ comp = Vanagon::Component::DSL.new('service-test', {}, cur_plat._platform)
244
+ comp.install_service('/programfiles/bin.exe')
245
+ comp2 = Vanagon::Component::DSL.new('service-test-2', {}, cur_plat._platform)
246
+ comp2.install_service('/programfiles/bin.exe')
247
+ expect(cur_plat._platform.generate_wix_dirs([comp._component.service, comp2._component.service].flatten.compact)).to eq( \
248
+ <<-HERE
249
+ <Directory Name="programfiles" Id="programfiles">
250
+ <Directory Id="SERVICETESTBINDIR" />
251
+ <Directory Id="SERVICETEST2BINDIR" />
252
+ </Directory>
253
+ HERE
254
+ )
255
+ end
256
+
257
+ it "returns correctly formatted multiple nested directories" do
258
+ cur_plat.instance_eval(plat[:block])
259
+ comp1 = Vanagon::Component::DSL.new('service-test1', {}, cur_plat._platform)
260
+ comp1.install_service('/opt/oneUp/twoUp/bin.exe')
261
+ comp2 = Vanagon::Component::DSL.new('service-test2', {}, cur_plat._platform)
262
+ comp2.install_service('/opt/oneUpAgain/twoUp/bin.exe')
263
+ comp3 = Vanagon::Component::DSL.new('service-test3', {}, cur_plat._platform)
264
+ comp3.install_service('/opt/oneUpAgain/twoUpAgain/bin.exe')
265
+ expect(cur_plat._platform.generate_wix_dirs([comp1._component.service, comp2._component.service, comp3._component.service].flatten.compact)).to eq( \
266
+ <<-HERE
267
+ <Directory Name="opt" Id="opt">
268
+ <Directory Name="oneUp" Id="oneUp">
269
+ <Directory Name="twoUp" Id="twoUp">
270
+ <Directory Id="SERVICETEST1BINDIR" />
271
+ </Directory>
272
+ </Directory>
273
+ <Directory Name="oneUpAgain" Id="oneUpAgain">
274
+ <Directory Name="twoUp" Id="twoUp">
275
+ <Directory Id="SERVICETEST2BINDIR" />
276
+ </Directory>
277
+ <Directory Name="twoUpAgain" Id="twoUpAgain">
278
+ <Directory Id="SERVICETEST3BINDIR" />
279
+ </Directory>
280
+ </Directory>
281
+ </Directory>
282
+ HERE
283
+ )
284
+ end
285
+ end
163
286
  end
164
287
  end
165
288
  end
@@ -154,5 +154,9 @@ describe "Vanagon::Utilities" do
154
154
  expect(Vanagon::Utilities).to receive(:remote_ssh_command).with(host, command, port).exactly(1).times.and_return(true)
155
155
  expect(Vanagon::Utilities.retry_with_timeout(tries, timeout) { Vanagon::Utilities.remote_ssh_command(host, command, port) }).to be(true)
156
156
  end
157
+
158
+ it 'raises a Vanagon::Error if the command times out' do
159
+ expect{ Vanagon::Utilities.retry_with_timeout(tries, timeout) { sleep 5 }.to raise_error(Vanagon::Error) }
160
+ end
157
161
  end
158
162
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vanagon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 0.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet Labs
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-08 00:00:00.000000000 Z
11
+ date: 2016-03-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -132,7 +132,7 @@ files:
132
132
  - resources/windows/nuget/chocolateyInstall.ps1
133
133
  - resources/windows/nuget/chocolateyUninstall.ps1
134
134
  - resources/windows/nuget/project.nuspec.erb
135
- - resources/windows/wix/componentrefs.wxs.erb
135
+ - resources/windows/wix/directorylist.wxs.erb
136
136
  - resources/windows/wix/filter.xslt.erb
137
137
  - resources/windows/wix/project.wxs.erb
138
138
  - resources/windows/wix/registryEntries.wxs.erb
@@ -157,6 +157,7 @@ files:
157
157
  - spec/lib/vanagon/component/source/http_spec.rb
158
158
  - spec/lib/vanagon/component/source_spec.rb
159
159
  - spec/lib/vanagon/component_spec.rb
160
+ - spec/lib/vanagon/driver_spec.rb
160
161
  - spec/lib/vanagon/engine/base_spec.rb
161
162
  - spec/lib/vanagon/engine/docker_spec.rb
162
163
  - spec/lib/vanagon/engine/hardware_spec.rb
@@ -195,7 +196,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
195
196
  version: '0'
196
197
  requirements: []
197
198
  rubyforge_project:
198
- rubygems_version: 2.4.6
199
+ rubygems_version: 2.5.1
199
200
  signing_key:
200
201
  specification_version: 3
201
202
  summary: All of your packages will fit into this van with this one simple trick.
@@ -209,6 +210,7 @@ test_files:
209
210
  - spec/lib/vanagon/component/source/http_spec.rb
210
211
  - spec/lib/vanagon/component/source_spec.rb
211
212
  - spec/lib/vanagon/component_spec.rb
213
+ - spec/lib/vanagon/driver_spec.rb
212
214
  - spec/lib/vanagon/engine/base_spec.rb
213
215
  - spec/lib/vanagon/engine/docker_spec.rb
214
216
  - spec/lib/vanagon/engine/hardware_spec.rb