Add optional support for docker-compose

Adds configuration switch to enable using docker-compose
to create and manage docker containers.
This commit is contained in:
Chris Roberts 2017-05-09 18:54:15 -07:00
parent eb0fd71baf
commit deba93ce5c
4 changed files with 179 additions and 6 deletions

View File

@ -19,6 +19,8 @@ module VagrantPlugins
# @return [String]
attr_accessor :build_dir
attr_accessor :compose
# An optional file name of a Dockerfile to be used when building
# the image. This requires Docker >1.5.0.
#
@ -138,6 +140,7 @@ module VagrantPlugins
@build_args = []
@build_dir = UNSET_VALUE
@cmd = UNSET_VALUE
@compose = UNSET_VALUE
@create_args = UNSET_VALUE
@dockerfile = UNSET_VALUE
@env = {}
@ -201,6 +204,7 @@ module VagrantPlugins
@build_args = [] if @build_args == UNSET_VALUE
@build_dir = nil if @build_dir == UNSET_VALUE
@cmd = [] if @cmd == UNSET_VALUE
@compose = false if @compose == UNSET_VALUE
@create_args = [] if @create_args == UNSET_VALUE
@dockerfile = nil if @dockerfile == UNSET_VALUE
@env ||= {}

View File

@ -1,7 +1,8 @@
require "json"
require "log4r"
require_relative "./driver/compose"
module VagrantPlugins
module DockerProvider
class Driver

View File

@ -0,0 +1,166 @@
require "json"
require "log4r"
module VagrantPlugins
module DockerProvider
class Driver
class Compose < Driver
# @return [String] Compose file format version
COMPOSE_VERSION = "2".freeze
# @return [Pathname] data directory to store composition
attr_reader :data_directory
# @return [Vagrant::Machine]
attr_reader :machine
# Create a new driver instance
#
# @param [Vagrant::Machine] machine Machine instance for this driver
def initialize(machine)
super()
@machine = machine
@data_directory = Pathname.new(machine.env.local_data_path).
join("docker-compose")
@data_directory.mkpath
@logger = Log4r::Logger.new("vagrant::docker::driver::compose")
@compose_lock = Mutex.new
end
def build(dir, **opts, &block)
update_composition do |composition|
composition["build"] = dir
end
end
def create(params, **opts, &block)
# NOTE: Use the direct machine name as we don't
# need to worry about uniqueness with compose
name = machine.name.to_s
image = params.fetch(:image)
links = params.fetch(:links)
ports = Array(params[:ports])
volumes = Array(params[:volumes])
cmd = Array(params.fetch(:cmd))
env = params.fetch(:env)
expose = Array(params[:expose])
begin
update_composition do |composition|
services = composition["services"] ||= {}
services[name] = {
"image" => image,
"environment" => env,
"expose" => expose,
"ports" => ports,
"volumes" => volumes,
"links" => links,
"command" => cmd
}
end
rescue
update_composition(false) do |composition|
composition["services"].delete(name)
end
raise
end
get_container_id(name)
end
def rm(cid)
if created?(cid)
destroy = false
compose_execute("rm", "-f", machine.name.to_s)
update_composition do |composition|
if composition["services"] && composition["services"].key?(machine.name.to_s)
@logger.info("Removing container `#{machine.name}`")
composition["services"].delete(machine.name.to_s)
destroy = composition["services"].empty?
end
end
if destroy
@logger.info("No containers remain. Destroying full environment.")
compose_execute("down", "--remove-orphans")
end
end
end
def created?(cid)
result = super
if !result
composition = get_current_composition
if composition["services"] && composition["services"].has_key?(machine.name.to_s)
result = true
end
end
result
end
private
# Lookup the ID for the container with the given name
#
# @param [String] name Name of container
# @return [String] Container ID
def get_container_id(name)
compose_execute("ps", "-q", name).chomp
end
# Execute a `docker-compose` command
def compose_execute(*cmd, **opts)
@compose_lock.synchronize do
execute("docker-compose", "-f", composition_path.to_s,
"-p", machine.env.cwd.basename.to_s, *cmd, **opts)
end
end
# Apply any changes made to the composition
def apply_composition!
machine.env.lock("compose", retry: true) do
compose_execute("up", "-d", "--remove-orphans")
end
end
# Update the composition and apply changes if requested
#
# @param [Boolean] apply Apply composition changes
def update_composition(apply=true)
machine.env.lock("compose", retry: true) do
composition = get_current_composition
yield composition
write_composition(composition)
apply_composition! if apply
end
end
# @return [Hash] current composition contents
def get_current_composition
composition = {"version" => COMPOSE_VERSION.dup}
if composition_path.exist?
composition.merge!(
YAML.load(composition_path.read)
)
end
composition
end
# Save the composition
#
# @param [Hash] composition New composition
def write_composition(composition)
tmp_file = Tempfile.new("vagrant-docker-compose")
tmp_file.write(composition.to_yaml)
tmp_file.close
@compose_lock.synchronize do
FileUtils.mv(tmp_file.path, composition_path.to_s)
end
end
# @return [Pathname] path to the docker-compose.yml file
def composition_path
data_directory.join("docker-compose.yml")
end
end
end
end
end

View File

@ -31,11 +31,13 @@ module VagrantPlugins
# Returns the driver instance for this provider.
def driver
return @driver if @driver
@driver = Driver.new
# If we are running on a host machine, then we set the executor
# to execute remotely.
if !@driver
if @machine.provider_config.compose
@driver = Driver::Compose.new(@machine)
else
@driver = Driver.new
end
end
if host_vm?
@driver.executor = Executor::Vagrant.new(host_vm)
end