def provision
      @logger.notify "Provisioning docker"

      @hosts.each do |host|
        @logger.notify "provisioning #{host.name}"

        container_opts = {}
        @logger.debug("Creating image")

        dockerfile = host['dockerfile']
        if dockerfile
          install_and_run_ssh = true
          container_opts['ExposedPorts'] = {'22/tcp' => {} }
          # assume that the dockerfile is in the repo and tests are running
          # from the root of the repo; maybe add support for external Dockerfiles
          # with external build dependencies later.
          if File.exist?(dockerfile)
            dir = File.expand_path(dockerfile).chomp(dockerfile)
            image = ::Docker::Image.build_from_dir(
              dir,
              { 'dockerfile' => dockerfile,
                :rm => true,
                :buildargs => buildargs_for(host)
              }
            )
          else
            raise "Unable to find dockerfile at #{dockerfile}"
          end
        elsif host['use_image_entry_point']
          install_and_run_ssh = true
          df = <<-DF
            FROM #{host['image']}
            EXPOSE 22
          DF

          cmd = host['docker_cmd']
          df += cmd if cmd

          image = ::Docker::Image.build(df, { rm: true, buildargs: buildargs_for(host) })

        else

          image = ::Docker::Image.build(dockerfile_for(host),
                                        { rm: true, buildargs: buildargs_for(host) })
        end

          if host['tag']
            image.tag({:repo => host['tag']})
          end

        if @docker_type == 'swarm'
          image_name = "#{@registry}/beaker/#{image.id}"
          ret = ::Docker::Image.search(:term => image_name)
          if ret.first.nil?
            @logger.debug("Image does not exist on registry. Pushing.")
            image.tag({:repo => image_name, :force => true})
            image.push
          end
        else
          image_name = image.id
        end

        container_opts.merge! ( {
          'Image' => image_name,
          'Hostname' => host.name,
          'HostConfig' => {
            'PortBindings' => {
              '22/tcp' => [{ 'HostPort' => rand.to_s[2..5], 'HostIp' => '0.0.0.0'}]
            },
            'PublishAllPorts' => true,
            'Privileged' => true,
            'RestartPolicy' => {
              'Name' => 'always'
            }
          }
        } )
        if host['dockeropts'] || @options[:dockeropts]
          dockeropts = host['dockeropts'] ? host['dockeropts'] : @options[:dockeropts]
          dockeropts.each do |k,v|
            container_opts[k] = v
          end
        end

        container = find_container(host)

        # Provisioning - Only provision if:
        # - provisioning was explicitly requested via options, or
        # - the host's container can't be found via its name or ID
        if @options[:provision] || container.nil?
          unless host['mount_folders'].nil?
            container_opts['HostConfig'] ||= {}
            container_opts['HostConfig']['Binds'] = host['mount_folders'].values.map do |mount|
              host_path = File.expand_path(mount['host_path'])
              # When using docker_toolbox and getting a "(Driveletter):/" path, convert windows path to VM mount
              if ENV['DOCKER_TOOLBOX_INSTALL_PATH'] && host_path =~ /^.\:\//
                host_path = "/" + host_path.gsub(/^.\:/, host_path[/^(.)/].downcase)
              end
              a = [ host_path, mount['container_path'] ]
              a << mount['opts'] if mount.has_key?('opts')
              a.join(':')
            end
          end

          if host['docker_cap_add']
            container_opts['HostConfig']['CapAdd'] = host['docker_cap_add']
          end

          if host['docker_container_name']
            container_opts['name'] = host['docker_container_name']
          end

          @logger.debug("Creating container from image #{image_name}")
          container = ::Docker::Container.create(container_opts)
        end

        if container.nil?
          raise RuntimeError, 'Cannot continue because no existing container ' +
                              'could be found and provisioning is disabled.'
        end

        fix_ssh(container) if @options[:provision] == false

        @logger.debug("Starting container #{container.id}")
        container.start

        if install_and_run_ssh
          @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']
          ip = URI.parse(ENV['DOCKER_HOST']).host
        else
          # Swarm or local docker host
          if in_container?
            ip = container.json["NetworkSettings"]["Gateway"]
          else
            ip = container.json["NetworkSettings"]["Ports"]["22/tcp"][0]["HostIp"]
          end
        end

        @logger.info("Using docker server at #{ip}")
        port = container.json["NetworkSettings"]["Ports"]["22/tcp"][0]["HostPort"]

        forward_ssh_agent = @options[:forward_ssh_agent] || false

        # Update host metadata
        host['ip']  = ip
        host['port'] = port
        host['ssh']  = {
          :password => root_password,
          :port => port,
          :forward_agent => forward_ssh_agent,
        }

        @logger.debug("node available as  ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@#{ip} -p #{port}")
        host['docker_container_id'] = container.id
        host['docker_image_id'] = image.id
        host['vm_ip'] = container.json["NetworkSettings"]["IPAddress"].to_s

      end

      hack_etc_hosts @hosts, @options

    end
def provision
      @logger.notify "Provisioning docker"

      @hosts.each do |host|
        @logger.notify "provisioning #{host.name}"

        container_opts = {}
        @logger.debug("Creating image")

        dockerfile = host['dockerfile']
        if dockerfile
          install_and_run_ssh = true
          container_opts['ExposedPorts'] = {'22/tcp' => {} }
          # assume that the dockerfile is in the repo and tests are running
          # from the root of the repo; maybe add support for external Dockerfiles
          # with external build dependencies later.
          if File.exist?(dockerfile)
            dir = File.expand_path(dockerfile).chomp(dockerfile)
            image = ::Docker::Image.build_from_dir(
              dir,
              { 'dockerfile' => dockerfile,
                :rm => true,
                :buildargs => buildargs_for(host)
              }
            )
          else
            raise "Unable to find dockerfile at #{dockerfile}"
          end
        elsif host['use_image_entry_point']
          install_and_run_ssh = true
          df = <<-DF
            FROM #{host['image']}
            EXPOSE 22
          DF

          cmd = host['docker_cmd']
          df += cmd if cmd

          image = ::Docker::Image.build(df, { rm: true, buildargs: buildargs_for(host) })

        else

          image = ::Docker::Image.build(dockerfile_for(host),
                                        { rm: true, buildargs: buildargs_for(host) })
        end

          if host['tag']
            image.tag({:repo => host['tag']})
          end

        if @docker_type == 'swarm'
          image_name = "#{@registry}/beaker/#{image.id}"
          ret = ::Docker::Image.search(:term => image_name)
          if ret.first.nil?
            @logger.debug("Image does not exist on registry. Pushing.")
            image.tag({:repo => image_name, :force => true})
            image.push
          end
        else
          image_name = image.id
        end

        container_opts.merge! ( {
          'Image' => image_name,
          'Hostname' => host.name,
          'HostConfig' => {
            'PortBindings' => {
              '22/tcp' => [{ 'HostPort' => rand.to_s[2..5], 'HostIp' => '0.0.0.0'}]
            },
            'PublishAllPorts' => true,
            'Privileged' => true,
            'RestartPolicy' => {
              'Name' => 'always'
            }
          }
        } )
        if host['dockeropts'] || @options[:dockeropts]
          dockeropts = host['dockeropts'] ? host['dockeropts'] : @options[:dockeropts]
          dockeropts.each do |k,v|
            container_opts[k] = v
          end
        end

        container = find_container(host)

        # Provisioning - Only provision if:
        # - provisioning was explicitly requested via options, or
        # - the host's container can't be found via its name or ID
        if @options[:provision] || container.nil?
          unless host['mount_folders'].nil?
            container_opts['HostConfig'] ||= {}
            container_opts['HostConfig']['Binds'] = host['mount_folders'].values.map do |mount|
              host_path = File.expand_path(mount['host_path'])
              # When using docker_toolbox and getting a "(Driveletter):/" path, convert windows path to VM mount
              if ENV['DOCKER_TOOLBOX_INSTALL_PATH'] && host_path =~ /^.\:\//
                host_path = "/" + host_path.gsub(/^.\:/, host_path[/^(.)/].downcase)
              end
              a = [ host_path, mount['container_path'] ]
              a << mount['opts'] if mount.has_key?('opts')
              a.join(':')
            end
          end

          if host['docker_cap_add']
            container_opts['HostConfig']['CapAdd'] = host['docker_cap_add']
          end

          if host['docker_container_name']
            container_opts['name'] = host['docker_container_name']
          end

          @logger.debug("Creating container from image #{image_name}")
          container = ::Docker::Container.create(container_opts)
        end

        if container.nil?
          raise RuntimeError, 'Cannot continue because no existing container ' +
                              'could be found and provisioning is disabled.'
        end

        fix_ssh(container) if @options[:provision] == false

        @logger.debug("Starting container #{container.id}")
        container.start

        if install_and_run_ssh
          @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']
          ip = URI.parse(ENV['DOCKER_HOST']).host
        else
          # Swarm or local docker host
          if in_container?
            ip = container.json["NetworkSettings"]["Gateway"]
          else
            ip = container.json["NetworkSettings"]["Ports"]["22/tcp"][0]["HostIp"]
          end
        end

        @logger.info("Using docker server at #{ip}")
        port = container.json["NetworkSettings"]["Ports"]["22/tcp"][0]["HostPort"]

        forward_ssh_agent = @options[:forward_ssh_agent] || false

        # Update host metadata
        host['ip']  = ip
        host['port'] = port
        host['ssh']  = {
          :password => root_password,
          :port => port,
          :forward_agent => forward_ssh_agent,
        }

        @logger.debug("node available as  ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@#{ip} -p #{port}")
        host['docker_container_id'] = container.id
        host['docker_image_id'] = image.id
        host['vm_ip'] = container.json["NetworkSettings"]["IPAddress"].to_s

      end

      hack_etc_hosts @hosts, @options

    end
Docker hypvervisor initializtion
 Env variables supported:
 DOCKER_REGISTRY: Docker registry URL
 DOCKER_HOST: Remote docker host
 DOCKER_BUILDARGS: Docker buildargs map
 @param [Host, Array<Host>, String, Symbol] hosts    One or more hosts to act upon,
                            or a role (String or Symbol) that identifies one or more hosts.
 @param [Hash{Symbol=>String}] options Options to pass on to the hypervisor