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:
parent
eb0fd71baf
commit
deba93ce5c
|
@ -19,6 +19,8 @@ module VagrantPlugins
|
||||||
# @return [String]
|
# @return [String]
|
||||||
attr_accessor :build_dir
|
attr_accessor :build_dir
|
||||||
|
|
||||||
|
attr_accessor :compose
|
||||||
|
|
||||||
# An optional file name of a Dockerfile to be used when building
|
# An optional file name of a Dockerfile to be used when building
|
||||||
# the image. This requires Docker >1.5.0.
|
# the image. This requires Docker >1.5.0.
|
||||||
#
|
#
|
||||||
|
@ -138,6 +140,7 @@ module VagrantPlugins
|
||||||
@build_args = []
|
@build_args = []
|
||||||
@build_dir = UNSET_VALUE
|
@build_dir = UNSET_VALUE
|
||||||
@cmd = UNSET_VALUE
|
@cmd = UNSET_VALUE
|
||||||
|
@compose = UNSET_VALUE
|
||||||
@create_args = UNSET_VALUE
|
@create_args = UNSET_VALUE
|
||||||
@dockerfile = UNSET_VALUE
|
@dockerfile = UNSET_VALUE
|
||||||
@env = {}
|
@env = {}
|
||||||
|
@ -201,6 +204,7 @@ module VagrantPlugins
|
||||||
@build_args = [] if @build_args == UNSET_VALUE
|
@build_args = [] if @build_args == UNSET_VALUE
|
||||||
@build_dir = nil if @build_dir == UNSET_VALUE
|
@build_dir = nil if @build_dir == UNSET_VALUE
|
||||||
@cmd = [] if @cmd == UNSET_VALUE
|
@cmd = [] if @cmd == UNSET_VALUE
|
||||||
|
@compose = false if @compose == UNSET_VALUE
|
||||||
@create_args = [] if @create_args == UNSET_VALUE
|
@create_args = [] if @create_args == UNSET_VALUE
|
||||||
@dockerfile = nil if @dockerfile == UNSET_VALUE
|
@dockerfile = nil if @dockerfile == UNSET_VALUE
|
||||||
@env ||= {}
|
@env ||= {}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
require "json"
|
require "json"
|
||||||
|
|
||||||
require "log4r"
|
require "log4r"
|
||||||
|
|
||||||
|
require_relative "./driver/compose"
|
||||||
|
|
||||||
module VagrantPlugins
|
module VagrantPlugins
|
||||||
module DockerProvider
|
module DockerProvider
|
||||||
class Driver
|
class Driver
|
||||||
|
|
|
@ -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
|
|
@ -31,11 +31,13 @@ module VagrantPlugins
|
||||||
|
|
||||||
# Returns the driver instance for this provider.
|
# Returns the driver instance for this provider.
|
||||||
def driver
|
def driver
|
||||||
return @driver if @driver
|
if !@driver
|
||||||
|
if @machine.provider_config.compose
|
||||||
|
@driver = Driver::Compose.new(@machine)
|
||||||
|
else
|
||||||
@driver = Driver.new
|
@driver = Driver.new
|
||||||
|
end
|
||||||
# If we are running on a host machine, then we set the executor
|
end
|
||||||
# to execute remotely.
|
|
||||||
if host_vm?
|
if host_vm?
|
||||||
@driver.executor = Executor::Vagrant.new(host_vm)
|
@driver.executor = Executor::Vagrant.new(host_vm)
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue