kitchen-dokken 2.10.0 → 2.12.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.
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: []