class Kitchen::Driver::Dokken
@author Sean OMeara <sean@chef.io>
Dokken driver for Kitchen.
def build_work_image(state)
def build_work_image(state) return if Docker::Image.exist?(work_image, docker_connection) FileUtils.mkdir_p context_root File.write("#{context_root}/Dockerfile", work_image_dockerfile) i = Docker::Image.build_from_dir(context_root, { 'nocache' => true, 'rm' => true }, docker_connection) i.tag('repo' => repo(work_image), 'tag' => tag(work_image), 'force' => true) state[:work_image] = work_image end
def chef_container_name
def chef_container_name "chef-#{chef_version}" end
def chef_image
def chef_image "someara/chef:#{chef_version}" end
def chef_version
def chef_version config[:chef_version] end
def container_exist?(name)
def container_exist?(name) return true if Docker::Container.get(name) rescue false end
def context_root
def context_root tmpdir = Dir.tmpdir "#{tmpdir}/dokken/#{instance_name}" end
def create(state)
def create(state) # image to config pull_platform_image # chef pull_chef_image start_chef_container state # data make_data_image start_data_container state # work image build_work_image state # runner start_runner_container state # misc save_misc_state state end
def create_container(args)
def create_container(args) c = Docker::Container.create(args.clone, docker_connection) rescue Docker::Error::ConflictError c = Docker::Container.get(args['name']) end
def data_container_name
def data_container_name "#{instance.name}-data" end
def data_image
def data_image config[:data_image] end
def delete_chef_container
def delete_chef_container debug "driver - deleting container #{chef_container_name}" delete_container chef_container_name end
def delete_container(name)
def delete_container(name) c = Docker::Container.get(name, docker_connection) puts "Destroying container #{name}." c.stop c.delete(force: true, v: true) rescue puts "Container #{name} not found. Nothing to delete." end
def delete_data
def delete_data debug "driver - deleting container #{data_container_name}" delete_container data_container_name end
def delete_image(name)
def delete_image(name) i = Docker::Image.get(name, docker_connection) i.remove(force: true) rescue Docker::Error => e puts "Image #{name} not found. Nothing to delete." end
def delete_runner
def delete_runner debug "driver - deleting container #{runner_container_name}" delete_container runner_container_name end
def delete_work_image
def delete_work_image return unless Docker::Image.exist?(work_image, docker_connection) i = Docker::Image.get(work_image, docker_connection) i.remove(force: true) end
def destroy(_state)
def destroy(_state) delete_data delete_runner delete_work_image end
def docker_connection
def docker_connection opts = Docker.options opts[:read_timeout] = config[:read_timeout] opts[:write_timeout] = config[:write_timeout] @docker_connection ||= Docker::Connection.new(config[:docker_host_url], opts) end
def image_prefix
def image_prefix config[:image_prefix] end
def instance_name
def instance_name instance.name end
def instance_platform_name
def instance_platform_name instance.platform.name end
def make_data_image
def make_data_image debug "driver - pulling #{data_image}" pull_if_missing data_image # -- or -- # debug 'driver - calling create_data_image' # create_data_image end
def platform_image
def platform_image config[:image] end
def pull_chef_image
def pull_chef_image debug "driver - pulling #{chef_image} #{repo(chef_image)} #{tag(chef_image)}" pull_if_missing chef_image end
def pull_if_missing(image)
def pull_if_missing(image) return if Docker::Image.exist?("#{repo(image)}:#{tag(image)}", docker_connection) pull_image image end
def pull_image(image)
def pull_image(image) retries ||= 3 Docker::Image.create({ 'fromImage' => repo(image), 'tag' => tag(image) }, docker_connection) rescue Docker::Error => e retry unless (tries -= 1).zero? raise e.message end
def pull_platform_image
def pull_platform_image debug "driver - pulling #{chef_image} #{repo(platform_image)} #{tag(platform_image)}" pull_if_missing platform_image end
def repo(image)
def repo(image) image.split(':')[0] end
def run_container(args)
def run_container(args) c = create_container(args) tries ||= 3 begin c.start return c rescue Docker::Error => e retry unless (tries -= 1).zero? raise e.message end end
def runner_container_name
def runner_container_name "#{instance.name}" end
def save_misc_state(state)
def save_misc_state(state) state[:platform_image] = platform_image state[:instance_name] = instance_name state[:instance_platform_name] = instance_platform_name state[:image_prefix] = image_prefix end
def start_chef_container(state)
def start_chef_container(state) c = Docker::Container.get(chef_container_name) rescue Docker::Error::NotFoundError begin debug "driver - creating volume container #{chef_container_name} from #{chef_image}" chef_container = create_container( 'name' => chef_container_name, 'Cmd' => 'true', 'Image' => "#{repo(chef_image)}:#{tag(chef_image)}" ) state[:chef_container] = chef_container.json rescue debug "driver - #{chef_container_name} alreay exists" end end
def start_data_container(state)
def start_data_container(state) debug "driver - creating #{data_container_name}" data_container = run_container( 'name' => data_container_name, 'Image' => "#{repo(data_image)}:#{tag(data_image)}", 'PortBindings' => { '22/tcp' => [ { 'HostPort' => '' } ] }, 'PublishAllPorts' => true ) state[:data_container] = data_container.json end
def start_runner_container(state)
def start_runner_container(state) debug "driver - starting #{runner_container_name}" runner_container = run_container( 'name' => runner_container_name, 'Cmd' => Shellwords.shellwords(config[:pid_one_command]), 'Image' => "#{repo(work_image)}:#{tag(work_image)}", 'HostConfig' => { 'Privileged' => config[:privileged], 'VolumesFrom' => [chef_container_name, data_container_name] } ) state[:runner_container] = runner_container.json end
def tag(image)
def tag(image) image.split(':')[1] || 'latest' end
def work_image
def work_image return "#{image_prefix}/#{instance_name}" unless image_prefix.nil? instance_name end
def work_image_dockerfile
def work_image_dockerfile from = "FROM #{platform_image}" custom = [] Array(config[:intermediate_instructions]).each { |c| custom << c } [from, custom].join("\n") end