diff --git a/spec/acceptance/compose_v3_spec.rb b/spec/acceptance/compose_v3_spec.rb index f00d031..d83e228 100644 --- a/spec/acceptance/compose_v3_spec.rb +++ b/spec/acceptance/compose_v3_spec.rb @@ -1,155 +1,157 @@ require 'spec_helper_acceptance' if fact('osfamily') == 'windows' install_dir = '/cygdrive/c/Program Files/Docker' file_extension = '.exe' docker_args = 'docker_ee => true' tmp_path = 'C:/cygwin64/tmp' test_container = 'nanoserver-sac2016' else if fact('os.name') == 'RedHat' docker_args = "repo_opt => '--enablerepo=localmirror-extras'" elsif fact('os.name') == 'Centos' docker_args = "repo_opt => '--enablerepo=localmirror-extras'" + elsif fact('os.name') == 'Ubuntu' && fact('os.release.full') == '14.04' + docker_args = "version => '18.06.1~ce~3-0~ubuntu'" else docker_args = '' end install_dir = '/usr/local/bin' file_extension = '' tmp_path = '/tmp' test_container = 'debian' end describe 'docker compose' do before(:all) do retry_on_error_matching(60, 5, /connection failure running/) do install_code = <<-code class { 'docker': #{docker_args} } class { 'docker::compose': version => '1.23.2', } code apply_manifest(install_code, :catch_failures=>true) end end context 'Creating compose v3 projects' do it 'should have docker compose installed' do shell('docker-compose --help', :acceptable_exit_codes => [0]) end before(:all) do @install = <<-code docker_compose { 'web': compose_files => ['#{tmp_path}/docker-compose-v3.yml'], ensure => present, } code apply_manifest(@install, :catch_failures=>true) end it 'should be idempotent' do apply_manifest(@install, :catch_changes=>true) end it 'should find a docker container' do shell('docker inspect web_compose_test_1', :acceptable_exit_codes => [0]) end end context 'creating compose projects with multi compose files' do before(:all) do @install = <<-pp1 docker_compose { 'web1': compose_files => ['#{tmp_path}/docker-compose-v3.yml', '#{tmp_path}/docker-compose-override-v3.yml'], ensure => present, } pp1 apply_manifest(@install, :catch_failures=>true) end it "should find container with #{test_container} tag" do shell("docker inspect web1_compose_test_1 | grep #{test_container}", :acceptable_exit_codes => [0]) end end context 'Destroying project with multiple compose files' do before(:all) do @install = <<-pp1 docker_compose { 'web1': compose_files => ['#{tmp_path}/docker-compose-v3.yml', '#{tmp_path}/docker-compose-override-v3.yml'], ensure => present, } pp1 @destroy = <<-pp2 docker_compose { 'web1': compose_files => ['#{tmp_path}/docker-compose-v3.yml', '#{tmp_path}/docker-compose-override-v3.yml'], ensure => absent, } pp2 apply_manifest(@install, :catch_failures=>true) apply_manifest(@destroy, :catch_failures=>true) end it 'should be idempotent' do apply_manifest(@destroy, :catch_changes=>true) end it 'should not find a docker container' do shell('docker inspect web1_compose_test_1', :acceptable_exit_codes => [1]) end end context 'Requesting a specific version of compose' do before(:all) do @version = '1.21.2' @pp = <<-code class { 'docker::compose': version => '#{@version}', } code apply_manifest(@pp, :catch_failures=>true) end it 'should be idempotent' do apply_manifest(@pp, :catch_changes=>true) end it 'should have installed the requested version' do shell('docker-compose --version', :acceptable_exit_codes => [0]) do |r| expect(r.stdout).to match(/#{@version}/) end end end context 'Removing docker compose' do before(:all) do @version = '1.21.2' @pp = <<-code class { 'docker::compose': ensure => absent, version => '#{@version}', } code apply_manifest(@pp, :catch_failures=>true) end it 'should be idempotent' do apply_manifest(@pp, :catch_changes=>true) end it 'should have removed the relevant files' do shell("test -e \"#{install_dir}/docker-compose#{file_extension}\"", :acceptable_exit_codes => [1]) shell("test -e \"#{install_dir}/docker-compose-#{@version}#{file_extension}\"", :acceptable_exit_codes => [1]) end after(:all) do install_code = <<-code class { 'docker': #{docker_args}} class { 'docker::compose': } code apply_manifest(install_code, :catch_failures=>true) end end end diff --git a/spec/acceptance/docker_custom_source_spec.rb b/spec/acceptance/docker_custom_source_spec.rb index 5128e5c..7e655ef 100644 --- a/spec/acceptance/docker_custom_source_spec.rb +++ b/spec/acceptance/docker_custom_source_spec.rb @@ -1,102 +1,106 @@ require 'spec_helper_acceptance' skip = false if fact('osfamily') == 'windows' docker_args = 'docker_ee => true, docker_ee_source_location => "https://download.docker.com/components/engine/windows-server/17.06/docker-17.06.2-ee-14.zip"' default_image = 'microsoft/nanoserver' default_image_tag = '10.0.14393.2189' - #The default args are set because: - #restart => 'always' - there is no service created to manage containers + #The default args are set because: + #restart => 'always' - there is no service created to manage containers #net => 'nat' - docker uses bridged by default when running a container. When installing docker on windows the default network is NAT. default_docker_run_arg = "restart => 'always', net => 'nat'," default_run_command = "ping 127.0.0.1 -t" docker_command = "\"/cygdrive/c/Program Files/Docker/docker\"" - skip = false + skip = false +elsif fact('os.name') == 'Ubuntu' && fact('os.release.full') == '14.04' + docker_args = "version => '18.06.1~ce~3-0~ubuntu'" + skip = true else - skip = true + docker_args = '' + skip = true end describe 'the Puppet Docker module' do context 'with download location', :skip => skip do let(:pp) {" class { 'docker': #{docker_args} } "} it 'should run successfully' do apply_manifest(pp, :catch_failures => true) end it 'should run idempotently' do apply_manifest(pp, :catch_changes => true) unless fact('selinux') == 'true' end it 'should be start a docker process' do - if fact('osfamily') == 'windows' + if fact('osfamily') == 'windows' shell('powershell Get-Process -Name dockerd') do |r| expect(r.stdout).to match(/ProcessName/) end else shell('ps aux | grep docker') do |r| expect(r.stdout).to match(/dockerd -H unix:\/\/\/var\/run\/docker.sock/) end end end it 'should install a working docker client' do shell("#{docker_command} ps", :acceptable_exit_codes => [0] ) end it 'should stop a running container and remove container' do pp=<<-EOS class { 'docker': #{docker_args} } docker::image { '#{default_image}': require => Class['docker'], } docker::run { 'container_3_6': image => '#{default_image}', command => '#{default_run_command}', require => Docker::Image['#{default_image}'], #{default_docker_run_arg} } EOS pp2=<<-EOS class { 'docker': #{docker_args} } docker::image { '#{default_image}': require => Class['docker'], } docker::run { 'container_3_6': ensure => 'absent', image => '#{default_image}', require => Docker::Image['#{default_image}'], } EOS apply_manifest(pp, :catch_failures => true) apply_manifest(pp, :catch_changes => true) unless fact('selinux') == 'true' # A sleep to give docker time to execute properly sleep 15 shell("#{docker_command} ps", :acceptable_exit_codes => [0]) apply_manifest(pp2, :catch_failures => true) apply_manifest(pp2, :catch_changes => true) unless fact('selinux') == 'true' # A sleep to give docker time to execute properly sleep 15 shell("#{docker_command} inspect container-3-6", :acceptable_exit_codes => [1]) if fact('osfamily') == 'windows' shell('test -f /cygdrive/c/Users/Administrator/AppData/Local/Temp/container-3-6.service', :acceptable_exit_codes => [1]) else shell('test -f /etc/systemd/system/container-3-6.service', :acceptable_exit_codes => [1]) end end end end \ No newline at end of file diff --git a/spec/acceptance/docker_full_spec.rb b/spec/acceptance/docker_full_spec.rb index dac1d3e..fc08d03 100644 --- a/spec/acceptance/docker_full_spec.rb +++ b/spec/acceptance/docker_full_spec.rb @@ -1,962 +1,964 @@ require 'spec_helper_acceptance' if fact('kernel') == 'windows' docker_args = 'docker_ee => true' default_image = 'microsoft/nanoserver' default_image_tag = '10.0.14393.2189' second_image = 'hello-world' default_digest = 'sha256:204c41542c0927ac0296802e44c56b886b47e99cf8220fb49d46951bd5fc1742' default_dockerfile = 'C:/Users/Administrator/AppData/Local/Temp/Dockerfile' dockerfile_test = 'C:/Windows/Dockerfile_test.txt' #The default args are set because: #restart => 'always' - there is no service created to manage containers #net => 'nat' - docker uses bridged by default when running a container. When installing docker on windows the default network is NAT. default_docker_run_arg = "restart => 'always', net => 'nat'," default_run_command = "ping 127.0.0.1 -t" docker_command = "\"/cygdrive/c/Program Files/Docker/docker\"" default_docker_exec_lr_command = 'cmd /c "ping 127.0.0.1 -t > c:\windows\temp\test_file.txt"' default_docker_exec_command = 'cmd /c "echo test > c:\windows\temp\test_file.txt"' docker_mount_path = 'C:/Users/Administrator/AppData/Local/Temp' storage_driver = "windowsfilter" else if fact('os.family') == 'RedHat' docker_args = "repo_opt => '--enablerepo=localmirror-extras'" + elsif fact('os.name') == 'Ubuntu' && fact('os.release.full') == '14.04' + docker_args = "version => '18.06.1~ce~3-0~ubuntu'" else docker_args = '' end default_image = 'alpine' second_image = 'busybox' default_image_tag = '3.7' default_digest = 'sha256:3dcdb92d7432d56604d4545cbd324b14e647b313626d99b889d0626de158f73a' default_dockerfile = '/root/Dockerfile' dockerfile_test = "#{default_dockerfile}_test.txt" docker_command = "docker" default_docker_run_arg = '' default_run_command = "init" default_docker_exec_lr_command = '/bin/sh -c "touch /root/test_file.txt; while true; do echo hello world; sleep 1; done"' default_docker_exec_command = 'touch /root/test_file.txt' docker_mount_path = "/root" storage_driver = "devicemapper" if fact('os.family') == 'Debian' && fact('os.release.major') =~ (/14.04|^8$/) storage_driver = "aufs" elsif fact('os.family') == 'RedHat' storage_driver = "devicemapper" else storage_driver = "overlay2" end end describe 'the Puppet Docker module' do context 'clean up before each test' do before(:each) do retry_on_error_matching(60, 5, /connection failure running/) do # Stop all container using systemd shell('ls -D -1 /etc/systemd/system/docker-container* | sed \'s/\/etc\/systemd\/system\///g\' | sed \'s/\.service//g\' | while read container; do service $container stop; done') # Delete all running containers shell("#{docker_command} rm -f $(#{docker_command} ps -a -q) || true") # Delete all existing images shell("#{docker_command} rmi -f $(#{docker_command} images -q) || true") # Check to make sure no images are present shell("#{docker_command} images | wc -l") do |r| expect(r.stdout).to match(/^0|1$/) end # Check to make sure no running containers are present shell("#{docker_command} ps | wc -l") do |r| expect(r.stdout).to match(/^0|1$/) end end end describe 'docker class' do context 'without any parameters' do let(:pp) {" class { 'docker': #{docker_args} } "} it 'should run successfully' do apply_manifest(pp, :catch_failures => true) end it 'should run idempotently' do apply_manifest(pp, :catch_changes => true) unless fact('selinux') == 'true' end it 'should be start a docker process' do if fact('osfamily') == 'windows' shell('powershell Get-Process -Name dockerd') do |r| expect(r.stdout).to match(/ProcessName/) end else shell('ps aux | grep docker') do |r| expect(r.stdout).to match(/dockerd -H unix:\/\/\/var\/run\/docker.sock/) end end end it 'should install a working docker client' do shell("#{docker_command} ps", :acceptable_exit_codes => [0] ) end it 'should stop a running container and remove container' do pp=<<-EOS class { 'docker': #{docker_args} } docker::image { '#{default_image}': require => Class['docker'], } docker::run { 'container_3_6': image => '#{default_image}', command => '#{default_run_command}', require => Docker::Image['#{default_image}'], #{default_docker_run_arg} } EOS pp2=<<-EOS class { 'docker': #{docker_args} } docker::image { '#{default_image}': require => Class['docker'], } docker::run { 'container_3_6': ensure => 'absent', image => '#{default_image}', require => Docker::Image['#{default_image}'], } EOS apply_manifest(pp, :catch_failures => true) apply_manifest(pp, :catch_changes => true) unless fact('selinux') == 'true' # A sleep to give docker time to execute properly sleep 15 shell("#{docker_command} ps", :acceptable_exit_codes => [0]) apply_manifest(pp2, :catch_failures => true) apply_manifest(pp2, :catch_changes => true) unless fact('selinux') == 'true' # A sleep to give docker time to execute properly sleep 15 shell("#{docker_command} inspect container-3-6", :acceptable_exit_codes => [1]) if fact('osfamily') == 'windows' shell('test -f /cygdrive/c/Users/Administrator/AppData/Local/Temp/container-3-6.service', :acceptable_exit_codes => [1]) else shell('test -f /etc/systemd/system/container-3-6.service', :acceptable_exit_codes => [1]) end end end context 'passing a storage driver' do before(:all) do @pp=<<-EOS class {'docker': #{docker_args}, storage_driver => "#{storage_driver}", } EOS apply_manifest(@pp, :catch_failures => true) sleep 15 end it 'should result in the docker daemon being configured with the specified storage driver' do shell("#{docker_command} info -f \"{{ .Driver}}\"") do |r| expect(r.stdout).to match (/#{storage_driver}/) end end end context 'passing a TCP address to bind to' do before(:all) do @pp =<<-EOS class { 'docker': tcp_bind => 'tcp://127.0.0.1:4444', #{docker_args} } EOS apply_manifest(@pp, :catch_failures => true) # A sleep to give docker time to execute properly sleep 4 end it 'should run idempotently' do apply_manifest(@pp, :catch_changes => true) unless fact('selinux') == 'true' end it 'should result in docker listening on the specified address' do if fact('osfamily') == 'windows' shell('netstat -a -b') do |r| expect(r.stdout).to match(/127.0.0.1:4444/) end else shell('netstat -tulpn | grep docker') do |r| expect(r.stdout).to match(/tcp\s+0\s+0\s+127.0.0.1:4444\s+0.0.0.0\:\*\s+LISTEN\s+\d+\/docker/) end end end end context 'bound to a particular unix socket' do before(:each) do @pp =<<-EOS class { 'docker': socket_bind => 'unix:///var/run/docker.sock', #{docker_args} } EOS apply_manifest(@pp, :catch_failures => true) # A sleep to give docker time to execute properly sleep 4 end it 'should run idempotently' do apply_manifest(@pp, :catch_changes => true) unless fact('selinux') == 'true' end it 'should show docker listening on the specified unix socket' do if fact('osfamily') != 'windows' shell('ps aux | grep docker') do |r| expect(r.stdout).to match(/unix:\/\/\/var\/run\/docker.sock/) end end end end context 'uninstall docker' do after(:all) do @pp =<<-EOS class {'docker': #{docker_args}, ensure => 'present' } EOS apply_manifest(@pp, :catch_failures => true) # Wait for reboot if windows sleep 300 if fact('osfamily') == 'windows' end it 'should uninstall successfully' do @pp =<<-EOS class {'docker': #{docker_args}, ensure => 'absent' } EOS apply_manifest(@pp, :catch_failures => true) sleep 4 shell('docker ps', :acceptable_exit_codes => [1, 127]) end end end describe 'docker::image' do it 'should successfully download an image from the Docker Hub' do pp=<<-EOS class { 'docker': #{docker_args} } docker::image { '#{default_image}': ensure => present, require => Class['docker'], } EOS apply_manifest(pp, :catch_failures => true) apply_manifest(pp, :catch_changes => true) unless fact('selinux') == 'true' # A sleep to give docker time to execute properly sleep 4 shell("#{docker_command} images") do |r| expect(r.stdout).to match(/#{default_image}/) end end it 'should successfully download an image based on a tag from the Docker Hub' do pp=<<-EOS class { 'docker': #{docker_args} } docker::image { '#{default_image}': ensure => present, image_tag => '#{default_image_tag}', require => Class['docker'], } EOS apply_manifest(pp, :catch_failures => true) apply_manifest(pp, :catch_changes => true) unless fact('selinux') == 'true' # A sleep to give docker time to execute properly sleep 4 shell("#{docker_command} images") do |r| expect(r.stdout).to match(/#{default_image}\s+#{default_image_tag}/) end end it 'should successfully download an image based on a digest from the Docker Hub' do pp=<<-EOS class { 'docker': #{docker_args} } docker::image { '#{default_image}': ensure => present, image_digest => '#{default_digest}', require => Class['docker'], } EOS apply_manifest(pp, :catch_failures => true) apply_manifest(pp, :catch_changes => true) unless fact('selinux') == 'true' # A sleep to give docker time to execute properly sleep 4 shell("#{docker_command} images --digests") do |r| expect(r.stdout).to match(/#{default_image}.*#{default_digest}/) end end it 'should create a new image based on a Dockerfile' do if fact('osfamily') == 'windows' run_cmd = 'RUN echo test > C:\\Windows\\Temp\\Dockerfile_test.txt' else run_cmd = "RUN echo test > #{dockerfile_test}" end pp=<<-EOS class { 'docker': #{docker_args} } docker::image { 'alpine_with_file': docker_file => "#{default_dockerfile}", require => Class['docker'], } file { '#{default_dockerfile}': ensure => present, content => "FROM #{default_image}\n#{run_cmd}", before => Docker::Image['alpine_with_file'], } EOS apply_manifest(pp, :catch_failures => true) apply_manifest(pp, :catch_changes => true) unless fact('selinux') == 'true' # A sleep to give docker time to execute properly sleep 4 if fact('osfamily') == 'windows' shell("#{docker_command} run alpine_with_file cmd /c dir Windows\\\\Temp") do |r| expect(r.stdout).to match(/_test.txt/) end else shell("#{docker_command} run alpine_with_file ls #{dockerfile_test}") do |r| expect(r.stdout).to match(/#{dockerfile_test}/) end end end it 'should create a new image based on a tar', :win_broken => true do pp=<<-EOS class { 'docker': #{docker_args} } docker::image { '#{default_image}': require => Class['docker'], ensure => present, } docker::run { 'container_2_4': image => '#{default_image}', command => '/bin/sh -c "touch /root/test_file_for_tar_test.txt; while true; do echo hello world; sleep 1; done"', require => Docker::Image['alpine'], } EOS pp2=<<-EOS class { 'docker': #{docker_args} } docker::image { 'alpine_from_commit': docker_tar => "/root/rootfs.tar" } EOS apply_manifest(pp, :catch_failures => true) apply_manifest(pp, :catch_changes => true) unless fact('selinux') == 'true' # A sleep to give docker time to execute properly sleep 4 # Commit currently running container as an image container_id = shell("#{docker_command} ps | awk 'FNR == 2 {print $1}'") shell("#{docker_command} commit #{container_id.stdout.strip} alpine_from_commit") # Stop all container using systemd shell('ls -D -1 /etc/systemd/system/docker-container* | sed \'s/\/etc\/systemd\/system\///g\' | sed \'s/\.service//g\' | while read container; do service $container stop; done') # Stop all running containers shell("#{docker_command} rm -f $(docker ps -a -q) || true") # Make sure no other containers are running shell("#{docker_command} ps | wc -l") do |r| expect(r.stdout).to match(/^1$/) end # Export new to a tar file shell("#{docker_command} save alpine_from_commit > /root/rootfs.tar") # Remove all images shell("#{docker_command} rmi $(docker images -q) || true") # Make sure no other images are present shell("#{docker_command} images | wc -l") do |r| expect(r.stdout).to match(/^1$/) end apply_manifest(pp2, :catch_failures => true) apply_manifest(pp2, :catch_changes => true) unless fact('selinux') == 'true' # A sleep to give docker time to execute properly sleep 4 shell("#{docker_command} run alpine_from_commit ls /root") do |r| expect(r.stdout).to match(/test_file_for_tar_test.txt/) end end it 'should successfully delete the image' do pp1=<<-EOS class { 'docker': #{docker_args} } docker::image { '#{default_image}': ensure => present, require => Class['docker'], } EOS apply_manifest(pp1, :catch_failures => true) pp2=<<-EOS class { 'docker': #{docker_args} } docker::image { '#{default_image}': ensure => absent, } EOS apply_manifest(pp2, :catch_failures => true) apply_manifest(pp2, :catch_changes => true) unless fact('selinux') == 'true' # A sleep to give docker time to execute properly sleep 4 shell("#{docker_command} images") do |r| expect(r.stdout).to_not match(/#{default_image}/) end end end describe "docker::run" do it 'should start a container with a configurable command' do pp=<<-EOS class { 'docker': #{docker_args} } docker::image { '#{default_image}': require => Class['docker'], } docker::run { 'container_3_1': image => '#{default_image}', command => '#{default_docker_exec_lr_command}', require => Docker::Image['#{default_image}'], #{default_docker_run_arg} } EOS apply_manifest(pp, :catch_failures => true) apply_manifest(pp, :catch_changes => true) unless fact('selinux') == 'true' # A sleep to give docker time to execute properly sleep 4 container_id = shell("#{docker_command} ps | awk 'FNR == 2 {print $1}'") if fact('osfamily') == 'windows' shell("#{docker_command} exec #{container_id.stdout.strip} cmd /c dir Windows\\\\Temp") do |r| expect(r.stdout).to match(/test_file.txt/) end else shell("#{docker_command} exec #{container_id.stdout.strip} ls /root") do |r| expect(r.stdout).to match(/test_file.txt/) end end container_name = shell("#{docker_command} ps | awk 'FNR == 2 {print $NF}'") expect("#{container_name.stdout.strip}").to match(/(container-3-1|container_3_1)/) end it 'should start a container with port configuration' do pp=<<-EOS class { 'docker': #{docker_args}} docker::image { '#{default_image}': require => Class['docker'], } docker::run { 'container_3_2': image => '#{default_image}', command => '#{default_run_command}', ports => ['4444'], expose => ['5555'], require => Docker::Image['#{default_image}'], #{default_docker_run_arg} } EOS apply_manifest(pp, :catch_failures => true) apply_manifest(pp, :catch_changes => true) unless fact('selinux') == 'true' # A sleep to give docker time to execute properly sleep 4 shell("#{docker_command} ps") do |r| expect(r.stdout).to match(/"#{default_run_command}".+5555\/tcp\, 0\.0\.0.0\:\d+\-\>4444\/tcp/) end end it 'should start a container with the hostname set' do pp=<<-EOS class { 'docker': #{docker_args} } docker::image { '#{default_image}': require => Class['docker'], } docker::run { 'container_3_3': image => '#{default_image}', command => '#{default_run_command}', hostname => 'testdomain.com', require => Docker::Image['#{default_image}'], #{default_docker_run_arg} } EOS apply_manifest(pp, :catch_failures => true) apply_manifest(pp, :catch_changes => true) unless fact('selinux') == 'true' # A sleep to give docker time to execute properly sleep 4 container_id = shell("#{docker_command} ps | awk 'FNR == 2 {print $1}'") shell("#{docker_command} exec #{container_id.stdout.strip} hostname") do |r| expect(r.stdout).to match(/testdomain.com/) end end it 'should start a container while mounting local volumes' do pp=<<-EOS class { 'docker': #{docker_args} } docker::image { '#{default_image}': require => Class['docker'], } docker::run { 'container_3_4': image => '#{default_image}', command => '#{default_run_command}', volumes => ["#{docker_mount_path}:#{docker_mount_path}/mnt:rw"], require => Docker::Image['#{default_image}'], #{default_docker_run_arg} } file { '#{docker_mount_path}/test_mount.txt': ensure => present, before => Docker::Run['container_3_4'], } EOS apply_manifest(pp, :catch_failures => true) apply_manifest(pp, :catch_changes => true) unless fact('selinux') == 'true' # A sleep to give docker time to execute properly sleep 4 container_id = shell("#{docker_command} ps | awk 'FNR == 2 {print $1}'") if fact('osfamily') == 'windows' shell("#{docker_command} exec #{container_id.stdout.strip} cmd /c dir Users\\\\Administrator\\\\AppData\\\\Local\\\\Temp\\\\mnt") do |r| expect(r.stdout).to match(/test_mount.txt/) end else shell("#{docker_command} exec #{container_id.stdout.strip} ls /root/mnt") do |r| expect(r.stdout).to match(/test_mount.txt/) end end end #cpuset is not supported on Docker Windows #STDERR: C:/Program Files/Docker/docker.exe: Error response from daemon: invalid option: Windows does not support CpusetCpus. it 'should start a container with cpuset paramater set', :win_broken => true do pp=<<-EOS class { 'docker': #{docker_args} } docker::image { '#{default_image}': require => Class['docker'], } docker::run { 'container_3_5_5': image => '#{default_image}', command => '#{default_run_command}', cpuset => ['0'], require => Docker::Image['#{default_image}'], #{default_docker_run_arg} } EOS apply_manifest(pp, :catch_failures => true) apply_manifest(pp, :catch_changes => true) unless fact('selinux') == 'true' # A sleep to give docker time to execute properly sleep 4 shell('#{docker_command} inspect container_3_5_5') do |r| expect(r.stdout).to match(/"CpusetCpus"\: "0"/) end end #leagacy container linking was not implemented on Windows. --link is a legacy Docker feature: https://docs.docker.com/network/links/ it 'should start multiple linked containers', :win_broken => true do pp=<<-EOS class { 'docker': #{docker_args} } docker::image { '#{default_image}': require => Class['docker'], } docker::run { 'container_3_5_1': image => '#{default_image}', command => '#{default_run_command}', require => Docker::Image['#{default_image}'], #{default_docker_run_arg} } EOS apply_manifest(pp, :catch_failures => true) apply_manifest(pp, :catch_changes => true) unless fact('selinux') == 'true' # A sleep to give docker time to execute properly sleep 4 container_1 = shell("#{docker_command} ps | awk 'FNR == 2 {print $NF}'") pp2=<<-EOS class { 'docker': #{docker_args} } docker::image { '#{default_image}': require => Class['docker'], } docker::run { 'container_3_5_2': image => '#{default_image}', command => '#{default_run_command}', depends => ['#{container_1.stdout.strip}'], links => "#{container_1.stdout.strip}:the_link", require => Docker::Image['#{default_image}'], #{default_docker_run_arg} } EOS apply_manifest(pp2, :catch_failures => true) apply_manifest(pp2, :catch_changes => true) unless fact('selinux') == 'true' # A sleep to give docker time to execute properly sleep 4 container_2 = shell("#{docker_command} ps | awk 'FNR == 2 {print $NF}'") container_id = shell("#{docker_command} ps | awk 'FNR == 2 {print $1}'") shell("#{docker_command} inspect -f \"{{ .HostConfig.Links }}\" #{container_id.stdout.strip}") do |r| expect(r.stdout).to match("/#{container_1.stdout.strip}:/#{container_2.stdout.strip}/the_link") end end it 'should stop a running container' do pp=<<-EOS class { 'docker': #{docker_args} } docker::image { '#{default_image}': require => Class['docker'], } docker::run { 'container_3_6': image => '#{default_image}', command => '#{default_run_command}', require => Docker::Image['#{default_image}'], #{default_docker_run_arg} } EOS pp2=<<-EOS class { 'docker': #{docker_args} } docker::image { '#{default_image}': require => Class['docker'], } docker::run { 'container_3_6': image => '#{default_image}', running => false, require => Docker::Image['#{default_image}'], #{default_docker_run_arg} } EOS apply_manifest(pp, :catch_failures => true) apply_manifest(pp, :catch_changes => true) unless fact('selinux') == 'true' # A sleep to give docker time to execute properly sleep 4 shell("#{docker_command} ps | wc -l") do |r| expect(r.stdout).to match(/^2$/) end apply_manifest(pp2, :catch_failures => true) apply_manifest(pp2, :catch_changes => true) unless fact('selinux') == 'true' # A sleep to give docker time to execute properly sleep 4 shell("#{docker_command} ps | wc -l") do |r| expect(r.stdout).to match(/^1$/) end end it 'should stop a running container and remove container' do pp=<<-EOS class { 'docker': #{docker_args} } docker::image { '#{default_image}': require => Class['docker'], } docker::run { 'container_3_6_1': image => '#{default_image}', command => '#{default_run_command}', require => Docker::Image['#{default_image}'], #{default_docker_run_arg} } EOS pp2=<<-EOS class { 'docker': #{docker_args} } docker::image { '#{default_image}': require => Class['docker'], } docker::run { 'container_3_6_1': ensure => 'absent', image => '#{default_image}', require => Docker::Image['#{default_image}'], } EOS apply_manifest(pp, :catch_failures => true) apply_manifest(pp, :catch_changes => true) unless fact('selinux') == 'true' # A sleep to give docker time to execute properly sleep 15 shell("#{docker_command} inspect container_3_6_1", :acceptable_exit_codes => [0]) apply_manifest(pp2, :catch_failures => true) apply_manifest(pp2, :catch_changes => true) unless fact('selinux') == 'true' # A sleep to give docker time to execute properly sleep 15 shell("#{docker_command} inspect container_3_6_1", :acceptable_exit_codes => [1]) end it 'should allow dependency for ordering of independent run and image' do pp=<<-EOS class { 'docker': #{docker_args} } docker::image { '#{default_image}': } docker::run { 'container_3_7_1': image => '#{default_image}', command => '#{default_run_command}', #{default_docker_run_arg} } docker::image { '#{second_image}': require => Docker::Run['container_3_7_1'], } docker::run { 'container_3_7_2': image => '#{second_image}', command => '#{default_run_command}', #{default_docker_run_arg} } EOS apply_manifest(pp, :catch_failures => true) apply_manifest(pp, :catch_changes => true) unless fact('selinux') == 'true' end it 'should restart a unhealthy container' do pp5=<<-EOS docker::run { 'container_3_7_3': image => '#{default_image}', command => '#{default_run_command}', health_check_cmd => 'echo', restart_on_unhealthy => true, #{default_docker_run_arg} } EOS pp_delete=<<-EOS docker::run { 'container_3_7_3': image => '#{default_image}', ensure => absent, } EOS if fact('osfamily') == 'windows' apply_manifest(pp5, :catch_failures => true) elsif fact('os.release.major') =~ (/14.04|8/) apply_manifest(pp5, :catch_failures => true) do |r| expect(r.stdout).to match(/container_3_7_3/) end else apply_manifest(pp5, :catch_failures => true) do |r| expect(r.stdout).to match(/docker-container_3_7_3-systemd-reload/) end end apply_manifest(pp_delete, :catch_failures => true) end end end describe "docker::exec" do it 'should run a command inside an already running container' do pp=<<-EOS class { 'docker': #{docker_args} } docker::image { '#{default_image}': require => Class['docker'], } docker::run { 'container_4_1': image => '#{default_image}', command => '#{default_run_command}', require => Docker::Image['#{default_image}'], #{default_docker_run_arg} } EOS apply_manifest(pp, :catch_failures => true) apply_manifest(pp, :catch_changes => true) unless fact('selinux') == 'true' # A sleep to give docker time to execute properly sleep 15 container_1 = shell("#{docker_command} ps | awk 'FNR == 2 {print $NF}'") pp2=<<-EOS class { 'docker': #{docker_args} } docker::exec { 'test_command': container => '#{container_1.stdout.strip}', command => '#{default_docker_exec_command}', tty => true, } EOS pp_delete=<<-EOS docker::run { 'container_4_1': image => '#{default_image}', ensure => absent, } EOS apply_manifest(pp2, :catch_failures => true) # A sleep to give docker time to execute properly sleep 4 container_id = shell("#{docker_command} ps | awk 'FNR == 2 {print $1}'") if fact('osfamily') == 'windows' shell("#{docker_command} exec #{container_id.stdout.strip} cmd /c dir Windows\\\\Temp") do |r| expect(r.stdout).to match(/test_file.txt/) end else shell("#{docker_command} exec #{container_id.stdout.strip} ls /root") do |r| expect(r.stdout).to match(/test_file.txt/) end end apply_manifest(pp_delete, :catch_failures => true) end it 'should only run if notified when refreshonly is true' do container_name = 'container_4_2' pp=<<-EOS class { 'docker': #{docker_args} } docker::image { '#{default_image}': } docker::run { '#{container_name}': image => '#{default_image}', command => '#{default_run_command}', #{default_docker_run_arg} } docker::exec { 'test_command': container => '#{container_name}', command => '#{default_docker_exec_command}', refreshonly => true, } EOS apply_manifest(pp, :catch_failures => true) apply_manifest(pp, :catch_changes => true) unless fact('selinux') == 'true' # A sleep to give docker time to execute properly sleep 4 if fact('osfamily') == 'windows' shell("#{docker_command} exec #{container_name} cmd /c dir Windows\\\\Temp") do |r| expect(r.stdout).to_not match(/test_file.txt/) end else shell("#{docker_command} exec #{container_name} ls /root") do |r| expect(r.stdout).to_not match(/test_file.txt/) end end pp_extra=<<-EOS file { '#{default_dockerfile}_dummy_file': ensure => 'present', notify => Docker::Exec['test_command'], } EOS pp_delete=<<-EOS docker::run { '#{container_name}': image => '#{default_image}', ensure => absent, } EOS pp2 = pp + pp_extra apply_manifest(pp2, :catch_failures => true) apply_manifest(pp2, :catch_changes => true) unless fact('selinux') == 'true' # A sleep to give docker time to execute properly sleep 4 if fact('osfamily') == 'windows' shell("#{docker_command} exec #{container_name} cmd /c dir Windows\\\\Temp") do |r| expect(r.stdout).to match(/test_file.txt/) end else shell("#{docker_command} exec #{container_name} ls /root") do |r| expect(r.stdout).to match(/test_file.txt/) end end apply_manifest(pp_delete, :catch_failures => true) end end end diff --git a/spec/acceptance/docker_spec.rb b/spec/acceptance/docker_spec.rb index 24936a4..2636c7c 100644 --- a/spec/acceptance/docker_spec.rb +++ b/spec/acceptance/docker_spec.rb @@ -1,214 +1,216 @@ require 'spec_helper_acceptance' broken = false registry_port = 5000 if fact('osfamily') == 'windows' win_host = only_host_with_role(hosts, 'default') @windows_ip = win_host.ip docker_arg = "docker_ee => true, extra_parameters => '\"insecure-registries\": [ \"#{@windows_ip}:5000\" ]'" docker_registry_image = 'stefanscherer/registry-windows' docker_network = 'nat' registry_host = @windows_ip config_file = '/cygdrive/c/Users/Administrator/.docker/config.json' root_dir = "C:/Users/Administrator/AppData/Local/Temp" server_strip = "#{registry_host}_#{registry_port}" bad_server_strip = "#{registry_host}_5001" broken = true else if fact('osfamily') == 'RedHat' docker_args = "repo_opt => '--enablerepo=localmirror-extras'" + elsif fact('os.name') == 'Ubuntu' && fact('os.release.full') == '14.04' + docker_args = "version => '18.06.1~ce~3-0~ubuntu'" else - docker_arg = '' + docker_args = '' end docker_registry_image = 'registry' docker_network = 'bridge' registry_host = '127.0.0.1' server_strip = "#{registry_host}:#{registry_port}" bad_server_strip = "#{registry_host}:5001" config_file = '/root/.docker/config.json' root_dir = "/root" end describe 'docker' do package_name = 'docker-ce' service_name = 'docker' command = 'docker' context 'When adding system user', :win_broken => broken do let(:pp) {" class { 'docker': #{docker_arg} docker_users => ['user1'] } "} it 'the docker daemon' do apply_manifest(pp, :catch_failures=>true) do |r| expect(r.stdout).to_not match(/docker-systemd-reload-before-service/) end end end context 'with default parameters', :win_broken => broken do let(:pp) {" class { 'docker': docker_users => [ 'testuser' ], #{docker_args} } docker::image { 'nginx': } docker::run { 'nginx': image => 'nginx', net => 'host', require => Docker::Image['nginx'], } docker::run { 'nginx2': image => 'nginx', restart => 'always', require => Docker::Image['nginx'], } "} it 'should apply with no errors' do apply_manifest(pp, :catch_failures=>true) end it 'should be idempotent' do apply_manifest(pp, :catch_changes=>true) end describe package(package_name) do it { is_expected.to be_installed } end describe service(service_name) do it { is_expected.to be_enabled } it { is_expected.to be_running } end describe command("#{command} version") do its(:exit_status) { should eq 0 } end describe command("#{command} images"), :sudo => true do its(:exit_status) { should eq 0 } its(:stdout) { should match /nginx/ } end describe command("#{command} inspect nginx"), :sudo => true do its(:exit_status) { should eq 0 } end describe command("#{command} inspect nginx2"), :sudo => true do its(:exit_status) { should eq 0 } end describe command("#{command} ps --no-trunc | grep `cat /var/run/docker-nginx2.cid`"), :sudo => true do its(:exit_status) { should eq 0 } its(:stdout) { should match /nginx -g 'daemon off;'/ } end describe command('netstat -tlndp') do its(:exit_status) { should eq 0 } its(:stdout) { should match /0\.0\.0\.0\:80/ } end describe command('id testuser | grep docker') do its(:exit_status) { should eq 0 } its(:stdout) { should match /docker/ } end end context "When asked to have the latest image of something", :win_broken => broken do let(:pp) {" class { 'docker': docker_users => [ 'testuser' ] } docker::image { 'busybox': ensure => latest } "} it 'should apply with no errors' do apply_manifest(pp, :catch_failures=>true) end it 'should be idempotent' do apply_manifest(pp, :catch_changes=>true) end end context "When registry_mirror is set", :win_broken => broken do let(:pp) {" class { 'docker': registry_mirror => 'http://testmirror.io' } "} it 'should apply with no errors' do apply_manifest(pp, :catch_failures=>true) end it 'should have a registry mirror set' do shell('ps -aux | grep docker') do |r| expect(r.stdout).to match(/--registry-mirror=http:\/\/testmirror.io/) end end end context 'registry' do before(:all) do @registry_address = "#{registry_host}:#{registry_port}" @registry_bad_address = "#{registry_host}:5001" # @registry_email = 'user@example.com' @manifest = <<-EOS class { 'docker': #{docker_arg}} docker::run { 'registry': image => '#{docker_registry_image}', pull_on_start => true, restart => 'always', net => '#{docker_network}', ports => '#{registry_port}:#{registry_port}', } EOS retry_on_error_matching(60, 5, /connection failure running/) do apply_manifest(@manifest, :catch_failures=>true) end # avoid a race condition with the registry taking time to start # on some operating systems sleep 10 end it 'should be able to login to the registry', :retry => 3, :retry_wait => 10 do manifest = <<-EOS docker::registry { '#{@registry_address}': username => 'username', password => 'password', } EOS apply_manifest(manifest, :catch_failures=>true) shell("grep #{@registry_address} #{config_file}", :acceptable_exit_codes => [0]) shell("test -e \"#{root_dir}/registry-auth-puppet_receipt_#{server_strip}_root\"", :acceptable_exit_codes => [0]) end it 'should be able to logout from the registry' do manifest = <<-EOS docker::registry { '#{@registry_address}': ensure=> absent, } EOS apply_manifest(manifest, :catch_failures=>true) shell("grep #{@registry_address} #{config_file}", :acceptable_exit_codes => [1,2]) # shell("grep #{@registry_email} #{@config_file}", :acceptable_exit_codes => [1,2]) end it 'should not create receipt if registry login fails' do manifest = <<-EOS docker::registry { '#{@registry_bad_address}': username => 'username', password => 'password', } EOS apply_manifest(manifest, :catch_failures=>true) shell("grep #{@registry_bad_address} #{config_file}", :acceptable_exit_codes => [1,2]) shell("test -e \"#{root_dir}/registry-auth-puppet_receipt_#{bad_server_strip}_root\"", :acceptable_exit_codes => [1]) end end end diff --git a/spec/acceptance/network_spec.rb b/spec/acceptance/network_spec.rb index 9152e04..082e47f 100644 --- a/spec/acceptance/network_spec.rb +++ b/spec/acceptance/network_spec.rb @@ -1,47 +1,51 @@ require 'spec_helper_acceptance' broken = false if fact('osfamily') == 'windows' puts "Not implemented on Windows" broken = true elsif fact('osfamily') == 'RedHat' - docker_args = "repo_opt => '--enablerepo=localmirror-extras'" + docker_args = "repo_opt => '--enablerepo=localmirror-extras'" +elsif fact('os.name') == 'Ubuntu' && fact('os.release.full') == '14.04' + docker_args = "version => '18.06.1~ce~3-0~ubuntu'" +else + docker_args = '' end describe 'docker network', :win_broken => broken do command = 'docker' before(:all) do install_code = "class { 'docker': #{docker_args}}" apply_manifest(install_code, :catch_failures=>true) end describe command("#{command} network --help") do its(:exit_status) { should eq 0 } end context 'with a local bridge network described in Puppet' do before(:all) do @name = 'test-network' @pp = <<-code docker_network { '#{@name}': ensure => present, } code apply_manifest(@pp, :catch_failures=>true) end it 'should be idempotent' do apply_manifest(@pp, :catch_changes=>true) end it 'should have created a network' do shell("#{command} network inspect #{@name}", :acceptable_exit_codes => [0]) end after(:all) do shell("#{command} network rm #{@name}") end end end diff --git a/spec/acceptance/plugin_spec.rb b/spec/acceptance/plugin_spec.rb index 971d3b2..b1b3e0e 100644 --- a/spec/acceptance/plugin_spec.rb +++ b/spec/acceptance/plugin_spec.rb @@ -1,45 +1,49 @@ require 'spec_helper_acceptance' broken = false if fact('osfamily') == 'windows' puts "Not implemented on Windows" broken = true elsif fact('osfamily') == 'RedHat' - docker_args = "repo_opt => '--enablerepo=localmirror-extras'" + docker_args = "repo_opt => '--enablerepo=localmirror-extras'" +elsif fact('os.name') == 'Ubuntu' && fact('os.release.full') == '14.04' + docker_args = "version => '18.06.1~ce~3-0~ubuntu'" +else + docker_args = '' end describe 'docker plugin', :win_broken => broken do command = 'docker' before(:all) do install_code = "class { 'docker': #{docker_args}}" apply_manifest(install_code, :catch_failures => true) end describe command("#{command} plugin --help") do its(:exit_status) { should eq 0 } end context 'manage a plugin' do before(:all) do @name = 'vieux/sshfs' @pp = <<-code docker::plugin { '#{@name}':} code apply_manifest(@pp, :catch_failures => true) end it 'should be idempotent' do apply_manifest(@pp, :catch_changes => true) end it 'should have installed a plugin' do shell("#{command} plugin inspect #{@name}", :acceptable_exit_codes => [0]) end after(:all) do shell("#{command} plugin rm -f #{@name}") end end end diff --git a/spec/acceptance/stack_spec.rb b/spec/acceptance/stack_spec.rb index c7568bc..ab4c73d 100644 --- a/spec/acceptance/stack_spec.rb +++ b/spec/acceptance/stack_spec.rb @@ -1,149 +1,153 @@ require 'spec_helper_acceptance' if fact('osfamily') == 'windows' docker_args = 'docker_ee => true' tmp_path = 'C:/cygwin64/tmp' test_container = 'nanoserver-sac2016' wait_for_container_seconds = 120 else - docker_args = '' + if fact('os.name') == 'Ubuntu' && fact('os.release.full') == '14.04' + docker_args = "version => '18.06.1~ce~3-0~ubuntu'" + else + docker_args = '' + end tmp_path = '/tmp' test_container = 'alpine' wait_for_container_seconds = 10 end describe 'docker stack' do before(:all) do retry_on_error_matching(60, 5, /connection failure running/) do @install_code = <<-code class { 'docker': #{docker_args} } docker::swarm {'cluster_manager': init => true, ensure => 'present', advertise_addr => $facts['networking']['ip'], listen_addr => $facts['networking']['ip'], require => Class['docker'], } code apply_manifest(@install_code, :catch_failures=>true) end end context 'Creating stack' do let(:install) {" docker_stack { 'web': compose_files => ['#{tmp_path}/docker-stack.yml'], ensure => present, }" } it 'should deploy stack' do apply_manifest(install, :catch_failures=>true) sleep wait_for_container_seconds end it 'should be idempotent' do apply_manifest(install, :catch_changes=>true) end it 'should find a stack' do shell('docker stack ls') do |r| expect(r.stdout).to match(/web/) end end it 'should find a docker container' do shell("docker ps | grep web_compose_test", :acceptable_exit_codes => [0]) end end context 'Destroying stack' do let(:install) {" docker_stack { 'web': compose_files => ['#{tmp_path}/docker-stack.yml'], ensure => present, }" } let(:destroy) {" docker_stack { 'web': compose_files => ['#{tmp_path}/docker-stack.yml'], ensure => absent, }" } it 'should run successfully' do apply_manifest(destroy, :catch_failures=>true) sleep 10 end it 'should be idempotent' do apply_manifest(destroy, :catch_changes=>true) sleep 5 end it 'should not find a docker stack' do sleep 5 shell('docker stack ls') do |r| expect(r.stdout).to_not match(/web/) end end end context 'creating stack with multi compose files' do before(:all) do @install_code = <<-code docker_stack { 'web': compose_files => ['#{tmp_path}/docker-stack.yml', '#{tmp_path}/docker-stack-override.yml'], ensure => present, } code apply_manifest(@install_code, :catch_failures=>true) end it "should find container with web_compose_test tag" do sleep wait_for_container_seconds shell("docker ps | grep web_compose_test", :acceptable_exit_codes => [0]) end end context 'Destroying project with multiple compose files' do before(:all) do @install_code = <<-code docker_stack { 'web': compose_files => ['#{tmp_path}/docker-stack.yml', '#{tmp_path}/docker-stack-override.yml'], ensure => present, } code apply_manifest(@install_code, :catch_failures=>true) @destroy_code = <<-code docker_stack { 'web': compose_files => ['#{tmp_path}/docker-stack.yml', '#{tmp_path}/docker-stack-override.yml'], ensure => absent, } code apply_manifest(@destroy_code, :catch_failures=>true) sleep 10# wait for containers to stop end it 'should be idempotent' do apply_manifest(@destroy_code, :catch_changes=>true) sleep 5 end it 'should not find a docker stack' do shell('docker stack ls') do |r| expect(r.stdout).to_not match(/web/) end end it 'should not find a docker container' do shell("docker ps | grep web_compose_test", :acceptable_exit_codes => [1]) end end end diff --git a/spec/acceptance/volume_spec.rb b/spec/acceptance/volume_spec.rb index 6970860..55fde5f 100644 --- a/spec/acceptance/volume_spec.rb +++ b/spec/acceptance/volume_spec.rb @@ -1,50 +1,54 @@ require 'spec_helper_acceptance' broken = false if fact('osfamily') == 'windows' docker_args = 'docker_ee => true' command = "\"/cygdrive/c/Program Files/Docker/docker\"" elsif ('osfamily') == 'RedHat' docker_args = "repo_opt => '--enablerepo=localmirror-extras'" command = 'docker' +elsif fact('os.name') == 'Ubuntu' && fact('os.release.full') == '14.04' + docker_args = "version => '18.06.1~ce~3-0~ubuntu'" + command = 'docker' else + docker_args = '' command = 'docker' end describe 'docker volume' do before(:all) do retry_on_error_matching(60, 5, /connection failure running/) do install_code = "class { 'docker': #{docker_args} }" apply_manifest(install_code, :catch_failures => true) end end it 'should expose volume subcommand' do shell("#{command} volume --help", :acceptable_exit_codes => [0]) end context 'with a local volume described in Puppet' do before(:all) do @name = 'test-volume' @pp = <<-code docker_volume { '#{@name}': ensure => present, } code apply_manifest(@pp, :catch_failures => true) end it 'should be idempotent' do apply_manifest(@pp, :catch_changes => true) end it 'should have created a volume' do shell("#{command} volume inspect #{@name}", :acceptable_exit_codes => [0]) end after(:all) do shell("#{command} volume rm #{@name}") end end end \ No newline at end of file