2013-11-26 21:48:51 +00:00
|
|
|
require 'digest/sha1'
|
|
|
|
|
|
|
|
module VagrantPlugins
|
2014-03-26 23:32:31 +00:00
|
|
|
module DockerProvisioner
|
2013-12-12 19:54:20 +00:00
|
|
|
class Client
|
2013-11-26 21:48:51 +00:00
|
|
|
def initialize(machine)
|
|
|
|
@machine = machine
|
|
|
|
end
|
|
|
|
|
2014-01-14 17:22:55 +00:00
|
|
|
def build_images(images)
|
2013-11-26 21:48:51 +00:00
|
|
|
@machine.communicate.tap do |comm|
|
2014-01-14 17:22:55 +00:00
|
|
|
images.each do |path, opts|
|
|
|
|
@machine.ui.info(I18n.t("vagrant.docker_building_single", path: path))
|
2014-08-22 15:41:01 +00:00
|
|
|
comm.sudo("docker build #{opts[:args]} #{path}") do |type, data|
|
|
|
|
handle_comm(type, data)
|
|
|
|
end
|
2013-11-26 21:48:51 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-01-14 17:22:55 +00:00
|
|
|
def pull_images(*images)
|
2013-12-09 22:52:45 +00:00
|
|
|
@machine.communicate.tap do |comm|
|
|
|
|
images.each do |image|
|
2014-01-14 17:22:55 +00:00
|
|
|
@machine.ui.info(I18n.t("vagrant.docker_pulling_single", name: image))
|
2014-08-22 15:41:01 +00:00
|
|
|
comm.sudo("docker pull #{image}") do |type, data|
|
|
|
|
handle_comm(type, data)
|
|
|
|
end
|
2013-12-09 22:52:45 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-11-26 21:48:51 +00:00
|
|
|
def start_service
|
|
|
|
if !daemon_running? && @machine.guest.capability?(:docker_start_service)
|
|
|
|
@machine.guest.capability(:docker_start_service)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def daemon_running?
|
2014-04-10 20:42:12 +00:00
|
|
|
@machine.guest.capability(:docker_daemon_running)
|
2013-11-26 21:48:51 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def run(containers)
|
|
|
|
containers.each do |name, config|
|
2013-11-29 03:26:35 +00:00
|
|
|
cids_dir = "/var/lib/vagrant/cids"
|
2013-11-26 21:48:51 +00:00
|
|
|
config[:cidfile] ||= "#{cids_dir}/#{Digest::SHA1.hexdigest name}"
|
|
|
|
|
2013-12-03 22:33:13 +00:00
|
|
|
@machine.ui.info(I18n.t("vagrant.docker_running", name: name))
|
2013-11-26 21:48:51 +00:00
|
|
|
@machine.communicate.sudo("mkdir -p #{cids_dir}")
|
2013-12-03 22:33:13 +00:00
|
|
|
run_container({
|
2014-04-12 01:50:02 +00:00
|
|
|
name: name,
|
|
|
|
original_name: name,
|
2013-12-03 22:33:13 +00:00
|
|
|
}.merge(config))
|
2013-11-26 21:48:51 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def run_container(config)
|
2013-12-03 22:36:14 +00:00
|
|
|
raise "Container's cidfile was not provided!" if !config[:cidfile]
|
2013-11-26 21:48:51 +00:00
|
|
|
|
|
|
|
id = "$(cat #{config[:cidfile]})"
|
|
|
|
|
2013-12-05 00:07:41 +00:00
|
|
|
if container_exists?(id)
|
2015-11-25 00:01:21 +00:00
|
|
|
|
|
|
|
if container_args_changed?(config) or container_img_changed?(config)
|
2015-07-10 17:32:02 +00:00
|
|
|
@machine.ui.info(I18n.t("vagrant.docker_restarting_container",
|
|
|
|
name: config[:name],
|
|
|
|
))
|
|
|
|
stop_container(id)
|
|
|
|
create_container(config)
|
|
|
|
else
|
|
|
|
start_container(id)
|
|
|
|
end
|
2013-11-26 21:48:51 +00:00
|
|
|
else
|
|
|
|
create_container(config)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-12-05 00:07:41 +00:00
|
|
|
def container_exists?(id)
|
2013-12-05 00:13:17 +00:00
|
|
|
lookup_container(id, true)
|
2013-11-26 21:48:51 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def start_container(id)
|
2013-12-03 22:36:14 +00:00
|
|
|
if !container_running?(id)
|
2013-11-26 21:48:51 +00:00
|
|
|
@machine.communicate.sudo("docker start #{id}")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-07-10 17:32:02 +00:00
|
|
|
def stop_container(id)
|
|
|
|
@machine.communicate.sudo %[
|
|
|
|
docker stop #{id}
|
|
|
|
docker rm #{id}
|
|
|
|
]
|
|
|
|
end
|
|
|
|
|
2013-11-26 21:48:51 +00:00
|
|
|
def container_running?(id)
|
2013-12-05 00:13:17 +00:00
|
|
|
lookup_container(id)
|
2013-11-26 21:48:51 +00:00
|
|
|
end
|
|
|
|
|
2015-11-25 00:01:21 +00:00
|
|
|
def container_img_changed?(config)
|
|
|
|
# Returns true if there is a container running with the given :name,
|
|
|
|
# and the container is not using the latest :image.
|
|
|
|
|
|
|
|
# Here, "docker inspect <container>" returns the id of the image
|
|
|
|
# that the container is using. We check that the latest image that
|
|
|
|
# has been built with that name (:image) matches the one that the
|
|
|
|
# container is running.
|
|
|
|
cmd = ("docker inspect --format='{{.Image}}' #{config[:name]} |" +
|
|
|
|
" grep $(docker images -q #{config[:image]})")
|
|
|
|
return !@machine.communicate.test(cmd)
|
|
|
|
end
|
|
|
|
|
2015-07-10 17:32:02 +00:00
|
|
|
def container_args_changed?(config)
|
|
|
|
path = container_data_path(config)
|
|
|
|
return true if !path.exist?
|
2014-04-12 01:50:02 +00:00
|
|
|
|
2015-07-10 17:32:02 +00:00
|
|
|
args = container_run_args(config)
|
|
|
|
sha = Digest::SHA1.hexdigest(args)
|
|
|
|
return true if path.read.chomp != sha
|
|
|
|
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
|
|
|
def create_container(config)
|
|
|
|
args = container_run_args(config)
|
2014-04-12 01:50:02 +00:00
|
|
|
|
2013-11-26 21:48:51 +00:00
|
|
|
@machine.communicate.sudo %[
|
|
|
|
rm -f #{config[:cidfile]}
|
2015-07-10 17:32:02 +00:00
|
|
|
docker run #{args}
|
2013-11-26 21:48:51 +00:00
|
|
|
]
|
2015-07-10 17:32:02 +00:00
|
|
|
|
|
|
|
name = container_name(config)
|
|
|
|
sha = Digest::SHA1.hexdigest(args)
|
|
|
|
container_data_path(config).open("w+") do |f|
|
|
|
|
f.write(sha)
|
|
|
|
end
|
2013-11-26 21:48:51 +00:00
|
|
|
end
|
2013-12-05 00:13:17 +00:00
|
|
|
|
|
|
|
def lookup_container(id, list_all = false)
|
|
|
|
docker_ps = "sudo docker ps -q"
|
|
|
|
docker_ps << " -a" if list_all
|
|
|
|
@machine.communicate.tap do |comm|
|
|
|
|
# Docker < 0.7.0 stores container IDs using its short version while
|
|
|
|
# recent versions use the full container ID
|
2016-02-28 12:00:35 +00:00
|
|
|
# using full container ID from now on.
|
|
|
|
return comm.test("#{docker_ps} --no-trunc | grep -wFq #{id}")
|
2013-12-05 00:13:17 +00:00
|
|
|
end
|
|
|
|
end
|
2015-07-07 18:17:24 +00:00
|
|
|
|
2015-07-10 17:32:02 +00:00
|
|
|
def container_name(config)
|
|
|
|
name = config[:name]
|
|
|
|
|
|
|
|
# If the name is the automatically assigned name, then
|
|
|
|
# replace the "/" with "-" because "/" is not a valid
|
|
|
|
# character for a docker container name.
|
|
|
|
name = name.gsub("/", "-") if name == config[:original_name]
|
|
|
|
name
|
|
|
|
end
|
|
|
|
|
|
|
|
def container_run_args(config)
|
|
|
|
name = container_name(config)
|
|
|
|
|
|
|
|
args = "--cidfile=#{config[:cidfile]} "
|
|
|
|
args << "-d " if config[:daemonize]
|
|
|
|
args << "--name #{name} " if name && config[:auto_assign_name]
|
|
|
|
args << "--restart=#{config[:restart]}" if config[:restart]
|
|
|
|
args << " #{config[:args]}" if config[:args]
|
|
|
|
|
|
|
|
"#{args} #{config[:image]} #{config[:cmd]}".strip
|
|
|
|
end
|
|
|
|
|
|
|
|
def container_data_path(config)
|
|
|
|
name = container_name(config)
|
|
|
|
@machine.data_dir.join("docker-#{name}")
|
|
|
|
end
|
|
|
|
|
2014-08-22 15:41:01 +00:00
|
|
|
protected
|
|
|
|
|
|
|
|
# This handles outputting the communication data back to the UI
|
|
|
|
def handle_comm(type, data)
|
|
|
|
if [:stderr, :stdout].include?(type)
|
|
|
|
# Clear out the newline since we add one
|
|
|
|
data = data.chomp
|
|
|
|
return if data.empty?
|
|
|
|
|
|
|
|
options = {}
|
|
|
|
#options[:color] = color if !config.keep_color
|
|
|
|
|
|
|
|
@machine.ui.info(data.chomp, options)
|
|
|
|
end
|
|
|
|
end
|
2013-11-26 21:48:51 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|