kitchen-dokken 2.10.0 → 2.12.1

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
  SHA256:
3
- metadata.gz: 9457c866ed76959da525c5c6ff2cf79ec23ce6f1bc0b3b4f19a1d65dbb8c53d1
4
- data.tar.gz: 12416b24465f12771c00ff3d702f0c2283a763e113710da7f5fa43b8f4c21ae5
3
+ metadata.gz: 517d0d0706aed8e8fc5889c874d6817fe65cfa571ba3a7a082d8256f707986cd
4
+ data.tar.gz: f5beb66dc47d9c4f58022c26af3ce8657c7fae40443eb2d3ee1b5ef2c01f5d0e
5
5
  SHA512:
6
- metadata.gz: 6b1bd8719579b19eca0d091b713c00d94cdfd08e4f3d4a5c79caae06301bfd0bc0f2e84dac691f4a55824e7e0a87a6bf7cc5cce1c3b29c35b9b4414aa2cf687a
7
- data.tar.gz: b943f80b7f0664155366080e2a9fe4d3b07e897b77c396d87d31d103c32935d16a7b44c75bf2525f729b028d2bb8bb7a77f8c9f2e98dd2a57ae7e67cb03fc326
6
+ metadata.gz: fd3d097a36e249e21f88ea3a1999207f9e45e464a9eb649d10c870d24d4a0705766dfb57df3ced2c82cda4e6cee04d5b53587b36743dbdd887da036d00c5d7c8
7
+ data.tar.gz: 755b9671c1aa2cbf14a90ddb9c730e3d50d5326a6244f2d8ece875b5bf5b5e9567c3c5f9661cd6cb6a00e6e4ec0594efcd5aa2fe0daf56e0fca7bf78c6a76d61
@@ -15,12 +15,12 @@
15
15
  # See the License for the specific language governing permissions and
16
16
  # limitations under the License.
17
17
 
18
- require 'digest'
19
- require 'kitchen'
20
- require 'tmpdir'
21
- require 'docker'
22
- require 'lockfile'
23
- require_relative '../helpers'
18
+ require "digest" unless defined?(Digest)
19
+ require "kitchen"
20
+ require "tmpdir" unless defined?(Dir.mktmpdir)
21
+ require "docker"
22
+ require "lockfile"
23
+ require_relative "../helpers"
24
24
 
25
25
  include Dokken::Helpers
26
26
 
@@ -37,31 +37,32 @@ module Kitchen
37
37
  default_config :binds, []
38
38
  default_config :cap_add, nil
39
39
  default_config :cap_drop, nil
40
- default_config :chef_image, 'chef/chef'
41
- default_config :chef_version, 'latest'
42
- default_config :data_image, 'dokken/kitchen-cache:latest'
40
+ default_config :chef_image, "chef/chef"
41
+ default_config :chef_version, "latest"
42
+ default_config :data_image, "dokken/kitchen-cache:latest"
43
43
  default_config :dns, nil
44
44
  default_config :dns_search, nil
45
+ default_config :docker_host_url, default_docker_host
45
46
  default_config :docker_info, docker_info
47
+ default_config :docker_registry, nil
46
48
  default_config :entrypoint, nil
47
49
  default_config :env, nil
48
- default_config :ports, nil
49
- default_config :docker_host_url, default_docker_host
50
- default_config :hostname, 'dokken'
50
+ default_config :hostname, "dokken"
51
51
  default_config :image_prefix, nil
52
52
  default_config :links, nil
53
- default_config :network_mode, 'dokken'
53
+ default_config :memory_limit, 0
54
+ default_config :network_mode, "dokken"
54
55
  default_config :pid_one_command, 'sh -c "trap exit 0 SIGTERM; while :; do sleep 1; done"'
56
+ default_config :ports, nil
55
57
  default_config :privileged, false
58
+ default_config :pull_chef_image, true
59
+ default_config :pull_platform_image, true
56
60
  default_config :read_timeout, 3600
57
61
  default_config :security_opt, nil
58
62
  default_config :tmpfs, {}
63
+ default_config :userns_host, false
59
64
  default_config :volumes, nil
60
65
  default_config :write_timeout, 3600
61
- default_config :userns_host, false
62
- default_config :pull_platform_image, true
63
- default_config :pull_chef_image, true
64
- default_config :memory_limit, 0
65
66
 
66
67
  # (see Base#create)
67
68
  def create(state)
@@ -126,37 +127,38 @@ module Kitchen
126
127
 
127
128
  def delete_work_image
128
129
  return unless ::Docker::Image.exist?(work_image, {}, docker_connection)
130
+
129
131
  with_retries { @work_image = ::Docker::Image.get(work_image, {}, docker_connection) }
130
132
 
131
133
  with_retries do
132
- begin
133
- with_retries { @work_image.remove(force: true) }
134
- rescue ::Docker::Error::ConflictError
135
- debug "driver - #{work_image} cannot be removed"
136
- end
134
+
135
+ with_retries { @work_image.remove(force: true) }
136
+ rescue ::Docker::Error::ConflictError
137
+ debug "driver - #{work_image} cannot be removed"
138
+
137
139
  end
138
140
  end
139
141
 
140
142
  def build_work_image(state)
141
- info('Building work image..')
143
+ info("Building work image..")
142
144
  return if ::Docker::Image.exist?(work_image, {}, docker_connection)
143
145
 
144
146
  begin
145
147
  @intermediate_image = ::Docker::Image.build(
146
148
  work_image_dockerfile,
147
149
  {
148
- 't' => work_image,
150
+ "t" => work_image,
149
151
  },
150
152
  docker_connection
151
153
  )
152
154
  # credit to https://github.com/someara/kitchen-dokken/issues/95#issue-224697526
153
155
  rescue Docker::Error::UnexpectedResponseError => e
154
- msg = 'work_image build failed: '
155
- msg += JSON.parse(e.to_s.split("\r\n").last)['error'].to_s
156
- msg += '. The common scenerios are incorrect intermediate'
157
- msg += 'instructions such as not including `-y` on an `apt-get` '
158
- msg += 'or similar. The other common scenerio is a transient '
159
- msg += 'error such as an unresponsive mirror.'
156
+ msg = "work_image build failed: "
157
+ msg += JSON.parse(e.to_s.split("\r\n").last)["error"].to_s
158
+ msg += ". The common scenarios are incorrect intermediate "
159
+ msg += "instructions such as not including `-y` on an `apt-get` "
160
+ msg += "or similar. The other common scenario is a transient "
161
+ msg += "error such as an unresponsive mirror."
160
162
  raise msg
161
163
  # fallback rescue above should catch most of the errors
162
164
  rescue => e
@@ -167,8 +169,10 @@ module Kitchen
167
169
  end
168
170
 
169
171
  def work_image_dockerfile
172
+ from = registry_image_path(platform_image)
173
+ debug("driver - Building work image from #{from}")
170
174
  dockerfile_contents = [
171
- "FROM #{platform_image}",
175
+ "FROM #{from}",
172
176
  "LABEL X-Built-By=kitchen-dokken X-Built-From=#{platform_image}",
173
177
  ]
174
178
  Array(config[:intermediate_instructions]).each do |c|
@@ -219,6 +223,7 @@ module Kitchen
219
223
 
220
224
  def work_image
221
225
  return "#{image_prefix}/#{instance_name}" unless image_prefix.nil?
226
+
222
227
  instance_name
223
228
  end
224
229
 
@@ -244,7 +249,7 @@ module Kitchen
244
249
  v
245
250
  else
246
251
  Array(v).each_with_object({}) do |y, h|
247
- name, opts = y.split(':', 2)
252
+ name, opts = y.split(":", 2)
248
253
  h[name.to_s] = opts.to_s
249
254
  end
250
255
  end
@@ -260,12 +265,13 @@ module Kitchen
260
265
  b = []
261
266
  v = Array(v).to_a # in case v.is_A?(Chef::Node::ImmutableArray)
262
267
  v.delete_if do |x|
263
- parts = x.split(':')
268
+ parts = x.split(":")
264
269
  b << x if parts.length > 1
265
270
  end
266
271
  b = nil if b.empty?
267
272
  config[:binds].push(b) unless config[:binds].include?(b) || b.nil?
268
273
  return PartialHash.new if v.empty?
274
+
269
275
  v.each_with_object(PartialHash.new) { |volume, h| h[volume] = {} }
270
276
  end
271
277
  end
@@ -281,41 +287,42 @@ module Kitchen
281
287
  debug "driver - starting #{runner_container_name}"
282
288
 
283
289
  config = {
284
- 'name' => runner_container_name,
285
- 'Cmd' => Shellwords.shellwords(self[:pid_one_command]),
286
- 'Image' => "#{repo(work_image)}:#{tag(work_image)}",
287
- 'Hostname' => self[:hostname],
288
- 'Env' => self[:env],
289
- 'ExposedPorts' => exposed_ports,
290
- 'Volumes' => dokken_volumes,
291
- 'HostConfig' => {
292
- 'Privileged' => self[:privileged],
293
- 'VolumesFrom' => dokken_volumes_from,
294
- 'Binds' => dokken_binds,
295
- 'Dns' => self[:dns],
296
- 'DnsSearch' => self[:dns_search],
297
- 'Links' => Array(self[:links]),
298
- 'CapAdd' => Array(self[:cap_add]),
299
- 'CapDrop' => Array(self[:cap_drop]),
300
- 'SecurityOpt' => Array(self[:security_opt]),
301
- 'NetworkMode' => self[:network_mode],
302
- 'PortBindings' => port_bindings,
303
- 'Tmpfs' => dokken_tmpfs,
304
- 'Memory' => self[:memory_limit],
290
+ "name" => runner_container_name,
291
+ "Cmd" => Shellwords.shellwords(self[:pid_one_command]),
292
+ # locally built image, must use short-name
293
+ "Image" => short_image_path(work_image),
294
+ "Hostname" => self[:hostname],
295
+ "Env" => self[:env],
296
+ "ExposedPorts" => exposed_ports,
297
+ "Volumes" => dokken_volumes,
298
+ "HostConfig" => {
299
+ "Privileged" => self[:privileged],
300
+ "VolumesFrom" => dokken_volumes_from,
301
+ "Binds" => dokken_binds,
302
+ "Dns" => self[:dns],
303
+ "DnsSearch" => self[:dns_search],
304
+ "Links" => Array(self[:links]),
305
+ "CapAdd" => Array(self[:cap_add]),
306
+ "CapDrop" => Array(self[:cap_drop]),
307
+ "SecurityOpt" => Array(self[:security_opt]),
308
+ "NetworkMode" => self[:network_mode],
309
+ "PortBindings" => port_bindings,
310
+ "Tmpfs" => dokken_tmpfs,
311
+ "Memory" => self[:memory_limit],
305
312
  },
306
- 'NetworkingConfig' => {
307
- 'EndpointsConfig' => {
313
+ "NetworkingConfig" => {
314
+ "EndpointsConfig" => {
308
315
  self[:network_mode] => {
309
- 'Aliases' => Array(self[:hostname]),
316
+ "Aliases" => Array(self[:hostname]),
310
317
  },
311
318
  },
312
319
  },
313
320
  }
314
321
  unless self[:entrypoint].to_s.empty?
315
- config['Entrypoint'] = self[:entrypoint]
322
+ config["Entrypoint"] = self[:entrypoint]
316
323
  end
317
324
  if self[:userns_host]
318
- config['HostConfig']['UsernsMode'] = 'host'
325
+ config["HostConfig"]["UsernsMode"] = "host"
319
326
  end
320
327
  runner_container = run_container(config)
321
328
  state[:runner_container] = runner_container.json
@@ -324,17 +331,18 @@ module Kitchen
324
331
  def start_data_container(state)
325
332
  debug "driver - creating #{data_container_name}"
326
333
  config = {
327
- 'name' => data_container_name,
328
- 'Image' => "#{repo(data_image)}:#{tag(data_image)}",
329
- 'HostConfig' => {
330
- 'PortBindings' => port_bindings,
331
- 'PublishAllPorts' => true,
332
- 'NetworkMode' => 'bridge',
334
+ "name" => data_container_name,
335
+ # locally built image, must use short-name
336
+ "Image" => short_image_path(data_image),
337
+ "HostConfig" => {
338
+ "PortBindings" => port_bindings,
339
+ "PublishAllPorts" => true,
340
+ "NetworkMode" => "bridge",
333
341
  },
334
- 'NetworkingConfig' => {
335
- 'EndpointsConfig' => {
342
+ "NetworkingConfig" => {
343
+ "EndpointsConfig" => {
336
344
  self[:network_mode] => {
337
- 'Aliases' => Array(self[:hostname]),
345
+ "Aliases" => Array(self[:hostname]),
338
346
  },
339
347
  },
340
348
  },
@@ -347,10 +355,10 @@ module Kitchen
347
355
  lockfile = Lockfile.new "#{home_dir}/.dokken-network.lock"
348
356
  begin
349
357
  lockfile.lock
350
- with_retries { ::Docker::Network.get('dokken', {}, docker_connection) }
351
- rescue
358
+ with_retries { ::Docker::Network.get("dokken", {}, docker_connection) }
359
+ rescue ::Docker::Error::NotFoundError
352
360
  begin
353
- with_retries { ::Docker::Network.create('dokken', {}) }
361
+ with_retries { ::Docker::Network.create("dokken", {}) }
354
362
  rescue ::Docker::Error => e
355
363
  debug "driver - error :#{e}:"
356
364
  end
@@ -360,32 +368,39 @@ module Kitchen
360
368
  end
361
369
 
362
370
  def make_data_image
363
- debug 'driver - calling create_data_image'
364
- create_data_image
371
+ debug "driver - calling create_data_image"
372
+ create_data_image(config[:docker_registry])
365
373
  end
366
374
 
367
375
  def create_chef_container(state)
368
376
  lockfile = Lockfile.new "#{home_dir}/.dokken-#{chef_container_name}.lock"
369
377
  begin
370
378
  lockfile.lock
371
- with_retries { ::Docker::Container.get(chef_container_name, {}, docker_connection) }
379
+ with_retries {
380
+ # TEMPORARY FIX - docker-api 2.0.0 has a buggy Docker::Container.get - use .all instead
381
+ # https://github.com/swipely/docker-api/issues/566
382
+ # ::Docker::Container.get(chef_container_name, {}, docker_connection)
383
+ found = ::Docker::Container.all({ all: true }, docker_connection).select { |c| c.info["Names"].include?("/#{chef_container_name}") }
384
+ raise ::Docker::Error::NotFoundError.new(chef_container_name) if found.empty?
385
+
386
+ debug "Chef container already exists, continuing"
387
+ }
372
388
  rescue ::Docker::Error::NotFoundError
389
+ debug "Chef container does not exist, creating a new Chef container"
373
390
  with_retries do
374
- begin
375
- debug "driver - creating volume container #{chef_container_name} from #{chef_image}"
376
- config = {
377
- 'name' => chef_container_name,
378
- 'Cmd' => 'true',
379
- 'Image' => "#{repo(chef_image)}:#{tag(chef_image)}",
380
- 'HostConfig' => {
381
- 'NetworkMode' => self[:network_mode],
382
- },
383
- }
384
- chef_container = create_container(config)
385
- state[:chef_container] = chef_container.json
386
- rescue ::Docker::Error => e
387
- raise "driver - #{chef_container_name} failed to create #{e}"
388
- end
391
+ debug "driver - creating volume container #{chef_container_name} from #{chef_image}"
392
+ config = {
393
+ "name" => chef_container_name,
394
+ "Cmd" => "true",
395
+ "Image" => registry_image_path(chef_image),
396
+ "HostConfig" => {
397
+ "NetworkMode" => self[:network_mode],
398
+ },
399
+ }
400
+ chef_container = create_container(config)
401
+ state[:chef_container] = chef_container.json
402
+ rescue ::Docker::Error, StandardError => e
403
+ raise "driver - #{chef_container_name} failed to create #{e}"
389
404
  end
390
405
  ensure
391
406
  lockfile.unlock
@@ -393,12 +408,12 @@ module Kitchen
393
408
  end
394
409
 
395
410
  def pull_platform_image
396
- debug "driver - pulling #{chef_image} #{repo(platform_image)} #{tag(platform_image)}"
411
+ debug "driver - pulling #{short_image_path(platform_image)}"
397
412
  config[:pull_platform_image] ? pull_image(platform_image) : pull_if_missing(platform_image)
398
413
  end
399
414
 
400
415
  def pull_chef_image
401
- debug "driver - pulling #{chef_image} #{repo(chef_image)} #{tag(chef_image)}"
416
+ debug "driver - pulling #{short_image_path(chef_image)}"
402
417
  config[:pull_chef_image] ? pull_image(chef_image) : pull_if_missing(chef_image)
403
418
  end
404
419
 
@@ -411,50 +426,83 @@ module Kitchen
411
426
 
412
427
  def container_exist?(name)
413
428
  return true if ::Docker::Container.get(name, {}, docker_connection)
414
- rescue
429
+ rescue StandardError, ::Docker::Error::NotFoundError
415
430
  false
416
431
  end
417
432
 
418
433
  def parse_image_name(image)
419
- parts = image.split(':')
434
+ parts = image.split(":")
420
435
 
421
436
  if parts.size > 2
422
437
  tag = parts.pop
423
- repo = parts.join(':')
438
+ repo = parts.join(":")
424
439
  else
425
- tag = parts[1] || 'latest'
440
+ tag = parts[1] || "latest"
426
441
  repo = parts[0]
427
442
  end
428
443
 
429
444
  [repo, tag]
430
445
  end
431
446
 
447
+ # Return the 'repo' half of a docker image path. Agnostic about if a
448
+ # registry is included, this effectively is just "before the colon"
449
+ #
450
+ # @param image [String] the docker image path to parse
451
+ # @return [String] the repo portion of `image`
432
452
  def repo(image)
433
453
  parse_image_name(image)[0]
434
454
  end
435
455
 
456
+ # Return the 'tag' of a docker image path. Will be `latest` if there
457
+ # is no explicit tag in the image path.
458
+ #
459
+ # @param image [String] the docker image path to parse
460
+ # @return [String] the tag of `image`
461
+ def tag(image)
462
+ parse_image_name(image)[1]
463
+ end
464
+
465
+ # Ensures an explicit tag on an image path.
466
+ #
467
+ # @param image [String] the docker image path to parse
468
+ # @return [String] `repo`:`tag`
469
+ def short_image_path(image)
470
+ "#{repo(image)}:#{tag(image)}"
471
+ end
472
+
473
+ # Qualifies the results of `short_image_path` with any registry the
474
+ # user has requested
475
+ #
476
+ # @param image [String] the docker image path to parse
477
+ # @return [String] The most fully-qualified registry path we cn make
478
+ def registry_image_path(image)
479
+ if config[:docker_registry]
480
+ "#{config[:docker_registry]}/#{short_image_path(image)}"
481
+ else
482
+ short_image_path(image)
483
+ end
484
+ end
485
+
436
486
  def create_container(args)
437
- with_retries { @container = ::Docker::Container.get(args['name'], {}, docker_connection) }
487
+ with_retries { @container = ::Docker::Container.get(args["name"], {}, docker_connection) }
438
488
  rescue
439
489
  with_retries do
440
- begin
441
- args['Env'] = [] if args['Env'].nil?
442
- args['Env'] << 'TEST_KITCHEN=1'
443
- args['Env'] << "CI=#{ENV['CI']}" if ENV.include? 'CI'
444
- info "Creating container #{args['name']}"
445
- debug "driver - create_container args #{args}"
446
- with_retries do
447
- begin
448
- @container = ::Docker::Container.create(args.clone, docker_connection)
449
- rescue ::Docker::Error::ConflictError
450
- debug "driver - rescue ConflictError: #{args['name']}"
451
- with_retries { @container = ::Docker::Container.get(args['name'], {}, docker_connection) }
452
- end
453
- end
454
- rescue ::Docker::Error => e
455
- debug "driver - error :#{e}:"
456
- raise "driver - failed to create_container #{args['name']}"
490
+ args["Env"] = [] if args["Env"].nil?
491
+ args["Env"] << "TEST_KITCHEN=1"
492
+ args["Env"] << "CI=#{ENV["CI"]}" if ENV.include? "CI"
493
+ info "Creating container #{args["name"]}"
494
+ debug "driver - create_container args #{args}"
495
+ with_retries do
496
+
497
+ @container = ::Docker::Container.create(args.clone, docker_connection)
498
+ rescue ::Docker::Error::ConflictError
499
+ debug "driver - rescue ConflictError: #{args["name"]}"
500
+ with_retries { @container = ::Docker::Container.get(args["name"], {}, docker_connection) }
457
501
  end
502
+ rescue ::Docker::Error => e
503
+ debug "driver - error :#{e}:"
504
+ raise "driver - failed to create_container #{args["name"]}"
505
+
458
506
  end
459
507
  end
460
508
 
@@ -462,14 +510,14 @@ module Kitchen
462
510
  create_container(args)
463
511
  with_retries do
464
512
  @container.start
465
- @container = ::Docker::Container.get(args['name'], {}, docker_connection)
466
- wait_running_state(args['name'], true)
513
+ @container = ::Docker::Container.get(args["name"], {}, docker_connection)
514
+ wait_running_state(args["name"], true)
467
515
  end
468
516
  @container
469
517
  end
470
518
 
471
519
  def container_state
472
- @container ? @container.info['State'] : {}
520
+ @container ? @container.info["State"] : {}
473
521
  end
474
522
 
475
523
  def stop_container(name)
@@ -493,18 +541,15 @@ module Kitchen
493
541
  @container = ::Docker::Container.get(name, {}, docker_connection)
494
542
  i = 0
495
543
  tries = 20
496
- until container_state['Running'] == v || container_state['FinishedAt'] != '0001-01-01T00:00:00Z'
544
+ until container_state["Running"] == v || container_state["FinishedAt"] != "0001-01-01T00:00:00Z"
497
545
  i += 1
498
546
  break if i == tries
547
+
499
548
  sleep 0.1
500
549
  @container = ::Docker::Container.get(name, {}, docker_connection)
501
550
  end
502
551
  end
503
552
 
504
- def tag(image)
505
- parse_image_name(image)[1]
506
- end
507
-
508
553
  def chef_container_name
509
554
  "chef-#{chef_version}"
510
555
  end
@@ -514,7 +559,8 @@ module Kitchen
514
559
  end
515
560
 
516
561
  def chef_version
517
- return 'latest' if config[:chef_version] == 'stable'
562
+ return "latest" if config[:chef_version] == "stable"
563
+
518
564
  config[:chef_version]
519
565
  end
520
566
 
@@ -531,27 +577,29 @@ module Kitchen
531
577
  end
532
578
 
533
579
  def platform_image_from_name
534
- platform, release = instance.platform.name.split('-')
535
- release ? [platform, release].join(':') : platform
580
+ platform, release = instance.platform.name.split("-")
581
+ release ? [platform, release].join(":") : platform
536
582
  end
537
583
 
538
584
  def pull_if_missing(image)
539
- return if ::Docker::Image.exist?("#{repo(image)}:#{tag(image)}", {}, docker_connection)
585
+ return if ::Docker::Image.exist?(registry_image_path(image), {}, docker_connection)
586
+
540
587
  pull_image image
541
588
  end
542
589
 
543
590
  # https://github.com/docker/docker/blob/4fcb9ac40ce33c4d6e08d5669af6be5e076e2574/registry/auth.go#L231
544
591
  def parse_registry_host(val)
545
- val.sub(%r{https?://}, '').split('/').first
592
+ val.sub(%r{https?://}, "").split("/").first
546
593
  end
547
594
 
548
595
  def pull_image(image)
596
+ path = registry_image_path(image)
549
597
  with_retries do
550
- if Docker::Image.exist?("#{repo(image)}:#{tag(image)}", {}, docker_connection)
551
- original_image = Docker::Image.get("#{repo(image)}:#{tag(image)}", {}, docker_connection)
598
+ if Docker::Image.exist?(path, {}, docker_connection)
599
+ original_image = Docker::Image.get(path, {}, docker_connection)
552
600
  end
553
601
 
554
- new_image = Docker::Image.create({ 'fromImage' => "#{repo(image)}:#{tag(image)}" }, docker_connection)
602
+ new_image = Docker::Image.create({ "fromImage" => path }, docker_connection)
555
603
 
556
604
  !(original_image && original_image.id.start_with?(new_image.id))
557
605
  end
@@ -18,6 +18,6 @@
18
18
  module Kitchen
19
19
  module Driver
20
20
  # Version string for Dokken Kitchen driver
21
- DOKKEN_VERSION = '2.10.0'.freeze
21
+ DOKKEN_VERSION = "2.12.1".freeze
22
22
  end
23
23
  end
@@ -1,23 +1,20 @@
1
1
  module Dokken
2
2
  module Helpers
3
3
  # https://stackoverflow.com/questions/517219/ruby-see-if-a-port-is-open
4
- require 'socket'
5
- require 'timeout'
4
+ require "socket" unless defined?(Socket)
5
+ require "timeout" unless defined?(Timeout)
6
6
 
7
7
  def port_open?(ip, port)
8
8
  begin
9
9
  Timeout.timeout(1) do
10
- begin
11
- s = TCPSocket.new(ip, port)
12
- s.close
13
- return true
14
- rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
15
- return false
16
- end
10
+ s = TCPSocket.new(ip, port)
11
+ s.close
12
+ return true
13
+ rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH, Errno::ENETDOWN
14
+ return false
17
15
  end
18
16
  rescue Timeout::Error
19
17
  end
20
-
21
18
  false
22
19
  end
23
20
 
@@ -59,9 +56,13 @@ X8N2N9ZNnORJqK374yGj1jWUU66mQhPvn49QpG8P2HEoh2RQjNvyHA==
59
56
  EOF
60
57
  end
61
58
 
62
- def data_dockerfile
59
+ def data_dockerfile(registry)
60
+ from = "centos:7"
61
+ if registry
62
+ from = "#{registry}/#{from}"
63
+ end
63
64
  <<-EOF
64
- FROM centos:7
65
+ FROM #{from}
65
66
  MAINTAINER Sean OMeara \"sean@sean.io\"
66
67
  ENV LANG en_US.UTF-8
67
68
 
@@ -86,32 +87,32 @@ VOLUME /opt/verifier
86
87
  EOF
87
88
  end
88
89
 
89
- def create_data_image
90
+ def create_data_image(registry)
90
91
  return if ::Docker::Image.exist?(data_image)
91
92
 
92
93
  tmpdir = Dir.tmpdir
93
94
  FileUtils.mkdir_p "#{tmpdir}/dokken"
94
- File.write("#{tmpdir}/dokken/Dockerfile", data_dockerfile)
95
+ File.write("#{tmpdir}/dokken/Dockerfile", data_dockerfile(registry))
95
96
  File.write("#{tmpdir}/dokken/authorized_keys", insecure_ssh_public_key)
96
97
 
97
98
  i = ::Docker::Image.build_from_dir(
98
99
  "#{tmpdir}/dokken",
99
- 'nocache' => true,
100
- 'rm' => true
100
+ "nocache" => true,
101
+ "rm" => true
101
102
  )
102
- i.tag('repo' => repo(data_image), 'tag' => tag(data_image), 'force' => true)
103
+ i.tag("repo" => repo(data_image), "tag" => tag(data_image), "force" => true)
103
104
  end
104
105
 
105
106
  def default_docker_host
106
- if ENV['DOCKER_HOST']
107
- ENV['DOCKER_HOST']
108
- elsif File.exist?('/var/run/docker.sock')
109
- 'unix:///var/run/docker.sock'
107
+ if ENV["DOCKER_HOST"]
108
+ ENV["DOCKER_HOST"]
109
+ elsif File.exist?("/var/run/docker.sock")
110
+ "unix:///var/run/docker.sock"
110
111
  # TODO: Docker for Windows also operates over a named pipe at
111
112
  # //./pipe/docker_engine that can be used if named pipe support is
112
113
  # added to the docker-api gem.
113
114
  else
114
- 'tcp://127.0.0.1:2375'
115
+ "tcp://127.0.0.1:2375"
115
116
  end
116
117
  end
117
118
 
@@ -152,7 +153,8 @@ VOLUME /opt/verifier
152
153
  # refs:
153
154
  # https://github.com/docker/machine/issues/1814
154
155
  # https://github.com/docker/toolbox/issues/607
155
- return Dir.home.sub 'C:/Users', '/c/Users' if Dir.home =~ /^C:/ && remote_docker_host?
156
+ return Dir.home.sub "C:/Users", "/c/Users" if Dir.home =~ /^C:/ && remote_docker_host?
157
+
156
158
  Dir.home
157
159
  end
158
160
 
@@ -185,7 +187,7 @@ VOLUME /opt/verifier
185
187
  x = Array(v).map { |a| parse_port(a) }
186
188
  x.flatten!
187
189
  x.each_with_object({}) do |y, h|
188
- h[y['container_port']] = {}
190
+ h[y["container_port"]] = {}
189
191
  end
190
192
  end
191
193
  end
@@ -198,52 +200,53 @@ VOLUME /opt/verifier
198
200
  x = Array(v).map { |a| parse_port(a) }
199
201
  x.flatten!
200
202
  x.each_with_object({}) do |y, h|
201
- h[y['container_port']] = [] unless h[y['container_port']]
202
- h[y['container_port']] << {
203
- 'HostIp' => y['host_ip'],
204
- 'HostPort' => y['host_port'],
203
+ h[y["container_port"]] = [] unless h[y["container_port"]]
204
+ h[y["container_port"]] << {
205
+ "HostIp" => y["host_ip"],
206
+ "HostPort" => y["host_port"],
205
207
  }
206
208
  end
207
209
  end
208
210
  end
209
211
 
210
212
  def parse_port(v)
211
- parts = v.split(':')
213
+ parts = v.split(":")
212
214
  case parts.length
213
215
  when 3
214
216
  host_ip = parts[0]
215
217
  host_port = parts[1]
216
218
  container_port = parts[2]
217
219
  when 2
218
- host_ip = '0.0.0.0'
220
+ host_ip = "0.0.0.0"
219
221
  host_port = parts[0]
220
222
  container_port = parts[1]
221
223
  when 1
222
- host_ip = ''
223
- host_port = ''
224
+ host_ip = ""
225
+ host_port = ""
224
226
  container_port = parts[0]
225
227
  end
226
- port_range, protocol = container_port.split('/')
227
- if port_range.include?('-')
228
- port_range = container_port.split('-')
228
+ port_range, protocol = container_port.split("/")
229
+ if port_range.include?("-")
230
+ port_range = container_port.split("-")
229
231
  port_range.map!(&:to_i)
230
232
  Chef::Log.fatal("FATAL: Invalid port range! #{container_port}") if port_range[0] > port_range[1]
231
233
  port_range = (port_range[0]..port_range[1]).to_a
232
234
  end
233
235
  # qualify the port-binding protocol even when it is implicitly tcp #427.
234
- protocol = 'tcp' if protocol.nil?
236
+ protocol = "tcp" if protocol.nil?
235
237
  Array(port_range).map do |port|
236
238
  {
237
- 'host_ip' => host_ip,
238
- 'host_port' => host_port,
239
- 'container_port' => "#{port}/#{protocol}",
239
+ "host_ip" => host_ip,
240
+ "host_port" => host_port,
241
+ "container_port" => "#{port}/#{protocol}",
240
242
  }
241
243
  end
242
244
  end
243
245
 
244
246
  def remote_docker_host?
245
- return false if config[:docker_info]['OperatingSystem'].include?('Boot2Docker')
246
- return true if config[:docker_host_url] =~ /^tcp:/
247
+ return false if config[:docker_info]["OperatingSystem"].include?("Boot2Docker")
248
+ return true if /^tcp:/.match?(config[:docker_host_url])
249
+
247
250
  false
248
251
  end
249
252
 
@@ -252,13 +255,13 @@ VOLUME /opt/verifier
252
255
  end
253
256
 
254
257
  def sandbox_dirs
255
- Dir.glob(File.join(sandbox_path, '*'))
258
+ Dir.glob(File.join(sandbox_path, "*"))
256
259
  end
257
260
 
258
261
  def create_sandbox
259
262
  info("Creating kitchen sandbox in #{sandbox_path}")
260
263
  unless ::Dir.exist?(sandbox_path)
261
- FileUtils.mkdir_p(sandbox_path, :mode => 0o755)
264
+ FileUtils.mkdir_p(sandbox_path, mode: 0o755)
262
265
  end
263
266
  end
264
267
  end
@@ -269,7 +272,7 @@ module Kitchen
269
272
  class Base
270
273
  def create_sandbox
271
274
  info("Creating kitchen sandbox in #{sandbox_path}")
272
- FileUtils.mkdir_p(sandbox_path, :mode => 0o755)
275
+ FileUtils.mkdir_p(sandbox_path, mode: 0o755)
273
276
  end
274
277
 
275
278
  # this MUST be named 'sandbox_path' because ruby.
@@ -291,7 +294,7 @@ module Kitchen
291
294
  def create_sandbox
292
295
  info("Creating kitchen sandbox in #{sandbox_path}")
293
296
  unless ::Dir.exist?(sandbox_path)
294
- FileUtils.mkdir_p(sandbox_path, :mode => 0o755)
297
+ FileUtils.mkdir_p(sandbox_path, mode: 0o755)
295
298
  end
296
299
  end
297
300
 
@@ -313,7 +316,7 @@ module Kitchen
313
316
  conn.execute(init_command)
314
317
  info("Transferring files to #{instance.to_str}")
315
318
  conn.upload(sandbox_dirs, config[:root_path])
316
- debug('Transfer complete')
319
+ debug("Transfer complete")
317
320
  end
318
321
 
319
322
  conn.execute(prepare_command)
@@ -15,9 +15,9 @@
15
15
  # See the License for the specific language governing permissions and
16
16
  # limitations under the License.
17
17
 
18
- require 'kitchen'
19
- require 'kitchen/provisioner/chef_zero'
20
- require_relative '../helpers'
18
+ require "kitchen"
19
+ require "kitchen/provisioner/chef_zero"
20
+ require_relative "../helpers"
21
21
 
22
22
  include Dokken::Helpers
23
23
 
@@ -29,11 +29,12 @@ module Kitchen
29
29
 
30
30
  plugin_version Kitchen::VERSION
31
31
 
32
- default_config :root_path, '/opt/kitchen'
33
- default_config :chef_binary, '/opt/chef/bin/chef-client'
34
- default_config :chef_options, ' -z'
35
- default_config :chef_log_level, 'warn'
36
- default_config :chef_output_format, 'doc'
32
+ default_config :root_path, "/opt/kitchen"
33
+ default_config :chef_binary, "/opt/chef/bin/chef-client"
34
+ default_config :chef_options, " -z"
35
+ default_config :chef_log_level, "warn"
36
+ default_config :chef_output_format, "doc"
37
+ default_config :profile_ruby, false
37
38
  default_config :docker_info, docker_info
38
39
  default_config :docker_host_url, default_docker_host
39
40
 
@@ -45,7 +46,7 @@ module Kitchen
45
46
  # driver and set it here. If we remove this, users will set their chef_version
46
47
  # to 14 in the driver and still get prompted for license acceptance because
47
48
  # the ChefZero provisioner defaults product_version to 'latest'.
48
- default_config :product_name, 'chef'
49
+ default_config :product_name, "chef"
49
50
  default_config :product_version do |provisioner|
50
51
  driver = provisioner.instance.driver
51
52
  driver[:chef_version]
@@ -73,15 +74,16 @@ module Kitchen
73
74
  rescue Kitchen::Transport::TransportFailed => ex
74
75
  raise ActionFailed, ex.message
75
76
  ensure
76
- return unless config[:clean_dokken_sandbox]
77
+ return unless config[:clean_dokken_sandbox] # rubocop: disable Lint/EnsureReturn
78
+
77
79
  cleanup_dokken_sandbox
78
80
  end
79
81
 
80
82
  def validate_config
81
83
  # check if we have an space for the user provided options
82
84
  # or add it if not to avoid issues
83
- unless config[:chef_options].start_with? ' '
84
- config[:chef_options].prepend(' ')
85
+ unless config[:chef_options].start_with? " "
86
+ config[:chef_options].prepend(" ")
85
87
  end
86
88
 
87
89
  # strip spaces from all other options
@@ -91,8 +93,8 @@ module Kitchen
91
93
 
92
94
  # if the user wants to be funny and pass empty strings
93
95
  # just use the defaults
94
- config[:chef_log_level] = 'warn' if config[:chef_log_level].empty?
95
- config[:chef_output_format] = 'doc' if config[:chef_output_format].empty?
96
+ config[:chef_log_level] = "warn" if config[:chef_log_level].empty?
97
+ config[:chef_output_format] = "doc" if config[:chef_output_format].empty?
96
98
  end
97
99
 
98
100
  private
@@ -104,8 +106,9 @@ module Kitchen
104
106
  cmd << config[:chef_options].to_s
105
107
  cmd << " -l #{config[:chef_log_level]}"
106
108
  cmd << " -F #{config[:chef_output_format]}"
107
- cmd << ' -c /opt/kitchen/client.rb'
108
- cmd << ' -j /opt/kitchen/dna.json'
109
+ cmd << " -c /opt/kitchen/client.rb"
110
+ cmd << " -j /opt/kitchen/dna.json"
111
+ cmd << "--profile-ruby" if config[:profile_ruby]
109
112
 
110
113
  chef_cmd(cmd)
111
114
  end
@@ -15,11 +15,11 @@
15
15
  # See the License for the specific language governing permissions and
16
16
  # limitations under the License.
17
17
 
18
- require 'kitchen'
19
- require 'net/scp'
20
- require 'tmpdir'
21
- require 'digest/sha1'
22
- require_relative '../helpers'
18
+ require "kitchen"
19
+ require "net/scp"
20
+ require "tmpdir" unless defined?(Dir.mktmpdir)
21
+ require "digest/sha1" unless defined?(Digest::SHA1)
22
+ require_relative "../helpers"
23
23
 
24
24
  include Dokken::Helpers
25
25
 
@@ -44,7 +44,7 @@ module Kitchen
44
44
  default_config :read_timeout, 3600
45
45
  default_config :write_timeout, 3600
46
46
  default_config :host_ip_override do |transport|
47
- transport.docker_for_mac_or_win? ? 'localhost' : false
47
+ transport.docker_for_mac_or_win? ? "localhost" : false
48
48
  end
49
49
 
50
50
  # (see Base#connection)
@@ -69,7 +69,7 @@ module Kitchen
69
69
 
70
70
  with_retries { @runner = ::Docker::Container.get(instance_name, {}, docker_connection) }
71
71
  with_retries do
72
- o = @runner.exec(Shellwords.shellwords(command), wait: options[:timeout], 'e' => { 'TERM' => 'xterm' }) { |_stream, chunk| print chunk.to_s }
72
+ o = @runner.exec(Shellwords.shellwords(command), wait: options[:timeout], "e" => { "TERM" => "xterm" }) { |_stream, chunk| print chunk.to_s }
73
73
  @exit_code = o[2]
74
74
  end
75
75
 
@@ -82,26 +82,26 @@ module Kitchen
82
82
  ssh_ip = options[:host_ip_override]
83
83
  ssh_port = options[:data_container][:NetworkSettings][:Ports][:"22/tcp"][0][:HostPort]
84
84
 
85
- elsif options[:docker_host_url] =~ /unix:/
86
- if options[:data_container][:NetworkSettings][:Ports][:"22/tcp"][0][:HostIp] == '0.0.0.0'
85
+ elsif /unix:/.match?(options[:docker_host_url])
86
+ if options[:data_container][:NetworkSettings][:Ports][:"22/tcp"][0][:HostIp] == "0.0.0.0"
87
87
  ssh_ip = options[:data_container][:NetworkSettings][:IPAddress]
88
- ssh_port = '22'
88
+ ssh_port = "22"
89
89
  else
90
90
  # we should read the proper mapped ip, since this allows us to upload the files
91
91
  ssh_ip = options[:data_container][:NetworkSettings][:Ports][:"22/tcp"][0][:HostIp]
92
92
  ssh_port = options[:data_container][:NetworkSettings][:Ports][:"22/tcp"][0][:HostPort]
93
93
  end
94
94
 
95
- elsif options[:docker_host_url] =~ /tcp:/
95
+ elsif /tcp:/.match?(options[:docker_host_url])
96
96
  name = options[:data_container][:Name]
97
97
 
98
98
  # DOCKER_HOST
99
- docker_host_url_ip = options[:docker_host_url].split('tcp://')[1].split(':')[0]
99
+ docker_host_url_ip = options[:docker_host_url].split("tcp://")[1].split(":")[0]
100
100
 
101
101
  # mapped IP of data container
102
- candidate_ip = ::Docker::Container.all.select do |x|
103
- x.info['Names'][0].eql?(name)
104
- end.first.info['NetworkSettings']['Networks']['dokken']['IPAddress']
102
+ candidate_ip = ::Docker::Container.all.find do |x|
103
+ x.info["Names"][0].eql?(name)
104
+ end.info["NetworkSettings"]["Networks"]["dokken"]["IPAddress"]
105
105
 
106
106
  # mapped port
107
107
  candidate_ssh_port = options[:data_container][:NetworkSettings][:Ports][:"22/tcp"][0][:HostPort]
@@ -114,22 +114,22 @@ module Kitchen
114
114
  ssh_ip = candidate_ip
115
115
  ssh_port = candidate_ssh_port
116
116
 
117
- elsif port_open?(candidate_ip, '22')
117
+ elsif port_open?(candidate_ip, "22")
118
118
  ssh_ip = candidate_ip
119
- ssh_port = '22'
119
+ ssh_port = "22"
120
120
  debug "candidate_ip - #{candidate_ip}/22 open"
121
121
  else
122
122
  ssh_ip = docker_host_url_ip
123
123
  ssh_port = candidate_ssh_port
124
124
  end
125
125
  else
126
- raise Kitchen::UserError, 'docker_host_url must be tcp:// or unix://'
126
+ raise Kitchen::UserError, "docker_host_url must be tcp:// or unix://"
127
127
  end
128
128
 
129
129
  debug "ssh_ip : #{ssh_ip}"
130
130
  debug "ssh_port : #{ssh_port}"
131
131
 
132
- tmpdir = Dir.tmpdir + '/dokken/'
132
+ tmpdir = Dir.tmpdir + "/dokken/"
133
133
  FileUtils.mkdir_p tmpdir.to_s, mode: 0o777
134
134
  tmpdir += Process.uid.to_s
135
135
  FileUtils.mkdir_p tmpdir.to_s
@@ -137,26 +137,26 @@ module Kitchen
137
137
  FileUtils.chmod(0o600, "#{tmpdir}/id_rsa")
138
138
 
139
139
  begin
140
- rsync_cmd = '/usr/bin/rsync -a -e'
141
- rsync_cmd << ' \''
142
- rsync_cmd << 'ssh -2'
140
+ rsync_cmd = "/usr/bin/rsync -a -e"
141
+ rsync_cmd << " '"
142
+ rsync_cmd << "ssh -2"
143
143
  rsync_cmd << " -i #{tmpdir}/id_rsa"
144
- rsync_cmd << ' -o CheckHostIP=no'
145
- rsync_cmd << ' -o Compression=no'
146
- rsync_cmd << ' -o PasswordAuthentication=no'
147
- rsync_cmd << ' -o StrictHostKeyChecking=no'
148
- rsync_cmd << ' -o UserKnownHostsFile=/dev/null'
149
- rsync_cmd << ' -o LogLevel=ERROR'
144
+ rsync_cmd << " -o CheckHostIP=no"
145
+ rsync_cmd << " -o Compression=no"
146
+ rsync_cmd << " -o PasswordAuthentication=no"
147
+ rsync_cmd << " -o StrictHostKeyChecking=no"
148
+ rsync_cmd << " -o UserKnownHostsFile=/dev/null"
149
+ rsync_cmd << " -o LogLevel=ERROR"
150
150
  rsync_cmd << " -p #{ssh_port}"
151
- rsync_cmd << '\''
152
- rsync_cmd << " #{locals.join(' ')} root@#{ssh_ip}:#{remote}"
151
+ rsync_cmd << "'"
152
+ rsync_cmd << " #{locals.join(" ")} root@#{ssh_ip}:#{remote}"
153
153
  debug "rsync_cmd :#{rsync_cmd}:"
154
154
  `#{rsync_cmd}`
155
155
  rescue Errno::ENOENT
156
- debug 'Rsync is not installed. Falling back to SCP.'
156
+ debug "Rsync is not installed. Falling back to SCP."
157
157
  locals.each do |local|
158
158
  Net::SCP.upload!(ssh_ip,
159
- 'root',
159
+ "root",
160
160
  local,
161
161
  remote,
162
162
  recursive: true,
@@ -169,8 +169,8 @@ module Kitchen
169
169
  @runner = options[:instance_name].to_s
170
170
  cols = `tput cols`
171
171
  lines = `tput lines`
172
- args = ['exec', '-e', "COLUMNS=#{cols}", '-e', "LINES=#{lines}", '-it', @runner, '/bin/bash', '-login', '-i']
173
- LoginCommand.new('docker', args)
172
+ args = ["exec", "-e", "COLUMNS=#{cols}", "-e", "LINES=#{lines}", "-it", @runner, "/bin/bash", "-login", "-i"]
173
+ LoginCommand.new("docker", args)
174
174
  end
175
175
 
176
176
  private
@@ -181,6 +181,7 @@ module Kitchen
181
181
 
182
182
  def work_image
183
183
  return "#{image_prefix}/#{instance_name}" unless image_prefix.nil?
184
+
184
185
  instance_name
185
186
  end
186
187
 
@@ -208,7 +209,7 @@ module Kitchen
208
209
  #
209
210
  # @return [TrueClass,FalseClass]
210
211
  def docker_for_mac_or_win?
211
- ::Docker.info(::Docker::Connection.new(config[:docker_host_url], {}))['Name'] == 'moby'
212
+ ::Docker.info(::Docker::Connection.new(config[:docker_host_url], {}))["Name"] == "moby"
212
213
  rescue
213
214
  false
214
215
  end
metadata CHANGED
@@ -1,29 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kitchen-dokken
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.10.0
4
+ version: 2.12.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean OMeara
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-14 00:00:00.000000000 Z
11
+ date: 2021-03-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: docker-api
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.33'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '3'
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
- - - "~>"
27
+ - - ">="
25
28
  - !ruby/object:Gem::Version
26
29
  version: '1.33'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '3'
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: lockfile
29
35
  requirement: !ruby/object:Gem::Requirement
@@ -58,7 +64,8 @@ dependencies:
58
64
  - - "<"
59
65
  - !ruby/object:Gem::Version
60
66
  version: '3'
61
- description: A Test Kitchen Driver for Dokken
67
+ description: A Test Kitchen Driver for Docker & Chef Infra optimized for rapid testing
68
+ using Chef Infra docker images
62
69
  email:
63
70
  - sean@sean.io
64
71
  executables: []
@@ -83,16 +90,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
83
90
  requirements:
84
91
  - - ">="
85
92
  - !ruby/object:Gem::Version
86
- version: '0'
93
+ version: '2.5'
87
94
  required_rubygems_version: !ruby/object:Gem::Requirement
88
95
  requirements:
89
96
  - - ">="
90
97
  - !ruby/object:Gem::Version
91
98
  version: '0'
92
99
  requirements: []
93
- rubygems_version: 3.1.2
100
+ rubygems_version: 3.1.4
94
101
  signing_key:
95
102
  specification_version: 4
96
- summary: A Test Kitchen Driver that talks to the Docker Remote API and uses Volumes
97
- to produce sparse container images
103
+ summary: A Test Kitchen Driver for Docker & Chef Infra optimized for rapid testing
104
+ using Chef Infra docker images
98
105
  test_files: []