chef-provisioning-docker 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +13 -8
- data/chef-provisioning-docker.gemspec +1 -1
- data/lib/chef/provisioning/docker_driver/docker_container_machine.rb +199 -13
- data/lib/chef/provisioning/docker_driver/docker_run_options.rb +593 -0
- data/lib/chef/provisioning/docker_driver/docker_transport.rb +120 -57
- data/lib/chef/provisioning/docker_driver/driver.rb +82 -148
- data/lib/chef/provisioning/docker_driver/version.rb +1 -1
- metadata +12 -5
| @@ -1,4 +1,6 @@ | |
| 1 1 | 
             
            require 'chef/provisioning/transport'
         | 
| 2 | 
            +
            require 'chef/provisioning/transport/ssh'
         | 
| 3 | 
            +
            require 'chef/provisioning/docker_driver/chef_zero_http_proxy'
         | 
| 2 4 | 
             
            require 'docker'
         | 
| 3 5 | 
             
            require 'archive/tar/minitar'
         | 
| 4 6 | 
             
            require 'shellwords'
         | 
| @@ -6,7 +8,7 @@ require 'uri' | |
| 6 8 | 
             
            require 'socket'
         | 
| 7 9 | 
             
            require 'mixlib/shellout'
         | 
| 8 10 | 
             
            require 'sys/proctable'
         | 
| 9 | 
            -
            require ' | 
| 11 | 
            +
            require 'tempfile'
         | 
| 10 12 |  | 
| 11 13 | 
             
            class Chef
         | 
| 12 14 | 
             
            module Provisioning
         | 
| @@ -20,15 +22,15 @@ module DockerDriver | |
| 20 22 | 
             
                attr_reader :config
         | 
| 21 23 | 
             
                attr_accessor :container
         | 
| 22 24 |  | 
| 23 | 
            -
                def execute(command, options | 
| 24 | 
            -
                  Chef::Log.debug("execute '#{command}' with options #{options}")
         | 
| 25 | 
            -
             | 
| 25 | 
            +
                def execute(command, timeout: nil, keep_stdin_open: nil, tty: nil, detached: nil, **options)
         | 
| 26 26 | 
             
                  opts = {}
         | 
| 27 | 
            -
                   | 
| 28 | 
            -
             | 
| 29 | 
            -
                   | 
| 27 | 
            +
                  opts[:tty] = tty unless tty.nil?
         | 
| 28 | 
            +
                  opts[:detached] = detached unless detached.nil?
         | 
| 29 | 
            +
                  opts[:stdin] = keep_stdin_open unless keep_stdin_open.nil?
         | 
| 30 | 
            +
                  opts[:wait] = timeout unless timeout.nil?
         | 
| 30 31 |  | 
| 31 32 | 
             
                  command = Shellwords.split(command) if command.is_a?(String)
         | 
| 33 | 
            +
                  Chef::Log.debug("execute #{command.inspect} on container #{container.id} with options #{opts}'")
         | 
| 32 34 | 
             
                  response = container.exec(command, opts) do |stream, chunk|
         | 
| 33 35 | 
             
                    case stream
         | 
| 34 36 | 
             
                    when :stdout
         | 
| @@ -47,9 +49,11 @@ module DockerDriver | |
| 47 49 | 
             
                  begin
         | 
| 48 50 | 
             
                    tarfile = ''
         | 
| 49 51 | 
             
                    # NOTE: this would be more efficient if we made it a stream and passed that to Minitar
         | 
| 50 | 
            -
                    container. | 
| 52 | 
            +
                    container.archive_out(path) do |block|
         | 
| 51 53 | 
             
                      tarfile << block
         | 
| 52 54 | 
             
                    end
         | 
| 55 | 
            +
                  rescue Docker::Error::NotFoundError
         | 
| 56 | 
            +
                    return nil
         | 
| 53 57 | 
             
                  rescue Docker::Error::ServerError
         | 
| 54 58 | 
             
                    if $!.message =~ /500/ || $!.message =~ /Could not find the file/
         | 
| 55 59 | 
             
                      return nil
         | 
| @@ -72,12 +76,12 @@ module DockerDriver | |
| 72 76 | 
             
                end
         | 
| 73 77 |  | 
| 74 78 | 
             
                def write_file(path, content)
         | 
| 75 | 
            -
                   | 
| 79 | 
            +
                  tar = StringIO.new(Docker::Util.create_tar(path => content))
         | 
| 80 | 
            +
                  container.archive_in_stream('/') { tar.read }
         | 
| 76 81 | 
             
                end
         | 
| 77 82 |  | 
| 78 83 | 
             
                def download_file(path, local_path)
         | 
| 79 | 
            -
                   | 
| 80 | 
            -
                  file = File.open(local_path, 'w')
         | 
| 84 | 
            +
                  file = File.open(local_path, 'wb')
         | 
| 81 85 | 
             
                  begin
         | 
| 82 86 | 
             
                    file.write(read_file(path))
         | 
| 83 87 | 
             
                    file.close
         | 
| @@ -87,71 +91,130 @@ module DockerDriver | |
| 87 91 | 
             
                end
         | 
| 88 92 |  | 
| 89 93 | 
             
                def upload_file(local_path, path)
         | 
| 90 | 
            -
                   | 
| 94 | 
            +
                  write_file(path, IO.read(local_path, mode: "rb"))
         | 
| 91 95 | 
             
                end
         | 
| 92 96 |  | 
| 93 | 
            -
                def make_url_available_to_remote( | 
| 94 | 
            -
                   | 
| 95 | 
            -
             | 
| 96 | 
            -
                  uri.scheme  | 
| 97 | 
            -
             | 
| 98 | 
            -
             | 
| 99 | 
            -
             | 
| 100 | 
            -
             | 
| 101 | 
            -
                     | 
| 102 | 
            -
             | 
| 103 | 
            -
             | 
| 104 | 
            -
             | 
| 105 | 
            -
             | 
| 106 | 
            -
             | 
| 107 | 
            -
             | 
| 108 | 
            -
             | 
| 109 | 
            -
             | 
| 110 | 
            -
             | 
| 111 | 
            -
             | 
| 112 | 
            -
                                   $1
         | 
| 113 | 
            -
                                 end
         | 
| 114 | 
            -
             | 
| 115 | 
            -
                      if !@proxy_thread
         | 
| 116 | 
            -
                        # Listen to docker instances only, and forward to localhost
         | 
| 117 | 
            -
                        @proxy_thread = Thread.new do
         | 
| 118 | 
            -
                          Chef::Log.debug("Starting proxy thread: #{uri.host}:#{uri.port} <--> #{host}:#{uri.port}")
         | 
| 119 | 
            -
                          ChefZeroHttpProxy.new(uri.host, uri.port, host, uri.port).run
         | 
| 97 | 
            +
                def make_url_available_to_remote(local_url)
         | 
| 98 | 
            +
                  uri = URI(local_url)
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                  if uri.scheme == "chefzero" || is_local_machine(uri.host)
         | 
| 101 | 
            +
                    # chefzero: URLs are just http URLs with a shortcut if you are in-process.
         | 
| 102 | 
            +
                    # The remote machine is definitely not in-process.
         | 
| 103 | 
            +
                    uri.scheme = "http" if uri.scheme == "chefzero"
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                    if docker_toolkit_transport
         | 
| 106 | 
            +
                      # Forward localhost on docker_machine -> chef-zero. The container will
         | 
| 107 | 
            +
                      # be able to access this because it was started with --net=host.
         | 
| 108 | 
            +
                      uri = docker_toolkit_transport.make_url_available_to_remote(uri.to_s)
         | 
| 109 | 
            +
                      uri = URI(uri)
         | 
| 110 | 
            +
                      @docker_toolkit_transport_thread ||= Thread.new do
         | 
| 111 | 
            +
                        begin
         | 
| 112 | 
            +
                          docker_toolkit_transport.send(:session).loop { true }
         | 
| 113 | 
            +
                        rescue
         | 
| 114 | 
            +
                          Chef::Log.error("SSH forwarding loop failed: #{$!}")
         | 
| 115 | 
            +
                          raise
         | 
| 120 116 | 
             
                        end
         | 
| 117 | 
            +
                        Chef::Log.debug("Session loop completed normally")
         | 
| 121 118 | 
             
                      end
         | 
| 122 | 
            -
                      Chef::Log.debug("Using Chef server URL: #{uri.to_s}")
         | 
| 123 | 
            -
             | 
| 124 | 
            -
                      return uri.to_s
         | 
| 125 119 | 
             
                    else
         | 
| 126 | 
            -
                       | 
| 120 | 
            +
                      # We are the host. Find the docker machine's gateway (us) and talk to that;
         | 
| 121 | 
            +
                      # and set up a little proxy that will forward the container's requests to
         | 
| 122 | 
            +
                      # chef-zero
         | 
| 123 | 
            +
                      result = execute('ip route list', :read_only => true)
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                      Chef::Log.debug("IP route: #{result.stdout}")
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                      if result.stdout =~ /default via (\S+)/
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                        old_uri = uri.dup
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                        uri.host = $1
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                        if !@proxy_thread
         | 
| 134 | 
            +
                          # Listen to docker instances only, and forward to localhost
         | 
| 135 | 
            +
                          @proxy_thread = Thread.new do
         | 
| 136 | 
            +
                            begin
         | 
| 137 | 
            +
                              Chef::Log.debug("Starting proxy thread: #{old_uri.host}:#{old_uri.port} <--> #{uri.host}:#{uri.port}")
         | 
| 138 | 
            +
                              ChefZeroHttpProxy.new(uri.host, uri.port, old_uri.host, old_uri.port).run
         | 
| 139 | 
            +
                            rescue
         | 
| 140 | 
            +
                              Chef::Log.error("Proxy thread unable to start: #{$!}")
         | 
| 141 | 
            +
                            end
         | 
| 142 | 
            +
                          end
         | 
| 143 | 
            +
                        end
         | 
| 144 | 
            +
                      else
         | 
| 145 | 
            +
                        raise "Cannot forward port: ip route ls did not show default in expected format.\nSTDOUT: #{result.stdout}"
         | 
| 146 | 
            +
                      end
         | 
| 147 | 
            +
             | 
| 127 148 | 
             
                    end
         | 
| 128 149 | 
             
                  end
         | 
| 129 | 
            -
             | 
| 150 | 
            +
             | 
| 151 | 
            +
                  uri.to_s
         | 
| 130 152 | 
             
                end
         | 
| 131 153 |  | 
| 132 154 | 
             
                def disconnect
         | 
| 133 | 
            -
                   | 
| 155 | 
            +
                  if @docker_toolkit_transport_thread
         | 
| 156 | 
            +
                    @docker_toolkit_transport_thread.kill
         | 
| 157 | 
            +
                    @docker_toolkit_transport_thread = nil
         | 
| 158 | 
            +
                  end
         | 
| 134 159 | 
             
                end
         | 
| 135 160 |  | 
| 136 161 | 
             
                def available?
         | 
| 137 162 | 
             
                end
         | 
| 138 163 |  | 
| 139 | 
            -
                 | 
| 140 | 
            -
             | 
| 141 | 
            -
             | 
| 142 | 
            -
             | 
| 143 | 
            -
             | 
| 144 | 
            -
             | 
| 145 | 
            -
                    if proc.respond_to?(:cmdline)
         | 
| 146 | 
            -
                      if proc.send(:cmdline).to_s =~ /.*--comment boot2docker.*/
         | 
| 147 | 
            -
                        return true
         | 
| 148 | 
            -
                      end
         | 
| 164 | 
            +
                def is_local_machine(host)
         | 
| 165 | 
            +
                  local_addrs = Socket.ip_address_list
         | 
| 166 | 
            +
                  host_addrs = Addrinfo.getaddrinfo(host, nil)
         | 
| 167 | 
            +
                  local_addrs.any? do |local_addr|
         | 
| 168 | 
            +
                    host_addrs.any? do |host_addr|
         | 
| 169 | 
            +
                      local_addr.ip_address == host_addr.ip_address
         | 
| 149 170 | 
             
                    end
         | 
| 150 171 | 
             
                  end
         | 
| 151 172 | 
             
                end
         | 
| 152 173 |  | 
| 153 | 
            -
                def  | 
| 154 | 
            -
                   | 
| 174 | 
            +
                def docker_toolkit_transport(connection_url=nil)
         | 
| 175 | 
            +
                  if !defined?(@docker_toolkit_transport)
         | 
| 176 | 
            +
                    # Figure out which docker-machine this container is in
         | 
| 177 | 
            +
                    begin
         | 
| 178 | 
            +
                      docker_machines = `docker-machine ls --format "{{.Name}},{{.URL}}"`
         | 
| 179 | 
            +
                    rescue Errno::ENOENT
         | 
| 180 | 
            +
                      Chef::Log.debug("docker-machine ls returned ENOENT: Docker Toolkit is presumably not installed.")
         | 
| 181 | 
            +
                      @docker_toolkit_transport = nil
         | 
| 182 | 
            +
                      return
         | 
| 183 | 
            +
                    end
         | 
| 184 | 
            +
             | 
| 185 | 
            +
                    connection_url ||= container.connection.url
         | 
| 186 | 
            +
             | 
| 187 | 
            +
                    Chef::Log.debug("Found docker machines:")
         | 
| 188 | 
            +
                    docker_machine = nil
         | 
| 189 | 
            +
                    docker_machines.lines.each do |line|
         | 
| 190 | 
            +
                      machine_name, machine_url = line.chomp.split(',', 2)
         | 
| 191 | 
            +
                      Chef::Log.debug("- #{machine_name} at URL #{machine_url.inspect}")
         | 
| 192 | 
            +
                      if machine_url == connection_url
         | 
| 193 | 
            +
                        Chef::Log.debug("Docker machine #{machine_name} at URL #{machine_url} matches the container's URL #{connection_url}! Will use it for port forwarding.")
         | 
| 194 | 
            +
                        docker_machine = machine_name
         | 
| 195 | 
            +
                      end
         | 
| 196 | 
            +
                    end
         | 
| 197 | 
            +
                    if !docker_machine
         | 
| 198 | 
            +
                      Chef::Log.debug("Docker Toolkit is installed, but no Docker machine's URL matches #{connection_url.inspect}. Assuming docker must be installed as well ...")
         | 
| 199 | 
            +
                      @docker_toolkit_transport = nil
         | 
| 200 | 
            +
                      return
         | 
| 201 | 
            +
                    end
         | 
| 202 | 
            +
             | 
| 203 | 
            +
                    # Get the SSH information for the docker-machine
         | 
| 204 | 
            +
                    docker_toolkit_json = `docker-machine inspect #{docker_machine}`
         | 
| 205 | 
            +
                    machine_info = JSON.parse(docker_toolkit_json, create_additions: false)["Driver"]
         | 
| 206 | 
            +
                    ssh_host = machine_info["IPAddress"]
         | 
| 207 | 
            +
                    ssh_username = machine_info["SSHUser"]
         | 
| 208 | 
            +
                    ssh_options = {
         | 
| 209 | 
            +
                      # port: machine_info["SSHPort"], seems to be bad information (44930???)
         | 
| 210 | 
            +
                      keys: [ machine_info["SSHKeyPath"] ],
         | 
| 211 | 
            +
                      keys_only: true
         | 
| 212 | 
            +
                    }
         | 
| 213 | 
            +
             | 
| 214 | 
            +
                    Chef::Log.debug("Docker Toolkit is installed. Will use SSH transport with docker-machine #{docker_machine.inspect} to perform port forwarding.")
         | 
| 215 | 
            +
                    @docker_toolkit_transport = Chef::Provisioning::Transport::SSH.new(ssh_host, ssh_username, ssh_options, {}, Chef::Config)
         | 
| 216 | 
            +
                  end
         | 
| 217 | 
            +
                  @docker_toolkit_transport
         | 
| 155 218 | 
             
                end
         | 
| 156 219 |  | 
| 157 220 | 
             
                class DockerResult
         | 
| @@ -28,6 +28,10 @@ module DockerDriver | |
| 28 28 | 
             
                  Driver.new(driver_url, config)
         | 
| 29 29 | 
             
                end
         | 
| 30 30 |  | 
| 31 | 
            +
                def driver_url
         | 
| 32 | 
            +
                  "docker:#{Docker.url}"
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
             | 
| 31 35 | 
             
                def initialize(driver_url, config)
         | 
| 32 36 | 
             
                  super
         | 
| 33 37 | 
             
                  url = Driver.connection_url(driver_url)
         | 
| @@ -37,10 +41,14 @@ module DockerDriver | |
| 37 41 | 
             
                    # to be set for command-line utilities
         | 
| 38 42 | 
             
                    ENV['DOCKER_HOST'] = url
         | 
| 39 43 | 
             
                    Chef::Log.debug("Setting Docker URL to #{url}")
         | 
| 40 | 
            -
                    Docker.url = url
         | 
| 41 44 | 
             
                  end
         | 
| 42 45 |  | 
| 43 | 
            -
                   | 
| 46 | 
            +
                  ENV['DOCKER_HOST'] ||= url if url
         | 
| 47 | 
            +
                  Docker.logger = Chef::Log
         | 
| 48 | 
            +
                  options = Docker.options.dup || {}
         | 
| 49 | 
            +
                  options.merge!(read_timeout: 600)
         | 
| 50 | 
            +
                  options.merge!(config[:docker_connection].hash_dup) if config && config[:docker_connection]
         | 
| 51 | 
            +
                  @connection = Docker::Connection.new(url || Docker.url, options)
         | 
| 44 52 | 
             
                end
         | 
| 45 53 |  | 
| 46 54 | 
             
                def self.canonicalize_url(driver_url, config)
         | 
| @@ -75,6 +83,8 @@ module DockerDriver | |
| 75 83 | 
             
                    action_handler,
         | 
| 76 84 | 
             
                    machine_spec
         | 
| 77 85 | 
             
                  )
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                  # Grab options from existing machine (TODO seems wrong) and set the machine_spec to that
         | 
| 78 88 | 
             
                  docker_options = machine_options[:docker_options]
         | 
| 79 89 | 
             
                  container_id = nil
         | 
| 80 90 | 
             
                  image_id = machine_options[:image_id]
         | 
| @@ -84,7 +94,6 @@ module DockerDriver | |
| 84 94 | 
             
                    image_id ||= machine_spec.reference['image_id']
         | 
| 85 95 | 
             
                    docker_options ||= machine_spec.reference['docker_options']
         | 
| 86 96 | 
             
                  end
         | 
| 87 | 
            -
             | 
| 88 97 | 
             
                  container_name ||= machine_spec.name
         | 
| 89 98 | 
             
                  machine_spec.reference = {
         | 
| 90 99 | 
             
                    'driver_url' => driver_url,
         | 
| @@ -93,110 +102,69 @@ module DockerDriver | |
| 93 102 | 
             
                    'host_node' => action_handler.host_node,
         | 
| 94 103 | 
             
                    'container_name' => container_name,
         | 
| 95 104 | 
             
                    'image_id' => image_id,
         | 
| 96 | 
            -
                    'docker_options' => docker_options,
         | 
| 105 | 
            +
                    'docker_options' => stringize_keys(docker_options),
         | 
| 97 106 | 
             
                    'container_id' => container_id
         | 
| 98 107 | 
             
                  }
         | 
| 99 | 
            -
                  build_container(machine_spec, docker_options)
         | 
| 100 108 | 
             
                end
         | 
| 101 109 |  | 
| 102 110 | 
             
                def ready_machine(action_handler, machine_spec, machine_options)
         | 
| 103 | 
            -
                  start_machine(action_handler, machine_spec, machine_options)
         | 
| 104 111 | 
             
                  machine_for(machine_spec, machine_options)
         | 
| 105 112 | 
             
                end
         | 
| 106 113 |  | 
| 107 | 
            -
                def  | 
| 114 | 
            +
                def start_machine(action_handler, machine_spec, machine_options)
         | 
| 108 115 | 
             
                  container = container_for(machine_spec)
         | 
| 109 | 
            -
                   | 
| 110 | 
            -
             | 
| 111 | 
            -
             | 
| 112 | 
            -
                    build_image(machine_spec, docker_options)
         | 
| 113 | 
            -
             | 
| 114 | 
            -
                  args = [
         | 
| 115 | 
            -
                    'docker',
         | 
| 116 | 
            -
                    'run',
         | 
| 117 | 
            -
                    '--name',
         | 
| 118 | 
            -
                    machine_spec.reference['container_name'],
         | 
| 119 | 
            -
                    '--detach'
         | 
| 120 | 
            -
                  ]
         | 
| 121 | 
            -
             | 
| 122 | 
            -
                  if docker_options[:keep_stdin_open]
         | 
| 123 | 
            -
                    args << '-i'
         | 
| 124 | 
            -
                  end
         | 
| 125 | 
            -
             | 
| 126 | 
            -
                  if docker_options[:env]      
         | 
| 127 | 
            -
                    docker_options[:env].each do |key, value|
         | 
| 128 | 
            -
                      args << '-e'
         | 
| 129 | 
            -
                      args << "#{key}=#{value}"
         | 
| 116 | 
            +
                  if container && !container.info['State']['Running']
         | 
| 117 | 
            +
                    action_handler.perform_action "start container #{machine_spec.name}" do
         | 
| 118 | 
            +
                      container.start!
         | 
| 130 119 | 
             
                    end
         | 
| 131 120 | 
             
                  end
         | 
| 121 | 
            +
                end
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                # Connect to machine without acquiring it
         | 
| 124 | 
            +
                def connect_to_machine(machine_spec, machine_options)
         | 
| 125 | 
            +
                  Chef::Log.debug('Connect to machine')
         | 
| 126 | 
            +
                  machine_for(machine_spec, machine_options)
         | 
| 127 | 
            +
                end
         | 
| 132 128 |  | 
| 133 | 
            -
             | 
| 134 | 
            -
             | 
| 135 | 
            -
             | 
| 136 | 
            -
             | 
| 129 | 
            +
                def destroy_machine(action_handler, machine_spec, machine_options)
         | 
| 130 | 
            +
                  container = container_for(machine_spec)
         | 
| 131 | 
            +
                  if container
         | 
| 132 | 
            +
                    image_id = container.info['Image']
         | 
| 133 | 
            +
                    action_handler.perform_action "stop and destroy container #{machine_spec.name}" do
         | 
| 134 | 
            +
                      container.stop
         | 
| 135 | 
            +
                      container.delete
         | 
| 137 136 | 
             
                    end
         | 
| 138 137 | 
             
                  end
         | 
| 138 | 
            +
                end
         | 
| 139 139 |  | 
| 140 | 
            -
             | 
| 141 | 
            -
             | 
| 142 | 
            -
             | 
| 143 | 
            -
             | 
| 140 | 
            +
                def stop_machine(action_handler, machine_spec, machine_options)
         | 
| 141 | 
            +
                  container = container_for(machine_spec)
         | 
| 142 | 
            +
                  if container.info['State']['Running']
         | 
| 143 | 
            +
                    action_handler.perform_action "stop container #{machine_spec.name}" do
         | 
| 144 | 
            +
                      container.stop!
         | 
| 144 145 | 
             
                    end
         | 
| 145 146 | 
             
                  end
         | 
| 146 | 
            -
             | 
| 147 | 
            -
                  args << image.id
         | 
| 148 | 
            -
                  args += Shellwords.split("/bin/sh -c 'while true;do sleep 1; done'")
         | 
| 149 | 
            -
             | 
| 150 | 
            -
                  cmdstr = Shellwords.join(args)
         | 
| 151 | 
            -
                  Chef::Log.debug("Executing #{cmdstr}")
         | 
| 152 | 
            -
             | 
| 153 | 
            -
                  cmd = Mixlib::ShellOut.new(cmdstr)
         | 
| 154 | 
            -
                  cmd.run_command
         | 
| 155 | 
            -
             | 
| 156 | 
            -
                  container = Docker::Container.get(machine_spec.reference['container_name'])
         | 
| 157 | 
            -
             | 
| 158 | 
            -
                  Chef::Log.debug("Container id: #{container.id}")
         | 
| 159 | 
            -
                  machine_spec.reference['container_id'] = container.id
         | 
| 160 | 
            -
                  container
         | 
| 161 147 | 
             
                end
         | 
| 162 148 |  | 
| 163 | 
            -
                 | 
| 164 | 
            -
             | 
| 165 | 
            -
             | 
| 166 | 
            -
                  source_repository = base_image[:repository]
         | 
| 167 | 
            -
                  source_tag = base_image[:tag]
         | 
| 168 | 
            -
             | 
| 169 | 
            -
                  target_tag = machine_spec.reference['container_name']
         | 
| 170 | 
            -
             | 
| 171 | 
            -
                  image = Docker::Image.create(
         | 
| 172 | 
            -
                    'fromImage' => source_name,
         | 
| 173 | 
            -
                    'repo' => source_repository,
         | 
| 174 | 
            -
                    'tag' => source_tag
         | 
| 175 | 
            -
                  )
         | 
| 176 | 
            -
                  
         | 
| 177 | 
            -
                  Chef::Log.debug("Allocated #{image}")
         | 
| 178 | 
            -
                  image.tag('repo' => 'chef', 'tag' => target_tag)
         | 
| 179 | 
            -
                  Chef::Log.debug("Tagged image #{image}")
         | 
| 180 | 
            -
             | 
| 181 | 
            -
                  machine_spec.reference['image_id'] = image.id
         | 
| 182 | 
            -
                  image
         | 
| 183 | 
            -
                end
         | 
| 149 | 
            +
                #
         | 
| 150 | 
            +
                # Images
         | 
| 151 | 
            +
                #
         | 
| 184 152 |  | 
| 185 153 | 
             
                def allocate_image(action_handler, image_spec, image_options, machine_spec, machine_options)
         | 
| 154 | 
            +
                  tag_container_image(action_handler, machine_spec, image_spec)
         | 
| 155 | 
            +
             | 
| 186 156 | 
             
                  # Set machine options on the image to match our newly created image
         | 
| 187 157 | 
             
                  image_spec.reference = {
         | 
| 188 158 | 
             
                    'driver_url' => driver_url,
         | 
| 189 159 | 
             
                    'driver_version' => Chef::Provisioning::DockerDriver::VERSION,
         | 
| 190 160 | 
             
                    'allocated_at' => Time.now.to_i,
         | 
| 191 | 
            -
                     | 
| 192 | 
            -
                       | 
| 193 | 
            -
                         | 
| 194 | 
            -
             | 
| 195 | 
            -
                        :tag => image_spec.name
         | 
| 196 | 
            -
                      },
         | 
| 197 | 
            -
                      :from_image => true
         | 
| 161 | 
            +
                    'docker_options' => {
         | 
| 162 | 
            +
                      'base_image' => {
         | 
| 163 | 
            +
                        'name' => image_spec.name
         | 
| 164 | 
            +
                      }
         | 
| 198 165 | 
             
                    }
         | 
| 199 166 | 
             
                  }
         | 
| 167 | 
            +
             | 
| 200 168 | 
             
                  # Workaround for chef/chef-provisioning-docker#37
         | 
| 201 169 | 
             
                  machine_spec.attrs[:keep_image] = true
         | 
| 202 170 | 
             
                end
         | 
| @@ -207,66 +175,33 @@ module DockerDriver | |
| 207 175 |  | 
| 208 176 | 
             
                # workaround for https://github.com/chef/chef-provisioning/issues/358.
         | 
| 209 177 | 
             
                def destroy_image(action_handler, image_spec, image_options, machine_options={})
         | 
| 210 | 
            -
                  image =  | 
| 178 | 
            +
                  image = image_for(image_spec)
         | 
| 211 179 | 
             
                  image.delete unless image.nil?
         | 
| 212 180 | 
             
                end
         | 
| 213 181 |  | 
| 214 | 
            -
                 | 
| 215 | 
            -
                def connect_to_machine(machine_spec, machine_options)
         | 
| 216 | 
            -
                  Chef::Log.debug('Connect to machine')
         | 
| 217 | 
            -
                  machine_for(machine_spec, machine_options)
         | 
| 218 | 
            -
                end
         | 
| 182 | 
            +
                private
         | 
| 219 183 |  | 
| 220 | 
            -
                def  | 
| 184 | 
            +
                def tag_container_image(action_handler, machine_spec, image_spec)
         | 
| 221 185 | 
             
                  container = container_for(machine_spec)
         | 
| 222 | 
            -
                   | 
| 223 | 
            -
             | 
| 224 | 
            -
                    container. | 
| 225 | 
            -
             | 
| 226 | 
            -
             | 
| 227 | 
            -
             | 
| 228 | 
            -
                    image = find_image(machine_spec)
         | 
| 229 | 
            -
                    Chef::Log.debug("Destroying image: chef:#{image.id}")
         | 
| 230 | 
            -
                    image.delete
         | 
| 186 | 
            +
                  existing_image = image_for(image_spec)
         | 
| 187 | 
            +
                  unless existing_image && existing_image.id == container.info['Image']
         | 
| 188 | 
            +
                    image = Docker::Image.get(container.info['Image'], {}, @connection)
         | 
| 189 | 
            +
                    action_handler.perform_action "tag image #{container.info['Image']} as chef-images/#{image_spec.name}" do
         | 
| 190 | 
            +
                      image.tag('repo' => image_spec.name, 'force' => true)
         | 
| 191 | 
            +
                    end
         | 
| 231 192 | 
             
                  end
         | 
| 232 193 | 
             
                end
         | 
| 233 194 |  | 
| 234 | 
            -
                def  | 
| 235 | 
            -
                   | 
| 236 | 
            -
                  return if container.nil?
         | 
| 237 | 
            -
             | 
| 238 | 
            -
                  container.stop if container.info['State']['Running']
         | 
| 195 | 
            +
                def to_camel_case(name)
         | 
| 196 | 
            +
                  name.split('_').map { |x| x.capitalize }.join("")
         | 
| 239 197 | 
             
                end
         | 
| 240 198 |  | 
| 241 | 
            -
                def  | 
| 242 | 
            -
                   | 
| 243 | 
            -
             | 
| 244 | 
            -
                   | 
| 245 | 
            -
             | 
| 246 | 
            -
             | 
| 247 | 
            -
                    rescue Docker::Error::NotFoundError
         | 
| 248 | 
            -
                    end
         | 
| 249 | 
            -
                  end
         | 
| 250 | 
            -
             | 
| 251 | 
            -
                  if image.nil?
         | 
| 252 | 
            -
                    image_name = "chef:#{machine_spec.reference['container_name']}"
         | 
| 253 | 
            -
                    if machine_spec.from_image
         | 
| 254 | 
            -
                      base_image = base_image_for(machine_spec)
         | 
| 255 | 
            -
                      image_name = "#{base_image[:repository]}:#{base_image[:tag]}"
         | 
| 256 | 
            -
                    end
         | 
| 257 | 
            -
             | 
| 258 | 
            -
                    image = Docker::Image.all.select {
         | 
| 259 | 
            -
                        |i| i.info['RepoTags'].include? image_name
         | 
| 260 | 
            -
                    }.first
         | 
| 261 | 
            -
             | 
| 262 | 
            -
                    if machine_spec.from_image && image.nil?
         | 
| 263 | 
            -
                      raise "Unable to locate machine_image for #{image_name}"
         | 
| 264 | 
            -
                    end
         | 
| 265 | 
            -
                  end
         | 
| 266 | 
            -
             | 
| 267 | 
            -
                  machine_spec.reference['image_id'] = image.id if image
         | 
| 268 | 
            -
             | 
| 269 | 
            -
                  image
         | 
| 199 | 
            +
                def to_snake_case(name)
         | 
| 200 | 
            +
                  # ExposedPorts -> _exposed_ports
         | 
| 201 | 
            +
                  name = name.gsub(/[A-Z]/) { |x| "_#{x.downcase}" }
         | 
| 202 | 
            +
                  # _exposed_ports -> exposed_ports
         | 
| 203 | 
            +
                  name = name[1..-1] if name.start_with?('_')
         | 
| 204 | 
            +
                  name
         | 
| 270 205 | 
             
                end
         | 
| 271 206 |  | 
| 272 207 | 
             
                def from_image_from_action_handler(action_handler, machine_spec)
         | 
| @@ -280,22 +215,11 @@ module DockerDriver | |
| 280 215 | 
             
                  end
         | 
| 281 216 | 
             
                end
         | 
| 282 217 |  | 
| 283 | 
            -
                def driver_url
         | 
| 284 | 
            -
                  "docker:#{Docker.url}"
         | 
| 285 | 
            -
                end
         | 
| 286 | 
            -
             | 
| 287 | 
            -
                def start_machine(action_handler, machine_spec, machine_options)
         | 
| 288 | 
            -
                  container = container_for(machine_spec)
         | 
| 289 | 
            -
                  if container && !container.info['State']['Running']
         | 
| 290 | 
            -
                    container.start
         | 
| 291 | 
            -
                  end
         | 
| 292 | 
            -
                end
         | 
| 293 | 
            -
             | 
| 294 218 | 
             
                def machine_for(machine_spec, machine_options)
         | 
| 295 219 | 
             
                  Chef::Log.debug('machine_for...')
         | 
| 296 | 
            -
                  docker_options = machine_options[:docker_options] || Mash.from_hash(machine_spec.reference['docker_options'])
         | 
| 220 | 
            +
                  docker_options = machine_options[:docker_options] || Mash.from_hash(machine_spec.reference['docker_options'] || {})
         | 
| 297 221 |  | 
| 298 | 
            -
                  container =  | 
| 222 | 
            +
                  container = container_for(machine_spec)
         | 
| 299 223 |  | 
| 300 224 | 
             
                  if machine_spec.from_image
         | 
| 301 225 | 
             
                    convergence_strategy = Chef::Provisioning::ConvergenceStrategy::NoConverge.new({}, config)
         | 
| @@ -310,22 +234,32 @@ module DockerDriver | |
| 310 234 | 
             
                    machine_spec,
         | 
| 311 235 | 
             
                    transport,
         | 
| 312 236 | 
             
                    convergence_strategy,
         | 
| 237 | 
            +
                    @connection,
         | 
| 313 238 | 
             
                    docker_options[:command]
         | 
| 314 239 | 
             
                  )
         | 
| 315 240 | 
             
                end
         | 
| 316 241 |  | 
| 317 242 | 
             
                def container_for(machine_spec)
         | 
| 318 | 
            -
                  container_id = machine_spec.reference['container_id']
         | 
| 319 243 | 
             
                  begin
         | 
| 320 | 
            -
                     | 
| 244 | 
            +
                    Docker::Container.get(machine_spec.name, {}, @connection)
         | 
| 245 | 
            +
                  rescue Docker::Error::NotFoundError
         | 
| 246 | 
            +
                  end
         | 
| 247 | 
            +
                end
         | 
| 248 | 
            +
             | 
| 249 | 
            +
                def image_for(image_spec)
         | 
| 250 | 
            +
                  begin
         | 
| 251 | 
            +
                    Docker::Image.get(image_spec.name, {}, @connection)
         | 
| 321 252 | 
             
                  rescue Docker::Error::NotFoundError
         | 
| 322 253 | 
             
                  end
         | 
| 323 254 | 
             
                end
         | 
| 324 255 |  | 
| 325 | 
            -
                def  | 
| 326 | 
            -
                   | 
| 327 | 
            -
             | 
| 328 | 
            -
             | 
| 256 | 
            +
                def stringize_keys(hash)
         | 
| 257 | 
            +
                  if hash
         | 
| 258 | 
            +
                    hash.each_with_object({}) do |(k,v),hash|
         | 
| 259 | 
            +
                      v = stringize_keys(v) if v.is_a?(Hash)
         | 
| 260 | 
            +
                      hash[k.to_s] = v
         | 
| 261 | 
            +
                    end
         | 
| 262 | 
            +
                  end
         | 
| 329 263 | 
             
                end
         | 
| 330 264 | 
             
              end
         | 
| 331 265 | 
             
            end
         |