diff -Nru ruby-kitchen-docker-2.7.0/CHANGELOG.md ruby-kitchen-docker-2.10.0/CHANGELOG.md --- ruby-kitchen-docker-2.7.0/CHANGELOG.md 2018-12-25 20:53:24.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/CHANGELOG.md 2020-04-02 10:46:32.000000000 +0000 @@ -1,5 +1,25 @@ # Kitchen-Docker Changelog +## 2.10.0 - Mar 28, 2020 + +* Switched from require to require_relative to slightly improve load time performance +* Allow for train gem 3.x +* Refactor driver to include Windows support (includes new transport for all supported platforms) + +## 2.9.0 - Mar 15, 2019 + +* Add automatic OS detection for amazonlinux, opensuse/leap, and opensuse/tumbleweed +* On Fedora containers uses dnf to setup the OS not yum + +## 2.8.0 - Jan 18, 2019 + +* Add new config option `use_internal_docker_network`, which allows running Docker within Docker. See readme for usage details. +* Resolve errors while loading libraries on archlinux +* Fix failures on Ubuntu 18.04 +* Check if image exists before attempting to remove it so we don't fail +* Add oraclelinux platform support +* Prevent `uninitialized constant Kitchen::Driver::Docker::Base64` error by requiring `base64` + ## 2.7.0 * Support for SUSE-based container images. diff -Nru ruby-kitchen-docker-2.7.0/debian/changelog ruby-kitchen-docker-2.10.0/debian/changelog --- ruby-kitchen-docker-2.7.0/debian/changelog 2018-12-27 06:35:09.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/debian/changelog 2021-12-08 18:02:40.000000000 +0000 @@ -1,3 +1,27 @@ +ruby-kitchen-docker (2.10.0-1) unstable; urgency=medium + + * Team upload. + * New upstream release. + + [ Debian Janitor ] + * Bump debhelper from old 11 to 12. + * Set debhelper-compat version in Build-Depends. + * Set upstream metadata fields: Bug-Database, Bug-Submit, Repository, + Repository-Browse. + * Update watch file format version to 4. + * Update standards version to 4.5.0, no changes needed. + + [ Daniel Leidert ] + * d/control (Standards-Version): Bump to 4.6.0. + (Build-Depends): Raise debhelper-compat and test-kitchen versions. + (Depends): Remove interpreters and use ${ruby:Depends}. + * d/copyright: Add Upstream-Contact field. + (Copyright): Add team. + * d/rules: Use gem installation layout and install upstream changelog. + * d/upstream/metadata: Add missing fields. + + -- Daniel Leidert Wed, 08 Dec 2021 19:02:40 +0100 + ruby-kitchen-docker (2.7.0-1) unstable; urgency=medium * Initial release (Closes: #917320) diff -Nru ruby-kitchen-docker-2.7.0/debian/compat ruby-kitchen-docker-2.10.0/debian/compat --- ruby-kitchen-docker-2.7.0/debian/compat 2018-12-25 21:02:26.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/debian/compat 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -11 diff -Nru ruby-kitchen-docker-2.7.0/debian/control ruby-kitchen-docker-2.10.0/debian/control --- ruby-kitchen-docker-2.7.0/debian/control 2018-12-27 06:34:41.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/debian/control 2021-12-08 18:02:40.000000000 +0000 @@ -1,16 +1,16 @@ Source: ruby-kitchen-docker Section: ruby Priority: optional -Maintainer: Debian Ruby Extras Maintainers +Maintainer: Debian Ruby Team Uploaders: Mathieu Parent -Build-Depends: debhelper (>= 11~), +Build-Depends: debhelper-compat (= 13), gem2deb, rake, - test-kitchen (>= 1.0.0), ruby-rspec, ruby-serverspec, - ruby-simplecov -Standards-Version: 4.3.0 + ruby-simplecov, + test-kitchen (>= 1.1) +Standards-Version: 4.6.0 Vcs-Git: https://salsa.debian.org/ruby-team/ruby-kitchen-docker.git Vcs-Browser: https://salsa.debian.org/ruby-team/ruby-kitchen-docker Homepage: https://github.com/portertech/kitchen-docker @@ -21,9 +21,8 @@ Package: ruby-kitchen-docker Architecture: all XB-Ruby-Versions: ${ruby:Versions} -Depends: ruby | ruby-interpreter, - test-kitchen (>= 1.0.0), - ${misc:Depends}, +Depends: ${misc:Depends}, + ${ruby:Depends}, ${shlibs:Depends} Description: Docker Driver for Test Kitchen A Docker Driver for Test Kitchen. diff -Nru ruby-kitchen-docker-2.7.0/debian/copyright ruby-kitchen-docker-2.10.0/debian/copyright --- ruby-kitchen-docker-2.7.0/debian/copyright 2018-12-26 06:30:12.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/debian/copyright 2021-12-08 18:02:40.000000000 +0000 @@ -1,5 +1,6 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: kitchen-docker +Upstream-Contact: https://github.com/test-kitchen/kitchen-docker/issues Source: https://github.com/test-kitchen/kitchen-docker Files: * @@ -9,8 +10,9 @@ Files: debian/* Copyright: 2018 Mathieu Parent + 2021 Debian Ruby Extras Maintainers License: Apache-2.0 -Comment: the Debian packaging is licensed under the same terms as the original package. +Comment: The Debian packaging is licensed under the same terms as the source. License: Apache-2.0 Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,6 +26,5 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - . - On Debian systems, the complete text of the Apache version 2.0 license - can be found in "/usr/share/common-licenses/Apache-2.0". +Comment: On Debian systems, the complete text of the Apache version 2.0 + license can be found in "/usr/share/common-licenses/Apache-2.0". diff -Nru ruby-kitchen-docker-2.7.0/debian/rules ruby-kitchen-docker-2.10.0/debian/rules --- ruby-kitchen-docker-2.7.0/debian/rules 2018-12-25 21:02:26.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/debian/rules 2021-12-08 18:02:40.000000000 +0000 @@ -1,6 +1,10 @@ #!/usr/bin/make -f export GEM2DEB_TEST_RUNNER = --check-dependencies +export DH_RUBY = --gem-install %: dh $@ --buildsystem=ruby --with ruby + +override_dh_installchangelogs: + dh_installchangelogs CHANGELOG.md diff -Nru ruby-kitchen-docker-2.7.0/debian/salsa-ci.yml ruby-kitchen-docker-2.10.0/debian/salsa-ci.yml --- ruby-kitchen-docker-2.7.0/debian/salsa-ci.yml 1970-01-01 00:00:00.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/debian/salsa-ci.yml 2021-12-08 18:02:40.000000000 +0000 @@ -0,0 +1,4 @@ +--- +include: + - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml + - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml diff -Nru ruby-kitchen-docker-2.7.0/debian/upstream/metadata ruby-kitchen-docker-2.10.0/debian/upstream/metadata --- ruby-kitchen-docker-2.7.0/debian/upstream/metadata 1970-01-01 00:00:00.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/debian/upstream/metadata 2021-12-08 18:02:40.000000000 +0000 @@ -0,0 +1,7 @@ +--- +Archive: GitHub +Bug-Database: https://github.com/test-kitchen/kitchen-docker/issues +Bug-Submit: https://github.com/test-kitchen/kitchen-docker/issues/new +Changelog: https://github.com/test-kitchen/kitchen-docker/blob/master/CHANGELOG.md +Repository: https://github.com/test-kitchen/kitchen-docker.git +Repository-Browse: https://github.com/test-kitchen/kitchen-docker diff -Nru ruby-kitchen-docker-2.7.0/debian/watch ruby-kitchen-docker-2.10.0/debian/watch --- ruby-kitchen-docker-2.7.0/debian/watch 2018-12-25 21:02:26.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/debian/watch 2021-12-08 18:02:40.000000000 +0000 @@ -1,2 +1,2 @@ -version=3 +version=4 https://gemwatch.debian.net/kitchen-docker .*/kitchen-docker-(.*).tar.gz diff -Nru ruby-kitchen-docker-2.7.0/docker.ps1 ruby-kitchen-docker-2.10.0/docker.ps1 --- ruby-kitchen-docker-2.7.0/docker.ps1 1970-01-01 00:00:00.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/docker.ps1 2020-04-02 10:46:32.000000000 +0000 @@ -0,0 +1,9 @@ +# This script is used to configure the Docker service for Windows builds in Travis CI +Write-Host "Configuring Docker service to listen on TCP port 2375..." +$dockerSvcArgs = (Get-WmiObject Win32_Service | ?{$_.Name -eq 'docker'} | Select PathName).PathName +$dockerSvcArgs = "$dockerSvcArgs -H tcp://0.0.0.0:2375 -H npipe:////./pipe/docker_engine" +Write-Host "Docker Service Args: $dockerSvcArgs" + +Get-WmiObject Win32_Service -Filter "Name='docker'" | Invoke-WmiMethod -Name Change -ArgumentList @($null,$null,$null,$null,$null, $dockerSvcArgs) | Out-Null + +Restart-Service docker -Force -Verbose diff -Nru ruby-kitchen-docker-2.7.0/.gitignore ruby-kitchen-docker-2.10.0/.gitignore --- ruby-kitchen-docker-2.7.0/.gitignore 2018-12-25 20:53:24.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/.gitignore 2020-04-02 10:46:32.000000000 +0000 @@ -17,3 +17,5 @@ tmp .kitchen/ .kitchen.local.yml +Dockerfile* +.DS_Store diff -Nru ruby-kitchen-docker-2.7.0/kitchen-docker.gemspec ruby-kitchen-docker-2.10.0/kitchen-docker.gemspec --- ruby-kitchen-docker-2.7.0/kitchen-docker.gemspec 2018-12-25 20:53:24.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/kitchen-docker.gemspec 2020-04-02 10:46:32.000000000 +0000 @@ -1,20 +1,18 @@ -# coding: utf-8 lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) -require 'kitchen/driver/docker_version' +require 'kitchen/docker/docker_version' Gem::Specification.new do |spec| spec.name = 'kitchen-docker' - spec.version = Kitchen::Driver::DOCKER_VERSION + spec.version = Kitchen::Docker::DOCKER_VERSION spec.authors = ['Sean Porter'] spec.email = ['portertech@gmail.com'] spec.description = %q{A Docker Driver for Test Kitchen} spec.summary = spec.description - spec.homepage = 'https://github.com/portertech/kitchen-docker' + spec.homepage = 'https://github.com/test-kitchen/kitchen-docker' spec.license = 'Apache 2.0' spec.files = `git ls-files`.split($/) - spec.executables = [] spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ['lib'] @@ -36,5 +34,6 @@ spec.add_development_dependency 'codecov', '~> 0.0', '>= 0.0.2' # Integration testing gems. - spec.add_development_dependency 'kitchen-inspec', '~> 0.14' + spec.add_development_dependency 'kitchen-inspec', '~> 1.1' + spec.add_development_dependency 'train', '>= 2.1', '< 4.0' # validate 4.x when it's released end diff -Nru ruby-kitchen-docker-2.7.0/.kitchen.windows.yml ruby-kitchen-docker-2.10.0/.kitchen.windows.yml --- ruby-kitchen-docker-2.7.0/.kitchen.windows.yml 1970-01-01 00:00:00.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/.kitchen.windows.yml 2020-04-02 10:46:32.000000000 +0000 @@ -0,0 +1,33 @@ +<% # Make sure the local copy of the driver is loaded %> +<% lib = File.expand_path('../lib', __FILE__) %> +<% $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) %> +--- +driver: + name: docker + provision_command: + - powershell -ExecutionPolicy Bypass -NoLogo -Command . { iwr -useb https://omnitruck.chef.io/install.ps1 } ^| iex; install + - powershell -Command $path=$env:Path + ';c:\opscode\chef\embedded\bin'; Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\' -Name Path -Value $path + +transport: + name: docker + socket: tcp://localhost:2375 + +provisioner: + name: dummy + +platforms: +- name: windows + driver_config: + image: mcr.microsoft.com/windows/servercore:1803 + platform: windows + +suites: +- name: default +- name: context + driver: + build_context: false +- name: inspec + driver: + provision_command: echo 1 + verifier: + name: inspec diff -Nru ruby-kitchen-docker-2.7.0/.kitchen.yml ruby-kitchen-docker-2.10.0/.kitchen.yml --- ruby-kitchen-docker-2.7.0/.kitchen.yml 2018-12-25 20:53:24.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/.kitchen.yml 2020-04-02 10:46:32.000000000 +0000 @@ -6,18 +6,31 @@ name: docker provision_command: curl -L https://www.chef.io/chef/install.sh | bash +transport: + name: docker + provisioner: name: dummy platforms: -- name: ubuntu-14.04 +- name: amazonlinux-2 - name: ubuntu-16.04 +- name: ubuntu-18.04 +- name: fedora-latest + driver: + provision_command: + - yum install libxcrypt-compat.x86_64 -y + - curl -L https://www.chef.io/chef/install.sh | bash - name: centos-6 - name: centos-7 +- name: oraclelinux-6 +- name: oraclelinux-7 - name: debian-8 -- name: opensuse-42.2 +- name: debian-9 +- name: opensuse-42.3 driver: - image: opensuse:42.2 + image: opensuse/leap:42.3 +- name: opensuse/leap-42 # - name: arch # driver: # image: base/archlinux @@ -41,7 +54,7 @@ driver: build_context: false - name: capabilities - excludes: [arch,unknown,centos-7,centos-6,dockerfile,opensuse-42.2] + includes: [debian-8,debian-9,ubuntu-16.04,ubuntu-18.04] driver: provision_command: - curl -L https://www.chef.io/chef/install.sh | bash diff -Nru ruby-kitchen-docker-2.7.0/lib/docker/version.rb ruby-kitchen-docker-2.10.0/lib/docker/version.rb --- ruby-kitchen-docker-2.7.0/lib/docker/version.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/lib/docker/version.rb 2020-04-02 10:46:32.000000000 +0000 @@ -0,0 +1,25 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +begin + require 'docker' + + # Override API_VERSION constant in docker-api gem to use version 1.24 of the Docker API + # This override is for the docker-api gem to communicate to the Docker engine on Windows + module Docker + VERSION = '0.0.0' + API_VERSION = '1.24' + end +rescue LoadError => e + logger.debug("[Docker] docker-api gem not found for InSpec verifier. #{e}") +end diff -Nru ruby-kitchen-docker-2.7.0/lib/kitchen/docker/container/linux.rb ruby-kitchen-docker-2.10.0/lib/kitchen/docker/container/linux.rb --- ruby-kitchen-docker-2.7.0/lib/kitchen/docker/container/linux.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/lib/kitchen/docker/container/linux.rb 2020-04-02 10:46:32.000000000 +0000 @@ -0,0 +1,211 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'base64' +require 'openssl' +require 'securerandom' +require 'shellwords' + +require_relative '../container' + +module Kitchen + module Docker + class Container + class Linux < Kitchen::Docker::Container + MUTEX_FOR_SSH_KEYS = Mutex.new + + def initialize(config) + super + end + + def create(state) + super + + debug('Creating Linux container') + generate_keys + + state[:ssh_key] = @config[:private_key] + state[:image_id] = build_image(state, dockerfile) unless state[:image_id] + state[:container_id] = run_container(state, 22) unless state[:container_id] + state[:hostname] = 'localhost' + state[:port] = container_ssh_port(state) + end + + def execute(command) + # Create temp script file and upload files to container + debug("Executing command on Linux container (Platform: #{@config[:platform]})") + filename = "docker-#{::SecureRandom.uuid}.sh" + temp_file = "./.kitchen/temp/#{filename}" + create_temp_file(temp_file, command) + + remote_path = @config[:temp_dir] + debug("Creating directory #{remote_path} on container") + create_dir_on_container(@config, remote_path) + + debug("Uploading temp file #{temp_file} to #{remote_path} on container") + upload(temp_file, remote_path) + + debug('Deleting temp file from local filesystem') + ::File.delete(temp_file) + + # Replace any environment variables used in the path and execute script file + debug("Executing temp script #{remote_path}/#{filename} on container") + remote_path = replace_env_variables(@config, remote_path) + + container_exec(@config, "/bin/bash #{remote_path}/#{filename}") + rescue => e + raise "Failed to execute command on Linux container. #{e}" + end + + protected + + def generate_keys + MUTEX_FOR_SSH_KEYS.synchronize do + if !File.exist?(@config[:public_key]) || !File.exist?(@config[:private_key]) + private_key = OpenSSL::PKey::RSA.new(2048) + blobbed_key = Base64.encode64(private_key.to_blob).gsub("\n", '') + public_key = "ssh-rsa #{blobbed_key} kitchen_docker_key" + File.open(@config[:private_key], 'w') do |file| + file.write(private_key) + file.chmod(0600) + end + File.open(@config[:public_key], 'w') do |file| + file.write(public_key) + file.chmod(0600) + end + end + end + end + + def parse_container_ssh_port(output) + _host, port = output.split(':') + port.to_i + rescue => e + raise ActionFailed, "Could not parse Docker port output for container SSH port. #{e}" + end + + def container_ssh_port(state) + return 22 if @config[:use_internal_docker_network] + + output = docker_command("port #{state[:container_id]} 22/tcp") + parse_container_ssh_port(output) + rescue => e + raise ActionFailed, "Docker reports container has no ssh port mapped. #{e}" + end + + def dockerfile + return dockerfile_template if @config[:dockerfile] + + from = "FROM #{@config[:image]}" + + platform = case @config[:platform] + when 'debian', 'ubuntu' + disable_upstart = <<-CODE + RUN [ ! -f "/sbin/initctl" ] || dpkg-divert --local --rename --add /sbin/initctl && ln -sf /bin/true /sbin/initctl + CODE + packages = <<-CODE + ENV DEBIAN_FRONTEND noninteractive + ENV container docker + RUN apt-get update + RUN apt-get install -y sudo openssh-server curl lsb-release + CODE + @config[:disable_upstart] ? disable_upstart + packages : packages + when 'rhel', 'centos', 'oraclelinux', 'amazonlinux' + <<-CODE + ENV container docker + RUN yum clean all + RUN yum install -y sudo openssh-server openssh-clients which curl + RUN [ -f "/etc/ssh/ssh_host_rsa_key" ] || ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key -N '' + RUN [ -f "/etc/ssh/ssh_host_dsa_key" ] || ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key -N '' + CODE + when 'fedora' + <<-CODE + ENV container docker + RUN dnf clean all + RUN dnf install -y sudo openssh-server openssh-clients which curl + RUN [ -f "/etc/ssh/ssh_host_rsa_key" ] || ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key -N '' + RUN [ -f "/etc/ssh/ssh_host_dsa_key" ] || ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key -N '' + CODE + when 'opensuse/tumbleweed', 'opensuse/leap', 'opensuse', 'sles' + <<-CODE + ENV container docker + RUN zypper install -y sudo openssh which curl + RUN [ -f "/etc/ssh/ssh_host_rsa_key" ] || ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key -N '' + RUN [ -f "/etc/ssh/ssh_host_dsa_key" ] || ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key -N '' + CODE + when 'arch' + # See https://bugs.archlinux.org/task/47052 for why we + # blank out limits.conf. + <<-CODE + RUN pacman --noconfirm -Sy archlinux-keyring + RUN pacman-db-upgrade + RUN pacman --noconfirm -Syu openssl openssh sudo curl + RUN [ -f "/etc/ssh/ssh_host_rsa_key" ] || ssh-keygen -A -t rsa -f /etc/ssh/ssh_host_rsa_key + RUN [ -f "/etc/ssh/ssh_host_dsa_key" ] || ssh-keygen -A -t dsa -f /etc/ssh/ssh_host_dsa_key + RUN echo >/etc/security/limits.conf + CODE + when 'gentoo' + <<-CODE + RUN emerge --sync + RUN emerge net-misc/openssh app-admin/sudo + RUN [ -f "/etc/ssh/ssh_host_rsa_key" ] || ssh-keygen -A -t rsa -f /etc/ssh/ssh_host_rsa_key + RUN [ -f "/etc/ssh/ssh_host_dsa_key" ] || ssh-keygen -A -t dsa -f /etc/ssh/ssh_host_dsa_key + CODE + when 'gentoo-paludis' + <<-CODE + RUN cave sync + RUN cave resolve -zx net-misc/openssh app-admin/sudo + RUN [ -f "/etc/ssh/ssh_host_rsa_key" ] || ssh-keygen -A -t rsa -f /etc/ssh/ssh_host_rsa_key + RUN [ -f "/etc/ssh/ssh_host_dsa_key" ] || ssh-keygen -A -t dsa -f /etc/ssh/ssh_host_dsa_key + CODE + else + raise ActionFailed, "Unknown platform '#{@config[:platform]}'" + end + + username = @config[:username] + public_key = IO.read(@config[:public_key]).strip + homedir = username == 'root' ? '/root' : "/home/#{username}" + + base = <<-CODE + RUN if ! getent passwd #{username}; then \ + useradd -d #{homedir} -m -s /bin/bash -p '*' #{username}; \ + fi + RUN echo "#{username} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers + RUN echo "Defaults !requiretty" >> /etc/sudoers + RUN mkdir -p #{homedir}/.ssh + RUN chown -R #{username} #{homedir}/.ssh + RUN chmod 0700 #{homedir}/.ssh + RUN touch #{homedir}/.ssh/authorized_keys + RUN chown #{username} #{homedir}/.ssh/authorized_keys + RUN chmod 0600 #{homedir}/.ssh/authorized_keys + RUN mkdir -p /run/sshd + CODE + + custom = '' + Array(@config[:provision_command]).each do |cmd| + custom << "RUN #{cmd}\n" + end + + ssh_key = "RUN echo #{Shellwords.escape(public_key)} >> #{homedir}/.ssh/authorized_keys" + + # Empty string to ensure the file ends with a newline. + output = [from, dockerfile_proxy_config, platform, base, custom, ssh_key, ''].join("\n") + debug('--- Start Dockerfile ---') + debug(output.strip) + debug('--- End Dockerfile ---') + output + end + end + end + end +end diff -Nru ruby-kitchen-docker-2.7.0/lib/kitchen/docker/container/windows.rb ruby-kitchen-docker-2.10.0/lib/kitchen/docker/container/windows.rb --- ruby-kitchen-docker-2.7.0/lib/kitchen/docker/container/windows.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/lib/kitchen/docker/container/windows.rb 2020-04-02 10:46:32.000000000 +0000 @@ -0,0 +1,84 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'securerandom' + +require_relative '../container' + +module Kitchen + module Docker + class Container + class Windows < Kitchen::Docker::Container + def initialize(config) + super + end + + def create(state) + super + + debug('Creating Windows container') + state[:username] = @config[:username] + state[:image_id] = build_image(state, dockerfile) unless state[:image_id] + state[:container_id] = run_container(state) unless state[:container_id] + end + + def execute(command) + # Create temp script file and upload files to container + debug('Executing command on Windows container') + filename = "docker-#{::SecureRandom.uuid}.ps1" + temp_file = ".\\.kitchen\\temp\\#{filename}" + create_temp_file(temp_file, command) + + remote_path = @config[:temp_dir].tr('/', '\\') + debug("Creating directory #{remote_path} on container") + create_dir_on_container(@config, remote_path) + + debug("Uploading temp file #{temp_file} to #{remote_path} on container") + upload(temp_file, remote_path) + + debug('Deleting temp file from local filesystem') + ::File.delete(temp_file) + + # Replace any environment variables used in the path and execute script file + debug("Executing temp script #{remote_path}\\#{filename} on container") + remote_path = replace_env_variables(@config, remote_path) + cmd = build_powershell_command("-File #{remote_path}\\#{filename}") + + container_exec(@config, cmd) + rescue => e + raise "Failed to execute command on Windows container. #{e}" + end + + protected + + def dockerfile + raise ActionFailed, "Unknown platform '#{@config[:platform]}'" unless @config[:platform] == 'windows' + return dockerfile_template if @config[:dockerfile] + + from = "FROM #{@config[:image]}" + + custom = '' + Array(@config[:provision_command]).each do |cmd| + custom << "RUN #{cmd}\n" + end + + output = [from, dockerfile_proxy_config, custom, ''].join("\n") + debug('--- Start Dockerfile ---') + debug(output.strip) + debug('--- End Dockerfile ---') + output + end + end + end + end +end diff -Nru ruby-kitchen-docker-2.7.0/lib/kitchen/docker/container.rb ruby-kitchen-docker-2.10.0/lib/kitchen/docker/container.rb --- ruby-kitchen-docker-2.7.0/lib/kitchen/docker/container.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/lib/kitchen/docker/container.rb 2020-04-02 10:46:32.000000000 +0000 @@ -0,0 +1,70 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require_relative 'helpers/cli_helper' +require_relative 'helpers/container_helper' +require_relative 'helpers/file_helper' +require_relative 'helpers/image_helper' + +module Kitchen + module Docker + class Container + include Kitchen::Docker::Helpers::CliHelper + include Kitchen::Docker::Helpers::ContainerHelper + include Kitchen::Docker::Helpers::FileHelper + include Kitchen::Docker::Helpers::ImageHelper + + def initialize(config) + @config = config + end + + def create(state) + if container_exists?(state) + info("Container ID #{state[:container_id]} already exists.") + elsif !container_exists?(state) && state[:container_id] + raise ActionFailed, "Container ID #{state[:container_id]} was found in the kitchen state data, "\ + 'but the container does not exist.' + end + + state[:username] = @config[:username] + state[:hostname] = 'localhost' + + if remote_socket? + state[:hostname] = socket_uri.host + elsif config[:use_internal_docker_network] + state[:hostname] = container_ip_address(state) + end + end + + def upload(locals, remote) + files = locals + files = Array(locals) unless locals.is_a?(Array) + + files.each do |file| + copy_file_to_container(@config, file, remote) + end + + files + end + + def destroy(state) + info("[Docker] Destroying Docker container #{state[:container_id]}") if state[:container_id] + remove_container(state) if container_exists?(state) + + if @config[:remove_images] && state[:image_id] + remove_image(state) if image_exists?(state) + end + end + end + end +end diff -Nru ruby-kitchen-docker-2.7.0/lib/kitchen/docker/docker_version.rb ruby-kitchen-docker-2.10.0/lib/kitchen/docker/docker_version.rb --- ruby-kitchen-docker-2.7.0/lib/kitchen/docker/docker_version.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/lib/kitchen/docker/docker_version.rb 2020-04-02 10:46:32.000000000 +0000 @@ -0,0 +1,21 @@ +# +# Copyright (C) 2014, Sean Porter +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module Kitchen + module Docker + # Version string for Docker Kitchen driver + DOCKER_VERSION = "2.10.0" + end +end diff -Nru ruby-kitchen-docker-2.7.0/lib/kitchen/docker/erb_context.rb ruby-kitchen-docker-2.10.0/lib/kitchen/docker/erb_context.rb --- ruby-kitchen-docker-2.7.0/lib/kitchen/docker/erb_context.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/lib/kitchen/docker/erb_context.rb 2020-04-02 10:46:32.000000000 +0000 @@ -0,0 +1,32 @@ +# +# Copyright (C) 2014, Sean Porter +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'erb' + +module Kitchen + module Docker + class ERBContext + def initialize(config={}) + config.each do |key, value| + instance_variable_set('@' + key.to_s, value) + end + end + + def get_binding + binding + end + end + end +end diff -Nru ruby-kitchen-docker-2.7.0/lib/kitchen/docker/helpers/cli_helper.rb ruby-kitchen-docker-2.10.0/lib/kitchen/docker/helpers/cli_helper.rb --- ruby-kitchen-docker-2.7.0/lib/kitchen/docker/helpers/cli_helper.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/lib/kitchen/docker/helpers/cli_helper.rb 2020-04-02 10:46:32.000000000 +0000 @@ -0,0 +1,147 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'kitchen' +require 'kitchen/configurable' +require 'kitchen/logging' +require 'kitchen/shell_out' + +module Kitchen + module Docker + module Helpers + module CliHelper + include Configurable + include Logging + include ShellOut + + def docker_command(cmd, options={}) + docker = config[:binary].dup + docker << " -H #{config[:socket]}" if config[:socket] + docker << ' --tls' if config[:tls] + docker << ' --tlsverify' if config[:tls_verify] + docker << " --tlscacert=#{config[:tls_cacert]}" if config[:tls_cacert] + docker << " --tlscert=#{config[:tls_cert]}" if config[:tls_cert] + docker << " --tlskey=#{config[:tls_key]}" if config[:tls_key] + logger.debug("docker_command: #{docker} #{cmd} shell_opts: #{docker_shell_opts(options)}") + run_command("#{docker} #{cmd}", docker_shell_opts(options)) + end + + def build_run_command(image_id, transport_port = nil) + cmd = 'run -d' + cmd << ' -i' if config[:interactive] + cmd << ' -t' if config[:tty] + cmd << build_env_variable_args(config[:env_variables]) if config[:env_variables] + cmd << " -p #{transport_port}" unless transport_port.nil? + Array(config[:forward]).each { |port| cmd << " -p #{port}" } + Array(config[:dns]).each { |dns| cmd << " --dns #{dns}" } + Array(config[:add_host]).each { |host, ip| cmd << " --add-host=#{host}:#{ip}" } + Array(config[:volume]).each { |volume| cmd << " -v #{volume}" } + Array(config[:volumes_from]).each { |container| cmd << " --volumes-from #{container}" } + Array(config[:links]).each { |link| cmd << " --link #{link}" } + Array(config[:devices]).each { |device| cmd << " --device #{device}" } + cmd << " --name #{config[:instance_name]}" if config[:instance_name] + cmd << ' -P' if config[:publish_all] + cmd << " -h #{config[:hostname]}" if config[:hostname] + cmd << " -m #{config[:memory]}" if config[:memory] + cmd << " -c #{config[:cpu]}" if config[:cpu] + cmd << " -e http_proxy=#{config[:http_proxy]}" if config[:http_proxy] + cmd << " -e https_proxy=#{config[:https_proxy]}" if config[:https_proxy] + cmd << ' --privileged' if config[:privileged] + Array(config[:cap_add]).each { |cap| cmd << " --cap-add=#{cap}"} if config[:cap_add] + Array(config[:cap_drop]).each { |cap| cmd << " --cap-drop=#{cap}"} if config[:cap_drop] + Array(config[:security_opt]).each { |opt| cmd << " --security-opt=#{opt}"} if config[:security_opt] + extra_run_options = config_to_options(config[:run_options]) + cmd << " #{extra_run_options}" unless extra_run_options.empty? + cmd << " #{image_id} #{config[:run_command]}" + logger.debug("build_run_command: #{cmd}") + cmd + end + + def build_exec_command(state, command) + cmd = 'exec' + cmd << ' -d' if config[:detach] + cmd << build_env_variable_args(config[:env_variables]) if config[:env_variables] + cmd << ' --privileged' if config[:privileged] + cmd << ' -t' if config[:tty] + cmd << ' -i' if config[:interactive] + cmd << " -u #{config[:username]}" if config[:username] + cmd << " -w #{config[:working_dir]}" if config[:working_dir] + cmd << " #{state[:container_id]}" + cmd << " #{command}" + logger.debug("build_exec_command: #{cmd}") + cmd + end + + def build_copy_command(local_file, remote_file, opts = {}) + cmd = 'cp' + cmd << ' -a' if opts[:archive] + cmd << " #{local_file} #{remote_file}" + cmd + end + + def build_powershell_command(args) + cmd = 'powershell -ExecutionPolicy Bypass -NoLogo ' + cmd << args + logger.debug("build_powershell_command: #{cmd}") + cmd + end + + def build_env_variable_args(vars) + raise ActionFailed, 'Environment variables are not of a Hash type' unless vars.is_a?(Hash) + + args = '' + vars.each do |k, v| + args << " -e #{k.to_s.strip}=\"#{v.to_s.strip}\"" + end + + args + end + + def dev_null + case RbConfig::CONFIG['host_os'] + when /mswin|msys|mingw|cygwin|bccwin|wince|emc/ + 'NUL' + else + '/dev/null' + end + end + + def docker_shell_opts(options = {}) + options[:live_stream] = nil if options[:suppress_output] + options.delete(:suppress_output) + + options + end + + # Convert the config input for `:build_options` or `:run_options` in to a + # command line string for use with Docker. + # + # @since 2.5.0 + # @param config [nil, String, Array, Hash] Config data to convert. + # @return [String] + def config_to_options(config) + case config + when nil + '' + when String + config + when Array + config.map { |c| config_to_options(c) }.join(' ') + when Hash + config.map { |k, v| Array(v).map { |c| "--#{k}=#{Shellwords.escape(c)}" }.join(' ') }.join(' ') + end + end + end + end + end +end diff -Nru ruby-kitchen-docker-2.7.0/lib/kitchen/docker/helpers/container_helper.rb ruby-kitchen-docker-2.10.0/lib/kitchen/docker/helpers/container_helper.rb --- ruby-kitchen-docker-2.7.0/lib/kitchen/docker/helpers/container_helper.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/lib/kitchen/docker/helpers/container_helper.rb 2020-04-02 10:46:32.000000000 +0000 @@ -0,0 +1,172 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'erb' +require 'json' +require 'shellwords' +require 'tempfile' +require 'uri' + +require 'kitchen' +require 'kitchen/configurable' +require_relative '../erb_context' +require_relative 'cli_helper' + +module Kitchen + module Docker + module Helpers + module ContainerHelper + include Configurable + include Kitchen::Docker::Helpers::CliHelper + + def parse_container_id(output) + container_id = output.chomp + + unless [12, 64].include?(container_id.size) + raise ActionFailed, 'Could not parse Docker run output for container ID' + end + + container_id + end + + def dockerfile_template + template = IO.read(File.expand_path(config[:dockerfile])) + context = Kitchen::Docker::ERBContext.new(config.to_hash) + ERB.new(template).result(context.get_binding) + end + + def remote_socket? + config[:socket] ? socket_uri.scheme == 'tcp' : false + end + + def socket_uri + URI.parse(config[:socket]) + end + + def dockerfile_path(file) + config[:build_context] ? Pathname.new(file.path).relative_path_from(Pathname.pwd).to_s : file.path + end + + def container_exists?(state) + state[:container_id] && !!docker_command("top #{state[:container_id]}") rescue false + end + + def container_exec(state, command) + cmd = build_exec_command(state, command) + docker_command(cmd) + rescue => e + raise "Failed to execute command on Docker container. #{e}" + end + + def create_dir_on_container(state, path) + path = replace_env_variables(state, path) + cmd = "mkdir -p #{path}" + + if state[:platform].include?('windows') + psh = "-Command if(-not (Test-Path \'#{path}\')) { New-Item -Path \'#{path}\' -Force }" + cmd = build_powershell_command(psh) + end + + cmd = build_exec_command(state, cmd) + docker_command(cmd) + rescue => e + raise "Failed to create directory #{path} on container. #{e}" + end + + def copy_file_to_container(state, local_file, remote_file) + debug("Copying local file #{local_file} to #{remote_file} on container") + + remote_file = replace_env_variables(state, remote_file) + + remote_file = "#{state[:container_id]}:#{remote_file}" + cmd = build_copy_command(local_file, remote_file) + docker_command(cmd) + rescue => e + raise "Failed to copy file #{local_file} to container. #{e}" + end + + def container_env_variables(state) + # Retrieves all environment variables from inside container + vars = {} + + if state[:platform].include?('windows') + cmd = build_powershell_command('-Command [System.Environment]::GetEnvironmentVariables() ^| ConvertTo-Json') + cmd = build_exec_command(state, cmd) + stdout = docker_command(cmd, suppress_output: !logger.debug?).strip + vars = ::JSON.parse(stdout) + else + cmd = build_exec_command(state, 'printenv') + stdout = docker_command(cmd, suppress_output: !logger.debug?).strip + stdout.split("\n").each { |line| vars[line.split('=')[0]] = line.split('=')[1] } + end + + vars + end + + def replace_env_variables(state, str) + if str.include?('$env:') + key = str[/\$env:(.*?)(\\|$)/, 1] + value = container_env_variables(state)[key].to_s.strip + str = str.gsub("$env:#{key}", value) + elsif str.include?('$') + key = str[/\$(.*?)(\/|$)/, 1] + value = container_env_variables(state)[key].to_s.strip + str = str.gsub("$#{key}", value) + end + + str + end + + def run_container(state, transport_port = nil) + cmd = build_run_command(state[:image_id], transport_port) + output = docker_command(cmd) + parse_container_id(output) + end + + def container_ip_address(state) + cmd = "inspect --format '{{ .NetworkSettings.IPAddress }}'" + cmd << " #{state[:container_id]}" + docker_command(cmd).strip + rescue + raise ActionFailed, 'Error getting internal IP of Docker container' + end + + def remove_container(state) + container_id = state[:container_id] + docker_command("stop -t 0 #{container_id}") + docker_command("rm #{container_id}") + end + + def dockerfile_proxy_config + env_variables = '' + if config[:http_proxy] + env_variables << "ENV http_proxy #{config[:http_proxy]}\n" + env_variables << "ENV HTTP_PROXY #{config[:http_proxy]}\n" + end + + if config[:https_proxy] + env_variables << "ENV https_proxy #{config[:https_proxy]}\n" + env_variables << "ENV HTTPS_PROXY #{config[:https_proxy]}\n" + end + + if config[:no_proxy] + env_variables << "ENV no_proxy #{config[:no_proxy]}\n" + env_variables << "ENV NO_PROXY #{config[:no_proxy]}\n" + end + + env_variables + end + end + end + end +end diff -Nru ruby-kitchen-docker-2.7.0/lib/kitchen/docker/helpers/file_helper.rb ruby-kitchen-docker-2.10.0/lib/kitchen/docker/helpers/file_helper.rb --- ruby-kitchen-docker-2.7.0/lib/kitchen/docker/helpers/file_helper.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/lib/kitchen/docker/helpers/file_helper.rb 2020-04-02 10:46:32.000000000 +0000 @@ -0,0 +1,40 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'fileutils' + +module Kitchen + module Docker + module Helpers + module FileHelper + def create_temp_file(file, contents) + debug("[Docker] Creating temp file #{file}") + debug('[Docker] --- Start Temp File Contents ---') + debug(contents) + debug('[Docker] --- End Temp File Contents ---') + + begin + path = ::File.dirname(file) + ::FileUtils.mkdir_p(path) unless ::Dir.exist?(path) + file = ::File.open(file, 'w') + file.write(contents) + rescue IOError => e + raise "Failed to write temp file. Error Details: #{e}" + ensure + file.close unless file.nil? + end + end + end + end + end +end diff -Nru ruby-kitchen-docker-2.7.0/lib/kitchen/docker/helpers/image_helper.rb ruby-kitchen-docker-2.10.0/lib/kitchen/docker/helpers/image_helper.rb --- ruby-kitchen-docker-2.7.0/lib/kitchen/docker/helpers/image_helper.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/lib/kitchen/docker/helpers/image_helper.rb 2020-04-02 10:46:32.000000000 +0000 @@ -0,0 +1,68 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'kitchen' +require 'kitchen/configurable' +require_relative 'cli_helper' +require_relative 'container_helper' + +module Kitchen + module Docker + module Helpers + module ImageHelper + include Configurable + include Kitchen::Docker::Helpers::CliHelper + include Kitchen::Docker::Helpers::ContainerHelper + + def parse_image_id(output) + output.each_line do |line| + if line =~ /image id|build successful|successfully built/i + return line.split(/\s+/).last + end + end + raise ActionFailed, 'Could not parse Docker build output for image ID' + end + + def remove_image(state) + image_id = state[:image_id] + docker_command("rmi #{image_id}") + end + + def build_image(state, dockerfile) + cmd = 'build' + cmd << ' --no-cache' unless config[:use_cache] + extra_build_options = config_to_options(config[:build_options]) + cmd << " #{extra_build_options}" unless extra_build_options.empty? + dockerfile_contents = dockerfile + build_context = config[:build_context] ? '.' : '-' + file = Tempfile.new('Dockerfile-kitchen', Dir.pwd) + output = begin + file.write(dockerfile) + file.close + docker_command("#{cmd} -f #{Shellwords.escape(dockerfile_path(file))} #{build_context}", + input: dockerfile_contents) + ensure + file.close unless file.closed? + file.unlink + end + + parse_image_id(output) + end + + def image_exists?(state) + state[:image_id] && !!docker_command("inspect --type=image #{state[:image_id]}") rescue false + end + end + end + end +end diff -Nru ruby-kitchen-docker-2.7.0/lib/kitchen/docker/helpers/inspec_helper.rb ruby-kitchen-docker-2.10.0/lib/kitchen/docker/helpers/inspec_helper.rb --- ruby-kitchen-docker-2.7.0/lib/kitchen/docker/helpers/inspec_helper.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/lib/kitchen/docker/helpers/inspec_helper.rb 2020-04-02 10:46:32.000000000 +0000 @@ -0,0 +1,40 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This helper should be removed when the kitchen-inspec gem has been updated to include these runner options +begin + require 'kitchen/verifier/inspec' + + # Add runner options for Docker transport for kitchen-inspec gem + module Kitchen + module Docker + module Helpers + module InspecHelper + Kitchen::Verifier::Inspec.class_eval do + def runner_options_for_docker(config_data) + opts = { + 'backend' => 'docker', + 'logger' => logger, + 'host' => config_data[:container_id], + } + logger.debug "Connect to Container: #{opts['host']}" + opts + end + end + end + end + end + end +rescue LoadError => e + logger.debug("[Docker] kitchen-inspec gem not found for InSpec verifier. #{e}") +end diff -Nru ruby-kitchen-docker-2.7.0/lib/kitchen/driver/docker/erb.rb ruby-kitchen-docker-2.10.0/lib/kitchen/driver/docker/erb.rb --- ruby-kitchen-docker-2.7.0/lib/kitchen/driver/docker/erb.rb 2018-12-25 20:53:24.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/lib/kitchen/driver/docker/erb.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,35 +0,0 @@ -# -*- encoding: utf-8 -*- -# -# Copyright (C) 2014, Sean Porter -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'erb' - -module Kitchen - - module Driver - - class DockerERBContext - def initialize(config={}) - config.each do |key, value| - instance_variable_set('@' + key.to_s, value) - end - end - - def get_binding - binding - end - end - end -end diff -Nru ruby-kitchen-docker-2.7.0/lib/kitchen/driver/docker.rb ruby-kitchen-docker-2.10.0/lib/kitchen/driver/docker.rb --- ruby-kitchen-docker-2.7.0/lib/kitchen/driver/docker.rb 2018-12-25 20:53:24.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/lib/kitchen/driver/docker.rb 2020-04-02 10:46:32.000000000 +0000 @@ -1,423 +1,164 @@ -# -*- encoding: utf-8 -*- -# -# Copyright (C) 2014, Sean Porter -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'kitchen' -require 'json' -require 'securerandom' -require 'uri' -require 'net/ssh' -require 'tempfile' -require 'shellwords' - -require 'kitchen/driver/base' - -require_relative './docker/erb' - -module Kitchen - module Driver - # Docker driver for Kitchen. - # - # @author Sean Porter - class Docker < Kitchen::Driver::Base - include ShellOut - - default_config :binary, 'docker' - default_config :socket, ENV['DOCKER_HOST'] || 'unix:///var/run/docker.sock' - default_config :privileged, false - default_config :cap_add, nil - default_config :cap_drop, nil - default_config :security_opt, nil - default_config :use_cache, true - default_config :remove_images, false - default_config :run_command, '/usr/sbin/sshd -D -o UseDNS=no -o UsePAM=no -o PasswordAuthentication=yes ' + - '-o UsePrivilegeSeparation=no -o PidFile=/tmp/sshd.pid' - default_config :username, 'kitchen' - default_config :tls, false - default_config :tls_verify, false - default_config :tls_cacert, nil - default_config :tls_cert, nil - default_config :tls_key, nil - default_config :publish_all, false - default_config :wait_for_sshd, true - default_config :private_key, File.join(Dir.pwd, '.kitchen', 'docker_id_rsa') - default_config :public_key, File.join(Dir.pwd, '.kitchen', 'docker_id_rsa.pub') - default_config :build_options, nil - default_config :run_options, nil - - default_config :use_sudo, false - - default_config :image do |driver| - driver.default_image - end - - default_config :platform do |driver| - driver.default_platform - end - - default_config :disable_upstart, true - - default_config :build_context do |driver| - !driver.remote_socket? - end - - default_config :instance_name do |driver| - # Borrowed from kitchen-rackspace - [ - driver.instance.name.gsub(/\W/, ''), - (Etc.getlogin || 'nologin').gsub(/\W/, ''), - Socket.gethostname.gsub(/\W/, '')[0..20], - Array.new(8) { rand(36).to_s(36) }.join - ].join('-') - end - - MUTEX_FOR_SSH_KEYS = Mutex.new - - def verify_dependencies - run_command("#{config[:binary]} >> #{dev_null} 2>&1", quiet: true, use_sudo: config[:use_sudo]) - rescue - raise UserError, - 'You must first install the Docker CLI tool http://www.docker.io/gettingstarted/' - end - - def dev_null - case RbConfig::CONFIG["host_os"] - when /mswin|msys|mingw|cygwin|bccwin|wince|emc/ - "NUL" - else - "/dev/null" - end - end - - def default_image - platform, release = instance.platform.name.split('-') - if platform == 'centos' && release - release = 'centos' + release.split('.').first - end - release ? [platform, release].join(':') : platform - end - - def default_platform - instance.platform.name.split('-').first - end - - def create(state) - generate_keys - state[:username] = config[:username] - state[:ssh_key] = config[:private_key] - state[:image_id] = build_image(state) unless state[:image_id] - state[:container_id] = run_container(state) unless state[:container_id] - state[:hostname] = remote_socket? ? socket_uri.host : 'localhost' - state[:port] = container_ssh_port(state) - if config[:wait_for_sshd] - instance.transport.connection(state) do |conn| - conn.wait_until_ready - end - end - end - - def destroy(state) - rm_container(state) if container_exists?(state) - if config[:remove_images] && state[:image_id] - rm_image(state) - end - end - - def remote_socket? - config[:socket] ? socket_uri.scheme == 'tcp' : false - end - - protected - - def socket_uri - URI.parse(config[:socket]) - end - - def docker_command(cmd, options={}) - docker = config[:binary].dup - docker << " -H #{config[:socket]}" if config[:socket] - docker << " --tls" if config[:tls] - docker << " --tlsverify" if config[:tls_verify] - docker << " --tlscacert=#{config[:tls_cacert]}" if config[:tls_cacert] - docker << " --tlscert=#{config[:tls_cert]}" if config[:tls_cert] - docker << " --tlskey=#{config[:tls_key]}" if config[:tls_key] - run_command("#{docker} #{cmd}", options.merge({ - quiet: !logger.debug?, - use_sudo: config[:use_sudo], - log_subject: Thor::Util.snake_case(self.class.to_s), - })) - end - - def generate_keys - MUTEX_FOR_SSH_KEYS.synchronize do - if !File.exist?(config[:public_key]) || !File.exist?(config[:private_key]) - private_key = OpenSSL::PKey::RSA.new(2048) - blobbed_key = Base64.encode64(private_key.to_blob).gsub("\n", '') - public_key = "ssh-rsa #{blobbed_key} kitchen_docker_key" - File.open(config[:private_key], 'w') do |file| - file.write(private_key) - file.chmod(0600) - end - File.open(config[:public_key], 'w') do |file| - file.write(public_key) - file.chmod(0600) - end - end - end - end - - def build_dockerfile - from = "FROM #{config[:image]}" - - env_variables = '' - if config[:http_proxy] - env_variables << "ENV http_proxy #{config[:http_proxy]}\n" - env_variables << "ENV HTTP_PROXY #{config[:http_proxy]}\n" - end - - if config[:https_proxy] - env_variables << "ENV https_proxy #{config[:https_proxy]}\n" - env_variables << "ENV HTTPS_PROXY #{config[:https_proxy]}\n" - end - - if config[:no_proxy] - env_variables << "ENV no_proxy #{config[:no_proxy]}\n" - env_variables << "ENV NO_PROXY #{config[:no_proxy]}\n" - end - - platform = case config[:platform] - when 'debian', 'ubuntu' - disable_upstart = <<-eos - RUN [ ! -f "/sbin/initctl" ] || dpkg-divert --local --rename --add /sbin/initctl && ln -sf /bin/true /sbin/initctl - eos - packages = <<-eos - ENV DEBIAN_FRONTEND noninteractive - ENV container docker - RUN apt-get update - RUN apt-get install -y sudo openssh-server curl lsb-release - eos - config[:disable_upstart] ? disable_upstart + packages : packages - when 'rhel', 'centos', 'fedora' - <<-eos - ENV container docker - RUN yum clean all - RUN yum install -y sudo openssh-server openssh-clients which curl - RUN [ -f "/etc/ssh/ssh_host_rsa_key" ] || ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key -N '' - RUN [ -f "/etc/ssh/ssh_host_dsa_key" ] || ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key -N '' - eos - when 'opensuse', 'sles' - <<-eos - ENV container docker - RUN zypper install -y sudo openssh which curl - RUN [ -f "/etc/ssh/ssh_host_rsa_key" ] || ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key -N '' - RUN [ -f "/etc/ssh/ssh_host_dsa_key" ] || ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key -N '' - eos - when 'arch' - # See https://bugs.archlinux.org/task/47052 for why we - # blank out limits.conf. - <<-eos - RUN pacman --noconfirm -Sy archlinux-keyring - RUN pacman-db-upgrade - RUN pacman --noconfirm -Sy openssl openssh sudo curl - RUN [ -f "/etc/ssh/ssh_host_rsa_key" ] || ssh-keygen -A -t rsa -f /etc/ssh/ssh_host_rsa_key - RUN [ -f "/etc/ssh/ssh_host_dsa_key" ] || ssh-keygen -A -t dsa -f /etc/ssh/ssh_host_dsa_key - RUN echo >/etc/security/limits.conf - eos - when 'gentoo' - <<-eos - RUN emerge --sync - RUN emerge net-misc/openssh app-admin/sudo - RUN [ -f "/etc/ssh/ssh_host_rsa_key" ] || ssh-keygen -A -t rsa -f /etc/ssh/ssh_host_rsa_key - RUN [ -f "/etc/ssh/ssh_host_dsa_key" ] || ssh-keygen -A -t dsa -f /etc/ssh/ssh_host_dsa_key - eos - when 'gentoo-paludis' - <<-eos - RUN cave sync - RUN cave resolve -zx net-misc/openssh app-admin/sudo - RUN [ -f "/etc/ssh/ssh_host_rsa_key" ] || ssh-keygen -A -t rsa -f /etc/ssh/ssh_host_rsa_key - RUN [ -f "/etc/ssh/ssh_host_dsa_key" ] || ssh-keygen -A -t dsa -f /etc/ssh/ssh_host_dsa_key - eos - else - raise ActionFailed, - "Unknown platform '#{config[:platform]}'" - end - - username = config[:username] - public_key = IO.read(config[:public_key]).strip - homedir = username == 'root' ? '/root' : "/home/#{username}" - - base = <<-eos - RUN if ! getent passwd #{username}; then \ - useradd -d #{homedir} -m -s /bin/bash -p '*' #{username}; \ - fi - RUN echo "#{username} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers - RUN echo "Defaults !requiretty" >> /etc/sudoers - RUN mkdir -p #{homedir}/.ssh - RUN chown -R #{username} #{homedir}/.ssh - RUN chmod 0700 #{homedir}/.ssh - RUN touch #{homedir}/.ssh/authorized_keys - RUN chown #{username} #{homedir}/.ssh/authorized_keys - RUN chmod 0600 #{homedir}/.ssh/authorized_keys - eos - custom = '' - Array(config[:provision_command]).each do |cmd| - custom << "RUN #{cmd}\n" - end - ssh_key = "RUN echo #{Shellwords.escape(public_key)} >> #{homedir}/.ssh/authorized_keys" - # Empty string to ensure the file ends with a newline. - [from, env_variables, platform, base, custom, ssh_key, ''].join("\n") - end - - def dockerfile - if config[:dockerfile] - template = IO.read(File.expand_path(config[:dockerfile])) - context = DockerERBContext.new(config.to_hash) - ERB.new(template).result(context.get_binding) - else - build_dockerfile - end - end - - def parse_image_id(output) - output.each_line do |line| - if line =~ /image id|build successful|successfully built/i - return line.split(/\s+/).last - end - end - raise ActionFailed, - 'Could not parse Docker build output for image ID' - end - - def build_image(state) - cmd = "build" - cmd << " --no-cache" unless config[:use_cache] - extra_build_options = config_to_options(config[:build_options]) - cmd << " #{extra_build_options}" unless extra_build_options.empty? - dockerfile_contents = dockerfile - build_context = config[:build_context] ? '.' : '-' - file = Tempfile.new('Dockerfile-kitchen', Dir.pwd) - output = begin - file.write(dockerfile) - file.close - docker_command("#{cmd} -f #{Shellwords.escape(dockerfile_path(file))} #{build_context}", :input => dockerfile_contents) - ensure - file.close unless file.closed? - file.unlink - end - parse_image_id(output) - end - - def parse_container_id(output) - container_id = output.chomp - unless [12, 64].include?(container_id.size) - raise ActionFailed, - 'Could not parse Docker run output for container ID' - end - container_id - end - - def build_run_command(image_id) - cmd = "run -d -p 22" - Array(config[:forward]).each {|port| cmd << " -p #{port}"} - Array(config[:dns]).each {|dns| cmd << " --dns #{dns}"} - Array(config[:add_host]).each {|host, ip| cmd << " --add-host=#{host}:#{ip}"} - Array(config[:volume]).each {|volume| cmd << " -v #{volume}"} - Array(config[:volumes_from]).each {|container| cmd << " --volumes-from #{container}"} - Array(config[:links]).each {|link| cmd << " --link #{link}"} - Array(config[:devices]).each {|device| cmd << " --device #{device}"} - cmd << " --name #{config[:instance_name]}" if config[:instance_name] - cmd << " -P" if config[:publish_all] - cmd << " -h #{config[:hostname]}" if config[:hostname] - cmd << " -m #{config[:memory]}" if config[:memory] - cmd << " -c #{config[:cpu]}" if config[:cpu] - cmd << " -e http_proxy=#{config[:http_proxy]}" if config[:http_proxy] - cmd << " -e https_proxy=#{config[:https_proxy]}" if config[:https_proxy] - cmd << " --privileged" if config[:privileged] - Array(config[:cap_add]).each {|cap| cmd << " --cap-add=#{cap}"} if config[:cap_add] - Array(config[:cap_drop]).each {|cap| cmd << " --cap-drop=#{cap}"} if config[:cap_drop] - Array(config[:security_opt]).each {|opt| cmd << " --security-opt=#{opt}"} if config[:security_opt] - extra_run_options = config_to_options(config[:run_options]) - cmd << " #{extra_run_options}" unless extra_run_options.empty? - cmd << " #{image_id} #{config[:run_command]}" - cmd - end - - def run_container(state) - cmd = build_run_command(state[:image_id]) - output = docker_command(cmd) - parse_container_id(output) - end - - def container_exists?(state) - state[:container_id] && !!docker_command("top #{state[:container_id]}") rescue false - end - - def parse_container_ssh_port(output) - begin - _host, port = output.split(':') - port.to_i - rescue - raise ActionFailed, - 'Could not parse Docker port output for container SSH port' - end - end - - def container_ssh_port(state) - begin - output = docker_command("port #{state[:container_id]} 22/tcp") - parse_container_ssh_port(output) - rescue - raise ActionFailed, - 'Docker reports container has no ssh port mapped' - end - end - - def rm_container(state) - container_id = state[:container_id] - docker_command("stop -t 0 #{container_id}") - docker_command("rm #{container_id}") - end - - def rm_image(state) - image_id = state[:image_id] - docker_command("rmi #{image_id}") - end - - # Convert the config input for `:build_options` or `:run_options` in to a - # command line string for use with Docker. - # - # @since 2.5.0 - # @param config [nil, String, Array, Hash] Config data to convert. - # @return [String] - def config_to_options(config) - case config - when nil - '' - when String - config - when Array - config.map {|c| config_to_options(c) }.join(' ') - when Hash - config.map {|k, v| Array(v).map {|c| "--#{k}=#{Shellwords.escape(c)}" }.join(' ') }.join(' ') - end - end - - def dockerfile_path(file) - config[:build_context] ? Pathname.new(file.path).relative_path_from(Pathname.pwd).to_s : file.path - end - - end - end -end +# +# Copyright (C) 2014, Sean Porter +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'kitchen' +require 'json' +require 'securerandom' +require 'net/ssh' + +require 'kitchen/driver/base' + +require_relative '../docker/container/linux' +require_relative '../docker/container/windows' +require_relative '../docker/helpers/cli_helper' +require_relative '../docker/helpers/container_helper' + +module Kitchen + module Driver + # Docker driver for Kitchen. + # + # @author Sean Porter + class Docker < Kitchen::Driver::Base + include Kitchen::Docker::Helpers::CliHelper + include Kitchen::Docker::Helpers::ContainerHelper + include ShellOut + + default_config :binary, 'docker' + default_config :build_options, nil + default_config :cap_add, nil + default_config :cap_drop, nil + default_config :disable_upstart, true + default_config :env_variables, nil + default_config :interactive, false + default_config :private_key, File.join(Dir.pwd, '.kitchen', 'docker_id_rsa') + default_config :privileged, false + default_config :public_key, File.join(Dir.pwd, '.kitchen', 'docker_id_rsa.pub') + default_config :publish_all, false + default_config :remove_images, false + default_config :run_options, nil + default_config :security_opt, nil + default_config :tls, false + default_config :tls_cacert, nil + default_config :tls_cert, nil + default_config :tls_key, nil + default_config :tls_verify, false + default_config :tty, false + default_config :use_cache, true + default_config :use_internal_docker_network, false + default_config :use_sudo, false + default_config :wait_for_transport, true + + default_config :build_context do |driver| + !driver.remote_socket? + end + + default_config :image do |driver| + driver.default_image + end + + default_config :instance_name do |driver| + # Borrowed from kitchen-rackspace + [ + driver.instance.name.gsub(/\W/, ''), + (Etc.getlogin || 'nologin').gsub(/\W/, ''), + Socket.gethostname.gsub(/\W/, '')[0..20], + Array.new(8) { rand(36).to_s(36) }.join + ].join('-') + end + + default_config :platform do |driver| + driver.default_platform + end + + default_config :run_command do |driver| + if driver.windows_os? + # Launch arbitrary process to keep the Windows container alive + # If running in interactive mode, launch powershell.exe instead + if driver[:interactive] + 'powershell.exe' + else + 'ping -t localhost' + end + else + '/usr/sbin/sshd -D -o UseDNS=no -o UsePAM=no -o PasswordAuthentication=yes '\ + '-o UsePrivilegeSeparation=no -o PidFile=/tmp/sshd.pid' + end + end + + default_config :socket do |driver| + socket = 'unix:///var/run/docker.sock' + socket = 'npipe:////./pipe/docker_engine' if driver.windows_os? + ENV['DOCKER_HOST'] || socket + end + + default_config :username do |driver| + # Return nil to prevent username from being added to Docker + # command line args for Windows if a username was not specified + if driver.windows_os? + nil + else + 'kitchen' + end + end + + def verify_dependencies + run_command("#{config[:binary]} >> #{dev_null} 2>&1", quiet: true, use_sudo: config[:use_sudo]) + rescue + raise UserError, 'You must first install the Docker CLI tool https://www.docker.com/get-started' + end + + def create(state) + container.create(state) + + wait_for_transport(state) + end + + def destroy(state) + container.destroy(state) + end + + def wait_for_transport(state) + if config[:wait_for_transport] + instance.transport.connection(state) do |conn| + conn.wait_until_ready + end + end + end + + def default_image + platform, release = instance.platform.name.split('-') + if platform == 'centos' && release + release = 'centos' + release.split('.').first + end + release ? [platform, release].join(':') : platform + end + + def default_platform + instance.platform.name.split('-').first + end + + protected + + def container + @container ||= if windows_os? + Kitchen::Docker::Container::Windows.new(config) + else + Kitchen::Docker::Container::Linux.new(config) + end + @container + end + end + end +end diff -Nru ruby-kitchen-docker-2.7.0/lib/kitchen/driver/docker_version.rb ruby-kitchen-docker-2.10.0/lib/kitchen/driver/docker_version.rb --- ruby-kitchen-docker-2.7.0/lib/kitchen/driver/docker_version.rb 2018-12-25 20:53:24.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/lib/kitchen/driver/docker_version.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -# -*- encoding: utf-8 -*- -# -# Copyright (C) 2014, Sean Porter -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -module Kitchen - - module Driver - - # Version string for Docker Kitchen driver - DOCKER_VERSION = "2.7.0" - end -end diff -Nru ruby-kitchen-docker-2.7.0/lib/kitchen/transport/docker.rb ruby-kitchen-docker-2.10.0/lib/kitchen/transport/docker.rb --- ruby-kitchen-docker-2.7.0/lib/kitchen/transport/docker.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/lib/kitchen/transport/docker.rb 2020-04-02 10:46:32.000000000 +0000 @@ -0,0 +1,111 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'kitchen' + +require_relative '../docker/container/linux' +require_relative '../docker/container/windows' + +require_relative '../docker/helpers/inspec_helper' + +require_relative '../../docker/version.rb' +require_relative '../../train/docker.rb' + +module Kitchen + module Transport + class Docker < Kitchen::Transport::Base + class DockerFailed < TransportFailed; end + + kitchen_transport_api_version 1 + plugin_version Kitchen::VERSION + + default_config :binary, 'docker' + default_config :env_variables, nil + default_config :interactive, false + default_config :privileged, false + default_config :tls, false + default_config :tls_cacert, nil + default_config :tls_cert, nil + default_config :tls_key, nil + default_config :tls_verify, false + default_config :tty, false + default_config :working_dir, nil + + default_config :socket do |transport| + socket = 'unix:///var/run/docker.sock' + socket = 'npipe:////./pipe/docker_engine' if transport.windows_os? + ENV['DOCKER_HOST'] || socket + end + + default_config :temp_dir do |transport| + if transport.windows_os? + '$env:TEMP' + else + '/tmp' + end + end + + default_config :username do |transport| + # Return an empty string to prevent username from being added to Docker + # command line args for Windows if a username was not specified + if transport.windows_os? + nil + else + 'kitchen' + end + end + + def connection(state, &block) + options = config.to_hash.merge(state) + options[:platform] = instance.platform.name + + # Set value for DOCKER_HOST environment variable for the docker-api gem + # This allows Windows systems to use the TCP socket for the InSpec verifier + # See the lib/docker.rb file here: https://github.com/swipely/docker-api/blob/master/lib/docker.rb + # default_socket_url is set to a Unix socket and env_url requires an environment variable to be set + ENV['DOCKER_HOST'] = options[:socket] if !options[:socket].nil? && ENV['DOCKER_HOST'].nil? + + Kitchen::Transport::Docker::Connection.new(options, &block) + end + + class Connection < Kitchen::Transport::Docker::Connection + # Include the InSpec patches to be able to execute tests on Windows containers + include Kitchen::Docker::Helpers::InspecHelper + + def execute(command) + return if command.nil? + + debug("[Docker] Executing command: #{command}") + info("[Docker] Executing command on container") + + container.execute(command) + rescue => e + raise DockerFailed, "Docker failed to execute command on container. Error Details: #{e}" + end + + def upload(locals, remote) + container.upload(locals, remote) + end + + def container + @container ||= if @options[:platform].include?('windows') + Kitchen::Docker::Container::Windows.new(@options) + else + Kitchen::Docker::Container::Linux.new(@options) + end + @container + end + end + end + end +end diff -Nru ruby-kitchen-docker-2.7.0/lib/train/docker.rb ruby-kitchen-docker-2.10.0/lib/train/docker.rb --- ruby-kitchen-docker-2.7.0/lib/train/docker.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/lib/train/docker.rb 2020-04-02 10:46:32.000000000 +0000 @@ -0,0 +1,125 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Monkey patched Docker train transport to support running the InSpec verifier on Windows +begin + # Requires train gem with a minimum version of 2.1.0 + require 'train' + + module Train::Transports + # Patched train transport with Windows support for InSpec verifier + class Docker < Train.plugin(1) + name 'docker' + + include_options Train::Extras::CommandWrapper + option :host, required: true + + def connection(state = {}, &block) + opts = merge_options(options, state || {}) + validate_options(opts) + + if @connection && @connection_options == opts + reuse_connection(&block) + else + create_new_connection(opts, &block) + end + end + + private + + # Creates a new Docker connection instance and save it for potential future + # reuse. + # + # @param options [Hash] connection options + # @return [Docker::Connection] a Docker connection instance + # @api private + def create_new_connection(options, &block) + if @connection + logger.debug("[Docker] shutting previous connection #{@connection}") + @connection.close + end + + @connection_options = options + @connection = Connection.new(options, &block) + end + + # Return the last saved Docker connection instance. + # + # @return [Docker::Connection] a Docker connection instance + # @api private + def reuse_connection + logger.debug("[Docker] reusing existing connection #{@connection}") + yield @connection if block_given? + @connection + end + end + end + + class Train::Transports::Docker + class Connection < BaseConnection + def initialize(conf) + super(conf) + @id = options[:host] + @container = ::Docker::Container.get(@id) || + fail("Can't find Docker container #{@id}") + @cmd_wrapper = nil + @cmd_wrapper = CommandWrapper.load(self, @options) + self + end + + def uri + if @container.nil? + "docker://#{@id}" + else + "docker://#{@container.id}" + end + end + + private + + def file_via_connection(path) + if os.aix? + Train::File::Remote::Aix.new(self, path) + elsif os.solaris? + Train::File::Remote::Unix.new(self, path) + elsif os.windows? + Train::File::Remote::Windows.new(self, path) + else + Train::File::Remote::Linux.new(self, path) + end + end + + def platform_specific_cmd(cmd) + return cmd if @container.info.nil? + if @container.info['Platform'] == 'windows' + return ['cmd.exe', '/c', cmd] + else + return ['/bin/sh', '-c', cmd] + end + end + + def run_command_via_connection(cmd, &_data_handler) + cmd = @cmd_wrapper.run(cmd) unless @cmd_wrapper.nil? + stdout, stderr, exit_status = @container.exec(platform_specific_cmd(cmd)) + CommandResult.new(stdout.join, stderr.join, exit_status) + rescue ::Docker::Error::DockerError => _ + raise + rescue => _ + # @TODO: differentiate any other error + raise + end + end + end +rescue LoadError => e + logger.debug("[Docker] train gem not found for InSpec verifier. #{e}") +end diff -Nru ruby-kitchen-docker-2.7.0/README.md ruby-kitchen-docker-2.10.0/README.md --- ruby-kitchen-docker-2.7.0/README.md 2018-12-25 20:53:24.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/README.md 2020-04-02 10:46:32.000000000 +0000 @@ -1,11 +1,11 @@ # Kitchen-Docker -[![Build Status](https://img.shields.io/travis/test-kitchen/kitchen-docker.svg)](https://travis-ci.org/test-kitchen/kitchen-docker) +[![Build Status](https://travis-ci.org/test-kitchen/kitchen-docker.svg?branch=master)](https://travis-ci.org/test-kitchen/kitchen-docker) [![Gem Version](https://img.shields.io/gem/v/kitchen-docker.svg)](https://rubygems.org/gems/kitchen-docker) [![Coverage](https://img.shields.io/codecov/c/github/test-kitchen/kitchen-docker.svg)](https://codecov.io/github/test-kitchen/kitchen-docker) [![License](https://img.shields.io/badge/license-Apache_2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) -A Test Kitchen Driver for Docker. +A Test Kitchen Driver and Transport for Docker. ## Requirements @@ -15,13 +15,15 @@ Please read the Test Kitchen [docs][test_kitchen_docs] for more details. -Example `.kitchen.local.yml`: +Example (Linux) `.kitchen.local.yml`: ```yaml --- driver: name: docker - + env_variables: + TEST_KEY: TEST_VALUE + platforms: - name: ubuntu run_list: @@ -32,6 +34,30 @@ platform: rhel run_list: - recipe[yum] + +transport: + name: docker +``` + +Example (Windows) `.kitchen.local.yml`: + +```yaml +--- +driver: + name: docker + +platforms: +- name: windows + driver_config: + image: mcr.microsoft.com/windows/servercore:1607 + platform: windows + run_list: + - recipe[chef_client] + +transport: + name: docker + env_variables: + TEST_KEY: TEST_VALUE ``` ## Default Configuration @@ -44,8 +70,8 @@ ```yaml --- platforms: -- name: ubuntu-12.04 -- name: centos-6.4 +- name: ubuntu-18.04 +- name: centos-7 ``` This will effectively generate a configuration similar to: @@ -53,13 +79,13 @@ ```yaml --- platforms: -- name: ubuntu-12.04 +- name: ubuntu-18.04 driver_config: - image: ubuntu:12.04 + image: ubuntu:18.04 platform: ubuntu -- name: centos-6.4 +- name: centos-7 driver_config: - image: centos:6.4 + image: centos:7 platform: centos ``` @@ -83,11 +109,9 @@ ### socket -The Docker daemon socket to use. By default, Docker will listen on -`unix:///var/run/docker.sock`, and no configuration here is required. If -Docker is binding to another host/port or Unix socket, you will need to set -this option. If a TCP socket is set, its host will be used for SSH access -to suite containers. +The Docker daemon socket to use. By default, Docker will listen on `unix:///var/run/docker.sock` (On Windows, `npipe:////./pipe/docker_engine`), +and no configuration here is required. If Docker is binding to another host/port or Unix socket, you will need to set this option. +If a TCP socket is set, its host will be used for SSH access to suite containers. Examples: @@ -99,10 +123,27 @@ socket: tcp://docker.example.com:4242 ``` -If you use [Docker for Windows](https://docs.docker.com/docker-for-windows/) - +If you are using the InSpec verifier on Windows, using named pipes for the Docker engine will not work with the Docker transport. +Set the socket option with the TCP socket address of the Docker engine as shown below: ```yaml -socket: npipe:////./pipe/docker_engine +socket: tcp://localhost:2375 +``` + +The Docker engine must be configured to listen on a TCP port (default port is 2375). This can be configured by editing the configuration file +(usually located in `C:\ProgramData\docker\config\daemon.json`) and adding the hosts value: +``` +"hosts": ["tcp://0.0.0.0:2375"] +``` + +Example configuration is shown below: +``` +{ + "registry-mirrors": [], + "insecure-registries": [], + "debug": true, + "experimental": false, + "hosts": ["tcp://0.0.0.0:2375"] +} ``` If you use [Boot2Docker](https://github.com/boot2docker/boot2docker) @@ -115,7 +156,6 @@ socket: tcp://192.168.59.103:2375 ``` - ### image The Docker image to use as the base for the suite containers. You can find @@ -129,10 +169,12 @@ The platform of the chosen image. This is used to properly bootstrap the suite container for Test Kitchen. Kitchen Docker currently supports: +* `arch` * `debian` or `ubuntu` -* `rhel` or `centos` +* `amazonlinux`, `rhel`, `centos`, `fedora` or `oraclelinux` * `gentoo` or `gentoo-paludis` -* `opensuse` or `sles` +* `opensuse/tumbleweed`, `opensuse/leap`, `opensuse` or `sles` +* `windows` The default will be computed, using the platform name (see the Default Configuration section for more details). @@ -181,6 +223,17 @@ provision_command: curl -L https://www.opscode.com/chef/install.sh | bash require_chef_omnibus: false ``` +### env_variables + +Adds environment variables to Docker container + +Examples: + +```yaml + env_variables: + TEST_KEY_1: TEST_VALUE + SOME_VAR: SOME_VALUE +``` ### use\_cache @@ -482,6 +535,19 @@ net: br3 ``` +### use_internal_docker_network + +If you want to use kitchen-docker from within another Docker container you'll +need to set this to true. When set to true uses port 22 as the SSH port and +the IP of the container that chef is going to run in as the hostname so that +you can connect to it over SSH from within another Docker container. + +Examples: + +```yaml + use_internal_docker_network: true +``` + ## Development * Source hosted at [GitHub][repo] diff -Nru ruby-kitchen-docker-2.7.0/test/integration/default/serverspec/default_spec.rb ruby-kitchen-docker-2.10.0/test/integration/default/serverspec/default_spec.rb --- ruby-kitchen-docker-2.7.0/test/integration/default/serverspec/default_spec.rb 2018-12-25 20:53:24.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/test/integration/default/serverspec/default_spec.rb 2020-04-02 10:46:32.000000000 +0000 @@ -15,7 +15,7 @@ # require 'serverspec' -set :backend, :exec +require 'spec_helper' # Just make sure the image launched and is reachable. describe command('true') do diff -Nru ruby-kitchen-docker-2.7.0/test/integration/default/serverspec/spec_helper.rb ruby-kitchen-docker-2.10.0/test/integration/default/serverspec/spec_helper.rb --- ruby-kitchen-docker-2.7.0/test/integration/default/serverspec/spec_helper.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/test/integration/default/serverspec/spec_helper.rb 2020-04-02 10:46:32.000000000 +0000 @@ -0,0 +1,21 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +case RbConfig::CONFIG['host_os'] +when /mswin|msys|mingw|cygwin|bccwin|wince|emc/ + set :backend, :cmd + set :os, :family => 'windows' +else + set :backend, :exec +end diff -Nru ruby-kitchen-docker-2.7.0/test/integration/inspec/inspec_spec.rb ruby-kitchen-docker-2.10.0/test/integration/inspec/inspec_spec.rb --- ruby-kitchen-docker-2.7.0/test/integration/inspec/inspec_spec.rb 2018-12-25 20:53:24.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/test/integration/inspec/inspec_spec.rb 2020-04-02 10:46:32.000000000 +0000 @@ -15,6 +15,12 @@ # # Just make sure the image launched and is reachable. -describe command('true') do - its(:exit_status) { is_expected.to eq 0 } +if os[:family] == 'windows' + describe command('echo 1') do + its(:exit_status) { is_expected.to eq 0 } + end +else + describe command('true') do + its(:exit_status) { is_expected.to eq 0 } + end end diff -Nru ruby-kitchen-docker-2.7.0/.travis.yml ruby-kitchen-docker-2.10.0/.travis.yml --- ruby-kitchen-docker-2.7.0/.travis.yml 2018-12-25 20:53:24.000000000 +0000 +++ ruby-kitchen-docker-2.10.0/.travis.yml 2020-04-02 10:46:32.000000000 +0000 @@ -1,22 +1,52 @@ -sudo: required -dist: trusty -language: ruby - -rvm: -- "2.3.1" -- "2.4.3" -- "2.5.1" +matrix: + include: + - os: linux + rvm: 2.4.9 + dist: xenial + language: ruby + cache: bundler + script: + - bundle exec docker version + - bundle exec kitchen --version + - bundle exec rake spec + - bundle exec kitchen test -d always + - os: linux + rvm: 2.5.7 + dist: xenial + language: ruby + cache: bundler + script: + - bundle exec docker version + - bundle exec kitchen --version + - bundle exec rake spec + - bundle exec kitchen test -d always + - os: linux + rvm: 2.6.5 + dist: xenial + language: ruby + cache: bundler + script: + - bundle exec docker version + - bundle exec kitchen --version + - bundle exec rake spec + - bundle exec kitchen test -d always + - os: windows + language: bash + install: + - choco install mingw + - choco install msys2 + - ridk.cmd exec pacman -S --noconfirm --needed base-devel mingw-w64-x86_64-toolchain + script: + - taskkill -IM "gpg-agent.exe" -F + - powershell -ExecutionPolicy Bypass -NoLogo -File docker.ps1 + - export KITCHEN_YAML=.kitchen.windows.yml + - ruby -v + - gem install bundler + - bundle install + - bundle exec docker version + - bundle exec kitchen --version + - bundle exec rake spec + - bundle exec kitchen test -d always services: - docker - -cache: - bundler: true - directories: - - /var/lib/docker - -script: -- bundle exec docker version -- bundle exec kitchen --version -- bundle exec rake spec -- bundle exec kitchen test -d always