diff --git a/README.md b/README.md index 7914ccc..d60f3da 100644 --- a/README.md +++ b/README.md @@ -6,25 +6,94 @@ Beaker library to use docker hypervisor This gem that allows you to use hosts with [docker](docker.md) hypervisor with [beaker](https://github.com/puppetlabs/beaker). -Beaker will automatically load the appropriate hypervisors for any given hosts file, so as long as your project dependencies are satisfied there's nothing else to do. No need to `require` this library in your tests. +Beaker will automatically load the appropriate hypervisors for any given hosts +file, so as long as your project dependencies are satisfied there's nothing else +to do. No need to `require` this library in your tests. -## With Beaker 3.x - -This library is included as a dependency of Beaker 3.x versions, so there's nothing to do. - -## With Beaker 4.x - -As of Beaker 4.0, all hypervisor and DSL extension libraries have been removed and are no longer dependencies. In order to use a specific hypervisor or DSL extension library in your project, you will need to include them alongside Beaker in your Gemfile or project.gemspec. E.g. +In order to use a specific hypervisor or DSL extension library in your project, +you will need to include them alongside Beaker in your Gemfile or +project.gemspec. E.g. ~~~ruby # Gemfile -gem 'beaker', '~>4.0' -gem 'beaker-aws' +gem 'beaker', '~> 4.0' +gem 'beaker-docker' # project.gemspec -s.add_runtime_dependency 'beaker', '~>4.0' -s.add_runtime_dependency 'beaker-aws' +s.add_runtime_dependency 'beaker', '~> 4.0' +s.add_runtime_dependency 'beaker-docker' ~~~ +## Nodeset Options + +The following is a sample nodeset: + +~~~yaml +HOSTS: + el8: + platform: el-8-x86_64 + hypervisor: docker + image: centos:8 + docker_cmd: '["/sbin/init"]' + # Run arbitrary things + docker_image_commands: + - 'touch /tmp/myfile' + dockeropts: + Labels: + thing: 'stuff' + HostConfig: + Privileged: true + el7: + platform: el-7-x86_64 + hypervisor: docker + image: centos:7 + # EL7 images do not support nested systemd + docker_cmd: '/usr/sbin/sshd -D -E /var/log/sshd.log' +CONFIG: + docker_cap_add: + - AUDIT_WRITE +~~~ + +## Privileged containers + +Containers are **not** run in privileged mode by default for safety. + +If you wish to enable privileged mode, simply set the following in your node: + +~~~yaml +dockeropts: + HostConfig: + Privileged: true +~~~ + +## Cleaning up after tests + +Containers created by this plugin may not be destroyed unless the tests complete +successfully. Each container created is prefixed by `beaker-` to make filtering +for clean up easier. + +A quick way to clean up all nodes is as follows: + +~~~sh +podman rm -f $( podman ps -q -f name="beaker-*" ) +~~~ + +# Working with `podman` + +If you're using a version of `podman` that has API socket support then you +should be able to simply set `DOCKER_HOST` to your socket and connect as usual. + +You also need to ensure that you're using a version of the `docker-api` gem that +supports `podman`. + +You may find that not all of your tests work as expected. This will be due to +the tighter system restrictions placed on containers by `podman`. You may need +to edit the `dockeropts` hash in your nodeset to include different flags in the +`HostConfig` section. + +See the +[HostConfig](https://any-api.com/docker_com/engine/docs/Definitions/HostConfig) +portion of the docker API for more information. + # Spec tests Spec test live under the `spec` folder. There are the default rake task and therefore can run with a simple command: @@ -34,7 +103,8 @@ bundle exec rake test:spec # Acceptance tests -There is a simple rake task to invoke acceptance test for the library: +There is a simple rake task to invoke acceptance test for the library: + ```bash bundle exec rake test:acceptance ``` diff --git a/acceptance/config/nodes/hosts.yaml b/acceptance/config/nodes/hosts.yaml index fcff9bd..4870e23 100644 --- a/acceptance/config/nodes/hosts.yaml +++ b/acceptance/config/nodes/hosts.yaml @@ -1,9 +1,9 @@ --- HOSTS: - ubuntu1604-64-1: - platform: ubuntu-1604-x86_64 + centos8: + platform: el-8-x86_64 hypervisor: docker - image: ubuntu:16.04 + image: centos:8 roles: - master - agent @@ -12,22 +12,24 @@ HOSTS: - classifier - default docker_cmd: '["/sbin/init"]' + docker_cap_add: + - AUDIT_WRITE dockeropts: Labels: one: '1' two: '2' - ubuntu1604-64-2: - platform: ubuntu-1604-x86_64 + centos7: + platform: el-7-x86_64 hypervisor: docker - image: ubuntu:16.04 + image: centos:7 roles: - agent - docker_cmd: '["/sbin/init"]' + docker_cmd: '/usr/sbin/sshd -D -E /var/log/sshd.log' + use_image_entrypoint: true + dockeropts: + HostConfig: + Privileged: true CONFIG: nfs_server: none consoleport: 443 log_level: verbose - dockeropts: - Labels: - one: '3' - two: '4' diff --git a/beaker-docker.gemspec b/beaker-docker.gemspec index 22f3f36..b21bbf1 100644 --- a/beaker-docker.gemspec +++ b/beaker-docker.gemspec @@ -32,7 +32,7 @@ Gem::Specification.new do |s| # Run time dependencies s.add_runtime_dependency 'stringify-hash', '~> 0.0.0' - s.add_runtime_dependency 'docker-api', '< 2.0.0' + s.add_runtime_dependency 'docker-api', '< 3.0.0' end diff --git a/lib/beaker/hypervisor/docker.rb b/lib/beaker/hypervisor/docker.rb index 92ad230..f39837d 100644 --- a/lib/beaker/hypervisor/docker.rb +++ b/lib/beaker/hypervisor/docker.rb @@ -19,19 +19,25 @@ def initialize(hosts, options) default_docker_options = { :write_timeout => 300, :read_timeout => 300 }.merge(::Docker.options || {}) # Merge docker options from the entry in hosts file ::Docker.options = default_docker_options.merge(@options[:docker_options] || {}) - # assert that the docker-api gem can talk to your docker - # enpoint. Will raise if there is a version mismatch + + # Ensure that we can correctly communicate with the docker API begin - ::Docker.validate_version! + @docker_version = ::Docker.version rescue Excon::Errors::SocketError => e - raise "Docker instance not connectable.\nError was: #{e}\nCheck your DOCKER_HOST variable has been set\nIf you are on OSX or Windows, you might not have Docker Machine setup correctly: https://docs.docker.com/machine/\n" + raise <<~ERRMSG + Docker instance not connectable + Error was: #{e} + * Check your DOCKER_HOST variable has been set + * If you are on OSX or Windows, you might not have Docker Machine setup correctly: https://docs.docker.com/machine/ + * If you are using rootless podman, you might need to set up your local socket and service + ERRMSG end # Pass on all the logging from docker-api to the beaker logger instance ::Docker.logger = @logger # Find out what kind of remote instance we are talking against - if ::Docker.version['Version'] =~ /swarm/ + if @docker_version['Version'] =~ /swarm/ @docker_type = 'swarm' unless ENV['DOCKER_REGISTRY'] raise "Using Swarm with beaker requires a private registry. Please setup the private registry and set the 'DOCKER_REGISTRY' env var" @@ -41,10 +47,21 @@ def initialize(hosts, options) else @docker_type = 'docker' end - end def install_and_run_ssh(host) + def host.enable_root_login(host,opts) + logger.debug("Root login already enabled for #{host}") + end + + # If the container is running ssh as its init process then this method + # will cause issues. + if host[:docker_cmd] =~ /sshd/ + def host.ssh_service_restart + self[:docker_container].exec(%w(kill -1 1)) + end + end + host['dockerfile'] || host['use_image_entry_point'] end @@ -62,7 +79,6 @@ def get_container_opts(host, image_name) '22/tcp' => [{ 'HostPort' => rand.to_s[2..5], 'HostIp' => '0.0.0.0'}] }, 'PublishAllPorts' => true, - 'Privileged' => true, 'RestartPolicy' => { 'Name' => 'always' } @@ -171,6 +187,8 @@ def provision if host['docker_container_name'] container_opts['name'] = host['docker_container_name'] + else + container_opts['name'] = 'beaker-' + SecureRandom.uuid.split('-').last end @logger.debug("Creating container from image #{image_name}") @@ -189,19 +207,26 @@ def provision @logger.debug("Starting container #{container.id}") container.start + # Preserve the ability to talk directly to the underlying API + # + # You can use any method defined by the docker-api gem on this object + # https://github.com/swipely/docker-api + host[:docker_container] = container + if install_and_run_ssh(host) @logger.notify("Installing ssh components and starting ssh daemon in #{host} container") install_ssh_components(container, host) # run fixssh to configure and start the ssh service fix_ssh(container, host) end + # Find out where the ssh port is from the container # When running on swarm DOCKER_HOST points to the swarm manager so we have to get the # IP of the swarm slave via the container data # When we are talking to a normal docker instance DOCKER_HOST can point to a remote docker instance. # Talking against a remote docker host which is a normal docker host - if @docker_type == 'docker' && ENV['DOCKER_HOST'] + if @docker_type == 'docker' && ENV['DOCKER_HOST'] && !ENV.fetch('DOCKER_HOST','').include?(':///') ip = URI.parse(ENV['DOCKER_HOST']).host else # Swarm or local docker host @@ -212,14 +237,29 @@ def provision end end + network_settings = container.json['NetworkSettings'] + host_config = container.json['HostConfig'] + @logger.info("Using docker server at #{ip}") - port = container.json["NetworkSettings"]["Ports"]["22/tcp"][0]["HostPort"] - forward_ssh_agent = @options[:forward_ssh_agent] || false + port = '22' + if host_config['NetworkMode'] == 'bridge' && network_settings['IPAddress'] && !network_settings['IPAddress'].empty? + host['ip'] = network_settings['IPAddress'] + else + port = network_settings['Ports']['22/tcp'][0]['HostPort'] + + # Update host metadata + host['ip'] = (ip == '0.0.0.0') ? '127.0.0.1' : ip + + if (port.to_i < 1024) && (Process.uid != 0) + raise "#{host} was given a port less than 1024 but you are not running as root, please try again" + end + end - # Update host metadata - host['ip'] = ip host['port'] = port + + forward_ssh_agent = @options[:forward_ssh_agent] || false + host['ssh'] = { :password => root_password, :port => port, @@ -232,10 +272,12 @@ def provision host['docker_image_id'] = image.id host['vm_ip'] = container.json["NetworkSettings"]["IPAddress"].to_s + def host.reboot + @logger.warn("Rebooting containers is ineffective...ignoring") + end end hack_etc_hosts @hosts, @options - end # This sideloads sshd after a container starts @@ -244,19 +286,23 @@ def install_ssh_components(container, host) when /ubuntu/, /debian/ container.exec(%w(apt-get update)) container.exec(%w(apt-get install -y openssh-server openssh-client)) + container.exec(%w(sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/*)) when /cumulus/ container.exec(%w(apt-get update)) container.exec(%w(apt-get install -y openssh-server openssh-client)) + container.exec(%w(sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/*)) when /fedora-(2[2-9])/ container.exec(%w(dnf clean all)) container.exec(%w(dnf install -y sudo openssh-server openssh-clients)) container.exec(%w(ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key)) container.exec(%w(ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key)) + container.exec(%w(sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/*)) when /^el-/, /centos/, /fedora/, /redhat/, /eos/ container.exec(%w(yum clean all)) container.exec(%w(yum install -y sudo openssh-server openssh-clients)) container.exec(%w(ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key)) container.exec(%w(ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key)) + container.exec(%w(sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/*)) when /opensuse/, /sles/ container.exec(%w(zypper -n in openssh)) container.exec(%w(ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key)) @@ -372,71 +418,76 @@ def dockerfile_for(host) case host['platform'] when /ubuntu/, /debian/ service_name = "ssh" - dockerfile += <<-EOF + dockerfile += <<~EOF RUN apt-get update RUN apt-get install -y openssh-server openssh-client #{Beaker::HostPrebuiltSteps::DEBIAN_PACKAGES.join(' ')} - EOF + RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/* + EOF when /cumulus/ - dockerfile += <<-EOF + dockerfile += <<~EOF RUN apt-get update RUN apt-get install -y openssh-server openssh-client #{Beaker::HostPrebuiltSteps::CUMULUS_PACKAGES.join(' ')} - EOF + EOF when /fedora-(2[2-9])/ - dockerfile += <<-EOF + dockerfile += <<~EOF RUN dnf clean all RUN dnf install -y sudo openssh-server openssh-clients #{Beaker::HostPrebuiltSteps::UNIX_PACKAGES.join(' ')} RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key RUN ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key - EOF + RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/* + EOF when /el-8/ - dockerfile += <<-EOF - RUN yum clean all - RUN yum install -y sudo openssh-server openssh-clients #{Beaker::HostPrebuiltSteps::RHEL8_PACKAGES.join(' ')} + dockerfile += <<~EOF + RUN dnf clean all + RUN dnf install -y sudo openssh-server openssh-clients #{Beaker::HostPrebuiltSteps::RHEL8_PACKAGES.join(' ')} RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key RUN ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key - EOF + RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/* + EOF when /^el-/, /centos/, /fedora/, /redhat/, /eos/ - dockerfile += <<-EOF + dockerfile += <<~EOF RUN yum clean all RUN yum install -y sudo openssh-server openssh-clients #{Beaker::HostPrebuiltSteps::UNIX_PACKAGES.join(' ')} RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key RUN ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key - EOF + RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/* + EOF when /opensuse/, /sles/ - dockerfile += <<-EOF + dockerfile += <<~EOF RUN zypper -n in openssh #{Beaker::HostPrebuiltSteps::SLES_PACKAGES.join(' ')} RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key RUN ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key RUN sed -ri 's/^#?UsePAM .*/UsePAM no/' /etc/ssh/sshd_config - EOF + RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/* + EOF when /archlinux/ - dockerfile += <<-EOF + dockerfile += <<~EOF RUN pacman --noconfirm -Sy archlinux-keyring RUN pacman --noconfirm -Syu RUN pacman -S --noconfirm openssh #{Beaker::HostPrebuiltSteps::ARCHLINUX_PACKAGES.join(' ')} RUN ssh-keygen -A RUN sed -ri 's/^#?UsePAM .*/UsePAM no/' /etc/ssh/sshd_config RUN systemctl enable sshd - EOF + EOF else # TODO add more platform steps here raise "platform #{host['platform']} not yet supported on docker" end # Make sshd directory, set root password - dockerfile += <<-EOF + dockerfile += <<~EOF RUN mkdir -p /var/run/sshd RUN echo root:#{root_password} | chpasswd - EOF + EOF # Configure sshd service to allowroot login using password # Also, disable reverse DNS lookups to prevent every. single. ssh # operation taking 30 seconds while the lookup times out. - dockerfile += <<-EOF + dockerfile += <<~EOF RUN sed -ri 's/^#?PermitRootLogin .*/PermitRootLogin yes/' /etc/ssh/sshd_config RUN sed -ri 's/^#?PasswordAuthentication .*/PasswordAuthentication yes/' /etc/ssh/sshd_config RUN sed -ri 's/^#?UseDNS .*/UseDNS no/' /etc/ssh/sshd_config - EOF + EOF # Any extra commands specified for the host diff --git a/spec/beaker/hypervisor/docker_spec.rb b/spec/beaker/hypervisor/docker_spec.rb index 7bbd801..932ab81 100644 --- a/spec/beaker/hypervisor/docker_spec.rb +++ b/spec/beaker/hypervisor/docker_spec.rb @@ -1,14 +1,6 @@ require 'spec_helper' require 'fakefs/spec_helpers' -# fake the docker-api -module Docker - class Image - end - class Container - end -end - module Beaker platforms = [ "ubuntu-14.04-x86_64", @@ -20,6 +12,8 @@ module Beaker ] describe Docker do + require 'docker' + let(:hosts) { the_hosts = make_hosts the_hosts[2]['dockeropts'] = { @@ -88,603 +82,598 @@ module Beaker end let (:docker) { ::Beaker::Docker.new( hosts, options ) } + let(:docker_options) { nil } + let (:version) { {"ApiVersion"=>"1.18", "Arch"=>"amd64", "GitCommit"=>"4749651", "GoVersion"=>"go1.4.2", "KernelVersion"=>"3.16.0-37-generic", "Os"=>"linux", "Version"=>"1.6.0"} } - before :each do - # Stub out all of the docker-api gem. we should never really call it - # from these tests - allow_any_instance_of( ::Beaker::Docker ).to receive(:require).with('docker') - allow( ::Docker ).to receive(:options).and_return(docker_options) - allow( ::Docker ).to receive(:options=) - allow( ::Docker ).to receive(:logger=) - allow( ::Docker ).to receive(:version).and_return(version) - allow( ::Docker::Image ).to receive(:build).and_return(image) - allow( ::Docker::Image ).to receive(:create).and_return(image) - allow( ::Docker::Container ).to receive(:create).and_return(container) - allow_any_instance_of( ::Docker::Container ).to receive(:start) - end + context 'with connection failure' do + describe '#initialize' do + before :each do + require 'excon' + expect( ::Docker ).to receive(:version).and_raise(Excon::Errors::SocketError.new( StandardError.new('oops') )).exactly(4).times + end - describe '#initialize, failure to validate' do - before :each do - require 'excon' - allow( ::Docker ).to receive(:validate_version!).and_raise(Excon::Errors::SocketError.new( StandardError.new('oops') )) - end - it 'should fail when docker not present' do - expect { docker }.to raise_error(RuntimeError, /Docker instance not connectable./) - expect { docker }.to raise_error(RuntimeError, /Check your DOCKER_HOST variable has been set/) - expect { docker }.to raise_error(RuntimeError, /If you are on OSX or Windows, you might not have Docker Machine setup correctly/) - expect { docker }.to raise_error(RuntimeError, /Error was: oops/) + it 'should fail when docker not present' do + expect { docker }.to raise_error(RuntimeError, /Docker instance not connectable/) + expect { docker }.to raise_error(RuntimeError, /Check your DOCKER_HOST variable has been set/) + expect { docker }.to raise_error(RuntimeError, /If you are on OSX or Windows, you might not have Docker Machine setup correctly/) + expect { docker }.to raise_error(RuntimeError, /Error was: oops/) + end end end - describe '#initialize' do + + context 'with a working connection' do before :each do - allow( ::Docker ).to receive(:validate_version!) - end + # Stub out all of the docker-api gem. we should never really call it + # from these tests + allow_any_instance_of( ::Beaker::Docker ).to receive(:require).with('docker') + allow( ::Docker ).to receive(:options).and_return(docker_options) + allow( ::Docker ).to receive(:options=) + allow( ::Docker ).to receive(:logger=) + allow( ::Docker ).to receive(:version).and_return(version) + allow( ::Docker::Image ).to receive(:build).and_return(image) + allow( ::Docker::Image ).to receive(:create).and_return(image) + allow( ::Docker::Container ).to receive(:create).and_return(container) + allow_any_instance_of( ::Docker::Container ).to receive(:start) + end + + describe '#initialize' do + it 'should require the docker gem' do + expect_any_instance_of( ::Beaker::Docker ).to receive(:require).with('docker').once - it 'should require the docker gem' do - expect_any_instance_of( ::Beaker::Docker ).to receive(:require).with('docker').once + docker + end - docker - end + it 'should fail when the gem is absent' do + allow_any_instance_of( ::Beaker::Docker ).to receive(:require).with('docker').and_raise(LoadError) + expect { docker }.to raise_error(LoadError) + end - it 'should fail when the gem is absent' do - allow_any_instance_of( ::Beaker::Docker ).to receive(:require).with('docker').and_raise(LoadError) - expect { docker }.to raise_error(LoadError) - end + it 'should set Docker options' do + expect( ::Docker ).to receive(:options=).with({:write_timeout => 300, :read_timeout => 300}).once - it 'should set Docker options' do - expect( ::Docker ).to receive(:options=).with({:write_timeout => 300, :read_timeout => 300}).once + docker + end - docker - end + context 'when Docker options are already set' do + let(:docker_options) {{:write_timeout => 600, :foo => :bar}} - context 'when Docker options are already set' do - let(:docker_options) {{:write_timeout => 600, :foo => :bar}} + it 'should not override Docker options' do + expect( ::Docker ).to receive(:options=).with({:write_timeout => 600, :read_timeout => 300, :foo => :bar}).once - it 'should not override Docker options' do - expect( ::Docker ).to receive(:options=).with({:write_timeout => 600, :read_timeout => 300, :foo => :bar}).once + docker + end + end + it 'should check the Docker gem can work with the api' do docker end - end - it 'should check the Docker gem can work with the api' do - expect( ::Docker ).to receive(:validate_version!).once + it 'should hook the Beaker logger into the Docker one' do + expect( ::Docker ).to receive(:logger=).with(logger) - docker + docker + end end - it 'should hook the Beaker logger into the Docker one' do - expect( ::Docker ).to receive(:logger=).with(logger) - - docker - end - end + describe '#install_ssh_components' do + let(:test_container) { double('container') } + let(:host) {hosts[0]} + before :each do + allow( docker ).to receive(:dockerfile_for) + end - describe '#install_ssh_components' do - let(:test_container) { double('container') } - let(:host) {hosts[0]} - before :each do - allow( ::Docker ).to receive(:validate_version!) - allow( docker ).to receive(:dockerfile_for) - end + platforms.each do |platform| + it 'should call exec at least twice' do + host['platform'] = platform + expect(test_container).to receive(:exec).at_least(:twice) + docker.install_ssh_components(test_container, host) + end + end - platforms.each do |platform| - it 'should call exec at least twice' do - host['platform'] = platform + it 'should accept alpine as valid platform' do + host['platform'] = 'alpine-3.8-x86_64' expect(test_container).to receive(:exec).at_least(:twice) docker.install_ssh_components(test_container, host) end - end - it 'should accept alpine as valid platform' do - host['platform'] = 'alpine-3.8-x86_64' - expect(test_container).to receive(:exec).at_least(:twice) - docker.install_ssh_components(test_container, host) + it 'should raise an error with an unsupported platform' do + host['platform'] = 'boogeyman-2000-x86_64' + expect{docker.install_ssh_components(test_container, host)}.to raise_error(RuntimeError, /boogeyman/) + end end - it 'should raise an error with an unsupported platform' do - host['platform'] = 'boogeyman-2000-x86_64' - expect{docker.install_ssh_components(test_container, host)}.to raise_error(RuntimeError, /boogeyman/) - end - end + describe '#provision' do + before :each do + allow( docker ).to receive(:dockerfile_for) + end - describe '#provision' do - before :each do - allow( ::Docker ).to receive(:validate_version!) - allow( docker ).to receive(:dockerfile_for) - end + context 'when the host has "tag" defined' do + before :each do + hosts.each do |host| + host['tag'] = 'my_tag' + end + end - context 'when the host has "tag" defined' do - before :each do - hosts.each do |host| - host['tag'] = 'my_tag' + it 'will tag the image with the value of the tag' do + expect( image ).to receive(:tag).with({:repo => 'my_tag'}).exactly(3).times + docker.provision end end - it 'will tag the image with the value of the tag' do - expect( image ).to receive(:tag).with({:repo => 'my_tag'}).exactly(3).times - docker.provision - end - end + context 'when the host has "use_image_entry_point" set to true on the host' do - context 'when the host has "use_image_entry_point" set to true on the host' do + before :each do + hosts.each do |host| + host['use_image_entry_point'] = true + end + end - before :each do - hosts.each do |host| - host['use_image_entry_point'] = true + it 'should not call #dockerfile_for but run methods necessary for ssh installation' do + expect( docker ).not_to receive(:dockerfile_for) + expect( docker ).to receive(:install_ssh_components).exactly(3).times #once per host + expect( docker ).to receive(:fix_ssh).exactly(3).times #once per host + docker.provision end end - it 'should not call #dockerfile_for but run methods necessary for ssh installation' do - expect( docker ).not_to receive(:dockerfile_for) - expect( docker ).to receive(:install_ssh_components).exactly(3).times #once per host - expect( docker ).to receive(:fix_ssh).exactly(3).times #once per host - docker.provision - end - end + context 'when the host has a "dockerfile" for the host' do - context 'when the host has a "dockerfile" for the host' do + before :each do + allow( docker ).to receive(:buildargs_for).and_return('buildargs') + hosts.each do |host| + host['dockerfile'] = 'mydockerfile' + end + end - before :each do - allow( docker ).to receive(:buildargs_for).and_return('buildargs') - hosts.each do |host| - host['dockerfile'] = 'mydockerfile' + it 'should not call #dockerfile_for but run methods necessary for ssh installation' do + allow( File ).to receive(:exist?).with('mydockerfile').and_return(true) + allow( ::Docker::Image ).to receive(:build_from_dir).with("/", hash_including(:rm => true, :buildargs => 'buildargs')).and_return(image) + expect( docker ).not_to receive(:dockerfile_for) + expect( docker ).to receive(:install_ssh_components).exactly(3).times #once per host + expect( docker ).to receive(:fix_ssh).exactly(3).times #once per host + docker.provision end end - it 'should not call #dockerfile_for but run methods necessary for ssh installation' do - allow( File ).to receive(:exist?).with('mydockerfile').and_return(true) - allow( ::Docker::Image ).to receive(:build_from_dir).with("/", hash_including(:rm => true, :buildargs => 'buildargs')).and_return(image) - expect( docker ).not_to receive(:dockerfile_for) - expect( docker ).to receive(:install_ssh_components).exactly(3).times #once per host - expect( docker ).to receive(:fix_ssh).exactly(3).times #once per host - docker.provision - end - end + it 'should call image create for hosts when use_image_as_is is defined' do + hosts.each do |host| + host['use_image_as_is'] = true + expect( docker ).not_to receive(:install_ssh_components) + expect( docker ).not_to receive(:fix_ssh) + expect( ::Docker::Image ).to receive(:create).with('fromImage' => host['image']) #once per host + expect( ::Docker::Image ).not_to receive(:build) + expect( ::Docker::Image ).not_to receive(:build_from_dir) + end - it 'should call image create for hosts when use_image_as_is is defined' do - hosts.each do |host| - host['use_image_as_is'] = true - expect( docker ).not_to receive(:install_ssh_components) - expect( docker ).not_to receive(:fix_ssh) - expect( ::Docker::Image ).to receive(:create).with('fromImage' => host['image']) #once per host - expect( ::Docker::Image ).not_to receive(:build) - expect( ::Docker::Image ).not_to receive(:build_from_dir) + docker.provision end - docker.provision - end + it 'should call dockerfile_for with all the hosts' do + hosts.each do |host| + expect( docker ).not_to receive(:install_ssh_components) + expect( docker ).not_to receive(:fix_ssh) + expect( docker ).to receive(:dockerfile_for).with(host).and_return('') + end - it 'should call dockerfile_for with all the hosts' do - hosts.each do |host| - expect( docker ).not_to receive(:install_ssh_components) - expect( docker ).not_to receive(:fix_ssh) - expect( docker ).to receive(:dockerfile_for).with(host).and_return('') + docker.provision end - docker.provision - end - - it 'should pass the Dockerfile on to Docker::Image.create' do - allow( docker ).to receive(:dockerfile_for).and_return('special testing value') - expect( ::Docker::Image ).to receive(:build).with('special testing value', { :rm => true, :buildargs => '{}' }) + it 'should pass the Dockerfile on to Docker::Image.create' do + allow( docker ).to receive(:dockerfile_for).and_return('special testing value') + expect( ::Docker::Image ).to receive(:build).with('special testing value', { :rm => true, :buildargs => '{}' }) - docker.provision - end + docker.provision + end - it 'should pass the buildargs from ENV DOCKER_BUILDARGS on to Docker::Image.create' do - allow( docker ).to receive(:dockerfile_for).and_return('special testing value') - ENV['DOCKER_BUILDARGS'] = 'HTTP_PROXY=http://1.1.1.1:3128' - expect( ::Docker::Image ).to receive(:build).with('special testing value', { :rm => true, :buildargs => "{\"HTTP_PROXY\":\"http://1.1.1.1:3128\"}" }) + it 'should pass the buildargs from ENV DOCKER_BUILDARGS on to Docker::Image.create' do + allow( docker ).to receive(:dockerfile_for).and_return('special testing value') + ENV['DOCKER_BUILDARGS'] = 'HTTP_PROXY=http://1.1.1.1:3128' + expect( ::Docker::Image ).to receive(:build).with('special testing value', { :rm => true, :buildargs => "{\"HTTP_PROXY\":\"http://1.1.1.1:3128\"}" }) - docker.provision - end + docker.provision + end - it 'should create a container based on the Image (identified by image.id)' do - hosts.each do |host| - expect( ::Docker::Container ).to receive(:create).with({ - 'Image' => image.id, - 'Hostname' => host.name, - 'HostConfig' => { - 'PortBindings' => { - '22/tcp' => [{ 'HostPort' => /\b\d{4}\b/, 'HostIp' => '0.0.0.0'}] + it 'should create a container based on the Image (identified by image.id)' do + hosts.each_with_index do |host,index| + expect( ::Docker::Container ).to receive(:create).with({ + 'Image' => image.id, + 'Hostname' => host.name, + 'HostConfig' => { + 'PortBindings' => { + '22/tcp' => [{ 'HostPort' => /\b\d{4}\b/, 'HostIp' => '0.0.0.0'}] + }, + 'PublishAllPorts' => true, + 'RestartPolicy' => { + 'Name' => 'always' + } }, - 'PublishAllPorts' => true, - 'Privileged' => true, - 'RestartPolicy' => { - 'Name' => 'always' - } - }, - 'Labels' => { - 'one' => 1, - 'two' => 2, - }, - }).with(hash_excluding('name')) - end + 'Labels' => { + 'one' => (index == 2 ? 3 : 1), + 'two' => (index == 2 ? 4 : 2), + }, + 'name' => /\Abeaker-/ + }) + end - docker.provision - end + docker.provision + end - it 'should pass the multiple buildargs from ENV DOCKER_BUILDARGS on to Docker::Image.create' do - allow( docker ).to receive(:dockerfile_for).and_return('special testing value') - ENV['DOCKER_BUILDARGS'] = 'HTTP_PROXY=http://1.1.1.1:3128 HTTPS_PROXY=https://1.1.1.1:3129' - expect( ::Docker::Image ).to receive(:build).with('special testing value', { :rm => true, :buildargs => "{\"HTTP_PROXY\":\"http://1.1.1.1:3128\",\"HTTPS_PROXY\":\"https://1.1.1.1:3129\"}" }) + it 'should pass the multiple buildargs from ENV DOCKER_BUILDARGS on to Docker::Image.create' do + allow( docker ).to receive(:dockerfile_for).and_return('special testing value') + ENV['DOCKER_BUILDARGS'] = 'HTTP_PROXY=http://1.1.1.1:3128 HTTPS_PROXY=https://1.1.1.1:3129' + expect( ::Docker::Image ).to receive(:build).with('special testing value', { :rm => true, :buildargs => "{\"HTTP_PROXY\":\"http://1.1.1.1:3128\",\"HTTPS_PROXY\":\"https://1.1.1.1:3129\"}" }) - docker.provision - end + docker.provision + end - it 'should create a container based on the Image (identified by image.id)' do - hosts.each do |host| - expect( ::Docker::Container ).to receive(:create).with({ - 'Image' => image.id, - 'Hostname' => host.name, - 'HostConfig' => { - 'PortBindings' => { - '22/tcp' => [{ 'HostPort' => /\b\d{4}\b/, 'HostIp' => '0.0.0.0'}] + it 'should create a container based on the Image (identified by image.id)' do + hosts.each_with_index do |host,index| + expect( ::Docker::Container ).to receive(:create).with({ + 'Image' => image.id, + 'Hostname' => host.name, + 'HostConfig' => { + 'PortBindings' => { + '22/tcp' => [{ 'HostPort' => /\b\d{4}\b/, 'HostIp' => '0.0.0.0'}] + }, + 'PublishAllPorts' => true, + 'RestartPolicy' => { + 'Name' => 'always' + } }, - 'PublishAllPorts' => true, - 'Privileged' => true, - 'RestartPolicy' => { - 'Name' => 'always' - } - }, - 'Labels' => { - 'one' => 1, - 'two' => 2, - }, - }).with(hash_excluding('name')) + 'Labels' => { + 'one' => (index == 2 ? 3 : 1), + 'two' => (index == 2 ? 4 : 2), + }, + 'name' => /\Abeaker-/ + }) + end + + docker.provision end - docker.provision - end + it 'should create a named container based on the Image (identified by image.id)' do + hosts.each_with_index do |host, index| + container_name = "spec-container-#{index}" + host['docker_container_name'] = container_name - it 'should create a named container based on the Image (identified by image.id)' do - hosts.each_with_index do |host, index| - container_name = "spec-container-#{index}" - host['docker_container_name'] = container_name - - allow(::Docker::Container).to receive(:all).and_return([]) - expect( ::Docker::Container ).to receive(:create).with({ - 'Image' => image.id, - 'Hostname' => host.name, - 'name' => container_name, - 'HostConfig' => { - 'PortBindings' => { - '22/tcp' => [{ 'HostPort' => /\b\d{4}\b/, 'HostIp' => '0.0.0.0'}] + allow(::Docker::Container).to receive(:all).and_return([]) + expect( ::Docker::Container ).to receive(:create).with({ + 'Image' => image.id, + 'Hostname' => host.name, + 'name' => container_name, + 'HostConfig' => { + 'PortBindings' => { + '22/tcp' => [{ 'HostPort' => /\b\d{4}\b/, 'HostIp' => '0.0.0.0'}] + }, + 'PublishAllPorts' => true, + 'RestartPolicy' => { + 'Name' => 'always' + } }, - 'PublishAllPorts' => true, - 'Privileged' => true, - 'RestartPolicy' => { - 'Name' => 'always' - } - }, - 'Labels' => { - 'one' => (index == 2 ? 3 : 1), - 'two' => (index == 2 ? 4 : 2), - }, - }) - end + 'Labels' => { + 'one' => (index == 2 ? 3 : 1), + 'two' => (index == 2 ? 4 : 2), + }, + }) + end - docker.provision - end + docker.provision + end - it 'should create a container with volumes bound' do - hosts.each_with_index do |host, index| - host['mount_folders'] = { - 'mount1' => { - 'host_path' => '/source_folder', - 'container_path' => '/mount_point', - }, - 'mount2' => { - 'host_path' => '/another_folder', - 'container_path' => '/another_mount', - 'opts' => 'ro', - }, - 'mount3' => { - 'host_path' => '/different_folder', - 'container_path' => '/different_mount', - 'opts' => 'rw', - }, - 'mount4' => { - 'host_path' => './', - 'container_path' => '/relative_mount', - }, - 'mount5' => { - 'host_path' => 'local_folder', - 'container_path' => '/another_relative_mount', - } - } - - expect( ::Docker::Container ).to receive(:create).with({ - 'Image' => image.id, - 'Hostname' => host.name, - 'HostConfig' => { - 'Binds' => [ - '/source_folder:/mount_point', - '/another_folder:/another_mount:ro', - '/different_folder:/different_mount:rw', - "#{File.expand_path('./')}:/relative_mount", - "#{File.expand_path('local_folder')}:/another_relative_mount", - ], - 'PortBindings' => { - '22/tcp' => [{ 'HostPort' => /\b\d{4}\b/, 'HostIp' => '0.0.0.0'}] + it 'should create a container with volumes bound' do + hosts.each_with_index do |host, index| + host['mount_folders'] = { + 'mount1' => { + 'host_path' => '/source_folder', + 'container_path' => '/mount_point', + }, + 'mount2' => { + 'host_path' => '/another_folder', + 'container_path' => '/another_mount', + 'opts' => 'ro', + }, + 'mount3' => { + 'host_path' => '/different_folder', + 'container_path' => '/different_mount', + 'opts' => 'rw', + }, + 'mount4' => { + 'host_path' => './', + 'container_path' => '/relative_mount', }, - 'PublishAllPorts' => true, - 'Privileged' => true, - 'RestartPolicy' => { - 'Name' => 'always' + 'mount5' => { + 'host_path' => 'local_folder', + 'container_path' => '/another_relative_mount', } - }, - 'Labels' => { - 'one' => (index == 2 ? 3 : 1), - 'two' => (index == 2 ? 4 : 2), - }, - }) - end - - docker.provision - end - - it 'should create a container with capabilities added' do - hosts.each_with_index do |host, index| - host['docker_cap_add'] = ['NET_ADMIN', 'SYS_ADMIN'] + } - expect( ::Docker::Container ).to receive(:create).with({ - 'Image' => image.id, - 'Hostname' => host.name, - 'HostConfig' => { - 'PortBindings' => { - '22/tcp' => [{ 'HostPort' => /\b\d{4}\b/, 'HostIp' => '0.0.0.0'}] + expect( ::Docker::Container ).to receive(:create).with({ + 'Image' => image.id, + 'Hostname' => host.name, + 'HostConfig' => { + 'Binds' => [ + '/source_folder:/mount_point', + '/another_folder:/another_mount:ro', + '/different_folder:/different_mount:rw', + "#{File.expand_path('./')}:/relative_mount", + "#{File.expand_path('local_folder')}:/another_relative_mount", + ], + 'PortBindings' => { + '22/tcp' => [{ 'HostPort' => /\b\d{4}\b/, 'HostIp' => '0.0.0.0'}] + }, + 'PublishAllPorts' => true, + 'RestartPolicy' => { + 'Name' => 'always' + } }, - 'PublishAllPorts' => true, - 'Privileged' => true, - 'RestartPolicy' => { - 'Name' => 'always' + 'Labels' => { + 'one' => (index == 2 ? 3 : 1), + 'two' => (index == 2 ? 4 : 2), }, - 'CapAdd' => ['NET_ADMIN', 'SYS_ADMIN'] - }, - 'Labels' => { - 'one' => (index == 2 ? 3 : 1), - 'two' => (index == 2 ? 4 : 2), - }, - }) - end + 'name' => /\Abeaker-/ + }) + end - docker.provision - end + docker.provision + end - it 'should start the container' do - expect( container ).to receive(:start) + it 'should create a container with capabilities added' do + hosts.each_with_index do |host, index| + host['docker_cap_add'] = ['NET_ADMIN', 'SYS_ADMIN'] + + expect( ::Docker::Container ).to receive(:create).with({ + 'Image' => image.id, + 'Hostname' => host.name, + 'HostConfig' => { + 'PortBindings' => { + '22/tcp' => [{ 'HostPort' => /\b\d{4}\b/, 'HostIp' => '0.0.0.0'}] + }, + 'PublishAllPorts' => true, + 'RestartPolicy' => { + 'Name' => 'always' + }, + 'CapAdd' => ['NET_ADMIN', 'SYS_ADMIN'] + }, + 'Labels' => { + 'one' => (index == 2 ? 3 : 1), + 'two' => (index == 2 ? 4 : 2), + }, + 'name' => /\Abeaker-/ + }) + end - docker.provision - end + docker.provision + end - context "connecting to ssh" do - before { @docker_host = ENV['DOCKER_HOST'] } - after { ENV['DOCKER_HOST'] = @docker_host } + it 'should start the container' do + expect( container ).to receive(:start) - it 'should expose port 22 to beaker' do - ENV['DOCKER_HOST'] = nil docker.provision - - expect( hosts[0]['ip'] ).to be === '127.0.1.1' - expect( hosts[0]['port'] ).to be === 8022 end - it 'should expose port 22 to beaker when using DOCKER_HOST' do - ENV['DOCKER_HOST'] = "tcp://192.0.2.2:2375" - docker.provision + context "connecting to ssh" do + before { @docker_host = ENV['DOCKER_HOST'] } + after { ENV['DOCKER_HOST'] = @docker_host } - expect( hosts[0]['ip'] ).to be === '192.0.2.2' - expect( hosts[0]['port'] ).to be === 8022 - end + it 'should expose port 22 to beaker' do + ENV['DOCKER_HOST'] = nil + docker.provision - it 'should have ssh agent forwarding enabled' do - ENV['DOCKER_HOST'] = nil - docker.provision + expect( hosts[0]['ip'] ).to be === '127.0.1.1' + expect( hosts[0]['port'] ).to be === 8022 + end - expect( hosts[0]['ip'] ).to be === '127.0.1.1' - expect( hosts[0]['port'] ).to be === 8022 - expect( hosts[0]['ssh'][:password] ).to be === 'root' - expect( hosts[0]['ssh'][:port] ).to be === 8022 - expect( hosts[0]['ssh'][:forward_agent] ).to be === true - end + it 'should expose port 22 to beaker when using DOCKER_HOST' do + ENV['DOCKER_HOST'] = "tcp://192.0.2.2:2375" + docker.provision - it 'should connect to gateway ip' do - FakeFS do - File.open('/.dockerenv', 'w') { } + expect( hosts[0]['ip'] ).to be === '192.0.2.2' + expect( hosts[0]['port'] ).to be === 8022 + end + + it 'should have ssh agent forwarding enabled' do + ENV['DOCKER_HOST'] = nil docker.provision - expect( hosts[0]['ip'] ).to be === '192.0.2.254' + expect( hosts[0]['ip'] ).to be === '127.0.1.1' expect( hosts[0]['port'] ).to be === 8022 + expect( hosts[0]['ssh'][:password] ).to be === 'root' + expect( hosts[0]['ssh'][:port] ).to be === 8022 + expect( hosts[0]['ssh'][:forward_agent] ).to be === true end - end - end + it 'should connect to gateway ip' do + FakeFS do + File.open('/.dockerenv', 'w') { } + docker.provision + + expect( hosts[0]['ip'] ).to be === '192.0.2.254' + expect( hosts[0]['port'] ).to be === 8022 + end + end - it "should generate a new /etc/hosts file referencing each host" do - ENV['DOCKER_HOST'] = nil - docker.provision - hosts.each do |host| - expect( docker ).to receive( :get_domain_name ).with( host ).and_return( 'labs.lan' ) - expect( docker ).to receive( :set_etc_hosts ).with( host, "127.0.0.1\tlocalhost localhost.localdomain\n192.0.2.1\tvm1.labs.lan vm1\n192.0.2.1\tvm2.labs.lan vm2\n192.0.2.1\tvm3.labs.lan vm3\n" ).once end - docker.hack_etc_hosts( hosts, options ) - end - it 'should record the image and container for later' do - docker.provision + it "should generate a new /etc/hosts file referencing each host" do + ENV['DOCKER_HOST'] = nil + docker.provision + hosts.each do |host| + expect( docker ).to receive( :get_domain_name ).with( host ).and_return( 'labs.lan' ) + expect( docker ).to receive( :set_etc_hosts ).with( host, "127.0.0.1\tlocalhost localhost.localdomain\n192.0.2.1\tvm1.labs.lan vm1\n192.0.2.1\tvm2.labs.lan vm2\n192.0.2.1\tvm3.labs.lan vm3\n" ).once + end + docker.hack_etc_hosts( hosts, options ) + end - expect( hosts[0]['docker_image_id'] ).to be === image.id - expect( hosts[0]['docker_container_id'] ).to be === container.id - end + it 'should record the image and container for later' do + docker.provision - context 'provision=false' do - let(:options) {{ - :logger => logger, - :forward_ssh_agent => true, - :provision => false - }} + expect( hosts[0]['docker_image_id'] ).to be === image.id + expect( hosts[0]['docker_container_id'] ).to be === container.id + end + context 'provision=false' do + let(:options) {{ + :logger => logger, + :forward_ssh_agent => true, + :provision => false + }} - it 'should fix ssh' do - hosts.each_with_index do |host, index| - container_name = "spec-container-#{index}" - host['docker_container_name'] = container_name - expect( ::Docker::Container ).to receive(:all).and_return([container]) - expect(docker).to receive(:fix_ssh).exactly(1).times + it 'should fix ssh' do + hosts.each_with_index do |host, index| + container_name = "spec-container-#{index}" + host['docker_container_name'] = container_name + + expect( ::Docker::Container ).to receive(:all).and_return([container]) + expect(docker).to receive(:fix_ssh).exactly(1).times + end + docker.provision end - docker.provision - end - it 'should not create a container if a named one already exists' do - hosts.each_with_index do |host, index| - container_name = "spec-container-#{index}" - host['docker_container_name'] = container_name + it 'should not create a container if a named one already exists' do + hosts.each_with_index do |host, index| + container_name = "spec-container-#{index}" + host['docker_container_name'] = container_name - expect( ::Docker::Container ).to receive(:all).and_return([container]) - expect( ::Docker::Container ).not_to receive(:create) - end + expect( ::Docker::Container ).to receive(:all).and_return([container]) + expect( ::Docker::Container ).not_to receive(:create) + end - docker.provision + docker.provision + end end end - end - describe '#cleanup' do - before :each do - # get into a state where there's something to clean - allow( ::Docker ).to receive(:validate_version!) - allow( ::Docker::Container ).to receive(:all).and_return([container]) - allow( ::Docker::Image ).to receive(:remove).with(image.id) - allow( docker ).to receive(:dockerfile_for) - docker.provision - end - - it 'should stop the containers' do - allow( docker ).to receive( :sleep ).and_return(true) - expect( container ).to receive(:kill) - docker.cleanup - end + describe '#cleanup' do + before :each do + # get into a state where there's something to clean + allow( ::Docker::Container ).to receive(:all).and_return([container]) + allow( ::Docker::Image ).to receive(:remove).with(image.id) + allow( docker ).to receive(:dockerfile_for) + docker.provision + end - it 'should delete the containers' do - allow( docker ).to receive( :sleep ).and_return(true) - expect( container ).to receive(:delete) - docker.cleanup - end + it 'should stop the containers' do + allow( docker ).to receive( :sleep ).and_return(true) + expect( container ).to receive(:kill) + docker.cleanup + end - it 'should delete the images' do - allow( docker ).to receive( :sleep ).and_return(true) - expect( ::Docker::Image ).to receive(:remove).with(image.id) - docker.cleanup - end + it 'should delete the containers' do + allow( docker ).to receive( :sleep ).and_return(true) + expect( container ).to receive(:delete) + docker.cleanup + end - it 'should not delete the image if docker_preserve_image is set to true' do - allow( docker ).to receive( :sleep ).and_return(true) - hosts.each do |host| - host['docker_preserve_image']=true + it 'should delete the images' do + allow( docker ).to receive( :sleep ).and_return(true) + expect( ::Docker::Image ).to receive(:remove).with(image.id) + docker.cleanup end - expect( ::Docker::Image ).to_not receive(:remove) - docker.cleanup - end - it 'should delete the image if docker_preserve_image is set to false' do - allow( docker ).to receive( :sleep ).and_return(true) - hosts.each do |host| - host['docker_preserve_image']=false + it 'should not delete the image if docker_preserve_image is set to true' do + allow( docker ).to receive( :sleep ).and_return(true) + hosts.each do |host| + host['docker_preserve_image']=true + end + expect( ::Docker::Image ).to_not receive(:remove) + docker.cleanup end - expect( ::Docker::Image ).to receive(:remove).with(image.id) - docker.cleanup - end - end + it 'should delete the image if docker_preserve_image is set to false' do + allow( docker ).to receive( :sleep ).and_return(true) + hosts.each do |host| + host['docker_preserve_image']=false + end + expect( ::Docker::Image ).to receive(:remove).with(image.id) + docker.cleanup + end - describe '#dockerfile_for' do - FakeFS.deactivate! - before :each do - allow( ::Docker ).to receive(:validate_version!) - end - it 'should raise on an unsupported platform' do - expect { docker.send(:dockerfile_for, {'platform' => 'a_sidewalk', 'image' => 'foobar' }) }.to raise_error(/platform a_sidewalk not yet supported/) end - it 'should set "ENV container docker"' do + describe '#dockerfile_for' do FakeFS.deactivate! - platforms.each do |platform| - dockerfile = docker.send(:dockerfile_for, { - 'platform' => platform, - 'image' => 'foobar', - }) - expect( dockerfile ).to be =~ /ENV container docker/ + it 'should raise on an unsupported platform' do + expect { docker.send(:dockerfile_for, {'platform' => 'a_sidewalk', 'image' => 'foobar' }) }.to raise_error(/platform a_sidewalk not yet supported/) end - end - it 'should add docker_image_commands as RUN statements' do - FakeFS.deactivate! - platforms.each do |platform| - dockerfile = docker.send(:dockerfile_for, { - 'platform' => platform, - 'image' => 'foobar', - 'docker_image_commands' => [ - 'special one', - 'special two', - 'special three', - ] - }) + it 'should set "ENV container docker"' do + FakeFS.deactivate! + platforms.each do |platform| + dockerfile = docker.send(:dockerfile_for, { + 'platform' => platform, + 'image' => 'foobar', + }) + expect( dockerfile ).to be =~ /ENV container docker/ + end + end - expect( dockerfile ).to be =~ /RUN special one\nRUN special two\nRUN special three/ + it 'should add docker_image_commands as RUN statements' do + FakeFS.deactivate! + platforms.each do |platform| + dockerfile = docker.send(:dockerfile_for, { + 'platform' => platform, + 'image' => 'foobar', + 'docker_image_commands' => [ + 'special one', + 'special two', + 'special three', + ] + }) + + expect( dockerfile ).to be =~ /RUN special one\nRUN special two\nRUN special three/ + end end - end - it 'should add docker_image_entrypoint' do - FakeFS.deactivate! - platforms.each do |platform| + it 'should add docker_image_entrypoint' do + FakeFS.deactivate! + platforms.each do |platform| + dockerfile = docker.send(:dockerfile_for, { + 'platform' => platform, + 'image' => 'foobar', + 'docker_image_entrypoint' => '/bin/bash' + }) + + expect( dockerfile ).to be =~ %r{ENTRYPOINT /bin/bash} + end + end + + it 'should use zypper on sles' do + FakeFS.deactivate! dockerfile = docker.send(:dockerfile_for, { - 'platform' => platform, + 'platform' => 'sles-12-x86_64', 'image' => 'foobar', - 'docker_image_entrypoint' => '/bin/bash' }) - expect( dockerfile ).to be =~ %r{ENTRYPOINT /bin/bash} + expect( dockerfile ).to be =~ /RUN zypper -n in openssh/ end - end - it 'should use zypper on sles' do - FakeFS.deactivate! - dockerfile = docker.send(:dockerfile_for, { - 'platform' => 'sles-12-x86_64', - 'image' => 'foobar', - }) + (22..29).to_a.each do | fedora_release | + it "should use dnf on fedora #{fedora_release}" do + FakeFS.deactivate! + dockerfile = docker.send(:dockerfile_for, { + 'platform' => "fedora-#{fedora_release}-x86_64", + 'image' => 'foobar', + }) - expect( dockerfile ).to be =~ /RUN zypper -n in openssh/ - end + expect( dockerfile ).to be =~ /RUN dnf install -y sudo/ + end + end - (22..29).to_a.each do | fedora_release | - it "should use dnf on fedora #{fedora_release}" do + it 'should use pacman on archlinux' do FakeFS.deactivate! dockerfile = docker.send(:dockerfile_for, { - 'platform' => "fedora-#{fedora_release}-x86_64", + 'platform' => 'archlinux-current-x86_64', 'image' => 'foobar', }) - expect( dockerfile ).to be =~ /RUN dnf install -y sudo/ + expect( dockerfile ).to be =~ /RUN pacman -S --noconfirm openssh/ end end - - it 'should use pacman on archlinux' do - FakeFS.deactivate! - dockerfile = docker.send(:dockerfile_for, { - 'platform' => 'archlinux-current-x86_64', - 'image' => 'foobar', - }) - - expect( dockerfile ).to be =~ /RUN pacman -S --noconfirm openssh/ - end end end end